Apache HTTPD
util.c
Go to the documentation of this file.
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*
18** DAV extension module for Apache 2.0.*
19** - various utilities, repository-independent
20*/
21
22#include "apr_strings.h"
23#include "apr_lib.h"
24
25#define APR_WANT_STRFUNC
26#include "apr_want.h"
27
28#include "mod_dav.h"
29
30#include "http_request.h"
31#include "http_config.h"
32#include "http_vhost.h"
33#include "http_log.h"
34#include "http_protocol.h"
35
37 apr_status_t aprerr, const char *desc)
38{
39 dav_error *err = apr_pcalloc(p, sizeof(*err));
40
41 /* DBG3("dav_new_error: %d %d %s", status, error_id, desc ? desc : "(no desc)"); */
42
43 err->status = status;
44 err->error_id = error_id;
45 err->desc = desc;
46 err->aprerr = aprerr;
47
48 return err;
49}
50
52 int error_id, apr_status_t aprerr,
53 const char *desc,
54 const char *namespace,
55 const char *tagname)
56{
57 dav_error *err = dav_new_error(p, status, error_id, aprerr, desc);
58
59 err->tagname = tagname;
60 err->namespace = namespace;
61
62 return err;
63}
64
65
67 int error_id, const char *desc,
68 dav_error *prev)
69{
70 dav_error *err = apr_pcalloc(p, sizeof(*err));
71
72 err->status = status;
73 err->error_id = error_id;
74 err->desc = desc;
75 err->prev = prev;
76
77 return err;
78}
79
81{
83
84 /* src error doesn't exist so nothing to join just return dest */
85 if (src == NULL) {
86 return dest;
87 }
88
89 /* dest error doesn't exist so nothing to join just return src */
90 if (curr == NULL) {
91 return src;
92 }
93
94 /* find last error in dest stack */
95 while (curr->prev != NULL) {
96 curr = curr->prev;
97 }
98
99 /* add the src error onto end of dest stack and return it */
100 curr->prev = src;
101 return dest;
102}
103
104/* ### Unclear if this was designed to be used with an uninitialized
105 * dav_buffer struct, but is used on by dav_lock_get_activelock().
106 * Hence check for pbuf->buf. */
109{
110 /* grow the buffer if necessary */
111 if (pbuf->cur_len + extra_needed > pbuf->alloc_len) {
112 char *newbuf;
113
114 pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD;
115 newbuf = apr_palloc(p, pbuf->alloc_len);
116 if (pbuf->buf)
117 memcpy(newbuf, pbuf->buf, pbuf->cur_len);
118 pbuf->buf = newbuf;
119 }
120}
121
124{
125 /* NOTE: this does not retain prior contents */
126
127 /* NOTE: this function is used to init the first pointer, too, since
128 the PAD will be larger than alloc_len (0) for zeroed structures */
129
130 /* grow if we don't have enough for the requested size plus padding */
131 if (size + DAV_BUFFER_PAD > pbuf->alloc_len) {
132 /* set the new length; min of MINSIZE */
133 pbuf->alloc_len = size + DAV_BUFFER_PAD;
134 if (pbuf->alloc_len < DAV_BUFFER_MINSIZE)
135 pbuf->alloc_len = DAV_BUFFER_MINSIZE;
136
137 pbuf->buf = apr_palloc(p, pbuf->alloc_len);
138 }
139 pbuf->cur_len = size;
140}
141
142
143/* initialize a buffer and copy the specified (null-term'd) string into it */
145 const char *str)
146{
147 dav_set_bufsize(p, pbuf, strlen(str));
148 memcpy(pbuf->buf, str, pbuf->cur_len + 1);
149}
150
151/* append a string to the end of the buffer, adjust length */
153 const char *str)
154{
155 apr_size_t len = strlen(str);
156
158 memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
159 pbuf->cur_len += len;
160}
161
162/* place a string on the end of the buffer, do NOT adjust length */
164 const char *str)
165{
166 apr_size_t len = strlen(str);
167
169 memcpy(pbuf->buf + pbuf->cur_len, str, len + 1);
170}
171
172/* place some memory on the end of a buffer; do NOT adjust length */
174 const void *mem, apr_size_t amt,
176{
178 memcpy(pbuf->buf + pbuf->cur_len, mem, amt);
179}
180
181/*
182** dav_lookup_uri()
183**
184** Extension for ap_sub_req_lookup_uri() which can't handle absolute
185** URIs properly.
186**
187** If NULL is returned, then an error occurred with parsing the URI or
188** the URI does not match the current server.
189*/
191 request_rec * r,
193{
195 const char *scheme;
198 char *new_file;
199 const char *domain;
200
201 /* first thing to do is parse the URI into various components */
202 if (apr_uri_parse(r->pool, uri, &comp) != APR_SUCCESS) {
203 result.err.status = HTTP_BAD_REQUEST;
204 result.err.desc = "Invalid syntax in Destination URI.";
205 return result;
206 }
207
208 /* the URI must be an absoluteURI (WEBDAV S9.3) */
209 if (comp.scheme == NULL && must_be_absolute) {
210 result.err.status = HTTP_BAD_REQUEST;
211 result.err.desc = "Destination URI must be an absolute URI.";
212 return result;
213 }
214
215 /* the URI must not have a query (args) or a fragment */
216 if (comp.query != NULL || comp.fragment != NULL) {
217 result.err.status = HTTP_BAD_REQUEST;
218 result.err.desc =
219 "Destination URI contains invalid components "
220 "(a query or a fragment).";
221 return result;
222 }
223
224 /* If the scheme or port was provided, then make sure that it matches
225 the scheme/port of this request. If the request must be absolute,
226 then require the (explicit/implicit) scheme/port be matching.
227
228 ### hmm. if a port wasn't provided (does the parse return port==0?),
229 ### but we're on a non-standard port, then we won't detect that the
230 ### URI's port implies the wrong one.
231 */
232 if (comp.scheme != NULL || comp.port != 0 || must_be_absolute)
233 {
234 /* ### not sure this works if the current request came in via https: */
235 scheme = r->parsed_uri.scheme;
236 if (scheme == NULL)
237 scheme = ap_http_scheme(r);
238
239 /* insert a port if the URI did not contain one */
240 if (comp.port == 0)
241 comp.port = apr_uri_port_of_scheme(comp.scheme);
242
243 /* now, verify that the URI uses the same scheme as the current.
244 request. the port must match our port.
245 */
247 if (ap_cstr_casecmp(comp.scheme, scheme) != 0
249 || comp.port != port
250#endif
251 ) {
252 result.err.status = HTTP_BAD_GATEWAY;
253 result.err.desc = apr_psprintf(r->pool,
254 "Destination URI refers to "
255 "different scheme or port "
256 "(%s://hostname:%d)" APR_EOL_STR
257 "(want: %s://hostname:%d)",
258 comp.scheme ? comp.scheme : scheme,
259 comp.port ? comp.port : port,
260 scheme, port);
261 return result;
262 }
263 }
264
265 /* we have verified the scheme, port, and general structure */
266
267 /*
268 ** Hrm. IE5 will pass unqualified hostnames for both the
269 ** Host: and Destination: headers. This breaks the
270 ** http_vhost.c::matches_aliases function.
271 **
272 ** For now, qualify unqualified comp.hostnames with
273 ** r->server->server_hostname.
274 **
275 ** ### this is a big hack. Apache should provide a better way.
276 ** ### maybe the admin should list the unqualified hosts in a
277 ** ### <ServerAlias> block?
278 */
279 if (comp.hostname != NULL
280 && strrchr(comp.hostname, '.') == NULL
281 && (domain = strchr(r->server->server_hostname, '.')) != NULL) {
282 comp.hostname = apr_pstrcat(r->pool, comp.hostname, domain, NULL);
283 }
284
285 /* now, if a hostname was provided, then verify that it represents the
286 same server as the current connection. note that we just use our
287 port, since we've verified the URI matches ours */
288#ifdef APACHE_PORT_HANDLING_IS_BUSTED
289 if (comp.hostname != NULL &&
290 !ap_matches_request_vhost(r, comp.hostname, port)) {
291 result.err.status = HTTP_BAD_GATEWAY;
292 result.err.desc = "Destination URI refers to a different server.";
293 return result;
294 }
295#endif
296
297 /* we have verified that the requested URI denotes the same server as
298 the current request. Therefore, we can use ap_sub_req_lookup_uri() */
299
300 /* reconstruct a URI as just the path */
302
303 /*
304 * Lookup the URI and return the sub-request. Note that we use the
305 * same HTTP method on the destination. This allows the destination
306 * to apply appropriate restrictions (e.g. readonly).
307 */
309
310 return result;
311}
312
313/* ---------------------------------------------------------------
314**
315** XML UTILITY FUNCTIONS
316*/
317
318/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
320 int ns, const char *tagname)
321{
322 return doc->root &&
323 doc->root->ns == ns &&
324 strcmp(doc->root->name, tagname) == 0;
325}
326
327/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
329 const char *tagname)
330{
331 return dav_validate_root_ns(doc, APR_XML_NS_DAV_ID, tagname);
332}
333
334/* find and return the next child with a tagname in the given namespace */
336 int ns, const char *tagname)
337{
338 apr_xml_elem *child = elem->next;
339
340 for (; child; child = child->next)
341 if (child->ns == ns && !strcmp(child->name, tagname))
342 return child;
343 return NULL;
344}
345
346/* find and return the (unique) child with a tagname in the given namespace */
348 int ns, const char *tagname)
349{
350 apr_xml_elem *child = elem->first_child;
351
352 for (; child; child = child->next)
353 if (child->ns == ns && !strcmp(child->name, tagname))
354 return child;
355 return NULL;
356}
357
358/* find and return the (unique) child with a given DAV: tagname */
360 const char *tagname)
361{
362 return dav_find_child_ns(elem, APR_XML_NS_DAV_ID, tagname);
363}
364
365/* find and return the attribute with a name in the given namespace */
367 int ns, const char *attrname)
368{
369 apr_xml_attr *attr = elem->attr;
370
371 for (; attr; attr = attr->next)
372 if (attr->ns == ns && !strcmp(attr->name, attrname))
373 return attr;
374 return NULL;
375}
376
377/* find and return the attribute with a given DAV: tagname */
383
384/* gather up all the CDATA into a single string */
386 int strip_white)
387{
388 apr_size_t len = 0;
389 apr_text *scan;
390 const apr_xml_elem *child;
391 char *cdata;
392 char *s;
394 const char *found_text = NULL; /* initialize to avoid gcc warning */
395 int found_count = 0;
396
397 for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
398 found_text = scan->text;
399 ++found_count;
400 len += strlen(found_text);
401 }
402
403 for (child = elem->first_child; child != NULL; child = child->next) {
404 for (scan = child->following_cdata.first;
405 scan != NULL;
406 scan = scan->next) {
407 found_text = scan->text;
408 ++found_count;
409 len += strlen(found_text);
410 }
411 }
412
413 /* some fast-path cases:
414 * 1) zero-length cdata
415 * 2) a single piece of cdata with no whitespace to strip
416 */
417 if (len == 0)
418 return "";
419 if (found_count == 1) {
420 if (!strip_white
422 && !apr_isspace(found_text[len - 1])))
423 return found_text;
424 }
425
426 cdata = s = apr_palloc(pool, len + 1);
427
428 for (scan = elem->first_cdata.first; scan != NULL; scan = scan->next) {
429 tlen = strlen(scan->text);
430 memcpy(s, scan->text, tlen);
431 s += tlen;
432 }
433
434 for (child = elem->first_child; child != NULL; child = child->next) {
435 for (scan = child->following_cdata.first;
436 scan != NULL;
437 scan = scan->next) {
438 tlen = strlen(scan->text);
439 memcpy(s, scan->text, tlen);
440 s += tlen;
441 }
442 }
443
444 *s = '\0';
445
446 if (strip_white) {
447 /* trim leading whitespace */
448 while (apr_isspace(*cdata)) { /* assume: return false for '\0' */
449 ++cdata;
450 --len;
451 }
452
453 /* trim trailing whitespace */
454 while (len-- > 0 && apr_isspace(cdata[len]))
455 continue;
456 cdata[len + 1] = '\0';
457 }
458
459 return cdata;
460}
461
463{
464 dav_xmlns_info *xi = apr_pcalloc(pool, sizeof(*xi));
465
466 xi->pool = pool;
467 xi->uri_prefix = apr_hash_make(pool);
468 xi->prefix_uri = apr_hash_make(pool);
469
470 return xi;
471}
472
474 const char *prefix, const char *uri)
475{
476 /* this "should" not overwrite a prefix mapping */
478
479 /* note: this may overwrite an existing URI->prefix mapping, but it
480 doesn't matter -- any prefix is usable to specify the URI. */
482}
483
485 const char *uri)
486{
487 const char *prefix;
488
489 if ((prefix = apr_hash_get(xi->uri_prefix, uri,
491 return prefix;
492
493 prefix = apr_psprintf(xi->pool, "g%d", xi->count++);
495 return prefix;
496}
497
499 const char *prefix)
500{
501 return apr_hash_get(xi->prefix_uri, prefix, APR_HASH_KEY_STRING);
502}
503
505 const char *uri)
506{
507 return apr_hash_get(xi->uri_prefix, uri, APR_HASH_KEY_STRING);
508}
509
512{
513 apr_hash_index_t *hi = apr_hash_first(xi->pool, xi->prefix_uri);
514
515 for (; hi != NULL; hi = apr_hash_next(hi)) {
516 const void *prefix;
517 void *uri;
518 const char *s;
519
521
522 s = apr_pstrcat(xi->pool, " xmlns:", (const char *)prefix, "=\"",
523 (const char *)uri, "\"", NULL);
524 apr_text_append(xi->pool, phdr, s);
525 }
526}
527
528/* ---------------------------------------------------------------
529**
530** Timeout header processing
531**
532*/
533
534/* dav_get_timeout: If the Timeout: header exists, return a time_t
535 * when this lock is expected to expire. Otherwise, return
536 * a time_t of DAV_TIMEOUT_INFINITE.
537 *
538 * It's unclear if DAV clients are required to understand
539 * Seconds-xxx and Infinity time values. We assume that they do.
540 * In addition, for now, that's all we understand, too.
541 */
543{
545
546 const char *timeout_const = apr_table_get(r->headers_in, "Timeout");
547 const char *timeout = apr_pstrdup(r->pool, timeout_const), *val;
548
549 if (timeout == NULL)
551
552 /* Use the first thing we understand, or infinity if
553 * we don't understand anything.
554 */
555
556 while ((val = ap_getword_white(r->pool, &timeout)) && strlen(val)) {
557 if (!strncmp(val, "Infinite", 8)) {
559 }
560
561 if (!strncmp(val, "Second-", 7)) {
562 val += 7;
563 /* ### We need to handle overflow better:
564 * ### timeout will be <= 2^32 - 1
565 */
566 expires = atol(val);
567 now = time(NULL);
568 return now + expires;
569 }
570 }
571
573}
574
575/* ---------------------------------------------------------------
576**
577** If Header processing
578**
579*/
580
581/* add_if_resource returns a new if_header, linking it to next_ih.
582 */
584 const char *uri, apr_size_t uri_len)
585{
586 dav_if_header *ih;
587
588 if ((ih = apr_pcalloc(p, sizeof(*ih))) == NULL)
589 return NULL;
590
591 ih->uri = uri;
592 ih->uri_len = uri_len;
593 ih->next = next_ih;
594
595 return ih;
596}
597
598/* add_if_state adds a condition to an if_header.
599 */
601 const char *state_token,
602 dav_if_state_type t, int condition,
604{
606
607 new_sl = apr_pcalloc(p, sizeof(*new_sl));
608
609 new_sl->condition = condition;
610 new_sl->type = t;
611
612 if (t == dav_if_opaquelock) {
613 dav_error *err;
614
615 if ((err = (*locks_hooks->parse_locktoken)(p, state_token,
616 &new_sl->locktoken)) != NULL) {
617 /* If the state token cannot be parsed, treat it as an
618 * unknown state; this will evaluate to "false" later
619 * during If header validation. */
620 if (err->error_id == DAV_ERR_LOCK_UNK_STATE_TOKEN) {
621 new_sl->type = dav_if_unknown;
622 }
623 else {
624 /* ### maybe add a higher-level description */
625 return err;
626 }
627 }
628 }
629 else
630 new_sl->etag = state_token;
631
632 new_sl->next = ih->state;
633 ih->state = new_sl;
634
635 return NULL;
636}
637
638/* fetch_next_token returns the substring from str+1
639 * to the next occurrence of char term, or \0, whichever
640 * occurs first. Leading whitespace is ignored.
641 */
642static char *dav_fetch_next_token(char **str, char term)
643{
644 char *sp;
645 char *token;
646
647 token = *str + 1;
648
649 while (*token && (*token == ' ' || *token == '\t'))
650 token++;
651
652 if ((sp = strchr(token, term)) == NULL)
653 return NULL;
654
655 *sp = '\0';
656 *str = sp;
657 return token;
658}
659
660/* dav_process_if_header:
661 *
662 * If NULL (no error) is returned, then **if_header points to the
663 * "If" productions structure (or NULL if "If" is not present).
664 *
665 * ### this part is bogus:
666 * If an error is encountered, the error is logged. Parent should
667 * return err->status.
668 */
670{
671 dav_error *err;
672 char *str;
673 char *list;
674 const char *state_token;
675 const char *uri = NULL; /* scope of current production; NULL=no-tag */
676 apr_size_t uri_len = 0;
677 apr_status_t rv;
678 dav_if_header *ih = NULL;
679 apr_uri_t parsed_uri;
681 enum {no_tagged, tagged, unknown} list_type = unknown;
682 int condition;
683
684 *p_ih = NULL;
685
686 if ((str = apr_pstrdup(r->pool, apr_table_get(r->headers_in, "If"))) == NULL)
687 return NULL;
688
689 while (*str) {
690 switch(*str) {
691 case '<':
692 /* Tagged-list production - following states apply to this uri */
693 if (list_type == no_tagged
694 || ((uri = dav_fetch_next_token(&str, '>')) == NULL)) {
697 "Invalid If-header: unclosed \"<\" or "
698 "unexpected tagged-list production.");
699 }
700
701 /* 2518 specifies this must be an absolute URI; just take the
702 * relative part for later comparison against r->uri */
703 if ((rv = apr_uri_parse(r->pool, uri, &parsed_uri)) != APR_SUCCESS
704 || !parsed_uri.path) {
707 "Invalid URI in tagged If-header.");
708 }
709 /* note that parsed_uri.path is allocated; we can trash it */
710
711 /* clean up the URI a bit */
712 if (!ap_normalize_path(parsed_uri.path,
717 "Invalid URI path tagged If-header.");
718 }
719
720 /* the resources we will compare to have unencoded paths */
721 if (ap_unescape_url(parsed_uri.path) != OK) {
724 "Invalid percent encoded URI in "
725 "tagged If-header.");
726 }
727
728 uri_len = strlen(parsed_uri.path);
729 if (uri_len > 1 && parsed_uri.path[uri_len - 1] == '/') {
730 parsed_uri.path[--uri_len] = '\0';
731 }
732
733 uri = parsed_uri.path;
735 break;
736
737 case '(':
738 /* List production */
739
740 /* If a uri has not been encountered, this is a No-Tagged-List */
741 if (list_type == unknown)
743
744 if ((list = dav_fetch_next_token(&str, ')')) == NULL) {
747 "Invalid If-header: unclosed \"(\".");
748 }
749
750 if ((ih = dav_add_if_resource(r->pool, ih, uri, uri_len)) == NULL) {
751 /* ### dav_add_if_resource() should return an error for us! */
754 "Internal server error parsing \"If:\" "
755 "header.");
756 }
757
758 condition = DAV_IF_COND_NORMAL;
759
760 while (*list) {
761 /* List is the entire production (in a uri scope) */
762
763 switch (*list) {
764 case '<':
765 if ((state_token = dav_fetch_next_token(&list, '>')) == NULL) {
766 /* ### add a description to this error */
769 }
770
772 condition, locks_hooks)) != NULL) {
773 /* ### maybe add a higher level description */
774 return err;
775 }
776 condition = DAV_IF_COND_NORMAL;
777 break;
778
779 case '[':
780 if ((state_token = dav_fetch_next_token(&list, ']')) == NULL) {
781 /* ### add a description to this error */
784 }
785
787 condition, locks_hooks)) != NULL) {
788 /* ### maybe add a higher level description */
789 return err;
790 }
791 condition = DAV_IF_COND_NORMAL;
792 break;
793
794 case 'N':
795 if (list[1] == 'o' && list[2] == 't') {
796 if (condition != DAV_IF_COND_NORMAL) {
799 "Invalid \"If:\" header: "
800 "Multiple \"not\" entries "
801 "for the same state.");
802 }
803 condition = DAV_IF_COND_NOT;
804 list += 2;
805 }
806 else {
809 "Invalid \"If:\" header: "
810 "Unexpected character in List");
811 }
812 break;
813
814 case ' ':
815 case '\t':
816 break;
817
818 default:
822 "Invalid \"If:\" "
823 "header: Unexpected "
824 "character encountered "
825 "(0x%02x, '%c').",
826 *list, *list));
827 }
828
829 list++;
830 }
831 break;
832
833 case ' ':
834 case '\t':
835 break;
836
837 default:
841 "Invalid \"If:\" header: "
842 "Unexpected character "
843 "encountered (0x%02x, '%c').",
844 *str, *str));
845 }
846
847 str++;
848 }
849
850 *p_ih = ih;
851 return NULL;
852}
853
854static int dav_find_submitted_locktoken(const dav_if_header *if_header,
855 const dav_lock *lock_list,
857{
858 for (; if_header != NULL; if_header = if_header->next) {
860
861 for (state_list = if_header->state;
862 state_list != NULL;
863 state_list = state_list->next) {
864
865 if (state_list->type == dav_if_opaquelock) {
866 const dav_lock *lock;
867
868 /* given state_list->locktoken, match it */
869
870 /*
871 ** The resource will have one or more lock tokens. We only
872 ** need to match one of them against any token in the
873 ** If: header.
874 **
875 ** One token case: It is an exclusive or shared lock. Either
876 ** way, we must find it.
877 **
878 ** N token case: They are shared locks. By policy, we need
879 ** to match only one. The resource's other
880 ** tokens may belong to somebody else (so we
881 ** shouldn't see them in the If: header anyway)
882 */
883 for (lock = lock_list; lock != NULL; lock = lock->next) {
884
885 if (!(*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
886 return 1;
887 }
888 }
889 }
890 }
891 }
892
893 return 0;
894}
895
896/* dav_validate_resource_state:
897 * Returns NULL if path/uri meets if-header and lock requirements
898 */
900 const dav_resource *resource,
901 dav_lockdb *lockdb,
902 const dav_if_header *if_header,
903 int flags,
905 request_rec *r)
906{
907 dav_error *err;
908 const char *uri;
909 const char *etag;
910 const dav_hooks_locks *locks_hooks = (lockdb ? lockdb->hooks : NULL);
914 dav_lock *lock;
915 int num_matched;
916 int num_that_apply;
917 int seen_locktoken;
918 apr_size_t uri_len;
919 const char *reason = NULL;
920
921 /* DBG1("validate: <%s>", resource->uri); */
922
923 /*
924 ** The resource will have one of three states:
925 **
926 ** 1) No locks. We have no special requirements that the user supply
927 ** specific locktokens. One of the state lists must match, and
928 ** we're done.
929 **
930 ** 2) One exclusive lock. The locktoken must appear *anywhere* in the
931 ** If: header. Of course, asserting the token in a "Not" term will
932 ** quickly fail that state list :-). If the locktoken appears in
933 ** one of the state lists *and* one state list matches, then we're
934 ** done.
935 **
936 ** 3) One or more shared locks. One of the locktokens must appear
937 ** *anywhere* in the If: header. If one of the locktokens appears,
938 ** and we match one state list, then we are done.
939 **
940 ** The <seen_locktoken> variable determines whether we have seen one
941 ** of this resource's locktokens in the If: header.
942 */
943
944 /*
945 ** If this is a new lock request, <flags> will contain the requested
946 ** lock scope. Three rules apply:
947 **
948 ** 1) Do not require a (shared) locktoken to be seen (when we are
949 ** applying another shared lock)
950 ** 2) If the scope is exclusive and we see any locks, fail.
951 ** 3) If the scope is shared and we see an exclusive lock, fail.
952 */
953
954 if (lockdb == NULL) {
955 /* we're in State 1. no locks. */
956 lock_list = NULL;
957 }
958 else {
959 /*
960 ** ### hrm... we don't need to have these fully
961 ** ### resolved since we're only looking at the
962 ** ### locktokens...
963 **
964 ** ### use get_locks w/ calltype=PARTIAL
965 */
966 if ((err = dav_lock_query(lockdb, resource, &lock_list)) != NULL) {
967 return dav_push_error(p,
969 "The locks could not be queried for "
970 "verification against a possible \"If:\" "
971 "header.",
972 err);
973 }
974
975 /* lock_list now determines whether we're in State 1, 2, or 3. */
976 }
977
978 /*
979 ** For a new, exclusive lock: if any locks exist, fail.
980 ** For a new, shared lock: if an exclusive lock exists, fail.
981 ** else, do not require a token to be seen.
982 */
984 if (lock_list != NULL) {
985 return dav_new_error(p, HTTP_LOCKED, 0, 0,
986 "Existing lock(s) on the requested resource "
987 "prevent an exclusive lock.");
988 }
989
990 /*
991 ** There are no locks, so we can pretend that we've already met
992 ** any requirement to find the resource's locks in an If: header.
993 */
994 seen_locktoken = 1;
995 }
996 else if (flags & DAV_LOCKSCOPE_SHARED) {
997 /*
998 ** Strictly speaking, we don't need this loop. Either the first
999 ** (and only) lock will be EXCLUSIVE, or none of them will be.
1000 */
1001 for (lock = lock_list; lock != NULL; lock = lock->next) {
1002 if (lock->scope == DAV_LOCKSCOPE_EXCLUSIVE) {
1003 return dav_new_error(p, HTTP_LOCKED, 0, 0,
1004 "The requested resource is already "
1005 "locked exclusively.");
1006 }
1007 }
1008
1009 /*
1010 ** The locks on the resource (if any) are all shared. Set the
1011 ** <seen_locktoken> flag to indicate that we do not need to find
1012 ** the locks in an If: header.
1013 */
1014 seen_locktoken = 1;
1015 }
1016 else {
1017 /*
1018 ** For methods other than LOCK:
1019 **
1020 ** If we have no locks or if the resource is not being modified
1021 ** (per RFC 4918 the lock token is not required on resources
1022 ** we are not changing), then <seen_locktoken> can be set to true --
1023 ** pretending that we've already met the requirement of seeing one
1024 ** of the resource's locks in the If: header.
1025 **
1026 ** Otherwise, it must be cleared and we'll look for one.
1027 */
1030 }
1031
1032 /*
1033 ** If there is no If: header, then we can shortcut some logic:
1034 **
1035 ** 1) if we do not need to find a locktoken in the (non-existent) If:
1036 ** header, then we are successful.
1037 **
1038 ** 2) if we must find a locktoken in the (non-existent) If: header, then
1039 ** we fail.
1040 */
1041 if (if_header == NULL) {
1042 if (seen_locktoken)
1043 return NULL;
1044
1045 return dav_new_error(p, HTTP_LOCKED, 0, 0,
1046 "This resource is locked and an \"If:\" header "
1047 "was not supplied to allow access to the "
1048 "resource.");
1049 }
1050 /* the If: header is present */
1051
1052 /*
1053 ** If a dummy header is present (because of a Lock-Token: header), then
1054 ** we are required to find that token in this resource's set of locks.
1055 ** If we have no locks, then we immediately fail.
1056 **
1057 ** This is a 400 (Bad Request) since they should only submit a locktoken
1058 ** that actually exists.
1059 **
1060 ** Don't issue this response if we're talking about the parent resource.
1061 ** It is okay for that resource to NOT have this locktoken.
1062 ** (in fact, it certainly will not: a dummy_header only occurs for the
1063 ** UNLOCK method, the parent is checked only for locknull resources,
1064 ** and the parent certainly does not have the (locknull's) locktoken)
1065 */
1066 if (lock_list == NULL && if_header->dummy_header) {
1068 return NULL;
1069 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
1070 "The locktoken specified in the \"Lock-Token:\" "
1071 "header is invalid because this resource has no "
1072 "outstanding locks.");
1073 }
1074
1075 /*
1076 ** Prepare the input URI. We want the URI to never have a trailing slash.
1077 **
1078 ** When URIs are placed into the dav_if_header structure, they are
1079 ** guaranteed to never have a trailing slash. If the URIs are equivalent,
1080 ** then it doesn't matter if they both lack a trailing slash -- they're
1081 ** still equivalent.
1082 **
1083 ** Note: we could also ensure that a trailing slash is present on both
1084 ** URIs, but the majority of URIs provided to us via a resource walk
1085 ** will not contain that trailing slash.
1086 */
1087 uri = resource->uri;
1088 uri_len = strlen(uri);
1089 if (uri[uri_len - 1] == '/') {
1090 dav_set_bufsize(p, pbuf, uri_len);
1091 memcpy(pbuf->buf, uri, uri_len);
1092 pbuf->buf[--uri_len] = '\0';
1093 uri = pbuf->buf;
1094 }
1095
1096 /* get the resource's etag; we may need it during the checks */
1097 etag = (*resource->hooks->getetag)(resource);
1098
1099 /* how many state_lists apply to this URI? */
1100 num_that_apply = 0;
1101
1102 /* If there are if-headers, fail if this resource
1103 * does not match at least one state_list.
1104 */
1105 for (ifhdr_scan = if_header;
1106 ifhdr_scan != NULL;
1108
1109 /* DBG2("uri=<%s> if_uri=<%s>", uri, ifhdr_scan->uri ? ifhdr_scan->uri : "(no uri)"); */
1110
1111 if (ifhdr_scan->uri != NULL
1112 && (uri_len != ifhdr_scan->uri_len
1113 || memcmp(uri, ifhdr_scan->uri, uri_len) != 0)) {
1114 /*
1115 ** A tagged-list's URI doesn't match this resource's URI.
1116 ** Skip to the next state_list to see if it will match.
1117 */
1118 continue;
1119 }
1120
1121 /* this state_list applies to this resource */
1122
1123 /*
1124 ** ### only one state_list should ever apply! a no-tag, or a tagged
1125 ** ### where S9.4.2 states only one can match.
1126 **
1127 ** ### revamp this code to loop thru ifhdr_scan until we find the
1128 ** ### matching state_list. process it. stop.
1129 */
1131
1132 /* To succeed, resource must match *all* of the states
1133 * specified in the state_list.
1134 */
1135 for (state_list = ifhdr_scan->state;
1136 state_list != NULL;
1137 state_list = state_list->next) {
1138
1139 switch(state_list->type) {
1140 case dav_if_etag:
1141 {
1142 const char *given_etag, *current_etag;
1143 int mismatch;
1144
1145 /* Do a weak entity comparison function as defined in
1146 * RFC 2616 13.3.3.
1147 */
1148 if (state_list->etag[0] == 'W' &&
1149 state_list->etag[1] == '/') {
1150 given_etag = state_list->etag + 2;
1151 }
1152 else {
1153 given_etag = state_list->etag;
1154 }
1155 if (etag[0] == 'W' &&
1156 etag[1] == '/') {
1157 current_etag = etag + 2;
1158 }
1159 else {
1160 current_etag = etag;
1161 }
1162
1164
1165 if (state_list->condition == DAV_IF_COND_NORMAL && mismatch) {
1166 /*
1167 ** The specified entity-tag does not match the
1168 ** entity-tag on the resource. This state_list is
1169 ** not going to match. Bust outta here.
1170 */
1171 reason =
1172 "an entity-tag was specified, but the resource's "
1173 "actual ETag does not match.";
1174 goto state_list_failed;
1175 }
1176 else if (state_list->condition == DAV_IF_COND_NOT
1177 && !mismatch) {
1178 /*
1179 ** The specified entity-tag DOES match the
1180 ** entity-tag on the resource. This state_list is
1181 ** not going to match. Bust outta here.
1182 */
1183 reason =
1184 "an entity-tag was specified using the \"Not\" form, "
1185 "but the resource's actual ETag matches the provided "
1186 "entity-tag.";
1187 goto state_list_failed;
1188 }
1189 break;
1190 }
1191
1192 case dav_if_opaquelock:
1193 if (lockdb == NULL) {
1194 if (state_list->condition == DAV_IF_COND_NOT) {
1195 /* the locktoken is definitely not there! (success) */
1196 continue;
1197 }
1198
1199 /* condition == DAV_IF_COND_NORMAL */
1200
1201 /*
1202 ** If no lockdb is provided, then validation fails for
1203 ** this state_list (NORMAL means we were supposed to
1204 ** find the token, which we obviously cannot do without
1205 ** a lock database).
1206 **
1207 ** Go and try the next state list.
1208 */
1209 reason =
1210 "a State-token was supplied, but a lock database "
1211 "is not available for to provide the required lock.";
1212 goto state_list_failed;
1213 }
1214
1215 /* Resource validation 'fails' if:
1216 * ANY of the lock->locktokens match
1217 * a NOT state_list->locktoken,
1218 * OR
1219 * NONE of the lock->locktokens match
1220 * a NORMAL state_list->locktoken.
1221 */
1222 num_matched = 0;
1223 for (lock = lock_list; lock != NULL; lock = lock->next) {
1224
1225 /*
1226 DBG2("compare: rsrc=%s ifhdr=%s",
1227 (*locks_hooks->format_locktoken)(p, lock->locktoken),
1228 (*locks_hooks->format_locktoken)(p, state_list->locktoken));
1229 */
1230
1231 /* nothing to do if the locktokens do not match. */
1232 if ((*locks_hooks->compare_locktoken)(state_list->locktoken, lock->locktoken)) {
1233 continue;
1234 }
1235
1236 /*
1237 ** We have now matched up one of the resource's locktokens
1238 ** to a locktoken in a State-token in the If: header.
1239 ** Note this fact, so that we can pass the overall
1240 ** requirement of seeing at least one of the resource's
1241 ** locktokens.
1242 */
1243 seen_locktoken = 1;
1244
1245 if (state_list->condition == DAV_IF_COND_NOT) {
1246 /*
1247 ** This state requires that the specified locktoken
1248 ** is NOT present on the resource. But we just found
1249 ** it. There is no way this state-list can now
1250 ** succeed, so go try another one.
1251 */
1252 reason =
1253 "a State-token was supplied, which used a "
1254 "\"Not\" condition. The State-token was found "
1255 "in the locks on this resource";
1256 goto state_list_failed;
1257 }
1258
1259 /* condition == DAV_IF_COND_NORMAL */
1260
1261 /* Validate auth_user: If an authenticated user created
1262 ** the lock, only the same user may submit that locktoken
1263 ** to manipulate a resource.
1264 */
1265 if (lock->auth_user &&
1266 (!r->user ||
1267 strcmp(lock->auth_user, r->user))) {
1268 const char *errmsg;
1269
1270 errmsg = apr_pstrcat(p, "User \"",
1271 r->user,
1272 "\" submitted a locktoken created "
1273 "by user \"",
1274 lock->auth_user, "\".", NULL);
1275 return dav_new_error(p, HTTP_FORBIDDEN, 0, 0, errmsg);
1276 }
1277
1278 /*
1279 ** We just matched a specified State-Token to one of the
1280 ** resource's locktokens.
1281 **
1282 ** Break out of the lock scan -- we only needed to find
1283 ** one match (actually, there shouldn't be any other
1284 ** matches in the lock list).
1285 */
1286 num_matched = 1;
1287 break;
1288 }
1289
1290 if (num_matched == 0
1291 && state_list->condition == DAV_IF_COND_NORMAL) {
1292 /*
1293 ** We had a NORMAL state, meaning that we should have
1294 ** found the State-Token within the locks on this
1295 ** resource. We didn't, so this state_list must fail.
1296 */
1297 reason =
1298 "a State-token was supplied, but it was not found "
1299 "in the locks on this resource.";
1300 goto state_list_failed;
1301 }
1302
1303 break;
1304
1305 case dav_if_unknown:
1306 /* Request is predicated on some unknown state token,
1307 * which must be presumed to *not* match, so fail
1308 * unless this is a Not condition. */
1309
1310 if (state_list->condition == DAV_IF_COND_NORMAL) {
1311 reason =
1312 "an unknown state token was supplied";
1313 goto state_list_failed;
1314 }
1315 break;
1316
1317 } /* switch */
1318 } /* foreach ( state_list ) */
1319
1320 /*
1321 ** We've checked every state in this state_list and none of them
1322 ** have failed. Since all of them succeeded, then we have a matching
1323 ** state list and we may be done.
1324 **
1325 ** The next requirement is that we have seen one of the resource's
1326 ** locktokens (if any). If we have, then we can just exit. If we
1327 ** haven't, then we need to keep looking.
1328 */
1329 if (seen_locktoken) {
1330 /* woo hoo! */
1331 return NULL;
1332 }
1333
1334 /*
1335 ** Haven't seen one. Let's break out of the search and just look
1336 ** for a matching locktoken.
1337 */
1338 break;
1339
1340 /*
1341 ** This label is used when we detect that a state_list is not
1342 ** going to match this resource. We bust out and try the next
1343 ** state_list.
1344 */
1346 ;
1347
1348 } /* foreach ( ifhdr_scan ) */
1349
1350 /*
1351 ** The above loop exits for one of two reasons:
1352 ** 1) a state_list matched and seen_locktoken is false.
1353 ** 2) all if_header structures were scanned, without (1) occurring
1354 */
1355
1356 if (ifhdr_scan == NULL) {
1357 /*
1358 ** We finished the loop without finding any matching state lists.
1359 */
1360
1361 /*
1362 ** If none of the state_lists apply to this resource, then we
1363 ** may have succeeded. Note that this scenario implies a
1364 ** tagged-list with no matching state_lists. If the If: header
1365 ** was a no-tag-list, then it would have applied to this resource.
1366 **
1367 ** S9.4.2 states that when no state_lists apply, then the header
1368 ** should be ignored.
1369 **
1370 ** If we saw one of the resource's locktokens, then we're done.
1371 ** If we did not see a locktoken, then we fail.
1372 */
1373 if (num_that_apply == 0) {
1374 if (seen_locktoken)
1375 return NULL;
1376
1377 /*
1378 ** We may have aborted the scan before seeing the locktoken.
1379 ** Rescan the If: header to see if we can find the locktoken
1380 ** somewhere.
1381 **
1382 ** Note that seen_locktoken == 0 implies lock_list != NULL
1383 ** which implies locks_hooks != NULL.
1384 */
1386 locks_hooks)) {
1387 /*
1388 ** We found a match! We're set... none of the If: header
1389 ** assertions apply (implicit success), and the If: header
1390 ** specified the locktoken somewhere. We're done.
1391 */
1392 return NULL;
1393 }
1394
1395 return dav_new_error(p, HTTP_LOCKED, 0 /* error_id */, 0,
1396 "This resource is locked and the \"If:\" "
1397 "header did not specify one of the "
1398 "locktokens for this resource's lock(s).");
1399 }
1400 /* else: one or more state_lists were applicable, but failed. */
1401
1402 /*
1403 ** If the dummy_header did not match, then they specified an
1404 ** incorrect token in the Lock-Token header. Forget whether the
1405 ** If: statement matched or not... we'll tell them about the
1406 ** bad Lock-Token first. That is considered a 400 (Bad Request).
1407 */
1408 if (if_header->dummy_header) {
1409 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
1410 "The locktoken specified in the "
1411 "\"Lock-Token:\" header did not specify one "
1412 "of this resource's locktoken(s).");
1413 }
1414
1415 if (reason == NULL) {
1417 "The preconditions specified by the \"If:\" "
1418 "header did not match this resource.");
1419 }
1420
1423 "The precondition(s) specified by "
1424 "the \"If:\" header did not match "
1425 "this resource. At least one "
1426 "failure is because: %s", reason));
1427 }
1428
1429 /* assert seen_locktoken == 0 */
1430
1431 /*
1432 ** ifhdr_scan != NULL implies we found a matching state_list.
1433 **
1434 ** Since we're still here, it also means that we have not yet found
1435 ** one the resource's locktokens in the If: header.
1436 **
1437 ** Scan all the if_headers and states looking for one of this
1438 ** resource's locktokens. Note that we need to go back and scan them
1439 ** all -- we may have aborted a scan with a failure before we saw a
1440 ** matching token.
1441 **
1442 ** Note that seen_locktoken == 0 implies lock_list != NULL which implies
1443 ** locks_hooks != NULL.
1444 */
1446 /*
1447 ** We found a match! We're set... we have a matching state list,
1448 ** and the If: header specified the locktoken somewhere. We're done.
1449 */
1450 return NULL;
1451 }
1452
1453 /*
1454 ** We had a matching state list, but the user agent did not specify one
1455 ** of this resource's locktokens. Tell them so.
1456 **
1457 ** Note that we need to special-case the message on whether a "dummy"
1458 ** header exists. If it exists, yet we didn't see a needed locktoken,
1459 ** then that implies the dummy header (Lock-Token header) did NOT
1460 ** specify one of this resource's locktokens. (this implies something
1461 ** in the real If: header matched)
1462 **
1463 ** We want to note the 400 (Bad Request) in favor of a 423 (Locked).
1464 */
1465 if (if_header->dummy_header) {
1466 return dav_new_error(p, HTTP_BAD_REQUEST, 0, 0,
1467 "The locktoken specified in the "
1468 "\"Lock-Token:\" header did not specify one "
1469 "of this resource's locktoken(s).");
1470 }
1471
1472 return dav_new_error(p, HTTP_LOCKED, 1 /* error_id */, 0,
1473 "This resource is locked and the \"If:\" header "
1474 "did not specify one of the "
1475 "locktokens for this resource's lock(s).");
1476}
1477
1478/* dav_validate_walker: Walker callback function to validate resource state */
1480{
1481 dav_walker_ctx *ctx = wres->walk_ctx;
1482 dav_error *err;
1483
1484 if ((err = dav_validate_resource_state(ctx->w.pool, wres->resource,
1485 ctx->w.lockdb,
1486 ctx->if_header, ctx->flags,
1487 &ctx->work_buf, ctx->r)) == NULL) {
1488 /* There was no error, so just bug out. */
1489 return NULL;
1490 }
1491
1492 /*
1493 ** If we have a serious server error, or if the request itself failed,
1494 ** then just return error (not a multistatus).
1495 */
1496 if (ap_is_HTTP_SERVER_ERROR(err->status)
1497 || (*wres->resource->hooks->is_same_resource)(wres->resource,
1498 ctx->w.root)) {
1499 /* ### maybe push a higher-level description? */
1500 return err;
1501 }
1502
1503 /* associate the error with the current URI */
1504 dav_add_response(wres, err->status, NULL);
1505
1506 return NULL;
1507}
1508
1509/* If-* header checking */
1511{
1512 const char *if_match, *if_none_match;
1513 int retVal;
1514
1515 /* If-Match '*' fix. Resource existence not checked by ap_meets_conditions.
1516 * If-Match '*' request should succeed only if the resource exists. */
1517 if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
1518 if (if_match[0] == '*' && resource_state != DAV_RESOURCE_EXISTS)
1520 }
1521
1523
1524 /* If-None-Match '*' fix. If-None-Match '*' request should succeed
1525 * if the resource does not exist. */
1527 /* Note. If if_none_match != NULL, if_none_match is the culprit.
1528 * Since, in presence of If-None-Match,
1529 * other If-* headers are undefined. */
1530 if ((if_none_match =
1531 apr_table_get(r->headers_in, "If-None-Match")) != NULL) {
1532 if (if_none_match[0] == '*'
1534 return OK;
1535 }
1536 }
1537 }
1538
1539 return retVal;
1540}
1541
1542/*
1543** dav_validate_request: Validate if-headers (and check for locks) on:
1544** (1) r->filename @ depth;
1545** (2) Parent of r->filename if check_parent == 1
1546**
1547** The check of parent should be done when it is necessary to verify that
1548** the parent collection will accept a new member (ie current resource
1549** state is null).
1550**
1551** Return OK on successful validation.
1552** On error, return appropriate HTTP_* code, and log error. If a multi-stat
1553** error is necessary, response will point to it, else NULL.
1554*/
1557 int depth,
1558 dav_locktoken *locktoken,
1559 dav_response **response,
1560 int flags,
1561 dav_lockdb *lockdb)
1562{
1563 dav_error *err;
1564 int result;
1565 dav_if_header *if_header;
1566 int lock_db_opened_locally = 0;
1569 dav_buffer work_buf = { 0 };
1571 int resource_state;
1572 const char *etag;
1573 int set_etag = 0;
1574
1575#if DAV_DEBUG
1576 if (depth && response == NULL) {
1577 /*
1578 ** ### bleck. we can't return errors for other URIs unless we have
1579 ** ### a "response" ptr.
1580 */
1582 "DESIGN ERROR: dav_validate_request called "
1583 "with depth>0, but no response ptr.");
1584 }
1585#endif
1586
1587 if (response != NULL)
1588 *response = NULL;
1589
1590 /* Set the ETag header required by dav_meets_conditions() */
1591 etag = apr_table_get(r->headers_out, "ETag");
1592 if (!etag) {
1593 etag = (*resource->hooks->getetag)(resource);
1594 if (etag && *etag) {
1595 apr_table_set(r->headers_out, "ETag", etag);
1596 set_etag = 1;
1597 }
1598 }
1599 /* Do the standard checks for conditional requests using
1600 * If-..-Since, If-Match etc */
1603 if (set_etag) {
1604 /*
1605 * If we have set an ETag to headers out above for
1606 * dav_meets_conditions() revert this here as we do not want to set
1607 * the ETag in responses to requests with methods where this might not
1608 * be desired.
1609 */
1610 apr_table_unset(r->headers_out, "ETag");
1611 }
1612 if (result != OK) {
1613 return dav_new_error(r->pool, result, 0, 0, NULL);
1614 }
1615
1616 /* always parse (and later process) the If: header */
1617 if ((err = dav_process_if_header(r, &if_header)) != NULL) {
1618 /* ### maybe add higher-level description */
1619 return err;
1620 }
1621
1622 /* If a locktoken was specified, create a dummy if_header with which
1623 * to validate resources. In the interim, figure out why DAV uses
1624 * locktokens in an if-header without a Lock-Token header to refresh
1625 * locks, but a Lock-Token header without an if-header to remove them.
1626 */
1627 if (locktoken != NULL) {
1629
1630 ifhdr_new = apr_pcalloc(r->pool, sizeof(*ifhdr_new));
1631 ifhdr_new->uri = resource->uri;
1632 ifhdr_new->uri_len = strlen(resource->uri);
1633 ifhdr_new->dummy_header = 1;
1634
1635 ifhdr_new->state = apr_pcalloc(r->pool, sizeof(*ifhdr_new->state));
1636 ifhdr_new->state->type = dav_if_opaquelock;
1637 ifhdr_new->state->condition = DAV_IF_COND_NORMAL;
1638 ifhdr_new->state->locktoken = locktoken;
1639
1640 ifhdr_new->next = if_header;
1641 if_header = ifhdr_new;
1642 }
1643
1644 /*
1645 ** If necessary, open the lock database (read-only, lazily);
1646 ** the validation process may need to retrieve or update lock info.
1647 ** Otherwise, assume provided lockdb is valid and opened rw.
1648 */
1649 if (lockdb == NULL) {
1650 if (locks_hooks != NULL) {
1651 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
1652 /* ### maybe insert higher-level comment */
1653 return err;
1654 }
1656 }
1657 }
1658
1659 /* (1) Validate the specified resource, at the specified depth.
1660 * Avoid the walk there is no if_header and we aren't planning
1661 * to modify this resource. */
1662 if (resource->exists && depth > 0 && !(!if_header && flags & DAV_VALIDATE_NO_MODIFY)) {
1663 dav_walker_ctx ctx = { { 0 } };
1665
1666 ctx.w.walk_type = DAV_WALKTYPE_NORMAL;
1667 ctx.w.func = dav_validate_walker;
1668 ctx.w.walk_ctx = &ctx;
1669 ctx.w.pool = r->pool;
1670 ctx.w.root = resource;
1671
1672 ctx.if_header = if_header;
1673 ctx.r = r;
1674 ctx.flags = flags;
1675
1676 if (lockdb != NULL) {
1677 ctx.w.lockdb = lockdb;
1678 ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL;
1679 }
1680
1681 err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
1682 if (err == NULL) {
1683 *response = multi_status;
1684 }
1685 /* else: implies a 5xx status code occurred. */
1686 }
1687 else {
1689 if_header, flags, &work_buf, r);
1690 }
1691
1692 /* (2) Validate the parent resource if requested */
1693 if (err == NULL && (flags & DAV_VALIDATE_PARENT)) {
1694 dav_resource *parent_resource;
1695
1696 err = (*repos_hooks->get_parent_resource)(resource, &parent_resource);
1697
1698 if (err == NULL && parent_resource == NULL) {
1700 "Cannot access parent of repository root.");
1701 }
1702 else if (err == NULL) {
1703 err = dav_validate_resource_state(r->pool, parent_resource, lockdb,
1704 if_header,
1706 &work_buf, r);
1707
1708 /*
1709 ** This error occurred on the parent resource. This implies that
1710 ** we have to create a multistatus response (to report the error
1711 ** against a URI other than the Request-URI). "Convert" this error
1712 ** into a multistatus response.
1713 */
1714 if (err != NULL) {
1716
1717 new_response->href = parent_resource->uri;
1718 new_response->status = err->status;
1719 new_response->desc =
1720 "A validation error has occurred on the parent resource, "
1721 "preventing the operation on the resource specified by "
1722 "the Request-URI.";
1723 if (err->desc != NULL) {
1724 new_response->desc = apr_pstrcat(r->pool,
1725 new_response->desc,
1726 " The error was: ",
1727 err->desc, NULL);
1728 }
1729
1730 /* assert: DAV_VALIDATE_PARENT implies response != NULL */
1731 new_response->next = *response;
1732 *response = new_response;
1733
1734 err = NULL;
1735 }
1736 }
1737 }
1738
1740 (*locks_hooks->close_lockdb)(lockdb);
1741
1742 /*
1743 ** If we don't have a (serious) error, and we have multistatus responses,
1744 ** then we need to construct an "error". This error will be the overall
1745 ** status returned, and the multistatus responses will go into its body.
1746 **
1747 ** For certain methods, the overall error will be a 424. The default is
1748 ** to construct a standard 207 response.
1749 */
1750 if (err == NULL && response != NULL && *response != NULL) {
1752
1753 if ((flags & DAV_VALIDATE_USE_424) != 0) {
1754 /* manufacture a 424 error to hold the multistatus response(s) */
1756 "An error occurred on another resource, "
1757 "preventing the requested operation on "
1758 "this resource.");
1759 }
1760
1761 /*
1762 ** Whatever caused the error, the Request-URI should have a 424
1763 ** associated with it since we cannot complete the method.
1764 **
1765 ** For a LOCK operation, insert an empty DAV:lockdiscovery property.
1766 ** For other methods, return a simple 424.
1767 */
1768 if ((flags & DAV_VALIDATE_ADD_LD) != 0) {
1769 propstat = apr_pcalloc(r->pool, sizeof(*propstat));
1770 propstat->text =
1771 "<D:propstat>" DEBUG_CR
1772 "<D:prop><D:lockdiscovery/></D:prop>" DEBUG_CR
1773 "<D:status>HTTP/1.1 424 Failed Dependency</D:status>" DEBUG_CR
1774 "</D:propstat>" DEBUG_CR;
1775 }
1776
1777 /* create the 424 response */
1779 new_response->href = resource->uri;
1781 new_response->propresult.propstats = propstat;
1782 new_response->desc =
1783 "An error occurred on another resource, preventing the "
1784 "requested operation on this resource.";
1785
1786 new_response->next = *response;
1787 *response = new_response;
1788
1789 /* manufacture a 207 error for the multistatus response(s) */
1790 return dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, 0,
1791 "Error(s) occurred on resources during the "
1792 "validation process.");
1793 }
1794
1795 return err;
1796}
1797
1798/* dav_get_locktoken_list:
1799 *
1800 * Sets ltl to a locktoken_list of all positive locktokens in header,
1801 * else NULL if no If-header, or no positive locktokens.
1802 */
1805{
1806 dav_error *err;
1807 dav_if_header *if_header;
1810
1811 *ltl = NULL;
1812
1813 if ((err = dav_process_if_header(r, &if_header)) != NULL) {
1814 /* ### add a higher-level description? */
1815 return err;
1816 }
1817
1818 while (if_header != NULL) {
1819 if_state = if_header->state; /* Beginning of the if_state linked list */
1820 while (if_state != NULL) {
1821 if (if_state->condition == DAV_IF_COND_NORMAL
1822 && if_state->type == dav_if_opaquelock) {
1824 lock_token->locktoken = if_state->locktoken;
1825 lock_token->next = *ltl;
1826 *ltl = lock_token;
1827 }
1828 if_state = if_state->next;
1829 }
1830 if_header = if_header->next;
1831 }
1832 if (*ltl == NULL) {
1833 /* No nodes added */
1835 "No locktokens were specified in the \"If:\" "
1836 "header, so the refresh could not be performed.");
1837 }
1838
1839 return NULL;
1840}
1841
1842#if 0 /* not needed right now... */
1843
1844static const char *strip_white(const char *s, apr_pool_t *pool)
1845{
1847
1848 /* trim leading whitespace */
1849 while (apr_isspace(*s)) /* assume: return false for '\0' */
1850 ++s;
1851
1852 /* trim trailing whitespace */
1853 idx = strlen(s) - 1;
1854 if (apr_isspace(s[idx])) {
1855 char *s2 = apr_pstrdup(pool, s);
1856
1857 while (apr_isspace(s2[idx]) && idx > 0)
1858 --idx;
1859 s2[idx + 1] = '\0';
1860 return s2;
1861 }
1862
1863 return s;
1864}
1865#endif
1866
1867#define DAV_LABEL_HDR "Label"
1868
1869/* dav_add_vary_header
1870 *
1871 * If there were any headers in the request which require a Vary header
1872 * in the response, add it.
1873 */
1876 const dav_resource *resource)
1877{
1878 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(in_req);
1879
1880 /* ### this is probably all wrong... I think there is a function in
1881 ### the Apache API to add things to the Vary header. need to check */
1882
1883 /* Only versioning headers require a Vary response header,
1884 * so only do this check if there is a versioning provider */
1885 if (vsn_hooks != NULL) {
1886 const char *target = apr_table_get(in_req->headers_in, DAV_LABEL_HDR);
1887
1888 /* If Target-Selector specified, add it to the Vary header */
1889 if (target != NULL) {
1890 const char *vary = apr_table_get(out_req->headers_out, "Vary");
1891
1892 if (vary == NULL)
1893 vary = DAV_LABEL_HDR;
1894 else
1895 vary = apr_pstrcat(out_req->pool, vary, "," DAV_LABEL_HDR,
1896 NULL);
1897
1898 apr_table_setn(out_req->headers_out, "Vary", vary);
1899 }
1900 }
1901}
1902
1903/* dav_can_auto_checkout
1904 *
1905 * Determine whether auto-checkout is enabled for a resource.
1906 * r - the request_rec
1907 * resource - the resource
1908 * auto_version - the value of the auto_versionable hook for the resource
1909 * lockdb - pointer to lock database (opened if necessary)
1910 * auto_checkout - set to 1 if auto-checkout enabled
1911 */
1913 request_rec *r,
1916 dav_lockdb **lockdb,
1917 int *auto_checkout)
1918{
1919 dav_error *err;
1921
1922 *auto_checkout = 0;
1923
1925 *auto_checkout = 1;
1926 }
1928 if (*lockdb == NULL) {
1930
1931 if (locks_hooks == NULL) {
1933 "Auto-checkout is only enabled for locked resources, "
1934 "but there is no lock provider.");
1935 }
1936
1937 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, lockdb)) != NULL) {
1939 "Cannot open lock database to determine "
1940 "auto-versioning behavior.",
1941 err);
1942 }
1943 }
1944
1945 if ((err = dav_lock_query(*lockdb, resource, &lock_list)) != NULL) {
1946 return dav_push_error(r->pool,
1948 "The locks could not be queried for "
1949 "determining auto-versioning behavior.",
1950 err);
1951 }
1952
1953 if (lock_list != NULL)
1954 *auto_checkout = 1;
1955 }
1956
1957 return NULL;
1958}
1959
1960/* see mod_dav.h for docco */
1962 request_rec *r,
1964 int parent_only,
1966{
1967 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
1968 dav_lockdb *lockdb = NULL;
1969 dav_error *err = NULL;
1970
1971 /* Initialize results */
1972 memset(av_info, 0, sizeof(*av_info));
1973
1974 /* if no versioning provider, just return */
1975 if (vsn_hooks == NULL)
1976 return NULL;
1977
1978 /* check parent resource if requested or if resource must be created */
1979 if (!resource->exists || parent_only) {
1981
1982 if ((err = (*resource->hooks->get_parent_resource)(resource,
1983 &parent)) != NULL)
1984 goto done;
1985
1986 if (parent == NULL || !parent->exists) {
1989 "Missing one or more intermediate "
1990 "collections. Cannot create resource %s.",
1991 ap_escape_html(r->pool, resource->uri)));
1992 goto done;
1993 }
1994
1995 av_info->parent_resource = parent;
1996
1997 /* if parent versioned and not checked out, see if it can be */
1998 if (parent->versioned && !parent->working) {
1999 int checkout_parent;
2000
2002 (*vsn_hooks->auto_versionable)(parent),
2003 &lockdb, &checkout_parent))
2004 != NULL) {
2005 goto done;
2006 }
2007
2008 if (!checkout_parent) {
2010 "<DAV:cannot-modify-checked-in-parent>");
2011 goto done;
2012 }
2013
2014 /* Try to checkout the parent collection.
2015 * Note that auto-versioning can only be applied to a version selector,
2016 * so no separate working resource will be created.
2017 */
2018 if ((err = (*vsn_hooks->checkout)(parent, 1 /*auto_checkout*/,
2019 0, 0, 0, NULL, NULL))
2020 != NULL)
2021 {
2024 "Unable to auto-checkout parent collection. "
2025 "Cannot create resource %s.",
2026 ap_escape_html(r->pool, resource->uri)),
2027 err);
2028 goto done;
2029 }
2030
2031 /* remember that parent was checked out */
2032 av_info->parent_checkedout = 1;
2033 }
2034 }
2035
2036 /* if only checking parent, we're done */
2037 if (parent_only)
2038 goto done;
2039
2040 /* if creating a new resource, see if it should be version-controlled */
2041 if (!resource->exists
2042 && (*vsn_hooks->auto_versionable)(resource) == DAV_AUTO_VERSION_ALWAYS) {
2043
2044 if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) {
2047 "Unable to create versioned resource %s.",
2048 ap_escape_html(r->pool, resource->uri)),
2049 err);
2050 goto done;
2051 }
2052
2053 /* remember that resource was created */
2054 av_info->resource_versioned = 1;
2055 }
2056
2057 /* if resource is versioned, make sure it is checked out */
2058 if (resource->versioned && !resource->working) {
2060
2062 (*vsn_hooks->auto_versionable)(resource),
2063 &lockdb, &checkout_resource)) != NULL) {
2064 goto done;
2065 }
2066
2067 if (!checkout_resource) {
2069 "<DAV:cannot-modify-version-controlled-content>");
2070 goto done;
2071 }
2072
2073 /* Auto-versioning can only be applied to version selectors, so
2074 * no separate working resource will be created. */
2075 if ((err = (*vsn_hooks->checkout)(resource, 1 /*auto_checkout*/,
2076 0, 0, 0, NULL, NULL))
2077 != NULL)
2078 {
2081 "Unable to checkout resource %s.",
2082 ap_escape_html(r->pool, resource->uri)),
2083 err);
2084 goto done;
2085 }
2086
2087 /* remember that resource was checked out */
2088 av_info->resource_checkedout = 1;
2089 }
2090
2091done:
2092
2093 /* make sure lock database is closed */
2094 if (lockdb != NULL)
2095 (*lockdb->hooks->close_lockdb)(lockdb);
2096
2097 /* if an error occurred, undo any auto-versioning operations already done */
2098 if (err != NULL) {
2099 dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, av_info);
2100 return err;
2101 }
2102
2103 return NULL;
2104}
2105
2106/* see mod_dav.h for docco */
2108 request_rec *r,
2110 int undo,
2111 int unlock,
2113{
2114 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
2115 dav_error *err = NULL;
2117
2118 /* If no versioning provider, this is a no-op */
2119 if (vsn_hooks == NULL)
2120 return NULL;
2121
2122 /* If undoing auto-checkouts, then do uncheckouts */
2123 if (undo) {
2124 if (resource != NULL) {
2125 if (av_info->resource_checkedout) {
2126 if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) {
2129 "Unable to undo auto-checkout "
2130 "of resource %s.",
2131 ap_escape_html(r->pool, resource->uri)),
2132 err);
2133 }
2134 }
2135
2136 if (av_info->resource_versioned) {
2137 dav_response *response;
2138
2139 /* ### should we do anything with the response? */
2140 if ((err = (*resource->hooks->remove_resource)(resource,
2141 &response)) != NULL) {
2144 "Unable to undo auto-version-control "
2145 "of resource %s.",
2146 ap_escape_html(r->pool, resource->uri)),
2147 err);
2148 }
2149 }
2150 }
2151
2152 if (av_info->parent_resource != NULL && av_info->parent_checkedout) {
2153 if ((err = (*vsn_hooks->uncheckout)(av_info->parent_resource)) != NULL) {
2156 "Unable to undo auto-checkout "
2157 "of parent collection %s.",
2158 ap_escape_html(r->pool, av_info->parent_resource->uri)),
2159 err);
2160 }
2161 }
2162
2163 return NULL;
2164 }
2165
2166 /* If the resource was checked out, and auto-checkin is enabled,
2167 * then check it in.
2168 */
2169 if (resource != NULL && resource->working
2170 && (unlock || av_info->resource_checkedout)) {
2171
2172 auto_version = (*vsn_hooks->auto_versionable)(resource);
2173
2176
2177 if ((err = (*vsn_hooks->checkin)(resource,
2178 0 /*keep_checked_out*/, NULL))
2179 != NULL) {
2182 "Unable to auto-checkin resource %s.",
2183 ap_escape_html(r->pool, resource->uri)),
2184 err);
2185 }
2186 }
2187 }
2188
2189 /* If parent resource was checked out, and auto-checkin is enabled,
2190 * then check it in.
2191 */
2192 if (!unlock
2193 && av_info->parent_checkedout
2194 && av_info->parent_resource != NULL
2195 && av_info->parent_resource->working) {
2196
2197 auto_version = (*vsn_hooks->auto_versionable)(av_info->parent_resource);
2198
2200 if ((err = (*vsn_hooks->checkin)(av_info->parent_resource,
2201 0 /*keep_checked_out*/, NULL))
2202 != NULL) {
2205 "Unable to auto-checkin parent collection %s.",
2206 ap_escape_html(r->pool, av_info->parent_resource->uri)),
2207 err);
2208 }
2209 }
2210 }
2211
2212 return NULL;
2213}
const char apr_size_t len
Definition ap_regex.h:187
APR general purpose library routines.
APR Strings library.
APR Standard Headers Support.
static sem_id lock
Definition threadpriv.c:21
request_rec * r
#define ap_http_scheme(r)
Definition httpd.h:297
#define OK
Definition httpd.h:456
int ap_meets_conditions(request_rec *r)
request_rec * ap_sub_req_method_uri(const char *method, const char *new_uri, const request_rec *r, ap_filter_t *next_filter)
Definition request.c:2242
const char apr_port_t port
Definition http_vhost.h:125
apr_brigade_flush void * ctx
const char * src
Definition apr_encode.h:167
const char apr_ssize_t int flags
Definition apr_encode.h:168
void ** resource
const char * uri
Definition apr_uri.h:159
#define APR_URI_UNP_OMITSITEPART
Definition apr_uri.h:64
const apr_xml_elem int apr_array_header_t int const char ** pbuf
Definition apr_xml.h:288
#define APR_XML_NS_DAV_ID
Definition apr_xml.h:133
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define HTTP_MULTI_STATUS
Definition httpd.h:497
#define HTTP_PRECONDITION_FAILED
Definition httpd.h:520
#define HTTP_BAD_GATEWAY
Definition httpd.h:537
#define HTTP_INTERNAL_SERVER_ERROR
Definition httpd.h:535
#define HTTP_FAILED_DEPENDENCY
Definition httpd.h:529
#define HTTP_LOCKED
Definition httpd.h:528
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_CONFLICT
Definition httpd.h:517
#define ap_is_HTTP_SERVER_ERROR(x)
Definition httpd.h:558
#define DAV_GET_HOOKS_LOCKS(r)
Definition mod_dav.h:808
dav_error * dav_new_error_tag(apr_pool_t *p, int status, int error_id, apr_status_t aprerr, const char *desc, const char *namespace, const char *tagname)
Definition util.c:51
void dav_buffer_init(apr_pool_t *p, dav_buffer *pbuf, const char *str)
Definition util.c:144
#define DAV_ERR_IF_UNCLOSED_PAREN
Definition mod_dav.h:214
dav_lookup_result dav_lookup_uri(const char *uri, request_rec *r, int must_be_absolute)
Definition util.c:190
#define DAV_ERR_IF_TAGGED
Definition mod_dav.h:213
void dav_buffer_append(apr_pool_t *p, dav_buffer *pbuf, const char *str)
Definition util.c:152
#define DAV_VALIDATE_NO_MODIFY
Definition mod_dav.h:1460
apr_xml_attr * dav_find_attr(const apr_xml_elem *elem, const char *attrname)
Definition util.c:378
void dav_buffer_place_mem(apr_pool_t *p, dav_buffer *pbuf, const void *mem, apr_size_t amt, apr_size_t pad)
Definition util.c:173
const char * dav_xmlns_get_prefix(dav_xmlns_info *xi, const char *uri)
Definition util.c:504
dav_auto_version
Definition mod_dav.h:2179
const char * dav_xmlns_get_uri(dav_xmlns_info *xi, const char *prefix)
Definition util.c:498
#define DAV_ERR_IF_MULTIPLE_NOT
Definition mod_dav.h:210
dav_error * dav_auto_checkin(request_rec *r, dav_resource *resource, int undo, int unlock, dav_auto_version_info *av_info)
Definition util.c:2107
void dav_add_vary_header(request_rec *in_req, request_rec *out_req, const dav_resource *resource)
Definition util.c:1874
#define DEBUG_CR
Definition mod_dav.h:65
dav_error * dav_lock_query(dav_lockdb *lockdb, const dav_resource *resource, dav_lock **locks)
Definition util_lock.c:369
int dav_validate_root_ns(const apr_xml_doc *doc, int ns, const char *tagname)
Definition util.c:319
dav_xmlns_info * dav_xmlns_create(apr_pool_t *pool)
Definition util.c:462
void dav_xmlns_generate(dav_xmlns_info *xi, apr_text_header *phdr)
Definition util.c:510
void dav_check_bufsize(apr_pool_t *p, dav_buffer *pbuf, apr_size_t extra_needed)
Definition util.c:107
#define DAV_INFINITY
Definition mod_dav.h:79
void dav_xmlns_add(dav_xmlns_info *xi, const char *prefix, const char *uri)
Definition util.c:473
time_t dav_get_timeout(request_rec *r)
Definition util.c:542
int dav_validate_root(const apr_xml_doc *doc, const char *tagname)
Definition util.c:328
int dav_get_resource_state(request_rec *r, const dav_resource *resource)
Definition util_lock.c:686
#define DAV_BUFFER_PAD
Definition mod_dav.h:451
#define DAV_VALIDATE_ADD_LD
Definition mod_dav.h:1457
dav_error * dav_join_error(dav_error *dest, dav_error *src)
Definition util.c:80
dav_error * dav_get_locktoken_list(request_rec *r, dav_locktoken_list **ltl)
Definition util.c:1803
#define DAV_ERR_IF_UNK_CHAR
Definition mod_dav.h:211
dav_error * dav_auto_checkout(request_rec *r, dav_resource *resource, int parent_only, dav_auto_version_info *av_info)
Definition util.c:1961
#define DAV_VALIDATE_USE_424
Definition mod_dav.h:1458
const char * dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool, int strip_white)
Definition util.c:385
apr_xml_elem * dav_find_child(const apr_xml_elem *elem, const char *tagname)
Definition util.c:359
dav_if_state_type
Definition mod_dav.h:841
dav_error * dav_push_error(apr_pool_t *p, int status, int error_id, const char *desc, dav_error *prev)
Definition util.c:66
#define DAV_ERR_LOCK_UNK_STATE_TOKEN
Definition mod_dav.h:232
#define DAV_VALIDATE_PARENT
Definition mod_dav.h:1456
#define DAV_DECLARE(type)
Definition mod_dav.h:85
apr_xml_elem * dav_find_child_ns(const apr_xml_elem *elem, int ns, const char *tagname)
Definition util.c:347
#define DAV_TIMEOUT_INFINITE
Definition mod_dav.h:1314
#define DAV_GET_HOOKS_VSN(r)
Definition mod_dav.h:809
#define DAV_VALIDATE_IS_PARENT
Definition mod_dav.h:1459
void dav_set_bufsize(apr_pool_t *p, dav_buffer *pbuf, apr_size_t size)
Definition util.c:122
apr_xml_attr * dav_find_attr_ns(const apr_xml_elem *elem, int ns, const char *attrname)
Definition util.c:366
#define DAV_ERR_IF_ABSENT
Definition mod_dav.h:212
void dav_buffer_place(apr_pool_t *p, dav_buffer *pbuf, const char *str)
Definition util.c:163
const char * dav_xmlns_add_uri(dav_xmlns_info *xi, const char *uri)
Definition util.c:484
#define DAV_ERR_IF_PARSE
Definition mod_dav.h:209
#define DAV_BUFFER_MINSIZE
Definition mod_dav.h:450
dav_error * dav_new_error(apr_pool_t *p, int status, int error_id, apr_status_t aprerr, const char *desc)
Definition util.c:36
apr_xml_elem * dav_find_next_ns(const apr_xml_elem *elem, int ns, const char *tagname)
Definition util.c:335
dav_error * dav_validate_request(request_rec *r, dav_resource *resource, int depth, dav_locktoken *locktoken, dav_response **response, int flags, dav_lockdb *lockdb)
Definition util.c:1555
#define DAV_RESOURCE_EXISTS
Definition mod_dav.h:1673
void dav_add_response(dav_walk_resource *wres, int status, dav_get_props_result *propstats)
Definition mod_dav.c:1245
@ DAV_AUTO_VERSION_LOCKED
Definition mod_dav.h:2182
@ DAV_AUTO_VERSION_ALWAYS
Definition mod_dav.h:2181
@ DAV_LOCKSCOPE_SHARED
Definition mod_dav.h:1344
@ DAV_LOCKSCOPE_EXCLUSIVE
Definition mod_dav.h:1343
@ dav_if_unknown
Definition mod_dav.h:844
@ dav_if_opaquelock
Definition mod_dav.h:843
@ dav_if_etag
Definition mod_dav.h:842
#define AP_NORMALIZE_DECODE_UNRESERVED
Definition httpd.h:1789
char * ap_getword_white(apr_pool_t *p, const char **line)
Definition util.c:751
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
int ap_normalize_path(char *path, unsigned int flags)
Definition util.c:489
#define ap_escape_html(p, s)
Definition httpd.h:1860
int ap_unescape_url(char *url)
Definition util.c:1939
#define AP_NORMALIZE_NOT_ABOVE_ROOT
Definition httpd.h:1788
apr_size_t size
apr_uint32_t val
Definition apr_atomic.h:66
const char int apr_pool_t * pool
Definition apr_cstr.h:84
const apr_array_header_t * list
Definition apr_cstr.h:105
#define apr_isspace(c)
Definition apr_lib.h:225
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
apr_array_header_t ** result
#define APR_HASH_KEY_STRING
Definition apr_hash.h:47
apr_interval_time_t t
apr_uint16_t apr_port_t
apr_pool_t * parent
Definition apr_pools.h:197
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
void apr_skiplistnode apr_skiplist_compare comp
void * mem
const char * s
Definition apr_strings.h:95
apr_int32_t apr_int32_t apr_int32_t err
const char const char *const const char *const apr_procattr_t * attr
int reason
int int status
Apache Configuration.
Apache Logging library.
HTTP protocol handling.
Apache Request library.
Virtual Host package.
apr_pool_t * p
Definition md_event.c:32
DAV extension module for Apache 2.0.*.
#define DAV_IF_COND_NORMAL
Definition mod_dav.h:852
#define DAV_IF_COND_NOT
Definition mod_dav.h:853
#define DAV_WALKTYPE_LOCKNULL
Definition mod_dav.h:1821
#define DAV_WALKTYPE_NORMAL
Definition mod_dav.h:1820
return NULL
Definition mod_so.c:359
static dav_error * dav_validate_walker(dav_walk_resource *wres, int calltype)
Definition util.c:1479
static int dav_meets_conditions(request_rec *r, int resource_state)
Definition util.c:1510
static int dav_find_submitted_locktoken(const dav_if_header *if_header, const dav_lock *lock_list, const dav_hooks_locks *locks_hooks)
Definition util.c:854
static dav_error * dav_add_if_state(apr_pool_t *p, dav_if_header *ih, const char *state_token, dav_if_state_type t, int condition, const dav_hooks_locks *locks_hooks)
Definition util.c:600
static char * dav_fetch_next_token(char **str, char term)
Definition util.c:642
static dav_error * dav_can_auto_checkout(request_rec *r, dav_resource *resource, dav_auto_version auto_version, dav_lockdb **lockdb, int *auto_checkout)
Definition util.c:1912
static dav_error * dav_validate_resource_state(apr_pool_t *p, const dav_resource *resource, dav_lockdb *lockdb, const dav_if_header *if_header, int flags, dav_buffer *pbuf, request_rec *r)
Definition util.c:899
static dav_if_header * dav_add_if_resource(apr_pool_t *p, dav_if_header *next_ih, const char *uri, apr_size_t uri_len)
Definition util.c:583
static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
Definition util.c:669
#define DAV_LABEL_HDR
Definition util.c:1867
apr_text * first
Definition apr_xml.h:66
char * scheme
Definition apr_uri.h:87
char * path
Definition apr_uri.h:99
apr_xml_elem * root
Definition apr_xml.h:202
const char * name
Definition apr_xml.h:164
struct apr_xml_elem * next
Definition apr_xml.h:178
apr_text_header following_cdata
Definition apr_xml.h:173
apr_sockaddr_t * local_addr
Definition httpd.h:1162
struct dav_error * prev
Definition mod_dav.h:131
void(* close_lockdb)(dav_lockdb *lockdb)
Definition mod_dav.h:1531
int(* is_same_resource)(const dav_resource *res1, const dav_resource *res2)
Definition mod_dav.h:1968
dav_error *(* vsn_control)(dav_resource *resource, const char *target)
Definition mod_dav.h:2310
dav_auto_version(* auto_versionable)(const dav_resource *resource)
Definition mod_dav.h:2295
dav_error *(* checkin)(dav_resource *resource, int keep_checked_out, dav_resource **version_resource)
Definition mod_dav.h:2361
dav_error *(* uncheckout)(dav_resource *resource)
Definition mod_dav.h:2348
dav_error *(* checkout)(dav_resource *resource, int auto_checkout, int is_unreserved, int is_fork_ok, int create_activity, apr_array_header_t *activities, dav_resource **working_resource)
Definition mod_dav.h:2338
apr_size_t uri_len
Definition mod_dav.h:864
struct dav_if_state_list * state
Definition mod_dav.h:865
const char * uri
Definition mod_dav.h:863
struct dav_if_header * next
Definition mod_dav.h:866
int dummy_header
Definition mod_dav.h:868
const dav_hooks_locks * hooks
Definition mod_dav.h:1334
const char * uri
Definition mod_dav.h:408
const dav_hooks_repository * hooks
Definition mod_dav.h:414
const dav_resource * resource
Definition mod_dav.h:1809
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
apr_pool_t * pool
Definition httpd.h:847
apr_uri_t parsed_uri
Definition httpd.h:1092
conn_rec * connection
Definition httpd.h:849
apr_table_t * headers_in
Definition httpd.h:976
server_rec * server
Definition httpd.h:851
const char * method
Definition httpd.h:900
apr_table_t * headers_out
Definition httpd.h:978
char * server_hostname
Definition httpd.h:1365
static apr_time_t now
Definition testtime.c:33
#define str
int ap_matches_request_vhost(request_rec *r, const char *host, apr_port_t port)
Definition vhost.c:953
IN ULONG IN INT timeout
#define ns(x)
Definition xmltok.c:1644