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

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

commit ccde0bedc978ba2a08e98d480df64e695a79e77d
Author: remm <r...@apache.org>
AuthorDate: Thu Oct 17 10:40:52 2024 +0200

    Remove WebDAV lock null support for compliance with RFC 4918
    
    Section 7.3 and annex D deprecate lock nulls and define a new behavior.
    http://www.webdav.org/specs/rfc4918.html#lock-null
    Instead a lock on a non existing resource will create an empty file
    locked with a regular lock, which is of course far simpler. When
    removing the lock, the corresponding file will not go away.
---
 .../apache/catalina/servlets/WebdavServlet.java    | 146 ++-------------------
 .../catalina/servlets/TestWebdavServlet.java       |   2 +-
 webapps/docs/changelog.xml                         |   5 +
 3 files changed, 16 insertions(+), 137 deletions(-)

diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java 
b/java/org/apache/catalina/servlets/WebdavServlet.java
index fe5fbfc123..6b79fe3498 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -16,6 +16,7 @@
  */
 package org.apache.catalina.servlets;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
@@ -222,16 +223,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
     private final ConcurrentHashMap<String,LockInfo> resourceLocks = new 
ConcurrentHashMap<>();
 
 
-    /**
-     * Repository of the lock-null resources.
-     * <p>
-     * Key : path of the collection containing the lock-null resource<br>
-     * Value : List of lock-null resource which are members of the collection. 
Each element of the List is the path
-     * associated with the lock-null resource.
-     */
-    private final ConcurrentHashMap<String,CopyOnWriteArrayList<String>> 
lockNullResources = new ConcurrentHashMap<>();
-
-
     /**
      * List of the inheritable collection locks.
      */
@@ -294,7 +285,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         for (LockInfo currentLock : resourceLocks.values()) {
             if (currentLock.hasExpired()) {
                 resourceLocks.remove(currentLock.path);
-                removeLockNull(currentLock.path);
             }
         }
         Iterator<LockInfo> collectionLocksIterator = 
collectionLocks.iterator();
@@ -302,7 +292,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             LockInfo currentLock = collectionLocksIterator.next();
             if (currentLock.hasExpired()) {
                 collectionLocksIterator.remove();
-                removeLockNull(currentLock.path);
             }
         }
     }
@@ -567,30 +556,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             return;
         }
 
-        if (!resource.exists()) {
-            int slash = path.lastIndexOf('/');
-            if (slash != -1) {
-                String parentPath = path.substring(0, slash);
-                List<String> currentLockNullResources = 
lockNullResources.get(parentPath);
-                if (currentLockNullResources != null) {
-                    for (String lockNullPath : currentLockNullResources) {
-                        if (lockNullPath.equals(path)) {
-                            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);
-                            parseLockNullProperties(req, generatedXML, 
lockNullPath, type, properties);
-                            generatedXML.writeElement("D", "multistatus", 
XMLWriter.CLOSING);
-                            generatedXML.sendData();
-                            return;
-                        }
-                    }
-                }
-            }
-        }
-
         if (!resource.exists()) {
             resp.sendError(HttpServletResponse.SC_NOT_FOUND);
             return;
@@ -635,18 +600,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                         stackBelow.addFirst(newPath);
                     }
 
-                    // Displaying the lock-null resources present in that
-                    // collection
-                    String lockPath = currentPath;
-                    if (lockPath.endsWith("/")) {
-                        lockPath = lockPath.substring(0, lockPath.length() - 
1);
-                    }
-                    List<String> currentLockNullResources = 
lockNullResources.get(lockPath);
-                    if (currentLockNullResources != null) {
-                        for (String lockNullPath : currentLockNullResources) {
-                            parseLockNullProperties(req, generatedXML, 
lockNullPath, type, properties);
-                        }
-                    }
                 }
 
                 if (stack.isEmpty()) {
@@ -742,8 +695,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
         if (resources.mkdir(path)) {
             resp.setStatus(WebdavStatus.SC_CREATED);
-            // Removing any lock-null resource which would be present
-            removeLockNull(path);
         } else {
             resp.sendError(WebdavStatus.SC_CONFLICT);
         }
@@ -785,8 +736,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
         super.doPut(req, resp);
 
-        // Removing any lock-null resource which would be present
-        removeLockNull(path);
     }
 
 
