Refactor WebDAV servlet escaping for XML. Add test case.
diff --git a/java/org/apache/catalina/servlets/WebdavServlet.java b/java/org/apache/catalina/servlets/WebdavServlet.java
index 096ed5a..3844853 100644
--- a/java/org/apache/catalina/servlets/WebdavServlet.java
+++ b/java/org/apache/catalina/servlets/WebdavServlet.java
@@ -52,7 +52,6 @@
 import org.apache.catalina.WebResource;
 import org.apache.catalina.connector.RequestFacade;
 import org.apache.catalina.util.DOMWriter;
-import org.apache.catalina.util.URLEncoder;
 import org.apache.catalina.util.XMLWriter;
 import org.apache.tomcat.util.buf.HexUtils;
 import org.apache.tomcat.util.http.ConcurrentDateFormat;
@@ -140,15 +139,6 @@
 
     // -------------------------------------------------------------- Constants
 
-    private static final URLEncoder URL_ENCODER_XML;
-    static {
-        URL_ENCODER_XML = (URLEncoder) URLEncoder.DEFAULT.clone();
-        // Remove '&' from the safe character set since while it it permitted
-        // in a URI path, it is not permitted in XML and encoding it is a simple
-        // way to address this.
-        URL_ENCODER_XML.removeSafeCharacter('&');
-    }
-
     private static final String METHOD_PROPFIND = "PROPFIND";
     private static final String METHOD_PROPPATCH = "PROPPATCH";
     private static final String METHOD_MKCOL = "MKCOL";
@@ -391,18 +381,6 @@
 
 
     /**
-     * URL rewriter.
-     *
-     * @param path Path which has to be rewritten
-     * @return the rewritten path
-     */
-    @Override
-    protected String rewriteUrl(String path) {
-        return URL_ENCODER_XML.encode(path, StandardCharsets.UTF_8);
-    }
-
-
-    /**
      * Override the DefaultServlet implementation and only use the PathInfo. If
      * the ServletPath is non-null, it will be because the WebDAV servlet has
      * been mapped to a url other than /* to configure editing at different url
diff --git a/java/org/apache/catalina/util/XMLWriter.java b/java/org/apache/catalina/util/XMLWriter.java
index 019c4b9..8290b73 100644
--- a/java/org/apache/catalina/util/XMLWriter.java
+++ b/java/org/apache/catalina/util/XMLWriter.java
@@ -19,6 +19,8 @@
 import java.io.IOException;
 import java.io.Writer;
 
+import org.apache.tomcat.util.security.Escape;
+
 /**
  * XMLWriter helper class.
  */
@@ -199,7 +201,7 @@
      * @param text Text to append
      */
     public void writeText(String text) {
-        buffer.append(text);
+        buffer.append(Escape.xml(text));
     }
 
 
diff --git a/test/org/apache/catalina/servlets/TestWebdavServlet.java b/test/org/apache/catalina/servlets/TestWebdavServlet.java
index fdf61a0..0ce1683 100644
--- a/test/org/apache/catalina/servlets/TestWebdavServlet.java
+++ b/test/org/apache/catalina/servlets/TestWebdavServlet.java
@@ -18,18 +18,25 @@
 
 import java.io.File;
 import java.io.IOException;
+import java.io.StringReader;
 import java.util.List;
 import java.util.Map;
 
+import javax.xml.parsers.SAXParserFactory;
+
 import jakarta.servlet.http.HttpServletResponse;
 
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.catalina.Context;
+import org.apache.catalina.Wrapper;
+import org.apache.catalina.startup.SimpleHttpClient;
 import org.apache.catalina.startup.Tomcat;
 import org.apache.catalina.startup.TomcatBaseTest;
 import org.apache.tomcat.util.buf.ByteChunk;
 import org.apache.tomcat.websocket.server.WsContextListener;
+import org.xml.sax.InputSource;
 
 public class TestWebdavServlet extends TomcatBaseTest {
 
@@ -149,4 +156,45 @@
         return TomcatBaseTest.getUrl(path, out, resHead);
     }
 
+    /*
+     * Bug 66609
+     */
+    @Test
+    public void testDirectoryListing() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp");
+        Context ctxt = tomcat.addContext("", appDir.getAbsolutePath());
+
+        Wrapper defaultServlet = Tomcat.addServlet(ctxt, "webdav", new WebdavServlet());
+        defaultServlet.addInitParameter("listings", "true");
+
+        ctxt.addServletMappingDecoded("/*", "webdav");
+        ctxt.addMimeMapping("html", "text/html");
+
+        tomcat.start();
+
+        Client client = new Client();
+        client.setPort(getPort());
+        client.setRequest(new String[] { "PROPFIND /bug66609/ HTTP/1.1" + SimpleHttpClient.CRLF +
+                                         "Host: localhost:" + getPort() + SimpleHttpClient.CRLF +
+                                         SimpleHttpClient.CRLF});
+        client.connect();
+        client.sendRequest();
+
+        client.setUseContentLength(true);
+        client.readResponse(true);
+
+        // This will throw an exception if the XML is not valid
+        SAXParserFactory.newInstance().newSAXParser().getXMLReader().parse(new InputSource(new StringReader(client.getResponseBody())));
+    }
+
+
+    private static final class Client extends SimpleHttpClient {
+
+        @Override
+        public boolean isResponseBodyOK() {
+            return true;
+        }
+    }
 }