This is an automated email from the ASF dual-hosted git repository.

remm pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git


The following commit(s) were added to refs/heads/11.0.x by this push:
     new f99dee941d Locking must check parent collections
f99dee941d is described below

commit f99dee941d463a2b43e3a01d48675ad170d738e2
Author: remm <r...@apache.org>
AuthorDate: Wed Oct 16 10:17:22 2024 +0200

    Locking must check parent collections
    
    Code cleanup.
    Call if processing in nearly all methods.
    Update fixmes.
    Update test for collection lock behavior fix.
---
 .../apache/catalina/servlets/WebdavServlet.java    | 118 +++++++++++++--------
 .../catalina/servlets/TestWebdavServlet.java       |  63 ++++++-----
 webapps/docs/changelog.xml                         |   4 +
 3 files changed, 118 insertions(+), 67 deletions(-)

diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java 
b/java/org/apache/catalina/servlets/WebdavServlet.java
index 59814d8150..ef399b27f6 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -403,11 +403,13 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
     protected boolean checkIfHeaders(HttpServletRequest request, 
HttpServletResponse response, WebResource resource)
             throws IOException {
 
-        if (!super.checkIfHeaders(request, response, resource)) {
+        // Skip regular HTTP evaluation for a null resource
+        if (resource != null && !super.checkIfHeaders(request, response, 
resource)) {
             return false;
         }
 
-        // TODO : Checking the WebDAV If header
+        // FIXME : Process the WebDAV If header
+
         return true;
     }
 
@@ -483,7 +485,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         }
 
         // Properties which are to be displayed.
-        List<String> properties = null;
+        List<String> properties = new ArrayList<>();
         // Propfind depth
         int depth = maxDepth;
         // Propfind type
@@ -503,8 +505,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             }
         }
 
-        Node propNode = null;
-
         if (req.getContentLengthLong() > 0) {
             DocumentBuilder documentBuilder = getDocumentBuilder();
 
@@ -528,7 +528,22 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                             String nodeName = getDAVNode(currentNode);
                             if ("prop".equals(nodeName)) {
                                 type = FIND_BY_PROPERTY;
-                                propNode = currentNode;
+                                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:
+                                            // href is a live property which 
is handled differently
+                                            String propertyName = 
getDAVNode(currentNode2);
+                                            // No support for non DAV: 
properties
+                                            if (propertyName != null) {
+                                                properties.add(propertyName);
+                                            }
+                                            break;
+                                    }
+                                }
                             }
                             if ("propname".equals(nodeName)) {
                                 type = FIND_PROPERTY_NAMES;
@@ -546,30 +561,10 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             }
         }
 
-        if (type == FIND_BY_PROPERTY) {
-            properties = new ArrayList<>();
-            // propNode must be non-null if type == FIND_BY_PROPERTY
-            @SuppressWarnings("null")
-            NodeList childList = propNode.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:
-                        // href is a live property which is handled differently
-                        String propertyName = getDAVNode(currentNode);
-                        // No support for non DAV: properties
-                        if (propertyName != null) {
-                            properties.add(propertyName);
-                        }
-                        break;
-                }
-            }
-        }
-
         WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return;
+        }
 
         if (!resource.exists()) {
             int slash = path.lastIndexOf('/');
@@ -672,7 +667,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
 
     /**
-     * PROPPATCH Method.
+     * PROPPATCH Method. Dead properties support is a SHOULD in the 
specification and are not implemented.
      *
      * @param req  The Servlet request
      * @param resp The Servlet response
@@ -681,18 +676,25 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
      */
     protected void doProppatch(HttpServletRequest req, HttpServletResponse 
resp) throws IOException {
 
+        String path = getRelativePath(req);
+
+        WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return;
+        }
+
         if (readOnly) {
             resp.sendError(WebdavStatus.SC_FORBIDDEN);
             return;
         }
 
-        if (isLocked(null, req)) {
+        if (isLocked(path, req)) {
             resp.sendError(WebdavStatus.SC_LOCKED);
             return;
         }
 
-        // FIXME
-        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
+        // FIXME: Compliant PROPPATCH processing
+        resp.sendError(HttpServletResponse.SC_FORBIDDEN);
     }
 
 
@@ -710,6 +712,9 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         String path = getRelativePath(req);
 
         WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return;
+        }
 
         // Can't create a collection if a resource already exists at the given
         // path
@@ -769,6 +774,9 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         }
 
         WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return;
+        }
         if (resource.isDirectory()) {
             sendNotAllowed(req, resp);
             return;
@@ -852,6 +860,10 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             resp.sendError(WebdavStatus.SC_LOCKED);
             return;
         }
+        WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return;
+        }
 
         LockInfo lock = new LockInfo(maxDepth);
 
@@ -1032,10 +1044,23 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
         lock.path = path;
 
