protected void doLock()

in java/org/apache/catalina/servlets/WebdavServlet.java [1326:1737]


    protected void doLock(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

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

        String path = getRelativePath(req);

        WebResource resource = resources.getResource(path);
        if (!checkIfHeaders(req, resp, resource)) {
            resp.setStatus(HttpServletResponse.SC_PRECONDITION_FAILED);
            return;
        }

        LockInfo lock = new LockInfo(maxDepth);
        lock.principal = req.getRemoteUser();

        // Parsing lock request

        // Parsing depth header

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

        if (depthStr == null) {
            lock.depth = maxDepth;
        } else {
            if (depthStr.equals("0")) {
                lock.depth = 0;
            } else if (depthStr.equals("infinity")) {
                lock.depth = maxDepth;
            } else {
                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                return;
            }
        }

        // Parsing timeout header

        int lockDuration = DEFAULT_TIMEOUT;
        String lockDurationStr = req.getHeader("Timeout");
        if (lockDurationStr != null) {
            if (lockDurationStr.startsWith("Second-")) {
                try {
                    lockDuration = Integer.parseInt(lockDurationStr.substring("Second-".length()));
                } catch (NumberFormatException e) {
                    // Ignore
                }
            } else if (lockDurationStr.equals("Infinite")) {
                lockDuration = MAX_TIMEOUT;
            }
            if (lockDuration == 0) {
                lockDuration = DEFAULT_TIMEOUT;
            }
            if (lockDuration > MAX_TIMEOUT) {
                lockDuration = MAX_TIMEOUT;
            }
        }
        lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000L);

        boolean lockCreation = false;

        Node lockInfoNode = null;

        byte[] body;
        try (InputStream is = req.getInputStream(); ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            IOTools.flow(is, os);
            body = os.toByteArray();
        } catch (IOException e) {
            resp.sendError(WebdavStatus.SC_BAD_REQUEST);
            return;
        }
        if (body.length > 0) {
            DocumentBuilder documentBuilder = getDocumentBuilder();

            try {
                Document document = documentBuilder.parse(new InputSource(new ByteArrayInputStream(body)));

                // Get the root element of the document
                Element rootElement = document.getDocumentElement();
                if (!"lockinfo".equals(getDAVNode(rootElement))) {
                    resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                    return;
                }
                lockInfoNode = rootElement;
                lockCreation = true;
            } catch (IOException | SAXException e) {
                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                return;
            }
        }

        if (lockInfoNode != null) {

            // Reading lock information

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

            Node lockScopeNode = null;
            Node lockTypeNode = null;
            Node lockOwnerNode = null;

            for (int i = 0; i < childList.getLength(); i++) {
                Node currentNode = childList.item(i);
                switch (currentNode.getNodeType()) {
                    case Node.TEXT_NODE:
                        break;
                    case Node.ELEMENT_NODE:
                        if ("lockscope".equals(getDAVNode(currentNode))) {
                            lockScopeNode = currentNode;
                        }
                        if ("locktype".equals(getDAVNode(currentNode))) {
                            lockTypeNode = currentNode;
                        }
                        if ("owner".equals(getDAVNode(currentNode))) {
                            lockOwnerNode = currentNode;
                        }
                        break;
                }
            }

            if (lockScopeNode != null) {

                childList = lockScopeNode.getChildNodes();
                for (int i = 0; i < childList.getLength(); i++) {
                    Node currentNode = childList.item(i);
                    switch (currentNode.getNodeType()) {
                        case Node.TEXT_NODE:
                            break;
                        case Node.ELEMENT_NODE:
                            lock.scope = getDAVNode(currentNode);
                            break;
                    }
                }

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

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

            if (lockTypeNode != null) {

                childList = lockTypeNode.getChildNodes();
                for (int i = 0; i < childList.getLength(); i++) {
                    Node currentNode = childList.item(i);
                    switch (currentNode.getNodeType()) {
                        case Node.TEXT_NODE:
                            break;
                        case Node.ELEMENT_NODE:
                            lock.type = getDAVNode(currentNode);
                            break;
                    }
                }

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

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

            if (lockOwnerNode != null) {

                childList = lockOwnerNode.getChildNodes();
                for (int i = 0; i < childList.getLength(); i++) {
                    Node currentNode = childList.item(i);
                    switch (currentNode.getNodeType()) {
                        case Node.TEXT_NODE:
                            lock.owner += currentNode.getNodeValue();
                            break;
                        case Node.ELEMENT_NODE:
                            strWriter = new StringWriter();
                            domWriter = new DOMWriter(strWriter);
                            domWriter.print(currentNode);
                            lock.owner += strWriter.toString();
                            break;
                    }
                }

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

            } else {
                lock.owner = "";
            }
        }

        lock.path = path;

        if (lockCreation) {

            // Check if the resource or a parent is already locked
            String parentPath = path;
            do {
                LockInfo parentLock = resourceLocks.get(parentPath);
                if (parentLock != null) {
                    if (parentLock.hasExpired()) {
                        resourceLocks.remove(parentPath);
                    } else if (parentLock.isExclusive() || lock.isExclusive()) {
                        // A parent collection of this collection is locked
                        resp.setStatus(WebdavStatus.SC_LOCKED);
                        return;
                    }
                }
                int slash = parentPath.lastIndexOf('/');
                if (slash < 0) {
                    break;
                }
                parentPath = parentPath.substring(0, slash);
            } while (true);

            // Generating lock id
            lock.token = UUID.randomUUID().toString();

            if (resource.isDirectory() && lock.depth == maxDepth) {

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

                // Checking if a child resource of this collection is
                // already locked
                // Note: it is likely faster in many cases to go over the full lock list than trying to go over all the
                // children (recursively)
                List<String> lockPaths = new ArrayList<>();
                for (LockInfo currentLock : resourceLocks.values()) {
                    if (currentLock.hasExpired()) {
                        resourceLocks.remove(currentLock.path);
                        continue;
                    }
                    if ((currentLock.isExclusive() || lock.isExclusive()) &&
                            currentLock.path.startsWith(lock.path + "/")) {
                        // A child resource of this collection is locked
                        lockPaths.add(currentLock.lockroot);
                    }
                }

                if (!lockPaths.isEmpty()) {

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

                    resp.setStatus(WebdavStatus.SC_MULTI_STATUS);

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

                    generatedXML.writeElement("D", DEFAULT_NAMESPACE, "multistatus", XMLWriter.OPENING);

                    generatedXML.writeElement("D", "response", XMLWriter.OPENING);
                    generatedXML.writeElement("D", "href", XMLWriter.OPENING);
                    generatedXML.writeText(getEncodedPath(path, resource, req));
                    generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
                    generatedXML.writeElement("D", "status", XMLWriter.OPENING);
                    generatedXML.writeText("HTTP/1.1 " + WebdavStatus.SC_FAILED_DEPENDENCY + " ");
                    generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
                    generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
                    for (String lockPath : lockPaths) {
                        generatedXML.writeElement("D", "response", XMLWriter.OPENING);
                        generatedXML.writeElement("D", "href", XMLWriter.OPENING);
                        generatedXML.writeText(lockPath);
                        generatedXML.writeElement("D", "href", XMLWriter.CLOSING);
                        generatedXML.writeElement("D", "status", XMLWriter.OPENING);
                        generatedXML.writeText("HTTP/1.1 " + WebdavStatus.SC_LOCKED + " ");
                        generatedXML.writeElement("D", "status", XMLWriter.CLOSING);
                        generatedXML.writeElement("D", "response", XMLWriter.CLOSING);
                    }

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

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

                    return;
                }

            } else {

                // Locking a single resource

                // Checking if a resource exists at this path
                if (!resource.exists()) {
                    // RFC 4918 removes lock null, instead an empty file is created
                    if (!resources.write(path, new ByteArrayInputStream(new byte[0]), false)) {
                        resp.sendError(WebdavStatus.SC_CONFLICT);
                        return;
                    } else {
                        resp.setStatus(HttpServletResponse.SC_CREATED);
                    }
                }

            }

            lock.lockroot = getEncodedPath(lock.path, resource, req);
            if (lock.isExclusive()) {
                resourceLocks.put(path, lock);
            } else {
                // Checking if there is already a shared lock on this path
                LockInfo sharedLock = resourceLocks.get(path);
                if (sharedLock == null) {
                    sharedLock = new LockInfo(maxDepth);
                    sharedLock.scope = "shared";
                    sharedLock.path = path;
                    sharedLock.lockroot = lock.lockroot;
                    sharedLock.depth = maxDepth;
                    resourceLocks.put(path, sharedLock);
                }
                sharedLock.sharedTokens.add(lock.token);
                sharedLocks.put(lock.token, lock);
            }

            // Add the Lock-Token header as by RFC 2518 8.10.1
            resp.addHeader("Lock-Token", "<" + LOCK_SCHEME + lock.token + ">");

        }

        if (!lockCreation) {

            String ifHeader = req.getHeader("If");
            if (ifHeader == null) {
                // Bad request
                resp.sendError(WebdavStatus.SC_BAD_REQUEST);
                return;
            }

            LockInfo toRenew = null;
            String parentPath = path;
            do {
                LockInfo parentLock = resourceLocks.get(parentPath);
                if (parentLock != null) {
                    if (parentLock.hasExpired()) {
                        resourceLocks.remove(parentPath);
                    } else {
                        // parentPath == currentPath is a check for the first loop
                        if (parentPath == path || parentLock.depth > 0) {
                            if (parentLock.isExclusive()) {
                                if (ifHeader.contains(":" + parentLock.token + ">") && (parentLock.principal == null ||
                                        parentLock.principal.equals(req.getRemoteUser()))) {
                                    toRenew = parentLock;
                                    break;
                                }
                            } else {
                                for (String token : parentLock.sharedTokens) {
                                    if (ifHeader.contains(":" + token + ">")) {
                                        LockInfo sharedLock = sharedLocks.get(token);
                                        if (sharedLock != null && (sharedLock.principal == null ||
                                                sharedLock.principal.equals(req.getRemoteUser()))) {
                                            if (parentPath == path || sharedLock.depth > 0) {
                                                toRenew = sharedLock;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                int slash = parentPath.lastIndexOf('/');
                if (slash < 0) {
                    break;
                }
                parentPath = parentPath.substring(0, slash);
            } while (true);

            if (toRenew != null) {
                if (!toRenew.hasExpired()) {
                    toRenew.expiresAt = lock.expiresAt;
                } else {
                    toRenew = null;
                }
            }
            lock = toRenew;
        }

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

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

        if (lock != null) {
            lock.toXML(generatedXML);
        }

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

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

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