diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoResourceFactory.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoResourceFactory.java index 229ff5ad6..104dbb64c 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoResourceFactory.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/CryptoResourceFactory.java @@ -24,6 +24,7 @@ import org.apache.jackrabbit.webdav.lock.LockManager; import org.apache.jackrabbit.webdav.lock.SimpleLockManager; import org.apache.logging.log4j.util.Strings; import org.cryptomator.crypto.Cryptor; +import org.cryptomator.webdav.exceptions.IORuntimeException; import org.eclipse.jetty.http.HttpHeader; public class CryptoResourceFactory implements DavResourceFactory, FileConstants { @@ -52,34 +53,36 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants return createRootDirectory(locator, request.getDavSession()); } - final Path filePath = getEncryptedFilePath(locator.getResourcePath()); - final Path dirFilePath = getEncryptedDirectoryFilePath(locator.getResourcePath()); - final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString()); - final String ifRangeHeader = request.getHeader(HttpHeader.IF_RANGE.asString()); - if (Files.exists(dirFilePath) || DavMethods.METHOD_MKCOL.equals(request.getMethod())) { - // DIRECTORY - return createDirectory(locator, request.getDavSession(), dirFilePath); - } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && isRangeSatisfiable(rangeHeader) && isIfRangePreconditionFulfilled(ifRangeHeader, filePath)) { - // FILE RANGE - final Pair requestRange = getRequestRange(rangeHeader); - response.setStatus(DavServletResponse.SC_PARTIAL_CONTENT); - return createFilePart(locator, request.getDavSession(), requestRange, filePath); - } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && isRangeSatisfiable(rangeHeader) && !isIfRangePreconditionFulfilled(ifRangeHeader, filePath)) { - // FULL FILE (if-range not fulfilled) - return createFile(locator, request.getDavSession(), filePath); - } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && !isRangeSatisfiable(rangeHeader)) { - // FULL FILE (unsatisfiable range) - response.setStatus(DavServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); - final EncryptedFile file = createFile(locator, request.getDavSession(), filePath); - response.addHeader(HttpHeader.CONTENT_RANGE.asString(), "bytes */" + file.getContentLength()); - return file; - } else if (Files.exists(filePath) || DavMethods.METHOD_PUT.equals(request.getMethod())) { - // FULL FILE (as requested) - return createFile(locator, request.getDavSession(), filePath); - } else { - // NO FILE OR FOLDER (e.g. for MOVE operations): - return createNonExisting(locator, request.getDavSession(), filePath, dirFilePath); + try { + final Path filePath = getEncryptedFilePath(locator.getResourcePath(), false); + final Path dirFilePath = getEncryptedDirectoryFilePath(locator.getResourcePath(), false); + final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString()); + final String ifRangeHeader = request.getHeader(HttpHeader.IF_RANGE.asString()); + if (Files.exists(dirFilePath) || DavMethods.METHOD_MKCOL.equals(request.getMethod())) { + // DIRECTORY + return createDirectory(locator, request.getDavSession(), dirFilePath); + } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && isRangeSatisfiable(rangeHeader) && isIfRangePreconditionFulfilled(ifRangeHeader, filePath)) { + // FILE RANGE + final Pair requestRange = getRequestRange(rangeHeader); + response.setStatus(DavServletResponse.SC_PARTIAL_CONTENT); + return createFilePart(locator, request.getDavSession(), requestRange, filePath); + } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && isRangeSatisfiable(rangeHeader) && !isIfRangePreconditionFulfilled(ifRangeHeader, filePath)) { + // FULL FILE (if-range not fulfilled) + return createFile(locator, request.getDavSession(), filePath); + } else if (Files.exists(filePath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null && !isRangeSatisfiable(rangeHeader)) { + // FULL FILE (unsatisfiable range) + response.setStatus(DavServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); + final EncryptedFile file = createFile(locator, request.getDavSession(), filePath); + response.addHeader(HttpHeader.CONTENT_RANGE.asString(), "bytes */" + file.getContentLength()); + return file; + } else if (Files.exists(filePath) || DavMethods.METHOD_PUT.equals(request.getMethod())) { + // FULL FILE (as requested) + return createFile(locator, request.getDavSession(), filePath); + } + } catch (NonExistingParentException e) { + // return non-existing } + return createNonExisting(locator, request.getDavSession()); } @Override @@ -88,16 +91,18 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants return createRootDirectory(locator, session); } - final Path filePath = getEncryptedFilePath(locator.getResourcePath()); - final Path dirFilePath = getEncryptedDirectoryFilePath(locator.getResourcePath()); - if (Files.exists(dirFilePath)) { - return createDirectory(locator, session, dirFilePath); - } else if (Files.exists(filePath)) { - return createFile(locator, session, filePath); - } else { - // e.g. for MOVE operations: - return createNonExisting(locator, session, filePath, dirFilePath); + try { + final Path filePath = getEncryptedFilePath(locator.getResourcePath(), false); + final Path dirFilePath = getEncryptedDirectoryFilePath(locator.getResourcePath(), false); + if (Files.exists(dirFilePath)) { + return createDirectory(locator, session, dirFilePath); + } else if (Files.exists(filePath)) { + return createFile(locator, session, filePath); + } + } catch (NonExistingParentException e) { + // return non-existing } + return createNonExisting(locator, session); } DavResource createChildDirectoryResource(DavResourceLocator locator, DavSession session, Path existingDirectoryFile) throws DavException { @@ -176,41 +181,42 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants /** * @return Absolute file path for a given cleartext file resourcePath. - * @throws IOException + * @throws NonExistingParentException If one ancestor of the enrypted path is missing */ - private Path getEncryptedFilePath(String relativeCleartextPath) throws DavException { + Path getEncryptedFilePath(String relativeCleartextPath, boolean createNonExisting) throws NonExistingParentException { final String parentCleartextPath = FilenameUtils.getPathNoEndSeparator(relativeCleartextPath); - final Path parent = createEncryptedDirectoryPath(parentCleartextPath); + final Path parent = getEncryptedDirectoryPath(parentCleartextPath, createNonExisting); final String cleartextFilename = FilenameUtils.getName(relativeCleartextPath); try { final String encryptedFilename = filenameTranslator.getEncryptedFilename(cleartextFilename); return parent.resolve(encryptedFilename); } catch (IOException e) { - throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e); + throw new IORuntimeException(e); } } /** * @return Absolute file path for a given cleartext file resourcePath. - * @throws IOException + * @throws NonExistingParentException If one ancestor of the enrypted path is missing */ - private Path getEncryptedDirectoryFilePath(String relativeCleartextPath) throws DavException { + Path getEncryptedDirectoryFilePath(String relativeCleartextPath, boolean createNonExisting) throws NonExistingParentException { final String parentCleartextPath = FilenameUtils.getPathNoEndSeparator(relativeCleartextPath); - final Path parent = createEncryptedDirectoryPath(parentCleartextPath); + final Path parent = getEncryptedDirectoryPath(parentCleartextPath, createNonExisting); final String cleartextFilename = FilenameUtils.getName(relativeCleartextPath); try { final String encryptedFilename = filenameTranslator.getEncryptedDirFileName(cleartextFilename); return parent.resolve(encryptedFilename); } catch (IOException e) { - throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e); + throw new IORuntimeException(e); } } - + /** + * @param createNonExisting if false, a {@link NonExistingParentException} will be thrown for missing ancestors. * @return Absolute directory path for a given cleartext directory resourcePath. - * @throws IOException + * @throws NonExistingParentException if one ancestor directory is missing. */ - private Path createEncryptedDirectoryPath(String relativeCleartextPath) throws DavException { + private Path getEncryptedDirectoryPath(String relativeCleartextPath, boolean createNonExisting) throws NonExistingParentException { assert Strings.isEmpty(relativeCleartextPath) || !relativeCleartextPath.endsWith("/"); try { final Path result; @@ -220,10 +226,13 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants result = dataRoot.resolve(fixedRootDirectory); } else { final String parentCleartextPath = FilenameUtils.getPathNoEndSeparator(relativeCleartextPath); - final Path parent = createEncryptedDirectoryPath(parentCleartextPath); + final Path parent = getEncryptedDirectoryPath(parentCleartextPath, createNonExisting); final String cleartextFilename = FilenameUtils.getName(relativeCleartextPath); final String encryptedFilename = filenameTranslator.getEncryptedDirFileName(cleartextFilename); final Path directoryFile = parent.resolve(encryptedFilename); + if (!createNonExisting && !Files.exists(directoryFile)) { + throw new NonExistingParentException(); + } final String directoryId = filenameTranslator.getDirectoryId(directoryFile, true); final String directory = cryptor.encryptDirectoryPath(directoryId, FileSystems.getDefault().getSeparator()); result = dataRoot.resolve(directory); @@ -231,7 +240,7 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants Files.createDirectories(result); return result; } catch (IOException e) { - throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e); + throw new IORuntimeException(e); } } @@ -263,8 +272,14 @@ public class CryptoResourceFactory implements DavResourceFactory, FileConstants return new EncryptedDir(this, locator, session, lockManager, cryptor, filenameTranslator, filePath); } - private NonExistingNode createNonExisting(DavResourceLocator locator, DavSession session, Path filePath, Path dirFilePath) { - return new NonExistingNode(this, locator, session, lockManager, cryptor, filePath, dirFilePath); + private NonExistingNode createNonExisting(DavResourceLocator locator, DavSession session) { + return new NonExistingNode(this, locator, session, lockManager, cryptor); + } + + static class NonExistingParentException extends Exception { + + private static final long serialVersionUID = 4421121746624627094L; + } } diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedDir.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedDir.java index b18129fb5..ce1364611 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedDir.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedDir.java @@ -260,7 +260,7 @@ class EncryptedDir extends AbstractEncryptedNode implements FileConstants { final Path srcPath = filePath; final Path dstPath; if (dest instanceof NonExistingNode) { - dstPath = ((NonExistingNode) dest).getDirFilePath(); + dstPath = ((NonExistingNode) dest).materializeDirFilePath(); } else { dstPath = dest.filePath; } @@ -278,7 +278,7 @@ class EncryptedDir extends AbstractEncryptedNode implements FileConstants { public void copy(AbstractEncryptedNode dest, boolean shallow) throws DavException, IOException { final Path dstDirFilePath; if (dest instanceof NonExistingNode) { - dstDirFilePath = ((NonExistingNode) dest).getDirFilePath(); + dstDirFilePath = ((NonExistingNode) dest).materializeDirFilePath(); } else { dstDirFilePath = dest.filePath; } diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java index dd52a84c2..63c2f9429 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/EncryptedFile.java @@ -120,7 +120,7 @@ class EncryptedFile extends AbstractEncryptedNode implements FileConstants { final Path srcPath = filePath; final Path dstPath; if (dest instanceof NonExistingNode) { - dstPath = ((NonExistingNode) dest).getFilePath(); + dstPath = ((NonExistingNode) dest).materializeFilePath(); } else { dstPath = dest.filePath; } @@ -137,7 +137,7 @@ class EncryptedFile extends AbstractEncryptedNode implements FileConstants { final Path srcPath = filePath; final Path dstPath; if (dest instanceof NonExistingNode) { - dstPath = ((NonExistingNode) dest).getFilePath(); + dstPath = ((NonExistingNode) dest).materializeFilePath(); } else { dstPath = dest.filePath; } diff --git a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/NonExistingNode.java b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/NonExistingNode.java index 8d194a1cb..f34eb69b5 100644 --- a/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/NonExistingNode.java +++ b/main/core/src/main/java/org/cryptomator/webdav/jackrabbit/NonExistingNode.java @@ -21,16 +21,12 @@ import org.apache.jackrabbit.webdav.io.OutputContext; import org.apache.jackrabbit.webdav.lock.LockManager; import org.apache.jackrabbit.webdav.property.DavProperty; import org.cryptomator.crypto.Cryptor; +import org.cryptomator.webdav.jackrabbit.CryptoResourceFactory.NonExistingParentException; class NonExistingNode extends AbstractEncryptedNode { - private final Path filePath; - private final Path dirFilePath; - - public NonExistingNode(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, Path filePath, Path dirFilePath) { + public NonExistingNode(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) { super(factory, locator, session, lockManager, cryptor, null); - this.filePath = filePath; - this.dirFilePath = dirFilePath; } @Override @@ -83,12 +79,26 @@ class NonExistingNode extends AbstractEncryptedNode { throw new UnsupportedOperationException("Resource doesn't exist."); } - public Path getFilePath() { - return filePath; + /** + * @return lazily resolved file path, e.g. needed during MOVE operations. + */ + public Path materializeFilePath() { + try { + return factory.getEncryptedFilePath(locator.getResourcePath(), true); + } catch (NonExistingParentException e) { + throw new IllegalStateException(e); + } } - public Path getDirFilePath() { - return dirFilePath; + /** + * @return lazily resolved directory file path, e.g. needed during MOVE operations. + */ + public Path materializeDirFilePath() { + try { + return factory.getEncryptedDirectoryFilePath(locator.getResourcePath(), true); + } catch (NonExistingParentException e) { + throw new IllegalStateException(e); + } } }