org.cryptomator.webdav.jackrabbit.DavLocatorFactoryImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.cryptomator.webdav.jackrabbit.DavLocatorFactoryImpl.java

Source

/*******************************************************************************
 * Copyright (c) 2014 Sebastian Stenzel
 * 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.jackrabbit;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;

import org.apache.commons.collections4.BidiMap;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.jackrabbit.webdav.DavLocatorFactory;
import org.apache.jackrabbit.webdav.DavResourceLocator;
import org.apache.jackrabbit.webdav.util.EncodeUtil;
import org.cryptomator.crypto.Cryptor;
import org.cryptomator.crypto.CryptorIOSupport;
import org.cryptomator.crypto.SensitiveDataSwipeListener;

class DavLocatorFactoryImpl implements DavLocatorFactory, SensitiveDataSwipeListener, CryptorIOSupport {

    private static final int MAX_CACHED_PATHS = 10000;
    private final Path fsRoot;
    private final Cryptor cryptor;
    private final BidiMap<String, String> pathCache = new BidiLRUMap<>(MAX_CACHED_PATHS); // <decryptedPath, encryptedPath>

    DavLocatorFactoryImpl(String fsRoot, Cryptor cryptor) {
        this.fsRoot = FileSystems.getDefault().getPath(fsRoot);
        this.cryptor = cryptor;
        cryptor.addSensitiveDataSwipeListener(this);
    }

    /* DavLocatorFactory */

    @Override
    public DavResourceLocator createResourceLocator(String prefix, String href) {
        final String fullPrefix = prefix.endsWith("/") ? prefix : prefix + "/";
        final String relativeHref = StringUtils.removeStart(href, fullPrefix);

        final String resourcePath = EncodeUtil.unescape(StringUtils.removeStart(relativeHref, "/"));
        return new DavResourceLocatorImpl(fullPrefix, resourcePath);
    }

    @Override
    public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String path,
            boolean isResourcePath) {
        final String fullPrefix = prefix.endsWith("/") ? prefix : prefix + "/";

        final String resourcePath = (isResourcePath) ? path : getResourcePath(path);
        return new DavResourceLocatorImpl(fullPrefix, resourcePath);
    }

    @Override
    public DavResourceLocator createResourceLocator(String prefix, String workspacePath, String resourcePath) {
        return createResourceLocator(prefix, workspacePath, resourcePath, true);
    }

    /* Encryption/Decryption */

    /**
     * @return Encrypted absolute paths on the file system.
     */
    private String getRepositoryPath(String resourcePath) {
        String encryptedPath = pathCache.get(resourcePath);
        if (encryptedPath == null) {
            encryptedPath = encryptRepositoryPath(resourcePath);
            pathCache.put(resourcePath, encryptedPath);
        }
        return encryptedPath;
    }

    private String encryptRepositoryPath(String resourcePath) {
        if (resourcePath == null) {
            return fsRoot.toString();
        }
        final String encryptedRepoPath = cryptor.encryptPath(resourcePath,
                FileSystems.getDefault().getSeparator().charAt(0), '/', this);
        return fsRoot.resolve(encryptedRepoPath).toString();
    }

    /**
     * @return Decrypted path for use in URIs.
     */
    private String getResourcePath(String repositoryPath) {
        String decryptedPath = pathCache.getKey(repositoryPath);
        if (decryptedPath == null) {
            decryptedPath = decryptResourcePath(repositoryPath);
            pathCache.put(decryptedPath, repositoryPath);
        }
        return decryptedPath;
    }

    private String decryptResourcePath(String repositoryPath) {
        final Path absRepoPath = FileSystems.getDefault().getPath(repositoryPath);
        if (fsRoot.equals(absRepoPath)) {
            return null;
        } else {
            final Path relativeRepositoryPath = fsRoot.relativize(absRepoPath);
            final String resourcePath = cryptor.decryptPath(relativeRepositoryPath.toString(),
                    FileSystems.getDefault().getSeparator().charAt(0), '/', this);
            return resourcePath;
        }
    }

    /* CryptorIOSupport */

    @Override
    public void writePathSpecificMetadata(String encryptedPath, byte[] encryptedMetadata) throws IOException {
        final Path metaDataFile = fsRoot.resolve(encryptedPath);
        Files.write(metaDataFile, encryptedMetadata, StandardOpenOption.WRITE, StandardOpenOption.CREATE,
                StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DSYNC);
    }

    @Override
    public byte[] readPathSpecificMetadata(String encryptedPath) throws IOException {
        final Path metaDataFile = fsRoot.resolve(encryptedPath);
        if (!Files.isReadable(metaDataFile)) {
            return null;
        } else {
            return Files.readAllBytes(metaDataFile);
        }
    }

    /* SensitiveDataSwipeListener */

    @Override
    public void swipeSensitiveData() {
        pathCache.clear();
    }

    /* Locator */

    private class DavResourceLocatorImpl implements DavResourceLocator {

        private final String prefix;
        private final String resourcePath;

        private DavResourceLocatorImpl(String prefix, String resourcePath) {
            this.prefix = prefix;
            this.resourcePath = resourcePath;
        }

        @Override
        public String getPrefix() {
            return prefix;
        }

        @Override
        public String getResourcePath() {
            return resourcePath;
        }

        @Override
        public String getWorkspacePath() {
            return isRootLocation() ? null : "";
        }

        @Override
        public String getWorkspaceName() {
            return getPrefix();
        }

        @Override
        public boolean isSameWorkspace(DavResourceLocator locator) {
            return (locator == null) ? false : isSameWorkspace(locator.getWorkspaceName());
        }

        @Override
        public boolean isSameWorkspace(String workspaceName) {
            return getWorkspaceName().equals(workspaceName);
        }

        @Override
        public String getHref(boolean isCollection) {
            final String href = getPrefix().concat(getResourcePath());
            if (isCollection && !href.endsWith("/")) {
                return href.concat("/");
            } else if (!isCollection && href.endsWith("/")) {
                return href.substring(0, href.length() - 1);
            } else {
                return href;
            }
        }

        @Override
        public boolean isRootLocation() {
            return getResourcePath() == null;
        }

        @Override
        public DavLocatorFactory getFactory() {
            return DavLocatorFactoryImpl.this;
        }

        @Override
        public String getRepositoryPath() {
            return DavLocatorFactoryImpl.this.getRepositoryPath(getResourcePath());
        }

        @Override
        public int hashCode() {
            final HashCodeBuilder builder = new HashCodeBuilder();
            builder.append(prefix);
            builder.append(resourcePath);
            return builder.toHashCode();
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof DavResourceLocatorImpl) {
                final DavResourceLocatorImpl other = (DavResourceLocatorImpl) obj;
                final EqualsBuilder builder = new EqualsBuilder();
                builder.append(this.prefix, other.prefix);
                builder.append(this.resourcePath, other.resourcePath);
                return builder.isEquals();
            } else {
                return false;
            }
        }

    }

}