util_lock.c revision 4d0b0b6d8341c5e54b2081665fc91b4e4f781753
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna/* ====================================================================
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * The Apache Software License, Version 1.1
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
5f5d1b4cc970b7f06ff8ef6526128e9a27303d88nd * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * reserved.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
031b91a62d25106ae69d4693475c79618dd5e884fielding * Redistribution and use in source and binary forms, with or without
031b91a62d25106ae69d4693475c79618dd5e884fielding * modification, are permitted provided that the following conditions
031b91a62d25106ae69d4693475c79618dd5e884fielding * are met:
031b91a62d25106ae69d4693475c79618dd5e884fielding *
031b91a62d25106ae69d4693475c79618dd5e884fielding * 1. Redistributions of source code must retain the above copyright
031b91a62d25106ae69d4693475c79618dd5e884fielding * notice, this list of conditions and the following disclaimer.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * 2. Redistributions in binary form must reproduce the above copyright
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * notice, this list of conditions and the following disclaimer in
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * the documentation and/or other materials provided with the
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * distribution.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * 3. The end-user documentation included with the redistribution,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * if any, must include the following acknowledgment:
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * "This product includes software developed by the
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * Apache Software Foundation (http://www.apache.org/)."
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * Alternately, this acknowledgment may appear in the software itself,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * if and wherever such third-party acknowledgments normally appear.
3ca6ee111e6044cb463e6dc45b9adcfa3050ff00rbowen *
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * 4. The names "Apache" and "Apache Software Foundation" must
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * not be used to endorse or promote products derived from this
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * software without prior written permission. For written
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * permission, please contact apache@apache.org.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * 5. Products derived from this software may not be called "Apache",
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * nor may "Apache" appear in their name, without prior written
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * permission of the Apache Software Foundation.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna *
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
397e9a692c0255ea1581b5427894c5fe0da68f83jerenkrantz * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9b68f730221daaa1d7576a157b93358f62e70533takashi * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9b68f730221daaa1d7576a157b93358f62e70533takashi * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * SUCH DAMAGE.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna * ====================================================================
78f97ce162b66a0dbfd7af4dcd9984f162569b04minfrin *
1a2bc936a6b4aaf5713c98a230a449fd878d1f06takashi * This software consists of voluntary contributions made by many
a1ef40892ffa2b44fc249423c5b6c42a74a84c68nd * individuals on behalf of the Apache Software Foundation. For more
78f97ce162b66a0dbfd7af4dcd9984f162569b04minfrin * information on the Apache Software Foundation, please see
a1ef40892ffa2b44fc249423c5b6c42a74a84c68nd * <http://www.apache.org/>.
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna */
8da42f0eb0217374d9519e9c9ca8fc3b90ccbaf6colm
8da42f0eb0217374d9519e9c9ca8fc3b90ccbaf6colm/*
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun** DAV repository-independent lock functions
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun*/
c9c3d339c0f2b9485326e059b6ec9c081ae9565ecolm
8da42f0eb0217374d9519e9c9ca8fc3b90ccbaf6colm#include "apr.h"
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "apr_strings.h"
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun#if APR_HAVE_STDIO_H
2a869ee6594f1e3e9039af332cd73f939509483acolm#include <stdio.h> /* for sprintf() */
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun#endif
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "mod_dav.h"
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "http_log.h"
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "http_config.h"
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "http_protocol.h"
b09705acacab4c960c608c71377aa3ec2bf9e8a1colm#include "http_core.h"
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna/* ---------------------------------------------------------------
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna**
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun** Property-related lock functions
44ee9a92415caf6322119d0ca2d20e169e3a1525takashi**
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna*/
6b797d6bd26bbf8589eb0e2b6cfa2fa808f456b1pquerna
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun/*
6b797d6bd26bbf8589eb0e2b6cfa2fa808f456b1pquerna** dav_lock_get_activelock: Returns a <lockdiscovery> containing
91be90604bd426eb406711cf00e1380966bdcf81jorton** an activelock element for every item in the lock_discovery tree
91be90604bd426eb406711cf00e1380966bdcf81jorton*/
91be90604bd426eb406711cf00e1380966bdcf81jortonconst char *dav_lock_get_activelock(request_rec *r, dav_lock *lock,
6b797d6bd26bbf8589eb0e2b6cfa2fa808f456b1pquerna dav_buffer *pbuf)
7248fa34fc5d506ec1f37a929f939fe075c1a030niq{
7248fa34fc5d506ec1f37a929f939fe075c1a030niq dav_lock *lock_scan;
7248fa34fc5d506ec1f37a929f939fe075c1a030niq const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
7248fa34fc5d506ec1f37a929f939fe075c1a030niq int count = 0;
7248fa34fc5d506ec1f37a929f939fe075c1a030niq dav_buffer work_buf = { 0 };
7248fa34fc5d506ec1f37a929f939fe075c1a030niq apr_pool_t *p = r->pool;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
88505bf463771d152b14e5d513a4562175183e31jorton /* If no locks or no lock provider, there are no locks */
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun if (lock == NULL || hooks == NULL) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun /*
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun ** Since resourcediscovery is defined with (activelock)*,
88505bf463771d152b14e5d513a4562175183e31jorton ** <D:activelock/> shouldn't be necessary for an empty lock.
ea8572f0df1c69b398e9d677648a0fa5f31b2d60pquerna */
e678e297836a60ee5a456f110cac71377763b96etakashi return "";
ea8572f0df1c69b398e9d677648a0fa5f31b2d60pquerna }
ea8572f0df1c69b398e9d677648a0fa5f31b2d60pquerna
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun /*
ea8572f0df1c69b398e9d677648a0fa5f31b2d60pquerna ** Note: it could be interesting to sum the lengths of the owners
ea8572f0df1c69b398e9d677648a0fa5f31b2d60pquerna ** and locktokens during this loop. However, the buffer
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq ** mechanism provides some rough padding so that we don't
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun ** really need to have an exact size. Further, constructing
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun ** locktoken strings could be relatively expensive.
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun */
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq for (lock_scan = lock; lock_scan != NULL; lock_scan = lock_scan->next)
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna count++;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna /* if a buffer was not provided, then use an internal buffer */
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna if (pbuf == NULL)
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna pbuf = &work_buf;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz /* reset the length before we start appending stuff */
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz pbuf->cur_len = 0;
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun /* prep the buffer with a "good" size */
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun dav_check_bufsize(p, pbuf, count * 300);
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun for (; lock != NULL; lock = lock->next) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun char tmp[100];
9b68f730221daaa1d7576a157b93358f62e70533takashi
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz#if DAV_DEBUG
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz if (lock->rectype == DAV_LOCKREC_INDIRECT_PARTIAL) {
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz /* ### crap. design error */
a6fc6b44b7f8ad7390864b3555341d3abf867f7end dav_buffer_append(p, pbuf,
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "DESIGN ERROR: attempted to product an "
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "activelock element from a partial, indirect "
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "lock record. Creating an XML parsing error "
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "to ease detection of this situation: <");
9cd3b05d7b70f07a742bbaf548fa4fa2bdbe5ce6noodl }
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna#endif
e678e297836a60ee5a456f110cac71377763b96etakashi
e678e297836a60ee5a456f110cac71377763b96etakashi dav_buffer_append(p, pbuf, "<D:activelock>" DEBUG_CR "<D:locktype>");
e678e297836a60ee5a456f110cac71377763b96etakashi switch (lock->type) {
e678e297836a60ee5a456f110cac71377763b96etakashi case DAV_LOCKTYPE_WRITE:
e678e297836a60ee5a456f110cac71377763b96etakashi dav_buffer_append(p, pbuf, "<D:write/>");
e678e297836a60ee5a456f110cac71377763b96etakashi break;
e678e297836a60ee5a456f110cac71377763b96etakashi default:
e678e297836a60ee5a456f110cac71377763b96etakashi /* ### internal error. log something? */
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna break;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna }
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_buffer_append(p, pbuf, "</D:locktype>" DEBUG_CR "<D:lockscope>");
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun switch (lock->scope) {
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna case DAV_LOCKSCOPE_EXCLUSIVE:
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_buffer_append(p, pbuf, "<D:exclusive/>");
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna break;
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna case DAV_LOCKSCOPE_SHARED:
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna dav_buffer_append(p, pbuf, "<D:shared/>");
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun break;
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun default:
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna /* ### internal error. log something? */
82d4a929c9d891a758667c94e09c871358acf6ecjerenkrantz break;
82d4a929c9d891a758667c94e09c871358acf6ecjerenkrantz }
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun dav_buffer_append(p, pbuf, "</D:lockscope>" DEBUG_CR);
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun sprintf(tmp, "<D:depth>%s</D:depth>" DEBUG_CR,
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna lock->depth == DAV_INFINITY ? "infinity" : "0");
82d4a929c9d891a758667c94e09c871358acf6ecjerenkrantz dav_buffer_append(p, pbuf, tmp);
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna if (lock->owner) {
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna /*
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna ** This contains a complete, self-contained <DAV:owner> element,
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna ** with namespace declarations and xml:lang handling. Just drop
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna ** it in.
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna */
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna dav_buffer_append(p, pbuf, lock->owner);
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna }
5b6890f4bf64eff2abec93f06ad5b5cb7560f780pquerna
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun dav_buffer_append(p, pbuf, "<D:timeout>");
ae98487433ef989150195609ec88ba323b46bb51pquerna if (lock->timeout == DAV_TIMEOUT_INFINITE) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun dav_buffer_append(p, pbuf, "Infinite");
362f98329c05da7b953ca60d0ad9045412e5f9d6pquerna }
362f98329c05da7b953ca60d0ad9045412e5f9d6pquerna else {
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna time_t now = time(NULL);
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna sprintf(tmp, "Second-%lu", (long unsigned int)(lock->timeout - now));
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_buffer_append(p, pbuf, tmp);
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna }
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_buffer_append(p, pbuf,
1d9308ed0075062953a246d16bcda888a1be1adeslive "</D:timeout>" DEBUG_CR
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna "<D:locktoken>" DEBUG_CR
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "<D:href>");
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun dav_buffer_append(p, pbuf,
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun (*hooks->format_locktoken)(p, lock->locktoken));
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_buffer_append(p, pbuf,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna "</D:href>" DEBUG_CR
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz "</D:locktoken>" DEBUG_CR
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz "</D:activelock>" DEBUG_CR);
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz }
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun return pbuf->buf;
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun}
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun/*
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun** dav_lock_parse_lockinfo: Validates the given xml_doc to contain a
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun** lockinfo XML element, then populates a dav_lock structure
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun** with its contents.
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun*/
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantzdav_error * dav_lock_parse_lockinfo(request_rec *r,
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz const dav_resource *resource,
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz dav_lockdb *lockdb,
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz const ap_xml_doc *doc,
5c3c22e3324f26848c1782e711c24faf28136012jerenkrantz dav_lock **lock_request)
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna{
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna apr_pool_t *p = r->pool;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_error *err;
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun ap_xml_elem *child;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna dav_lock *lock;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna if (!dav_validate_root(doc, "lockinfo")) {
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna return dav_new_error(p, HTTP_BAD_REQUEST, 0,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna "The request body contains an unexpected "
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna "XML root element.");
a1ef40892ffa2b44fc249423c5b6c42a74a84c68nd }
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna if ((err = (*lockdb->hooks->create_lock)(lockdb, resource,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna &lock)) != NULL) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun return dav_push_error(p, err->status, 0,
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun "Could not parse the lockinfo due to an "
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna "internal problem creating a lock structure.",
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna err);
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna }
7248fa34fc5d506ec1f37a929f939fe075c1a030niq
7248fa34fc5d506ec1f37a929f939fe075c1a030niq lock->depth = dav_get_depth(r, DAV_INFINITY);
7248fa34fc5d506ec1f37a929f939fe075c1a030niq if (lock->depth == -1) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun return dav_new_error(p, HTTP_BAD_REQUEST, 0,
7248fa34fc5d506ec1f37a929f939fe075c1a030niq "An invalid Depth header was specified.");
7248fa34fc5d506ec1f37a929f939fe075c1a030niq }
7248fa34fc5d506ec1f37a929f939fe075c1a030niq lock->timeout = dav_get_timeout(r);
7248fa34fc5d506ec1f37a929f939fe075c1a030niq
7248fa34fc5d506ec1f37a929f939fe075c1a030niq /* Parse elements in the XML body */
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq for (child = doc->root->first_child; child; child = child->next) {
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq if (strcmp(child->name, "locktype") == 0
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq && child->first_child
5c915a9a86688ef70d4e17a5a459f85e9f0e5d00niq && lock->type == DAV_LOCKTYPE_UNKNOWN) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun if (strcmp(child->first_child->name, "write") == 0) {
91be90604bd426eb406711cf00e1380966bdcf81jorton lock->type = DAV_LOCKTYPE_WRITE;
91be90604bd426eb406711cf00e1380966bdcf81jorton continue;
91be90604bd426eb406711cf00e1380966bdcf81jorton }
91be90604bd426eb406711cf00e1380966bdcf81jorton }
91be90604bd426eb406711cf00e1380966bdcf81jorton if (strcmp(child->name, "lockscope") == 0
91be90604bd426eb406711cf00e1380966bdcf81jorton && child->first_child
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun && lock->scope == DAV_LOCKSCOPE_UNKNOWN) {
91be90604bd426eb406711cf00e1380966bdcf81jorton if (strcmp(child->first_child->name, "exclusive") == 0)
26be97276918b2dbf2b49852c4c9203552e5e40eniq lock->scope = DAV_LOCKSCOPE_EXCLUSIVE;
91be90604bd426eb406711cf00e1380966bdcf81jorton else if (strcmp(child->first_child->name, "shared") == 0)
7dd2be584ba36c686d9f748b55451f54f0043bccniq lock->scope = DAV_LOCKSCOPE_SHARED;
7dd2be584ba36c686d9f748b55451f54f0043bccniq if (lock->scope != DAV_LOCKSCOPE_UNKNOWN)
7dd2be584ba36c686d9f748b55451f54f0043bccniq continue;
7dd2be584ba36c686d9f748b55451f54f0043bccniq }
7dd2be584ba36c686d9f748b55451f54f0043bccniq
7dd2be584ba36c686d9f748b55451f54f0043bccniq if (strcmp(child->name, "owner") == 0 && lock->owner == NULL) {
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun const char *text;
98f1378491b66e5383a5340d9afe91dabf7f5010colm
7dd2be584ba36c686d9f748b55451f54f0043bccniq /* quote all the values in the <DAV:owner> element */
98f1378491b66e5383a5340d9afe91dabf7f5010colm ap_xml_quote_elem(p, child);
7dd2be584ba36c686d9f748b55451f54f0043bccniq
d875a16355b0db5971788e6374841ea5aa6e6b86nilgun /*
7dd2be584ba36c686d9f748b55451f54f0043bccniq ** Store a full <DAV:owner> element with namespace definitions
7dd2be584ba36c686d9f748b55451f54f0043bccniq ** and an xml:lang definition, if applicable.
7dd2be584ba36c686d9f748b55451f54f0043bccniq */
7dd2be584ba36c686d9f748b55451f54f0043bccniq ap_xml_to_text(p, child, AP_XML_X2T_FULL_NS_LANG, doc->namespaces,
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna NULL, &text, NULL);
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna lock->owner = text;
0c294487ab3a4e567dc574bb2ad81c7ce07cfd31pquerna
continue;
}
return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0,
apr_psprintf(p,
"The server cannot satisfy the "
"LOCK request due to an unknown XML "
"element (\"%s\") within the "
"DAV:lockinfo element.",
child->name));
}
*lock_request = lock;
return NULL;
}
/* ---------------------------------------------------------------
**
** General lock functions
**
*/
/* dav_lock_walker: Walker callback function to record indirect locks */
static dav_error * dav_lock_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
dav_error *err;
/* We don't want to set indirects on the target */
if ((*wres->resource->hooks->is_same_resource)(wres->resource,
ctx->w.root))
return NULL;
if ((err = (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb,
wres->resource, 1,
ctx->lock)) != NULL) {
if (ap_is_HTTP_SERVER_ERROR(err->status)) {
/* ### add a higher-level description? */
return err;
}
/* add to the multistatus response */
dav_add_response(wres, err->status, NULL);
/*
** ### actually, this is probably wrong: we want to fail the whole
** ### LOCK process if something goes bad. maybe the caller should
** ### do a dav_unlock() (e.g. a rollback) if any errors occurred.
*/
}
return NULL;
}
/*
** dav_add_lock: Add a direct lock for resource, and indirect locks for
** all children, bounded by depth.
** ### assume request only contains one lock
*/
dav_error * dav_add_lock(request_rec *r, const dav_resource *resource,
dav_lockdb *lockdb, dav_lock *lock,
dav_response **response)
{
dav_error *err;
int depth = lock->depth;
*response = NULL;
/* Requested lock can be:
* Depth: 0 for null resource, existing resource, or existing collection
* Depth: Inf for existing collection
*/
/*
** 2518 9.2 says to ignore depth if target is not a collection (it has
** no internal children); pretend the client gave the correct depth.
*/
if (!resource->collection) {
depth = 0;
}
/* In all cases, first add direct entry in lockdb */
/*
** Append the new (direct) lock to the resource's existing locks.
**
** Note: this also handles locknull resources
*/
if ((err = (*lockdb->hooks->append_locks)(lockdb, resource, 0,
lock)) != NULL) {
/* ### maybe add a higher-level description */
return err;
}
if (depth > 0) {
/* Walk existing collection and set indirect locks */
dav_walker_ctx ctx = { { 0 } };
dav_response *multi_status;
ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH;
ctx.w.func = dav_lock_walker;
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
ctx.w.root = resource;
ctx.w.lockdb = lockdb;
ctx.r = r;
ctx.lock = lock;
err = (*resource->hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
if (err != NULL) {
/* implies a 5xx status code occurred. screw the multistatus */
return err;
}
if (multi_status != NULL) {
/* manufacture a 207 error for the multistatus response */
*response = multi_status;
return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
"Error(s) occurred on resources during the "
"addition of a depth lock.");
}
}
return NULL;
}
/*
** dav_lock_query: Opens the lock database. Returns a linked list of
** dav_lock structures for all direct locks on path.
*/
DAV_DECLARE(dav_error*) dav_lock_query(dav_lockdb *lockdb,
const dav_resource *resource,
dav_lock **locks)
{
/* If no lock database, return empty result */
if (lockdb == NULL) {
*locks = NULL;
return NULL;
}
/* ### insert a higher-level description? */
return (*lockdb->hooks->get_locks)(lockdb, resource,
DAV_GETLOCKS_RESOLVED,
locks);
}
/* dav_unlock_walker: Walker callback function to remove indirect locks */
static dav_error * dav_unlock_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
dav_error *err;
/* Before removing the lock, do any auto-checkin required */
if (wres->resource->working) {
/* ### get rid of this typecast */
if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource,
0 /*undo*/, 1 /*unlock*/, NULL))
!= NULL) {
return err;
}
}
if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb,
wres->resource,
ctx->locktoken)) != NULL) {
/* ### should we stop or return a multistatus? looks like STOP */
/* ### add a higher-level description? */
return err;
}
return NULL;
}
/*
** dav_get_direct_resource:
**
** Find a lock on the specified resource, then return the resource the
** lock was applied to (in other words, given a (possibly) indirect lock,
** return the direct lock's corresponding resource).
**
** If the lock is an indirect lock, this usually means traversing up the
** namespace [repository] hierarchy. Note that some lock providers may be
** able to return this information with a traversal.
*/
static dav_error * dav_get_direct_resource(apr_pool_t *p,
dav_lockdb *lockdb,
const dav_locktoken *locktoken,
const dav_resource *resource,
const dav_resource **direct_resource)
{
if (lockdb->hooks->lookup_resource != NULL) {
return (*lockdb->hooks->lookup_resource)(lockdb, locktoken,
resource, direct_resource);
}
*direct_resource = NULL;
/* Find the top of this lock-
* If r->filename's direct locks include locktoken, use r->filename.
* If r->filename's indirect locks include locktoken, retry r->filename/..
* Else fail.
*/
while (resource != NULL) {
dav_error *err;
dav_lock *lock;
dav_resource *parent;
/*
** Find the lock specified by <locktoken> on <resource>. If it is
** an indirect lock, then partial results are okay. We're just
** trying to find the thing and know whether it is a direct or
** an indirect lock.
*/
if ((err = (*lockdb->hooks->find_lock)(lockdb, resource, locktoken,
1, &lock)) != NULL) {
/* ### add a higher-level desc? */
return err;
}
/* not found! that's an error. */
if (lock == NULL) {
return dav_new_error(p, HTTP_BAD_REQUEST, 0,
"The specified locktoken does not correspond "
"to an existing lock on this resource.");
}
if (lock->rectype == DAV_LOCKREC_DIRECT) {
/* we found the direct lock. return this resource. */
*direct_resource = resource;
return NULL;
}
/* the lock was indirect. move up a level in the URL namespace */
if ((err = (*resource->hooks->get_parent_resource)(resource,
&parent)) != NULL) {
/* ### add a higher-level desc? */
return err;
}
resource = parent;
}
return dav_new_error(p, HTTP_INTERNAL_SERVER_ERROR, 0,
"The lock database is corrupt. A direct lock could "
"not be found for the corresponding indirect lock "
"on this resource.");
}
/*
** dav_unlock: Removes all direct and indirect locks for r->filename,
** with given locktoken. If locktoken == null_locktoken, all locks
** are removed. If r->filename represents an indirect lock,
** we must unlock the appropriate direct lock.
** Returns OK or appropriate HTTP_* response and logs any errors.
**
** ### We've already crawled the tree to ensure everything was locked
** by us; there should be no need to incorporate a rollback.
*/
int dav_unlock(request_rec *r, const dav_resource *resource,
const dav_locktoken *locktoken)
{
int result;
dav_lockdb *lockdb;
const dav_resource *lock_resource = resource;
const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
const dav_hooks_repository *repos_hooks = resource->hooks;
dav_walker_ctx ctx = { { 0 } };
dav_response *multi_status;
dav_error *err;
/* If no locks provider, then there is nothing to unlock. */
if (hooks == NULL) {
return OK;
}
/* 2518 requires the entire lock to be removed if resource/locktoken
* point to an indirect lock. We need resource of the _direct_
* lock in order to walk down the tree and remove the locks. So,
* If locktoken != null_locktoken,
* Walk up the resource hierarchy until we see a direct lock.
* Or, we could get the direct lock's db/key, pick out the URL
* and do a subrequest. I think walking up is faster and will work
* all the time.
* Else
* Just start removing all locks at and below resource.
*/
if ((err = (*hooks->open_lockdb)(r, 0, 1, &lockdb)) != NULL) {
/* ### return err! maybe add a higher-level desc */
/* ### map result to something nice; log an error */
return HTTP_INTERNAL_SERVER_ERROR;
}
if (locktoken != NULL
&& (err = dav_get_direct_resource(r->pool, lockdb,
locktoken, resource,
&lock_resource)) != NULL) {
/* ### add a higher-level desc? */
/* ### should return err! */
return err->status;
}
/* At this point, lock_resource/locktoken refers to a direct lock (key), ie
* the root of a depth > 0 lock, or locktoken is null.
*/
ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
ctx.w.func = dav_unlock_walker;
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
ctx.w.root = lock_resource;
ctx.w.lockdb = lockdb;
ctx.r = r;
ctx.locktoken = locktoken;
err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
/* ### fix this! */
/* ### do something with multi_status */
result = err == NULL ? OK : err->status;
(*hooks->close_lockdb)(lockdb);
return result;
}
/* dav_inherit_walker: Walker callback function to inherit locks */
static dav_error * dav_inherit_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
if (ctx->skip_root
&& (*wres->resource->hooks->is_same_resource)(wres->resource,
ctx->w.root)) {
return NULL;
}
/* ### maybe add a higher-level desc */
return (*ctx->w.lockdb->hooks->append_locks)(ctx->w.lockdb,
wres->resource, 1,
ctx->lock);
}
/*
** dav_inherit_locks: When a resource or collection is added to a collection,
** locks on the collection should be inherited to the resource/collection.
** (MOVE, MKCOL, etc) Here we propagate any direct or indirect locks from
** parent of resource to resource and below.
*/
static dav_error * dav_inherit_locks(request_rec *r, dav_lockdb *lockdb,
const dav_resource *resource,
int use_parent)
{
dav_error *err;
const dav_resource *which_resource;
dav_lock *locks;
dav_lock *scan;
dav_lock *prev;
dav_walker_ctx ctx = { { 0 } };
const dav_hooks_repository *repos_hooks = resource->hooks;
dav_response *multi_status;
if (use_parent) {
dav_resource *parent;
if ((err = (*repos_hooks->get_parent_resource)(resource,
&parent)) != NULL) {
/* ### add a higher-level desc? */
return err;
}
if (parent == NULL) {
/* ### map result to something nice; log an error */
return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
"Could not fetch parent resource. Unable to "
"inherit locks from the parent and apply "
"them to this resource.");
}
which_resource = parent;
}
else {
which_resource = resource;
}
if ((err = (*lockdb->hooks->get_locks)(lockdb, which_resource,
DAV_GETLOCKS_PARTIAL,
&locks)) != NULL) {
/* ### maybe add a higher-level desc */
return err;
}
if (locks == NULL) {
/* No locks to propagate, just return */
return NULL;
}
/*
** (1) Copy all indirect locks from our parent;
** (2) Create indirect locks for the depth infinity, direct locks
** in our parent.
**
** The append_locks call in the walker callback will do the indirect
** conversion, but we need to remove any direct locks that are NOT
** depth "infinity".
*/
for (scan = locks, prev = NULL;
scan != NULL;
prev = scan, scan = scan->next) {
if (scan->rectype == DAV_LOCKREC_DIRECT
&& scan->depth != DAV_INFINITY) {
if (prev == NULL)
locks = scan->next;
else
prev->next = scan->next;
}
}
/* <locks> has all our new locks. Walk down and propagate them. */
ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
ctx.w.func = dav_inherit_walker;
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
ctx.w.root = resource;
ctx.w.lockdb = lockdb;
ctx.r = r;
ctx.lock = locks;
ctx.skip_root = !use_parent;
/* ### do something with multi_status */
return (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
}
/* ---------------------------------------------------------------
**
** Functions dealing with lock-null resources
**
*/
/*
** dav_get_resource_state: Returns the state of the resource
** r->filename: DAV_RESOURCE_NULL, DAV_RESOURCE_LOCK_NULL,
** or DAV_RESOURCE_EXIST.
**
** Returns DAV_RESOURCE_ERROR if an error occurs.
*/
int dav_get_resource_state(request_rec *r, const dav_resource *resource)
{
const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
if (resource->exists)
return DAV_RESOURCE_EXISTS;
if (hooks != NULL) {
dav_error *err;
dav_lockdb *lockdb;
int locks_present;
/*
** A locknull resource has the form:
**
** known-dir "/" locknull-file
**
** It would be nice to look into <resource> to verify this form,
** but it does not have enough information for us. Instead, we
** can look at the path_info. If the form does not match, then
** there is no way we could have a locknull resource -- it must
** be a plain, null resource.
**
** Apache sets r->filename to known-dir/unknown-file and r->path_info
** to "" for the "proper" case. If anything is in path_info, then
** it can't be a locknull resource.
**
** ### I bet this path_info hack doesn't work for repositories.
** ### Need input from repository implementors! What kind of
** ### restructure do we need? New provider APIs?
*/
if (r->path_info != NULL && *r->path_info != '\0') {
return DAV_RESOURCE_NULL;
}
if ((err = (*hooks->open_lockdb)(r, 1, 1, &lockdb)) == NULL) {
/* note that we might see some expired locks... *shrug* */
err = (*hooks->has_locks)(lockdb, resource, &locks_present);
(*hooks->close_lockdb)(lockdb);
}
if (err != NULL) {
/* ### don't log an error. return err. add higher-level desc. */
ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
"Failed to query lock-null status for %s",
r->filename);
return DAV_RESOURCE_ERROR;
}
if (locks_present)
return DAV_RESOURCE_LOCK_NULL;
}
return DAV_RESOURCE_NULL;
}
dav_error * dav_notify_created(request_rec *r,
dav_lockdb *lockdb,
const dav_resource *resource,
int resource_state,
int depth)
{
dav_error *err;
if (resource_state == DAV_RESOURCE_LOCK_NULL) {
/*
** The resource is no longer a locknull resource. This will remove
** the special marker.
**
** Note that a locknull resource has already inherited all of the
** locks from the parent. We do not need to call dav_inherit_locks.
**
** NOTE: some lock providers record locks for locknull resources using
** a different key than for regular resources. this will shift
** the lock information between the two key types.
*/
(void)(*lockdb->hooks->remove_locknull_state)(lockdb, resource);
/*
** There are resources under this one, which are new. We must
** propagate the locks down to the new resources.
*/
if (depth > 0 &&
(err = dav_inherit_locks(r, lockdb, resource, 0)) != NULL) {
/* ### add a higher level desc? */
return err;
}
}
else if (resource_state == DAV_RESOURCE_NULL) {
/* ### should pass depth to dav_inherit_locks so that it can
** ### optimize for the depth==0 case.
*/
/* this resource should inherit locks from its parent */
if ((err = dav_inherit_locks(r, lockdb, resource, 1)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
"The resource was created successfully, but "
"there was a problem inheriting locks from "
"the parent resource.",
err);
return err;
}
}
/* else the resource already exists and its locks are correct. */
return NULL;
}