in java/org/apache/catalina/servlets/WebdavServlet.java [803:997]
protected void doPropfind(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (!isListings()) {
sendNotAllowed(req, resp);
return;
}
String path = getRelativePath(req);
// Properties which are to be displayed.
List<Node> properties = new ArrayList<>();
// Propfind depth
int depth;
// Propfind type
PropfindType type = null;
String depthStr = req.getHeader("Depth");
if (depthStr == null) {
depth = maxDepth;
} else {
switch (depthStr) {
case "0" -> depth = 0;
case "1" -> depth = 1;
case "infinity" -> depth = maxDepth;
default -> {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
}
}
byte[] body;
try (InputStream is = req.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
IOTools.flow(is, os);
body = os.toByteArray();
} catch (IOException e) {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
if (body.length > 0) {
DocumentBuilder documentBuilder = getDocumentBuilder();
try {
Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(body)));
// Get the root element of the document
Element rootElement = document.getDocumentElement();
if (!"propfind".equals(getDAVNode(rootElement))) {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
NodeList childList = rootElement.getChildNodes();
for (int i = 0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
String nodeName = getDAVNode(currentNode);
if ("prop".equals(nodeName)) {
if (type != null) {
// Another was already defined
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
type = PropfindType.FIND_BY_PROPERTY;
NodeList propChildList = currentNode.getChildNodes();
for (int j = 0; j < propChildList.getLength(); j++) {
Node currentNode2 = propChildList.item(j);
switch (currentNode2.getNodeType()) {
case Node.TEXT_NODE:
break;
case Node.ELEMENT_NODE:
properties.add(currentNode2);
break;
}
}
}
if ("propname".equals(nodeName)) {
if (type != null) {
// Another was already defined
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
type = PropfindType.FIND_PROPERTY_NAMES;
}
if ("allprop".equals(nodeName)) {
if (type != null) {
// Another was already defined
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
type = PropfindType.FIND_ALL_PROP;
}
break;
}
}
} catch (SAXException | IOException e) {
// Something went wrong - bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
if (type == null) {
// Nothing meaningful in the propfind element
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
} else {
type = PropfindType.FIND_ALL_PROP;
}
WebResource resource = resources.getResource(path);
if (!checkIfHeaders(req, resp, resource)) {
resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
if (!resource.exists()) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
resp.setContentType("text/xml; charset=UTF-8");
// Create multistatus object
XMLWriter generatedXML = new XMLWriter(resp.getWriter());
generatedXML.writeXMLHeader();
generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING);
if (depth == 0) {
propfindResource(generatedXML, getEncodedPath(path, resource, req), path, type, properties,
resource.isFile(), resource.getCreation(), resource.getLastModified(), resource.getContentLength(),
getServletContext().getMimeType(resource.getName()), generateETag(resource));
} else {
// The stack always contains the object of the current level
Deque<String> stack = new ArrayDeque<>();
stack.addFirst(path);
// Stack of the objects one level below
Deque<String> stackBelow = new ArrayDeque<>();
while ((!stack.isEmpty()) && (depth >= 0)) {
String currentPath = stack.remove();
// Exclude any resource in the /WEB-INF and /META-INF subdirectories
if (isSpecialPath(currentPath)) {
continue;
}
resource = resources.getResource(currentPath);
// File is in directory listing but doesn't appear to exist
// Broken symlink or odd permission settings?
if (resource.exists()) {
propfindResource(generatedXML, getEncodedPath(currentPath, resource, req), currentPath, type,
properties, resource.isFile(), resource.getCreation(), resource.getLastModified(),
resource.getContentLength(), getServletContext().getMimeType(resource.getName()),
generateETag(resource));
}
if (resource.isDirectory() && (depth > 0)) {
String[] entries = resources.list(currentPath);
for (String entry : entries) {
String newPath = currentPath;
if (!(newPath.endsWith("/"))) {
newPath += "/";
}
newPath += entry;
stackBelow.addFirst(newPath);
}
}
if (stack.isEmpty()) {
depth--;
stack = stackBelow;
stackBelow = new ArrayDeque<>();
}
generatedXML.sendData();
}
}
generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING);
generatedXML.sendData();
}