mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-17 10:11:27 +00:00
several WebDAV compliance fixes
This commit is contained in:
@@ -11,12 +11,12 @@ package org.cryptomator.filesystem.inmem;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.FileAlreadyExistsException;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
|
||||
import org.apache.commons.io.FileExistsException;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
@@ -53,12 +53,15 @@ class InMemoryFile extends InMemoryNode implements File {
|
||||
writeLock.lock();
|
||||
final InMemoryFolder parent = parent().get();
|
||||
parent.existingChildren.compute(this.name(), (k, v) -> {
|
||||
if (v == null || v == this) {
|
||||
this.lastModified = Instant.now();
|
||||
this.creationTime = Instant.now();
|
||||
return this;
|
||||
if (v != null && v != this) {
|
||||
// other file or folder with same name already exists.
|
||||
throw new UncheckedIOException(new FileAlreadyExistsException(k));
|
||||
} else {
|
||||
throw new UncheckedIOException(new FileExistsException(k));
|
||||
if (v == null) {
|
||||
this.creationTime = Instant.now();
|
||||
}
|
||||
this.lastModified = Instant.now();
|
||||
return this;
|
||||
}
|
||||
});
|
||||
return new InMemoryWritableFile(this::setLastModified, this::setCreationTime, this::getContent, this::setContent, this::delete, writeLock);
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.io.UncheckedIOException;
|
||||
import java.time.Instant;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.io.FileExistsException;
|
||||
@@ -24,7 +24,7 @@ import org.cryptomator.filesystem.Folder;
|
||||
|
||||
class InMemoryFolder extends InMemoryNode implements Folder {
|
||||
|
||||
final Map<String, InMemoryNode> existingChildren = new TreeMap<>();
|
||||
final Map<String, InMemoryNode> existingChildren = new ConcurrentHashMap<>();
|
||||
|
||||
private final WeakValuedCache<String, InMemoryFolder> folders = WeakValuedCache.usingLoader(this::newFolder);
|
||||
private final WeakValuedCache<String, InMemoryFile> files = WeakValuedCache.usingLoader(this::newFile);
|
||||
@@ -67,11 +67,12 @@ class InMemoryFolder extends InMemoryNode implements Folder {
|
||||
}
|
||||
parent.create();
|
||||
parent.existingChildren.compute(name, (k, v) -> {
|
||||
if (v == null) {
|
||||
if (v != null) {
|
||||
// other file or folder with same name already exists.
|
||||
throw new UncheckedIOException(new FileExistsException(k));
|
||||
} else {
|
||||
this.lastModified = Instant.now();
|
||||
return this;
|
||||
} else {
|
||||
throw new UncheckedIOException(new FileExistsException(k));
|
||||
}
|
||||
});
|
||||
assert this.exists();
|
||||
@@ -83,11 +84,11 @@ class InMemoryFolder extends InMemoryNode implements Folder {
|
||||
if (target.exists()) {
|
||||
target.delete();
|
||||
}
|
||||
assert !target.exists();
|
||||
assert!target.exists();
|
||||
target.create();
|
||||
this.copyTo(target);
|
||||
this.delete();
|
||||
assert !this.exists();
|
||||
assert!this.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -109,7 +110,12 @@ class InMemoryFolder extends InMemoryNode implements Folder {
|
||||
subFolder.delete();
|
||||
}
|
||||
}
|
||||
assert !this.exists();
|
||||
assert!this.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCreationTime(Instant instant) throws UncheckedIOException {
|
||||
creationTime = instant;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ import org.apache.commons.lang3.SystemUtils;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.webdav.filters.AcceptRangeFilter;
|
||||
import org.cryptomator.webdav.filters.MacChunkedPutCompatibilityFilter;
|
||||
import org.cryptomator.webdav.filters.MkcolComplianceFilter;
|
||||
import org.cryptomator.webdav.filters.UriNormalizationFilter;
|
||||
import org.cryptomator.webdav.filters.UriNormalizationFilter.ResourceTypeChecker;
|
||||
import org.cryptomator.webdav.filters.UriNormalizationFilter.ResourceTypeChecker.ResourceType;
|
||||
@@ -64,6 +65,7 @@ class WebDavServletContextFactory {
|
||||
final ServletContextHandler servletContext = new ServletContextHandler(null, contextPath, ServletContextHandler.SESSIONS);
|
||||
final ServletHolder servletHolder = new ServletHolder(contextPath, new WebDavServlet(contextRoot, root));
|
||||
servletContext.addServlet(servletHolder, WILDCARD);
|
||||
servletContext.addFilter(MkcolComplianceFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(AcceptRangeFilter.class, WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
servletContext.addFilter(new FilterHolder(new UriNormalizationFilter(resourceTypeChecker)), WILDCARD, EnumSet.of(DispatcherType.REQUEST));
|
||||
if (SystemUtils.IS_OS_MAC_OSX) {
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Sebastian Stenzel and others.
|
||||
* This file is licensed under the terms of the MIT license.
|
||||
* See the LICENSE.txt file for more info.
|
||||
*
|
||||
* Contributors:
|
||||
* Sebastian Stenzel - initial API and implementation
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* Responds with status code 415, if an attempt is made to create a collection with a body.
|
||||
*
|
||||
* See https://tools.ietf.org/html/rfc2518#section-8.3.1:
|
||||
* "If the server receives a MKCOL request entity type it does not support or understand
|
||||
* it MUST respond with a 415 (Unsupported Media Type) status code."
|
||||
*/
|
||||
public class MkcolComplianceFilter implements HttpFilter {
|
||||
|
||||
private static final String METHOD_MKCOL = "MKCOL";
|
||||
private static final String HEADER_TRANSFER_ENCODING = "Transfer-Encoding";
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
boolean hasBody = request.getContentLengthLong() > 0 || request.getHeader(HEADER_TRANSFER_ENCODING) != null;
|
||||
if (METHOD_MKCOL.equalsIgnoreCase(request.getMethod()) && hasBody) {
|
||||
response.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE, "MKCOL with body not supported.");
|
||||
} else {
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@
|
||||
package org.cryptomator.webdav.filters;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.function.Function;
|
||||
import java.net.URI;
|
||||
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
@@ -37,68 +37,11 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
private static final String[] FILE_METHODS = {"PUT"};
|
||||
private static final String[] DIRECTORY_METHODS = {"MKCOL"};
|
||||
|
||||
private final ResourceTypeChecker resourceTypeChecker;
|
||||
|
||||
public UriNormalizationFilter(ResourceTypeChecker resourceTypeChecker) {
|
||||
this.resourceTypeChecker = resourceTypeChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
// no-op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
ResourceType resourceType = resourceTypeChecker.typeOfResource(request.getPathInfo());
|
||||
HttpServletRequest normalizedRequest = resourceType.normalizedRequest(request);
|
||||
chain.doFilter(normalizedRequest, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
private static HttpServletRequest normalizedFileRequest(HttpServletRequest originalRequest) {
|
||||
LOG.debug("Treating resource as file: {}", originalRequest.getRequestURI());
|
||||
return new FileUriRequest(originalRequest);
|
||||
}
|
||||
|
||||
private static HttpServletRequest normalizedFolderRequest(HttpServletRequest originalRequest) {
|
||||
LOG.debug("Treating resource as folder: {}", originalRequest.getRequestURI());
|
||||
return new FolderUriRequest(originalRequest);
|
||||
}
|
||||
|
||||
private static HttpServletRequest normalizedRequestForUnknownResource(HttpServletRequest originalRequest) {
|
||||
final String requestMethod = originalRequest.getMethod().toUpperCase();
|
||||
if (ArrayUtils.contains(FILE_METHODS, requestMethod)) {
|
||||
return normalizedFileRequest(originalRequest);
|
||||
} else if (ArrayUtils.contains(DIRECTORY_METHODS, requestMethod)) {
|
||||
return normalizedFolderRequest(originalRequest);
|
||||
} else {
|
||||
LOG.debug("Could not determine resource type of resource: {}", originalRequest.getRequestURI());
|
||||
return originalRequest;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ResourceTypeChecker {
|
||||
|
||||
enum ResourceType {
|
||||
FILE(UriNormalizationFilter::normalizedFileRequest), //
|
||||
FOLDER(UriNormalizationFilter::normalizedFolderRequest), //
|
||||
UNKNOWN(UriNormalizationFilter::normalizedRequestForUnknownResource);
|
||||
|
||||
private final Function<HttpServletRequest, HttpServletRequest> wrapper;
|
||||
|
||||
private ResourceType(Function<HttpServletRequest, HttpServletRequest> wrapper) {
|
||||
this.wrapper = wrapper;
|
||||
}
|
||||
|
||||
private HttpServletRequest normalizedRequest(HttpServletRequest request) {
|
||||
return wrapper.apply(request);
|
||||
}
|
||||
FILE, FOLDER, UNKNOWN;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -111,10 +54,67 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
|
||||
}
|
||||
|
||||
private final ResourceTypeChecker resourceTypeChecker;
|
||||
private String contextPath;
|
||||
|
||||
public UriNormalizationFilter(ResourceTypeChecker resourceTypeChecker) {
|
||||
this.resourceTypeChecker = resourceTypeChecker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
contextPath = filterConfig.getServletContext().getContextPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilterHttp(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
final ResourceType resourceType = resourceTypeChecker.typeOfResource(request.getPathInfo());
|
||||
final HttpServletRequest normalizedRequest;
|
||||
switch (resourceType) {
|
||||
case FILE:
|
||||
normalizedRequest = normalizedFileRequest(request);
|
||||
break;
|
||||
case FOLDER:
|
||||
normalizedRequest = normalizedFolderRequest(request);
|
||||
break;
|
||||
default:
|
||||
normalizedRequest = normalizedRequestForUnknownResource(request);
|
||||
break;
|
||||
}
|
||||
chain.doFilter(normalizedRequest, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// no-op
|
||||
}
|
||||
|
||||
private HttpServletRequest normalizedFileRequest(HttpServletRequest originalRequest) {
|
||||
LOG.trace("Treating resource as file: {}", originalRequest.getRequestURI());
|
||||
return new FileUriRequest(originalRequest);
|
||||
}
|
||||
|
||||
private HttpServletRequest normalizedFolderRequest(HttpServletRequest originalRequest) {
|
||||
LOG.trace("Treating resource as folder: {}", originalRequest.getRequestURI());
|
||||
return new FolderUriRequest(originalRequest);
|
||||
}
|
||||
|
||||
private HttpServletRequest normalizedRequestForUnknownResource(HttpServletRequest originalRequest) {
|
||||
final String requestMethod = originalRequest.getMethod().toUpperCase();
|
||||
if (ArrayUtils.contains(FILE_METHODS, requestMethod)) {
|
||||
return normalizedFileRequest(originalRequest);
|
||||
} else if (ArrayUtils.contains(DIRECTORY_METHODS, requestMethod)) {
|
||||
return normalizedFolderRequest(originalRequest);
|
||||
} else {
|
||||
LOG.debug("Could not determine resource type of resource: {}", originalRequest.getRequestURI());
|
||||
return originalRequest;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts headers containing URIs depending on the request URI.
|
||||
*/
|
||||
private static class SuffixPreservingRequest extends HttpServletRequestWrapper {
|
||||
private class SuffixPreservingRequest extends HttpServletRequestWrapper {
|
||||
|
||||
private static final String HEADER_DESTINATION = "Destination";
|
||||
private static final String METHOD_MOVE = "MOVE";
|
||||
@@ -122,32 +122,53 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
|
||||
public SuffixPreservingRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
request.getContextPath();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
if ((METHOD_MOVE.equalsIgnoreCase(getMethod()) || METHOD_COPY.equalsIgnoreCase(getMethod())) && HEADER_DESTINATION.equalsIgnoreCase(name)) {
|
||||
return sameSuffixAsUri(super.getHeader(name));
|
||||
final String uri = URI.create(super.getHeader(name)).getPath();
|
||||
return bestGuess(uri);
|
||||
} else {
|
||||
return super.getHeader(name);
|
||||
}
|
||||
}
|
||||
|
||||
private String sameSuffixAsUri(String str) {
|
||||
final String uri = this.getRequestURI();
|
||||
if (uri.endsWith("/")) {
|
||||
return StringUtils.appendIfMissing(str, "/");
|
||||
} else {
|
||||
return StringUtils.removeEnd(str, "/");
|
||||
private String bestGuess(String uri) {
|
||||
final String pathWithinContext = StringUtils.removeStart(uri, contextPath);
|
||||
final ResourceType resourceType = resourceTypeChecker.typeOfResource(pathWithinContext);
|
||||
switch (resourceType) {
|
||||
case FILE:
|
||||
System.out.println("DST is file " + uri);
|
||||
return asFileUri(uri);
|
||||
case FOLDER:
|
||||
System.out.println("DST is folder " + uri);
|
||||
return asFolderUri(uri);
|
||||
default:
|
||||
System.out.println("DST doesn't exist " + uri);
|
||||
if (this.getRequestURI().endsWith("/")) {
|
||||
return asFolderUri(uri);
|
||||
} else {
|
||||
return asFileUri(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected String asFileUri(String uri) {
|
||||
return StringUtils.removeEnd(uri, "/");
|
||||
}
|
||||
|
||||
protected String asFolderUri(String uri) {
|
||||
return StringUtils.appendIfMissing(uri, "/");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP request, whose URI never ends on "/".
|
||||
*/
|
||||
private static class FileUriRequest extends SuffixPreservingRequest {
|
||||
private class FileUriRequest extends SuffixPreservingRequest {
|
||||
|
||||
public FileUriRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
@@ -155,7 +176,7 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return StringUtils.removeEnd(super.getRequestURI(), "/");
|
||||
return asFileUri(super.getRequestURI());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -163,7 +184,7 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
/**
|
||||
* HTTP request, whose URI always ends on "/".
|
||||
*/
|
||||
private static class FolderUriRequest extends SuffixPreservingRequest {
|
||||
private class FolderUriRequest extends SuffixPreservingRequest {
|
||||
|
||||
public FolderUriRequest(HttpServletRequest request) {
|
||||
super(request);
|
||||
@@ -171,7 +192,7 @@ public class UriNormalizationFilter implements HttpFilter {
|
||||
|
||||
@Override
|
||||
public String getRequestURI() {
|
||||
return StringUtils.appendIfMissing(super.getRequestURI(), "/");
|
||||
return asFolderUri(super.getRequestURI());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import java.util.Optional;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
import org.apache.jackrabbit.webdav.DavResourceIterator;
|
||||
import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
import org.apache.jackrabbit.webdav.DavSession;
|
||||
import org.apache.jackrabbit.webdav.io.InputContext;
|
||||
import org.apache.jackrabbit.webdav.io.OutputContext;
|
||||
@@ -25,6 +26,8 @@ import org.apache.jackrabbit.webdav.property.DavProperty;
|
||||
import org.apache.jackrabbit.webdav.property.DavPropertyName;
|
||||
import org.apache.jackrabbit.webdav.property.DavPropertySet;
|
||||
import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.Folder;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.jackrabbit.FileLocator;
|
||||
@@ -73,7 +76,23 @@ class DavFile extends DavNode<FileLocator> {
|
||||
public void move(DavResource destination) throws DavException {
|
||||
if (destination instanceof DavFile) {
|
||||
DavFile dst = (DavFile) destination;
|
||||
if (dst.node.exists()) {
|
||||
// Overwrite header already checked by AbstractWebdavServlet#validateDestination
|
||||
dst.node.delete();
|
||||
} else if (!dst.node.parent().get().exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.moveTo(dst.node);
|
||||
} else if (destination instanceof DavFolder) {
|
||||
DavFolder dst = (DavFolder) destination;
|
||||
Folder parent = dst.node.parent().get();
|
||||
File newDst = parent.file(dst.node.name());
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!parent.exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.moveTo(newDst);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
|
||||
}
|
||||
@@ -83,9 +102,25 @@ class DavFile extends DavNode<FileLocator> {
|
||||
public void copy(DavResource destination, boolean shallow) throws DavException {
|
||||
if (destination instanceof DavFile) {
|
||||
DavFile dst = (DavFile) destination;
|
||||
if (dst.node.exists()) {
|
||||
// Overwrite header already checked by AbstractWebdavServlet#validateDestination
|
||||
dst.node.delete();
|
||||
} else if (!dst.node.parent().get().exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.copyTo(dst.node);
|
||||
} else if (destination instanceof DavFolder) {
|
||||
DavFolder dst = (DavFolder) destination;
|
||||
Folder parent = dst.node.parent().get();
|
||||
File newDst = parent.file(dst.node.name());
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!parent.exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.copyTo(newDst);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
|
||||
throw new IllegalArgumentException("Destination not a DavFile: " + destination.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -124,7 +124,22 @@ class DavFolder extends DavNode<FolderLocator> {
|
||||
public void move(DavResource destination) throws DavException {
|
||||
if (destination instanceof DavFolder) {
|
||||
DavFolder dst = (DavFolder) destination;
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!dst.node.parent().get().exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.moveTo(dst.node);
|
||||
} else if (destination instanceof DavFile) {
|
||||
DavFile dst = (DavFile) destination;
|
||||
Folder parent = dst.node.parent().get();
|
||||
Folder newDst = parent.folder(dst.node.name());
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!parent.exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.moveTo(newDst);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
|
||||
}
|
||||
@@ -132,11 +147,31 @@ class DavFolder extends DavNode<FolderLocator> {
|
||||
|
||||
@Override
|
||||
public void copy(DavResource destination, boolean shallow) throws DavException {
|
||||
if (shallow) {
|
||||
throw new UnsupportedOperationException("Shallow copy of directories not supported.");
|
||||
} else if (destination instanceof DavFolder) {
|
||||
if (destination instanceof DavFolder) {
|
||||
DavFolder dst = (DavFolder) destination;
|
||||
node.copyTo(dst.node);
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!dst.node.parent().get().exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
dst.node.create();
|
||||
if (shallow) {
|
||||
// http://www.webdav.org/specs/rfc2518.html#copy.for.collections
|
||||
node.creationTime().ifPresent(dst::setCreationTime);
|
||||
dst.setModificationTime(node.lastModified());
|
||||
} else {
|
||||
node.copyTo(dst.node);
|
||||
}
|
||||
} else if (destination instanceof DavFile) {
|
||||
DavFile dst = (DavFile) destination;
|
||||
Folder parent = dst.node.parent().get();
|
||||
Folder newDst = parent.folder(dst.node.name());
|
||||
if (dst.node.exists()) {
|
||||
dst.node.delete();
|
||||
} else if (!parent.exists()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Destination's parent doesn't exist.");
|
||||
}
|
||||
node.copyTo(newDst);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Destination not a DavFolder: " + destination.getClass().getName());
|
||||
}
|
||||
|
||||
@@ -126,6 +126,36 @@ public class UriNormalizationFilterTest {
|
||||
Assert.assertEquals("/404/", wrappedReq.getValue().getHeader("Destination"));
|
||||
}
|
||||
|
||||
/* MIXED */
|
||||
|
||||
@Test
|
||||
public void testCopyFileToFolderRequest() throws IOException, ServletException {
|
||||
Mockito.when(request.getPathInfo()).thenReturn("/file/");
|
||||
Mockito.when(request.getRequestURI()).thenReturn("/file/");
|
||||
Mockito.when(request.getMethod()).thenReturn("COPY");
|
||||
Mockito.when(request.getHeader("Destination")).thenReturn("/folder");
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
ArgumentCaptor<HttpServletRequest> wrappedReq = ArgumentCaptor.forClass(HttpServletRequest.class);
|
||||
Mockito.verify(chain).doFilter(wrappedReq.capture(), Mockito.any(ServletResponse.class));
|
||||
Assert.assertEquals("/file", wrappedReq.getValue().getRequestURI());
|
||||
Assert.assertEquals("/folder/", wrappedReq.getValue().getHeader("Destination"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMoveFolderToFileRequest() throws IOException, ServletException {
|
||||
Mockito.when(request.getPathInfo()).thenReturn("/folder");
|
||||
Mockito.when(request.getRequestURI()).thenReturn("/folder");
|
||||
Mockito.when(request.getMethod()).thenReturn("COPY");
|
||||
Mockito.when(request.getHeader("Destination")).thenReturn("/file/");
|
||||
filter.doFilter(request, response, chain);
|
||||
|
||||
ArgumentCaptor<HttpServletRequest> wrappedReq = ArgumentCaptor.forClass(HttpServletRequest.class);
|
||||
Mockito.verify(chain).doFilter(wrappedReq.capture(), Mockito.any(ServletResponse.class));
|
||||
Assert.assertEquals("/folder/", wrappedReq.getValue().getRequestURI());
|
||||
Assert.assertEquals("/file", wrappedReq.getValue().getHeader("Destination"));
|
||||
}
|
||||
|
||||
/* UNKNOWN */
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user