private boolean copyResource()

in java/org/apache/catalina/servlets/WebdavServlet.java [2015:2181]


    private boolean copyResource(String path, HttpServletRequest req, HttpServletResponse resp) throws IOException {

        // Check the source exists
        WebResource source = resources.getResource(path);
        if (!source.exists()) {
            resp.sendError(WebdavStatus.SC_NOT_FOUND);
            return false;
        }
        if (!checkIfHeaders(req, resp, source)) {
            resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
            return false;
        }

        // Parsing destination header
        // See RFC 4918
        String destinationHeader = req.getHeader("Destination");

        if (destinationHeader == null || destinationHeader.isEmpty()) {
            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
            return false;
        }

        URI destinationUri;
        try {
            destinationUri = new URI(destinationHeader);
        } catch (URISyntaxException e) {
            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
            return false;
        }

        String destinationPath = destinationUri.getPath();

        // Destination isn't allowed to use '.' or '..' segments
        if (!destinationPath.equals(RequestUtil.normalize(destinationPath))) {
            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
            return false;
        }

        if (destinationUri.isAbsolute()) {
            // Scheme and host need to match
            if (!req.getScheme().equals(destinationUri.getScheme()) ||
                    !req.getServerName().equals(destinationUri.getHost())) {
                resp.sendError(WebdavStatus.SC_FORBIDDEN);
                return false;
            }
            // Port needs to match too but handled separately as the logic is a
            // little more complicated
            if (req.getServerPort() != destinationUri.getPort()) {
                if (destinationUri.getPort() == -1 && ("http".equals(req.getScheme()) && req.getServerPort() == 80 ||
                        "https".equals(req.getScheme()) && req.getServerPort() == 443)) {
                    // All good.
                } else {
                    resp.sendError(WebdavStatus.SC_FORBIDDEN);
                    return false;
                }
            }
        }

        if (destinationPath.length() > 1 && destinationPath.endsWith("/")) {
            destinationPath = destinationPath.substring(0, destinationPath.length() - 1);
        }

        // Cross-context operations aren't supported
        String reqContextPath = getPathPrefix(req);
        String expectedTargetPath = reqContextPath;
        // Also ensure copy (and move) operations do not escape the configured sub-path when limited to the sub-path
        if (serveSubpathOnly && req.getServletPath() != null) {
            expectedTargetPath = expectedTargetPath + req.getServletPath();
        }
        if (!destinationPath.startsWith(expectedTargetPath + "/")) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        // Remove context path & servlet path
        destinationPath = destinationPath.substring(reqContextPath.length());

        if (debug > 0) {
            log("Dest path: " + destinationPath);
        }

        // Check destination path to protect special subdirectories
        if (isSpecialPath(destinationPath)) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        if (destinationPath.equals(path)) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        // Check src / dest are not sub-dirs of each other
        if (destinationPath.startsWith(path) && destinationPath.charAt(path.length()) == '/' ||
                path.startsWith(destinationPath) && path.charAt(destinationPath.length()) == '/') {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        // Check if destination is locked
        if (isLocked(destinationPath, req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return false;
        }

        boolean overwrite = true;
        String overwriteHeader = req.getHeader("Overwrite");
        if (overwriteHeader != null) {
            overwrite = overwriteHeader.equalsIgnoreCase("T");
        }

        // Overwriting the destination
        WebResource destination = resources.getResource(destinationPath);
        if (overwrite) {
            // Delete destination resource, if it exists
            if (destination.exists()) {
                if (!deleteResource(destinationPath, req, resp, true)) {
                    return false;
                }
            } else {
                resp.setStatus(WebdavStatus.SC_CREATED);
            }
        } else {
            // If the destination exists, then it's a conflict
            if (destination.exists()) {
                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                return false;
            }
        }

        // Copying source to destination

        Map<String,Integer> errorList = new LinkedHashMap<>();

        boolean infiniteCopy = true;
        String depthHeader = req.getHeader("Depth");
        if (depthHeader != null) {
            if (depthHeader.equals("infinity")) {
                // NO-OP - this is the default
            } else if (depthHeader.equals("0")) {
                infiniteCopy = false;
            } else {
                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                return false;
            }
        }

        boolean result = copyResource(errorList, path, destinationPath, infiniteCopy);

        if ((!result) || (!errorList.isEmpty())) {
            if (errorList.size() == 1) {
                resp.sendError(errorList.values().iterator().next().intValue());
            } else {
                sendReport(req, resp, errorList);
            }
            return false;
        }

        // Copy was successful
        if (destination.exists()) {
            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
        } else {
            resp.setStatus(WebdavStatus.SC_CREATED);
        }

        return true;
    }