@@ -1190,12 +1139,11 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
                     // Checking if a resource exists at this path
                     if (!resource.exists()) {
-
-                        // "Creating" a lock-null resource
-                        int slash = lock.path.lastIndexOf('/');
-                        String parentPath = lock.path.substring(0, slash);
-
-                        lockNullResources.computeIfAbsent(parentPath, k -> new 
CopyOnWriteArrayList<>()).add(lock.path);
+                        // 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;
+                        }
                     }
 
                     lock.tokens.add(lockToken);
@@ -1307,8 +1255,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
             if (lock.tokens.isEmpty()) {
                 resourceLocks.remove(path);
-                // Removing any lock-null resource which would be present
-                removeLockNull(path);
             }
 
         }
@@ -1328,8 +1274,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                 }
                 if (lock.tokens.isEmpty()) {
                     collectionLocks.remove(lock);
-                    // Removing any lock-null resource which would be present
-                    removeLockNull(path);
                     break;
                 }
             }
@@ -1598,10 +1542,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
             resp.setStatus(WebdavStatus.SC_CREATED);
         }
 
-        // Removing any lock-null resource which would be present at
-        // the destination path
-        removeLockNull(destinationPath);
-
         return true;
     }
 
@@ -1946,52 +1886,14 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
 
         String rewrittenUrl = rewriteUrl(href);
 
-        generatePropFindResponse(generatedXML, rewrittenUrl, path, type, 
properties, resource.isFile(), false,
+        generatePropFindResponse(generatedXML, rewrittenUrl, path, type, 
properties, resource.isFile(),
                 resource.getCreation(), resource.getLastModified(), 
resource.getContentLength(),
                 getServletContext().getMimeType(resource.getName()), 
generateETag(resource));
     }
 
 
-    /**
-     * Propfind helper method. Displays the properties of a lock-null resource.
-     *
-     * @param req          The servlet request
-     * @param generatedXML XML response to the Propfind request
-     * @param path         Path of the current resource
-     * @param type         Propfind type
-     * @param properties   If the propfind type is find properties by name, 
then this List contains those properties
-     */
-    private void parseLockNullProperties(HttpServletRequest req, XMLWriter 
generatedXML, String path, int type,
-            List<String> properties) {
-
-        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
-        if (isSpecialPath(path)) {
-            return;
-        }
-
-        // Retrieving the lock associated with the lock-null resource
-        LockInfo lock = resourceLocks.get(path);
-
-        if (lock == null) {
-            return;
-        }
-
-        String absoluteUri = req.getRequestURI();
-        String relativePath = getRelativePath(req);
-        String toAppend = path.substring(relativePath.length());
-        if (!toAppend.startsWith("/")) {
-            toAppend = "/" + toAppend;
-        }
-
-        String rewrittenUrl = rewriteUrl(RequestUtil.normalize(absoluteUri + 
toAppend));
-
-        generatePropFindResponse(generatedXML, rewrittenUrl, path, type, 
properties, true, true,
-                lock.creationDate.getTime(), lock.creationDate.getTime(), 0, 
"", "");
-    }
-
-
     private void generatePropFindResponse(XMLWriter generatedXML, String 
rewrittenUrl, String path, int propFindType,
-            List<String> properties, boolean isFile, boolean isLockNull, long 
created, long lastModified,
+            List<String> properties, boolean isFile, long created, long 
lastModified,
             long contentLength, String contentType, String eTag) {
 
         generatedXML.writeElement("D", "response", XMLWriter.OPENING);
@@ -2026,13 +1928,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                         generatedXML.writeProperty("D", "getcontenttype", 
contentType);
                     }
                     generatedXML.writeProperty("D", "getetag", eTag);
