diff --git a/engines/base/src/main/java/org/alfresco/transform/base/fs/FileManager.java b/engines/base/src/main/java/org/alfresco/transform/base/fs/FileManager.java index bb1b5631..f1575bea 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/fs/FileManager.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/fs/FileManager.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2025 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -26,16 +26,16 @@ */ package org.alfresco.transform.base.fs; -import org.alfresco.transform.base.logging.LogEntry; -import org.alfresco.transform.common.ExtensionService; -import org.alfresco.transform.exceptions.TransformException; -import org.springframework.core.io.Resource; -import org.springframework.core.io.UrlResource; -import org.springframework.http.ResponseEntity; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.util.UriUtils; +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.util.StringUtils.getFilename; + +import static org.alfresco.transform.common.ExtensionService.getExtensionForMimetype; -import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -43,14 +43,19 @@ import java.io.OutputStream; import java.net.MalformedURLException; import java.net.URL; import java.nio.file.Files; +import java.util.UUID; +import jakarta.servlet.http.HttpServletRequest; -import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; -import static org.alfresco.transform.common.ExtensionService.getExtensionForMimetype; -import static org.springframework.http.HttpHeaders.CONTENT_DISPOSITION; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.INSUFFICIENT_STORAGE; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.util.StringUtils.getFilename; +import org.apache.commons.lang3.StringUtils; +import org.springframework.core.io.Resource; +import org.springframework.core.io.UrlResource; +import org.springframework.http.ResponseEntity; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriUtils; + +import org.alfresco.transform.base.logging.LogEntry; +import org.alfresco.transform.common.ExtensionService; +import org.alfresco.transform.exceptions.TransformException; public class FileManager { @@ -58,16 +63,19 @@ public class FileManager public static final String TARGET_FILE = "targetFile"; private FileManager() - { - } + {} - public static File createSourceFile(HttpServletRequest request, InputStream inputStream, String sourceMimetype) + public static File createSourceFile(HttpServletRequest request, InputStream inputStream, String sourceMimetype, String sourceFileName) { try { - String extension = "."+getExtensionForMimetype(sourceMimetype); - File file = TempFileProvider.createTempFile("source_", extension); + String extension = "." + getExtensionForMimetype(sourceMimetype); + File file = StringUtils.isEmpty(sourceFileName) + ? TempFileProvider.createTempFile("source_", extension) + : TempFileProvider.createFileWithinUUIDTempDir(sourceFileName); + Files.copy(inputStream, file.toPath(), REPLACE_EXISTING); + if (request != null) { request.setAttribute(SOURCE_FILE, file); @@ -85,7 +93,7 @@ public class FileManager { try { - String extension = "."+ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype); + String extension = "." + ExtensionService.getExtensionForTargetMimetype(targetMimetype, sourceMimetype); File file = TempFileProvider.createTempFile("target_", extension); if (request != null) { @@ -120,20 +128,20 @@ public class FileManager else { throw new TransformException(INTERNAL_SERVER_ERROR, - "Could not read the target file: " + file.getPath()); + "Could not read the target file: " + file.getPath()); } } catch (MalformedURLException e) { throw new TransformException(INTERNAL_SERVER_ERROR, - "The target filename was malformed: " + file.getPath(), e); + "The target filename was malformed: " + file.getPath(), e); } } public static InputStream getMultipartFileInputStream(MultipartFile sourceMultipartFile) { InputStream inputStream; - if (sourceMultipartFile == null) + if (sourceMultipartFile == null) { throw new TransformException(BAD_REQUEST, "Required request part 'file' is not present"); } @@ -192,7 +200,7 @@ public class FileManager // targetFilename should never be null (will be "transform."+), so we should not worry about encodePath(null) targetFilename = UriUtils.encodePath(getFilename(targetFilename), "UTF-8"); return ResponseEntity.ok().header(CONTENT_DISPOSITION, - "attachment; filename*=UTF-8''" + targetFilename).body(targetResource); + "attachment; filename*=UTF-8''" + targetFilename).body(targetResource); } /** @@ -201,8 +209,7 @@ public class FileManager public static class TempFileProvider { private TempFileProvider() - { - } + {} public static File createTempFile(final String prefix, final String suffix) { @@ -214,11 +221,22 @@ public class FileManager catch (IOException e) { throw new RuntimeException( - "Failed to created temp file: \n prefix: " + prefix + - "\n suffix: " + suffix + "\n directory: " + directory, e); + "Failed to created temp file: \n prefix: " + prefix + + "\n suffix: " + suffix + "\n directory: " + directory, + e); } } + public static File createFileWithinUUIDTempDir(String sourceFileName) + { + File tempDir = new File(getTempDir(), UUID.randomUUID().toString()); + if (!tempDir.mkdirs() && !tempDir.exists()) + { + throw new TransformException(INSUFFICIENT_STORAGE, "Failed to create temp directory: " + tempDir); + } + return new File(tempDir, sourceFileName); + } + private static File getTempDir() { final String dirName = "Alfresco"; diff --git a/engines/base/src/main/java/org/alfresco/transform/base/transform/ProcessHandler.java b/engines/base/src/main/java/org/alfresco/transform/base/transform/ProcessHandler.java index 132be315..80e063a1 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/transform/ProcessHandler.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/transform/ProcessHandler.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2022 - 2023 Alfresco Software Limited + * Copyright (C) 2022 - 2025 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -26,19 +26,12 @@ */ package org.alfresco.transform.base.transform; -import org.alfresco.transform.base.CustomTransformer; -import org.alfresco.transform.base.TransformController; -import org.alfresco.transform.base.logging.LogEntry; -import org.alfresco.transform.base.probes.ProbeTransform; -import org.alfresco.transform.base.registry.CustomTransformers; -import org.alfresco.transform.client.model.TransformRequest; -import org.alfresco.transform.exceptions.TransformException; -import org.alfresco.transform.common.TransformerDebug; -import org.alfresco.transform.registry.TransformServiceRegistry; -import org.springframework.web.multipart.MultipartFile; +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; +import static org.springframework.http.HttpStatus.OK; + +import static org.alfresco.transform.common.RequestParamMap.*; -import jakarta.jms.Destination; -import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -46,30 +39,32 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import jakarta.jms.Destination; +import jakarta.servlet.http.HttpServletRequest; -import static org.alfresco.transform.common.RequestParamMap.DIRECT_ACCESS_URL; -import static org.alfresco.transform.common.RequestParamMap.SOURCE_EXTENSION; -import static org.alfresco.transform.common.RequestParamMap.SOURCE_MIMETYPE; -import static org.alfresco.transform.common.RequestParamMap.TARGET_EXTENSION; -import static org.alfresco.transform.common.RequestParamMap.TARGET_MIMETYPE; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; -import static org.springframework.http.HttpStatus.OK; +import org.springframework.web.multipart.MultipartFile; + +import org.alfresco.transform.base.CustomTransformer; +import org.alfresco.transform.base.TransformController; +import org.alfresco.transform.base.logging.LogEntry; +import org.alfresco.transform.base.probes.ProbeTransform; +import org.alfresco.transform.base.registry.CustomTransformers; +import org.alfresco.transform.client.model.TransformRequest; +import org.alfresco.transform.common.TransformerDebug; +import org.alfresco.transform.exceptions.TransformException; +import org.alfresco.transform.registry.TransformServiceRegistry; /** - * Provides the transform logic common to http (upload/download), message and probe requests. See - * {@link TransformHandler#handleHttpRequest(HttpServletRequest, MultipartFile, String, String, Map, ProbeTransform)}, - * {@link TransformHandler#handleMessageRequest(TransformRequest, Long, Destination, ProbeTransform)} and - * {@link TransformHandler#handleProbeRequest(String, String, Map, File, File, ProbeTransform)}. Note the handing of transform requests - * via a message queue is the same as via the {@link TransformController#transform(TransformRequest, Long, Destination)}. + * Provides the transform logic common to http (upload/download), message and probe requests. See {@link TransformHandler#handleHttpRequest(HttpServletRequest, MultipartFile, String, String, Map, ProbeTransform)}, {@link TransformHandler#handleMessageRequest(TransformRequest, Long, Destination, ProbeTransform)} and {@link TransformHandler#handleProbeRequest(String, String, Map, File, File, ProbeTransform)}. Note the handing of transform requests via a message queue is the same as via the {@link TransformController#transform(TransformRequest, Long, Destination)}. */ abstract class ProcessHandler extends FragmentHandler { private static final List NON_TRANSFORM_OPTION_REQUEST_PARAMETERS = Arrays.asList(SOURCE_EXTENSION, - TARGET_EXTENSION, TARGET_MIMETYPE, SOURCE_MIMETYPE, DIRECT_ACCESS_URL); + TARGET_EXTENSION, TARGET_MIMETYPE, SOURCE_MIMETYPE, DIRECT_ACCESS_URL, SOURCE_FILENAME); protected final String sourceMimetype; protected final String targetMimetype; + protected final String sourceFileName; private final Map transformOptions; protected String reference; private final TransformServiceRegistry transformRegistry; @@ -78,11 +73,12 @@ abstract class ProcessHandler extends FragmentHandler private final CustomTransformers customTransformers; ProcessHandler(String sourceMimetype, String targetMimetype, Map transformOptions, - String reference, TransformServiceRegistry transformRegistry, TransformerDebug transformerDebug, - ProbeTransform probeTransform, CustomTransformers customTransformers) + String reference, TransformServiceRegistry transformRegistry, TransformerDebug transformerDebug, + ProbeTransform probeTransform, CustomTransformers customTransformers) { this.sourceMimetype = sourceMimetype; this.targetMimetype = targetMimetype; + this.sourceFileName = transformOptions.getOrDefault(SOURCE_FILENAME, null); this.transformOptions = cleanTransformOptions(transformOptions); this.reference = reference; @@ -107,7 +103,6 @@ abstract class ProcessHandler extends FragmentHandler super.init(); } - public String getReference() { return reference; @@ -118,6 +113,7 @@ abstract class ProcessHandler extends FragmentHandler LogEntry.start(); transformManager.setSourceMimetype(sourceMimetype); transformManager.setTargetMimetype(targetMimetype); + transformManager.setSourceFileName(sourceFileName); probeTransform.incrementTransformerCount(); try { @@ -131,13 +127,13 @@ abstract class ProcessHandler extends FragmentHandler } catch (TransformException e) { - transformerDebug.logFailure(reference, " Error: "+e.getMessage()); + transformerDebug.logFailure(reference, " Error: " + e.getMessage()); LogEntry.setStatusCodeAndMessage(e.getStatus(), e.getMessage()); handleTransformException(e); } catch (Exception e) { - transformerDebug.logFailure(reference, " Error: "+e.getMessage()); + transformerDebug.logFailure(reference, " Error: " + e.getMessage()); LogEntry.setStatusCodeAndMessage(INTERNAL_SERVER_ERROR, e.getMessage()); handleException(e); } @@ -174,8 +170,7 @@ abstract class ProcessHandler extends FragmentHandler } protected void sendTransformResponse(TransformManagerImpl transformManager) - { - } + {} protected void handleTransformException(TransformException e) { @@ -188,19 +183,19 @@ abstract class ProcessHandler extends FragmentHandler } private String getTransformerName(final String sourceMimetype, long sourceSizeInBytes, final String targetMimetype, - final Map transformOptions) + final Map transformOptions) { final String transformerName = transformRegistry.findTransformerName(sourceMimetype, - sourceSizeInBytes, targetMimetype, transformOptions, null); + sourceSizeInBytes, targetMimetype, transformOptions, null); if (transformerName == null) { - throw new TransformException(BAD_REQUEST, "No transforms for: "+ - sourceMimetype+ - (sourceSizeInBytes >= 0 ? " ("+TransformerDebug.fileSize(sourceSizeInBytes)+")" : "")+ - " -> "+targetMimetype+ - transformOptions.entrySet().stream() - .map(entry -> entry.getKey()+"="+entry.getValue()) - .collect(Collectors.joining(", ", " ", ""))); + throw new TransformException(BAD_REQUEST, "No transforms for: " + + sourceMimetype + + (sourceSizeInBytes >= 0 ? " (" + TransformerDebug.fileSize(sourceSizeInBytes) + ")" : "") + + " -> " + targetMimetype + + transformOptions.entrySet().stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining(", ", " ", ""))); } return transformerName; } @@ -210,7 +205,7 @@ abstract class ProcessHandler extends FragmentHandler CustomTransformer customTransformer = customTransformers.get(transformName); if (customTransformer == null) { - throw new TransformException(INTERNAL_SERVER_ERROR, "Custom Transformer "+transformName+" not found"); + throw new TransformException(INTERNAL_SERVER_ERROR, "Custom Transformer " + transformName + " not found"); } return customTransformer; } diff --git a/engines/base/src/main/java/org/alfresco/transform/base/transform/TransformManagerImpl.java b/engines/base/src/main/java/org/alfresco/transform/base/transform/TransformManagerImpl.java index b6fcc945..65d325e3 100644 --- a/engines/base/src/main/java/org/alfresco/transform/base/transform/TransformManagerImpl.java +++ b/engines/base/src/main/java/org/alfresco/transform/base/transform/TransformManagerImpl.java @@ -2,7 +2,7 @@ * #%L * Alfresco Transform Core * %% - * Copyright (C) 2005 - 2023 Alfresco Software Limited + * Copyright (C) 2005 - 2025 Alfresco Software Limited * %% * This file is part of the Alfresco software. * - @@ -26,18 +26,21 @@ */ package org.alfresco.transform.base.transform; -import org.alfresco.transform.base.TransformManager; -import org.alfresco.transform.base.fs.FileManager; -import org.alfresco.transform.base.util.OutputStreamLengthRecorder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; - -import jakarta.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.Locale; +import jakarta.servlet.http.HttpServletRequest; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import org.alfresco.transform.base.TransformManager; +import org.alfresco.transform.base.fs.FileManager; +import org.alfresco.transform.base.util.OutputStreamLengthRecorder; /** * Manages the input and output streams and any temporary files that have been created. @@ -60,6 +63,17 @@ public class TransformManagerImpl implements TransformManager private boolean createTargetFileCalled; private Boolean startedWithSourceFile; private Boolean startedWithTargetFile; + private String sourceFileName; + + public String getSourceFileName() + { + return sourceFileName; + } + + public void setSourceFileName(String sourceFileName) + { + this.sourceFileName = sourceFileName; + } public void setRequest(HttpServletRequest request) { @@ -71,7 +85,8 @@ public class TransformManagerImpl implements TransformManager this.processHandler = processHandler; } - @Override public String getRequestId() + @Override + public String getRequestId() { return processHandler.getReference(); } @@ -149,7 +164,8 @@ public class TransformManagerImpl implements TransformManager keepTargetFile = true; } - @Override public File createSourceFile() + @Override + public File createSourceFile() { if (createSourceFileCalled) { @@ -159,12 +175,13 @@ public class TransformManagerImpl implements TransformManager if (sourceFile == null) { - sourceFile = FileManager.createSourceFile(request, inputStream, sourceMimetype); + sourceFile = FileManager.createSourceFile(request, inputStream, sourceMimetype, sourceFileName); } return sourceFile; } - @Override public File createTargetFile() + @Override + public File createTargetFile() { if (createTargetFileCalled) { @@ -204,8 +221,19 @@ public class TransformManagerImpl implements TransformManager { logger.error("Failed to delete temporary source file {}", sourceFile.getPath()); } + if (sourceFile != null) + { + File parentDir = sourceFile.getParentFile(); + if (parentDir != null + && !StringUtils.equalsAny(parentDir.getName().toLowerCase(Locale.ROOT), "alfresco", "temp", "tmp") + && !parentDir.delete()) + { + logger.error("Failed to delete parent directory {}", parentDir.getPath()); + } + } outputStreamLengthRecorder = null; sourceFile = null; + sourceFileName = null; createSourceFileCalled = false; startedWithSourceFile = null; } diff --git a/engines/base/src/main/resources/templates/test.html b/engines/base/src/main/resources/templates/test.html index d7d5fc77..0a5ea02d 100644 --- a/engines/base/src/main/resources/templates/test.html +++ b/engines/base/src/main/resources/templates/test.html @@ -10,6 +10,7 @@
+
file
sourceFilename
directAccessUrl
sourceMimetype