mirror of
https://github.com/cryptomator/cryptomator.git
synced 2024-11-23 03:59:51 +00:00
some more destruction...
This commit is contained in:
parent
4cf872f916
commit
be369b480b
@ -44,20 +44,19 @@ abstract class AbstractEncryptedNode implements DavResource {
|
||||
private static final String DAV_COMPLIANCE_CLASSES = "1, 2";
|
||||
|
||||
protected final CryptoResourceFactory factory;
|
||||
protected final CryptoLocator locator;
|
||||
protected final DavResourceLocator locator;
|
||||
protected final DavSession session;
|
||||
protected final LockManager lockManager;
|
||||
protected final Cryptor cryptor;
|
||||
protected final DavPropertySet properties;
|
||||
|
||||
protected AbstractEncryptedNode(CryptoResourceFactory factory, CryptoLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
protected AbstractEncryptedNode(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
this.factory = factory;
|
||||
this.locator = locator;
|
||||
this.session = session;
|
||||
this.lockManager = lockManager;
|
||||
this.cryptor = cryptor;
|
||||
this.properties = new DavPropertySet();
|
||||
this.determineProperties();
|
||||
}
|
||||
|
||||
protected abstract Path getPhysicalPath();
|
||||
@ -89,7 +88,7 @@ abstract class AbstractEncryptedNode implements DavResource {
|
||||
}
|
||||
|
||||
@Override
|
||||
public CryptoLocator getLocator() {
|
||||
public DavResourceLocator getLocator() {
|
||||
return locator;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,112 @@
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.jackrabbit.webdav.DavLocatorFactory;
|
||||
import org.apache.jackrabbit.webdav.DavResourceLocator;
|
||||
import org.apache.jackrabbit.webdav.util.EncodeUtil;
|
||||
import org.apache.logging.log4j.util.Strings;
|
||||
|
||||
public class CleartextLocatorFactory implements DavLocatorFactory {
|
||||
|
||||
private final String pathPrefix;
|
||||
|
||||
public CleartextLocatorFactory(String pathPrefix) {
|
||||
this.pathPrefix = pathPrefix;
|
||||
}
|
||||
|
||||
// resourcePath == repositoryPath. No encryption here.
|
||||
|
||||
@Override
|
||||
public DavResourceLocator createResourceLocator(String prefix, String href) {
|
||||
final String fullPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
|
||||
final String relativeHref = StringUtils.removeStart(href, fullPrefix);
|
||||
|
||||
final String relativeCleartextPath = EncodeUtil.unescape(StringUtils.removeStart(relativeHref, "/"));
|
||||
return new CleartextLocator(relativeCleartextPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath) {
|
||||
return new CleartextLocator(resourcePath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String path, boolean isResourcePath) {
|
||||
return new CleartextLocator(path);
|
||||
}
|
||||
|
||||
private class CleartextLocator implements DavResourceLocator {
|
||||
|
||||
private final String relativeCleartextPath;
|
||||
|
||||
private CleartextLocator(String relativeCleartextPath) {
|
||||
this.relativeCleartextPath = FilenameUtils.normalizeNoEndSeparator(relativeCleartextPath, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix() {
|
||||
return pathPrefix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getResourcePath() {
|
||||
return relativeCleartextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkspacePath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWorkspaceName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameWorkspace(DavResourceLocator locator) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSameWorkspace(String workspaceName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHref(boolean isCollection) {
|
||||
final String encodedResourcePath = EncodeUtil.escapePath(getResourcePath());
|
||||
final String fullPrefix = pathPrefix.endsWith("/") ? pathPrefix : pathPrefix + "/";
|
||||
final String href = fullPrefix.concat(encodedResourcePath);
|
||||
assert !href.endsWith("/");
|
||||
if (isCollection) {
|
||||
return href.concat("/");
|
||||
} else {
|
||||
return href;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRootLocation() {
|
||||
return Strings.isEmpty(relativeCleartextPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DavLocatorFactory getFactory() {
|
||||
return CleartextLocatorFactory.this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRepositoryPath() {
|
||||
return relativeCleartextPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Locator: " + relativeCleartextPath + " (Prefix: " + pathPrefix + ")";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,13 +1,19 @@
|
||||
package org.cryptomator.webdav.jackrabbit;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.apache.commons.httpclient.HttpStatus;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavMethods;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
@ -18,99 +24,168 @@ import org.apache.jackrabbit.webdav.DavServletResponse;
|
||||
import org.apache.jackrabbit.webdav.DavSession;
|
||||
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.cryptomator.crypto.CryptorMetadataSupport;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
|
||||
public class CryptoResourceFactory implements DavResourceFactory {
|
||||
public class CryptoResourceFactory implements DavResourceFactory, CryptorMetadataSupport {
|
||||
|
||||
private final LockManager lockManager = new SimpleLockManager();
|
||||
private final Cryptor cryptor;
|
||||
private final CryptoWarningHandler cryptoWarningHandler;
|
||||
private final ExecutorService backgroundTaskExecutor;
|
||||
private final Path dataRoot;
|
||||
private final Path metadataRoot;
|
||||
|
||||
CryptoResourceFactory(Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler, ExecutorService backgroundTaskExecutor) {
|
||||
CryptoResourceFactory(Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler, ExecutorService backgroundTaskExecutor, String fsRoot) {
|
||||
this.cryptor = cryptor;
|
||||
this.cryptoWarningHandler = cryptoWarningHandler;
|
||||
this.backgroundTaskExecutor = backgroundTaskExecutor;
|
||||
this.dataRoot = FileSystems.getDefault().getPath(fsRoot).resolve("d");
|
||||
this.metadataRoot = FileSystems.getDefault().getPath(fsRoot).resolve("m");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DavResource createResource(DavResourceLocator locator, DavServletRequest request, DavServletResponse response) throws DavException {
|
||||
if (locator instanceof CryptoLocator) {
|
||||
return createResource((CryptoLocator) locator, request, response);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource locator of type " + locator.getClass().getName());
|
||||
if (DavMethods.METHOD_MKCOL.equals(request.getMethod()) || locator.isRootLocation()) {
|
||||
final Path dirpath = getEncryptedDirectoryPath(locator.getResourcePath());
|
||||
return createDirectory(locator, request.getDavSession(), dirpath);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final DavResource createResource(DavResourceLocator locator, DavSession session) throws DavException {
|
||||
if (locator instanceof CryptoLocator) {
|
||||
return createResource((CryptoLocator) locator, session);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported resource locator of type " + locator.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
private DavResource createResource(CryptoLocator locator, DavServletRequest request, DavServletResponse response) throws DavException {
|
||||
final Path filepath = FileSystems.getDefault().getPath(locator.getRepositoryPath());
|
||||
Path dirpath = null;
|
||||
try {
|
||||
dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath(DavMethods.METHOD_MKCOL.equals(request.getMethod())));
|
||||
} catch (FileNotFoundException e) {
|
||||
// no-op
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
final Path filepath = getEncryptedFilePath(locator.getResourcePath());
|
||||
final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
|
||||
|
||||
if (Files.isDirectory(dirpath)) {
|
||||
return createDirectory(locator, request.getDavSession());
|
||||
if (filepath.getFileName().toString().endsWith(".dir")) {
|
||||
final Path dirpath = getEncryptedDirectoryPath(locator.getResourcePath());
|
||||
return createDirectory(locator, request.getDavSession(), dirpath);
|
||||
} else if (Files.isRegularFile(filepath) && DavMethods.METHOD_GET.equals(request.getMethod()) && rangeHeader != null) {
|
||||
response.setStatus(HttpStatus.SC_PARTIAL_CONTENT);
|
||||
return createFilePart(locator, request.getDavSession(), request);
|
||||
return createFilePart(locator, request.getDavSession(), request, filepath);
|
||||
} else if (Files.isRegularFile(filepath) || DavMethods.METHOD_PUT.equals(request.getMethod())) {
|
||||
return createFile(locator, request.getDavSession());
|
||||
return createFile(locator, request.getDavSession(), filepath);
|
||||
} else {
|
||||
return createNonExisting(locator, request.getDavSession());
|
||||
}
|
||||
}
|
||||
|
||||
private DavResource createResource(CryptoLocator locator, DavSession session) throws DavException {
|
||||
final Path filepath = FileSystems.getDefault().getPath(locator.getRepositoryPath());
|
||||
Path dirpath = null;
|
||||
try {
|
||||
dirpath = FileSystems.getDefault().getPath(locator.getDirectoryPath(false));
|
||||
} catch (FileNotFoundException e) {
|
||||
// no-op
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
@Override
|
||||
public final DavResource createResource(DavResourceLocator locator, DavSession session) throws DavException {
|
||||
if (locator.isRootLocation()) {
|
||||
final Path dirpath = getEncryptedDirectoryPath(locator.getResourcePath());
|
||||
return createDirectory(locator, session, dirpath);
|
||||
}
|
||||
|
||||
if (Files.isDirectory(dirpath)) {
|
||||
return createDirectory(locator, session);
|
||||
final Path filepath = getEncryptedFilePath(locator.getResourcePath());
|
||||
if (filepath.getFileName().toString().endsWith(".dir")) {
|
||||
final Path dirpath = getEncryptedDirectoryPath(locator.getResourcePath());
|
||||
return createDirectory(locator, session, dirpath);
|
||||
} else if (Files.isRegularFile(filepath)) {
|
||||
return createFile(locator, session);
|
||||
return createFile(locator, session, filepath);
|
||||
} else {
|
||||
return createNonExisting(locator, session);
|
||||
}
|
||||
}
|
||||
|
||||
private EncryptedFile createFilePart(CryptoLocator locator, DavSession session, DavServletRequest request) {
|
||||
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor, cryptoWarningHandler, backgroundTaskExecutor);
|
||||
/**
|
||||
* @return Absolute file path for a given cleartext file resourcePath.
|
||||
* @throws IOException
|
||||
*/
|
||||
Path getEncryptedFilePath(String relativeCleartextPath) throws DavException {
|
||||
final String parentCleartextPath = FilenameUtils.getPathNoEndSeparator(relativeCleartextPath);
|
||||
final Path parent = getEncryptedDirectoryPath(parentCleartextPath);
|
||||
final String cleartextFilename = FilenameUtils.getName(relativeCleartextPath);
|
||||
try {
|
||||
final String encryptedFilename = cryptor.encryptFilename(cleartextFilename, this);
|
||||
return parent.resolve(encryptedFilename);
|
||||
} catch (IOException e) {
|
||||
throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private EncryptedFile createFile(CryptoLocator locator, DavSession session) {
|
||||
return new EncryptedFile(this, locator, session, lockManager, cryptor, cryptoWarningHandler);
|
||||
/**
|
||||
* @return Absolute directory path for a given cleartext directory resourcePath.
|
||||
* @throws IOException
|
||||
*/
|
||||
Path getEncryptedDirectoryPath(String relativeCleartextPath) throws DavException {
|
||||
assert Strings.isEmpty(relativeCleartextPath) || !relativeCleartextPath.endsWith("/");
|
||||
try {
|
||||
final Path result;
|
||||
if (Strings.isEmpty(relativeCleartextPath)) {
|
||||
// root level
|
||||
final String fixedRootDirectory = cryptor.encryptDirectoryPath("", FileSystems.getDefault().getSeparator());
|
||||
result = dataRoot.resolve(fixedRootDirectory);
|
||||
} else {
|
||||
final String parentCleartextPath = FilenameUtils.getPathNoEndSeparator(relativeCleartextPath);
|
||||
final Path parent = getEncryptedDirectoryPath(parentCleartextPath);
|
||||
final String cleartextFilename = FilenameUtils.getName(relativeCleartextPath);
|
||||
final String encryptedFilename = cryptor.encryptFilename(cleartextFilename, CryptoResourceFactory.this);
|
||||
final Path directoryFile = parent.resolve(encryptedFilename);
|
||||
final String directoryId;
|
||||
if (Files.exists(directoryFile)) {
|
||||
directoryId = new String(readAllBytesAtomically(directoryFile), StandardCharsets.UTF_8);
|
||||
} else {
|
||||
directoryId = UUID.randomUUID().toString();
|
||||
writeAllBytesAtomically(directoryFile, directoryId.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
final String directory = cryptor.encryptDirectoryPath(directoryId, FileSystems.getDefault().getSeparator());
|
||||
result = dataRoot.resolve(directory);
|
||||
}
|
||||
Files.createDirectories(result);
|
||||
return result;
|
||||
} catch (IOException e) {
|
||||
throw new DavException(DavServletResponse.SC_INTERNAL_SERVER_ERROR, e);
|
||||
}
|
||||
}
|
||||
|
||||
private EncryptedDir createDirectory(CryptoLocator locator, DavSession session) {
|
||||
return new EncryptedDir(this, locator, session, lockManager, cryptor);
|
||||
private EncryptedFile createFilePart(DavResourceLocator locator, DavSession session, DavServletRequest request, Path filePath) {
|
||||
return new EncryptedFilePart(this, locator, session, request, lockManager, cryptor, cryptoWarningHandler, backgroundTaskExecutor, filePath);
|
||||
}
|
||||
|
||||
private NonExistingNode createNonExisting(CryptoLocator locator, DavSession session) {
|
||||
private EncryptedFile createFile(DavResourceLocator locator, DavSession session, Path filePath) {
|
||||
return new EncryptedFile(this, locator, session, lockManager, cryptor, cryptoWarningHandler, filePath);
|
||||
}
|
||||
|
||||
private EncryptedDir createDirectory(DavResourceLocator locator, DavSession session, Path dirPath) {
|
||||
return new EncryptedDir(this, locator, session, lockManager, cryptor, dirPath);
|
||||
}
|
||||
|
||||
private NonExistingNode createNonExisting(DavResourceLocator locator, DavSession session) {
|
||||
return new NonExistingNode(this, locator, session, lockManager, cryptor);
|
||||
}
|
||||
|
||||
/* IO support */
|
||||
|
||||
private void writeAllBytesAtomically(Path path, byte[] bytes) throws IOException {
|
||||
try (final FileChannel c = FileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DSYNC); final FileLock lock = c.lock()) {
|
||||
c.write(ByteBuffer.wrap(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] readAllBytesAtomically(Path path) throws IOException {
|
||||
try (final FileChannel c = FileChannel.open(path, StandardOpenOption.READ, StandardOpenOption.DSYNC); final FileLock lock = c.lock(0L, Long.MAX_VALUE, true)) {
|
||||
final ByteBuffer buffer = ByteBuffer.allocate((int) c.size());
|
||||
c.read(buffer);
|
||||
return buffer.array();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeMetadata(String metadataGroup, byte[] encryptedMetadata) throws IOException {
|
||||
final Path metadataDir = metadataRoot.resolve(metadataGroup.substring(0, 2));
|
||||
Files.createDirectories(metadataDir);
|
||||
final Path metadataFile = metadataDir.resolve(metadataGroup.substring(2));
|
||||
writeAllBytesAtomically(metadataFile, encryptedMetadata);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] readMetadata(String metadataGroup) throws IOException {
|
||||
final Path metadataDir = metadataRoot.resolve(metadataGroup.substring(0, 2));
|
||||
final Path metadataFile = metadataDir.resolve(metadataGroup.substring(2));
|
||||
if (!Files.isReadable(metadataFile)) {
|
||||
return null;
|
||||
} else {
|
||||
return readAllBytesAtomically(metadataFile);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,16 +11,16 @@ package org.cryptomator.webdav.jackrabbit;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
@ -37,9 +37,9 @@ import org.apache.jackrabbit.webdav.property.DefaultDavProperty;
|
||||
import org.apache.jackrabbit.webdav.property.ResourceType;
|
||||
import org.cryptomator.crypto.Cryptor;
|
||||
import org.cryptomator.crypto.exceptions.CounterOverflowException;
|
||||
import org.cryptomator.crypto.exceptions.DecryptFailedException;
|
||||
import org.cryptomator.crypto.exceptions.EncryptFailedException;
|
||||
import org.cryptomator.webdav.exceptions.DavRuntimeException;
|
||||
import org.cryptomator.webdav.exceptions.DecryptFailedRuntimeException;
|
||||
import org.cryptomator.webdav.exceptions.IORuntimeException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -47,18 +47,20 @@ import org.slf4j.LoggerFactory;
|
||||
class EncryptedDir extends AbstractEncryptedNode {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EncryptedDir.class);
|
||||
private final Path directoryPath;
|
||||
|
||||
public EncryptedDir(CryptoResourceFactory factory, CryptoLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
public EncryptedDir(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, Path directoryPath) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
if (directoryPath == null || !Files.isDirectory(directoryPath)) {
|
||||
throw new IllegalArgumentException("directoryPath must be an existing directory, but was " + directoryPath);
|
||||
}
|
||||
this.directoryPath = directoryPath;
|
||||
determineProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path getPhysicalPath() {
|
||||
try {
|
||||
return locator.getEncryptedDirectoryPath(false);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
return directoryPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -68,17 +70,14 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
try {
|
||||
return Files.isDirectory(locator.getEncryptedDirectoryPath(false));
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
assert Files.isDirectory(directoryPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getModificationTime() {
|
||||
try {
|
||||
return Files.getLastModifiedTime(locator.getEncryptedDirectoryPath(false)).toMillis();
|
||||
return Files.getLastModifiedTime(directoryPath).toMillis();
|
||||
} catch (IOException e) {
|
||||
return -1;
|
||||
}
|
||||
@ -101,19 +100,18 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
}
|
||||
}
|
||||
|
||||
private void addMemberDir(CryptoLocator childLocator, InputContext inputContext) throws DavException {
|
||||
private void addMemberDir(DavResourceLocator childLocator, InputContext inputContext) throws DavException {
|
||||
try {
|
||||
Files.createDirectories(childLocator.getEncryptedDirectoryPath(true));
|
||||
// the following invokation will create nonexisting directories:
|
||||
factory.getEncryptedDirectoryPath(childLocator.getResourcePath());
|
||||
} catch (SecurityException e) {
|
||||
throw new DavException(DavServletResponse.SC_FORBIDDEN, e);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Failed to create subdirectory.", e);
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addMemberFile(CryptoLocator childLocator, InputContext inputContext) throws DavException {
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(childLocator.getEncryptedFilePath(), StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
private void addMemberFile(DavResourceLocator childLocator, InputContext inputContext) throws DavException {
|
||||
final Path filePath = factory.getEncryptedFilePath(childLocator.getResourcePath());
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
|
||||
cryptor.encryptFile(inputContext.getInputStream(), channel);
|
||||
} catch (SecurityException e) {
|
||||
throw new DavException(DavServletResponse.SC_FORBIDDEN, e);
|
||||
@ -134,17 +132,17 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
@Override
|
||||
public DavResourceIterator getMembers() {
|
||||
try {
|
||||
final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(locator.getEncryptedDirectoryPath(false), cryptor.getPayloadFilesFilter());
|
||||
final DirectoryStream<Path> directoryStream = Files.newDirectoryStream(directoryPath, cryptor.getPayloadFilesFilter());
|
||||
final List<DavResource> result = new ArrayList<>();
|
||||
|
||||
for (final Path childPath : directoryStream) {
|
||||
try {
|
||||
final DavResourceLocator childLocator = locator.getFactory().createSubresourceLocator(locator, childPath.getFileName().toString());
|
||||
// final DavResourceLocator childLocator = locator.getFactory().createResourceLocator(locator.getPrefix(),
|
||||
// locator.getWorkspacePath(), childPath.toString(), false);
|
||||
final String cleartextFilename = cryptor.decryptFilename(childPath.getFileName().toString(), factory);
|
||||
final String cleartextFilepath = FilenameUtils.concat(getResourcePath(), cleartextFilename);
|
||||
final DavResourceLocator childLocator = locator.getFactory().createResourceLocator(locator.getPrefix(), locator.getWorkspacePath(), cleartextFilepath);
|
||||
final DavResource resource = factory.createResource(childLocator, session);
|
||||
result.add(resource);
|
||||
} catch (DecryptFailedRuntimeException e) {
|
||||
} catch (DecryptFailedException e) {
|
||||
LOG.warn("Decryption of resource failed: " + childPath);
|
||||
continue;
|
||||
}
|
||||
@ -160,7 +158,7 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeMember(DavResource member) {
|
||||
public void removeMember(DavResource member) throws DavException {
|
||||
if (member instanceof AbstractEncryptedNode) {
|
||||
removeMember((AbstractEncryptedNode) member);
|
||||
} else {
|
||||
@ -168,13 +166,19 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeMember(AbstractEncryptedNode member) {
|
||||
private void removeMember(AbstractEncryptedNode member) throws DavException {
|
||||
try {
|
||||
Files.deleteIfExists(member.getLocator().getEncryptedFilePath());
|
||||
if (member.isCollection()) {
|
||||
member.getMembers().forEachRemaining(m -> securelyRemoveMemberOfCollection(member, m));
|
||||
Files.deleteIfExists(member.getLocator().getEncryptedDirectoryPath(false));
|
||||
// remove sub-members recursively before deleting own directory
|
||||
for (Iterator<DavResource> iterator = member.getMembers(); iterator.hasNext();) {
|
||||
DavResource m = iterator.next();
|
||||
member.removeMember(m);
|
||||
}
|
||||
final Path memberDirectoryPath = factory.getEncryptedDirectoryPath(member.getResourcePath());
|
||||
Files.deleteIfExists(memberDirectoryPath);
|
||||
}
|
||||
final Path memberPath = factory.getEncryptedFilePath(member.getResourcePath());
|
||||
Files.deleteIfExists(memberPath);
|
||||
} catch (FileNotFoundException e) {
|
||||
// no-op
|
||||
} catch (IOException e) {
|
||||
@ -182,58 +186,52 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
}
|
||||
}
|
||||
|
||||
private void securelyRemoveMemberOfCollection(DavResource collection, DavResource member) {
|
||||
try {
|
||||
collection.removeMember(member);
|
||||
} catch (DavException e) {
|
||||
throw new IllegalStateException("DavException should not be thrown by collections of type EncryptedDir. Collections is of type " + collection.getClass().getName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void move(AbstractEncryptedNode dest) throws DavException, IOException {
|
||||
final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
|
||||
final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
|
||||
final Path srcFile = this.locator.getEncryptedFilePath();
|
||||
final Path dstFile = dest.locator.getEncryptedFilePath();
|
||||
|
||||
// check for conflicts:
|
||||
if (Files.exists(dstDir) && Files.getLastModifiedTime(dstDir).toMillis() > Files.getLastModifiedTime(dstDir).toMillis()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Directory at destination already exists: " + dstDir.toString());
|
||||
}
|
||||
|
||||
// move:
|
||||
Files.createDirectories(dstDir);
|
||||
try {
|
||||
Files.move(srcDir, dstDir, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
Files.move(srcFile, dstFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.move(srcDir, dstDir, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.move(srcFile, dstFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
// final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
|
||||
// final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
|
||||
// final Path srcFile = this.locator.getEncryptedFilePath();
|
||||
// final Path dstFile = dest.locator.getEncryptedFilePath();
|
||||
//
|
||||
// // check for conflicts:
|
||||
// if (Files.exists(dstDir) && Files.getLastModifiedTime(dstDir).toMillis() > Files.getLastModifiedTime(dstDir).toMillis()) {
|
||||
// throw new DavException(DavServletResponse.SC_CONFLICT, "Directory at destination already exists: " + dstDir.toString());
|
||||
// }
|
||||
//
|
||||
// // move:
|
||||
// Files.createDirectories(dstDir);
|
||||
// try {
|
||||
// Files.move(srcDir, dstDir, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// Files.move(srcFile, dstFile, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// } catch (AtomicMoveNotSupportedException e) {
|
||||
// Files.move(srcDir, dstDir, StandardCopyOption.REPLACE_EXISTING);
|
||||
// Files.move(srcFile, dstFile, StandardCopyOption.REPLACE_EXISTING);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(AbstractEncryptedNode dest, boolean shallow) throws DavException, IOException {
|
||||
final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
|
||||
final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
|
||||
final Path srcFile = this.locator.getEncryptedFilePath();
|
||||
final Path dstFile = dest.locator.getEncryptedFilePath();
|
||||
|
||||
// check for conflicts:
|
||||
if (Files.exists(dstDir) && Files.getLastModifiedTime(dstDir).toMillis() > Files.getLastModifiedTime(dstDir).toMillis()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "Directory at destination already exists: " + dstDir.toString());
|
||||
}
|
||||
|
||||
// copy:
|
||||
Files.createDirectories(dstDir);
|
||||
try {
|
||||
Files.copy(srcDir, dstDir, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
Files.copy(srcFile, dstFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.copy(srcDir, dstDir, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(srcFile, dstFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
// final Path srcDir = this.locator.getEncryptedDirectoryPath(false);
|
||||
// final Path dstDir = dest.locator.getEncryptedDirectoryPath(true);
|
||||
// final Path srcFile = this.locator.getEncryptedFilePath();
|
||||
// final Path dstFile = dest.locator.getEncryptedFilePath();
|
||||
//
|
||||
// // check for conflicts:
|
||||
// if (Files.exists(dstDir) && Files.getLastModifiedTime(dstDir).toMillis() > Files.getLastModifiedTime(dstDir).toMillis()) {
|
||||
// throw new DavException(DavServletResponse.SC_CONFLICT, "Directory at destination already exists: " + dstDir.toString());
|
||||
// }
|
||||
//
|
||||
// // copy:
|
||||
// Files.createDirectories(dstDir);
|
||||
// try {
|
||||
// Files.copy(srcDir, dstDir, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// Files.copy(srcFile, dstFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// } catch (AtomicMoveNotSupportedException e) {
|
||||
// Files.copy(srcDir, dstDir, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
// Files.copy(srcFile, dstFile, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -243,23 +241,15 @@ class EncryptedDir extends AbstractEncryptedNode {
|
||||
|
||||
@Override
|
||||
protected void determineProperties() {
|
||||
Path path;
|
||||
try {
|
||||
path = locator.getEncryptedDirectoryPath(false);
|
||||
} catch (IOException e) {
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
properties.add(new ResourceType(ResourceType.COLLECTION));
|
||||
properties.add(new DefaultDavProperty<Integer>(DavPropertyName.ISCOLLECTION, 1));
|
||||
if (Files.exists(path)) {
|
||||
try {
|
||||
final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.CREATIONDATE, FileTimeUtils.toRfc1123String(attrs.creationTime())));
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.GETLASTMODIFIED, FileTimeUtils.toRfc1123String(attrs.lastModifiedTime())));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error determining metadata " + path.toString(), e);
|
||||
// don't add any further properties
|
||||
}
|
||||
try {
|
||||
final BasicFileAttributes attrs = Files.readAttributes(directoryPath, BasicFileAttributes.class);
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.CREATIONDATE, FileTimeUtils.toRfc1123String(attrs.creationTime())));
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.GETLASTMODIFIED, FileTimeUtils.toRfc1123String(attrs.lastModifiedTime())));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error determining metadata " + directoryPath.toString(), e);
|
||||
// don't add any further properties
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,17 +11,15 @@ package org.cryptomator.webdav.jackrabbit;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.SeekableByteChannel;
|
||||
import java.nio.file.AtomicMoveNotSupportedException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
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.DavResourceLocator;
|
||||
import org.apache.jackrabbit.webdav.DavSession;
|
||||
import org.apache.jackrabbit.webdav.io.InputContext;
|
||||
import org.apache.jackrabbit.webdav.io.OutputContext;
|
||||
@ -42,15 +40,21 @@ class EncryptedFile extends AbstractEncryptedNode {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(EncryptedFile.class);
|
||||
|
||||
protected final CryptoWarningHandler cryptoWarningHandler;
|
||||
protected final Path filePath;
|
||||
|
||||
public EncryptedFile(CryptoResourceFactory factory, CryptoLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler) {
|
||||
public EncryptedFile(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler, Path filePath) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
if (filePath == null) {
|
||||
throw new IllegalArgumentException("filePath must not be null");
|
||||
}
|
||||
this.cryptoWarningHandler = cryptoWarningHandler;
|
||||
this.filePath = filePath;
|
||||
this.determineProperties();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Path getPhysicalPath() {
|
||||
return locator.getEncryptedFilePath();
|
||||
return filePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -75,11 +79,10 @@ class EncryptedFile extends AbstractEncryptedNode {
|
||||
|
||||
@Override
|
||||
public void spool(OutputContext outputContext) throws IOException {
|
||||
final Path path = locator.getEncryptedFilePath();
|
||||
if (Files.isRegularFile(path)) {
|
||||
outputContext.setModificationTime(Files.getLastModifiedTime(path).toMillis());
|
||||
if (Files.isRegularFile(filePath)) {
|
||||
outputContext.setModificationTime(Files.getLastModifiedTime(filePath).toMillis());
|
||||
outputContext.setProperty(HttpHeader.ACCEPT_RANGES.asString(), HttpHeaderValue.BYTES.asString());
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ)) {
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final Long contentLength = cryptor.decryptedContentLength(channel);
|
||||
if (contentLength != null) {
|
||||
outputContext.setContentLength(contentLength);
|
||||
@ -92,20 +95,19 @@ class EncryptedFile extends AbstractEncryptedNode {
|
||||
} catch (MacAuthenticationFailedException e) {
|
||||
cryptoWarningHandler.macAuthFailed(getLocator().getResourcePath());
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + path.toString(), e);
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void determineProperties() {
|
||||
final Path path = locator.getEncryptedFilePath();
|
||||
if (Files.exists(path)) {
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ)) {
|
||||
if (Files.isRegularFile(filePath)) {
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final Long contentLength = cryptor.decryptedContentLength(channel);
|
||||
properties.add(new DefaultDavProperty<Long>(DavPropertyName.GETCONTENTLENGTH, contentLength));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error reading filesize " + path.toString(), e);
|
||||
LOG.error("Error reading filesize " + filePath.toString(), e);
|
||||
throw new IORuntimeException(e);
|
||||
} catch (MacAuthenticationFailedException e) {
|
||||
LOG.warn("Content length couldn't be determined due to MAC authentication violation.");
|
||||
@ -113,12 +115,12 @@ class EncryptedFile extends AbstractEncryptedNode {
|
||||
}
|
||||
|
||||
try {
|
||||
final BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
final BasicFileAttributes attrs = Files.readAttributes(filePath, BasicFileAttributes.class);
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.CREATIONDATE, FileTimeUtils.toRfc1123String(attrs.creationTime())));
|
||||
properties.add(new DefaultDavProperty<String>(DavPropertyName.GETLASTMODIFIED, FileTimeUtils.toRfc1123String(attrs.lastModifiedTime())));
|
||||
properties.add(new HttpHeaderProperty(HttpHeader.ACCEPT_RANGES.asString(), HttpHeaderValue.BYTES.asString()));
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error determining metadata " + path.toString(), e);
|
||||
LOG.error("Error determining metadata " + filePath.toString(), e);
|
||||
throw new IORuntimeException(e);
|
||||
}
|
||||
}
|
||||
@ -126,38 +128,40 @@ class EncryptedFile extends AbstractEncryptedNode {
|
||||
|
||||
@Override
|
||||
public void move(AbstractEncryptedNode dest) throws DavException, IOException {
|
||||
final Path src = this.locator.getEncryptedFilePath();
|
||||
final Path dst = dest.locator.getEncryptedFilePath();
|
||||
|
||||
// check for conflicts:
|
||||
if (Files.exists(dst) && Files.getLastModifiedTime(dst).toMillis() > Files.getLastModifiedTime(src).toMillis()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "File at destination already exists: " + dst.toString());
|
||||
}
|
||||
|
||||
// move:
|
||||
try {
|
||||
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
// final Path src = this.locator.getEncryptedFilePath();
|
||||
// final Path dst = dest.locator.getEncryptedFilePath();
|
||||
//
|
||||
// // check for conflicts:
|
||||
// if (Files.exists(dst) && Files.getLastModifiedTime(dst).toMillis() > Files.getLastModifiedTime(src).toMillis()) {
|
||||
// throw new DavException(DavServletResponse.SC_CONFLICT, "File at destination already exists: " + dst.toString());
|
||||
// }
|
||||
//
|
||||
// // move:
|
||||
// try {
|
||||
// Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// } catch (AtomicMoveNotSupportedException e) {
|
||||
// Files.move(src, dst, StandardCopyOption.REPLACE_EXISTING);
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copy(AbstractEncryptedNode dest, boolean shallow) throws DavException, IOException {
|
||||
final Path src = this.locator.getEncryptedFilePath();
|
||||
final Path dst = dest.locator.getEncryptedFilePath();
|
||||
|
||||
// check for conflicts:
|
||||
if (Files.exists(dst) && Files.getLastModifiedTime(dst).toMillis() > Files.getLastModifiedTime(src).toMillis()) {
|
||||
throw new DavException(DavServletResponse.SC_CONFLICT, "File at destination already exists: " + dst.toString());
|
||||
}
|
||||
|
||||
// copy:
|
||||
try {
|
||||
Files.copy(src, dst, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
} catch (AtomicMoveNotSupportedException e) {
|
||||
Files.copy(src, dst, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
}
|
||||
throw new UnsupportedOperationException("not yet implemented");
|
||||
// final Path src = this.locator.getEncryptedFilePath();
|
||||
// final Path dst = dest.locator.getEncryptedFilePath();
|
||||
//
|
||||
// // check for conflicts:
|
||||
// if (Files.exists(dst) && Files.getLastModifiedTime(dst).toMillis() > Files.getLastModifiedTime(src).toMillis()) {
|
||||
// throw new DavException(DavServletResponse.SC_CONFLICT, "File at destination already exists: " + dst.toString());
|
||||
// }
|
||||
//
|
||||
// // copy:
|
||||
// try {
|
||||
// Files.copy(src, dst, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
|
||||
// } catch (AtomicMoveNotSupportedException e) {
|
||||
// Files.copy(src, dst, StandardCopyOption.COPY_ATTRIBUTES, StandardCopyOption.REPLACE_EXISTING);
|
||||
// }
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,9 +55,9 @@ class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
private final Set<Pair<Long, Long>> requestedContentRanges = new HashSet<Pair<Long, Long>>();
|
||||
|
||||
public EncryptedFilePart(CryptoResourceFactory factory, CryptoLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler,
|
||||
ExecutorService backgroundTaskExecutor) {
|
||||
super(factory, locator, session, lockManager, cryptor, cryptoWarningHandler);
|
||||
public EncryptedFilePart(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, DavServletRequest request, LockManager lockManager, Cryptor cryptor, CryptoWarningHandler cryptoWarningHandler,
|
||||
ExecutorService backgroundTaskExecutor, Path filePath) {
|
||||
super(factory, locator, session, lockManager, cryptor, cryptoWarningHandler, filePath);
|
||||
final String rangeHeader = request.getHeader(HttpHeader.RANGE.asString());
|
||||
if (rangeHeader == null) {
|
||||
throw new IllegalArgumentException("HTTP request doesn't contain a range header");
|
||||
@ -125,25 +125,23 @@ class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
@Override
|
||||
public void spool(OutputContext outputContext) throws IOException {
|
||||
final Path path = locator.getEncryptedFilePath();
|
||||
if (Files.isRegularFile(path)) {
|
||||
outputContext.setModificationTime(Files.getLastModifiedTime(path).toMillis());
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ)) {
|
||||
final Long fileSize = cryptor.decryptedContentLength(channel);
|
||||
final Pair<Long, Long> range = getUnionRange(fileSize);
|
||||
final Long rangeLength = range.getRight() - range.getLeft() + 1;
|
||||
outputContext.setContentLength(rangeLength);
|
||||
outputContext.setProperty(HttpHeader.CONTENT_RANGE.asString(), getContentRangeHeader(range.getLeft(), range.getRight(), fileSize));
|
||||
if (outputContext.hasStream()) {
|
||||
cryptor.decryptRange(channel, outputContext.getOutputStream(), range.getLeft(), rangeLength);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Unexpected end of stream during delivery of partial content (client hung up).");
|
||||
}
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + path.toString(), e);
|
||||
assert Files.isRegularFile(filePath);
|
||||
outputContext.setModificationTime(Files.getLastModifiedTime(filePath).toMillis());
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final Long fileSize = cryptor.decryptedContentLength(channel);
|
||||
final Pair<Long, Long> range = getUnionRange(fileSize);
|
||||
final Long rangeLength = range.getRight() - range.getLeft() + 1;
|
||||
outputContext.setContentLength(rangeLength);
|
||||
outputContext.setProperty(HttpHeader.CONTENT_RANGE.asString(), getContentRangeHeader(range.getLeft(), range.getRight(), fileSize));
|
||||
if (outputContext.hasStream()) {
|
||||
cryptor.decryptRange(channel, outputContext.getOutputStream(), range.getLeft(), rangeLength);
|
||||
}
|
||||
} catch (EOFException e) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Unexpected end of stream during delivery of partial content (client hung up).");
|
||||
}
|
||||
} catch (DecryptFailedException e) {
|
||||
throw new IOException("Error decrypting file " + filePath.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,9 +151,9 @@ class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
private class MacAuthenticationJob implements Runnable {
|
||||
|
||||
private final CryptoLocator locator;
|
||||
private final DavResourceLocator locator;
|
||||
|
||||
public MacAuthenticationJob(final CryptoLocator locator) {
|
||||
public MacAuthenticationJob(final DavResourceLocator locator) {
|
||||
if (locator == null) {
|
||||
throw new IllegalArgumentException("locator must not be null.");
|
||||
}
|
||||
@ -164,18 +162,16 @@ class EncryptedFilePart extends EncryptedFile {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
final Path path = locator.getEncryptedFilePath();
|
||||
if (Files.isRegularFile(path) && Files.isReadable(path)) {
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(path, StandardOpenOption.READ)) {
|
||||
final boolean authentic = cryptor.isAuthentic(channel);
|
||||
if (!authentic) {
|
||||
cryptoWarningHandler.macAuthFailed(locator.getResourcePath());
|
||||
}
|
||||
} catch (ClosedByInterruptException ex) {
|
||||
LOG.debug("Couldn't finish MAC verification due to interruption of worker thread.");
|
||||
} catch (IOException e) {
|
||||
LOG.error("IOException during MAC verification of " + path.toString(), e);
|
||||
assert Files.isRegularFile(filePath);
|
||||
try (final SeekableByteChannel channel = Files.newByteChannel(filePath, StandardOpenOption.READ)) {
|
||||
final boolean authentic = cryptor.isAuthentic(channel);
|
||||
if (!authentic) {
|
||||
cryptoWarningHandler.macAuthFailed(locator.getResourcePath());
|
||||
}
|
||||
} catch (ClosedByInterruptException ex) {
|
||||
LOG.debug("Couldn't finish MAC verification due to interruption of worker thread.");
|
||||
} catch (IOException e) {
|
||||
LOG.error("IOException during MAC verification of " + filePath.toString(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ import java.nio.file.Path;
|
||||
import org.apache.jackrabbit.webdav.DavException;
|
||||
import org.apache.jackrabbit.webdav.DavResource;
|
||||
import org.apache.jackrabbit.webdav.DavResourceIterator;
|
||||
import org.apache.jackrabbit.webdav.DavResourceLocator;
|
||||
import org.apache.jackrabbit.webdav.DavSession;
|
||||
import org.apache.jackrabbit.webdav.io.InputContext;
|
||||
import org.apache.jackrabbit.webdav.io.OutputContext;
|
||||
@ -22,7 +23,7 @@ import org.cryptomator.crypto.Cryptor;
|
||||
|
||||
class NonExistingNode extends AbstractEncryptedNode {
|
||||
|
||||
public NonExistingNode(CryptoResourceFactory factory, CryptoLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
public NonExistingNode(CryptoResourceFactory factory, DavResourceLocator locator, DavSession session, LockManager lockManager, Cryptor cryptor) {
|
||||
super(factory, locator, session, lockManager, cryptor);
|
||||
}
|
||||
|
||||
|
@ -47,8 +47,8 @@ public class WebDavServlet extends AbstractWebdavServlet {
|
||||
final String fsRoot = config.getInitParameter(CFG_FS_ROOT);
|
||||
backgroundTaskExecutor = Executors.newCachedThreadPool();
|
||||
davSessionProvider = new DavSessionProviderImpl();
|
||||
davLocatorFactory = new CryptoLocatorFactory(fsRoot, cryptor);
|
||||
davResourceFactory = new CryptoResourceFactory(cryptor, cryptoWarningHandler, backgroundTaskExecutor);
|
||||
davLocatorFactory = new CleartextLocatorFactory(config.getServletContext().getContextPath()); // CryptoLocatorFactory(fsRoot, cryptor);
|
||||
davResourceFactory = new CryptoResourceFactory(cryptor, cryptoWarningHandler, backgroundTaskExecutor, fsRoot);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -288,8 +288,8 @@ public class Aes256Cryptor implements Cryptor, AesCryptographicConfiguration, Fi
|
||||
}
|
||||
|
||||
@Override
|
||||
public String encryptDirectoryPath(String cleartextPath, String nativePathSep) {
|
||||
final byte[] cleartextBytes = cleartextPath.getBytes(StandardCharsets.UTF_8);
|
||||
public String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep) {
|
||||
final byte[] cleartextBytes = cleartextDirectoryId.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] encryptedBytes = AesSivCipherUtil.sivEncrypt(primaryMasterKey, hMacMasterKey, cleartextBytes);
|
||||
final byte[] hashed = sha256().digest(encryptedBytes);
|
||||
final String encryptedThenHashedPath = ENCRYPTED_FILENAME_CODEC.encodeAsString(hashed);
|
||||
|
@ -47,11 +47,11 @@ public interface Cryptor extends Destroyable {
|
||||
/**
|
||||
* Encrypts a given plaintext path representing a directory structure. See {@link #encryptFilename(String, CryptorMetadataSupport)} for contents inside directories.
|
||||
*
|
||||
* @param cleartextPath A relative path (UTF-8 encoded), whose path components are separated by '/'
|
||||
* @param cleartextDirectoryId A relative path (UTF-8 encoded), whose path components are separated by '/'
|
||||
* @param nativePathSep Path separator like "/" used on local file system. Must not be null, even if cleartextPath is a sole file name without any path separators.
|
||||
* @return Encrypted path.
|
||||
*/
|
||||
String encryptDirectoryPath(String cleartextPath, String nativePathSep);
|
||||
String encryptDirectoryPath(String cleartextDirectoryId, String nativePathSep);
|
||||
|
||||
/**
|
||||
* Encrypts the name of a file. See {@link #encryptDirectoryPath(String, char)} for parent dir.
|
||||
|
Loading…
Reference in New Issue
Block a user