-                    if (isLockNull) {
-                        generatedXML.writeElement("D", "resourcetype", 
XMLWriter.OPENING);
-                        generatedXML.writeElement("D", "lock-null", 
XMLWriter.NO_CONTENT);
-                        generatedXML.writeElement("D", "resourcetype", 
XMLWriter.CLOSING);
-                    } else {
-                        generatedXML.writeElement("D", "resourcetype", 
XMLWriter.NO_CONTENT);
-                    }
+                    generatedXML.writeElement("D", "resourcetype", 
XMLWriter.NO_CONTENT);
                 } else {
                     generatedXML.writeProperty("D", "getlastmodified", 
FastHttpDateFormat.formatDate(lastModified));
                     generatedXML.writeElement("D", "resourcetype", 
XMLWriter.OPENING);
@@ -2135,13 +2031,7 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
                         }
                     } else if (property.equals("resourcetype")) {
                         if (isFile) {
-                            if (isLockNull) {
-                                generatedXML.writeElement("D", "resourcetype", 
XMLWriter.OPENING);
-                                generatedXML.writeElement("D", "lock-null", 
XMLWriter.NO_CONTENT);
-                                generatedXML.writeElement("D", "resourcetype", 
XMLWriter.CLOSING);
-                            } else {
-                                generatedXML.writeElement("D", "resourcetype", 
XMLWriter.NO_CONTENT);
-                            }
+                            generatedXML.writeElement("D", "resourcetype", 
XMLWriter.NO_CONTENT);
                         } else {
                             generatedXML.writeElement("D", "resourcetype", 
XMLWriter.OPENING);
                             generatedXML.writeElement("D", "collection", 
XMLWriter.NO_CONTENT);
@@ -2282,21 +2172,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
     }
 
 
-    private void removeLockNull(String path) {
-        int slash = path.lastIndexOf('/');
-        if (slash >= 0) {
-            String parentPath = path.substring(0, slash);
-            List<String> paths = lockNullResources.get(parentPath);
-            if (paths != null) {
-                paths.remove(path);
-                if (paths.isEmpty()) {
-                    lockNullResources.remove(parentPath);
-                }
-            }
-        }
-    }
-
-
     private String getDAVNode(Node node) {
         if (node.getNamespaceURI().equals(DEFAULT_NAMESPACE)) {
             return node.getLocalName();
@@ -2330,7 +2205,6 @@ public class WebdavServlet extends DefaultServlet 
implements PeriodicEventListen
         String owner = "";
         List<String> tokens = Collections.synchronizedList(new ArrayList<>());
         long expiresAt = 0;
-        Date creationDate = new Date();
 
 
         // ----------------------------------------------------- Public Methods
diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java 
b/test/org/apache/catalina/servlets/TestWebdavServlet.java
index 22688b6ddd..df5cb2832b 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -442,7 +442,7 @@ public class TestWebdavServlet extends TomcatBaseTest {
                 SimpleHttpClient.CRLF + CONTENT });
         client.connect();
         client.processRequest(true);
-        Assert.assertEquals(HttpServletResponse.SC_CREATED, 
client.getStatusCode());
+        Assert.assertEquals(HttpServletResponse.SC_NO_CONTENT, 
client.getStatusCode());
 
         // Verify that this also removes the lock by doing another PUT without 
the token
         client.setRequest(new String[] { "DELETE /myfolder/file5.txt HTTP/1.1" 
+ SimpleHttpClient.CRLF +
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index ac3a13e769..953bba198a 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -160,6 +160,11 @@
         WebDAV Delete should remove any existing lock on successfully deleted
         resources. (remm)
       </fix>
+      <update>
+        Remove WebDAV lock null support in accordance with RFC 4918 section 7.3
+        and annex D. Instead a lock on a non existing resource will create an
+        empty file locked with a regular lock. (remm)
+      </update>
     </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