-        WebResource resource = resources.getResource(path);
-
         if (lockRequestType == LOCK_CREATION) {
 
+            // Check if a parent is already locked
+            Iterator<LockInfo> collectionLocksIterator = 
collectionLocks.iterator();
+            while (collectionLocksIterator.hasNext()) {
+                LockInfo currentLock = collectionLocksIterator.next();
+                if (currentLock.hasExpired()) {
+                    collectionLocksIterator.remove();
+                    continue;
+                }
+                if (lock.path.startsWith(currentLock.path + "/") && 
(currentLock.isExclusive() || lock.isExclusive())) {
+                    // A parent collection of this collection is locked
+                    resp.setStatus(WebdavStatus.SC_LOCKED);
+                    return;
+                }
+            }
+
             // Generating lock id
             String lockTokenStr = req.getServletPath() + "-" + lock.type + "-" 
+ lock.scope + "-" +
                     req.getUserPrincipal() + "-" + lock.depth + "-" + 
lock.owner + "-" + lock.tokens + "-" +
@@ -1050,14 +1075,14 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                 // Checking if a child resource of this collection is
                 // already locked
                 List<String> lockPaths = new ArrayList<>();
-                Iterator<LockInfo> collectionLocksIterator = 
collectionLocks.iterator();
+                collectionLocksIterator = collectionLocks.iterator();
                 while (collectionLocksIterator.hasNext()) {
                     LockInfo currentLock = collectionLocksIterator.next();
                     if (currentLock.hasExpired()) {
                         collectionLocksIterator.remove();
                         continue;
                     }
-                    if (currentLock.path.startsWith(lock.path) && 
(currentLock.isExclusive() || lock.isExclusive())) {
+                    if (currentLock.path.startsWith(lock.path + "/") && 
(currentLock.isExclusive() || lock.isExclusive())) {
                         // A child collection of this collection is locked
                         lockPaths.add(currentLock.path);
                     }
@@ -1067,7 +1092,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                         resourceLocks.remove(currentLock.path);
                         continue;
                     }
-                    if (currentLock.path.startsWith(lock.path) && 
(currentLock.isExclusive() || lock.isExclusive())) {
+                    if (currentLock.path.startsWith(lock.path + "/") && 
(currentLock.isExclusive() || lock.isExclusive())) {
                         // A child resource of this collection is locked
                         lockPaths.add(currentLock.path);
                     }
@@ -1078,7 +1103,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                     // One of the child paths was locked
                     // We generate a multistatus error report
 
-                    resp.setStatus(WebdavStatus.SC_CONFLICT);
+                    resp.setStatus(WebdavStatus.SC_MULTI_STATUS);
 
                     XMLWriter generatedXML = new XMLWriter();
                     generatedXML.writeXMLHeader();
@@ -1247,9 +1272,8 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         }
 
         String path = getRelativePath(req);
-
-        if (isLocked(path, req)) {
-            resp.sendError(WebdavStatus.SC_LOCKED);
+        WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
             return;
         }
 
@@ -1310,7 +1334,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
     /**
      * Check to see if a resource is currently write locked. The method will 
look at the "If" header to make sure the
-     * client has give the appropriate lock tokens.
+     * client has demonstrated knowledge of the appropriate lock tokens.
      *
      * @param path The relative path
      * @param req Servlet request
@@ -1329,6 +1353,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             ifHeader = "";
         }
 
+        // Normally a lock token is not sent using this header
         String lockTokenHeader = req.getHeader("Lock-Token");
         if (lockTokenHeader == null) {
             lockTokenHeader = "";
@@ -1413,6 +1438,9 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             resp.sendError(WebdavStatus.SC_NOT_FOUND);
             return false;
         }
+        if (!checkIfHeaders(req, resp, source)) {
+            return false;
+        }
 
         // Parsing destination header
         // See RFC 4918
@@ -1661,6 +1689,10 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
      * @throws IOException If an IO error occurs
      */
     private boolean deleteResource(String path, HttpServletRequest req, 
HttpServletResponse resp) throws IOException {
+        WebResource resource = resources.getResource(path);
+        if (!checkIfHeaders(req, resp, resource)) {
+            return false;
+        }
         return deleteResource(path, req, resp, true);
     }
 
diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java 
b/test/org/apache/catalina/servlets/TestWebdavServlet.java
index 154305b504..a3d3056a84 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -380,7 +380,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
         client.setRequest(new String[] { "PUT /myfolder/file4.txt HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
                 "Content-Length: 6" + SimpleHttpClient.CRLF +
-                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
+                "If: " + lockToken + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF + CONTENT });
         client.connect();
@@ -397,10 +397,19 @@ public class TestWebdavServlet extends TomcatBaseTest {
         client.processRequest(true);
         Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode());
 
+        // Unlock /myfolder
+        client.setRequest(new String[] { "UNLOCK /myfolder HTTP/1.1" + 
SimpleHttpClient.CRLF +
+                "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
+                "Connection: Close" + SimpleHttpClient.CRLF +
+                SimpleHttpClient.CRLF });
+        client.connect();
+        client.processRequest(true);
+        Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, 
client.getStatusCode());
+
         client.setRequest(new String[] { "LOCK /myfolder/file5.txt HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
                 "Content-Length: " + LOCK_BODY.length() + 
SimpleHttpClient.CRLF +
-                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF + LOCK_BODY });
         client.connect();
@@ -415,22 +424,11 @@ public class TestWebdavServlet extends TomcatBaseTest {
         }
         Assert.assertNotNull(lockTokenFile);
 
-        // Try to add /myfolder/file5.txt to myfolder with lock token but 
without lock null
-        client.setRequest(new String[] { "PUT /myfolder/file5.txt HTTP/1.1" + 
SimpleHttpClient.CRLF +
-                "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
-                "Content-Length: 6" + SimpleHttpClient.CRLF +
-                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
-                "Connection: Close" + SimpleHttpClient.CRLF +
-                SimpleHttpClient.CRLF + CONTENT });
-        client.connect();
-        client.processRequest(true);
-        Assert.assertEquals(WebdavStatus.SC_LOCKED, client.getStatusCode());
-
         // Same but with lock token and lock null
         client.setRequest(new String[] { "PUT /myfolder/file5.txt HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
                 "Content-Length: 6" + SimpleHttpClient.CRLF +
-                "Lock-Token: " + lockToken + ", " + lockTokenFile + 
SimpleHttpClient.CRLF +
+                "If: " + lockTokenFile + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF + CONTENT });
         client.connect();
@@ -441,13 +439,30 @@ public class TestWebdavServlet extends TomcatBaseTest {
         client.setRequest(new String[] { "UNLOCK /myfolder/file5.txt HTTP/1.1" 
+ SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
                 "Lock-Token: " + lockTokenFile + SimpleHttpClient.CRLF +
-                "If: " + lockToken + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF });
         client.connect();
         client.processRequest(true);
         Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, 
client.getStatusCode());
 
+        // Lock /myfolder again
+        client.setRequest(new String[] { "LOCK /myfolder HTTP/1.1" + 
SimpleHttpClient.CRLF +
+                "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+                "Content-Length: " + LOCK_BODY.length() + 
SimpleHttpClient.CRLF +
+                "Connection: Close" + SimpleHttpClient.CRLF +
+                SimpleHttpClient.CRLF + LOCK_BODY });
+        client.connect();
+        client.processRequest(true);
+        Assert.assertEquals(HttpServletResponse.SC_OK, client.getStatusCode());
+        
Assert.assertTrue(client.getResponseBody().contains("opaquelocktoken:"));
+        lockToken = null;
+        for (String header : client.getResponseHeaders()) {
+            if (header.startsWith("Lock-Token: ")) {
+                lockToken = header.substring("Lock-Token: ".length());
+            }
+        }
+        Assert.assertNotNull(lockToken);
+
         // Copy /myfolder/file5.txt to /myfolder/file6.txt without lock 
(should not work)
         client.setRequest(new String[] { "COPY /myfolder/file5.txt HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
@@ -478,26 +493,26 @@ public class TestWebdavServlet extends TomcatBaseTest {
         client.processRequest(true);
         Assert.assertEquals(HttpServletResponse.SC_CREATED, 
client.getStatusCode());
 
-        // Unlock /myfolder
-        client.setRequest(new String[] { "UNLOCK /myfolder HTTP/1.1" + 
SimpleHttpClient.CRLF +
+        // Verify that everything created is there
+        client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
-                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF });
         client.connect();
         client.processRequest(true);
-        Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, 
client.getStatusCode());
+        Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, 
client.getStatusCode());
+        
Assert.assertFalse(client.getResponseBody().contains("/myfolder/file4.txt"));
+        Assert.assertTrue(client.getResponseBody().contains("/file7.txt"));
 
-        // Verify that everything created is there
-        client.setRequest(new String[] { "PROPFIND / HTTP/1.1" + 
SimpleHttpClient.CRLF +
+        // Unlock /myfolder again
+        client.setRequest(new String[] { "UNLOCK /myfolder HTTP/1.1" + 
SimpleHttpClient.CRLF +
                 "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+                "Lock-Token: " + lockToken + SimpleHttpClient.CRLF +
                 "Connection: Close" + SimpleHttpClient.CRLF +
                 SimpleHttpClient.CRLF });
         client.connect();
         client.processRequest(true);
-        Assert.assertEquals(WebdavStatus.SC_MULTI_STATUS, 
client.getStatusCode());
-        
Assert.assertFalse(client.getResponseBody().contains("/myfolder/file4.txt"));
-        Assert.assertTrue(client.getResponseBody().contains("/file7.txt"));
+        Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, 
client.getStatusCode());
 
         // Delete /myfolder
         client.setRequest(new String[] { "DELETE /myfolder HTTP/1.1" + 
SimpleHttpClient.CRLF +
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index 6e5a27ba43..d9dbd5c979 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -152,6 +152,10 @@
       <fix>
         Enforce <code>DAV:</code> namespace on WebDAV XML elements. (remm)
       </fix>
+      <fix>
+        Do not allow a new WebDAV lock on a child resource if a parent
+        collection is locked (RFC 4918 section 6.1). (remm)
+      </fix>
     </changelog>
   </subsection>
   <subsection name="Coyote">


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to