FileDocCategorySizeDatePackage
WebdavServlet.javaAPI DocApache Tomcat 6.0.14110740Fri Jul 20 04:20:34 BST 2007org.apache.catalina.servlets

WebdavServlet

public class WebdavServlet extends DefaultServlet
Servlet which adds support for WebDAV level 2. All the basic HTTP requests are handled by the DefaultServlet.
author
Remy Maucherat
version
$Revision: 543681 $ $Date: 2007-06-02 02:42:59 +0200 (sam., 02 juin 2007) $

Fields Summary
private static final String
METHOD_HEAD
private static final String
METHOD_PROPFIND
private static final String
METHOD_PROPPATCH
private static final String
METHOD_MKCOL
private static final String
METHOD_COPY
private static final String
METHOD_MOVE
private static final String
METHOD_LOCK
private static final String
METHOD_UNLOCK
private static final int
INFINITY
Default depth is infite.
private static final int
FIND_BY_PROPERTY
PROPFIND - Specify a property mask.
private static final int
FIND_ALL_PROP
PROPFIND - Display all properties.
private static final int
FIND_PROPERTY_NAMES
PROPFIND - Return property names.
private static final int
LOCK_CREATION
Create a new lock.
private static final int
LOCK_REFRESH
Refresh lock.
private static final int
DEFAULT_TIMEOUT
Default lock timeout value.
private static final int
MAX_TIMEOUT
Maximum lock timeout.
protected static final String
DEFAULT_NAMESPACE
Default namespace.
protected static final SimpleDateFormat
creationDateFormat
Simple date format for the creation date ISO representation (partial).
protected static MessageDigest
md5Helper
MD5 message digest provider.
protected static final org.apache.catalina.util.MD5Encoder
md5Encoder
The MD5 helper object for this class.
private Hashtable
resourceLocks
Repository of the locks put on single resources.

Key : path
Value : LockInfo

private Hashtable
lockNullResources
Repository of the lock-null resources.

Key : path of the collection containing the lock-null resource
Value : Vector of lock-null resource which are members of the collection. Each element of the Vector is the path associated with the lock-null resource.

private Vector
collectionLocks
Vector of the heritable locks.

Key : path
Value : LockInfo

private String
secret
Secret information used to generate reasonably secure lock ids.
Constructors Summary
Methods Summary
protected booleancheckIfHeaders(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, org.apache.naming.resources.ResourceAttributes resourceAttributes)
Check if the conditions specified in the optional If headers are satisfied.

param
request The servlet request we are processing
param
response The servlet response we are creating
param
resourceAttributes The resource information
return
boolean true if the resource meets all the specified conditions, and false if any of the conditions is not satisfied, in which case request processing is stopped


        if (!super.checkIfHeaders(request, response, resourceAttributes))
            return false;

        // TODO : Checking the WebDAV If header
        return true;

    
private booleancopyResource(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Copy a resource.

param
req Servlet request
param
resp Servlet response
return
boolean true if the copy is successful


        // Parsing destination header

        String destinationPath = req.getHeader("Destination");

        if (destinationPath == null) {
            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
            return false;
        }

        // Remove url encoding from destination
        destinationPath = RequestUtil.URLDecode(destinationPath, "UTF8");

        int protocolIndex = destinationPath.indexOf("://");
        if (protocolIndex >= 0) {
            // if the Destination URL contains the protocol, we can safely
            // trim everything upto the first "/" character after "://"
            int firstSeparator =
                destinationPath.indexOf("/", protocolIndex + 4);
            if (firstSeparator < 0) {
                destinationPath = "/";
            } else {
                destinationPath = destinationPath.substring(firstSeparator);
            }
        } else {
            String hostName = req.getServerName();
            if ((hostName != null) && (destinationPath.startsWith(hostName))) {
                destinationPath = destinationPath.substring(hostName.length());
            }

            int portIndex = destinationPath.indexOf(":");
            if (portIndex >= 0) {
                destinationPath = destinationPath.substring(portIndex);
            }

            if (destinationPath.startsWith(":")) {
                int firstSeparator = destinationPath.indexOf("/");
                if (firstSeparator < 0) {
                    destinationPath = "/";
                } else {
                    destinationPath =
                        destinationPath.substring(firstSeparator);
                }
            }
        }

        // Normalise destination path (remove '.' and '..')
        destinationPath = normalize(destinationPath);

        String contextPath = req.getContextPath();
        if ((contextPath != null) &&
            (destinationPath.startsWith(contextPath))) {
            destinationPath = destinationPath.substring(contextPath.length());
        }

        String pathInfo = req.getPathInfo();
        if (pathInfo != null) {
            String servletPath = req.getServletPath();
            if ((servletPath != null) &&
                (destinationPath.startsWith(servletPath))) {
                destinationPath = destinationPath
                    .substring(servletPath.length());
            }
        }

        if (debug > 0)
            log("Dest path :" + destinationPath);

        if ((destinationPath.toUpperCase().startsWith("/WEB-INF")) ||
            (destinationPath.toUpperCase().startsWith("/META-INF"))) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        String path = getRelativePath(req);

        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
            (path.toUpperCase().startsWith("/META-INF"))) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        if (destinationPath.equals(path)) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        // Parsing overwrite header

        boolean overwrite = true;
        String overwriteHeader = req.getHeader("Overwrite");

        if (overwriteHeader != null) {
            if (overwriteHeader.equalsIgnoreCase("T")) {
                overwrite = true;
            } else {
                overwrite = false;
            }
        }

        // Overwriting the destination

        boolean exists = true;
        try {
            resources.lookup(destinationPath);
        } catch (NamingException e) {
            exists = false;
        }

        if (overwrite) {

            // Delete destination resource, if it exists
            if (exists) {
                if (!deleteResource(destinationPath, req, resp, true)) {
                    return false;
                }
            } else {
                resp.setStatus(WebdavStatus.SC_CREATED);
            }

        } else {

            // If the destination exists, then it's a conflict
            if (exists) {
                resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                return false;
            }

        }

        // Copying source to destination

        Hashtable<String,Integer> errorList = new Hashtable<String,Integer>();

        boolean result = copyResource(resources, errorList,
                                      path, destinationPath);

        if ((!result) || (!errorList.isEmpty())) {

            sendReport(req, resp, errorList);
            return false;

        }

        // Removing any lock-null resource which would be present at
        // the destination path
        lockNullResources.remove(destinationPath);

        return true;

    
private booleancopyResource(javax.naming.directory.DirContext resources, java.util.Hashtable errorList, java.lang.String source, java.lang.String dest)
Copy a collection.

