842ae4bd224140319ae7feec1872b93dfd491143fielding/* Licensed to the Apache Software Foundation (ASF) under one or more
842ae4bd224140319ae7feec1872b93dfd491143fielding * contributor license agreements. See the NOTICE file distributed with
842ae4bd224140319ae7feec1872b93dfd491143fielding * this work for additional information regarding copyright ownership.
842ae4bd224140319ae7feec1872b93dfd491143fielding * The ASF licenses this file to You under the Apache License, Version 2.0
842ae4bd224140319ae7feec1872b93dfd491143fielding * (the "License"); you may not use this file except in compliance with
842ae4bd224140319ae7feec1872b93dfd491143fielding * the License. You may obtain a copy of the License at
71da3cca78eea6010f89b139ecadb79e6d213c4fnd * Unless required by applicable law or agreed to in writing, software
71da3cca78eea6010f89b139ecadb79e6d213c4fnd * distributed under the License is distributed on an "AS IS" BASIS,
71da3cca78eea6010f89b139ecadb79e6d213c4fnd * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
71da3cca78eea6010f89b139ecadb79e6d213c4fnd * See the License for the specific language governing permissions and
71da3cca78eea6010f89b139ecadb79e6d213c4fnd * limitations under the License.
b0fb330a8581c8bfab5e523084f9f39264a52b12gstein** DAV extension module for Apache 2.0.*
f4c310fd2555c6faca1f980f00b161eadb089023gstein** - various utilities, repository-independent
99d46a23c6eac800f327b29f8009f7d7da986230trawickDAV_DECLARE(dav_error*) dav_new_error(apr_pool_t *p, int status, int error_id,
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(dav_error*) dav_new_error_tag(apr_pool_t *p, int status,
99d46a23c6eac800f327b29f8009f7d7da986230trawick const char *desc,
5b03ba47ff7225cacb131f14b019332af27da960gstein const char *namespace,
5b03ba47ff7225cacb131f14b019332af27da960gstein const char *tagname)
99d46a23c6eac800f327b29f8009f7d7da986230trawick dav_error *err = dav_new_error(p, status, error_id, aprerr, desc);
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(dav_error*) dav_push_error(apr_pool_t *p, int status,
691db92094897494d6c31326108da20088bc175etrawickDAV_DECLARE(dav_error*) dav_join_error(dav_error *dest, dav_error *src)
691db92094897494d6c31326108da20088bc175etrawick /* src error doesn't exist so nothing to join just return dest */
691db92094897494d6c31326108da20088bc175etrawick /* dest error doesn't exist so nothing to join just return src */
691db92094897494d6c31326108da20088bc175etrawick /* find last error in dest stack */
691db92094897494d6c31326108da20088bc175etrawick /* add the src error onto end of dest stack and return it */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf,
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* grow the buffer if necessary */
f4c310fd2555c6faca1f980f00b161eadb089023gstein if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_set_bufsize(apr_pool_t * p, dav_buffer *pbuf,
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* NOTE: this does not retain prior contents */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* NOTE: this function is used to init the first pointer, too, since
f4c310fd2555c6faca1f980f00b161eadb089023gstein the PAD will be larger than alloc_len (0) for zeroed structures */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* grow if we don't have enough for the requested size plus padding */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* set the new length; min of MINSIZE */
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* initialize a buffer and copy the specified (null-term'd) string into it */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf,
98e9c4a310bb623ff788680f88b6bd200ff36a24wrowe const char *str)
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* append a string to the end of the buffer, adjust length */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf,
98e9c4a310bb623ff788680f88b6bd200ff36a24wrowe const char *str)
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* place a string on the end of the buffer, do NOT adjust length */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf,
98e9c4a310bb623ff788680f88b6bd200ff36a24wrowe const char *str)
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* place some memory on the end of a buffer; do NOT adjust length */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(void) dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf,
f4c310fd2555c6faca1f980f00b161eadb089023gstein** dav_lookup_uri()
f4c310fd2555c6faca1f980f00b161eadb089023gstein** Extension for ap_sub_req_lookup_uri() which can't handle absolute
f4c310fd2555c6faca1f980f00b161eadb089023gstein** URIs properly.
f4c310fd2555c6faca1f980f00b161eadb089023gstein** If NULL is returned, then an error occurred with parsing the URI or
f4c310fd2555c6faca1f980f00b161eadb089023gstein** the URI does not match the current server.
0206c121a68a63559b2e843288e81bcf16093e46jerenkrantzDAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri,
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* first thing to do is parse the URI into various components */
8b99f2a316c5e2fa6ab208206fdd7fc2bfc4a921dougm if (apr_uri_parse(r->pool, uri, &comp) != APR_SUCCESS) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe result.err.desc = "Invalid syntax in Destination URI.";
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* the URI must be an absoluteURI (WEBDAV S9.3) */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe result.err.desc = "Destination URI must be an absolute URI.";
48f35e10f195dd594d75738fc536bb885eda537cgstein /* the URI must not have a query (args) or a fragment */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Destination URI contains invalid components "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "(a query or a fragment).";
48f35e10f195dd594d75738fc536bb885eda537cgstein /* If the scheme or port was provided, then make sure that it matches
48f35e10f195dd594d75738fc536bb885eda537cgstein the scheme/port of this request. If the request must be absolute,
48f35e10f195dd594d75738fc536bb885eda537cgstein then require the (explicit/implicit) scheme/port be matching.
48f35e10f195dd594d75738fc536bb885eda537cgstein ### hmm. if a port wasn't provided (does the parse return port==0?),
48f35e10f195dd594d75738fc536bb885eda537cgstein ### but we're on a non-standard port, then we won't detect that the
48f35e10f195dd594d75738fc536bb885eda537cgstein ### URI's port implies the wrong one.
48f35e10f195dd594d75738fc536bb885eda537cgstein if (comp.scheme != NULL || comp.port != 0 || must_be_absolute)
48f35e10f195dd594d75738fc536bb885eda537cgstein /* ### not sure this works if the current request came in via https: */
48f35e10f195dd594d75738fc536bb885eda537cgstein /* insert a port if the URI did not contain one */
48f35e10f195dd594d75738fc536bb885eda537cgstein /* now, verify that the URI uses the same scheme as the current.
48f35e10f195dd594d75738fc536bb885eda537cgstein request. the port must match our port.
48f35e10f195dd594d75738fc536bb885eda537cgstein "Destination URI refers to "
48f35e10f195dd594d75738fc536bb885eda537cgstein "different scheme or port "
48f35e10f195dd594d75738fc536bb885eda537cgstein "(want: %s://hostname:%d)",
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* we have verified the scheme, port, and general structure */
e8f95a682820a599fe41b22977010636be5c2717jim ** Hrm. IE5 will pass unqualified hostnames for both the
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Host: and Destination: headers. This breaks the
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** http_vhost.c::matches_aliases function.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For now, qualify unqualified comp.hostnames with
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** r->server->server_hostname.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** ### this is a big hack. Apache should provide a better way.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** ### maybe the admin should list the unqualified hosts in a
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** ### <ServerAlias> block?
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe && (domain = strchr(r->server->server_hostname, '.')) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe comp.hostname = apr_pstrcat(r->pool, comp.hostname, domain, NULL);
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* now, if a hostname was provided, then verify that it represents the
f4c310fd2555c6faca1f980f00b161eadb089023gstein same server as the current connection. note that we just use our
f4c310fd2555c6faca1f980f00b161eadb089023gstein port, since we've verified the URI matches ours */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe result.err.desc = "Destination URI refers to a different server.";
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* we have verified that the requested URI denotes the same server as
f4c310fd2555c6faca1f980f00b161eadb089023gstein the current request. Therefore, we can use ap_sub_req_lookup_uri() */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* reconstruct a URI as just the path */
8b99f2a316c5e2fa6ab208206fdd7fc2bfc4a921dougm new_file = apr_uri_unparse(r->pool, &comp, APR_URI_UNP_OMITSITEPART);
f4c310fd2555c6faca1f980f00b161eadb089023gstein * Lookup the URI and return the sub-request. Note that we use the
f4c310fd2555c6faca1f980f00b161eadb089023gstein * same HTTP method on the destination. This allows the destination
f4c310fd2555c6faca1f980f00b161eadb089023gstein * to apply appropriate restrictions (e.g. readonly).
f958dac1550254a59b45f4655138bb34dad5e76egstein result.rnew = ap_sub_req_method_uri(r->method, new_file, r, NULL);
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* ---------------------------------------------------------------
f4c310fd2555c6faca1f980f00b161eadb089023gstein** XML UTILITY FUNCTIONS
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
0206c121a68a63559b2e843288e81bcf16093e46jerenkrantzDAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc,
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* find and return the (unique) child with a given DAV: tagname */
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if (child->ns == APR_XML_NS_DAV_ID && !strcmp(child->name, tagname))
f39230a531b23d94f86a087963299bbe2e431a4agstein/* gather up all the CDATA into a single string */
0946f90438dcf29a5fe5d9e21559b3b9d640bc12wroweDAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool,
1e2133fe37e6cbcd683233057ef62236bc8e5826trawick const char *found_text = NULL; /* initialize to avoid gcc warning */
ab78b55c6dc4431d2c68d6bb4d169ba1554290a8gstein for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
f39230a531b23d94f86a087963299bbe2e431a4agstein for (child = elem->first_child; child != NULL; child = child->next) {
ab78b55c6dc4431d2c68d6bb4d169ba1554290a8gstein /* some fast-path cases:
ab78b55c6dc4431d2c68d6bb4d169ba1554290a8gstein * 1) zero-length cdata
ab78b55c6dc4431d2c68d6bb4d169ba1554290a8gstein * 2) a single piece of cdata with no whitespace to strip
f39230a531b23d94f86a087963299bbe2e431a4agstein for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
f39230a531b23d94f86a087963299bbe2e431a4agstein for (child = elem->first_child; child != NULL; child = child->next) {
f39230a531b23d94f86a087963299bbe2e431a4agstein /* trim leading whitespace */
057480777378361da24068b75e3cb07b95fd6ffdbreser while (apr_isspace(*cdata)) { /* assume: return false for '\0' */
f39230a531b23d94f86a087963299bbe2e431a4agstein /* trim trailing whitespace */
b5989e567e4fac5b3ab1252024ae19b0a54893a7gsteinDAV_DECLARE(dav_xmlns_info *) dav_xmlns_create(apr_pool_t *pool)
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein /* this "should" not overwrite a prefix mapping */
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein apr_hash_set(xi->prefix_uri, prefix, APR_HASH_KEY_STRING, uri);
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein /* note: this may overwrite an existing URI->prefix mapping, but it
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein doesn't matter -- any prefix is usuable to specify the URI. */
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein apr_hash_set(xi->uri_prefix, uri, APR_HASH_KEY_STRING, prefix);
b5989e567e4fac5b3ab1252024ae19b0a54893a7gsteinDAV_DECLARE(const char *) dav_xmlns_add_uri(dav_xmlns_info *xi,
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein const char *uri)
b5989e567e4fac5b3ab1252024ae19b0a54893a7gsteinDAV_DECLARE(const char *) dav_xmlns_get_uri(dav_xmlns_info *xi,
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein const char *prefix)
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein return apr_hash_get(xi->prefix_uri, prefix, APR_HASH_KEY_STRING);
b5989e567e4fac5b3ab1252024ae19b0a54893a7gsteinDAV_DECLARE(const char *) dav_xmlns_get_prefix(dav_xmlns_info *xi,
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein const char *uri)
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein return apr_hash_get(xi->uri_prefix, uri, APR_HASH_KEY_STRING);
b5989e567e4fac5b3ab1252024ae19b0a54893a7gsteinDAV_DECLARE(void) dav_xmlns_generate(dav_xmlns_info *xi,
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein apr_hash_index_t *hi = apr_hash_first(xi->pool, xi->prefix_uri);
b5989e567e4fac5b3ab1252024ae19b0a54893a7gstein const char *s;
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* ---------------------------------------------------------------
f4c310fd2555c6faca1f980f00b161eadb089023gstein** Timeout header processing
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* dav_get_timeout: If the Timeout: header exists, return a time_t
f4c310fd2555c6faca1f980f00b161eadb089023gstein * when this lock is expected to expire. Otherwise, return
f4c310fd2555c6faca1f980f00b161eadb089023gstein * a time_t of DAV_TIMEOUT_INFINITE.
f4c310fd2555c6faca1f980f00b161eadb089023gstein * It's unclear if DAV clients are required to understand
f4c310fd2555c6faca1f980f00b161eadb089023gstein * Seconds-xxx and Infinity time values. We assume that they do.
f4c310fd2555c6faca1f980f00b161eadb089023gstein * In addition, for now, that's all we understand, too.
0206c121a68a63559b2e843288e81bcf16093e46jerenkrantzDAV_DECLARE(time_t) dav_get_timeout(request_rec *r)
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm const char *timeout_const = apr_table_get(r->headers_in, "Timeout");
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm const char *timeout = apr_pstrdup(r->pool, timeout_const), *val;
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* Use the first thing we understand, or infinity if
f4c310fd2555c6faca1f980f00b161eadb089023gstein * we don't understand anything.
f4c310fd2555c6faca1f980f00b161eadb089023gstein while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### We need to handle overflow better:
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * ### timeout will be <= 2^32 - 1
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* ---------------------------------------------------------------
f4c310fd2555c6faca1f980f00b161eadb089023gstein** If Header processing
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* add_if_resource returns a new if_header, linking it to next_ih.
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic dav_if_header *dav_add_if_resource(apr_pool_t *p, dav_if_header *next_ih,
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* add_if_state adds a condition to an if_header.
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic dav_error * dav_add_if_state(apr_pool_t *p, dav_if_header *ih,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe const char *state_token,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = (*locks_hooks->parse_locktoken)(p, state_token,
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton /* If the state token cannot be parsed, treat it as an
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton * unknown state; this will evaluate to "false" later
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton * during If header validation. */
5a8f3bcf803321e69b226d3b98314305a68a586cjerenkrantz if (err->error_id == DAV_ERR_LOCK_UNK_STATE_TOKEN) {
5a8f3bcf803321e69b226d3b98314305a68a586cjerenkrantz /* ### maybe add a higher-level description */
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* fetch_next_token returns the substring from str+1
f4c310fd2555c6faca1f980f00b161eadb089023gstein * to the next occurence of char term, or \0, whichever
f4c310fd2555c6faca1f980f00b161eadb089023gstein * occurs first. Leading whitespace is ignored.
f4c310fd2555c6faca1f980f00b161eadb089023gsteinstatic char *dav_fetch_next_token(char **str, char term)
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* dav_process_if_header:
f4c310fd2555c6faca1f980f00b161eadb089023gstein * If NULL (no error) is returned, then **if_header points to the
f4c310fd2555c6faca1f980f00b161eadb089023gstein * "If" productions structure (or NULL if "If" is not present).
f4c310fd2555c6faca1f980f00b161eadb089023gstein * ### this part is bogus:
f4c310fd2555c6faca1f980f00b161eadb089023gstein * If an error is encountered, the error is logged. Parent should
f4c310fd2555c6faca1f980f00b161eadb089023gstein * return err->status.
f4c310fd2555c6faca1f980f00b161eadb089023gsteinstatic dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe const char *uri = NULL; /* scope of current production; NULL=no-tag */
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
f4c310fd2555c6faca1f980f00b161eadb089023gstein enum {no_tagged, tagged, unknown} list_type = unknown;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm if ((str = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "If"))) == NULL)
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe switch(*str) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* Tagged-list production - following states apply to this uri */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe || ((uri = dav_fetch_next_token(&str, '>')) == NULL)) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Invalid If-header: unclosed \"<\" or "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "unexpected tagged-list production.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* 2518 specifies this must be an absolute URI; just take the
f4c310fd2555c6faca1f980f00b161eadb089023gstein * relative part for later comparison against r->uri */
f8033d657a57eab45af44368774d8beb3e4f7f35pquerna if ((rv = apr_uri_parse(r->pool, uri, &parsed_uri)) != APR_SUCCESS
f4c310fd2555c6faca1f980f00b161eadb089023gstein "Invalid URI in tagged If-header.");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* note that parsed_uri.path is allocated; we can trash it */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* clean up the URI a bit */
1aac1c71105133d669960501bdf2274e63561054minfrin /* the resources we will compare to have unencoded paths */
f0d00d46501a748e59cf7e0ab04f493d33833818rjung "Invalid percent encoded URI in "
f0d00d46501a748e59cf7e0ab04f493d33833818rjung "tagged If-header.");
28418cf4450c797588e74e91a3b22ed85f42423aminfrin if (uri_len > 1 && parsed_uri.path[uri_len - 1] == '/') {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* List production */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* If a uri has not been encountered, this is a No-Tagged-List */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((list = dav_fetch_next_token(&str, ')')) == NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Invalid If-header: unclosed \"(\".");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((ih = dav_add_if_resource(r->pool, ih, uri, uri_len)) == NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### dav_add_if_resource() should return an error for us! */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Internal server error parsing \"If:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "header.");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* List is the entire production (in a uri scope) */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe switch (*list) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((state_token = dav_fetch_next_token(&list, '>')) == NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### add a description to this error */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_opaquelock,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### maybe add a higher level description */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((state_token = dav_fetch_next_token(&list, ']')) == NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### add a description to this error */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = dav_add_if_state(r->pool, ih, state_token, dav_if_etag,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### maybe add a higher level description */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Invalid \"If:\" header: "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Multiple \"not\" entries "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "for the same state.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein "Invalid \"If:\" "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "header: Unexpected "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "character encountered "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "(0x%02x, '%c').",
f4c310fd2555c6faca1f980f00b161eadb089023gstein "Invalid \"If:\" header: "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "Unexpected character "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "encountered (0x%02x, '%c').",
f4c310fd2555c6faca1f980f00b161eadb089023gsteinstatic int dav_find_submitted_locktoken(const dav_if_header *if_header,
f4c310fd2555c6faca1f980f00b161eadb089023gstein for (; if_header != NULL; if_header = if_header->next) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* given state_list->locktoken, match it */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** The resource will have one or more lock tokens. We only
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** need to match one of them against any token in the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If: header.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** One token case: It is an exclusive or shared lock. Either
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** way, we must find it.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** N token case: They are shared locks. By policy, we need
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** to match only one. The resource's other
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** tokens may belong to somebody else (so we
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** shouldn't see them in the If: header anyway)
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe for (lock = lock_list; lock != NULL; lock = lock->next) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if (!(*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* dav_validate_resource_state:
f4c310fd2555c6faca1f980f00b161eadb089023gstein * Returns NULL if path/uri meets if-header and lock requirements
1ccd992d37d62c8cb2056126f2234f64ec189bfddougmstatic dav_error * dav_validate_resource_state(apr_pool_t *p,
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_locks *locks_hooks = (lockdb ? lockdb->hooks : NULL);
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* DBG1("validate: <%s>", resource->uri); */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** The resource will have one of three states:
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 1) No locks. We have no special requirements that the user supply
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** specific locktokens. One of the state lists must match, and
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** we're done.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 2) One exclusive lock. The locktoken must appear *anywhere* in the
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If: header. Of course, asserting the token in a "Not" term will
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** quickly fail that state list :-). If the locktoken appears in
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** one of the state lists *and* one state list matches, then we're
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 3) One or more shared locks. One of the locktokens must appear
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** *anywhere* in the If: header. If one of the locktokens appears,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** and we match one state list, then we are done.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** The <seen_locktoken> variable determines whether we have seen one
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** of this resource's locktokens in the If: header.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If this is a new lock request, <flags> will contain the requested
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** lock scope. Three rules apply:
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 1) Do not require a (shared) locktoken to be seen (when we are
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** applying another shared lock)
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 2) If the scope is exclusive and we see any locks, fail.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 3) If the scope is shared and we see an exclusive lock, fail.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* we're in State 1. no locks. */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### hrm... we don't need to have these fully
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### resolved since we're only looking at the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### locktokens...
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### use get_locks w/ calltype=PARTIAL
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The locks could not be queried for "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "verification against a possible \"If:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* lock_list now determines whether we're in State 1, 2, or 3. */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For a new, exclusive lock: if any locks exist, fail.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For a new, shared lock: if an exclusive lock exists, fail.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** else, do not require a token to be seen.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Existing lock(s) on the requested resource "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "prevent an exclusive lock.");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** There are no locks, so we can pretend that we've already met
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** any requirement to find the resource's locks in an If: header.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Strictly speaking, we don't need this loop. Either the first
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** (and only) lock will be EXCLUSIVE, or none of them will be.
f4c310fd2555c6faca1f980f00b161eadb089023gstein for (lock = lock_list; lock != NULL; lock = lock->next) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The requested resource is already "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "locked exclusively.");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** The locks on the resource (if any) are all shared. Set the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** <seen_locktoken> flag to indicate that we do not need to find
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** the locks in an If: header.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** For methods other than LOCK:
89fc13eb43e6c78d3377e9ef0d79d343a138041bbreser ** If we have no locks or if the resource is not being modified
89fc13eb43e6c78d3377e9ef0d79d343a138041bbreser ** (per RFC 4918 the lock token is not required on resources
89fc13eb43e6c78d3377e9ef0d79d343a138041bbreser ** we are not changing), then <seen_locktoken> can be set to true --
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** pretending that we've already met the requirement of seeing one
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** of the resource's locks in the If: header.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Otherwise, it must be cleared and we'll look for one.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If there is no If: header, then we can shortcut some logic:
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 1) if we do not need to find a locktoken in the (non-existent) If:
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** header, then we are successful.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 2) if we must find a locktoken in the (non-existent) If: header, then
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** we fail.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "This resource is locked and an \"If:\" header "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "was not supplied to allow access to the "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "resource.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* the If: header is present */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If a dummy header is present (because of a Lock-Token: header), then
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** we are required to find that token in this resource's set of locks.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If we have no locks, then we immediately fail.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** This is a 400 (Bad Request) since they should only submit a locktoken
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** that actually exists.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Don't issue this response if we're talking about the parent resource.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** It is okay for that resource to NOT have this locktoken.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** (in fact, it certainly will not: a dummy_header only occurs for the
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** UNLOCK method, the parent is checked only for locknull resources,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** and the parent certainly does not have the (locknull's) locktoken)
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The locktoken specified in the \"Lock-Token:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "header is invalid because this resource has no "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "outstanding locks.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Prepare the input URI. We want the URI to never have a trailing slash.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** When URIs are placed into the dav_if_header structure, they are
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** guaranteed to never have a trailing slash. If the URIs are equivalent,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** then it doesn't matter if they both lack a trailing slash -- they're
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** still equivalent.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Note: we could also ensure that a trailing slash is present on both
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** URIs, but the majority of URIs provided to us via a resource walk
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** will not contain that trailing slash.
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* get the resource's etag; we may need it during the checks */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* how many state_lists apply to this URI? */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* If there are if-headers, fail if this resource
f4c310fd2555c6faca1f980f00b161eadb089023gstein * does not match at least one state_list.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* DBG2("uri=<%s> if_uri=<%s>", uri, ifhdr_scan->uri ? ifhdr_scan->uri : "(no uri)"); */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** A tagged-list's URI doesn't match this resource's URI.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Skip to the next state_list to see if it will match.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* this state_list applies to this resource */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### only one state_list should ever apply! a no-tag, or a tagged
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### where S9.4.2 states only one can match.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### revamp this code to loop thru ifhdr_scan until we find the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### matching state_list. process it. stop.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* To succeed, resource must match *all* of the states
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * specified in the state_list.
3ded62d7f2c9b12616d718b8c97d3044baa9ecdbjerenkrantz /* Do a weak entity comparison function as defined in
3ded62d7f2c9b12616d718b8c97d3044baa9ecdbjerenkrantz * RFC 2616 13.3.3.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** The specified entity-tag does not match the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** entity-tag on the resource. This state_list is
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** not going to match. Bust outta here.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "an entity-tag was specified, but the resource's "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "actual ETag does not match.";
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** The specified entity-tag DOES match the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** entity-tag on the resource. This state_list is
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** not going to match. Bust outta here.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "an entity-tag was specified using the \"Not\" form, "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "but the resource's actual ETag matches the provided "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "entity-tag.";
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* the locktoken is definitely not there! (success) */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* condition == DAV_IF_COND_NORMAL */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If no lockdb is provided, then validation fails for
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** this state_list (NORMAL means we were supposed to
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** find the token, which we obviously cannot do without
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** a lock database).
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Go and try the next state list.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "a State-token was supplied, but a lock database "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "is not available for to provide the required lock.";
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* Resource validation 'fails' if:
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * ANY of the lock->locktokens match
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * a NOT state_list->locktoken,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * NONE of the lock->locktokens match
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe * a NORMAL state_list->locktoken.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe for (lock = lock_list; lock != NULL; lock = lock->next) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe DBG2("compare: rsrc=%s ifhdr=%s",
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe (*locks_hooks->format_locktoken)(p, lock->locktoken),
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe (*locks_hooks->format_locktoken)(p, state_list->locktoken));
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* nothing to do if the locktokens do not match. */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We have now matched up one of the resource's locktokens
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** to a locktoken in a State-token in the If: header.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Note this fact, so that we can pass the overall
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** requirement of seeing at least one of the resource's
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** locktokens.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** This state requires that the specified locktoken
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** is NOT present on the resource. But we just found
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** it. There is no way this state-list can now
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** succeed, so go try another one.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "a State-token was supplied, which used a "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "\"Not\" condition. The State-token was found "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "in the locks on this resource";
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* condition == DAV_IF_COND_NORMAL */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* Validate auth_user: If an authenticated user created
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** the lock, only the same user may submit that locktoken
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** to manipulate a resource.
f4c310fd2555c6faca1f980f00b161eadb089023gstein "\" submitted a locktoken created "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "by user \"",
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(p, HTTP_FORBIDDEN, 0, 0, errmsg);
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We just matched a specified State-Token to one of the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** resource's locktokens.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Break out of the lock scan -- we only needed to find
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** one match (actually, there shouldn't be any other
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** matches in the lock list).
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We had a NORMAL state, meaning that we should have
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** found the State-Token within the locks on this
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** resource. We didn't, so this state_list must fail.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "a State-token was supplied, but it was not found "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "in the locks on this resource.";
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton /* Request is predicated on some unknown state token,
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton * which must be presumed to *not* match, so fail
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton * unless this is a Not condition. */
42f2b4b0e9b84dfd8acbb9c0da18a07c664e30a1jorton "an unknown state token was supplied";
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe } /* switch */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe } /* foreach ( state_list ) */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We've checked every state in this state_list and none of them
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** have failed. Since all of them succeeded, then we have a matching
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** state list and we may be done.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** The next requirement is that we have seen one of the resource's
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** locktokens (if any). If we have, then we can just exit. If we
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** haven't, then we need to keep looking.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* woo hoo! */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Haven't seen one. Let's break out of the search and just look
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** for a matching locktoken.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** This label is used when we detect that a state_list is not
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** going to match this resource. We bust out and try the next
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** state_list.
f4c310fd2555c6faca1f980f00b161eadb089023gstein } /* foreach ( ifhdr_scan ) */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** The above loop exits for one of two reasons:
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 1) a state_list matched and seen_locktoken is false.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** 2) all if_header structures were scanned, without (1) occurring
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We finished the loop without finding any matching state lists.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If none of the state_lists apply to this resource, then we
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** may have succeeded. Note that this scenario implies a
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** tagged-list with no matching state_lists. If the If: header
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** was a no-tag-list, then it would have applied to this resource.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** S9.4.2 states that when no state_lists apply, then the header
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** should be ignored.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If we saw one of the resource's locktokens, then we're done.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If we did not see a locktoken, then we fail.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We may have aborted the scan before seeing the locktoken.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** Rescan the If: header to see if we can find the locktoken
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** somewhere.
f80b9e9d38dff8bc3f51406475adb99d7fe888cegstein ** Note that seen_locktoken == 0 implies lock_list != NULL
f80b9e9d38dff8bc3f51406475adb99d7fe888cegstein ** which implies locks_hooks != NULL.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if (dav_find_submitted_locktoken(if_header, lock_list,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We found a match! We're set... none of the If: header
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** assertions apply (implicit success), and the If: header
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** specified the locktoken somewhere. We're done.
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(p, HTTP_LOCKED, 0 /* error_id */, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "This resource is locked and the \"If:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "header did not specify one of the "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "locktokens for this resource's lock(s).");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* else: one or more state_lists were applicable, but failed. */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If the dummy_header did not match, then they specified an
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** incorrect token in the Lock-Token header. Forget whether the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** If: statement matched or not... we'll tell them about the
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** bad Lock-Token first. That is considered a 400 (Bad Request).
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The locktoken specified in the "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "\"Lock-Token:\" header did not specify one "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "of this resource's locktoken(s).");
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The preconditions specified by the \"If:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "header did not match this resource.");
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(p, HTTP_PRECONDITION_FAILED, 0, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The precondition(s) specified by "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "the \"If:\" header did not match "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "this resource. At least one "
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* assert seen_locktoken == 0 */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** ifhdr_scan != NULL implies we found a matching state_list.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Since we're still here, it also means that we have not yet found
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** one the resource's locktokens in the If: header.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Scan all the if_headers and states looking for one of this
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** resource's locktokens. Note that we need to go back and scan them
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** all -- we may have aborted a scan with a failure before we saw a
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** matching token.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Note that seen_locktoken == 0 implies lock_list != NULL which implies
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** locks_hooks != NULL.
f4c310fd2555c6faca1f980f00b161eadb089023gstein if (dav_find_submitted_locktoken(if_header, lock_list, locks_hooks)) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** We found a match! We're set... we have a matching state list,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** and the If: header specified the locktoken somewhere. We're done.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** We had a matching state list, but the user agent did not specify one
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** of this resource's locktokens. Tell them so.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Note that we need to special-case the message on whether a "dummy"
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** header exists. If it exists, yet we didn't see a needed locktoken,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** then that implies the dummy header (Lock-Token header) did NOT
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** specify one of this resource's locktokens. (this implies something
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** in the real If: header matched)
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** We want to note the 400 (Bad Request) in favor of a 423 (Locked).
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The locktoken specified in the "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "\"Lock-Token:\" header did not specify one "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "of this resource's locktoken(s).");
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(p, HTTP_LOCKED, 1 /* error_id */, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "This resource is locked and the \"If:\" header "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "did not specify one of the "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "locktokens for this resource's lock(s).");
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* dav_validate_walker: Walker callback function to validate resource state */
52c1d304b1bd8e05da40a7cded2ecb9f0ba614c5gsteinstatic dav_error * dav_validate_walker(dav_walk_resource *wres, int calltype)
52c1d304b1bd8e05da40a7cded2ecb9f0ba614c5gstein if ((err = dav_validate_resource_state(ctx->w.pool, wres->resource,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* There was no error, so just bug out. */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If we have a serious server error, or if the request itself failed,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** then just return error (not a multistatus).
52c1d304b1bd8e05da40a7cded2ecb9f0ba614c5gstein || (*wres->resource->hooks->is_same_resource)(wres->resource,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### maybe push a higher-level description? */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* associate the error with the current URI */
ef1ab47476366272bf32be1439057f972bfe86edniq/* If-* header checking */
ef1ab47476366272bf32be1439057f972bfe86edniqstatic int dav_meets_conditions(request_rec *r, int resource_state)
ef1ab47476366272bf32be1439057f972bfe86edniq /* If-Match '*' fix. Resource existence not checked by ap_meets_conditions.
ef1ab47476366272bf32be1439057f972bfe86edniq * If-Match '*' request should succeed only if the resource exists. */
ef1ab47476366272bf32be1439057f972bfe86edniq if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
5d760c4a4841be07965080c4cec527d8f5172ef5rpluem if (if_match[0] == '*' && resource_state != DAV_RESOURCE_EXISTS)
5bfaaf573bacb45c1cf290ce85ecc676587e8a64jim /* If-None-Match '*' fix. If-None-Match '*' request should succeed
ef1ab47476366272bf32be1439057f972bfe86edniq * if the resource does not exist. */
ef1ab47476366272bf32be1439057f972bfe86edniq /* Note. If if_none_match != NULL, if_none_match is the culprit.
5bfaaf573bacb45c1cf290ce85ecc676587e8a64jim * Since, in presence of If-None-Match,
ef1ab47476366272bf32be1439057f972bfe86edniq * other If-* headers are undefined. */
5d760c4a4841be07965080c4cec527d8f5172ef5rpluem apr_table_get(r->headers_in, "If-None-Match")) != NULL) {
f4c310fd2555c6faca1f980f00b161eadb089023gstein** dav_validate_request: Validate if-headers (and check for locks) on:
f4c310fd2555c6faca1f980f00b161eadb089023gstein** (1) r->filename @ depth;
f4c310fd2555c6faca1f980f00b161eadb089023gstein** (2) Parent of r->filename if check_parent == 1
f4c310fd2555c6faca1f980f00b161eadb089023gstein** The check of parent should be done when it is necessary to verify that
f4c310fd2555c6faca1f980f00b161eadb089023gstein** the parent collection will accept a new member (ie current resource
f4c310fd2555c6faca1f980f00b161eadb089023gstein** state is null).
f4c310fd2555c6faca1f980f00b161eadb089023gstein** Return OK on successful validation.
f4c310fd2555c6faca1f980f00b161eadb089023gstein** On error, return appropriate HTTP_* code, and log error. If a multi-stat
f4c310fd2555c6faca1f980f00b161eadb089023gstein** error is necessary, response will point to it, else NULL.
e8f95a682820a599fe41b22977010636be5c2717jimDAV_DECLARE(dav_error *) dav_validate_request(request_rec *r,
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_repository *repos_hooks = resource->hooks;
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** ### bleck. we can't return errors for other URIs unless we have
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** ### a "response" ptr.
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "DESIGN ERROR: dav_validate_request called "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "with depth>0, but no response ptr.");
ef1ab47476366272bf32be1439057f972bfe86edniq /* Set the ETag header required by dav_meets_conditions() */
e8f95a682820a599fe41b22977010636be5c2717jim /* Do the standard checks for conditional requests using
f4c310fd2555c6faca1f980f00b161eadb089023gstein * If-..-Since, If-Match etc */
d64231e66711504d8e33d594fc5c27ae86e7b629rpluem resource_state = dav_get_resource_state(r, resource);
d64231e66711504d8e33d594fc5c27ae86e7b629rpluem * If we have set an ETag to headers out above for
d64231e66711504d8e33d594fc5c27ae86e7b629rpluem * dav_meets_conditions() revert this here as we do not want to set
d64231e66711504d8e33d594fc5c27ae86e7b629rpluem * the ETag in responses to requests with methods where this might not
d64231e66711504d8e33d594fc5c27ae86e7b629rpluem * be desired.
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* always parse (and later process) the If: header */
f4c310fd2555c6faca1f980f00b161eadb089023gstein if ((err = dav_process_if_header(r, &if_header)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### maybe add higher-level description */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* If a locktoken was specified, create a dummy if_header with which
f4c310fd2555c6faca1f980f00b161eadb089023gstein * to validate resources. In the interim, figure out why DAV uses
f4c310fd2555c6faca1f980f00b161eadb089023gstein * locktokens in an if-header without a Lock-Token header to refresh
f4c310fd2555c6faca1f980f00b161eadb089023gstein * locks, but a Lock-Token header without an if-header to remove them.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ifhdr_new->state = apr_pcalloc(r->pool, sizeof(*ifhdr_new->state));
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If necessary, open the lock database (read-only, lazily);
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** the validation process may need to retrieve or update lock info.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Otherwise, assume provided lockdb is valid and opened rw.
f4c310fd2555c6faca1f980f00b161eadb089023gstein if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### maybe insert higher-level comment */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* (1) Validate the specified resource, at the specified depth */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* else: implies a 5xx status code occurred. */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe err = dav_validate_resource_state(r->pool, resource, lockdb,
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* (2) Validate the parent resource if requested */
866b521be8a30b2798ad3c3b73de5e965edd7c2fgstein err = (*repos_hooks->get_parent_resource)(resource, &parent_resource);
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Cannot access parent of repository root.");
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe err = dav_validate_resource_state(r->pool, parent_resource, lockdb,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** This error occurred on the parent resource. This implies that
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** we have to create a multistatus response (to report the error
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** against a URI other than the Request-URI). "Convert" this error
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ** into a multistatus response.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe new_response = apr_pcalloc(r->pool, sizeof(*new_response));
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "A validation error has occurred on the parent resource, "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "preventing the operation on the resource specified by "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "the Request-URI.";
f4c310fd2555c6faca1f980f00b161eadb089023gstein " The error was: ",
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* assert: DAV_VALIDATE_PARENT implies response != NULL */
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** If we don't have a (serious) error, and we have multistatus responses,
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** then we need to construct an "error". This error will be the overall
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** status returned, and the multistatus responses will go into its body.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For certain methods, the overall error will be a 424. The default is
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** to construct a standard 207 response.
f4c310fd2555c6faca1f980f00b161eadb089023gstein if (err == NULL && response != NULL && *response != NULL) {
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* manufacture a 424 error to hold the multistatus response(s) */
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(r->pool, HTTP_FAILED_DEPENDENCY, 0, 0,
f4c310fd2555c6faca1f980f00b161eadb089023gstein "An error occurred on another resource, "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "preventing the requested operation on "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "this resource.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** Whatever caused the error, the Request-URI should have a 424
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** associated with it since we cannot complete the method.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For a LOCK operation, insert an empty DAV:lockdiscovery property.
f4c310fd2555c6faca1f980f00b161eadb089023gstein ** For other methods, return a simple 424.
f4c310fd2555c6faca1f980f00b161eadb089023gstein "<D:status>HTTP/1.1 424 Failed Dependency</D:status>" DEBUG_CR
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* create the 424 response */
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm new_response = apr_pcalloc(r->pool, sizeof(*new_response));
f4c310fd2555c6faca1f980f00b161eadb089023gstein "An error occurred on another resource, preventing the "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "requested operation on this resource.";
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* manufacture a 207 error for the multistatus response(s) */
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, 0,
f4c310fd2555c6faca1f980f00b161eadb089023gstein "Error(s) occurred on resources during the "
f4c310fd2555c6faca1f980f00b161eadb089023gstein "validation process.");
f4c310fd2555c6faca1f980f00b161eadb089023gstein/* dav_get_locktoken_list:
f4c310fd2555c6faca1f980f00b161eadb089023gstein * Sets ltl to a locktoken_list of all positive locktokens in header,
f4c310fd2555c6faca1f980f00b161eadb089023gstein * else NULL if no If-header, or no positive locktokens.
0206c121a68a63559b2e843288e81bcf16093e46jerenkrantzDAV_DECLARE(dav_error *) dav_get_locktoken_list(request_rec *r,
f4c310fd2555c6faca1f980f00b161eadb089023gstein if ((err = dav_process_if_header(r, &if_header)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### add a higher-level description? */
5f6996f1e3091d47af542c2437464bbc7e2e5b67jailletc if_state = if_header->state; /* Beginning of the if_state linked list */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe lock_token = apr_pcalloc(r->pool, sizeof(dav_locktoken_list));
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* No nodes added */
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(r->pool, HTTP_BAD_REQUEST, DAV_ERR_IF_ABSENT, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "No locktokens were specified in the \"If:\" "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "header, so the refresh could not be performed.");
f39230a531b23d94f86a087963299bbe2e431a4agstein#if 0 /* not needed right now... */
50bd75672ef114fb839dd9643c192b432fdf344cgsteinstatic const char *strip_white(const char *s, apr_pool_t *pool)
50bd75672ef114fb839dd9643c192b432fdf344cgstein /* trim leading whitespace */
50bd75672ef114fb839dd9643c192b432fdf344cgstein while (apr_isspace(*s)) /* assume: return false for '\0' */
50bd75672ef114fb839dd9643c192b432fdf344cgstein /* trim trailing whitespace */
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein/* dav_add_vary_header
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * If there were any headers in the request which require a Vary header
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * in the response, add it.
0206c121a68a63559b2e843288e81bcf16093e46jerenkrantzDAV_DECLARE(void) dav_add_vary_header(request_rec *in_req,
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(in_req);
f39230a531b23d94f86a087963299bbe2e431a4agstein /* ### this is probably all wrong... I think there is a function in
f39230a531b23d94f86a087963299bbe2e431a4agstein ### the Apache API to add things to the Vary header. need to check */
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* Only versioning headers require a Vary response header,
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * so only do this check if there is a versioning provider */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe const char *target = apr_table_get(in_req->headers_in, DAV_LABEL_HDR);
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe const char *vary = apr_table_get(out_req->headers_out, "Vary");
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* If Target-Selector specified, add it to the Vary header */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe vary = apr_pstrcat(out_req->pool, vary, "," DAV_LABEL_HDR,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein/* dav_can_auto_checkout
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * Determine whether auto-checkout is enabled for a resource.
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * r - the request_rec
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * resource - the resource
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * auto_version - the value of the auto_versionable hook for the resource
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * lockdb - pointer to lock database (opened if necessary)
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * auto_checkout - set to 1 if auto-checkout enabled
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
99d46a23c6eac800f327b29f8009f7d7da986230trawick return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0, 0,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "Auto-checkout is only enabled for locked resources, "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "but there is no lock provider.");
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, lockdb)) != NULL) {
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "Cannot open lock database to determine "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "auto-versioning behavior.",
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if ((err = dav_lock_query(*lockdb, resource, &lock_list)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "The locks could not be queried for "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "determining auto-versioning behavior.",
50bd75672ef114fb839dd9643c192b432fdf344cgstein/* see mod_dav.h for docco */
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* Initialize results */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if no versioning provider, just return */
f4c310fd2555c6faca1f980f00b161eadb089023gstein /* check parent resource if requested or if resource must be created */
866b521be8a30b2798ad3c3b73de5e965edd7c2fgstein if ((err = (*resource->hooks->get_parent_resource)(resource,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Missing one or more intermediate "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "collections. Cannot create resource %s.",
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if parent versioned and not checked out, see if it can be */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "<DAV:cannot-modify-checked-in-parent>");
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* Try to checkout the parent collection.
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * Note that auto-versioning can only be applied to a version selector,
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * so no separate working resource will be created.
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = (*vsn_hooks->checkout)(parent, 1 /*auto_checkout*/,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to auto-checkout parent collection. "
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Cannot create resource %s.",
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* remember that parent was checked out */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if only checking parent, we're done */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if creating a new resource, see if it should be version-controlled */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein && (*vsn_hooks->auto_versionable)(resource) == DAV_AUTO_VERSION_ALWAYS) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to create versioned resource %s.",
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* remember that resource was created */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if resource is versioned, make sure it is checked out */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "<DAV:cannot-modify-version-controlled-content>");
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* Auto-versioning can only be applied to version selectors, so
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein * no separate working resource will be created. */
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = (*vsn_hooks->checkout)(resource, 1 /*auto_checkout*/,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to checkout resource %s.",
83719c22db4a6d0575bb4f7f34382d7b185a6f74gstein /* remember that resource was checked out */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* make sure lock database is closed */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* if an error occurred, undo any auto-versioning operations already done */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, av_info);
50bd75672ef114fb839dd9643c192b432fdf344cgstein/* see mod_dav.h for docco */
f4c310fd2555c6faca1f980f00b161eadb089023gstein const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* If no versioning provider, this is a no-op */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* If undoing auto-checkouts, then do uncheckouts */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) {
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to undo auto-checkout "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "of resource %s.",
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe /* ### should we do anything with the response? */
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if ((err = (*resource->hooks->remove_resource)(resource,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to undo auto-version-control "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "of resource %s.",
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if (av_info->parent_resource != NULL && av_info->parent_checkedout) {
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein if ((err = (*vsn_hooks->uncheckout)(av_info->parent_resource)) != NULL) {
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to undo auto-checkout "
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein "of parent collection %s.",
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe ap_escape_html(r->pool, av_info->parent_resource->uri)),
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* If the resource was checked out, and auto-checkin is enabled,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * then check it in.
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein auto_version = (*vsn_hooks->auto_versionable)(resource);
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein (unlock && (auto_version == DAV_AUTO_VERSION_LOCKED))) {
f4c310fd2555c6faca1f980f00b161eadb089023gstein return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to auto-checkin resource %s.",
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein /* If parent resource was checked out, and auto-checkin is enabled,
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein * then check it in.
4d0b0b6d8341c5e54b2081665fc91b4e4f781753gstein auto_version = (*vsn_hooks->auto_versionable)(av_info->parent_resource);
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe if ((err = (*vsn_hooks->checkin)(av_info->parent_resource,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
6f15570e3adc0faf87bf55f70857028276fc9e32wrowe "Unable to auto-checkin parent collection %s.",