in java/org/apache/catalina/servlets/WebdavServlet.java [1326:1737]
protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
if (isReadOnly()) {
resp.sendError(WebdavStatus.SC_FORBIDDEN);
return;
}
String path = getRelativePath(req);
WebResource resource = resources.getResource(path);
if (!checkIfHeaders(req, resp, resource)) {
resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
return;
}
LockInfo lock = new LockInfo(maxDepth);
lock.principal = req.getRemoteUser();
// Parsing lock request
// Parsing depth header
String depthStr = req.getHeader("Depth");
if (depthStr == null) {
lock.depth = maxDepth;
} else {
if (depthStr.equals("0")) {
lock.depth = 0;
} else if (depthStr.equals("infinity")) {
lock.depth = maxDepth;
} else {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
}
// Parsing timeout header
int lockDuration = DEFAULT_TIMEOUT;
String lockDurationStr = req.getHeader("Timeout");
if (lockDurationStr != null) {
if (lockDurationStr.startsWith("Second-")) {
try {
lockDuration = Integer.parseInt(lockDurationStr.substring("Second-".length()));
} catch (NumberFormatException e) {
// Ignore
}
} else if (lockDurationStr.equals("Infinite")) {
lockDuration = MAX_TIMEOUT;
}
if (lockDuration == 0) {
lockDuration = DEFAULT_TIMEOUT;
}
if (lockDuration > MAX_TIMEOUT) {
lockDuration = MAX_TIMEOUT;
}
}
lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000L);
boolean lockCreation = false;
Node lockInfoNode = null;
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 (!"lockinfo".equals(getDAVNode(rootElement))) {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
lockInfoNode = rootElement;
lockCreation = true;
} catch (IOException | SAXException e) {
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
}
if (lockInfoNode != null) {
// Reading lock information
NodeList childList = lockInfoNode.getChildNodes();
StringWriter strWriter;
DOMWriter domWriter;
Node lockScopeNode = null;
Node lockTypeNode = null;
Node lockOwnerNode = null;
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:
if ("lockscope".equals(getDAVNode(currentNode))) {
lockScopeNode = currentNode;
}
if ("locktype".equals(getDAVNode(currentNode))) {
lockTypeNode = currentNode;
}
if ("owner".equals(getDAVNode(currentNode))) {
lockOwnerNode = currentNode;
}
break;
}
}
if (lockScopeNode != null) {
childList = lockScopeNode.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:
lock.scope = getDAVNode(currentNode);
break;
}
}
if (lock.scope == null) {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
} else {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
if (lockTypeNode != null) {
childList = lockTypeNode.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:
lock.type = getDAVNode(currentNode);
break;
}
}
if (lock.type == null) {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
} else {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
if (lockOwnerNode != null) {
childList = lockOwnerNode.getChildNodes();
for (int i = 0; i < childList.getLength(); i++) {
Node currentNode = childList.item(i);
switch (currentNode.getNodeType()) {
case Node.TEXT_NODE:
lock.owner += currentNode.getNodeValue();
break;
case Node.ELEMENT_NODE:
strWriter = new StringWriter();
domWriter = new DOMWriter(strWriter);
domWriter.print(currentNode);
lock.owner += strWriter.toString();
break;
}
}
if (lock.owner == null) {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
} else {
lock.owner = "";
}
}
lock.path = path;
if (lockCreation) {
// Check if the resource or a parent is already locked
String parentPath = path;
do {
LockInfo parentLock = resourceLocks.get(parentPath);
if (parentLock != null) {
if (parentLock.hasExpired()) {
resourceLocks.remove(parentPath);
} else if (parentLock.isExclusive() || lock.isExclusive()) {
// A parent collection of this collection is locked
resp.setStatus(WebdavStatus.SC_LOCKED);
return;
}
}
int slash = parentPath.lastIndexOf('/');
if (slash < 0) {
break;
}
parentPath = parentPath.substring(0, slash);
} while (true);
// Generating lock id
lock.token = UUID.randomUUID().toString();
if (resource.isDirectory() && lock.depth == maxDepth) {
// Locking a collection (and all its member resources)
// Checking if a child resource of this collection is
// already locked
// Note: it is likely faster in many cases to go over the full lock list than trying to go over all the
// children (recursively)
List<String> lockPaths = new ArrayList<>();
for (LockInfo currentLock : resourceLocks.values()) {
if (currentLock.hasExpired()) {
resourceLocks.remove(currentLock.path);
continue;
}
if ((currentLock.isExclusive() || lock.isExclusive()) &&
currentLock.path.startsWith(lock.path + "/")) {
// A child resource of this collection is locked
lockPaths.add(currentLock.lockroot);
}
}
if (!lockPaths.isEmpty()) {
// One of the child paths was locked
// We generate a multistatus error report
resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeXMLHeader();
generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING);
generatedXML.writeElement("D", "response", XMLWriter.OPENING);
generatedXML.writeElement("D", "href", XMLWriter.OPENING);
generatedXML.writeText(getEncodedPath(path, resource, req));
generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
generatedXML.writeElement("D", "status", XMLWriter.OPENING);
generatedXML.writeText("HTTP/1.1 " + WebdavStatus.SC_FAILED_DEPENDENCY + " ");
generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
for (String lockPath : lockPaths) {
generatedXML.writeElement("D", "response", XMLWriter.OPENING);
generatedXML.writeElement("D", "href", XMLWriter.OPENING);
generatedXML.writeText(lockPath);
generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
generatedXML.writeElement("D", "status", XMLWriter.OPENING);
generatedXML.writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED + " ");
generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
}
generatedXML.writeElement("D", "multistatus", XMLWriter.CLOSING);
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.close();
return;
}
} else {
// Locking a single resource
// Checking if a resource exists at this path
if (!resource.exists()) {
// RFC 4918 removes lock null, instead an empty file is created
if (!resources.write(path, new ByteArrayInputStream(new byte[0]), false)) {
resp.sendError(WebdavStatus.SC_CONFLICT);
return;
} else {
resp.setStatus(HttpServletResponse.SC_CREATED);
}
}
}
lock.lockroot = getEncodedPath(lock.path, resource, req);
if (lock.isExclusive()) {
resourceLocks.put(path, lock);
} else {
// Checking if there is already a shared lock on this path
LockInfo sharedLock = resourceLocks.get(path);
if (sharedLock == null) {
sharedLock = new LockInfo(maxDepth);
sharedLock.scope = "shared";
sharedLock.path = path;
sharedLock.lockroot = lock.lockroot;
sharedLock.depth = maxDepth;
resourceLocks.put(path, sharedLock);
}
sharedLock.sharedTokens.add(lock.token);
sharedLocks.put(lock.token, lock);
}
// Add the Lock-Token header as by RFC 2518 8.10.1
resp.addHeader("Lock-Token", "<" + LOCK_SCHEME + lock.token + ">");
}
if (!lockCreation) {
String ifHeader = req.getHeader("If");
if (ifHeader == null) {
// Bad request
resp.sendError(WebdavStatus.SC_BAD_REQUEST);
return;
}
LockInfo toRenew = null;
String parentPath = path;
do {
LockInfo parentLock = resourceLocks.get(parentPath);
if (parentLock != null) {
if (parentLock.hasExpired()) {
resourceLocks.remove(parentPath);
} else {
// parentPath == currentPath is a check for the first loop
if (parentPath == path || parentLock.depth > 0) {
if (parentLock.isExclusive()) {
if (ifHeader.contains(":" + parentLock.token + ">") && (parentLock.principal == null ||
parentLock.principal.equals(req.getRemoteUser()))) {
toRenew = parentLock;
break;
}
} else {
for (String token : parentLock.sharedTokens) {
if (ifHeader.contains(":" + token + ">")) {
LockInfo sharedLock = sharedLocks.get(token);
if (sharedLock != null && (sharedLock.principal == null ||
sharedLock.principal.equals(req.getRemoteUser()))) {
if (parentPath == path || sharedLock.depth > 0) {
toRenew = sharedLock;
break;
}
}
}
}
}
}
}
}
int slash = parentPath.lastIndexOf('/');
if (slash < 0) {
break;
}
parentPath = parentPath.substring(0, slash);
} while (true);
if (toRenew != null) {
if (!toRenew.hasExpired()) {
toRenew.expiresAt = lock.expiresAt;
} else {
toRenew = null;
}
}
lock = toRenew;
}
// Set the status, then generate the XML response containing
// the lock information
XMLWriter generatedXML = new XMLWriter();
generatedXML.writeXMLHeader();
generatedXML.writeElement("D", DEFAULT_NAMESPACE, "prop", XMLWriter.OPENING);
generatedXML.writeElement("D", "lockdiscovery", XMLWriter.OPENING);
if (lock != null) {
lock.toXML(generatedXML);
}
generatedXML.writeElement("D", "lockdiscovery", XMLWriter.CLOSING);
generatedXML.writeElement("D", "prop", XMLWriter.CLOSING);
resp.setContentType("text/xml; charset=UTF-8");
Writer writer = resp.getWriter();
writer.write(generatedXML.toString());
writer.close();
}