param
resources Resources implementation to be used
param
errorList Hashtable containing the list of errors which occurred during the copy operation
param
source Path of the resource to be copied
param
dest Destination path


        if (debug > 1)
            log("Copy: " + source + " To: " + dest);

        Object object = null;
        try {
            object = resources.lookup(source);
        } catch (NamingException e) {
        }

        if (object instanceof DirContext) {

            try {
                resources.createSubcontext(dest);
            } catch (NamingException e) {
                errorList.put
                    (dest, new Integer(WebdavStatus.SC_CONFLICT));
                return false;
            }

            try {
                NamingEnumeration enumeration = resources.list(source);
                while (enumeration.hasMoreElements()) {
                    NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
                    String childDest = dest;
                    if (!childDest.equals("/"))
                        childDest += "/";
                    childDest += ncPair.getName();
                    String childSrc = source;
                    if (!childSrc.equals("/"))
                        childSrc += "/";
                    childSrc += ncPair.getName();
                    copyResource(resources, errorList, childSrc, childDest);
                }
            } catch (NamingException e) {
                errorList.put
                    (dest, new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                return false;
            }

        } else {

            if (object instanceof Resource) {
                try {
                    resources.bind(dest, object);
                } catch (NamingException e) {
                    errorList.put
                        (source,
                         new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                    return false;
                }
            } else {
                errorList.put
                    (source,
                     new Integer(WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                return false;
            }

        }

        return true;

    
private voiddeleteCollection(javax.servlet.http.HttpServletRequest req, javax.naming.directory.DirContext resources, java.lang.String path, java.util.Hashtable errorList)
Deletes a collection.

param
resources Resources implementation associated with the context
param
path Path to the collection to be deleted
param
errorList Contains the list of the errors which occurred


        if (debug > 1)
            log("Delete:" + path);

        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
            (path.toUpperCase().startsWith("/META-INF"))) {
            errorList.put(path, new Integer(WebdavStatus.SC_FORBIDDEN));
            return;
        }

        String ifHeader = req.getHeader("If");
        if (ifHeader == null)
            ifHeader = "";

        String lockTokenHeader = req.getHeader("Lock-Token");
        if (lockTokenHeader == null)
            lockTokenHeader = "";

        Enumeration enumeration = null;
        try {
            enumeration = resources.list(path);
        } catch (NamingException e) {
            errorList.put(path, new Integer
                (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
            return;
        }

        while (enumeration.hasMoreElements()) {
            NameClassPair ncPair = (NameClassPair) enumeration.nextElement();
            String childName = path;
            if (!childName.equals("/"))
                childName += "/";
            childName += ncPair.getName();

            if (isLocked(childName, ifHeader + lockTokenHeader)) {

                errorList.put(childName, new Integer(WebdavStatus.SC_LOCKED));

            } else {

                try {
                    Object object = resources.lookup(childName);
                    if (object instanceof DirContext) {
                        deleteCollection(req, resources, childName, errorList);
                    }

                    try {
                        resources.unbind(childName);
                    } catch (NamingException e) {
                        if (!(object instanceof DirContext)) {
                            // If it's not a collection, then it's an unknown
                            // error
                            errorList.put
                                (childName, new Integer
                                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                        }
                    }
                } catch (NamingException e) {
                    errorList.put
                        (childName, new Integer
                            (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
                }
            }

        }

    
private booleandeleteResource(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Delete a resource.

param
req Servlet request
param
resp Servlet response
return
boolean true if the copy is successful


        String path = getRelativePath(req);

        return deleteResource(path, req, resp, true);

    
private booleandeleteResource(java.lang.String path, javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp, boolean setStatus)
Delete a resource.

param
path Path of the resource which is to be deleted
param
req Servlet request
param
resp Servlet response
param
setStatus Should the response status be set on successful completion


        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
            (path.toUpperCase().startsWith("/META-INF"))) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return false;
        }

        String ifHeader = req.getHeader("If");
        if (ifHeader == null)
            ifHeader = "";

        String lockTokenHeader = req.getHeader("Lock-Token");
        if (lockTokenHeader == null)
            lockTokenHeader = "";

        if (isLocked(path, ifHeader + lockTokenHeader)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return false;
        }

        boolean exists = true;
        Object object = null;
        try {
            object = resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
        }

        if (!exists) {
            resp.sendError(WebdavStatus.SC_NOT_FOUND);
            return false;
        }

        boolean collection = (object instanceof DirContext);

        if (!collection) {
            try {
                resources.unbind(path);
            } catch (NamingException e) {
                resp.sendError(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
                return false;
            }
        } else {

            Hashtable<String,Integer> errorList =
                new Hashtable<String,Integer>();

            deleteCollection(req, resources, path, errorList);
            try {
                resources.unbind(path);
            } catch (NamingException e) {
                errorList.put(path, new Integer
                    (WebdavStatus.SC_INTERNAL_SERVER_ERROR));
            }

            if (!errorList.isEmpty()) {

                sendReport(req, resp, errorList);
                return false;

            }

        }
        if (setStatus) {
            resp.setStatus(WebdavStatus.SC_NO_CONTENT);
        }
        return true;

    
private java.lang.StringBufferdetermineMethodsAllowed(javax.naming.directory.DirContext resources, javax.servlet.http.HttpServletRequest req)
Determines the methods normally allowed for the resource.


        StringBuffer methodsAllowed = new StringBuffer();
        boolean exists = true;
        Object object = null;
        try {
            String path = getRelativePath(req);

            object = resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
        }

        if (!exists) {
            methodsAllowed.append("OPTIONS, MKCOL, PUT, LOCK");
            return methodsAllowed;
        }

        methodsAllowed.append("OPTIONS, GET, HEAD, POST, DELETE, TRACE");
        methodsAllowed.append(", PROPPATCH, COPY, MOVE, LOCK, UNLOCK");

        if (listings) {
            methodsAllowed.append(", PROPFIND");
        }

        if (!(object instanceof DirContext)) {
            methodsAllowed.append(", PUT");
        }

        return methodsAllowed;
    
protected voiddoCopy(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
COPY Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        copyResource(req, resp);

    
protected voiddoDelete(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
DELETE Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        deleteResource(req, resp);

    
protected voiddoLock(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
LOCK Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        LockInfo lock = new LockInfo();

        // Parsing lock request

        // Parsing depth header

        String depthStr = req.getHeader("Depth");

        if (depthStr == null) {
            lock.depth = INFINITY;
        } else {
            if (depthStr.equals("0")) {
                lock.depth = 0;
            } else {
                lock.depth = INFINITY;
            }
        }

        // Parsing timeout header

        int lockDuration = DEFAULT_TIMEOUT;
        String lockDurationStr = req.getHeader("Timeout");
        if (lockDurationStr == null) {
            lockDuration = DEFAULT_TIMEOUT;
        } else {
            int commaPos = lockDurationStr.indexOf(",");
            // If multiple timeouts, just use the first
            if (commaPos != -1) {
                lockDurationStr = lockDurationStr.substring(0,commaPos);
            }
            if (lockDurationStr.startsWith("Second-")) {
                lockDuration =
                    (new Integer(lockDurationStr.substring(7))).intValue();
            } else {
                if (lockDurationStr.equalsIgnoreCase("infinity")) {
                    lockDuration = MAX_TIMEOUT;
                } else {
                    try {
                        lockDuration =
                            (new Integer(lockDurationStr)).intValue();
                    } catch (NumberFormatException e) {
                        lockDuration = MAX_TIMEOUT;
                    }
                }
            }
            if (lockDuration == 0) {
                lockDuration = DEFAULT_TIMEOUT;
            }
            if (lockDuration > MAX_TIMEOUT) {
                lockDuration = MAX_TIMEOUT;
            }
        }
        lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);

        int lockRequestType = LOCK_CREATION;

        Node lockInfoNode = null;

        DocumentBuilder documentBuilder = getDocumentBuilder();

        try {
            Document document = documentBuilder.parse(new InputSource
                (req.getInputStream()));

            // Get the root element of the document
            Element rootElement = document.getDocumentElement();
            lockInfoNode = rootElement;
        } catch (IOException e) {
            lockRequestType = LOCK_REFRESH;
        } catch (SAXException e) {
            lockRequestType = LOCK_REFRESH;
        }

        if (lockInfoNode != null) {

            // Reading lock information

            NodeList childList = lockInfoNode.getChildNodes();
            StringWriter strWriter = null;
            DOMWriter domWriter = null;

            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:
                    String nodeName = currentNode.getNodeName();
                    if (nodeName.endsWith("lockscope")) {
                        lockScopeNode = currentNode;
                    }
                    if (nodeName.endsWith("locktype")) {
                        lockTypeNode = currentNode;
                    }
                    if (nodeName.endsWith("owner")) {
                        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:
                        String tempScope = currentNode.getNodeName();
                        if (tempScope.indexOf(':") != -1) {
                            lock.scope = tempScope.substring
                                (tempScope.indexOf(':") + 1);
                        } else {
                            lock.scope = tempScope;
                        }
                        break;
                    }
                }

                if (lock.scope == null) {
                    // Bad request
                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                }

            } else {
                // Bad request
                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
            }

            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:
                        String tempType = currentNode.getNodeName();
                        if (tempType.indexOf(':") != -1) {
                            lock.type =
                                tempType.substring(tempType.indexOf(':") + 1);
                        } else {
                            lock.type = tempType;
                        }
                        break;
                    }
                }

                if (lock.type == null) {
                    // Bad request
                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                }

            } else {
                // Bad request
                resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
            }

            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, true);
                        domWriter.setQualifiedNames(false);
                        domWriter.print(currentNode);
                        lock.owner += strWriter.toString();
                        break;
                    }
                }

                if (lock.owner == null) {
                    // Bad request
                    resp.setStatus(WebdavStatus.SC_BAD_REQUEST);
                }

            } else {
                lock.owner = new String();
            }

        }

        String path = getRelativePath(req);

        lock.path = path;

        boolean exists = true;
        Object object = null;
        try {
            object = resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
        }

        Enumeration locksList = null;

        if (lockRequestType == LOCK_CREATION) {

            // Generating lock id
            String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
                + lock.scope + "-" + req.getUserPrincipal() + "-"
                + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
                + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
                + secret;
            String lockToken =
                md5Encoder.encode(md5Helper.digest(lockTokenStr.getBytes()));

            if ( (exists) && (object instanceof DirContext) &&
                 (lock.depth == INFINITY) ) {

                // Locking a collection (and all its member resources)

                // Checking if a child resource of this collection is
                // already locked
                Vector<String> lockPaths = new Vector<String>();
                locksList = collectionLocks.elements();
                while (locksList.hasMoreElements()) {
                    LockInfo currentLock = (LockInfo) locksList.nextElement();
                    if (currentLock.hasExpired()) {
                        resourceLocks.remove(currentLock.path);
                        continue;
                    }
                    if ( (currentLock.path.startsWith(lock.path)) &&
                         ((currentLock.isExclusive()) ||
                          (lock.isExclusive())) ) {
                        // A child collection of this collection is locked
                        lockPaths.addElement(currentLock.path);
                    }
                }
                locksList = resourceLocks.elements();
                while (locksList.hasMoreElements()) {
                    LockInfo currentLock = (LockInfo) locksList.nextElement();
                    if (currentLock.hasExpired()) {
                        resourceLocks.remove(currentLock.path);
                        continue;
                    }
                    if ( (currentLock.path.startsWith(lock.path)) &&
                         ((currentLock.isExclusive()) ||
                          (lock.isExclusive())) ) {
                        // A child resource of this collection is locked
                        lockPaths.addElement(currentLock.path);
                    }
                }

                if (!lockPaths.isEmpty()) {

                    // One of the child paths was locked
                    // We generate a multistatus error report

                    Enumeration lockPathsList = lockPaths.elements();

                    resp.setStatus(WebdavStatus.SC_CONFLICT);

                    XMLWriter generatedXML = new XMLWriter();
                    generatedXML.writeXMLHeader();

                    generatedXML.writeElement
                        (null, "multistatus" + generateNamespaceDeclarations(),
                         XMLWriter.OPENING);

                    while (lockPathsList.hasMoreElements()) {
                        generatedXML.writeElement(null, "response",
                                                  XMLWriter.OPENING);
                        generatedXML.writeElement(null, "href",
                                                  XMLWriter.OPENING);
                        generatedXML
                            .writeText((String) lockPathsList.nextElement());
                        generatedXML.writeElement(null, "href",
                                                  XMLWriter.CLOSING);
                        generatedXML.writeElement(null, "status",
                                                  XMLWriter.OPENING);
                        generatedXML
                            .writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED
                                       + " " + WebdavStatus
                                       .getStatusText(WebdavStatus.SC_LOCKED));
                        generatedXML.writeElement(null, "status",
                                                  XMLWriter.CLOSING);

                        generatedXML.writeElement(null, "response",
                                                  XMLWriter.CLOSING);
                    }

                    generatedXML.writeElement(null, "multistatus",
                                          XMLWriter.CLOSING);

                    Writer writer = resp.getWriter();
                    writer.write(generatedXML.toString());
                    writer.close();

                    return;

                }

                boolean addLock = true;

                // Checking if there is already a shared lock on this path
                locksList = collectionLocks.elements();
                while (locksList.hasMoreElements()) {

                    LockInfo currentLock = (LockInfo) locksList.nextElement();
                    if (currentLock.path.equals(lock.path)) {

                        if (currentLock.isExclusive()) {
                            resp.sendError(WebdavStatus.SC_LOCKED);
                            return;
                        } else {
                            if (lock.isExclusive()) {
                                resp.sendError(WebdavStatus.SC_LOCKED);
                                return;
                            }
                        }

                        currentLock.tokens.addElement(lockToken);
                        lock = currentLock;
                        addLock = false;

                    }

                }

                if (addLock) {
                    lock.tokens.addElement(lockToken);
                    collectionLocks.addElement(lock);
                }

            } else {

                // Locking a single resource

                // Retrieving an already existing lock on that resource
                LockInfo presentLock = (LockInfo) resourceLocks.get(lock.path);
                if (presentLock != null) {

                    if ((presentLock.isExclusive()) || (lock.isExclusive())) {
                        // If either lock is exclusive, the lock can't be
                        // granted
                        resp.sendError(WebdavStatus.SC_PRECONDITION_FAILED);
                        return;
                    } else {
                        presentLock.tokens.addElement(lockToken);
                        lock = presentLock;
                    }

                } else {

                    lock.tokens.addElement(lockToken);
                    resourceLocks.put(lock.path, lock);

                    // Checking if a resource exists at this path
                    exists = true;
                    try {
                        object = resources.lookup(path);
                    } catch (NamingException e) {
                        exists = false;
                    }
                    if (!exists) {

                        // "Creating" a lock-null resource
                        int slash = lock.path.lastIndexOf('/");
                        String parentPath = lock.path.substring(0, slash);

                        Vector<String> lockNulls =
                            lockNullResources.get(parentPath);
                        if (lockNulls == null) {
                            lockNulls = new Vector<String>();
                            lockNullResources.put(parentPath, lockNulls);
                        }

                        lockNulls.addElement(lock.path);

                    }
                    // Add the Lock-Token header as by RFC 2518 8.10.1
                    // - only do this for newly created locks
                    resp.addHeader("Lock-Token", "<opaquelocktoken:"
                                   + lockToken + ">");
                }

            }

        }

        if (lockRequestType == LOCK_REFRESH) {

            String ifHeader = req.getHeader("If");
            if (ifHeader == null)
                ifHeader = "";

            // Checking resource locks

            LockInfo toRenew = (LockInfo) resourceLocks.get(path);
            Enumeration tokenList = null;
            if (lock != null) {

                // At least one of the tokens of the locks must have been given

                tokenList = toRenew.tokens.elements();
                while (tokenList.hasMoreElements()) {
                    String token = (String) tokenList.nextElement();
                    if (ifHeader.indexOf(token) != -1) {
                        toRenew.expiresAt = lock.expiresAt;
                        lock = toRenew;
                    }
                }

            }

            // Checking inheritable collection locks

            Enumeration collectionLocksList = collectionLocks.elements();
            while (collectionLocksList.hasMoreElements()) {
                toRenew = (LockInfo) collectionLocksList.nextElement();
                if (path.equals(toRenew.path)) {

                    tokenList = toRenew.tokens.elements();
                    while (tokenList.hasMoreElements()) {
                        String token = (String) tokenList.nextElement();
                        if (ifHeader.indexOf(token) != -1) {
                            toRenew.expiresAt = lock.expiresAt;
                            lock = toRenew;
                        }
                    }

                }
            }

        }

        // Set the status, then generate the XML response containing
        // the lock information
        XMLWriter generatedXML = new XMLWriter();
        generatedXML.writeXMLHeader();
        generatedXML.writeElement(null, "prop"
                                  + generateNamespaceDeclarations(),
                                  XMLWriter.OPENING);

        generatedXML.writeElement(null, "lockdiscovery",
                                  XMLWriter.OPENING);

        lock.toXML(generatedXML);

        generatedXML.writeElement(null, "lockdiscovery",
                                  XMLWriter.CLOSING);

        generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);

        resp.setStatus(WebdavStatus.SC_OK);
        resp.setContentType("text/xml; charset=UTF-8");
        Writer writer = resp.getWriter();
        writer.write(generatedXML.toString());
        writer.close();

    
protected voiddoMkcol(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
MKCOL Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        String path = getRelativePath(req);

        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
            (path.toUpperCase().startsWith("/META-INF"))) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        boolean exists = true;
        Object object = null;
        try {
            object = resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
        }

        // Can't create a collection if a resource already exists at the given
        // path
        if (exists) {
            // Get allowed methods
            StringBuffer methodsAllowed = determineMethodsAllowed(resources,
                                                                  req);

            resp.addHeader("Allow", methodsAllowed.toString());

            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
            return;
        }

        if (req.getInputStream().available() > 0) {
            DocumentBuilder documentBuilder = getDocumentBuilder();
            try {
                Document document = documentBuilder.parse
                    (new InputSource(req.getInputStream()));
                // TODO : Process this request body
                resp.sendError(WebdavStatus.SC_NOT_IMPLEMENTED);
                return;

            } catch(SAXException saxe) {
                // Parse error - assume invalid content
                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                return;
            }
        }

        boolean result = true;
        try {
            resources.createSubcontext(path);
        } catch (NamingException e) {
            result = false;
        }

        if (!result) {
            resp.sendError(WebdavStatus.SC_CONFLICT,
                           WebdavStatus.getStatusText
                           (WebdavStatus.SC_CONFLICT));
        } else {
            resp.setStatus(WebdavStatus.SC_CREATED);
            // Removing any lock-null resource which would be present
            lockNullResources.remove(path);
        }

    
protected voiddoMove(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
MOVE Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        String path = getRelativePath(req);

        if (copyResource(req, resp)) {
            deleteResource(path, req, resp, false);
        }

    
protected voiddoOptions(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
OPTIONS Method.

param
req The request
param
resp The response
throws
ServletException If an error occurs
throws
IOException If an IO error occurs


        resp.addHeader("DAV", "1,2");

        StringBuffer methodsAllowed = determineMethodsAllowed(resources,
                                                              req);

        resp.addHeader("Allow", methodsAllowed.toString());
        resp.addHeader("MS-Author-Via", "DAV");

    
protected voiddoPropfind(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
PROPFIND Method.


        if (!listings) {
            // Get allowed methods
            StringBuffer methodsAllowed = determineMethodsAllowed(resources,
                                                                  req);

            resp.addHeader("Allow", methodsAllowed.toString());
            resp.sendError(WebdavStatus.SC_METHOD_NOT_ALLOWED);
            return;
        }

        String path = getRelativePath(req);
        if (path.endsWith("/"))
            path = path.substring(0, path.length() - 1);

        if ((path.toUpperCase().startsWith("/WEB-INF")) ||
            (path.toUpperCase().startsWith("/META-INF"))) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        // Properties which are to be displayed.
        Vector<String> properties = null;
        // Propfind depth
        int depth = INFINITY;
        // Propfind type
        int type = FIND_ALL_PROP;

        String depthStr = req.getHeader("Depth");

        if (depthStr == null) {
            depth = INFINITY;
        } else {
            if (depthStr.equals("0")) {
                depth = 0;
            } else if (depthStr.equals("1")) {
                depth = 1;
            } else if (depthStr.equals("infinity")) {
                depth = INFINITY;
            }
        }

        Node propNode = null;

        DocumentBuilder documentBuilder = getDocumentBuilder();

        try {
            Document document = documentBuilder.parse
                (new InputSource(req.getInputStream()));

            // Get the root element of the document
            Element rootElement = document.getDocumentElement();
            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:
                    if (currentNode.getNodeName().endsWith("prop")) {
                        type = FIND_BY_PROPERTY;
                        propNode = currentNode;
                    }
                    if (currentNode.getNodeName().endsWith("propname")) {
                        type = FIND_PROPERTY_NAMES;
                    }
                    if (currentNode.getNodeName().endsWith("allprop")) {
                        type = FIND_ALL_PROP;
                    }
                    break;
                }
            }
        } catch (SAXException e) {
            // Most likely there was no content : we use the defaults.
        } catch (IOException e) {
            // Most likely there was no content : we use the defaults.
        }

        if (type == FIND_BY_PROPERTY) {
            properties = new Vector<String>();
            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:
                    String nodeName = currentNode.getNodeName();
                    String propertyName = null;
                    if (nodeName.indexOf(':") != -1) {
                        propertyName = nodeName.substring
                            (nodeName.indexOf(':") + 1);
                    } else {
                        propertyName = nodeName;
                    }
                    // href is a live property which is handled differently
                    properties.addElement(propertyName);
                    break;
                }
            }

        }

        boolean exists = true;
        Object object = null;
        try {
            object = resources.lookup(path);
        } catch (NamingException e) {
            exists = false;
            int slash = path.lastIndexOf('/");
            if (slash != -1) {
                String parentPath = path.substring(0, slash);
                Vector currentLockNullResources =
                    (Vector) lockNullResources.get(parentPath);
                if (currentLockNullResources != null) {
                    Enumeration lockNullResourcesList =
                        currentLockNullResources.elements();
                    while (lockNullResourcesList.hasMoreElements()) {
                        String lockNullPath = (String)
                            lockNullResourcesList.nextElement();
                        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
                                (null, "multistatus"
                                 + generateNamespaceDeclarations(),
                                 XMLWriter.OPENING);
                            parseLockNullProperties
                                (req, generatedXML, lockNullPath, type,
                                 properties);
                            generatedXML.writeElement(null, "multistatus",
                                                      XMLWriter.CLOSING);
                            generatedXML.sendData();
                            return;
                        }
                    }
                }
            }
        }

        if (!exists) {
            resp.sendError(HttpServletResponse.SC_NOT_FOUND, path);
            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(null, "multistatus"
                                  + generateNamespaceDeclarations(),
                                  XMLWriter.OPENING);

        if (depth == 0) {
            parseProperties(req, generatedXML, path, type,
                            properties);
        } else {
            // The stack always contains the object of the current level
            Stack<String> stack = new Stack<String>();
            stack.push(path);

            // Stack of the objects one level below
            Stack<String> stackBelow = new Stack<String>();

            while ((!stack.isEmpty()) && (depth >= 0)) {

                String currentPath = (String) stack.pop();
                parseProperties(req, generatedXML, currentPath,
                                type, properties);

                try {
                    object = resources.lookup(currentPath);
                } catch (NamingException e) {
                    continue;
                }

                if ((object instanceof DirContext) && (depth > 0)) {

                    try {
                        NamingEnumeration enumeration = resources.list(currentPath);
                        while (enumeration.hasMoreElements()) {
                            NameClassPair ncPair =
                                (NameClassPair) enumeration.nextElement();
                            String newPath = currentPath;
                            if (!(newPath.endsWith("/")))
                                newPath += "/";
                            newPath += ncPair.getName();
                            stackBelow.push(newPath);
                        }
                    } catch (NamingException e) {
                        resp.sendError
                            (HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                             path);
                        return;
                    }

                    // Displaying the lock-null resources present in that
                    // collection
                    String lockPath = currentPath;
                    if (lockPath.endsWith("/"))
                        lockPath =
                            lockPath.substring(0, lockPath.length() - 1);
                    Vector currentLockNullResources =
                        (Vector) lockNullResources.get(lockPath);
                    if (currentLockNullResources != null) {
                        Enumeration lockNullResourcesList =
                            currentLockNullResources.elements();
                        while (lockNullResourcesList.hasMoreElements()) {
                            String lockNullPath = (String)
                                lockNullResourcesList.nextElement();
                            parseLockNullProperties
                                (req, generatedXML, lockNullPath, type,
                                 properties);
                        }
                    }

                }

                if (stack.isEmpty()) {
                    depth--;
                    stack = stackBelow;
                    stackBelow = new Stack<String>();
                }

                generatedXML.sendData();

            }
        }

        generatedXML.writeElement(null, "multistatus",
                                  XMLWriter.CLOSING);

        generatedXML.sendData();

    
protected voiddoProppatch(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
PROPPATCH Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);

    
protected voiddoPut(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Process a POST request for the specified resource.

param
req The servlet request we are processing
param
resp The servlet response we are creating
exception
IOException if an input/output error occurs
exception
ServletException if a servlet-specified error occurs


        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        super.doPut(req, resp);

        String path = getRelativePath(req);

        // Removing any lock-null resource which would be present
        lockNullResources.remove(path);

    
protected voiddoUnlock(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
UNLOCK Method.


        if (readOnly) {
            resp.sendError(WebdavStatus.SC_FORBIDDEN);
            return;
        }

        if (isLocked(req)) {
            resp.sendError(WebdavStatus.SC_LOCKED);
            return;
        }

        String path = getRelativePath(req);

        String lockTokenHeader = req.getHeader("Lock-Token");
        if (lockTokenHeader == null)
            lockTokenHeader = "";

        // Checking resource locks

        LockInfo lock = (LockInfo) resourceLocks.get(path);
        Enumeration tokenList = null;
        if (lock != null) {

            // At least one of the tokens of the locks must have been given

            tokenList = lock.tokens.elements();
            while (tokenList.hasMoreElements()) {
                String token = (String) tokenList.nextElement();
                if (lockTokenHeader.indexOf(token) != -1) {
                    lock.tokens.removeElement(token);
                }
            }

            if (lock.tokens.isEmpty()) {
                resourceLocks.remove(path);
                // Removing any lock-null resource which would be present
                lockNullResources.remove(path);
            }

        }

        // Checking inheritable collection locks

        Enumeration collectionLocksList = collectionLocks.elements();
        while (collectionLocksList.hasMoreElements()) {
            lock = (LockInfo) collectionLocksList.nextElement();
            if (path.equals(lock.path)) {

                tokenList = lock.tokens.elements();
                while (tokenList.hasMoreElements()) {
                    String token = (String) tokenList.nextElement();
                    if (lockTokenHeader.indexOf(token) != -1) {
                        lock.tokens.removeElement(token);
                        break;
                    }
                }

                if (lock.tokens.isEmpty()) {
                    collectionLocks.removeElement(lock);
                    // Removing any lock-null resource which would be present
                    lockNullResources.remove(path);
                }

            }
        }

        resp.setStatus(WebdavStatus.SC_NO_CONTENT);

    
private booleangenerateLockDiscovery(java.lang.String path, org.apache.catalina.util.XMLWriter generatedXML)
Print the lock discovery information associated with a path.

param
path Path
param
generatedXML XML data to which the locks info will be appended
return
true if at least one lock was displayed


        LockInfo resourceLock = (LockInfo) resourceLocks.get(path);
        Enumeration collectionLocksList = collectionLocks.elements();

        boolean wroteStart = false;

        if (resourceLock != null) {
            wroteStart = true;
            generatedXML.writeElement(null, "lockdiscovery",
                                      XMLWriter.OPENING);
            resourceLock.toXML(generatedXML);
        }

        while (collectionLocksList.hasMoreElements()) {
            LockInfo currentLock =
                (LockInfo) collectionLocksList.nextElement();
            if (path.startsWith(currentLock.path)) {
                if (!wroteStart) {
                    wroteStart = true;
                    generatedXML.writeElement(null, "lockdiscovery",
                                              XMLWriter.OPENING);
                }
                currentLock.toXML(generatedXML);
            }
        }

        if (wroteStart) {
            generatedXML.writeElement(null, "lockdiscovery",
                                      XMLWriter.CLOSING);
        } else {
            return false;
        }

        return true;

    
private java.lang.StringgenerateNamespaceDeclarations()
Generate the namespace declarations.

        return " xmlns=\"" + DEFAULT_NAMESPACE + "\"";
    
protected javax.xml.parsers.DocumentBuildergetDocumentBuilder()
Return JAXP document builder instance.

        DocumentBuilder documentBuilder = null;
        DocumentBuilderFactory documentBuilderFactory = null;
        try {
            documentBuilderFactory = DocumentBuilderFactory.newInstance();
            documentBuilderFactory.setNamespaceAware(true);
            documentBuilder = documentBuilderFactory.newDocumentBuilder();
        } catch(ParserConfigurationException e) {
            throw new ServletException
                (sm.getString("webdavservlet.jaxpfailed"));
        }
        return documentBuilder;
    
private java.lang.StringgetISOCreationDate(long creationDate)
Get creation date in ISO format.

        StringBuffer creationDateValue = new StringBuffer
            (creationDateFormat.format
             (new Date(creationDate)));
        /*
        int offset = Calendar.getInstance().getTimeZone().getRawOffset()
            / 3600000; // FIXME ?
        if (offset < 0) {
            creationDateValue.append("-");
            offset = -offset;
        } else if (offset > 0) {
            creationDateValue.append("+");
        }
        if (offset != 0) {
            if (offset < 10)
                creationDateValue.append("0");
            creationDateValue.append(offset + ":00");
        } else {
            creationDateValue.append("Z");
        }
        */
        return creationDateValue.toString();
    
public voidinit()
Initialize this servlet.



    // --------------------------------------------------------- Public Methods


            
      
          

        super.init();

        if (getServletConfig().getInitParameter("secret") != null)
            secret = getServletConfig().getInitParameter("secret");

        // Load the MD5 helper used to calculate signatures.
        try {
            md5Helper = MessageDigest.getInstance("MD5");
        } catch (NoSuchAlgorithmException e) {
            throw new UnavailableException("No MD5");
        }

    
private booleanisLocked(javax.servlet.http.HttpServletRequest req)
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.

param
req Servlet request
return
boolean true if the resource is locked (and no appropriate lock token has been found for at least one of the non-shared locks which are present on the resource).


        String path = getRelativePath(req);

        String ifHeader = req.getHeader("If");
        if (ifHeader == null)
            ifHeader = "";

        String lockTokenHeader = req.getHeader("Lock-Token");
        if (lockTokenHeader == null)
            lockTokenHeader = "";

        return isLocked(path, ifHeader + lockTokenHeader);

    
private booleanisLocked(java.lang.String path, java.lang.String ifHeader)
Check to see if a resource is currently write locked.

param
path Path of the resource
param
ifHeader "If" HTTP header which was included in the request
return
boolean true if the resource is locked (and no appropriate lock token has been found for at least one of the non-shared locks which are present on the resource).


        // Checking resource locks

        LockInfo lock = (LockInfo) resourceLocks.get(path);
        Enumeration tokenList = null;
        if ((lock != null) && (lock.hasExpired())) {
            resourceLocks.remove(path);
        } else if (lock != null) {

            // At least one of the tokens of the locks must have been given

            tokenList = lock.tokens.elements();
            boolean tokenMatch = false;
            while (tokenList.hasMoreElements()) {
                String token = (String) tokenList.nextElement();
                if (ifHeader.indexOf(token) != -1)
                    tokenMatch = true;
            }
            if (!tokenMatch)
                return true;

        }

        // Checking inheritable collection locks

        Enumeration collectionLocksList = collectionLocks.elements();
        while (collectionLocksList.hasMoreElements()) {
            lock = (LockInfo) collectionLocksList.nextElement();
            if (lock.hasExpired()) {
                collectionLocks.removeElement(lock);
            } else if (path.startsWith(lock.path)) {

                tokenList = lock.tokens.elements();
                boolean tokenMatch = false;
                while (tokenList.hasMoreElements()) {
                    String token = (String) tokenList.nextElement();
                    if (ifHeader.indexOf(token) != -1)
                        tokenMatch = true;
                }
                if (!tokenMatch)
                    return true;

            }
        }

        return false;

    
protected java.lang.Stringnormalize(java.lang.String path)
Return a context-relative path, beginning with a "/", that represents the canonical version of the specified path after ".." and "." elements are resolved out. If the specified path attempts to go outside the boundaries of the current context (i.e. too many ".." path elements are present), return null instead.

param
path Path to be normalized


        if (path == null)
            return null;

        // Create a place for the normalized path
        String normalized = path;

        if (normalized == null)
            return (null);

        if (normalized.equals("/."))
            return "/";

        // Normalize the slashes and add leading slash if necessary
        if (normalized.indexOf('\\") >= 0)
            normalized = normalized.replace('\\", '/");
        if (!normalized.startsWith("/"))
            normalized = "/" + normalized;

        // Resolve occurrences of "//" in the normalized path
        while (true) {
            int index = normalized.indexOf("//");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                normalized.substring(index + 1);
        }

        // Resolve occurrences of "/./" in the normalized path
        while (true) {
            int index = normalized.indexOf("/./");
            if (index < 0)
                break;
            normalized = normalized.substring(0, index) +
                normalized.substring(index + 2);
        }

        // Resolve occurrences of "/../" in the normalized path
        while (true) {
            int index = normalized.indexOf("/../");
            if (index < 0)
                break;
            if (index == 0)
                return (null);  // Trying to go outside our context
            int index2 = normalized.lastIndexOf('/", index - 1);
            normalized = normalized.substring(0, index2) +
                normalized.substring(index + 3);
        }

        // Return the normalized path that we have completed
        return (normalized);

    
private voidparseLockNullProperties(javax.servlet.http.HttpServletRequest req, org.apache.catalina.util.XMLWriter generatedXML, java.lang.String path, int type, java.util.Vector propertiesVector)
Propfind helper method. Dispays the properties of a lock-null resource.

param
resources Resources object associated with this context
param
generatedXML XML response to the Propfind request
param
path Path of the current resource
param
type Propfind type
param
propertiesVector If the propfind type is find properties by name, then this Vector contains those properties


        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
        // (the "toUpperCase()" avoids problems on Windows systems)
        if (path.toUpperCase().startsWith("/WEB-INF") ||
            path.toUpperCase().startsWith("/META-INF"))
            return;

        // Retrieving the lock associated with the lock-null resource
        LockInfo lock = (LockInfo) resourceLocks.get(path);

        if (lock == null)
            return;

        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
        String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
                                   + WebdavStatus.getStatusText
                                   (WebdavStatus.SC_OK));

        // Generating href element
        generatedXML.writeElement(null, "href", XMLWriter.OPENING);

        String absoluteUri = req.getRequestURI();
        String relativePath = getRelativePath(req);
        String toAppend = path.substring(relativePath.length());
        if (!toAppend.startsWith("/"))
            toAppend = "/" + toAppend;

        generatedXML.writeText(rewriteUrl(normalize(absoluteUri + toAppend)));

        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);

        String resourceName = path;
        int lastSlash = path.lastIndexOf('/");
        if (lastSlash != -1)
            resourceName = resourceName.substring(lastSlash + 1);

        switch (type) {

        case FIND_ALL_PROP :

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            generatedXML.writeProperty
                (null, "creationdate",
                 getISOCreationDate(lock.creationDate.getTime()));
            generatedXML.writeElement
                (null, "displayname", XMLWriter.OPENING);
            generatedXML.writeData(resourceName);
            generatedXML.writeElement
                (null, "displayname", XMLWriter.CLOSING);
            generatedXML.writeProperty(null, "getlastmodified",
                                       FastHttpDateFormat.formatDate
                                       (lock.creationDate.getTime(), null));
            generatedXML.writeProperty
                (null, "getcontentlength", String.valueOf(0));
            generatedXML.writeProperty(null, "getcontenttype", "");
            generatedXML.writeProperty(null, "getetag", "");
            generatedXML.writeElement(null, "resourcetype",
                                      XMLWriter.OPENING);
            generatedXML.writeElement(null, "lock-null", XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "resourcetype",
                                      XMLWriter.CLOSING);

            generatedXML.writeProperty(null, "source", "");

            String supportedLocks = "<lockentry>"
                + "<lockscope><exclusive/></lockscope>"
                + "<locktype><write/></locktype>"
                + "</lockentry>" + "<lockentry>"
                + "<lockscope><shared/></lockscope>"
                + "<locktype><write/></locktype>"
                + "</lockentry>";
            generatedXML.writeElement(null, "supportedlock",
                                      XMLWriter.OPENING);
            generatedXML.writeText(supportedLocks);
            generatedXML.writeElement(null, "supportedlock",
                                      XMLWriter.CLOSING);

            generateLockDiscovery(path, generatedXML);

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            break;

        case FIND_PROPERTY_NAMES :

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            generatedXML.writeElement(null, "creationdate",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "displayname",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "getcontentlanguage",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "getcontentlength",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "getcontenttype",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "getetag",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "getlastmodified",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "resourcetype",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "source",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "lockdiscovery",
                                      XMLWriter.NO_CONTENT);

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            break;

        case FIND_BY_PROPERTY :

            Vector<String> propertiesNotFound = new Vector<String>();

            // Parse the list of properties

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            Enumeration properties = propertiesVector.elements();

            while (properties.hasMoreElements()) {

                String property = (String) properties.nextElement();

                if (property.equals("creationdate")) {
                    generatedXML.writeProperty
                        (null, "creationdate",
                         getISOCreationDate(lock.creationDate.getTime()));
                } else if (property.equals("displayname")) {
                    generatedXML.writeElement
                        (null, "displayname", XMLWriter.OPENING);
                    generatedXML.writeData(resourceName);
                    generatedXML.writeElement
                        (null, "displayname", XMLWriter.CLOSING);
                } else if (property.equals("getcontentlanguage")) {
                    generatedXML.writeElement(null, "getcontentlanguage",
                                              XMLWriter.NO_CONTENT);
                } else if (property.equals("getcontentlength")) {
                    generatedXML.writeProperty
                        (null, "getcontentlength", (String.valueOf(0)));
                } else if (property.equals("getcontenttype")) {
                    generatedXML.writeProperty
                        (null, "getcontenttype", "");
                } else if (property.equals("getetag")) {
                    generatedXML.writeProperty(null, "getetag", "");
                } else if (property.equals("getlastmodified")) {
                    generatedXML.writeProperty
                        (null, "getlastmodified",
                          FastHttpDateFormat.formatDate
                         (lock.creationDate.getTime(), null));
                } else if (property.equals("resourcetype")) {
                    generatedXML.writeElement(null, "resourcetype",
                                              XMLWriter.OPENING);
                    generatedXML.writeElement(null, "lock-null",
                                              XMLWriter.NO_CONTENT);
                    generatedXML.writeElement(null, "resourcetype",
                                              XMLWriter.CLOSING);
                } else if (property.equals("source")) {
                    generatedXML.writeProperty(null, "source", "");
                } else if (property.equals("supportedlock")) {
                    supportedLocks = "<lockentry>"
                        + "<lockscope><exclusive/></lockscope>"
                        + "<locktype><write/></locktype>"
                        + "</lockentry>" + "<lockentry>"
                        + "<lockscope><shared/></lockscope>"
                        + "<locktype><write/></locktype>"
                        + "</lockentry>";
                    generatedXML.writeElement(null, "supportedlock",
                                              XMLWriter.OPENING);
                    generatedXML.writeText(supportedLocks);
                    generatedXML.writeElement(null, "supportedlock",
                                              XMLWriter.CLOSING);
                } else if (property.equals("lockdiscovery")) {
                    if (!generateLockDiscovery(path, generatedXML))
                        propertiesNotFound.addElement(property);
                } else {
                    propertiesNotFound.addElement(property);
                }

            }

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            Enumeration propertiesNotFoundList = propertiesNotFound.elements();

            if (propertiesNotFoundList.hasMoreElements()) {

                status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
                                    + " " + WebdavStatus.getStatusText
                                    (WebdavStatus.SC_NOT_FOUND));

                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

                while (propertiesNotFoundList.hasMoreElements()) {
                    generatedXML.writeElement
                        (null, (String) propertiesNotFoundList.nextElement(),
                         XMLWriter.NO_CONTENT);
                }

                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
                generatedXML.writeText(status);
                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            }

            break;

        }

        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);

    
private voidparseProperties(javax.servlet.http.HttpServletRequest req, org.apache.catalina.util.XMLWriter generatedXML, java.lang.String path, int type, java.util.Vector propertiesVector)
Propfind helper method.

param
req The servlet request
param
resources Resources object associated with this context
param
generatedXML XML response to the Propfind request
param
path Path of the current resource
param
type Propfind type
param
propertiesVector If the propfind type is find properties by name, then this Vector contains those properties


        // Exclude any resource in the /WEB-INF and /META-INF subdirectories
        // (the "toUpperCase()" avoids problems on Windows systems)
        if (path.toUpperCase().startsWith("/WEB-INF") ||
            path.toUpperCase().startsWith("/META-INF"))
            return;

        CacheEntry cacheEntry = resources.lookupCache(path);

        generatedXML.writeElement(null, "response", XMLWriter.OPENING);
        String status = new String("HTTP/1.1 " + WebdavStatus.SC_OK + " "
                                   + WebdavStatus.getStatusText
                                   (WebdavStatus.SC_OK));

        // Generating href element
        generatedXML.writeElement(null, "href", XMLWriter.OPENING);

        String href = req.getContextPath() + req.getServletPath();
        if ((href.endsWith("/")) && (path.startsWith("/")))
            href += path.substring(1);
        else
            href += path;
        if ((cacheEntry.context != null) && (!href.endsWith("/")))
            href += "/";

        generatedXML.writeText(rewriteUrl(href));

        generatedXML.writeElement(null, "href", XMLWriter.CLOSING);

        String resourceName = path;
        int lastSlash = path.lastIndexOf('/");
        if (lastSlash != -1)
            resourceName = resourceName.substring(lastSlash + 1);

        switch (type) {

        case FIND_ALL_PROP :

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            generatedXML.writeProperty
                (null, "creationdate",
                 getISOCreationDate(cacheEntry.attributes.getCreation()));
            generatedXML.writeElement(null, "displayname", XMLWriter.OPENING);
            generatedXML.writeData(resourceName);
            generatedXML.writeElement(null, "displayname", XMLWriter.CLOSING);
            if (cacheEntry.resource != null) {
                generatedXML.writeProperty
                    (null, "getlastmodified", FastHttpDateFormat.formatDate
                           (cacheEntry.attributes.getLastModified(), null));
                generatedXML.writeProperty
                    (null, "getcontentlength",
                     String.valueOf(cacheEntry.attributes.getContentLength()));
                String contentType = getServletContext().getMimeType
                    (cacheEntry.name);
                if (contentType != null) {
                    generatedXML.writeProperty(null, "getcontenttype",
                                               contentType);
                }
                generatedXML.writeProperty(null, "getetag",
                                           getETag(cacheEntry.attributes));
                generatedXML.writeElement(null, "resourcetype",
                                          XMLWriter.NO_CONTENT);
            } else {
                generatedXML.writeElement(null, "resourcetype",
                                          XMLWriter.OPENING);
                generatedXML.writeElement(null, "collection",
                                          XMLWriter.NO_CONTENT);
                generatedXML.writeElement(null, "resourcetype",
                                          XMLWriter.CLOSING);
            }

            generatedXML.writeProperty(null, "source", "");

            String supportedLocks = "<lockentry>"
                + "<lockscope><exclusive/></lockscope>"
                + "<locktype><write/></locktype>"
                + "</lockentry>" + "<lockentry>"
                + "<lockscope><shared/></lockscope>"
                + "<locktype><write/></locktype>"
                + "</lockentry>";
            generatedXML.writeElement(null, "supportedlock",
                                      XMLWriter.OPENING);
            generatedXML.writeText(supportedLocks);
            generatedXML.writeElement(null, "supportedlock",
                                      XMLWriter.CLOSING);

            generateLockDiscovery(path, generatedXML);

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            break;

        case FIND_PROPERTY_NAMES :

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            generatedXML.writeElement(null, "creationdate",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "displayname",
                                      XMLWriter.NO_CONTENT);
            if (cacheEntry.resource != null) {
                generatedXML.writeElement(null, "getcontentlanguage",
                                          XMLWriter.NO_CONTENT);
                generatedXML.writeElement(null, "getcontentlength",
                                          XMLWriter.NO_CONTENT);
                generatedXML.writeElement(null, "getcontenttype",
                                          XMLWriter.NO_CONTENT);
                generatedXML.writeElement(null, "getetag",
                                          XMLWriter.NO_CONTENT);
                generatedXML.writeElement(null, "getlastmodified",
                                          XMLWriter.NO_CONTENT);
            }
            generatedXML.writeElement(null, "resourcetype",
                                      XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "source", XMLWriter.NO_CONTENT);
            generatedXML.writeElement(null, "lockdiscovery",
                                      XMLWriter.NO_CONTENT);

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            break;

        case FIND_BY_PROPERTY :

            Vector<String> propertiesNotFound = new Vector<String>();

            // Parse the list of properties

            generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
            generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

            Enumeration<String> properties = propertiesVector.elements();

            while (properties.hasMoreElements()) {

                String property = (String) properties.nextElement();

                if (property.equals("creationdate")) {
                    generatedXML.writeProperty
                        (null, "creationdate",
                         getISOCreationDate(cacheEntry.attributes.getCreation()));
                } else if (property.equals("displayname")) {
                    generatedXML.writeElement
                        (null, "displayname", XMLWriter.OPENING);
                    generatedXML.writeData(resourceName);
                    generatedXML.writeElement
                        (null, "displayname", XMLWriter.CLOSING);
                } else if (property.equals("getcontentlanguage")) {
                    if (cacheEntry.context != null) {
                        propertiesNotFound.addElement(property);
                    } else {
                        generatedXML.writeElement(null, "getcontentlanguage",
                                                  XMLWriter.NO_CONTENT);
                    }
                } else if (property.equals("getcontentlength")) {
                    if (cacheEntry.context != null) {
                        propertiesNotFound.addElement(property);
                    } else {
                        generatedXML.writeProperty
                            (null, "getcontentlength",
                             (String.valueOf(cacheEntry.attributes.getContentLength())));
                    }
                } else if (property.equals("getcontenttype")) {
                    if (cacheEntry.context != null) {
                        propertiesNotFound.addElement(property);
                    } else {
                        generatedXML.writeProperty
                            (null, "getcontenttype",
                             getServletContext().getMimeType
                             (cacheEntry.name));
                    }
                } else if (property.equals("getetag")) {
                    if (cacheEntry.context != null) {
                        propertiesNotFound.addElement(property);
                    } else {
                        generatedXML.writeProperty
                            (null, "getetag", getETag(cacheEntry.attributes));
                    }
                } else if (property.equals("getlastmodified")) {
                    if (cacheEntry.context != null) {
                        propertiesNotFound.addElement(property);
                    } else {
                        generatedXML.writeProperty
                            (null, "getlastmodified", FastHttpDateFormat.formatDate
                                    (cacheEntry.attributes.getLastModified(), null));
                    }
                } else if (property.equals("resourcetype")) {
                    if (cacheEntry.context != null) {
                        generatedXML.writeElement(null, "resourcetype",
                                                  XMLWriter.OPENING);
                        generatedXML.writeElement(null, "collection",
                                                  XMLWriter.NO_CONTENT);
                        generatedXML.writeElement(null, "resourcetype",
                                                  XMLWriter.CLOSING);
                    } else {
                        generatedXML.writeElement(null, "resourcetype",
                                                  XMLWriter.NO_CONTENT);
                    }
                } else if (property.equals("source")) {
                    generatedXML.writeProperty(null, "source", "");
                } else if (property.equals("supportedlock")) {
                    supportedLocks = "<lockentry>"
                        + "<lockscope><exclusive/></lockscope>"
                        + "<locktype><write/></locktype>"
                        + "</lockentry>" + "<lockentry>"
                        + "<lockscope><shared/></lockscope>"
                        + "<locktype><write/></locktype>"
                        + "</lockentry>";
                    generatedXML.writeElement(null, "supportedlock",
                                              XMLWriter.OPENING);
                    generatedXML.writeText(supportedLocks);
                    generatedXML.writeElement(null, "supportedlock",
                                              XMLWriter.CLOSING);
                } else if (property.equals("lockdiscovery")) {
                    if (!generateLockDiscovery(path, generatedXML))
                        propertiesNotFound.addElement(property);
                } else {
                    propertiesNotFound.addElement(property);
                }

            }

            generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML.writeText(status);
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            Enumeration propertiesNotFoundList = propertiesNotFound.elements();

            if (propertiesNotFoundList.hasMoreElements()) {

                status = new String("HTTP/1.1 " + WebdavStatus.SC_NOT_FOUND
                                    + " " + WebdavStatus.getStatusText
                                    (WebdavStatus.SC_NOT_FOUND));

                generatedXML.writeElement(null, "propstat", XMLWriter.OPENING);
                generatedXML.writeElement(null, "prop", XMLWriter.OPENING);

                while (propertiesNotFoundList.hasMoreElements()) {
                    generatedXML.writeElement
                        (null, (String) propertiesNotFoundList.nextElement(),
                         XMLWriter.NO_CONTENT);
                }

                generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
                generatedXML.writeElement(null, "status", XMLWriter.OPENING);
                generatedXML.writeText(status);
                generatedXML.writeElement(null, "status", XMLWriter.CLOSING);
                generatedXML.writeElement(null, "propstat", XMLWriter.CLOSING);

            }

            break;

        }

        generatedXML.writeElement(null, "response", XMLWriter.CLOSING);

    
private voidsendReport(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp, java.util.Hashtable errorList)
Send a multistatus element containing a complete error report to the client.

param
req Servlet request
param
resp Servlet response
param
errorList List of error to be displayed


        resp.setStatus(WebdavStatus.SC_MULTI_STATUS);

        String absoluteUri = req.getRequestURI();
        String relativePath = getRelativePath(req);

        XMLWriter generatedXML = new XMLWriter();
        generatedXML.writeXMLHeader();

        generatedXML.writeElement(null, "multistatus"
                                  + generateNamespaceDeclarations(),
                                  XMLWriter.OPENING);

        Enumeration pathList = errorList.keys();
        while (pathList.hasMoreElements()) {

            String errorPath = (String) pathList.nextElement();
            int errorCode = ((Integer) errorList.get(errorPath)).intValue();

            generatedXML.writeElement(null, "response", XMLWriter.OPENING);

            generatedXML.writeElement(null, "href", XMLWriter.OPENING);
            String toAppend = errorPath.substring(relativePath.length());
            if (!toAppend.startsWith("/"))
                toAppend = "/" + toAppend;
            generatedXML.writeText(absoluteUri + toAppend);
            generatedXML.writeElement(null, "href", XMLWriter.CLOSING);
            generatedXML.writeElement(null, "status", XMLWriter.OPENING);
            generatedXML
                .writeText("HTTP/1.1 " + errorCode + " "
                           + WebdavStatus.getStatusText(errorCode));
            generatedXML.writeElement(null, "status", XMLWriter.CLOSING);

            generatedXML.writeElement(null, "response", XMLWriter.CLOSING);

        }

        generatedXML.writeElement(null, "multistatus", XMLWriter.CLOSING);

        Writer writer = resp.getWriter();
        writer.write(generatedXML.toString());
        writer.close();

    
protected voidservice(javax.servlet.http.HttpServletRequest req, javax.servlet.http.HttpServletResponse resp)
Handles the special WebDAV methods.


        String method = req.getMethod();

        if (debug > 0) {
            String path = getRelativePath(req);
            log("[" + method + "] " + path);
        }

        if (method.equals(METHOD_PROPFIND)) {
            doPropfind(req, resp);
        } else if (method.equals(METHOD_PROPPATCH)) {
            doProppatch(req, resp);
        } else if (method.equals(METHOD_MKCOL)) {
            doMkcol(req, resp);
        } else if (method.equals(METHOD_COPY)) {
            doCopy(req, resp);
        } else if (method.equals(METHOD_MOVE)) {
            doMove(req, resp);
        } else if (method.equals(METHOD_LOCK)) {
            doLock(req, resp);
        } else if (method.equals(METHOD_UNLOCK)) {
            doUnlock(req, resp);
        } else {
            // DefaultServlet processing
            super.service(req, resp);
        }