codes.thischwa.c5c.DispatcherPUT.java Source code

Java tutorial

Introduction

Here is the source code for codes.thischwa.c5c.DispatcherPUT.java

Source

/*
 * C5Connector.Java - The Java backend for the filemanager of corefive.
 * It's a bridge between the filemanager and a storage backend and 
 * works like a transparent VFS or proxy.
 * Copyright (C) Thilo Schwarz
 * 
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
package codes.thischwa.c5c;

import java.awt.Dimension;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.HashSet;
import java.util.Set;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.Part;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import codes.thischwa.c5c.GenericConnector.StreamContent;
import codes.thischwa.c5c.exception.C5CException;
import codes.thischwa.c5c.exception.FilemanagerException;
import codes.thischwa.c5c.exception.FilemanagerException.Key;
import codes.thischwa.c5c.filemanager.FilemanagerConfig;
import codes.thischwa.c5c.filemanager.Resize;
import codes.thischwa.c5c.requestcycle.Context;
import codes.thischwa.c5c.requestcycle.RequestData;
import codes.thischwa.c5c.requestcycle.response.GenericResponse;
import codes.thischwa.c5c.requestcycle.response.mode.Replace;
import codes.thischwa.c5c.requestcycle.response.mode.SaveFile;
import codes.thischwa.c5c.requestcycle.response.mode.UploadFile;
import codes.thischwa.c5c.util.FileUtils;
import codes.thischwa.c5c.util.StringUtils;
import codes.thischwa.c5c.util.VirtualFile;

/**
 * Dispatches the PUT-request from the 'main' servlet {@link ConnectorServlet} to the implementation of the 
 * {@link Connector}. The parameters of the request will be prepared before dispatching them to connector.
 */
final class DispatcherPUT extends GenericDispatcher {
    private static Logger logger = LoggerFactory.getLogger(DispatcherPUT.class);

    /**
     * Instantiates and initializes the connector (object which extends the {@link GenericConnector});
     * 
     * @param connector
     *            the implementation of the {@link Connector} interface
     */
    DispatcherPUT(Connector connector) {
        super(connector);
    }

    @Override
    GenericResponse doRequest() {
        logger.debug("Entering DispatcherPUT#doRequest");

        InputStream in = null;
        try {
            Context ctx = RequestData.getContext();
            FilemanagerAction mode = ctx.getMode();
            HttpServletRequest req = ctx.getServletRequest();
            FilemanagerConfig conf = UserObjectProxy.getFilemanagerUserConfig(req);
            switch (mode) {
            case UPLOAD: {
                boolean overwrite = conf.getUpload().isOverwrite();
                String currentPath = IOUtils.toString(req.getPart("currentpath").getInputStream());
                String backendPath = buildBackendPath(currentPath);
                Part uploadPart = req.getPart("newfile");
                String newName = getFileName(uploadPart);

                // Some browsers transfer the entire source path not just the filename
                String fileName = FilenameUtils.getName(newName); // TODO check forceSingleExtension
                String sanitizedName = FileUtils.sanitizeName(fileName);
                if (!overwrite)
                    sanitizedName = getUniqueName(backendPath, sanitizedName);
                logger.debug("* upload -> currentpath: {}, filename: {}, sanitized filename: {}", currentPath,
                        fileName, sanitizedName);

                // check 'overwrite' and unambiguity
                String uniqueName = getUniqueName(backendPath, sanitizedName);
                if (!overwrite && !uniqueName.equals(sanitizedName)) {
                    throw new FilemanagerException(FilemanagerAction.UPLOAD,
                            FilemanagerException.Key.FileAlreadyExists, sanitizedName);
                }
                sanitizedName = uniqueName;

                in = uploadPart.getInputStream();

                // save the file temporary
                Path tempPath = saveTemp(in, sanitizedName);

                // pre-process the upload
                imageProcessingAndSizeCheck(tempPath, sanitizedName, uploadPart.getSize(), conf);

                connector.upload(backendPath, sanitizedName,
                        new BufferedInputStream(Files.newInputStream(tempPath)));

                logger.debug("successful uploaded {} bytes", uploadPart.getSize());
                Files.delete(tempPath);
                UploadFile ufResp = new UploadFile(currentPath, sanitizedName);
                ufResp.setName(newName);
                ufResp.setPath(currentPath);
                return ufResp;
            }
            case REPLACE: {
                String newFilePath = IOUtils.toString(req.getPart("newfilepath").getInputStream());
                String backendPath = buildBackendPath(newFilePath);
                Part uploadPart = req.getPart("fileR");
                logger.debug("* replacefile -> urlPath: {}, backendPath: {}", newFilePath, backendPath);

                // check if backendPath is protected
                boolean protect = connector.isProtected(backendPath);
                if (protect) {
                    throw new FilemanagerException(FilemanagerAction.UPLOAD,
                            FilemanagerException.Key.NotAllowedSystem, backendPath);
                }

                // check if file already exits
                VirtualFile vf = new VirtualFile(backendPath, false);
                String fileName = vf.getName();
                String uniqueName = getUniqueName(vf.getFolder(), fileName);
                if (uniqueName.equals(fileName)) {
                    throw new FilemanagerException(FilemanagerAction.UPLOAD, FilemanagerException.Key.FileNotExists,
                            backendPath);
                }

                in = uploadPart.getInputStream();

                // save the file temporary
                Path tempPath = saveTemp(in, fileName);

                // pre-process the upload
                imageProcessingAndSizeCheck(tempPath, fileName, uploadPart.getSize(), conf);

                connector.replace(backendPath, new BufferedInputStream(Files.newInputStream(tempPath)));
                logger.debug("successful replaced {} bytes", uploadPart.getSize());
                VirtualFile vfUrlPath = new VirtualFile(newFilePath, false);
                return new Replace(vfUrlPath.getFolder(), vfUrlPath.getName());
            }
            case SAVEFILE: {
                String urlPath = req.getParameter("path");
                String backendPath = buildBackendPath(urlPath);
                logger.debug("* savefile -> urlPath: {}, backendPath: {}", urlPath, backendPath);
                String content = req.getParameter("content");
                connector.saveFile(backendPath, content);
                return new SaveFile(urlPath);
            }
            default: {
                logger.error("Unknown 'mode' for POST: {}", req.getParameter("mode"));
                throw new C5CException(UserObjectProxy.getFilemanagerErrorMessage(Key.ModeError));
            }
            }
        } catch (C5CException e) {
            return ErrorResponseFactory.buildErrorResponseForUpload(e.getMessage());
        } catch (ServletException e) {
            logger.error("A ServletException was thrown while uploading: " + e.getMessage(), e);
            return ErrorResponseFactory.buildErrorResponseForUpload(e.getMessage(), 200);
        } catch (IOException e) {
            logger.error("A IOException was thrown while uploading: " + e.getMessage(), e);
            return ErrorResponseFactory.buildErrorResponseForUpload(e.getMessage(), 200);
        } finally {
            IOUtils.closeQuietly(in);
        }
    };

