util.c revision 83719c22db4a6d0575bb4f7f34382d7b185a6f74
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering/* ====================================================================
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * The Apache Software License, Version 1.1
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * Copyright (c) 2000 The Apache Software Foundation. All rights
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * Redistribution and use in source and binary forms, with or without
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * modification, are permitted provided that the following conditions
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * 1. Redistributions of source code must retain the above copyright
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * notice, this list of conditions and the following disclaimer.
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * 2. Redistributions in binary form must reproduce the above copyright
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * notice, this list of conditions and the following disclaimer in
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * the documentation and/or other materials provided with the
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * 3. The end-user documentation included with the redistribution,
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * if any, must include the following acknowledgment:
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * "This product includes software developed by the
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * Apache Software Foundation (http://www.apache.org/)."
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek * Alternately, this acknowledgment may appear in the software itself,
21ac6ff143cc8bebfbd1818af28e8c6f82cd5265Zbigniew Jędrzejewski-Szmek * if and wherever such third-party acknowledgments normally appear.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * 4. The names "Apache" and "Apache Software Foundation" must
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * not be used to endorse or promote products derived from this
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * software without prior written permission. For written
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * permission, please contact apache@apache.org.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * 5. Products derived from this software may not be called "Apache",
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * nor may "Apache" appear in their name, without prior written
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * permission of the Apache Software Foundation.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * ====================================================================
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * This software consists of voluntary contributions made by many
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * individuals on behalf of the Apache Software Foundation. For more
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * information on the Apache Software Foundation, please see
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** DAV extension module for Apache 2.0.*
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** - various utilities, repository-independent
91913f584af38b29a816cca959ba648acd60ac9fLennart Poetteringdav_error *dav_new_error(apr_pool_t *p, int status, int error_id, const char *desc)
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering dav_error *err = apr_pcalloc(p, sizeof(*err));
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poetteringdav_error *dav_push_error(apr_pool_t *p, int status, int error_id, const char *desc,
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt dav_error *err = apr_pcalloc(p, sizeof(*err));
91913f584af38b29a816cca959ba648acd60ac9fLennart Poetteringvoid dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t extra_needed)
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* grow the buffer if necessary */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek newbuf = apr_palloc(p, pbuf->alloc_len);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(newbuf, pbuf->buf, pbuf->cur_len);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekvoid dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf, size_t size)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* NOTE: this does not retain prior contents */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* NOTE: this function is used to init the first pointer, too, since
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek the PAD will be larger than alloc_len (0) for zeroed structures */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* grow if we don't have enough for the requested size plus padding */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (size + DAV_BUFFER_PAD > pbuf->alloc_len) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* set the new length; min of MINSIZE */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek pbuf->alloc_len = size + DAV_BUFFER_PAD;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (pbuf->alloc_len < DAV_BUFFER_MINSIZE)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek pbuf->alloc_len = DAV_BUFFER_MINSIZE;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek pbuf->buf = apr_palloc(p, pbuf->alloc_len);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* initialize a buffer and copy the specified (null-term'd) string into it */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekvoid dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, const char *str)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_set_bufsize(p, pbuf, strlen(str));
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(pbuf->buf, str, pbuf->cur_len + 1);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* append a string to the end of the buffer, adjust length */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekvoid dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, const char *str)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_check_bufsize(p, pbuf, len + 1);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* place a string on the end of the buffer, do NOT adjust length */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekvoid dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, const char *str)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_check_bufsize(p, pbuf, len + 1);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* place some memory on the end of a buffer; do NOT adjust length */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekvoid dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, const void *mem,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_check_bufsize(p, pbuf, amt + pad);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(pbuf->buf + pbuf->cur_len, mem, amt);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** dav_lookup_uri()
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** Extension for ap_sub_req_lookup_uri() which can't handle absolute
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** URIs properly.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** If NULL is returned, then an error occurred with parsing the URI or
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** the URI does not match the current server.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekdav_lookup_result dav_lookup_uri(const char *uri, request_rec * r)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_lookup_result result = { 0 };
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering unsigned short port = ntohs(r->connection->local_addr.sin_port);
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering /* first thing to do is parse the URI into various components */
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering if (ap_parse_uri_components(r->pool, uri, &comp) != HTTP_OK) {
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering result.err.desc = "Invalid syntax in Destination URI.";
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering /* the URI must be an absoluteURI (WEBDAV S9.3) */
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering result.err.desc = "Destination URI must be an absolute URI.";
c454426c54c9beb274f415a80c64a4f1580700e7Lennart Poettering /* ### not sure this works if the current request came in via https: */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* insert a port if the URI did not contain one */
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt comp.port = ap_default_port_for_scheme(comp.scheme);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* now, verify that the URI uses the same scheme as the current request.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek the port, must match our port.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek the URI must not have a query (args) or a fragment
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (strcasecmp(comp.scheme, scheme) != 0 ||
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek result.err.desc = apr_psprintf(r->pool,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Destination URI refers to different "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "scheme or port (%s://hostname:%d)"
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek APR_EOL_STR "(want: %s://hostname:%d)",
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek comp.scheme ? comp.scheme : scheme,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (comp.query != NULL || comp.fragment != NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek result.err.status = HTTP_BAD_REQUEST;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Destination URI contains invalid components "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "(a query or a fragment).";
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* we have verified the scheme, port, and general structure */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Hrm. IE5 will pass unqualified hostnames for both the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Host: and Destination: headers. This breaks the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** http_vhost.c::matches_aliases function.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** For now, qualify unqualified comp.hostnames with
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** r->server->server_hostname.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### this is a big hack. Apache should provide a better way.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### maybe the admin should list the unqualified hosts in a
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### <ServerAlias> block?
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (strrchr(comp.hostname, '.') == NULL &&
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek (domain = strchr(r->server->server_hostname, '.')) != NULL) {
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt comp.hostname = apr_pstrcat(r->pool, comp.hostname, domain, NULL);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* now, if a hostname was provided, then verify that it represents the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek same server as the current connection. note that we just use our
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek port, since we've verified the URI matches ours */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek !ap_matches_request_vhost(r, comp.hostname, port)) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek result.err.status = HTTP_BAD_GATEWAY;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek result.err.desc = "Destination URI refers to a different server.";
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* we have verified that the requested URI denotes the same server as
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek the current request. Therefore, we can use ap_sub_req_lookup_uri() */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* reconstruct a URI as just the path */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek new_file = ap_unparse_uri_components(r->pool, &comp, UNP_OMITSITEPART);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * Lookup the URI and return the sub-request. Note that we use the
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering * same HTTP method on the destination. This allows the destination
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering * to apply appropriate restrictions (e.g. readonly).
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering result.rnew = ap_sub_req_method_uri(r->method, new_file, r);
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering/* ---------------------------------------------------------------
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering** XML UTILITY FUNCTIONS
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekint dav_validate_root(const ap_xml_doc *doc, const char *tagname)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek doc->root->ns == AP_XML_NS_DAV_ID &&
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek strcmp(doc->root->name, tagname) == 0;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* find and return the (unique) child with a given DAV: tagname */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekap_xml_elem *dav_find_child(const ap_xml_elem *elem, const char *tagname)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ap_xml_elem *child = elem->first_child;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (; child; child = child->next)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (child->ns == AP_XML_NS_DAV_ID && !strcmp(child->name, tagname))
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* ---------------------------------------------------------------
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering** Timeout header processing
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* dav_get_timeout: If the Timeout: header exists, return a time_t
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * when this lock is expected to expire. Otherwise, return
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * a time_t of DAV_TIMEOUT_INFINITE.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * It's unclear if DAV clients are required to understand
28f90ea25f8d8f2bfbe6d3e6a4846943eb92e4c5Zbigniew Jędrzejewski-Szmek * Seconds-xxx and Infinity time values. We assume that they do.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * In addition, for now, that's all we understand, too.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmektime_t dav_get_timeout(request_rec *r)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek time_t now, expires = DAV_TIMEOUT_INFINITE;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const char *timeout_const = apr_table_get(r->headers_in, "Timeout");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const char *timeout = apr_pstrdup(r->pool, timeout_const), *val;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* Use the first thing we understand, or infinity if
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * we don't understand anything.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (!strncmp(val, "Infinite", 8)) {
28f90ea25f8d8f2bfbe6d3e6a4846943eb92e4c5Zbigniew Jędrzejewski-Szmek if (!strncmp(val, "Second-", 7)) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### We need to handle overflow better:
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * ### timeout will be <= 2^32 - 1
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* ---------------------------------------------------------------
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek** If Header processing
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* add_if_resource returns a new if_header, linking it to next_ih.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekstatic dav_if_header *dav_add_if_resource(apr_pool_t *p, dav_if_header *next_ih,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const char *uri, size_t uri_len)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((ih = apr_pcalloc(p, sizeof(*ih))) == NULL)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek/* add_if_state adds a condition to an if_header.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmekstatic dav_error * dav_add_if_state(apr_pool_t *p, dav_if_header *ih,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_if_state_type t, int condition,
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering new_sl = apr_pcalloc(p, sizeof(*new_sl));
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((err = (*locks_hooks->parse_locktoken)(p, state_token,
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* ### maybe add a higher-level description */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering/* fetch_next_token returns the substring from str+1
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering * to the next occurence of char term, or \0, whichever
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering * occurs first. Leading whitespace is ignored.
91913f584af38b29a816cca959ba648acd60ac9fLennart Poetteringstatic char *dav_fetch_next_token(char **str, char term)
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering while (*token && (*token == ' ' || *token == '\t'))
762a5766dc65058c245c87d326ae3d403d85ea06Lennart Poettering/* dav_process_if_header:
762a5766dc65058c245c87d326ae3d403d85ea06Lennart Poettering * If NULL (no error) is returned, then **if_header points to the
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering * "If" productions structure (or NULL if "If" is not present).
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering * ### this part is bogus:
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering * If an error is encountered, the error is logged. Parent should
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering * return err->status.
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poetteringstatic dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const char *uri = NULL; /* scope of current production; NULL=no-tag */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek enum {no_tagged, tagged, unknown} list_type = unknown;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((str = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "If"))) == NULL)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* Tagged-list production - following states apply to this uri */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek || ((uri = dav_fetch_next_token(&str, '>')) == NULL)) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid If-header: unclosed \"<\" or "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "unexpected tagged-list production.");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* 2518 specifies this must be an absolute URI; just take the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * relative part for later comparison against r->uri */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (ap_parse_uri_components(r->pool, uri, &parsed_uri) != HTTP_OK) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid URI in tagged If-header.");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* note that parsed_uri.path is allocated; we can trash it */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* clean up the URI a bit */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek uri_len = strlen(parsed_uri.path);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (uri_len > 1 && parsed_uri.path[uri_len - 1] == '/')
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek parsed_uri.path[--uri_len] = '\0';
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* List production */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* If a uri has not been encountered, this is a No-Tagged-List */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((list = dav_fetch_next_token(&str, ')')) == NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid If-header: unclosed \"(\".");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((ih = dav_add_if_resource(r->pool, ih, uri, uri_len)) == NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### dav_add_if_resource() should return an error for us! */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Internal server error parsing \"If:\" "
cb7bb815d2ad7945e63214d21cec1d70152ba6abMarkus Knetschke /* List is the entire production (in a uri scope) */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((state_token = dav_fetch_next_token(&list, '>')) == NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### add a description to this error */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_opaquelock,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek condition, locks_hooks)) != NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### maybe add a higher level description */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((state_token = dav_fetch_next_token(&list, ']')) == NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### add a description to this error */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_etag,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek condition, locks_hooks)) != NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* ### maybe add a higher level description */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (list[1] == 'o' && list[2] == 't') {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (condition != DAV_IF_COND_NORMAL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid \"If:\" header: "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Multiple \"not\" entries "
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt "for the same state.");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid \"If:\" "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "header: Unexpected "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "character encountered "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "(0x%02x, '%c').",
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(r->pool, HTTP_BAD_REQUEST,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Invalid \"If:\" header: "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Unexpected character "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "encountered (0x%02x, '%c').",
3fe22bb4b6b5faf27683ad2e231b5a69b6e63a9eLennart Poetteringstatic int dav_find_submitted_locktoken(const dav_if_header *if_header,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (; if_header != NULL; if_header = if_header->next) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek const dav_if_state_list *state_list;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (state_list = if_header->state;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek state_list = state_list->next) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (state_list->type == dav_if_opaquelock) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* given state_list->locktoken, match it */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** The resource will have one or more lock tokens. We only
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** need to match one of them against any token in the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** One token case: It is an exclusive or shared lock. Either
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** way, we must find it.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** N token case: They are shared locks. By policy, we need
d6ce17c7f02ed3facdb45f65f546e587c2f00950Lennart Poettering ** to match only one. The resource's other
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** tokens may belong to somebody else (so we
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** shouldn't see them in the If: header anyway)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (lock = lock_list; lock != NULL; lock = lock->next) {
d6ce17c7f02ed3facdb45f65f546e587c2f00950Lennart Poettering if (!(*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
7de304525deafe4eb86060321e39787138dbbadfLennart Poettering/* dav_validate_resource_state:
7de304525deafe4eb86060321e39787138dbbadfLennart Poettering * Returns NULL if path/uri meets if-header and lock requirements
7de304525deafe4eb86060321e39787138dbbadfLennart Poetteringstatic dav_error * dav_validate_resource_state(apr_pool_t *p,
7de304525deafe4eb86060321e39787138dbbadfLennart Poettering const dav_hooks_locks *locks_hooks = (lockdb ? lockdb->hooks : NULL);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* DBG1("validate: <%s>", resource->uri); */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** The resource will have one of three states:
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** 1) No locks. We have no special requirements that the user supply
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** specific locktokens. One of the state lists must match, and
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** 2) One exclusive lock. The locktoken must appear *anywhere* in the
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** If: header. Of course, asserting the token in a "Not" term will
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** quickly fail that state list :-). If the locktoken appears in
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** one of the state lists *and* one state list matches, then we're
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** 3) One or more shared locks. One of the locktokens must appear
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** *anywhere* in the If: header. If one of the locktokens appears,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** and we match one state list, then we are done.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** The <seen_locktoken> variable determines whether we have seen one
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** of this resource's locktokens in the If: header.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** If this is a new lock request, <flags> will contain the requested
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** lock scope. Three rules apply:
12b42c76672a66c2d4ea7212c14f8f1b5a62b78dTom Gundersen ** 1) Do not require a (shared) locktoken to be seen (when we are
12b42c76672a66c2d4ea7212c14f8f1b5a62b78dTom Gundersen ** applying another shared lock)
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** 2) If the scope is exclusive and we see any locks, fail.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** 3) If the scope is shared and we see an exclusive lock, fail.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* we're in State 1. no locks. */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### hrm... we don't need to have these fully
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### resolved since we're only looking at the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### locktokens...
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** ### use get_locks w/ calltype=PARTIAL
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "The locks could not be queried for "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "verification against a possible \"If:\" "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* lock_list now determines whether we're in State 1, 2, or 3. */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** For a new, exclusive lock: if any locks exist, fail.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** For a new, shared lock: if an exclusive lock exists, fail.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** else, do not require a token to be seen.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (flags & DAV_LOCKSCOPE_EXCLUSIVE) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(p, HTTP_LOCKED, 0,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "Existing lock(s) on the requested resource "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "prevent an exclusive lock.");
1d3eaa93616a2e9f6568b754a65c884766bac6c4Jay Strict ** There are no locks, so we can pretend that we've already met
6b94875fa3a7280e7f1c0df11027b927b6961edeRichard Maw ** any requirement to find the resource's locks in an If: header.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek else if (flags & DAV_LOCKSCOPE_SHARED) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Strictly speaking, we don't need this loop. Either the first
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** (and only) lock will be EXCLUSIVE, or none of them will be.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (lock = lock_list; lock != NULL; lock = lock->next) {
dd2b607b7d1ce355e93f9f71cd256ec20b8ae9c4Thomas Hindoe Paaboel Andersen if (lock->scope == DAV_LOCKSCOPE_EXCLUSIVE) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek return dav_new_error(p, HTTP_LOCKED, 0,
6b94875fa3a7280e7f1c0df11027b927b6961edeRichard Maw "The requested resource is already "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "locked exclusively.");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** The locks on the resource (if any) are all shared. Set the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** <seen_locktoken> flag to indicate that we do not need to find
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** the locks in an If: header.
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** For methods other than LOCK:
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** If we have no locks, then <seen_locktoken> can be set to true --
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt ** pretending that we've already met the requirement of seeing one
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** of the resource's locks in the If: header.
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** Otherwise, it must be cleared and we'll look for one.
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** If there is no If: header, then we can shortcut some logic:
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** 1) if we do not need to find a locktoken in the (non-existent) If:
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** header, then we are successful.
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** 2) if we must find a locktoken in the (non-existent) If: header, then
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering "This resource is locked and an \"If:\" header "
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering "was not supplied to allow access to the "
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering /* the If: header is present */
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** If a dummy header is present (because of a Lock-Token: header), then
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** we are required to find that token in this resource's set of locks.
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** If we have no locks, then we immediately fail.
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** This is a 400 (Bad Request) since they should only submit a locktoken
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt ** that actually exists.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** Don't issue this response if we're talking about the parent resource.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** It is okay for that resource to NOT have this locktoken.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** (in fact, it certainly will not: a dummy_header only occurs for the
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** UNLOCK method, the parent is checked only for locknull resources,
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** and the parent certainly does not have the (locknull's) locktoken)
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering if (lock_list == NULL && if_header->dummy_header) {
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering "The locktoken specified in the \"Lock-Token:\" "
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering "header is invalid because this resource has no "
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering "outstanding locks.");
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Prepare the input URI. We want the URI to never have a trailing slash.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** When URIs are placed into the dav_if_header structure, they are
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering ** guaranteed to never have a trailing slash. If the URIs are equivalent,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** then it doesn't matter if they both lack a trailing slash -- they're
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** still equivalent.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Note: we could also ensure that a trailing slash is present on both
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** URIs, but the majority of URIs provided to us via a resource walk
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** will not contain that trailing slash.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek dav_set_bufsize(p, pbuf, uri_len);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek memcpy(pbuf->buf, uri, uri_len);
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* get the resource's etag; we may need it during the checks */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering etag = (*resource->hooks->getetag)(resource);
b938cb902c3b5bca807a94b277672c64d6767886Jan Engelhardt /* how many state_lists apply to this URI? */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* If there are if-headers, fail if this resource
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering * does not match at least one state_list.
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* DBG2("uri=<%s> if_uri=<%s>", uri, ifhdr_scan->uri ? ifhdr_scan->uri : "(no uri)"); */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering || memcmp(uri, ifhdr_scan->uri, uri_len) != 0)) {
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt ** A tagged-list's URI doesn't match this resource's URI.
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering ** Skip to the next state_list to see if it will match.
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering /* this state_list applies to this resource */
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering ** ### only one state_list should ever apply! a no-tag, or a tagged
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering ** ### where S9.4.2 states only one can match.
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering ** ### revamp this code to loop thru ifhdr_scan until we find the
91913f584af38b29a816cca959ba648acd60ac9fLennart Poettering ** ### matching state_list. process it. stop.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* To succeed, resource must match *all* of the states
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * specified in the state_list.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek for (state_list = ifhdr_scan->state;
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek state_list = state_list->next) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek int mismatch = strcmp(state_list->etag, etag);
7de304525deafe4eb86060321e39787138dbbadfLennart Poettering if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) {
7de304525deafe4eb86060321e39787138dbbadfLennart Poettering ** The specified entity-tag does not match the
b43d75c378d919900e5c1e82a82e3e17dd3de9f9Lennart Poettering ** entity-tag on the resource. This state_list is
b43d75c378d919900e5c1e82a82e3e17dd3de9f9Lennart Poettering ** not going to match. Bust outta here.
b43d75c378d919900e5c1e82a82e3e17dd3de9f9Lennart Poettering "an entity-tag was specified, but the resource's "
b43d75c378d919900e5c1e82a82e3e17dd3de9f9Lennart Poettering "actual ETag does not match.";
af40e5d3acbdcff09c8809cd1b86ecf8871f65f0Lennart Poettering else if (state_list->condition == DAV_IF_COND_NOT
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** The specified entity-tag DOES match the
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** entity-tag on the resource. This state_list is
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** not going to match. Bust outta here.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "an entity-tag was specified using the \"Not\" form, "
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "but the resource's actual ETag matches the provided "
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt "entity-tag.";
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if (state_list->condition == DAV_IF_COND_NOT) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* the locktoken is definitely not there! (success) */
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt /* condition == DAV_IF_COND_NORMAL */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** If no lockdb is provided, then validation fails for
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** this state_list (NORMAL means we were supposed to
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** find the token, which we obviously cannot do without
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** a lock database).
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** Go and try the next state list.
e0ea94c1e2ab3930c85c6057189a2a829a13a800Lennart Poettering "a State-token was supplied, but a lock database "
e0ea94c1e2ab3930c85c6057189a2a829a13a800Lennart Poettering "is not available for to provide the required lock.";
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* Resource validation 'fails' if:
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * ANY of the lock->locktokens match
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * a NOT state_list->locktoken,
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * NONE of the lock->locktokens match
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek * a NORMAL state_list->locktoken.
ac92ced5bb41def1d90f871d6c8cfec2b03c0c7dBenjamin Franzke for (lock = lock_list; lock != NULL; lock = lock->next) {
ac92ced5bb41def1d90f871d6c8cfec2b03c0c7dBenjamin Franzke DBG2("compare: rsrc=%s ifhdr=%s",
ac92ced5bb41def1d90f871d6c8cfec2b03c0c7dBenjamin Franzke (*locks_hooks->format_locktoken)(p, lock->locktoken),
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek (*locks_hooks->format_locktoken)(p, state_list->locktoken));
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* nothing to do if the locktokens do not match. */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek if ((*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** We have now matched up one of the resource's locktokens
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** to a locktoken in a State-token in the If: header.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** Note this fact, so that we can pass the overall
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering ** requirement of seeing at least one of the resource's
a8eaaee72a2f06e0fb64fb71de3b71ecba31dafbJan Engelhardt ** locktokens.
6e9efa59209d48fc69a456fbadb2b5c113f503a6Lennart Poettering if (state_list->condition == DAV_IF_COND_NOT) {
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering ** This state requires that the specified locktoken
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering ** is NOT present on the resource. But we just found
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering ** it. There is no way this state-list can now
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering ** succeed, so go try another one.
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering "a State-token was supplied, which used a "
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering "\"Not\" condition. The State-token was found "
ef3100e9637adda26fa19e7ee8606788320dcde3Lennart Poettering "in the locks on this resource";
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* condition == DAV_IF_COND_NORMAL */
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek /* Validate auth_user: If an authenticated user created
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** the lock, only the same user may submit that locktoken
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** to manipulate a resource.
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek strcmp(lock->auth_user, r->user))) {
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek errmsg = apr_pstrcat(p, "User \"",
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek "\" submitted a locktoken created "
16eb4024887b1b79fc56706fda25eadaecdef2d4Zbigniew Jędrzejewski-Szmek return dav_new_error(p, HTTP_UNAUTHORIZED, 0, errmsg);
798d3a524ea57aaf40cb53858aaa45ec702f012dZbigniew Jędrzejewski-Szmek ** We just matched a specified State-Token to one of the
19887cd06a3af2f045e763986eda19e208bd3f85Zbigniew Jędrzejewski-Szmek ** resource's locktokens.
if (num_matched == 0
reason =
goto state_list_failed;
if (seen_locktoken) {
return NULL;
if (num_that_apply == 0) {
if (seen_locktoken)
return NULL;
locks_hooks)) {
return NULL;
apr_psprintf(p,
return NULL;
return NULL;
return err;
return NULL;
int result;
int lock_db_opened_locally = 0;
#if DAV_DEBUG
return err;
return err;
ctx.r = r;
&work_buf, r);
return err;
return err;
return NULL;
--idx;
return s2;
/* see mod_dav.h for docco */
const char *ws_uri;
return HTTP_BAD_REQUEST;
return OK;
/* see mod_dav.h for docco */
const char **target,
int *is_label)
*is_label = 0;
return HTTP_BAD_REQUEST;
return HTTP_BAD_REQUEST;
return OK;
/* see mod_dav.h for docco */
int parent_only,
const char *body;
if (!parent_only) {
return NULL;
!= NULL)
if (!parent_only) {
return NULL;
!= NULL)
return NULL;
/* see mod_dav.h for docco */
request_rec *r,
int undo,
const char *body;
if (undo)
if (undo)
return NULL;