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;
}