    private void imageProcessingAndSizeCheck(Path tempPath, String sanitizedName, long fileSize,
            FilemanagerConfig conf) throws C5CException, IOException {
        Integer maxSize = (conf.getUpload().isFileSizeLimitAuto()) ? PropertiesLoader.getMaxUploadSize()
                : conf.getUpload().getFileSizeLimit();
        if (fileSize > maxSize.longValue() * 1024 * 1024)
            throw new FilemanagerException(FilemanagerAction.UPLOAD,
                    FilemanagerException.Key.UploadFilesSmallerThan, String.valueOf(maxSize));
        String extension = FilenameUtils.getExtension(sanitizedName);

        // check image only
        boolean isImageExt = checkImageExtension(sanitizedName, conf.getUpload().isImagesOnly(),
                conf.getImages().getExtensions());
        if (!isImageExt)
            return;

        // remove exif data
        Path woExifPath = UserObjectProxy.removeExif(tempPath);
        if (!tempPath.equals(woExifPath)) {
            Files.move(woExifPath, tempPath, StandardCopyOption.REPLACE_EXISTING);
        }

        // check if the file is really an image
        InputStream in = new BufferedInputStream(Files.newInputStream(tempPath, StandardOpenOption.READ));
        Dimension dim = getDimension(in);
        if (isImageExt && dim == null)
            throw new FilemanagerException(FilemanagerAction.UPLOAD, FilemanagerException.Key.UploadImagesOnly);
        IOUtils.closeQuietly(in);

        // check if resize is enabled and fix it, if necessary 
        Resize resize = conf.getImages().getResize();
        if (resize.isEnabled()
                && (dim.getHeight() > resize.getMaxHeight() || dim.getWidth() > resize.getMaxWidth())) {
            logger.debug("process resize");
            StreamContent sc = connector.resize(new BufferedInputStream(Files.newInputStream(tempPath)), extension,
                    new Dimension(resize.getMaxWidth(), resize.getMaxHeight()));
            Files.copy(sc.getInputStream(), tempPath, StandardCopyOption.REPLACE_EXISTING);
            IOUtils.closeQuietly(sc.getInputStream());
        }
    }

    private Path saveTemp(InputStream in, String name) throws IOException {
        String baseName = FilenameUtils.getBaseName(name);
        String ext = FilenameUtils.getExtension(name);
        try {
            Path tempPath = Files.createTempFile(UserObjectProxy.getTempDirectory(), baseName, "." + ext);
            Files.copy(in, tempPath, StandardCopyOption.REPLACE_EXISTING);
            return tempPath;
        } catch (IOException e) {
            throw e;
        } finally {
            IOUtils.closeQuietly(in);
        }

    }

    private Dimension getDimension(InputStream imageIn) {
        try {
            return UserObjectProxy.getDimension(imageIn);
        } catch (IOException e) {
            return null;
        }
    }

    private boolean checkImageExtension(String path, boolean imageOnly, Set<String> imageExtensions)
            throws FilemanagerException {
        String imgExt = FilenameUtils.getExtension(path);
        boolean isImgExt = imageExtensions.contains(imgExt);
        if (imageOnly && !isImgExt)
            throw new FilemanagerException(FilemanagerAction.UPLOAD, FilemanagerException.Key.UploadImagesOnly);
        return isImgExt;
    }

    private String getUniqueName(String backendPath, String name) throws C5CException {
        Set<GenericConnector.FileProperties> props = connector.getFolder(backendPath, false);
        Set<String> existingNames = new HashSet<>();
        for (GenericConnector.FileProperties fp : props) {
            existingNames.add(fp.getName());
        }
        return StringUtils.getUniqueName(existingNames, name);
    }

    private String getFileName(final Part part) {
        final String partHeader = part.getHeader("content-disposition");
        for (String content : partHeader.split(";")) {
            if (content.trim().startsWith("filename")) {
                return content.substring(content.indexOf('=') + 1).trim().replace("\"", "");
            }
        }
        return null;
    }
}