Apache HTTPD
mod_dav.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 *
20 * This module is repository-independent. It depends on hooks provided by a
21 * repository implementation.
22 *
23 * APACHE ISSUES:
24 * - within a DAV hierarchy, if an unknown method is used and we default
25 * to Apache's implementation, it sends back an OPTIONS with the wrong
26 * set of methods -- there is NO HOOK for us.
27 * therefore: we need to manually handle the HTTP_METHOD_NOT_ALLOWED
28 * and HTTP_NOT_IMPLEMENTED responses (not ap_send_error_response).
29 * - process_mkcol_body() had to dup code from ap_setup_client_block().
30 * - it would be nice to get status lines from Apache for arbitrary
31 * status codes
32 * - it would be nice to be able to extend Apache's set of response
33 * codes so that it doesn't return 500 when an unknown code is placed
34 * into r->status.
35 * - http_vhost functions should apply "const" to their params
36 *
37 * DESIGN NOTES:
38 * - For PROPFIND, we batch up the entire response in memory before
39 * sending it. We may want to reorganize around sending the information
40 * as we suck it in from the propdb. Alternatively, we should at least
41 * generate a total Content-Length if we're going to buffer in memory
42 * so that we can keep the connection open.
43 */
44
45#include "apr_strings.h"
46#include "apr_lib.h" /* for apr_is* */
47
48#define APR_WANT_STRFUNC
49#include "apr_want.h"
50
51#include "httpd.h"
52#include "http_config.h"
53#include "http_core.h"
54#include "http_log.h"
55#include "http_main.h"
56#include "http_protocol.h"
57#include "http_request.h"
58#include "util_script.h"
59
60#include "mod_dav.h"
61
62#include "ap_provider.h"
63
64
65/* ### what is the best way to set this? */
66#define DAV_DEFAULT_PROVIDER "filesystem"
67
68/* used to denote that mod_dav will be handling this request */
69#define DAV_HANDLER_NAME "dav-handler"
70
72
73enum {
77};
78
79/* per-dir configuration */
80typedef struct {
81 const char *provider_name;
83 const char *dir;
84 const char *base;
88
90
91/* per-server configuration */
92typedef struct {
93 int unused;
94
96
97#define DAV_INHERIT_VALUE(parent, child, field) \
98 ((child)->field ? (child)->field : (parent)->field)
99
100
101/* forward-declare for use in configuration lookup */
102extern module DAV_DECLARE_DATA dav_module;
103
104/* DAV methods */
105enum {
111
112
114 server_rec *s)
115{
116 /* DBG0("dav_init_handler"); */
117
118 /* Register DAV methods */
121
122 return OK;
123}
124
126{
128
130
131 /* ### this isn't used at the moment... */
132
133 return newconf;
134}
135
137{
138#if 0
139 dav_server_conf *child = overrides;
140#endif
142
144
145 /* ### nothing to merge right now... */
146
147 return newconf;
148}
149
151{
152 /* NOTE: dir==NULL creates the default per-dir config */
153
154 dav_dir_conf *conf;
155
156 conf = (dav_dir_conf *)apr_pcalloc(p, sizeof(*conf));
157
158 /* clean up the directory to remove any trailing slash */
159 if (dir != NULL) {
160 char *d;
161 apr_size_t l;
162
163 l = strlen(dir);
164 d = apr_pstrmemdup(p, dir, l);
165 if (l > 1 && d[l - 1] == '/')
166 d[l - 1] = '\0';
167 conf->dir = d;
168 }
169
170 return conf;
171}
172
173static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
174{
176 dav_dir_conf *child = overrides;
178
179 /* DBG3("dav_merge_dir_config: new=%08lx base=%08lx overrides=%08lx",
180 (long)newconf, (long)base, (long)overrides); */
181
182 newconf->provider_name = DAV_INHERIT_VALUE(parent, child, provider_name);
183 newconf->provider = DAV_INHERIT_VALUE(parent, child, provider);
184 if (parent->provider_name != NULL) {
185 if (child->provider_name == NULL) {
187 "\"DAV Off\" cannot be used to turn off a subtree "
188 "of a DAV-enabled location.");
189 }
190 else if (strcasecmp(child->provider_name,
191 parent->provider_name) != 0) {
193 "A subtree cannot specify a different DAV provider "
194 "than its parent.");
195 }
196 }
197
198 newconf->locktimeout = DAV_INHERIT_VALUE(parent, child, locktimeout);
199 newconf->dir = DAV_INHERIT_VALUE(parent, child, dir);
200 newconf->base = DAV_INHERIT_VALUE(parent, child, base);
201 newconf->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child,
202 allow_depthinfinity);
203 newconf->allow_lockdiscovery = DAV_INHERIT_VALUE(parent, child,
204 allow_lockdiscovery);
205
206 return newconf;
207}
208
210{
211 dav_dir_conf *conf = ap_get_module_config(r->per_dir_config, &dav_module);
212 return conf ? conf->provider_name : NULL;
213}
214
216{
217 dav_dir_conf *conf;
218
219 conf = ap_get_module_config(r->per_dir_config, &dav_module);
220 /* assert: conf->provider_name != NULL
221 (otherwise, DAV is disabled, and we wouldn't be here) */
222
223 /* assert: conf->provider != NULL
224 (checked when conf->provider_name is set) */
225 return conf->provider;
226}
227
232
237
242
247
252
253/*
254 * Command handler for the DAV directive, which is TAKE1.
255 */
256static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
257{
258 dav_dir_conf *conf = (dav_dir_conf *)config;
259
260 if (strcasecmp(arg1, "on") == 0) {
262 }
263 else if (strcasecmp(arg1, "off") == 0) {
264 conf->provider_name = NULL;
265 conf->provider = NULL;
266 }
267 else {
268 conf->provider_name = arg1;
269 }
270
271 if (conf->provider_name != NULL) {
272 /* lookup and cache the actual provider now */
274
275 if (conf->provider == NULL) {
276 /* by the time they use it, the provider should be loaded and
277 registered with us. */
278 return apr_psprintf(cmd->pool,
279 "Unknown DAV provider: %s",
280 conf->provider_name);
281 }
282 }
283
284 return NULL;
285}
286
287/*
288 * Command handler for the DAVBasePath directive, which is TAKE1
289 */
290static const char *dav_cmd_davbasepath(cmd_parms *cmd, void *config, const char *arg1)
291{
292 dav_dir_conf *conf = config;
293
294 conf->base = arg1;
295
296 return NULL;
297}
298
299/*
300 * Command handler for the DAVDepthInfinity directive, which is FLAG.
301 */
302static const char *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config,
303 int arg)
304{
305 dav_dir_conf *conf = (dav_dir_conf *)config;
306
307 if (arg)
309 else
311 return NULL;
312}
313
314/*
315 * Command handler for the DAVLockDiscovery directive, which is FLAG.
316 */
317static const char *dav_cmd_davlockdiscovery(cmd_parms *cmd, void *config,
318 int arg)
319{
320 dav_dir_conf *conf = (dav_dir_conf *)config;
321
322 if (arg)
324 else
326 return NULL;
327}
328
329/*
330 * Command handler for DAVMinTimeout directive, which is TAKE1
331 */
332static const char *dav_cmd_davmintimeout(cmd_parms *cmd, void *config,
333 const char *arg1)
334{
335 dav_dir_conf *conf = (dav_dir_conf *)config;
336
337 conf->locktimeout = atoi(arg1);
338 if (conf->locktimeout < 0)
339 return "DAVMinTimeout requires a non-negative integer.";
340
341 return NULL;
342}
343
344/*
345** dav_error_response()
346**
347** Send a nice response back to the user. In most cases, Apache doesn't
348** allow us to provide details in the body about what happened. This
349** function allows us to completely specify the response body.
350**
351** ### this function is not logging any errors! (e.g. the body)
352*/
353static int dav_error_response(request_rec *r, int status, const char *body)
354{
355 r->status = status;
357
358 ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
359
360 /* begin the response now... */
361 ap_rvputs(r,
363 r->status_line,
365 &r->status_line[4],
367 body,
369 ap_psignature("<hr />\n", r),
371 NULL);
372
373 /* the response has been sent. */
374 /*
375 * ### Use of DONE obviates logging..!
376 */
377 return DONE;
378}
379
380
381/*
382 * Send a "standardized" error response based on the error's namespace & tag
383 */
385 dav_error *err)
386{
387 r->status = err->status;
388
390
392 "<D:error xmlns:D=\"DAV:\"", r);
393
394 if (err->desc != NULL) {
395 /* ### should move this namespace somewhere (with the others!) */
396 ap_rputs(" xmlns:m=\"http://apache.org/dav/xmlns\"", r);
397 }
398
399 if (err->childtags) {
400 if (err->namespace != NULL) {
402 " xmlns:C=\"%s\">" DEBUG_CR
403 "<C:%s>%s</C:%s>" DEBUG_CR,
404 err->namespace,
405 err->tagname, err->childtags, err->tagname);
406 }
407 else {
409 ">" DEBUG_CR
410 "<D:%s>%s</D:%s>" DEBUG_CR,
411 err->tagname, err->childtags, err->tagname);
412 }
413 }
414 else {
415 if (err->namespace != NULL) {
417 " xmlns:C=\"%s\">" DEBUG_CR
418 "<C:%s/>" DEBUG_CR,
419 err->namespace, err->tagname);
420 }
421 else {
423 ">" DEBUG_CR
424 "<D:%s/>" DEBUG_CR, err->tagname);
425 }
426 }
427
428 /* here's our mod_dav specific tag: */
429 if (err->desc != NULL) {
431 "<m:human-readable errcode=\"%d\">" DEBUG_CR
432 "%s" DEBUG_CR
433 "</m:human-readable>" DEBUG_CR,
434 err->error_id,
435 apr_xml_quote_string(r->pool, err->desc, 0));
436 }
437
438 ap_rputs("</D:error>" DEBUG_CR, r);
439
440 /* the response has been sent. */
441 /*
442 * ### Use of DONE obviates logging..!
443 */
444 return DONE;
445}
446
447
448/*
449 * Apache's URI escaping does not replace '&' since that is a valid character
450 * in a URI (to form a query section). We must explicitly handle it so that
451 * we can embed the URI into an XML document.
452 */
453static const char *dav_xml_escape_uri(apr_pool_t *p, const char *uri)
454{
455 const char *e_uri = ap_escape_uri(p, uri);
456
457 /* check the easy case... */
458 if (ap_strchr_c(e_uri, '&') == NULL)
459 return e_uri;
460
461 /* there was a '&', so more work is needed... sigh. */
462
463 /*
464 * Note: this is a teeny bit of overkill since we know there are no
465 * '<' or '>' characters, but who cares.
466 */
467 return apr_xml_quote_string(p, e_uri, 0);
468}
469
470
471/* Write a complete RESPONSE object out as a <DAV:response> xml
472 element. Data is sent into brigade BB, which is auto-flushed into
473 the output filter stack for request R. Use POOL for any temporary
474 allocations.
475
476 [Presumably the <multistatus> tag has already been written; this
477 routine is shared by dav_send_multistatus and dav_stream_response.]
478*/
481 request_rec *r,
483{
484 apr_text *t = NULL;
485
486 if (response->propresult.xmlns == NULL) {
487 ap_fputs(r->output_filters, bb, "<D:response>");
488 }
489 else {
490 ap_fputs(r->output_filters, bb, "<D:response");
491 for (t = response->propresult.xmlns; t; t = t->next) {
492 ap_fputs(r->output_filters, bb, t->text);
493 }
494 ap_fputc(r->output_filters, bb, '>');
495 }
496
498 DEBUG_CR "<D:href>",
499 dav_xml_escape_uri(pool, response->href),
500 "</D:href>" DEBUG_CR,
501 NULL);
502
503 if (response->propresult.propstats == NULL) {
504 /* use the Status-Line text from Apache. Note, this will
505 * default to 500 Internal Server Error if first->status
506 * is not a known (or valid) status code.
507 */
509 "<D:status>HTTP/1.1 ",
510 ap_get_status_line(response->status),
511 "</D:status>" DEBUG_CR,
512 NULL);
513 }
514 else {
515 /* assume this includes <propstat> and is quoted properly */
516 for (t = response->propresult.propstats; t; t = t->next) {
517 ap_fputs(r->output_filters, bb, t->text);
518 }
519 }
520
521 if (response->desc != NULL) {
522 /*
523 * We supply the description, so we know it doesn't have to
524 * have any escaping/encoding applied to it.
525 */
527 "<D:responsedescription>",
528 response->desc,
529 "</D:responsedescription>" DEBUG_CR,
530 NULL);
531 }
532
533 ap_fputs(r->output_filters, bb, "</D:response>" DEBUG_CR);
534}
535
536
537/* Factorized helper function: prep request_rec R for a multistatus
538 response and write <multistatus> tag into BB, destined for
539 R->output_filters. Use xml NAMESPACES in initial tag, if
540 non-NULL. */
542 request_rec *r, int status,
544{
545 /* Set the correct status and Content-Type */
546 r->status = status;
548
549 /* Send the headers and actual multistatus response now... */
551 "<D:multistatus xmlns:D=\"DAV:\"");
552
553 if (namespaces != NULL) {
554 int i;
555
556 for (i = namespaces->nelts; i--; ) {
557 ap_fprintf(r->output_filters, bb, " xmlns:ns%d=\"%s\"", i,
559 }
560 }
561
563}
564
565/* Finish a multistatus response started by dav_begin_multistatus: */
568{
569 apr_bucket *b;
570
571 ap_fputs(r->output_filters, bb, "</D:multistatus>" DEBUG_CR);
572
573 /* indicate the end of the response body */
576
577 /* deliver whatever might be remaining in the brigade */
578 return ap_pass_brigade(r->output_filters, bb);
579}
580
602
603/*
604 * dav_log_err()
605 *
606 * Write error information to the log.
607 */
608static void dav_log_err(request_rec *r, dav_error *err, int level)
609{
611
612 /* Log the errors */
613 /* ### should have a directive to log the first or all */
614 for (errscan = err; errscan != NULL; errscan = errscan->prev) {
615 if (errscan->desc == NULL)
616 continue;
617
618 /* Intentional no APLOGNO */
619 ap_log_rerror(APLOG_MARK, level, errscan->aprerr, r, "%s [%d, #%d]",
620 errscan->desc, errscan->status, errscan->error_id);
621 }
622}
623
624/*
625 * dav_handle_err()
626 *
627 * Handle the standard error processing. <err> must be non-NULL.
628 *
629 * <response> is set by the following:
630 * - dav_validate_request()
631 * - dav_add_lock()
632 * - repos_hooks->remove_resource
633 * - repos_hooks->move_resource
634 * - repos_hooks->copy_resource
635 * - vsn_hooks->update
636 */
638 dav_response *response)
639{
640 /* log the errors */
642
643 if (!ap_is_HTTP_VALID_RESPONSE(err->status)) {
644 /* we have responded already */
645 return AP_FILTER_ERROR;
646 }
647
648 if (response == NULL) {
650
651 /* our error messages are safe; tell Apache this */
652 apr_table_setn(r->notes, "verbose-error-to", "*");
653
654 /* Didn't get a multistatus response passed in, but we still
655 might be able to generate a standard <D:error> response.
656 Search the error stack for an errortag. */
657 while (stackerr != NULL && stackerr->tagname == NULL)
658 stackerr = stackerr->prev;
659
660 if (stackerr != NULL && stackerr->tagname != NULL)
662
663 return err->status;
664 }
665
666 /* send the multistatus and tell Apache the request/response is DONE. */
667 dav_send_multistatus(r, err->status, response, NULL);
668 return DONE;
669}
670
671/* handy function for return values of methods that (may) create things.
672 * locn if provided is assumed to be escaped. */
673static int dav_created(request_rec *r, const char *locn, const char *what,
674 int replaced)
675{
676 const char *body;
677
678 if (locn == NULL) {
680 }
681
682 /* did the target resource already exist? */
683 if (replaced) {
684 /* Apache will supply a default message */
685 return HTTP_NO_CONTENT;
686 }
687
688 /* Per HTTP/1.1, S10.2.2: add a Location header to contain the
689 * URI that was created. */
690
691 /* Convert locn to an absolute URI, and return in Location header */
693
694 /* ### insert an ETag header? see HTTP/1.1 S10.2.2 */
695
696 /* Apache doesn't allow us to set a variable body for HTTP_CREATED, so
697 * we must manufacture the entire response. */
698 body = apr_pstrcat(r->pool, what, " ", ap_escape_html(r->pool, locn),
699 " has been created.", NULL);
700 return dav_error_response(r, HTTP_CREATED, body);
701}
702
703/* ### move to dav_util? */
705{
706 const char *depth = apr_table_get(r->headers_in, "Depth");
707
708 if (depth == NULL) {
709 return def_depth;
710 }
711
712 if (ap_cstr_casecmp(depth, "infinity") == 0) {
713 return DAV_INFINITY;
714 }
715 else if (strcmp(depth, "0") == 0) {
716 return 0;
717 }
718 else if (strcmp(depth, "1") == 0) {
719 return 1;
720 }
721
722 /* The caller will return an HTTP_BAD_REQUEST. This will augment the
723 * default message that Apache provides. */
725 "An invalid Depth header was specified.");
726 return -1;
727}
728
730{
731 const char *overwrite = apr_table_get(r->headers_in, "Overwrite");
732
733 if (overwrite == NULL) {
734 return 1; /* default is "T" */
735 }
736
737 if ((*overwrite == 'F' || *overwrite == 'f') && overwrite[1] == '\0') {
738 return 0;
739 }
740
741 if ((*overwrite == 'T' || *overwrite == 't') && overwrite[1] == '\0') {
742 return 1;
743 }
744
745 /* The caller will return an HTTP_BAD_REQUEST. This will augment the
746 * default message that Apache provides. */
748 "An invalid Overwrite header was specified.");
749 return -1;
750}
751
752/* resolve a request URI to a resource descriptor.
753 *
754 * If label_allowed != 0, then allow the request target to be altered by
755 * a Label: header.
756 *
757 * If use_checked_in is true, then the repository provider should return
758 * the resource identified by the DAV:checked-in property of the resource
759 * identified by the Request-URI.
760 */
763{
764 dav_dir_conf *conf;
765 const char *label = NULL, *base;
766 dav_error *err;
767
768 /* if the request target can be overridden, get any target selector */
769 if (label_allowed) {
770 label = apr_table_get(r->headers_in, "label");
771 }
772
773 conf = ap_get_module_config(r->per_dir_config, &dav_module);
774 /* assert: conf->provider != NULL */
775 if (conf->provider == NULL) {
778 "DAV not enabled for %s",
779 ap_escape_html(r->pool, r->uri)));
780 }
781
782 /* Take the repos root from DAVBasePath if configured, else the
783 * path of the enclosing section. */
784 base = conf->base ? conf->base : conf->dir;
785
786 /* resolve the resource */
787 err = (*conf->provider->repos->get_resource)(r, base,
789 res_p);
790 if (err != NULL) {
791 /* In the error path, give a hint that DavBasePath needs to be
792 * used if the location was configured via a regex match. */
793 if (!conf->base) {
795
796 if (cdc->r) {
798 "failed to find repository for location configured "
799 "via regex match - missing DAVBasePath?");
800 }
801 }
802
803 err = dav_push_error(r->pool, err->status, 0,
804 "Could not fetch resource information.", err);
805 return err;
806 }
807
808 /* Note: this shouldn't happen, but just be sure... */
809 if (*res_p == NULL) {
810 /* ### maybe use HTTP_INTERNAL_SERVER_ERROR */
811 return dav_new_error(r->pool, HTTP_NOT_FOUND, 0, 0,
813 "The provider did not define a "
814 "resource for %s.",
815 ap_escape_html(r->pool, r->uri)));
816 }
817
818 /* ### hmm. this doesn't feel like the right place or thing to do */
819 /* if there were any input headers requiring a Vary header in the response,
820 * add it now */
822
823 return NULL;
824}
825
827 int ro,
828 dav_lockdb **lockdb)
829{
831
832 if (hooks == NULL) {
833 *lockdb = NULL;
834 return NULL;
835 }
836
837 /* open the thing lazily */
838 return (*hooks->open_lockdb)(r, ro, 0, lockdb);
839}
840
842{
843 (lockdb->hooks->close_lockdb)(lockdb);
844}
845
853{
854 const char *range_c;
855 char *range;
856 char *dash;
857 char *slash;
858
859 range_c = apr_table_get(r->headers_in, "content-range");
860 if (range_c == NULL)
861 return 0;
862
863 range = apr_pstrdup(r->pool, range_c);
864 if (ap_cstr_casecmpn(range, "bytes ", 6) != 0
865 || (dash = ap_strchr(range + 6, '-')) == NULL
866 || (slash = ap_strchr(range + 6, '/')) == NULL) {
867 /* malformed header */
868 return -1;
869 }
870
871 *dash++ = *slash++ = '\0';
872
873 /* detect invalid ranges */
874 if (!ap_parse_strict_length(range_start, range + 6)) {
875 return -1;
876 }
878 || *range_end < *range_start) {
879 return -1;
880 }
881
882 if (*slash != '*') {
884
886 || dummy <= *range_end) {
887 return -1;
888 }
889 }
890
891 /* we now have a valid range */
892 return 1;
893}
894
895/* handle the GET method */
897{
899 dav_error *err;
900 int status;
901
902 /* This method should only be called when the resource is not
903 * visible to Apache. We will fetch the resource from the repository,
904 * then create a subrequest for Apache to handle.
905 */
906 err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
907 &resource);
908 if (err != NULL)
909 return dav_handle_err(r, err, NULL);
910
911 /* check for any method preconditions */
913 && err) {
914 return dav_handle_err(r, err, NULL);
915 }
916
917 if (!resource->exists) {
918 /* Apache will supply a default error for this. */
919 return HTTP_NOT_FOUND;
920 }
921
922 /* set up the HTTP headers for the response */
923 if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
924 err = dav_push_error(r->pool, err->status, 0,
925 "Unable to set up HTTP headers.",
926 err);
927 return dav_handle_err(r, err, NULL);
928 }
929
930 /* Handle conditional requests */
932 if (status) {
933 return status;
934 }
935
936 if (r->header_only) {
937 return DONE;
938 }
939
940 /* okay... time to deliver the content */
941 if ((err = (*resource->hooks->deliver)(resource,
942 r->output_filters)) != NULL) {
943 err = dav_push_error(r->pool, err->status, 0,
944 "Unable to deliver content.",
945 err);
946 return dav_handle_err(r, err, NULL);
947 }
948
949 return DONE;
950}
951
952/* validate resource/locks on POST, then pass to the default handler */
954{
956 dav_error *err;
957
958 /* Ask repository module to resolve the resource */
959 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
960 &resource);
961 if (err != NULL)
962 return dav_handle_err(r, err, NULL);
963
964 /* check for any method preconditions */
966 && err) {
967 return dav_handle_err(r, err, NULL);
968 }
969
970 /* Note: depth == 0. Implies no need for a multistatus response. */
973 /* ### add a higher-level description? */
974 return dav_handle_err(r, err, NULL);
975 }
976
977 return DECLINED;
978}
979
980/* handle the PUT method */
982{
984 int resource_state;
987 const char *body;
988 dav_error *err;
991 dav_stream *stream;
993 int has_range;
996
997 /* Ask repository module to resolve the resource */
998 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
999 &resource);
1000 if (err != NULL)
1001 return dav_handle_err(r, err, NULL);
1002
1003 /* check for any method preconditions */
1005 && err) {
1006 return dav_handle_err(r, err, NULL);
1007 }
1008
1009 /* If not a file or collection resource, PUT not allowed */
1011 && resource->type != DAV_RESOURCE_TYPE_WORKING) {
1012 body = apr_psprintf(r->pool,
1013 "Cannot create resource %s with PUT.",
1014 ap_escape_html(r->pool, r->uri));
1015 return dav_error_response(r, HTTP_CONFLICT, body);
1016 }
1017
1018 /* Cannot PUT a collection */
1019 if (resource->collection) {
1021 "Cannot PUT to a collection.");
1022
1023 }
1024
1026
1027 /*
1028 * Note: depth == 0 normally requires no multistatus response. However,
1029 * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
1030 * other than the Request-URI, thereby requiring a multistatus.
1031 *
1032 * If the resource does not exist (DAV_RESOURCE_NULL), then we must
1033 * check the resource *and* its parent. If the resource exists or is
1034 * a locknull resource, then we check only the resource.
1035 */
1040 /* ### add a higher-level description? */
1042 }
1043
1045 if (has_range < 0) {
1046 /* RFC 2616 14.16: If we receive an invalid Content-Range we must
1047 * not use the content.
1048 */
1049 body = apr_psprintf(r->pool,
1050 "Malformed Content-Range header for PUT %s.",
1051 ap_escape_html(r->pool, r->uri));
1052 return dav_error_response(r, HTTP_BAD_REQUEST, body);
1053 } else if (has_range) {
1055 }
1056 else {
1058 }
1059
1060 /* make sure the resource can be modified (if versioning repository) */
1062 0 /* not parent_only */,
1063 &av_info)) != NULL) {
1064 /* ### add a higher-level description? */
1065 return dav_handle_err(r, err, NULL);
1066 }
1067
1068 /* Create the new file in the repository */
1069 if ((err = (*resource->hooks->open_stream)(resource, mode,
1070 &stream)) != NULL) {
1071 int status = err->status ? err->status : HTTP_FORBIDDEN;
1072 if (status > 299) {
1075 "Unable to PUT new contents for %s.",
1076 ap_escape_html(r->pool, r->uri)),
1077 err);
1078 }
1079 else {
1080 err = NULL;
1081 }
1082 }
1083
1084 if (err == NULL && has_range) {
1085 /* a range was provided. seek to the start */
1086 err = (*resource->hooks->seek_stream)(stream, range_start);
1087 }
1088
1089 if (err == NULL) {
1091 apr_bucket *b;
1092 int seen_eos = 0;
1093
1095
1096 do {
1098
1101
1102 if (rc != APR_SUCCESS) {
1103 int http_err;
1104 char *msg = ap_escape_html(r->pool, r->uri);
1106 msg = apr_psprintf(r->pool, "An error occurred while reading "
1107 "the request body (URI: %s)",
1108 msg);
1109 err = dav_new_error(r->pool, http_err, 0, rc, msg);
1110 break;
1111 }
1112
1113 for (b = APR_BRIGADE_FIRST(bb);
1114 b != APR_BRIGADE_SENTINEL(bb);
1115 b = APR_BUCKET_NEXT(b))
1116 {
1117 const char *data;
1119
1120 if (APR_BUCKET_IS_EOS(b)) {
1121 seen_eos = 1;
1122 break;
1123 }
1124
1126 continue;
1127 }
1128
1129 if (err == NULL) {
1130 /* write whatever we read, until we see an error */
1132 if (rc != APR_SUCCESS) {
1135 "An error occurred while"
1136 " reading the request body"
1137 " from the bucket (URI: %s)",
1138 ap_escape_html(r->pool, r->uri)));
1139 break;
1140 }
1141
1142 err = (*resource->hooks->write_stream)(stream, data, len);
1143 }
1144 }
1145
1147 } while (!seen_eos);
1148
1150
1151 err2 = (*resource->hooks->close_stream)(stream,
1152 err == NULL /* commit */);
1154 }
1155
1156 /*
1157 * Ensure that we think the resource exists now.
1158 * ### eek. if an error occurred during the write and we did not commit,
1159 * ### then the resource might NOT exist (e.g. dav_fs_repos.c)
1160 */
1161 if (err == NULL) {
1162 resource->exists = 1;
1163 }
1164
1165 /* restore modifiability of resources back to what they were */
1166 err2 = dav_auto_checkin(r, resource, err != NULL /* undo if error */,
1167 0 /*unlock*/, &av_info);
1168
1169 /* check for errors now */
1170 if (err != NULL) {
1171 err = dav_join_error(err, err2); /* don't forget err2 */
1172 return dav_handle_err(r, err, NULL);
1173 }
1174
1175 if (err2 != NULL) {
1176 /* just log a warning */
1177 err2 = dav_push_error(r->pool, err2->status, 0,
1178 "The PUT was successful, but there "
1179 "was a problem automatically checking in "
1180 "the resource or its parent collection.",
1181 err2);
1183 }
1184
1185 /* ### place the Content-Type and Content-Language into the propdb */
1186
1187 if (locks_hooks != NULL) {
1188 dav_lockdb *lockdb;
1189
1190 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
1191 /* The file creation was successful, but the locking failed. */
1192 err = dav_push_error(r->pool, err->status, 0,
1193 "The file was PUT successfully, but there "
1194 "was a problem opening the lock database "
1195 "which prevents inheriting locks from the "
1196 "parent resources.",
1197 err);
1198 return dav_handle_err(r, err, NULL);
1199 }
1200
1201 /* notify lock system that we have created/replaced a resource */
1203
1204 (*locks_hooks->close_lockdb)(lockdb);
1205
1206 if (err != NULL) {
1207 /* The file creation was successful, but the locking failed. */
1208 err = dav_push_error(r->pool, err->status, 0,
1209 "The file was PUT successfully, but there "
1210 "was a problem updating its lock "
1211 "information.",
1212 err);
1213 return dav_handle_err(r, err, NULL);
1214 }
1215 }
1216
1217 /* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
1218
1219 /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
1220 return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
1221}
1222
1223
1224/* Use POOL to temporarily construct a dav_response object (from WRES
1225 STATUS, and PROPSTATS) and stream it via WRES's ctx->brigade. */
1227 int status,
1228 dav_get_props_result *propstats,
1230{
1231 dav_response resp = { 0 };
1232 dav_walker_ctx *ctx = wres->walk_ctx;
1233
1234 resp.href = wres->resource->uri;
1235 resp.status = status;
1236 if (propstats) {
1237 resp.propresult = *propstats;
1238 }
1239
1241}
1242
1243
1244/* ### move this to dav_util? */
1246 int status, dav_get_props_result *propstats)
1247{
1249
1250 /* just drop some data into an dav_response */
1251 resp = apr_pcalloc(wres->pool, sizeof(*resp));
1252 resp->href = apr_pstrdup(wres->pool, wres->resource->uri);
1253 resp->status = status;
1254 if (propstats) {
1255 resp->propresult = *propstats;
1256 }
1257
1258 resp->next = wres->response;
1259 wres->response = resp;
1260}
1261
1262
1263/* handle the DELETE method */
1265{
1268 dav_error *err;
1269 dav_error *err2;
1271 int result;
1272 int depth;
1273
1274 /* We don't use the request body right now, so torch it. */
1275 if ((result = ap_discard_request_body(r)) != OK) {
1276 return result;
1277 }
1278
1279 /* Ask repository module to resolve the resource */
1280 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
1281 &resource);
1282 if (err != NULL)
1283 return dav_handle_err(r, err, NULL);
1284
1285 /* check for any method preconditions */
1287 && err) {
1288 return dav_handle_err(r, err, NULL);
1289 }
1290
1291 if (!resource->exists) {
1292 /* Apache will supply a default error for this. */
1293 return HTTP_NOT_FOUND;
1294 }
1295
1296 /* 2518 says that depth must be infinity only for collections.
1297 * For non-collections, depth is ignored, unless it is an illegal value (1).
1298 */
1299 depth = dav_get_depth(r, DAV_INFINITY);
1300
1301 if (resource->collection && depth != DAV_INFINITY) {
1302 /* This supplies additional information for the default message. */
1304 "Depth must be \"infinity\" for DELETE of a collection.");
1305 return HTTP_BAD_REQUEST;
1306 }
1307
1308 if (!resource->collection && depth == 1) {
1309 /* This supplies additional information for the default message. */
1311 "Depth of \"1\" is not allowed for DELETE.");
1312 return HTTP_BAD_REQUEST;
1313 }
1314
1315 /*
1316 ** If any resources fail the lock/If: conditions, then we must fail
1317 ** the delete. Each of the failing resources will be listed within
1318 ** a DAV:multistatus body, wrapped into a 424 response.
1319 **
1320 ** Note that a failure on the resource itself does not generate a
1321 ** multistatus response -- only internal members/collections.
1322 */
1323 if ((err = dav_validate_request(r, resource, depth, NULL,
1326 | DAV_VALIDATE_USE_424, NULL)) != NULL) {
1327 err = dav_push_error(r->pool, err->status, 0,
1329 "Could not DELETE %s due to a failed "
1330 "precondition (e.g. locks).",
1331 ap_escape_html(r->pool, r->uri)),
1332 err);
1334 }
1335
1336 /* ### RFC 2518 s. 8.10.5 says to remove _all_ locks, not just those
1337 * locked by the token(s) in the if_header.
1338 */
1339 if ((result = dav_unlock(r, resource, NULL)) != OK) {
1340 return result;
1341 }
1342
1343 /* if versioned resource, make sure parent is checked out */
1344 if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
1345 &av_info)) != NULL) {
1346 /* ### add a higher-level description? */
1347 return dav_handle_err(r, err, NULL);
1348 }
1349
1350 /* try to remove the resource */
1351 err = (*resource->hooks->remove_resource)(resource, &multi_response);
1352
1353 /* restore writability of parent back to what it was */
1354 err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
1355 0 /*unlock*/, &av_info);
1356
1357 /* check for errors now */
1358 if (err != NULL) {
1359 err = dav_push_error(r->pool, err->status, 0,
1361 "Could not DELETE %s.",
1362 ap_escape_html(r->pool, r->uri)),
1363 err);
1365 }
1366 if (err2 != NULL) {
1367 /* just log a warning */
1368 err = dav_push_error(r->pool, err2->status, 0,
1369 "The DELETE was successful, but there "
1370 "was a problem automatically checking in "
1371 "the parent collection.",
1372 err2);
1374 }
1375
1376 /* ### HTTP_NO_CONTENT if no body, HTTP_OK if there is a body (some day) */
1377
1378 /* Apache will supply a default error for this. */
1379 return HTTP_NO_CONTENT;
1380}
1381
1382/* generate DAV:supported-method-set OPTIONS response */
1384 const apr_xml_elem *elem,
1385 const apr_table_t *methods,
1386 apr_text_header *body)
1387{
1388 const apr_array_header_t *arr;
1389 const apr_table_entry_t *elts;
1390 apr_xml_elem *child;
1392 char *s;
1393 int i;
1394
1395 apr_text_append(r->pool, body, "<D:supported-method-set>" DEBUG_CR);
1396
1397 if (elem->first_child == NULL) {
1398 /* show all supported methods */
1400 elts = (const apr_table_entry_t *)arr->elts;
1401
1402 for (i = 0; i < arr->nelts; ++i) {
1403 if (elts[i].key == NULL)
1404 continue;
1405
1406 s = apr_pstrcat(r->pool,
1407 "<D:supported-method D:name=\"",
1408 elts[i].key,
1409 "\"/>" DEBUG_CR, NULL);
1410 apr_text_append(r->pool, body, s);
1411 }
1412 }
1413 else {
1414 /* check for support of specific methods */
1415 for (child = elem->first_child; child != NULL; child = child->next) {
1416 if (child->ns == APR_XML_NS_DAV_ID
1417 && strcmp(child->name, "supported-method") == 0) {
1418 const char *name = NULL;
1419
1420 /* go through attributes to find method name */
1421 for (attr = child->attr; attr != NULL; attr = attr->next) {
1422 if (attr->ns == APR_XML_NS_DAV_ID
1423 && strcmp(attr->name, "name") == 0)
1424 name = attr->value;
1425 }
1426
1427 if (name == NULL) {
1428 return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0,
1429 "A DAV:supported-method element "
1430 "does not have a \"name\" attribute");
1431 }
1432
1433 /* see if method is supported */
1434 if (apr_table_get(methods, name) != NULL) {
1435 s = apr_pstrcat(r->pool,
1436 "<D:supported-method D:name=\"",
1437 name, "\"/>" DEBUG_CR, NULL);
1438 apr_text_append(r->pool, body, s);
1439 }
1440 }
1441 }
1442 }
1443
1444 apr_text_append(r->pool, body, "</D:supported-method-set>" DEBUG_CR);
1445 return NULL;
1446}
1447
1448/* generate DAV:supported-live-property-set OPTIONS response */
1450 const dav_resource *resource,
1451 const apr_xml_elem *elem,
1452 apr_text_header *body)
1453{
1454 dav_lockdb *lockdb;
1455 dav_propdb *propdb;
1456 apr_xml_elem *child;
1458 dav_error *err;
1459
1460 /* open lock database, to report on supported lock properties */
1461 if ((err = dav_open_lockdb(r, 1, &lockdb)) != NULL) {
1462 return dav_push_error(r->pool, err->status, 0,
1463 "The lock database could not be opened, "
1464 "preventing the reporting of supported lock "
1465 "properties.",
1466 err);
1467 }
1468
1469 /* open the property database (readonly) for the resource */
1470 if ((err = dav_open_propdb(r, lockdb, resource, DAV_PROPDB_RO, NULL,
1471 &propdb)) != NULL) {
1472 if (lockdb != NULL)
1473 (*lockdb->hooks->close_lockdb)(lockdb);
1474
1475 return dav_push_error(r->pool, err->status, 0,
1476 "The property database could not be opened, "
1477 "preventing report of supported properties.",
1478 err);
1479 }
1480
1481 apr_text_append(r->pool, body, "<D:supported-live-property-set>" DEBUG_CR);
1482
1483 if (elem->first_child == NULL) {
1484 /* show all supported live properties */
1486 body->last->next = props.propstats;
1487 while (body->last->next != NULL)
1488 body->last = body->last->next;
1489 }
1490 else {
1491 /* check for support of specific live property */
1492 for (child = elem->first_child; child != NULL; child = child->next) {
1493 if (child->ns == APR_XML_NS_DAV_ID
1494 && strcmp(child->name, "supported-live-property") == 0) {
1495 const char *name = NULL;
1496 const char *nmspace = NULL;
1497
1498 /* go through attributes to find name and namespace */
1499 for (attr = child->attr; attr != NULL; attr = attr->next) {
1500 if (attr->ns == APR_XML_NS_DAV_ID) {
1501 if (strcmp(attr->name, "name") == 0)
1502 name = attr->value;
1503 else if (strcmp(attr->name, "namespace") == 0)
1504 nmspace = attr->value;
1505 }
1506 }
1507
1508 if (name == NULL) {
1510 "A DAV:supported-live-property "
1511 "element does not have a \"name\" "
1512 "attribute");
1513 break;
1514 }
1515
1516 /* default namespace to DAV: */
1517 if (nmspace == NULL)
1518 nmspace = "DAV:";
1519
1520 /* check for support of property */
1521 dav_get_liveprop_supported(propdb, nmspace, name, body);
1522 }
1523 }
1524 }
1525
1526 apr_text_append(r->pool, body, "</D:supported-live-property-set>" DEBUG_CR);
1527
1528 dav_close_propdb(propdb);
1529
1530 if (lockdb != NULL)
1531 (*lockdb->hooks->close_lockdb)(lockdb);
1532
1533 return err;
1534}
1535
1536
1537/* generate DAV:supported-report-set OPTIONS response */
1539 const dav_resource *resource,
1540 const apr_xml_elem *elem,
1541 apr_text_header *body)
1542{
1543 apr_xml_elem *child;
1545 dav_error *err = NULL;
1546 char *s;
1548 const dav_report_elem *rp;
1549
1550 apr_text_append(r->pool, body, "<D:supported-report-set>" DEBUG_CR);
1551
1552 reports = apr_array_make(r->pool, 5, sizeof(const char *));
1554 if (err != NULL) {
1555 return dav_push_error(r->pool, err->status, 0,
1556 "DAV:supported-report-set could not be "
1557 "determined due to a problem fetching the "
1558 "available reports for this resource.",
1559 err);
1560 }
1561
1562 if (elem->first_child == NULL) {
1563 int i;
1564
1565 /* show all supported reports */
1566 rp = (const dav_report_elem *)reports->elts;
1567 for (i = 0; i < reports->nelts; i++, rp++) {
1568 /* Note: we presume reports->namespace is
1569 * properly XML/URL quoted */
1570 s = apr_pstrcat(r->pool,
1571 "<D:supported-report D:name=\"",
1572 rp->name,
1573 "\" D:namespace=\"",
1574 rp->nmspace,
1575 "\"/>" DEBUG_CR, NULL);
1576 apr_text_append(r->pool, body, s);
1577 }
1578 }
1579 else {
1580 /* check for support of specific report */
1581 for (child = elem->first_child; child != NULL; child = child->next) {
1582 if (child->ns == APR_XML_NS_DAV_ID
1583 && strcmp(child->name, "supported-report") == 0) {
1584 const char *name = NULL;
1585 const char *nmspace = NULL;
1586 int i;
1587
1588 /* go through attributes to find name and namespace */
1589 for (attr = child->attr; attr != NULL; attr = attr->next) {
1590 if (attr->ns == APR_XML_NS_DAV_ID) {
1591 if (strcmp(attr->name, "name") == 0)
1592 name = attr->value;
1593 else if (strcmp(attr->name, "namespace") == 0)
1594 nmspace = attr->value;
1595 }
1596 }
1597
1598 if (name == NULL) {
1599 return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0,
1600 "A DAV:supported-report element "
1601 "does not have a \"name\" attribute");
1602 }
1603
1604 /* default namespace to DAV: */
1605 if (nmspace == NULL) {
1606 nmspace = "DAV:";
1607 }
1608
1609 rp = (const dav_report_elem *)reports->elts;
1610 for (i = 0; i < reports->nelts; i++, rp++) {
1611 if (strcmp(name, rp->name) == 0
1612 && strcmp(nmspace, rp->nmspace) == 0) {
1613 /* Note: we presume reports->nmspace is
1614 * properly XML/URL quoted
1615 */
1616 s = apr_pstrcat(r->pool,
1617 "<D:supported-report "
1618 "D:name=\"",
1619 rp->name,
1620 "\" D:namespace=\"",
1621 rp->nmspace,
1622 "\"/>" DEBUG_CR, NULL);
1623 apr_text_append(r->pool, body, s);
1624 break;
1625 }
1626 }
1627 }
1628 }
1629 }
1630
1631 apr_text_append(r->pool, body, "</D:supported-report-set>" DEBUG_CR);
1632 return NULL;
1633}
1634
1635
1636/* handle the SEARCH method */
1638{
1641 dav_error *err;
1643
1644 /* If no search provider, decline the request */
1645 if (search_hooks == NULL)
1646 return DECLINED;
1647
1648 /* This method should only be called when the resource is not
1649 * visible to Apache. We will fetch the resource from the repository,
1650 * then create a subrequest for Apache to handle.
1651 */
1652 err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
1653 &resource);
1654 if (err != NULL)
1655 return dav_handle_err(r, err, NULL);
1656
1657 if (!resource->exists) {
1658 /* Apache will supply a default error for this. */
1659 return HTTP_NOT_FOUND;
1660 }
1661
1662 /* set up the HTTP headers for the response */
1663 if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
1664 err = dav_push_error(r->pool, err->status, 0,
1665 "Unable to set up HTTP headers.",
1666 err);
1667 return dav_handle_err(r, err, NULL);
1668 }
1669
1670 if (r->header_only) {
1671 return DONE;
1672 }
1673
1674 /* okay... time to search the content */
1675 /* Let's validate XML and process walk function
1676 * in the hook function
1677 */
1678 if ((err = (*search_hooks->search_resource)(r, &multi_status)) != NULL) {
1679 /* ### add a higher-level description? */
1680 return dav_handle_err(r, err, NULL);
1681 }
1682
1683 /* We have results in multi_status */
1684 /* Should I pass namespace?? */
1686
1687 return DONE;
1688}
1689
1690
1691/* handle the OPTIONS method */
1693{
1695 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
1699 const char *dav_level;
1700 char *allow;
1701 char *s;
1702 const apr_array_header_t *arr;
1703 const apr_table_entry_t *elts;
1706 apr_text_header body = { 0 };
1707 apr_text *t;
1708 int text_size;
1709 int result;
1710 int i;
1712 apr_xml_doc *doc;
1713 const apr_xml_elem *elem;
1714 dav_error *err;
1715
1718
1719 /* resolve the resource */
1720 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
1721 &resource);
1722 if (err != NULL)
1723 return dav_handle_err(r, err, NULL);
1724
1725 /* parse any request body */
1726 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
1727 return result;
1728 }
1729 /* note: doc == NULL if no request body */
1730
1731 /* check for any method preconditions */
1733 && err) {
1734 return dav_handle_err(r, err, NULL);
1735 }
1736
1737 if (doc && !dav_validate_root(doc, "options")) {
1739 "The \"options\" element was not found.");
1740 return HTTP_BAD_REQUEST;
1741 }
1742
1743 /* determine which providers are available */
1744 dav_level = "1";
1745
1746 if (locks_hooks != NULL) {
1747 dav_level = "1,2";
1748 }
1749
1750 if (binding_hooks != NULL)
1751 dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
1752
1753 /* DAV header additions registered by external modules */
1755 entry = (ap_list_provider_names_t *)extensions->elts;
1756
1757 for (i = 0; i < extensions->nelts; i++, entry++) {
1758 const dav_options_provider *options =
1760
1761 if (options && options->dav_header) {
1762 apr_text_header hoptions = { 0 };
1763
1764 options->dav_header(r, resource, &hoptions);
1765 for (t = hoptions.first; t && t->text; t = t->next)
1766 dav_level = apr_pstrcat(r->pool, dav_level, ",", t->text, NULL);
1767 }
1768 }
1769
1770 /* ###
1771 * MSFT Web Folders chokes if length of DAV header value > 63 characters!
1772 * To workaround that, we use separate DAV headers for versioning and
1773 * live prop provider namespace URIs.
1774 * ###
1775 */
1777
1778 /*
1779 * If there is a versioning provider, generate DAV headers
1780 * for versioning options.
1781 */
1782 if (vsn_hooks != NULL) {
1783 (*vsn_hooks->get_vsn_options)(r->pool, &vsn_options);
1784
1785 for (t = vsn_options.first; t != NULL; t = t->next)
1786 apr_table_addn(r->headers_out, "DAV", t->text);
1787 }
1788
1789 /*
1790 * Gather property set URIs from all the liveprop providers,
1791 * and generate a separate DAV header for each URI, to avoid
1792 * problems with long header lengths.
1793 */
1794 uri_ary = apr_array_make(r->pool, 5, sizeof(const char *));
1796 for (i = 0; i < uri_ary->nelts; ++i) {
1797 if (((char **)uri_ary->elts)[i] != NULL)
1798 apr_table_addn(r->headers_out, "DAV", ((char **)uri_ary->elts)[i]);
1799 }
1800
1801 /* this tells MSFT products to skip looking for FrontPage extensions */
1802 apr_table_setn(r->headers_out, "MS-Author-Via", "DAV");
1803
1804 /*
1805 * Determine which methods are allowed on the resource.
1806 * Three cases: resource is null (3), is lock-null (7.4), or exists.
1807 *
1808 * All cases support OPTIONS, and if there is a lock provider, LOCK.
1809 * (Lock-) null resources also support MKCOL and PUT.
1810 * Lock-null supports PROPFIND and UNLOCK.
1811 * Existing resources support lots of stuff.
1812 */
1813
1814 apr_table_addn(methods, "OPTIONS", "");
1815
1816 /* ### take into account resource type */
1818 {
1820 /* resource exists */
1821 apr_table_addn(methods, "GET", "");
1822 apr_table_addn(methods, "HEAD", "");
1823 apr_table_addn(methods, "POST", "");
1824 apr_table_addn(methods, "DELETE", "");
1825 apr_table_addn(methods, "TRACE", "");
1826 apr_table_addn(methods, "PROPFIND", "");
1827 apr_table_addn(methods, "PROPPATCH", "");
1828 apr_table_addn(methods, "COPY", "");
1829 apr_table_addn(methods, "MOVE", "");
1830
1831 if (!resource->collection)
1832 apr_table_addn(methods, "PUT", "");
1833
1834 if (locks_hooks != NULL) {
1835 apr_table_addn(methods, "LOCK", "");
1836 apr_table_addn(methods, "UNLOCK", "");
1837 }
1838
1839 break;
1840
1842 /* resource is lock-null. */
1843 apr_table_addn(methods, "MKCOL", "");
1844 apr_table_addn(methods, "PROPFIND", "");
1845 apr_table_addn(methods, "PUT", "");
1846
1847 if (locks_hooks != NULL) {
1848 apr_table_addn(methods, "LOCK", "");
1849 apr_table_addn(methods, "UNLOCK", "");
1850 }
1851
1852 break;
1853
1854 case DAV_RESOURCE_NULL:
1855 /* resource is null. */
1856 apr_table_addn(methods, "MKCOL", "");
1857 apr_table_addn(methods, "PUT", "");
1858
1859 if (locks_hooks != NULL)
1860 apr_table_addn(methods, "LOCK", "");
1861
1862 break;
1863
1864 default:
1865 /* ### internal error! */
1866 break;
1867 }
1868
1869 /* If there is a versioning provider, add versioning methods */
1870 if (vsn_hooks != NULL) {
1871 if (!resource->exists) {
1872 if ((*vsn_hooks->versionable)(resource))
1873 apr_table_addn(methods, "VERSION-CONTROL", "");
1874
1875 if (vsn_hooks->can_be_workspace != NULL
1876 && (*vsn_hooks->can_be_workspace)(resource))
1877 apr_table_addn(methods, "MKWORKSPACE", "");
1878
1879 if (vsn_hooks->can_be_activity != NULL
1880 && (*vsn_hooks->can_be_activity)(resource))
1881 apr_table_addn(methods, "MKACTIVITY", "");
1882 }
1883 else if (!resource->versioned) {
1884 if ((*vsn_hooks->versionable)(resource))
1885 apr_table_addn(methods, "VERSION-CONTROL", "");
1886 }
1887 else if (resource->working) {
1888 apr_table_addn(methods, "CHECKIN", "");
1889
1890 /* ### we might not support this DeltaV option */
1891 apr_table_addn(methods, "UNCHECKOUT", "");
1892 }
1893 else if (vsn_hooks->add_label != NULL) {
1894 apr_table_addn(methods, "CHECKOUT", "");
1895 apr_table_addn(methods, "LABEL", "");
1896 }
1897 else {
1898 apr_table_addn(methods, "CHECKOUT", "");
1899 }
1900 }
1901
1902 /* If there is a bindings provider, see if resource is bindable */
1903 if (binding_hooks != NULL
1904 && (*binding_hooks->is_bindable)(resource)) {
1905 apr_table_addn(methods, "BIND", "");
1906 }
1907
1908 /* If there is a search provider, set SEARCH in option */
1909 if (search_hooks != NULL) {
1910 apr_table_addn(methods, "SEARCH", "");
1911 }
1912
1913 /* additional methods registered by external modules */
1915 entry = (ap_list_provider_names_t *)extensions->elts;
1916
1917 for (i = 0; i < extensions->nelts; i++, entry++) {
1918 const dav_options_provider *options =
1920
1921 if (options && options->dav_method) {
1922 apr_text_header hoptions = { 0 };
1923
1924 options->dav_method(r, resource, &hoptions);
1925 for (t = hoptions.first; t && t->text; t = t->next)
1926 apr_table_addn(methods, t->text, "");
1927 }
1928 }
1929
1930 /* Generate the Allow header */
1932 elts = (const apr_table_entry_t *)arr->elts;
1933 text_size = 0;
1934
1935 /* first, compute total length */
1936 for (i = 0; i < arr->nelts; ++i) {
1937 if (elts[i].key == NULL)
1938 continue;
1939
1940 /* add 1 for comma or null */
1941 text_size += strlen(elts[i].key) + 1;
1942 }
1943
1945
1946 for (i = 0; i < arr->nelts; ++i) {
1947 if (elts[i].key == NULL)
1948 continue;
1949
1950 if (s != allow)
1951 *s++ = ',';
1952
1953 strcpy(s, elts[i].key);
1954 s += strlen(s);
1955 }
1956
1957 apr_table_setn(r->headers_out, "Allow", allow);
1958
1959
1960 /* If there is search set_option_head function, set head */
1961 /* DASL: <DAV:basicsearch>
1962 * DASL: <http://foo.bar.com/syntax1>
1963 * DASL: <http://akuma.com/syntax2>
1964 */
1965 if (search_hooks != NULL
1966 && *search_hooks->set_option_head != NULL) {
1967 if ((err = (*search_hooks->set_option_head)(r)) != NULL) {
1968 return dav_handle_err(r, err, NULL);
1969 }
1970 }
1971
1972 /* if there was no request body, then there is no response body */
1973 if (doc == NULL) {
1975
1976 /* ### this sends a Content-Type. the default OPTIONS does not. */
1977
1978 /* ### the default (ap_send_http_options) returns OK, but I believe
1979 * ### that is because it is the default handler and nothing else
1980 * ### will run after the thing. */
1981 return DONE;
1982 }
1983
1984 /* handle each options request */
1985 for (elem = doc->root->first_child; elem != NULL; elem = elem->next) {
1986 /* check for something we recognize first */
1987 int core_option = 0;
1988 dav_error *err = NULL;
1989
1990 if (elem->ns == APR_XML_NS_DAV_ID) {
1991 if (strcmp(elem->name, "supported-method-set") == 0) {
1993 core_option = 1;
1994 }
1995 else if (strcmp(elem->name, "supported-live-property-set") == 0) {
1997 core_option = 1;
1998 }
1999 else if (strcmp(elem->name, "supported-report-set") == 0) {
2001 core_option = 1;
2002 }
2003 }
2004
2005 if (err != NULL)
2006 return dav_handle_err(r, err, NULL);
2007
2008 /* if unrecognized option, pass to versioning provider */
2009 if (!core_option && vsn_hooks != NULL) {
2010 if ((err = (*vsn_hooks->get_option)(resource, elem, &body))
2011 != NULL) {
2012 return dav_handle_err(r, err, NULL);
2013 }
2014 }
2015 }
2016
2017 /* send the options response */
2018 r->status = HTTP_OK;
2020
2021 /* send the headers and response body */
2023 "<D:options-response xmlns:D=\"DAV:\">" DEBUG_CR, r);
2024
2025 for (t = body.first; t != NULL; t = t->next)
2026 ap_rputs(t->text, r);
2027
2028 ap_rputs("</D:options-response>" DEBUG_CR, r);
2029
2030 /* we've sent everything necessary to the client. */
2031 return DONE;
2032}
2033
2035{
2036 const apr_xml_elem *elem;
2037 apr_text_header hdr = { 0 };
2038
2039 /* just return if we built the thing already */
2040 if (ctx->propstat_404 != NULL) {
2041 return;
2042 }
2043
2044 apr_text_append(ctx->w.pool, &hdr,
2045 "<D:propstat>" DEBUG_CR
2046 "<D:prop>" DEBUG_CR);
2047
2048 elem = dav_find_child(ctx->doc->root, "prop");
2049 for (elem = elem->first_child; elem; elem = elem->next) {
2050 apr_text_append(ctx->w.pool, &hdr,
2051 apr_xml_empty_elem(ctx->w.pool, elem));
2052 }
2053
2054 apr_text_append(ctx->w.pool, &hdr,
2055 "</D:prop>" DEBUG_CR
2056 "<D:status>HTTP/1.1 404 Not Found</D:status>" DEBUG_CR
2057 "</D:propstat>" DEBUG_CR);
2058
2059 ctx->propstat_404 = hdr.first;
2060}
2061
2063{
2064 dav_walker_ctx *ctx = wres->walk_ctx;
2065 dav_dir_conf *conf;
2066 int flags = DAV_PROPDB_RO;
2067 dav_error *err;
2068 dav_propdb *propdb;
2069 dav_get_props_result propstats = { 0 };
2070
2071 /* check for any method preconditions */
2072 if (dav_run_method_precondition(ctx->r, NULL, wres->resource, ctx->doc, &err) != DECLINED
2073 && err) {
2074 apr_pool_clear(ctx->scratchpool);
2075 return NULL;
2076 }
2077
2078 conf = ap_get_module_config(ctx->r->per_dir_config, &dav_module);
2079 if (conf && conf->allow_lockdiscovery == DAV_ENABLED_OFF)
2081
2082 /*
2083 ** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since
2084 ** dav_get_allprops() does not need to do namespace translation,
2085 ** we're okay.
2086 **
2087 ** Note: we cast to lose the "const". The propdb won't try to change
2088 ** the resource, however, since we are opening readonly.
2089 */
2090 err = dav_popen_propdb(ctx->scratchpool,
2091 ctx->r, ctx->w.lockdb, wres->resource, flags,
2092 ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
2093 if (err != NULL) {
2094 /* ### do something with err! */
2095
2096 if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) {
2098
2099 /* some props were expected on this collection/resource */
2101 badprops.propstats = ctx->propstat_404;
2102 dav_stream_response(wres, 0, &badprops, ctx->scratchpool);
2103 }
2104 else {
2105 /* no props on this collection/resource */
2106 dav_stream_response(wres, HTTP_OK, NULL, ctx->scratchpool);
2107 }
2108
2109 apr_pool_clear(ctx->scratchpool);
2110 return NULL;
2111 }
2112 /* ### what to do about closing the propdb on server failure? */
2113
2114 if (ctx->propfind_type == DAV_PROPFIND_IS_PROP) {
2115 propstats = dav_get_props(propdb, ctx->doc);
2116 }
2117 else {
2121 propstats = dav_get_allprops(propdb, what);
2122 }
2123 dav_stream_response(wres, 0, &propstats, ctx->scratchpool);
2124
2125 dav_close_propdb(propdb);
2126
2127 /* at this point, ctx->scratchpool has been used to stream a
2128 single response. this function fully controls the pool, and
2129 thus has the right to clear it for the next iteration of this
2130 callback. */
2131 apr_pool_clear(ctx->scratchpool);
2132
2133 return NULL;
2134}
2135
2136/* handle the PROPFIND method */
2138{
2140 int depth;
2141 dav_error *err;
2142 int result;
2143 apr_xml_doc *doc;
2144 dav_walker_ctx ctx = { { 0 } };
2146
2147 /* Ask repository module to resolve the resource */
2148 err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
2149 &resource);
2150 if (err != NULL)
2151 return dav_handle_err(r, err, NULL);
2152
2153 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
2154 return result;
2155 }
2156 /* note: doc == NULL if no request body */
2157
2158 /* check for any method preconditions */
2160 && err) {
2161 return dav_handle_err(r, err, NULL);
2162 }
2163
2165 /* Apache will supply a default error for this. */
2166 return HTTP_NOT_FOUND;
2167 }
2168
2169 if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) {
2170 /* dav_get_depth() supplies additional information for the
2171 * default message. */
2172 return HTTP_BAD_REQUEST;
2173 }
2174
2175 if (depth == DAV_INFINITY && resource->collection) {
2176 dav_dir_conf *conf;
2178 &dav_module);
2179 /* default is to DISALLOW these requests */
2180 if (conf->allow_depthinfinity != DAV_ENABLED_ON) {
2183 "PROPFIND requests with a "
2184 "Depth of \"infinity\" are "
2185 "not allowed for %s.",
2187 r->uri)));
2188 }
2189 }
2190
2191 if (doc && !dav_validate_root(doc, "propfind")) {
2192 /* This supplies additional information for the default message. */
2194 "The \"propfind\" element was not found.");
2195 return HTTP_BAD_REQUEST;
2196 }
2197
2198 /* ### validate that only one of these three elements is present */
2199
2200 if (doc == NULL || dav_find_child(doc->root, "allprop") != NULL) {
2201 /* note: no request body implies allprop */
2202 ctx.propfind_type = DAV_PROPFIND_IS_ALLPROP;
2203 }
2204 else if (dav_find_child(doc->root, "propname") != NULL) {
2205 ctx.propfind_type = DAV_PROPFIND_IS_PROPNAME;
2206 }
2207 else if (dav_find_child(doc->root, "prop") != NULL) {
2208 ctx.propfind_type = DAV_PROPFIND_IS_PROP;
2209 }
2210 else {
2211 /* "propfind" element must have one of the above three children */
2212
2213 /* This supplies additional information for the default message. */
2215 "The \"propfind\" element does not contain one of "
2216 "the required child elements (the specific command).");
2217 return HTTP_BAD_REQUEST;
2218 }
2219
2221 ctx.w.func = dav_propfind_walker;
2222 ctx.w.walk_ctx = &ctx;
2223 ctx.w.pool = r->pool;
2224 ctx.w.root = resource;
2225
2226 ctx.doc = doc;
2227 ctx.r = r;
2229 apr_pool_create(&ctx.scratchpool, r->pool);
2230 apr_pool_tag(ctx.scratchpool, "mod_dav-scratch");
2231
2232 if ((err = dav_open_lockdb(r, 1, &ctx.w.lockdb)) != NULL) {
2233 err = dav_push_error(r->pool, err->status, 0,
2234 "The lock database could not be opened, "
2235 "preventing access to the various lock "
2236 "properties for the PROPFIND.",
2237 err);
2238 return dav_handle_err(r, err, NULL);
2239 }
2240 if (ctx.w.lockdb != NULL) {
2241 /* if we have a lock database, then we can walk locknull resources */
2242 ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL;
2243 }
2244
2245 /* send <multistatus> tag, with all doc->namespaces attached. */
2246
2247 /* NOTE: we *cannot* leave out the doc's namespaces from the
2248 initial <multistatus> tag. if a 404 was generated for an HREF,
2249 then we need to spit out the doc's namespaces for use by the
2250 404. Note that <response> elements will override these ns0,
2251 ns1, etc, but NOT within the <response> scope for the
2252 badprops. */
2254 doc ? doc->namespaces : NULL);
2255
2256 /* Have the provider walk the resource. */
2257 err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
2258
2259 if (ctx.w.lockdb != NULL) {
2260 (*ctx.w.lockdb->hooks->close_lockdb)(ctx.w.lockdb);
2261 }
2262
2263 if (err != NULL) {
2264 /* If an error occurred during the resource walk, there's
2265 basically nothing we can do but abort the connection and
2266 log an error. This is one of the limitations of HTTP; it
2267 needs to "know" the entire status of the response before
2268 generating it, which is just impossible in these streamy
2269 response situations. */
2270 err = dav_push_error(r->pool, err->status, 0,
2271 "Provider encountered an error while streaming"
2272 " a multistatus PROPFIND response.", err);
2274 r->connection->aborted = 1;
2275 return DONE;
2276 }
2277
2279
2280 /* the response has been sent. */
2281 return DONE;
2282}
2283
2286{
2287 apr_text_header hdr = { 0 };
2288 int i = prop_ctx->nelts;
2292 const char *s;
2293
2294 /* ### might be nice to sort by status code and description */
2295
2296 for ( ; i-- > 0; ++ctx ) {
2298 "<D:propstat>" DEBUG_CR
2299 "<D:prop>");
2301 apr_text_append(p, &hdr, "</D:prop>" DEBUG_CR);
2302
2303 if (ctx->err == NULL) {
2304 /* nothing was assigned here yet, so make it a 424 */
2305
2306 if (ctx->operation == DAV_PROP_OP_SET) {
2307 if (err424_set == NULL)
2309 "Attempted DAV:set operation "
2310 "could not be completed due "
2311 "to other errors.");
2312 ctx->err = err424_set;
2313 }
2314 else if (ctx->operation == DAV_PROP_OP_DELETE) {
2315 if (err424_delete == NULL)
2317 "Attempted DAV:remove "
2318 "operation could not be "
2319 "completed due to other "
2320 "errors.");
2321 ctx->err = err424_delete;
2322 }
2323 }
2324
2325 s = apr_psprintf(p,
2326 "<D:status>"
2327 "HTTP/1.1 %d (status)"
2328 "</D:status>" DEBUG_CR,
2329 ctx->err->status);
2330 apr_text_append(p, &hdr, s);
2331
2332 /* ### we should use compute_desc if necessary... */
2333 if (ctx->err->desc != NULL) {
2334 apr_text_append(p, &hdr, "<D:responsedescription>" DEBUG_CR);
2335 apr_text_append(p, &hdr, ctx->err->desc);
2336 apr_text_append(p, &hdr, "</D:responsedescription>" DEBUG_CR);
2337 }
2338
2339 apr_text_append(p, &hdr, "</D:propstat>" DEBUG_CR);
2340 }
2341
2342 return hdr.first;
2343}
2344
2347{
2348 apr_text_header hdr = { 0 };
2349 int i = prop_ctx->nelts;
2351
2352 /*
2353 * ### we probably need to revise the way we assemble the response...
2354 * ### this code assumes everything will return status==200.
2355 */
2356
2358 "<D:propstat>" DEBUG_CR
2359 "<D:prop>" DEBUG_CR);
2360
2361 for ( ; i-- > 0; ++ctx ) {
2363 }
2364
2366 "</D:prop>" DEBUG_CR
2367 "<D:status>HTTP/1.1 200 OK</D:status>" DEBUG_CR
2368 "</D:propstat>" DEBUG_CR);
2369
2370 return hdr.first;
2371}
2372
2374{
2375 dav_log_err(ctx->r, ctx->err, APLOG_ERR);
2376}
2377
2378/*
2379 * Call <func> for each context. This can stop when an error occurs, or
2380 * simply iterate through the whole list.
2381 *
2382 * Returns 1 if an error occurs (and the iteration is aborted). Returns 0
2383 * if all elements are processed.
2384 *
2385 * If <reverse> is true (non-zero), then the list is traversed in
2386 * reverse order.
2387 */
2390 int reverse)
2391{
2392 int i = ctx_list->nelts;
2394
2395 if (reverse)
2396 ctx += i;
2397
2398 while (i--) {
2399 if (reverse)
2400 --ctx;
2401
2402 (*func)(ctx);
2404 return 1;
2405 }
2406
2407 if (!reverse)
2408 ++ctx;
2409 }
2410
2411 return 0;
2412}
2413
2414/* handle the PROPPATCH method */
2416{
2417 dav_error *err;
2419 int result;
2420 apr_xml_doc *doc;
2421 apr_xml_elem *child;
2422 dav_propdb *propdb;
2423 int failure = 0;
2424 dav_response resp = { 0 };
2429
2430 /* Ask repository module to resolve the resource */
2431 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
2432 &resource);
2433 if (err != NULL)
2434 return dav_handle_err(r, err, NULL);
2435
2436 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
2437 return result;
2438 }
2439 /* note: doc == NULL if no request body */
2440
2441 /* check for any method preconditions */
2443 && err) {
2444 return dav_handle_err(r, err, NULL);
2445 }
2446
2447 if (!resource->exists) {
2448 /* Apache will supply a default error for this. */
2449 return HTTP_NOT_FOUND;
2450 }
2451
2452 if (doc == NULL || !dav_validate_root(doc, "propertyupdate")) {
2453 /* This supplies additional information for the default message. */
2455 "The request body does not contain "
2456 "a \"propertyupdate\" element.");
2457 return HTTP_BAD_REQUEST;
2458 }
2459
2460 /* Check If-Headers and existing locks */
2461 /* Note: depth == 0. Implies no need for a multistatus response. */
2464 /* ### add a higher-level description? */
2465 return dav_handle_err(r, err, NULL);
2466 }
2467
2468 /* make sure the resource can be modified (if versioning repository) */
2470 0 /* not parent_only */,
2471 &av_info)) != NULL) {
2472 /* ### add a higher-level description? */
2473 return dav_handle_err(r, err, NULL);
2474 }
2475
2478 &propdb)) != NULL) {
2479 /* undo any auto-checkout */
2480 dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
2481
2484 "Could not open the property "
2485 "database for %s.",
2486 ap_escape_html(r->pool, r->uri)),
2487 err);
2488 return dav_handle_err(r, err, NULL);
2489 }
2490 /* ### what to do about closing the propdb on server failure? */
2491
2492 /* ### validate "live" properties */
2493
2494 /* set up an array to hold property operation contexts */
2495 ctx_list = apr_array_make(r->pool, 10, sizeof(dav_prop_ctx));
2496
2497 /* do a first pass to ensure that all "remove" properties exist */
2498 for (child = doc->root->first_child; child; child = child->next) {
2499 int is_remove;
2502
2503 /* Ignore children that are not set/remove */
2504 if (child->ns != APR_XML_NS_DAV_ID
2505 || (!(is_remove = (strcmp(child->name, "remove") == 0))
2506 && strcmp(child->name, "set") != 0)) {
2507 continue;
2508 }
2509
2510 /* make sure that a "prop" child exists for set/remove */
2511 if ((prop_group = dav_find_child(child, "prop")) == NULL) {
2512 dav_close_propdb(propdb);
2513
2514 /* undo any auto-checkout */
2515 dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
2516
2517 /* This supplies additional information for the default message. */
2519 "A \"prop\" element is missing inside "
2520 "the propertyupdate command.");
2521 return HTTP_BAD_REQUEST;
2522 }
2523
2524 for (one_prop = prop_group->first_child; one_prop;
2525 one_prop = one_prop->next) {
2526
2528 ctx->propdb = propdb;
2530 ctx->prop = one_prop;
2531
2532 ctx->r = r; /* for later use by dav_prop_log_errors() */
2533
2535
2536 if ( DAV_PROP_CTX_HAS_ERR(*ctx) ) {
2537 failure = 1;
2538 }
2539 }
2540 }
2541
2542 /* ### should test that we found at least one set/remove */
2543
2544 /* execute all of the operations */
2546 failure = 1;
2547 }
2548
2549 /* generate a failure/success response */
2550 if (failure) {
2553 }
2554 else {
2557 }
2558
2559 /* make sure this gets closed! */
2560 dav_close_propdb(propdb);
2561
2562 /* complete any auto-versioning */
2563 dav_auto_checkin(r, resource, failure, 0 /*unlock*/, &av_info);
2564
2565 /* log any errors that occurred */
2567
2568 resp.href = resource->uri;
2569
2570 /* ### should probably use something new to pass along this text... */
2571 resp.propresult.propstats = propstat_text;
2572
2574
2575 /* the response has been sent. */
2576 return DONE;
2577}
2578
2580{
2581 /* This is snarfed from ap_setup_client_block(). We could get pretty
2582 * close to this behavior by passing REQUEST_NO_BODY, but we need to
2583 * return HTTP_UNSUPPORTED_MEDIA_TYPE (while ap_setup_client_block
2584 * returns HTTP_REQUEST_ENTITY_TOO_LARGE). */
2585
2586 const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
2587 const char *lenp = apr_table_get(r->headers_in, "Content-Length");
2588
2589 /* make sure to set the Apache request fields properly. */
2591 r->read_chunked = 0;
2592 r->remaining = 0;
2593
2594 if (tenc) {
2595 if (ap_cstr_casecmp(tenc, "chunked")) {
2596 /* Use this instead of Apache's default error string */
2598 "Unknown Transfer-Encoding %s", tenc);
2599 return HTTP_NOT_IMPLEMENTED;
2600 }
2601
2602 r->read_chunked = 1;
2603 }
2604 else if (lenp) {
2606 r->remaining = 0;
2607 /* This supplies additional information for the default message. */
2609 "Invalid Content-Length %s", lenp);
2610 return HTTP_BAD_REQUEST;
2611 }
2612 }
2613
2614 if (r->read_chunked || r->remaining > 0) {
2615 /* ### log something? */
2616
2617 /* Apache will supply a default error for this. */
2619 }
2620
2621 /*
2622 * Get rid of the body. this will call ap_setup_client_block(), but
2623 * our copy above has already verified its work.
2624 */
2625 return ap_discard_request_body(r);
2626}
2627
2628/* handle the MKCOL method */
2630{
2632 int resource_state;
2635 dav_error *err;
2636 dav_error *err2;
2637 int result;
2639
2640 /* handle the request body */
2641 /* ### this may move lower once we start processing bodies */
2642 if ((result = process_mkcol_body(r)) != OK) {
2643 return result;
2644 }
2645
2646 /* Ask repository module to resolve the resource */
2647 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
2648 &resource);
2649 if (err != NULL)
2650 return dav_handle_err(r, err, NULL);
2651
2652 /* check for any method preconditions */
2654 && err) {
2655 return dav_handle_err(r, err, NULL);
2656 }
2657
2658 if (resource->exists) {
2659 /* oops. something was already there! */
2660
2661 /* Apache will supply a default error for this. */
2662 /* ### we should provide a specific error message! */
2664 }
2665
2667
2668 /*
2669 * Check If-Headers and existing locks.
2670 *
2671 * Note: depth == 0 normally requires no multistatus response. However,
2672 * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
2673 * other than the Request-URI, thereby requiring a multistatus.
2674 *
2675 * If the resource does not exist (DAV_RESOURCE_NULL), then we must
2676 * check the resource *and* its parent. If the resource exists or is
2677 * a locknull resource, then we check only the resource.
2678 */
2683 /* ### add a higher-level description? */
2684 return dav_handle_err(r, err, multi_status);
2685 }
2686
2687 /* if versioned resource, make sure parent is checked out */
2688 if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
2689 &av_info)) != NULL) {
2690 /* ### add a higher-level description? */
2691 return dav_handle_err(r, err, NULL);
2692 }
2693
2694 /* try to create the collection */
2695 resource->collection = 1;
2696 err = (*resource->hooks->create_collection)(resource);
2697
2698 /* restore modifiability of parent back to what it was */
2699 err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
2700 0 /*unlock*/, &av_info);
2701
2702 /* check for errors now */
2703 if (err != NULL) {
2704 return dav_handle_err(r, err, NULL);
2705 }
2706 if (err2 != NULL) {
2707 /* just log a warning */
2708 err = dav_push_error(r->pool, err2->status, 0,
2709 "The MKCOL was successful, but there "
2710 "was a problem automatically checking in "
2711 "the parent collection.",
2712 err2);
2714 }
2715
2716 if (locks_hooks != NULL) {
2717 dav_lockdb *lockdb;
2718
2719 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
2720 /* The directory creation was successful, but the locking failed. */
2721 err = dav_push_error(r->pool, err->status, 0,
2722 "The MKCOL was successful, but there "
2723 "was a problem opening the lock database "
2724 "which prevents inheriting locks from the "
2725 "parent resources.",
2726 err);
2727 return dav_handle_err(r, err, NULL);
2728 }
2729
2730 /* notify lock system that we have created/replaced a resource */
2732
2733 (*locks_hooks->close_lockdb)(lockdb);
2734
2735 if (err != NULL) {
2736 /* The dir creation was successful, but the locking failed. */
2737 err = dav_push_error(r->pool, err->status, 0,
2738 "The MKCOL was successful, but there "
2739 "was a problem updating its lock "
2740 "information.",
2741 err);
2742 return dav_handle_err(r, err, NULL);
2743 }
2744 }
2745
2746 /* return an appropriate response (HTTP_CREATED) */
2747 return dav_created(r, NULL, "Collection", 0);
2748}
2749
2750/* handle the COPY and MOVE methods */
2751static int dav_method_copymove(request_rec *r, int is_move)
2752{
2757 const char *body;
2758 const char *dest;
2759 dav_error *err;
2760 dav_error *err2;
2761 dav_error *err3;
2764 int is_dir;
2765 int overwrite;
2766 int depth;
2767 int result;
2768 dav_lockdb *lockdb;
2769 int replace_dest;
2770 int resnew_state;
2771
2772 /* Ask repository module to resolve the resource */
2773 err = dav_get_resource(r, !is_move /* label_allowed */,
2774 0 /* use_checked_in */, &resource);
2775 if (err != NULL)
2776 return dav_handle_err(r, err, NULL);
2777
2778 /* check for any method preconditions */
2780 && err) {
2781 return dav_handle_err(r, err, NULL);
2782 }
2783
2784 if (!resource->exists) {
2785 /* Apache will supply a default error for this. */
2786 return HTTP_NOT_FOUND;
2787 }
2788
2789 /* If not a file or collection resource, COPY/MOVE not allowed */
2790 /* ### allow COPY/MOVE of DeltaV resource types */
2791 if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
2792 body = apr_psprintf(r->pool,
2793 "Cannot COPY/MOVE resource %s.",
2794 ap_escape_html(r->pool, r->uri));
2796 }
2797
2798 /* get the destination URI */
2799 dest = apr_table_get(r->headers_in, "Destination");
2800 if (dest == NULL) {
2801 /* Look in headers provided by Netscape's Roaming Profiles */
2802 const char *nscp_host = apr_table_get(r->headers_in, "Host");
2803 const char *nscp_path = apr_table_get(r->headers_in, "New-uri");
2804
2805 if (nscp_host != NULL && nscp_path != NULL)
2806 dest = apr_pstrcat(r->pool, "http://", nscp_host, nscp_path, NULL);
2807 }
2808 if (dest == NULL) {
2809 /* This supplies additional information for the default message. */
2811 "The request is missing a Destination header.");
2812 return HTTP_BAD_REQUEST;
2813 }
2814
2815 lookup = dav_lookup_uri(dest, r, 1 /* must_be_absolute */);
2816 if (lookup.rnew == NULL) {
2817 if (lookup.err.status == HTTP_BAD_REQUEST) {
2818 /* This supplies additional information for the default message. */
2820 "%s", lookup.err.desc);
2821 return HTTP_BAD_REQUEST;
2822 }
2823
2824 /* ### this assumes that dav_lookup_uri() only generates a status
2825 * ### that Apache can provide a status line for!! */
2826
2827 return dav_error_response(r, lookup.err.status, lookup.err.desc);
2828 }
2829 if (lookup.rnew->status != HTTP_OK) {
2830 const char *auth = apr_table_get(lookup.rnew->err_headers_out,
2831 "WWW-Authenticate");
2832 if (lookup.rnew->status == HTTP_UNAUTHORIZED && auth != NULL) {
2833 /* propagate the WWW-Authorization header up from the
2834 * subreq so the client sees it. */
2835 apr_table_setn(r->err_headers_out, "WWW-Authenticate",
2836 apr_pstrdup(r->pool, auth));
2837 }
2838
2839 /* ### how best to report this... */
2840 return dav_error_response(r, lookup.rnew->status,
2841 "Destination URI had an error.");
2842 }
2843
2844 /* Resolve destination resource */
2845 err = dav_get_resource(lookup.rnew, 0 /* label_allowed */,
2846 0 /* use_checked_in */, &resnew);
2847 if (err != NULL)
2848 return dav_handle_err(r, err, NULL);
2849
2850 /* check for any method preconditions */
2852 && err) {
2853 return dav_handle_err(r, err, NULL);
2854 }
2855
2856 /* are the two resources handled by the same repository? */
2857 if (resource->hooks != resnew->hooks) {
2858 /* ### this message exposes some backend config, but screw it... */
2860 "Destination URI is handled by a "
2861 "different repository than the source URI. "
2862 "MOVE or COPY between repositories is "
2863 "not possible.");
2864 }
2865
2866 /* get and parse the overwrite header value */
2867 if ((overwrite = dav_get_overwrite(r)) < 0) {
2868 /* dav_get_overwrite() supplies additional information for the
2869 * default message. */
2870 return HTTP_BAD_REQUEST;
2871 }
2872
2873 /* quick failure test: if dest exists and overwrite is false. */
2874 if (resnew->exists && !overwrite) {
2875 /* Supply some text for the error response body. */
2877 "Destination is not empty and "
2878 "Overwrite is not \"T\"");
2879 }
2880
2881 /* are the source and destination the same? */
2882 if ((*resource->hooks->is_same_resource)(resource, resnew)) {
2883 /* Supply some text for the error response body. */
2885 "Source and Destination URIs are the same.");
2886
2887 }
2888
2889 is_dir = resource->collection;
2890
2891 /* get and parse the Depth header value. "0" and "infinity" are legal. */
2892 if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) {
2893 /* dav_get_depth() supplies additional information for the
2894 * default message. */
2895 return HTTP_BAD_REQUEST;
2896 }
2897 if (depth == 1) {
2898 /* This supplies additional information for the default message. */
2900 "Depth must be \"0\" or \"infinity\" for COPY or MOVE.");
2901 return HTTP_BAD_REQUEST;
2902 }
2903 if (is_move && is_dir && depth != DAV_INFINITY) {
2904 /* This supplies additional information for the default message. */
2906 "Depth must be \"infinity\" when moving a collection.");
2907 return HTTP_BAD_REQUEST;
2908 }
2909
2910 /*
2911 * Check If-Headers and existing locks for each resource in the source.
2912 * We will return a 424 response with a DAV:multistatus body.
2913 * The multistatus responses will contain the information about any
2914 * resource that fails the validation.
2915 *
2916 * We check the parent resource, too, if this is a MOVE. Moving the
2917 * resource effectively removes it from the parent collection, so we
2918 * must ensure that we have met the appropriate conditions.
2919 *
2920 * If a problem occurs with the Request-URI itself, then a plain error
2921 * (rather than a multistatus) will be returned.
2922 */
2923 if ((err = dav_validate_request(r, resource, depth, NULL,
2925 (is_move ? DAV_VALIDATE_PARENT
2929 NULL)) != NULL) {
2930 err = dav_push_error(r->pool, err->status, 0,
2932 "Could not %s %s due to a failed "
2933 "precondition on the source "
2934 "(e.g. locks).",
2935 is_move ? "MOVE" : "COPY",
2936 ap_escape_html(r->pool, r->uri)),
2937 err);
2939 }
2940
2941 /*
2942 * Check If-Headers and existing locks for destination. Note that we
2943 * use depth==infinity since the target (hierarchy) will be deleted
2944 * before the move/copy is completed.
2945 *
2946 * Note that we are overwriting the target, which implies a DELETE, so
2947 * we are subject to the error/response rules as a DELETE. Namely, we
2948 * will return a 424 error if any of the validations fail.
2949 * (see dav_method_delete() for more information)
2950 */
2954 | DAV_VALIDATE_USE_424, NULL)) != NULL) {
2955 err = dav_push_error(r->pool, err->status, 0,
2957 "Could not MOVE/COPY %s due to a "
2958 "failed precondition on the "
2959 "destination (e.g. locks).",
2960 ap_escape_html(r->pool, r->uri)),
2961 err);
2963 }
2964
2965 if (is_dir
2966 && depth == DAV_INFINITY
2967 && (*resource->hooks->is_parent_resource)(resource, resnew)) {
2968 /* Supply some text for the error response body. */
2970 "Source collection contains the "
2971 "Destination.");
2972
2973 }
2974 if (is_dir
2975 && (*resnew->hooks->is_parent_resource)(resnew, resource)) {
2976 /* The destination must exist (since it contains the source), and
2977 * a condition above implies Overwrite==T. Obviously, we cannot
2978 * delete the Destination before the MOVE/COPY, as that would
2979 * delete the Source.
2980 */
2981
2982 /* Supply some text for the error response body. */
2984 "Destination collection contains the Source "
2985 "and Overwrite has been specified.");
2986 }
2987
2988 /* ### for now, we don't need anything in the body */
2989 if ((result = ap_discard_request_body(r)) != OK) {
2990 return result;
2991 }
2992
2993 if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) {
2994 /* ### add a higher-level description? */
2995 return dav_handle_err(r, err, NULL);
2996 }
2997
2998 /* remove any locks from the old resources */
2999 /*
3000 * ### this is Yet Another Traversal. if we do a rename(), then we
3001 * ### really don't have to do this in some cases since the inode
3002 * ### values will remain constant across the move. but we can't
3003 * ### know that fact from outside the provider :-(
3004 *
3005 * ### note that we now have a problem atomicity in the move/copy
3006 * ### since a failure after this would have removed locks (technically,
3007 * ### this is okay to do, but really...)
3008 */
3009 if (is_move && lockdb != NULL) {
3010 /* ### this is wrong! it blasts direct locks on parent resources */
3011 /* ### pass lockdb! */
3012 (void)dav_unlock(r, resource, NULL);
3013 }
3014
3015 /* if this is a move, then the source parent collection will be modified */
3016 if (is_move) {
3017 if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
3018 &src_av_info)) != NULL) {
3019 if (lockdb != NULL)
3020 (*lockdb->hooks->close_lockdb)(lockdb);
3021
3022 /* ### add a higher-level description? */
3023 return dav_handle_err(r, err, NULL);
3024 }
3025 }
3026
3027 /*
3028 * Remember the initial state of the destination, so the lock system
3029 * can be notified as to how it changed.
3030 */
3032
3033 /* In a MOVE operation, the destination is replaced by the source.
3034 * In a COPY operation, if the destination exists, is under version
3035 * control, and is the same resource type as the source,
3036 * then it should not be replaced, but modified to be a copy of
3037 * the source.
3038 */
3039 if (!resnew->exists)
3040 replace_dest = 0;
3041 else if (is_move || !resource->versioned)
3042 replace_dest = 1;
3043 else if (resource->type != resnew->type)
3044 replace_dest = 1;
3045 else if ((resource->collection == 0) != (resnew->collection == 0))
3046 replace_dest = 1;
3047 else
3048 replace_dest = 0;
3049
3050 /* If the destination must be created or replaced,
3051 * make sure the parent collection is writable
3052 */
3053 if (!resnew->exists || replace_dest) {
3054 if ((err = dav_auto_checkout(r, resnew, 1 /*parent_only*/,
3055 &dst_av_info)) != NULL) {
3056 /* could not make destination writable:
3057 * if move, restore state of source parent
3058 */
3059 if (is_move) {
3060 (void)dav_auto_checkin(r, NULL, 1 /* undo */,
3061 0 /*unlock*/, &src_av_info);
3062 }
3063
3064 if (lockdb != NULL)
3065 (*lockdb->hooks->close_lockdb)(lockdb);
3066
3067 /* ### add a higher-level description? */
3068 return dav_handle_err(r, err, NULL);
3069 }
3070 }
3071
3072 /* If source and destination parents are the same, then
3073 * use the same resource object, so status updates to one are reflected
3074 * in the other, when doing auto-versioning. Otherwise,
3075 * we may try to checkin the parent twice.
3076 */
3077 if (src_av_info.parent_resource != NULL
3078 && dst_av_info.parent_resource != NULL
3079 && (*src_av_info.parent_resource->hooks->is_same_resource)
3080 (src_av_info.parent_resource, dst_av_info.parent_resource)) {
3081
3082 dst_av_info.parent_resource = src_av_info.parent_resource;
3083 }
3084
3085 /* If destination is being replaced, remove it first
3086 * (we know Ovewrite must be TRUE). Then try to copy/move the resource.
3087 */
3088 if (replace_dest)
3089 err = (*resnew->hooks->remove_resource)(resnew, &multi_response);
3090
3091 if (err == NULL) {
3092 if (is_move)
3093 err = (*resource->hooks->move_resource)(resource, resnew,
3095 else
3096 err = (*resource->hooks->copy_resource)(resource, resnew, depth,
3098 }
3099
3100 /* perform any auto-versioning cleanup */
3101 err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
3102 0 /*unlock*/, &dst_av_info);
3103
3104 if (is_move) {
3105 err3 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
3106 0 /*unlock*/, &src_av_info);
3107 }
3108 else
3109 err3 = NULL;
3110
3111 /* check for error from remove/copy/move operations */
3112 if (err != NULL) {
3113 if (lockdb != NULL)
3114 (*lockdb->hooks->close_lockdb)(lockdb);
3115
3116 err = dav_push_error(r->pool, err->status, 0,
3118 "Could not MOVE/COPY %s.",
3119 ap_escape_html(r->pool, r->uri)),
3120 err);
3122 }
3123
3124 /* check for errors from auto-versioning */
3125 if (err2 != NULL) {
3126 /* just log a warning */
3127 err = dav_push_error(r->pool, err2->status, 0,
3128 "The MOVE/COPY was successful, but there was a "
3129 "problem automatically checking in the "
3130 "source parent collection.",
3131 err2);
3133 }
3134 if (err3 != NULL) {
3135 /* just log a warning */
3136 err = dav_push_error(r->pool, err3->status, 0,
3137 "The MOVE/COPY was successful, but there was a "
3138 "problem automatically checking in the "
3139 "destination or its parent collection.",
3140 err3);
3142 }
3143
3144 /* propagate any indirect locks at the target */
3145 if (lockdb != NULL) {
3146
3147 /* notify lock system that we have created/replaced a resource */
3148 err = dav_notify_created(r, lockdb, resnew, resnew_state, depth);
3149
3150 (*lockdb->hooks->close_lockdb)(lockdb);
3151
3152 if (err != NULL) {
3153 /* The move/copy was successful, but the locking failed. */
3154 err = dav_push_error(r->pool, err->status, 0,
3155 "The MOVE/COPY was successful, but there "
3156 "was a problem updating the lock "
3157 "information.",
3158 err);
3159 return dav_handle_err(r, err, NULL);
3160 }
3161 }
3162
3163 /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
3164 return dav_created(r, lookup.rnew->unparsed_uri, "Destination",
3166}
3167
3168/* dav_method_lock: Handler to implement the DAV LOCK method
3169 * Returns appropriate HTTP_* response.
3170 */
3172{
3173 dav_error *err;
3177 int result;
3178 int depth;
3179 int new_lock_request = 0;
3180 apr_xml_doc *doc;
3181 dav_lock *lock;
3183 dav_lockdb *lockdb;
3184 int resource_state;
3185
3186 /* If no locks provider, decline the request */
3188 if (locks_hooks == NULL)
3189 return DECLINED;
3190
3191 if ((result = ap_xml_parse_input(r, &doc)) != OK)
3192 return result;
3193
3194 depth = dav_get_depth(r, DAV_INFINITY);
3195 if (depth != 0 && depth != DAV_INFINITY) {
3197 "Depth must be 0 or \"infinity\" for LOCK.");
3198 return HTTP_BAD_REQUEST;
3199 }
3200
3201 /* Ask repository module to resolve the resource */
3202 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
3203 &resource);
3204 if (err != NULL)
3205 return dav_handle_err(r, err, NULL);
3206
3207 /* check for any method preconditions */
3209 && err) {
3210 return dav_handle_err(r, err, NULL);
3211 }
3212
3213 /* Check if parent collection exists */
3214 if ((err = resource->hooks->get_parent_resource(resource, &parent)) != NULL) {
3215 /* ### add a higher-level description? */
3216 return dav_handle_err(r, err, NULL);
3217 }
3218 if (parent && (!parent->exists || parent->collection != 1)) {
3221 "The parent resource of %s does not "
3222 "exist or is not a collection.",
3223 ap_escape_html(r->pool, r->uri)));
3224 return dav_handle_err(r, err, NULL);
3225 }
3226
3227 /*
3228 * Open writable. Unless an error occurs, we'll be
3229 * writing into the database.
3230 */
3231 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
3232 /* ### add a higher-level description? */
3233 return dav_handle_err(r, err, NULL);
3234 }
3235
3236 if (doc != NULL) {
3237 if ((err = dav_lock_parse_lockinfo(r, resource, lockdb, doc,
3238 &lock)) != NULL) {
3239 /* ### add a higher-level description to err? */
3240 goto error;
3241 }
3242 new_lock_request = 1;
3243
3244 lock->auth_user = apr_pstrdup(r->pool, r->user);
3245 }
3246
3248
3249 /*
3250 * Check If-Headers and existing locks.
3251 *
3252 * If this will create a locknull resource, then the LOCK will affect
3253 * the parent collection (much like a PUT/MKCOL). For that case, we must
3254 * validate the parent resource's conditions.
3255 */
3260 | (new_lock_request ? lock->scope : 0)
3262 lockdb)) != OK) {
3263 err = dav_push_error(r->pool, err->status, 0,
3265 "Could not LOCK %s due to a failed "
3266 "precondition (e.g. other locks).",
3267 ap_escape_html(r->pool, r->uri)),
3268 err);
3269 goto error;
3270 }
3271
3272 if (new_lock_request == 0) {
3274
3275 /*
3276 * Refresh request
3277 * ### Assumption: We can renew multiple locks on the same resource
3278 * ### at once. First harvest all the positive lock-tokens given in
3279 * ### the If header. Then modify the lock entries for this resource
3280 * ### with the new Timeout val.
3281 */
3282
3283 if ((err = dav_get_locktoken_list(r, &ltl)) != NULL) {
3284 err = dav_push_error(r->pool, err->status, 0,
3286 "The lock refresh for %s failed "
3287 "because no lock tokens were "
3288 "specified in an \"If:\" "
3289 "header.",
3290 ap_escape_html(r->pool, r->uri)),
3291 err);
3292 goto error;
3293 }
3294
3295 if ((err = (*locks_hooks->refresh_locks)(lockdb, resource, ltl,
3297 &lock)) != NULL) {
3298 /* ### add a higher-level description to err? */
3299 goto error;
3300 }
3301 } else {
3302 /* New lock request */
3303 char *locktoken_txt;
3304 dav_dir_conf *conf;
3305
3307 &dav_module);
3308
3309 /* apply lower bound (if any) from DAVMinTimeout directive */
3310 if (lock->timeout != DAV_TIMEOUT_INFINITE
3311 && lock->timeout < time(NULL) + conf->locktimeout)
3312 lock->timeout = time(NULL) + conf->locktimeout;
3313
3315 if (err != NULL) {
3316 /* ### add a higher-level description to err? */
3317 goto error;
3318 }
3319
3321 (*locks_hooks->format_locktoken)(r->pool,
3322 lock->locktoken),
3323 ">", NULL);
3324
3325 apr_table_setn(r->headers_out, "Lock-Token", locktoken_txt);
3326 }
3327
3328 (*locks_hooks->close_lockdb)(lockdb);
3329
3330 r->status = HTTP_OK;
3332
3333 ap_rputs(DAV_XML_HEADER DEBUG_CR "<D:prop xmlns:D=\"DAV:\">" DEBUG_CR, r);
3334 if (lock == NULL)
3335 ap_rputs("<D:lockdiscovery/>" DEBUG_CR, r);
3336 else {
3337 ap_rprintf(r,
3338 "<D:lockdiscovery>" DEBUG_CR
3339 "%s" DEBUG_CR
3340 "</D:lockdiscovery>" DEBUG_CR,
3342 }
3343 ap_rputs("</D:prop>", r);
3344
3345 /* the response has been sent. */
3346 return DONE;
3347
3348 error:
3349 (*locks_hooks->close_lockdb)(lockdb);
3351}
3352
3353/* dav_method_unlock: Handler to implement the DAV UNLOCK method
3354 * Returns appropriate HTTP_* response.
3355 */
3357{
3358 dav_error *err;
3361 int result;
3362 const char *const_locktoken_txt;
3363 char *locktoken_txt;
3364 dav_locktoken *locktoken = NULL;
3365 int resource_state;
3367
3368 /* If no locks provider, decline the request */
3370 if (locks_hooks == NULL)
3371 return DECLINED;
3372
3374 "Lock-Token")) == NULL) {
3376 "Unlock failed (%s): "
3377 "No Lock-Token specified in header", r->filename);
3378 return HTTP_BAD_REQUEST;
3379 }
3380
3382 if (locktoken_txt[0] != '<') {
3383 /* ### should provide more specifics... */
3384 return HTTP_BAD_REQUEST;
3385 }
3386 locktoken_txt++;
3387
3388 if (locktoken_txt[strlen(locktoken_txt) - 1] != '>') {
3389 /* ### should provide more specifics... */
3390 return HTTP_BAD_REQUEST;
3391 }
3392 locktoken_txt[strlen(locktoken_txt) - 1] = '\0';
3393
3394 if ((err = (*locks_hooks->parse_locktoken)(r->pool, locktoken_txt,
3395 &locktoken)) != NULL) {
3398 "The UNLOCK on %s failed -- an "
3399 "invalid lock token was specified "
3400 "in the \"If:\" header.",
3401 ap_escape_html(r->pool, r->uri)),
3402 err);
3403 return dav_handle_err(r, err, NULL);
3404 }
3405
3406 /* Ask repository module to resolve the resource */
3407 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
3408 &resource);
3409 if (err != NULL)
3410 return dav_handle_err(r, err, NULL);
3411
3412 /* check for any method preconditions */
3414 && err) {
3415 return dav_handle_err(r, err, NULL);
3416 }
3417
3419
3420 /*
3421 * Check If-Headers and existing locks.
3422 *
3423 * Note: depth == 0 normally requires no multistatus response. However,
3424 * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI
3425 * other than the Request-URI, thereby requiring a multistatus.
3426 *
3427 * If the resource is a locknull resource, then the UNLOCK will affect
3428 * the parent collection (much like a delete). For that case, we must
3429 * validate the parent resource's conditions.
3430 */
3431 if ((err = dav_validate_request(r, resource, 0, locktoken,
3436 /* ### add a higher-level description? */
3438 }
3439
3440 /* ### RFC 2518 s. 8.11: If this resource is locked by locktoken,
3441 * _all_ resources locked by locktoken are released. It does not say
3442 * resource has to be the root of an infinite lock. Thus, an UNLOCK
3443 * on any part of an infinite lock will remove the lock on all resources.
3444 *
3445 * For us, if r->filename represents an indirect lock (part of an infinity lock),
3446 * we must actually perform an UNLOCK on the direct lock for this resource.
3447 */
3448 if ((result = dav_unlock(r, resource, locktoken)) != OK) {
3449 return result;
3450 }
3451
3452 return HTTP_NO_CONTENT;
3453}
3454
3456{
3458 int resource_state;
3461 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
3462 dav_error *err;
3463 apr_xml_doc *doc;
3464 const char *target = NULL;
3465 int result;
3466
3467 /* if no versioning provider, decline the request */
3468 if (vsn_hooks == NULL)
3469 return DECLINED;
3470
3471 /* ask repository module to resolve the resource */
3472 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
3473 &resource);
3474 if (err != NULL)
3475 return dav_handle_err(r, err, NULL);
3476
3477 /* parse the request body (may be a version-control element) */
3478 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
3479 return result;
3480 }
3481 /* note: doc == NULL if no request body */
3482
3483 /* check for any method preconditions */
3485 && err) {
3486 return dav_handle_err(r, err, NULL);
3487 }
3488
3489 /* remember the pre-creation resource state */
3491
3492 if (doc != NULL) {
3493 const apr_xml_elem *child;
3495
3496 if (!dav_validate_root(doc, "version-control")) {
3498 "The request body does not contain "
3499 "a \"version-control\" element.");
3500 return HTTP_BAD_REQUEST;
3501 }
3502
3503 /* get the version URI */
3504 if ((child = dav_find_child(doc->root, "version")) == NULL) {
3506 "The \"version-control\" element does not contain "
3507 "a \"version\" element.");
3508 return HTTP_BAD_REQUEST;
3509 }
3510
3511 if ((child = dav_find_child(child, "href")) == NULL) {
3513 "The \"version\" element does not contain "
3514 "an \"href\" element.");
3515 return HTTP_BAD_REQUEST;
3516 }
3517
3518 /* get version URI */
3520 &target, &tsize);
3521 if (tsize == 0) {
3523 "An \"href\" element does not contain a URI.");
3524 return HTTP_BAD_REQUEST;
3525 }
3526 }
3527
3528 /* Check request preconditions */
3529
3530 /* ### need a general mechanism for reporting precondition violations
3531 * ### (should be returning XML document for 403/409 responses)
3532 */
3533
3534 /* if not versioning existing resource, must specify version to select */
3535 if (!resource->exists && target == NULL) {
3537 "<DAV:initial-version-required/>");
3538 return dav_handle_err(r, err, NULL);
3539 }
3540 else if (resource->exists) {
3541 /* cannot add resource to existing version history */
3542 if (target != NULL) {
3544 "<DAV:cannot-add-to-existing-history/>");
3545 return dav_handle_err(r, err, NULL);
3546 }
3547
3548 /* resource must be unversioned and versionable, or version selector */
3550 || (!resource->versioned && !(vsn_hooks->versionable)(resource))) {
3552 "<DAV:must-be-versionable/>");
3553 return dav_handle_err(r, err, NULL);
3554 }
3555
3556 /* the DeltaV spec says if resource is a version selector,
3557 * then VERSION-CONTROL is a no-op
3558 */
3559 if (resource->versioned) {
3560 /* set the Cache-Control header, per the spec */
3561 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
3562
3563 /* no body */
3565
3566 return DONE;
3567 }
3568 }
3569
3570 /* Check If-Headers and existing locks */
3571 /* Note: depth == 0. Implies no need for a multistatus response. */
3576 return dav_handle_err(r, err, NULL);
3577 }
3578
3579 /* if in versioned collection, make sure parent is checked out */
3580 if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
3581 &av_info)) != NULL) {
3582 return dav_handle_err(r, err, NULL);
3583 }
3584
3585 /* attempt to version-control the resource */
3586 if ((err = (*vsn_hooks->vsn_control)(resource, target)) != NULL) {
3587 dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
3590 "Could not VERSION-CONTROL resource %s.",
3591 ap_escape_html(r->pool, r->uri)),
3592 err);
3593 return dav_handle_err(r, err, NULL);
3594 }
3595
3596 /* revert writability of parent directory */
3597 err = dav_auto_checkin(r, resource, 0 /*undo*/, 0 /*unlock*/, &av_info);
3598 if (err != NULL) {
3599 /* just log a warning */
3600 err = dav_push_error(r->pool, err->status, 0,
3601 "The VERSION-CONTROL was successful, but there "
3602 "was a problem automatically checking in "
3603 "the parent collection.",
3604 err);
3606 }
3607
3608 /* if the resource is lockable, let lock system know of new resource */
3609 if (locks_hooks != NULL
3610 && (*locks_hooks->get_supportedlock)(resource) != NULL) {
3611 dav_lockdb *lockdb;
3612
3613 if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
3614 /* The resource creation was successful, but the locking failed. */
3615 err = dav_push_error(r->pool, err->status, 0,
3616 "The VERSION-CONTROL was successful, but there "
3617 "was a problem opening the lock database "
3618 "which prevents inheriting locks from the "
3619 "parent resources.",
3620 err);
3621 return dav_handle_err(r, err, NULL);
3622 }
3623
3624 /* notify lock system that we have created/replaced a resource */
3626
3627 (*locks_hooks->close_lockdb)(lockdb);
3628
3629 if (err != NULL) {
3630 /* The dir creation was successful, but the locking failed. */
3631 err = dav_push_error(r->pool, err->status, 0,
3632 "The VERSION-CONTROL was successful, but there "
3633 "was a problem updating its lock "
3634 "information.",
3635 err);
3636 return dav_handle_err(r, err, NULL);
3637 }
3638 }
3639
3640 /* set the Cache-Control header, per the spec */
3641 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
3642
3643 /* return an appropriate response (HTTP_CREATED) */
3644 return dav_created(r, resource->uri, "Version selector", 0 /*replaced*/);
3645}
3646
3647/* handle the CHECKOUT method */
3649{
3652 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
3653 dav_error *err;
3654 int result;
3655 apr_xml_doc *doc;
3656 int apply_to_vsn = 0;
3657 int is_unreserved = 0;
3658 int is_fork_ok = 0;
3659 int create_activity = 0;
3661
3662 /* If no versioning provider, decline the request */
3663 if (vsn_hooks == NULL)
3664 return DECLINED;
3665
3666 if ((result = ap_xml_parse_input(r, &doc)) != OK)
3667 return result;
3668
3669 if (doc != NULL) {
3670 const apr_xml_elem *aset;
3671
3672 if (!dav_validate_root(doc, "checkout")) {
3673 /* This supplies additional information for the default msg. */
3675 "The request body, if present, must be a "
3676 "DAV:checkout element.");
3677 return HTTP_BAD_REQUEST;
3678 }
3679
3680 if (dav_find_child(doc->root, "apply-to-version") != NULL) {
3681 if (apr_table_get(r->headers_in, "label") != NULL) {
3682 /* ### we want generic 403/409 XML reporting here */
3683 /* ### DAV:must-not-have-label-and-apply-to-version */
3685 "DAV:apply-to-version cannot be "
3686 "used in conjunction with a "
3687 "Label header.");
3688 }
3689 apply_to_vsn = 1;
3690 }
3691
3692 is_unreserved = dav_find_child(doc->root, "unreserved") != NULL;
3693 is_fork_ok = dav_find_child(doc->root, "fork-ok") != NULL;
3694
3695 if ((aset = dav_find_child(doc->root, "activity-set")) != NULL) {
3696 if (dav_find_child(aset, "new") != NULL) {
3697 create_activity = 1;
3698 }
3699 else {
3700 const apr_xml_elem *child = aset->first_child;
3701
3702 activities = apr_array_make(r->pool, 1, sizeof(const char *));
3703
3704 for (; child != NULL; child = child->next) {
3705 if (child->ns == APR_XML_NS_DAV_ID
3706 && strcmp(child->name, "href") == 0) {
3707 const char *href;
3708
3709 href = dav_xml_get_cdata(child, r->pool,
3710 1 /* strip_white */);
3711 *(const char **)apr_array_push(activities) = href;
3712 }
3713 }
3714
3715 if (activities->nelts == 0) {
3716 /* no href's is a DTD violation:
3717 <!ELEMENT activity-set (href+ | new)>
3718 */
3719
3720 /* This supplies additional info for the default msg. */
3722 "Within the DAV:activity-set element, the "
3723 "DAV:new element must be used, or at least "
3724 "one DAV:href must be specified.");
3725 return HTTP_BAD_REQUEST;
3726 }
3727 }
3728 }
3729 }
3730
3731 /* Ask repository module to resolve the resource */
3732 err = dav_get_resource(r, 1 /*label_allowed*/, apply_to_vsn, &resource);
3733 if (err != NULL)
3734 return dav_handle_err(r, err, NULL);
3735
3736 /* check for any method preconditions */
3738 && err) {
3739 return dav_handle_err(r, err, NULL);
3740 }
3741
3742 if (!resource->exists) {
3743 /* Apache will supply a default error for this. */
3744 return HTTP_NOT_FOUND;
3745 }
3746
3747 /* Check the state of the resource: must be a file or collection,
3748 * must be versioned, and must not already be checked out.
3749 */
3751 && resource->type != DAV_RESOURCE_TYPE_VERSION) {
3753 "Cannot checkout this type of resource.");
3754 }
3755
3756 if (!resource->versioned) {
3758 "Cannot checkout unversioned resource.");
3759 }
3760
3761 if (resource->working) {
3763 "The resource is already checked out to the workspace.");
3764 }
3765
3766 /* ### do lock checks, once behavior is defined */
3767
3768 /* Do the checkout */
3769 if ((err = (*vsn_hooks->checkout)(resource, 0 /*auto_checkout*/,
3772 &working_resource)) != NULL) {
3775 "Could not CHECKOUT resource %s.",
3776 ap_escape_html(r->pool, r->uri)),
3777 err);
3778 return dav_handle_err(r, err, NULL);
3779 }
3780
3781 /* set the Cache-Control header, per the spec */
3782 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
3783
3784 /* if no working resource created, return OK,
3785 * else return CREATED with working resource URL in Location header
3786 */
3787 if (working_resource == NULL) {
3788 /* no body */
3790 return DONE;
3791 }
3792
3793 return dav_created(r, working_resource->uri, "Checked-out resource", 0);
3794}
3795
3796/* handle the UNCHECKOUT method */
3798{
3800 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
3801 dav_error *err;
3802 int result;
3803
3804 /* If no versioning provider, decline the request */
3805 if (vsn_hooks == NULL)
3806 return DECLINED;
3807
3808 if ((result = ap_discard_request_body(r)) != OK) {
3809 return result;
3810 }
3811
3812 /* Ask repository module to resolve the resource */
3813 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
3814 &resource);
3815 if (err != NULL)
3816 return dav_handle_err(r, err, NULL);
3817
3818 /* check for any method preconditions */
3820 && err) {
3821 return dav_handle_err(r, err, NULL);
3822 }
3823
3824 if (!resource->exists) {
3825 /* Apache will supply a default error for this. */
3826 return HTTP_NOT_FOUND;
3827 }
3828
3829 /* Check the state of the resource: must be a file or collection,
3830 * must be versioned, and must be checked out.
3831 */
3832 if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
3834 "Cannot uncheckout this type of resource.");
3835 }
3836
3837 if (!resource->versioned) {
3839 "Cannot uncheckout unversioned resource.");
3840 }
3841
3842 if (!resource->working) {
3844 "The resource is not checked out to the workspace.");
3845 }
3846
3847 /* ### do lock checks, once behavior is defined */
3848
3849 /* Do the uncheckout */
3850 if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) {
3853 "Could not UNCHECKOUT resource %s.",
3854 ap_escape_html(r->pool, r->uri)),
3855 err);
3856 return dav_handle_err(r, err, NULL);
3857 }
3858
3859 /* no body */
3861
3862 return DONE;
3863}
3864
3865/* handle the CHECKIN method */
3867{
3870 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
3871 dav_error *err;
3872 int result;
3873 apr_xml_doc *doc;
3874 int keep_checked_out = 0;
3875
3876 /* If no versioning provider, decline the request */
3877 if (vsn_hooks == NULL)
3878 return DECLINED;
3879
3880 if ((result = ap_xml_parse_input(r, &doc)) != OK)
3881 return result;
3882
3883 if (doc != NULL) {
3884 if (!dav_validate_root(doc, "checkin")) {
3885 /* This supplies additional information for the default msg. */
3887 "The request body, if present, must be a "
3888 "DAV:checkin element.");
3889 return HTTP_BAD_REQUEST;
3890 }
3891
3892 keep_checked_out = dav_find_child(doc->root, "keep-checked-out") != NULL;
3893 }
3894
3895 /* Ask repository module to resolve the resource */
3896 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
3897 &resource);
3898 if (err != NULL)
3899 return dav_handle_err(r, err, NULL);
3900
3901 /* check for any method preconditions */
3903 && err) {
3904 return dav_handle_err(r, err, NULL);
3905 }
3906
3907 if (!resource->exists) {
3908 /* Apache will supply a default error for this. */
3909 return HTTP_NOT_FOUND;
3910 }
3911
3912 /* Check the state of the resource: must be a file or collection,
3913 * must be versioned, and must be checked out.
3914 */
3915 if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
3917 "Cannot checkin this type of resource.");
3918 }
3919
3920 if (!resource->versioned) {
3922 "Cannot checkin unversioned resource.");
3923 }
3924
3925 if (!resource->working) {
3927 "The resource is not checked out.");
3928 }
3929
3930 /* ### do lock checks, once behavior is defined */
3931
3932 /* Do the checkin */
3933 if ((err = (*vsn_hooks->checkin)(resource, keep_checked_out, &new_version))
3934 != NULL) {
3937 "Could not CHECKIN resource %s.",
3938 ap_escape_html(r->pool, r->uri)),
3939 err);
3940 return dav_handle_err(r, err, NULL);
3941 }
3942
3943 return dav_created(r, new_version->uri, "Version", 0);
3944}
3945
3947{
3949 dav_resource *version = NULL;
3950 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
3951 apr_xml_doc *doc;
3952 apr_xml_elem *child;
3953 int is_label = 0;
3954 int depth;
3955 int result;
3957 const char *target;
3959 dav_error *err;
3961
3962 /* If no versioning provider, or UPDATE not supported,
3963 * decline the request */
3964 if (vsn_hooks == NULL || vsn_hooks->update == NULL)
3965 return DECLINED;
3966
3967 if ((depth = dav_get_depth(r, 0)) < 0) {
3968 /* dav_get_depth() supplies additional information for the
3969 * default message. */
3970 return HTTP_BAD_REQUEST;
3971 }
3972
3973 /* parse the request body */
3974 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
3975 return result;
3976 }
3977
3978 if (doc == NULL || !dav_validate_root(doc, "update")) {
3979 /* This supplies additional information for the default message. */
3981 "The request body does not contain "
3982 "an \"update\" element.");
3983 return HTTP_BAD_REQUEST;
3984 }
3985
3986 /* check for label-name or version element, but not both */
3987 if ((child = dav_find_child(doc->root, "label-name")) != NULL)
3988 is_label = 1;
3989 else if ((child = dav_find_child(doc->root, "version")) != NULL) {
3990 /* get the href element */
3991 if ((child = dav_find_child(child, "href")) == NULL) {
3993 "The version element does not contain "
3994 "an \"href\" element.");
3995 return HTTP_BAD_REQUEST;
3996 }
3997 }
3998 else {
4000 "The \"update\" element does not contain "
4001 "a \"label-name\" or \"version\" element.");
4002 return HTTP_BAD_REQUEST;
4003 }
4004
4005 /* a depth greater than zero is only allowed for a label */
4006 if (!is_label && depth != 0) {
4008 "Depth must be zero for UPDATE with a version");
4009 return HTTP_BAD_REQUEST;
4010 }
4011
4012 /* get the target value (a label or a version URI) */
4014 &target, &tsize);
4015 if (tsize == 0) {
4017 "A \"label-name\" or \"href\" element does not contain "
4018 "any content.");
4019 return HTTP_BAD_REQUEST;
4020 }
4021
4022 /* Ask repository module to resolve the resource */
4023 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
4024 &resource);
4025 if (err != NULL)
4026 return dav_handle_err(r, err, NULL);
4027
4028 /* check for any method preconditions */
4030 && err) {
4031 return dav_handle_err(r, err, NULL);
4032 }
4033
4034 if (!resource->exists) {
4035 /* Apache will supply a default error for this. */
4036 return HTTP_NOT_FOUND;
4037 }
4038
4039 /* ### need a general mechanism for reporting precondition violations
4040 * ### (should be returning XML document for 403/409 responses)
4041 */
4043 || !resource->versioned || resource->working) {
4045 "<DAV:must-be-checked-in-version-controlled-resource>");
4046 }
4047
4048 /* if target is a version, resolve the version resource */
4049 /* ### dav_lookup_uri only allows absolute URIs; is that OK? */
4050 if (!is_label) {
4051 lookup = dav_lookup_uri(target, r, 0 /* must_be_absolute */);
4052 if (lookup.rnew == NULL) {
4053 if (lookup.err.status == HTTP_BAD_REQUEST) {
4054 /* This supplies additional information for the default message. */
4056 "%s", lookup.err.desc);
4057 return HTTP_BAD_REQUEST;
4058 }
4059
4060 /* ### this assumes that dav_lookup_uri() only generates a status
4061 * ### that Apache can provide a status line for!! */
4062
4063 return dav_error_response(r, lookup.err.status, lookup.err.desc);
4064 }
4065 if (lookup.rnew->status != HTTP_OK) {
4066 /* ### how best to report this... */
4067 return dav_error_response(r, lookup.rnew->status,
4068 "Version URI had an error.");
4069 }
4070
4071 /* resolve version resource */
4072 err = dav_get_resource(lookup.rnew, 0 /* label_allowed */,
4073 0 /* use_checked_in */, &version);
4074 if (err != NULL)
4075 return dav_handle_err(r, err, NULL);
4076
4077 /* NULL out target, since we're using a version resource */
4078 target = NULL;
4079 }
4080
4081 /* do the UPDATE operation */
4082 err = (*vsn_hooks->update)(resource, version, target, depth, &multi_response);
4083
4084 if (err != NULL) {
4085 err = dav_push_error(r->pool, err->status, 0,
4087 "Could not UPDATE %s.",
4088 ap_escape_html(r->pool, r->uri)),
4089 err);
4091 }
4092
4093 /* set the Cache-Control header, per the spec */
4094 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
4095
4096 /* no body */
4098
4099 return DONE;
4100}
4101
4102/* context maintained during LABEL treewalk */
4104{
4105 /* input: */
4107
4108 /* original request */
4110
4111 /* label being manipulated */
4112 const char *label;
4113
4114 /* label operation */
4116#define DAV_LABEL_ADD 1
4117#define DAV_LABEL_SET 2
4118#define DAV_LABEL_REMOVE 3
4119
4120 /* version provider hooks */
4122
4124
4126{
4128 dav_error *err = NULL;
4129
4130 /* check for any method preconditions */
4132 && err) {
4133 /* precondition failed, dropping through */
4134 }
4135
4136 /* Check the state of the resource: must be a version or
4137 * non-checkedout version selector
4138 */
4139 /* ### need a general mechanism for reporting precondition violations
4140 * ### (should be returning XML document for 403/409 responses)
4141 */
4142 else if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION &&
4144 || !wres->resource->versioned)) {
4145 err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, 0,
4146 "<DAV:must-be-version-or-version-selector/>");
4147 }
4148 else if (wres->resource->working) {
4149 err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, 0,
4150 "<DAV:must-not-be-checked-out/>");
4151 }
4152 else {
4153 /* do the label operation */
4154 if (ctx->label_op == DAV_LABEL_REMOVE)
4155 err = (*ctx->vsn_hooks->remove_label)(wres->resource, ctx->label);
4156 else
4157 err = (*ctx->vsn_hooks->add_label)(wres->resource, ctx->label,
4158 ctx->label_op == DAV_LABEL_SET);
4159 }
4160
4161 if (err != NULL) {
4162 /* ### need utility routine to add response with description? */
4163 dav_add_response(wres, err->status, NULL);
4164 wres->response->desc = err->desc;
4165 }
4166
4167 return NULL;
4168}
4169
4171{
4173 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4174 apr_xml_doc *doc;
4175 apr_xml_elem *child;
4176 int depth;
4177 int result;
4179 dav_error *err;
4180 dav_label_walker_ctx ctx = { { 0 } };
4182
4183 /* If no versioning provider, or the provider doesn't support
4184 * labels, decline the request */
4185 if (vsn_hooks == NULL || vsn_hooks->add_label == NULL)
4186 return DECLINED;
4187
4188 /* parse the request body */
4189 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
4190 return result;
4191 }
4192
4193 /* Ask repository module to resolve the resource */
4194 err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
4195 &resource);
4196 if (err != NULL)
4197 return dav_handle_err(r, err, NULL);
4198
4199 /* check for any method preconditions */
4201 && err) {
4202 return dav_handle_err(r, err, NULL);
4203 }
4204
4205 if (!resource->exists) {
4206 /* Apache will supply a default error for this. */
4207 return HTTP_NOT_FOUND;
4208 }
4209
4210 if ((depth = dav_get_depth(r, 0)) < 0) {
4211 /* dav_get_depth() supplies additional information for the
4212 * default message. */
4213 return HTTP_BAD_REQUEST;
4214 }
4215
4216 if (doc == NULL || !dav_validate_root(doc, "label")) {
4217 /* This supplies additional information for the default message. */
4219 "The request body does not contain "
4220 "a \"label\" element.");
4221 return HTTP_BAD_REQUEST;
4222 }
4223
4224 /* check for add, set, or remove element */
4225 if ((child = dav_find_child(doc->root, "add")) != NULL) {
4226 ctx.label_op = DAV_LABEL_ADD;
4227 }
4228 else if ((child = dav_find_child(doc->root, "set")) != NULL) {
4229 ctx.label_op = DAV_LABEL_SET;
4230 }
4231 else if ((child = dav_find_child(doc->root, "remove")) != NULL) {
4232 ctx.label_op = DAV_LABEL_REMOVE;
4233 }
4234 else {
4236 "The \"label\" element does not contain "
4237 "an \"add\", \"set\", or \"remove\" element.");
4238 return HTTP_BAD_REQUEST;
4239 }
4240
4241 /* get the label string */
4242 if ((child = dav_find_child(child, "label-name")) == NULL) {
4244 "The label command element does not contain "
4245 "a \"label-name\" element.");
4246 return HTTP_BAD_REQUEST;
4247 }
4248
4250 &ctx.label, &tsize);
4251 if (tsize == 0) {
4253 "A \"label-name\" element does not contain "
4254 "a label name.");
4255 return HTTP_BAD_REQUEST;
4256 }
4257
4258 /* do the label operation walk */
4259 ctx.w.walk_type = DAV_WALKTYPE_NORMAL;
4260 ctx.w.func = dav_label_walker;
4261 ctx.w.walk_ctx = &ctx;
4262 ctx.w.pool = r->pool;
4263 ctx.w.root = resource;
4264 ctx.r = r;
4265 ctx.vsn_hooks = vsn_hooks;
4266
4267 err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
4268
4269 if (err != NULL) {
4270 /* some sort of error occurred which terminated the walk */
4271 err = dav_push_error(r->pool, err->status, 0,
4272 "The LABEL operation was terminated prematurely.",
4273 err);
4274 return dav_handle_err(r, err, multi_status);
4275 }
4276
4277 if (multi_status != NULL) {
4278 /* One or more resources had errors. If depth was zero, convert
4279 * response to simple error, else make sure there is an
4280 * overall error to pass to dav_handle_err()
4281 */
4282 if (depth == 0) {
4283 err = dav_new_error(r->pool, multi_status->status, 0, 0,
4284 multi_status->desc);
4286 }
4287 else {
4289 "Errors occurred during the LABEL operation.");
4290 }
4291
4292 return dav_handle_err(r, err, multi_status);
4293 }
4294
4295 /* set the Cache-Control header, per the spec */
4296 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
4297
4298 /* no body */
4300
4301 return DONE;
4302}
4303
4305 const dav_resource *resource,
4306 const apr_xml_doc *doc,
4307 ap_filter_t *output, dav_error **err)
4308{
4309 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4310
4311 if (vsn_hooks) {
4312 *err = (*vsn_hooks->deliver_report)(r, resource, doc,
4313 r->output_filters);
4314 return OK;
4315 }
4316
4317 return DECLINED;
4318}
4319
4321 request_rec *r,
4322 const dav_resource *resource,
4324 dav_error **err)
4325{
4326 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4327
4328 if (vsn_hooks) {
4329 const dav_report_elem *rp;
4330
4331 (*err) = (*vsn_hooks->avail_reports)(resource, &rp);
4332 while (rp && rp->name) {
4333
4335
4336 report->nmspace = rp->nmspace;
4337 report->name = rp->name;
4338
4339 rp++;
4340 }
4341 }
4342
4343}
4344
4346{
4348 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4349 apr_xml_doc *doc;
4350 dav_error *err = NULL;
4351
4352 int result;
4353 int label_allowed;
4354
4355 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
4356 return result;
4357 }
4358 if (doc == NULL) {
4359 /* This supplies additional information for the default msg. */
4361 "The request body must specify a report.");
4362 return HTTP_BAD_REQUEST;
4363 }
4364
4365 /* Ask repository module to resolve the resource.
4366 * First determine whether a Target-Selector header is allowed
4367 * for this report.
4368 */
4369 label_allowed = vsn_hooks ? (*vsn_hooks->report_label_header_allowed)(doc) : 0;
4370 err = dav_get_resource(r, label_allowed, 0 /* use_checked_in */,
4371 &resource);
4372 if (err != NULL) {
4373 return dav_handle_err(r, err, NULL);
4374 }
4375
4376 /* check for any method preconditions */
4378 && err) {
4379 return dav_handle_err(r, err, NULL);
4380 }
4381
4382 if (!resource->exists) {
4383 /* Apache will supply a default error for this. */
4384 return HTTP_NOT_FOUND;
4385 }
4386
4387 /* set up defaults for the report response */
4388 r->status = HTTP_OK;
4390 err = NULL;
4391
4392 /* run report hook */
4394 r->output_filters, &err);
4395 if (err != NULL) {
4396
4397 if (! r->sent_bodyct) {
4398 /* No data has been sent to client yet; throw normal error. */
4399 return dav_handle_err(r, err, NULL);
4400 }
4401
4402 /* If an error occurred during the report delivery, there's
4403 basically nothing we can do but abort the connection and
4404 log an error. This is one of the limitations of HTTP; it
4405 needs to "know" the entire status of the response before
4406 generating it, which is just impossible in these streamy
4407 response situations. */
4408 err = dav_push_error(r->pool, err->status, 0,
4409 "Provider encountered an error while streaming"
4410 " a REPORT response.", err);
4412 r->connection->aborted = 1;
4413
4414 return DONE;
4415 }
4416 switch (result) {
4417 case OK:
4418 return DONE;
4419 case DECLINED:
4420 /* No one handled the report */
4421 return HTTP_NOT_IMPLEMENTED;
4422 default:
4423 return DONE;
4424 }
4425
4426 return DONE;
4427}
4428
4430{
4432 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4433 dav_error *err;
4434 apr_xml_doc *doc;
4435 int result;
4436
4437 /* if no versioning provider, or the provider does not support workspaces,
4438 * decline the request
4439 */
4440 if (vsn_hooks == NULL || vsn_hooks->make_workspace == NULL)
4441 return DECLINED;
4442
4443 /* ask repository module to resolve the resource */
4444 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
4445 &resource);
4446 if (err != NULL)
4447 return dav_handle_err(r, err, NULL);
4448
4449 /* parse the request body (must be a mkworkspace element) */
4450 if ((result = ap_xml_parse_input(r, &doc)) != OK) {
4451 return result;
4452 }
4453
4454 /* check for any method preconditions */
4456 && err) {
4457 return dav_handle_err(r, err, NULL);
4458 }
4459
4460 if (doc == NULL
4461 || !dav_validate_root(doc, "mkworkspace")) {
4463 "The request body does not contain "
4464 "a \"mkworkspace\" element.");
4465 return HTTP_BAD_REQUEST;
4466 }
4467
4468 /* Check request preconditions */
4469
4470 /* ### need a general mechanism for reporting precondition violations
4471 * ### (should be returning XML document for 403/409 responses)
4472 */
4473
4474 /* resource must not already exist */
4475 if (resource->exists) {
4477 "<DAV:resource-must-be-null/>");
4478 return dav_handle_err(r, err, NULL);
4479 }
4480
4481 /* ### what about locking? */
4482
4483 /* attempt to create the workspace */
4484 if ((err = (*vsn_hooks->make_workspace)(resource, doc)) != NULL) {
4485 err = dav_push_error(r->pool, err->status, 0,
4487 "Could not create workspace %s.",
4488 ap_escape_html(r->pool, r->uri)),
4489 err);
4490 return dav_handle_err(r, err, NULL);
4491 }
4492
4493 /* set the Cache-Control header, per the spec */
4494 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
4495
4496 /* return an appropriate response (HTTP_CREATED) */
4497 return dav_created(r, resource->uri, "Workspace", 0 /*replaced*/);
4498}
4499
4501{
4503 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4504 dav_error *err;
4505 int result;
4506
4507 /* if no versioning provider, or the provider does not support activities,
4508 * decline the request
4509 */
4510 if (vsn_hooks == NULL || vsn_hooks->make_activity == NULL)
4511 return DECLINED;
4512
4513 /* ask repository module to resolve the resource */
4514 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
4515 &resource);
4516 if (err != NULL)
4517 return dav_handle_err(r, err, NULL);
4518
4519 /* check for any method preconditions */
4521 && err) {
4522 return dav_handle_err(r, err, NULL);
4523 }
4524
4525 /* MKACTIVITY does not have a defined request body. */
4526 if ((result = ap_discard_request_body(r)) != OK) {
4527 return result;
4528 }
4529
4530 /* Check request preconditions */
4531
4532 /* ### need a general mechanism for reporting precondition violations
4533 * ### (should be returning XML document for 403/409 responses)
4534 */
4535
4536 /* resource must not already exist */
4537 if (resource->exists) {
4539 "<DAV:resource-must-be-null/>");
4540 return dav_handle_err(r, err, NULL);
4541 }
4542
4543 /* the provider must say whether the resource can be created as
4544 an activity, i.e. whether the location is ok. */
4545 if (vsn_hooks->can_be_activity != NULL
4546 && !(*vsn_hooks->can_be_activity)(resource)) {
4548 "<DAV:activity-location-ok/>");
4549 return dav_handle_err(r, err, NULL);
4550 }
4551
4552 /* ### what about locking? */
4553
4554 /* attempt to create the activity */
4555 if ((err = (*vsn_hooks->make_activity)(resource)) != NULL) {
4556 err = dav_push_error(r->pool, err->status, 0,
4558 "Could not create activity %s.",
4559 ap_escape_html(r->pool, r->uri)),
4560 err);
4561 return dav_handle_err(r, err, NULL);
4562 }
4563
4564 /* set the Cache-Control header, per the spec */
4565 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
4566
4567 /* return an appropriate response (HTTP_CREATED) */
4568 return dav_created(r, resource->uri, "Activity", 0 /*replaced*/);
4569}
4570
4572{
4573 /* ### */
4575}
4576
4578{
4581 const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
4582 dav_error *err;
4583 int result;
4584 apr_xml_doc *doc;
4588 const char *source;
4589 int no_auto_merge;
4590 int no_checkout;
4592
4593 /* If no versioning provider, decline the request */
4594 if (vsn_hooks == NULL)
4595 return DECLINED;
4596
4597 if ((result = ap_xml_parse_input(r, &doc)) != OK)
4598 return result;
4599
4600 if (doc == NULL || !dav_validate_root(doc, "merge")) {
4601 /* This supplies additional information for the default msg. */
4603 "The request body must be present and must be a "
4604 "DAV:merge element.");
4605 return HTTP_BAD_REQUEST;
4606 }
4607
4608 if ((source_elem = dav_find_child(doc->root, "source")) == NULL) {
4609 /* This supplies additional information for the default msg. */
4611 "The DAV:merge element must contain a DAV:source "
4612 "element.");
4613 return HTTP_BAD_REQUEST;
4614 }
4615 if ((href_elem = dav_find_child(source_elem, "href")) == NULL) {
4616 /* This supplies additional information for the default msg. */
4618 "The DAV:source element must contain a DAV:href "
4619 "element.");
4620 return HTTP_BAD_REQUEST;
4621 }
4622 source = dav_xml_get_cdata(href_elem, r->pool, 1 /* strip_white */);
4623
4624 /* get a subrequest for the source, so that we can get a dav_resource
4625 for that source. */
4626 lookup = dav_lookup_uri(source, r, 0 /* must_be_absolute */);
4627 if (lookup.rnew == NULL) {
4628 if (lookup.err.status == HTTP_BAD_REQUEST) {
4629 /* This supplies additional information for the default message. */
4631 "%s", lookup.err.desc);
4632 return HTTP_BAD_REQUEST;
4633 }
4634
4635 /* ### this assumes that dav_lookup_uri() only generates a status
4636 * ### that Apache can provide a status line for!! */
4637
4638 return dav_error_response(r, lookup.err.status, lookup.err.desc);
4639 }
4640 if (lookup.rnew->status != HTTP_OK) {
4641 /* ### how best to report this... */
4642 return dav_error_response(r, lookup.rnew->status,
4643 "Merge source URI had an error.");
4644 }
4645 err = dav_get_resource(lookup.rnew, 0 /* label_allowed */,
4646 0 /* use_checked_in */, &source_resource);
4647 if (err != NULL)
4648 return dav_handle_err(r, err, NULL);
4649
4650 /* check for any method preconditions */
4652 && err) {
4653 return dav_handle_err(r, err, NULL);
4654 }
4655
4656 no_auto_merge = dav_find_child(doc->root, "no-auto-merge") != NULL;
4657 no_checkout = dav_find_child(doc->root, "no-checkout") != NULL;
4658
4659 prop_elem = dav_find_child(doc->root, "prop");
4660
4661 /* ### check RFC. I believe the DAV:merge element may contain any
4662 ### element also allowed within DAV:checkout. need to extract them
4663 ### here, and pass them along.
4664 ### if so, then refactor the CHECKOUT method handling so we can reuse
4665 ### the code. maybe create a structure to hold CHECKOUT parameters
4666 ### which can be passed to the checkout() and merge() hooks. */
4667
4668 /* Ask repository module to resolve the resource */
4669 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
4670 &resource);
4671 if (err != NULL)
4672 return dav_handle_err(r, err, NULL);
4673
4674 /* check for any method preconditions */
4676 && err) {
4677 return dav_handle_err(r, err, NULL);
4678 }
4679
4680 if (!resource->exists) {
4681 /* Apache will supply a default error for this. */
4682 return HTTP_NOT_FOUND;
4683 }
4684
4685 /* ### check the source and target resources flags/types */
4686
4687 /* ### do lock checks, once behavior is defined */
4688
4689 /* set the Cache-Control header, per the spec */
4690 /* ### correct? */
4691 apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
4692
4693 /* Initialize these values for a standard MERGE response. If the MERGE
4694 is going to do something different (i.e. an error), then it must
4695 return a dav_error, and we'll reset these values properly. */
4696 r->status = HTTP_OK;
4697 ap_set_content_type(r, "text/xml");
4698
4699 /* ### should we do any preliminary response generation? probably not,
4700 ### because we may have an error, thus demanding something else in
4701 ### the response body. */
4702
4703 /* Do the merge, including any response generation. */
4704 if ((err = (*vsn_hooks->merge)(resource, source_resource,
4706 prop_elem,
4707 r->output_filters)) != NULL) {
4708 /* ### is err->status the right error here? */
4709 err = dav_push_error(r->pool, err->status, 0,
4711 "Could not MERGE resource \"%s\" "
4712 "into \"%s\".",
4714 ap_escape_html(r->pool, r->uri)),
4715 err);
4716 return dav_handle_err(r, err, NULL);
4717 }
4718
4719 /* the response was fully generated by the merge() hook. */
4720 /* ### urk. does this prevent logging? need to check... */
4721 return DONE;
4722}
4723
4725{
4730 const char *dest;
4731 dav_error *err;
4732 dav_error *err2;
4735 int overwrite;
4736
4737 /* If no bindings provider, decline the request */
4738 if (binding_hooks == NULL)
4739 return DECLINED;
4740
4741 /* Ask repository module to resolve the resource */
4742 err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
4743 &resource);
4744 if (err != NULL)
4745 return dav_handle_err(r, err, NULL);
4746
4747 /* check for any method preconditions */
4749 && err) {
4750 return dav_handle_err(r, err, NULL);
4751 }
4752
4753 if (!resource->exists) {
4754 /* Apache will supply a default error for this. */
4755 return HTTP_NOT_FOUND;
4756 }
4757
4758 /* get the destination URI */
4759 dest = apr_table_get(r->headers_in, "Destination");
4760 if (dest == NULL) {
4761 /* This supplies additional information for the default message. */
4763 "The request is missing a Destination header.");
4764 return HTTP_BAD_REQUEST;
4765 }
4766
4767 lookup = dav_lookup_uri(dest, r, 0 /* must_be_absolute */);
4768 if (lookup.rnew == NULL) {
4769 if (lookup.err.status == HTTP_BAD_REQUEST) {
4770 /* This supplies additional information for the default message. */
4772 "%s", lookup.err.desc);
4773 return HTTP_BAD_REQUEST;
4774 }
4775 else if (lookup.err.status == HTTP_BAD_GATEWAY) {
4776 /* ### Bindings protocol draft 02 says to return 507
4777 * ### (Cross Server Binding Forbidden); Apache already defines 507
4778 * ### as HTTP_INSUFFICIENT_STORAGE. So, for now, we'll return
4779 * ### HTTP_FORBIDDEN
4780 */
4782 "Cross server bindings are not "
4783 "allowed by this server.");
4784 }
4785
4786 /* ### this assumes that dav_lookup_uri() only generates a status
4787 * ### that Apache can provide a status line for!! */
4788
4789 return dav_error_response(r, lookup.err.status, lookup.err.desc);
4790 }
4791 if (lookup.rnew->status != HTTP_OK) {
4792 /* ### how best to report this... */
4793 return dav_error_response(r, lookup.rnew->status,
4794 "Destination URI had an error.");
4795 }
4796
4797 /* resolve binding resource */
4798 err = dav_get_resource(lookup.rnew, 0 /* label_allowed */,
4799 0 /* use_checked_in */, &binding);
4800 if (err != NULL)
4801 return dav_handle_err(r, err, NULL);
4802
4803 /* check for any method preconditions */
4805 && err) {
4806 return dav_handle_err(r, err, NULL);
4807 }
4808
4809 /* are the two resources handled by the same repository? */
4810 if (resource->hooks != binding->hooks) {
4811 /* ### this message exposes some backend config, but screw it... */
4813 "Destination URI is handled by a "
4814 "different repository than the source URI. "
4815 "BIND between repositories is not possible.");
4816 }
4817
4818 /* get and parse the overwrite header value */
4819 if ((overwrite = dav_get_overwrite(r)) < 0) {
4820 /* dav_get_overwrite() supplies additional information for the
4821 * default message. */
4822 return HTTP_BAD_REQUEST;
4823 }
4824
4825 /* quick failure test: if dest exists and overwrite is false. */
4826 if (binding->exists && !overwrite) {
4828 "Destination is not empty and "
4829 "Overwrite is not \"T\"");
4830 }
4831
4832 /* are the source and destination the same? */
4833 if ((*resource->hooks->is_same_resource)(resource, binding)) {
4835 "Source and Destination URIs are the same.");
4836 }
4837
4838 /*
4839 * Check If-Headers and existing locks for destination. Note that we
4840 * use depth==infinity since the target (hierarchy) will be deleted
4841 * before the move/copy is completed.
4842 *
4843 * Note that we are overwriting the target, which implies a DELETE, so
4844 * we are subject to the error/response rules as a DELETE. Namely, we
4845 * will return a 424 error if any of the validations fail.
4846 * (see dav_method_delete() for more information)
4847 */
4851 | DAV_VALIDATE_USE_424, NULL)) != NULL) {
4852 err = dav_push_error(r->pool, err->status, 0,
4854 "Could not BIND %s due to a "
4855 "failed precondition on the "
4856 "destination (e.g. locks).",
4857 ap_escape_html(r->pool, r->uri)),
4858 err);
4860 }
4861
4862 /* guard against creating circular bindings */
4863 if (resource->collection
4864 && (*resource->hooks->is_parent_resource)(resource, binding)) {
4866 "Source collection contains the Destination.");
4867 }
4868 if (resource->collection
4869 && (*resource->hooks->is_parent_resource)(binding, resource)) {
4870 /* The destination must exist (since it contains the source), and
4871 * a condition above implies Overwrite==T. Obviously, we cannot
4872 * delete the Destination before the BIND, as that would
4873 * delete the Source.
4874 */
4875
4877 "Destination collection contains the Source and "
4878 "Overwrite has been specified.");
4879 }
4880
4881 /* prepare the destination collection for modification */
4882 if ((err = dav_auto_checkout(r, binding, 1 /* parent_only */,
4883 &av_info)) != NULL) {
4884 /* could not make destination writable */
4885 return dav_handle_err(r, err, NULL);
4886 }
4887
4888 /* If target exists, remove it first (we know Ovewrite must be TRUE).
4889 * Then try to bind to the resource.
4890 */
4891 if (binding->exists)
4892 err = (*resource->hooks->remove_resource)(binding, &multi_response);
4893
4894 if (err == NULL) {
4895 err = (*binding_hooks->bind_resource)(resource, binding);
4896 }
4897
4898 /* restore parent collection states */
4900 err != NULL /* undo if error */,
4901 0 /* unlock */, &av_info);
4902
4903 /* check for error from remove/bind operations */
4904 if (err != NULL) {
4905 err = dav_push_error(r->pool, err->status, 0,
4907 "Could not BIND %s.",
4908 ap_escape_html(r->pool, r->uri)),
4909 err);
4911 }
4912
4913 /* check for errors from reverting writability */
4914 if (err2 != NULL) {
4915 /* just log a warning */
4916 err = dav_push_error(r->pool, err2->status, 0,
4917 "The BIND was successful, but there was a "
4918 "problem automatically checking in the "
4919 "source parent collection.",
4920 err2);
4922 }
4923
4924 /* return an appropriate response (HTTP_CREATED) */
4925 /* ### spec doesn't say what happens when destination was replaced */
4926 return dav_created(r, lookup.rnew->unparsed_uri, "Binding", 0);
4927}
4928
4929
4930/*
4931 * Response handler for DAV resources
4932 */
4934{
4935 if (strcmp(r->handler, DAV_HANDLER_NAME) != 0)
4936 return DECLINED;
4937
4938 /* Reject requests with an unescaped hash character, as these may
4939 * be more destructive than the user intended. */
4940 if (r->parsed_uri.fragment != NULL) {
4942 "buggy client used un-escaped hash in Request-URI");
4944 "The request was invalid: the URI included "
4945 "an un-escaped hash character");
4946 }
4947
4948 /* ### do we need to do anything with r->proxyreq ?? */
4949
4950 /*
4951 * ### anything else to do here? could another module and/or
4952 * ### config option "take over" the handler here? i.e. how do
4953 * ### we lock down this hierarchy so that we are the ultimate
4954 * ### arbiter? (or do we simply depend on the administrator
4955 * ### to avoid conflicting configurations?)
4956 */
4957
4958 /*
4959 * Set up the methods mask, since that's one of the reasons this handler
4960 * gets called, and lower-level things may need the info.
4961 *
4962 * First, set the mask to the methods we handle directly. Since by
4963 * definition we own our managed space, we unconditionally set
4964 * the r->allowed field rather than ORing our values with anything
4965 * any other module may have put in there.
4966 *
4967 * These are the HTTP-defined methods that we handle directly.
4968 */
4969 r->allowed = 0
4970 | (AP_METHOD_BIT << M_GET)
4971 | (AP_METHOD_BIT << M_PUT)
4972 | (AP_METHOD_BIT << M_DELETE)
4974 | (AP_METHOD_BIT << M_INVALID);
4975
4976 /*
4977 * These are the DAV methods we handle.
4978 */
4979 r->allowed |= 0
4980 | (AP_METHOD_BIT << M_COPY)
4981 | (AP_METHOD_BIT << M_LOCK)
4982 | (AP_METHOD_BIT << M_UNLOCK)
4983 | (AP_METHOD_BIT << M_MKCOL)
4984 | (AP_METHOD_BIT << M_MOVE)
4987
4988 /*
4989 * These are methods that we don't handle directly, but let the
4990 * server's default handler do for us as our agent.
4991 */
4992 r->allowed |= 0
4993 | (AP_METHOD_BIT << M_POST);
4994
4995 /* ### hrm. if we return HTTP_METHOD_NOT_ALLOWED, then an Allow header
4996 * ### is sent; it will need the other allowed states; since the default
4997 * ### handler is not called on error, then it doesn't add the other
4998 * ### allowed states, so we must
4999 */
5000
5001 /* ### we might need to refine this for just where we return the error.
5002 * ### also, there is the issue with other methods (see ISSUES)
5003 */
5004
5005 /* dispatch the appropriate method handler */
5006 if (r->method_number == M_GET) {
5007 return dav_method_get(r);
5008 }
5009
5010 if (r->method_number == M_PUT) {
5011 return dav_method_put(r);
5012 }
5013
5014 if (r->method_number == M_POST) {
5015 return dav_method_post(r);
5016 }
5017
5018 if (r->method_number == M_DELETE) {
5019 return dav_method_delete(r);
5020 }
5021
5022 if (r->method_number == M_OPTIONS) {
5023 return dav_method_options(r);
5024 }
5025
5026 if (r->method_number == M_PROPFIND) {
5027 return dav_method_propfind(r);
5028 }
5029
5030 if (r->method_number == M_PROPPATCH) {
5031 return dav_method_proppatch(r);
5032 }
5033
5034 if (r->method_number == M_MKCOL) {
5035 return dav_method_mkcol(r);
5036 }
5037
5038 if (r->method_number == M_COPY) {
5040 }
5041
5042 if (r->method_number == M_MOVE) {
5044 }
5045
5046 if (r->method_number == M_LOCK) {
5047 return dav_method_lock(r);
5048 }
5049
5050 if (r->method_number == M_UNLOCK) {
5051 return dav_method_unlock(r);
5052 }
5053
5055 return dav_method_vsn_control(r);
5056 }
5057
5058 if (r->method_number == M_CHECKOUT) {
5059 return dav_method_checkout(r);
5060 }
5061
5062 if (r->method_number == M_UNCHECKOUT) {
5063 return dav_method_uncheckout(r);
5064 }
5065
5066 if (r->method_number == M_CHECKIN) {
5067 return dav_method_checkin(r);
5068 }
5069
5070 if (r->method_number == M_UPDATE) {
5071 return dav_method_update(r);
5072 }
5073
5074 if (r->method_number == M_LABEL) {
5075 return dav_method_label(r);
5076 }
5077
5078 if (r->method_number == M_REPORT) {
5079 return dav_method_report(r);
5080 }
5081
5082 if (r->method_number == M_MKWORKSPACE) {
5084 }
5085
5086 if (r->method_number == M_MKACTIVITY) {
5088 }
5089
5092 }
5093
5094 if (r->method_number == M_MERGE) {
5095 return dav_method_merge(r);
5096 }
5097
5098 /* BIND method */
5100 return dav_method_bind(r);
5101 }
5102
5103 /* DASL method */
5105 return dav_method_search(r);
5106 }
5107
5108 /* ### add'l methods for Advanced Collections, ACLs */
5109
5110 return DECLINED;
5111}
5112
5114{
5115 dav_dir_conf *conf;
5116
5117 /* quickly ignore any HTTP/0.9 requests which aren't subreqs. */
5118 if (r->assbackwards && !r->main) {
5119 return DECLINED;
5120 }
5121
5123 &dav_module);
5124
5125 /* if DAV is not enabled, then we've got nothing to do */
5126 if (conf->provider == NULL) {
5127 return DECLINED;
5128 }
5129
5130 /* We are going to handle almost every request. In certain cases,
5131 the provider maps to the filesystem (thus, handle_get is
5132 FALSE), and core Apache will handle it. a For that case, we
5133 just return right away. */
5134 if (r->method_number == M_GET) {
5135 /*
5136 * ### need some work to pull Content-Type and Content-Language
5137 * ### from the property database.
5138 */
5139
5140 /*
5141 * If the repository hasn't indicated that it will handle the
5142 * GET method, then just punt.
5143 *
5144 * ### this isn't quite right... taking over the response can break
5145 * ### things like mod_negotiation. need to look into this some more.
5146 */
5147 if (!conf->provider->repos->handle_get) {
5148 return DECLINED;
5149 }
5150 }
5151
5152 /* ### this is wrong. We should only be setting the r->handler for the
5153 * requests that mod_dav knows about. If we set the handler for M_POST
5154 * requests, then CGI scripts that use POST will return the source for the
5155 * script. However, mod_dav DOES handle POST, so something else needs
5156 * to be fixed.
5157 */
5158 if (r->method_number != M_POST) {
5159
5160 /* We are going to be handling the response for this resource. */
5162 return OK;
5163 }
5164
5165 return DECLINED;
5166}
5167
5185
5186/*---------------------------------------------------------------------------
5187 *
5188 * Configuration info for the module
5189 */
5190
5191static const command_rec dav_cmds[] =
5192{
5193 /* per directory/location */
5195 "specify the DAV provider for a directory or location"),
5196
5197 /* per directory/location */
5199 "specify the DAV repository base URL"),
5200
5201 /* per directory/location, or per server */
5202 AP_INIT_TAKE1("DAVMinTimeout", dav_cmd_davmintimeout, NULL,
5204 "specify minimum allowed timeout"),
5205
5206 /* per directory/location, or per server */
5207 AP_INIT_FLAG("DAVDepthInfinity", dav_cmd_davdepthinfinity, NULL,
5209 "allow Depth infinity PROPFIND requests"),
5210
5211 /* per directory/location, or per server */
5212 AP_INIT_FLAG("DAVLockDiscovery", dav_cmd_davlockdiscovery, NULL,
5214 "allow lock discovery by PROPFIND requests"),
5215
5216 { NULL }
5217};
5218
5219module DAV_DECLARE_DATA dav_module =
5220{
5221 STANDARD20_MODULE_STUFF,
5222 dav_create_dir_config, /* dir config creater */
5223 dav_merge_dir_config, /* dir merger --- default is to override */
5224 dav_create_server_config, /* server config */
5225 dav_merge_server_config, /* merge server config */
5226 dav_cmds, /* command table */
5227 register_hooks, /* register hooks */
5228};
5229
5234 APR_HOOK_LINK(deliver_report)
5238
5242
5245 const char *ns_uri, const char *name,
5248
5253
5254APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, deliver_report,
5255 (request_rec *r,
5257 const apr_xml_doc *doc,
5258 ap_filter_t *output, dav_error **err),
5259 (r, resource, doc, output, err), DECLINED)
5260
5265
5267 (request_rec *r,
5269 const apr_xml_doc *doc,
5270 dav_error **err),
5271 (r, src, dest, doc, err), DECLINED)
Apache Provider API.
const char apr_size_t len
Definition ap_regex.h:187
static int report(abts_suite *suite)
Definition abts.c:212
APR general purpose library routines.
apr_size_t const unsigned char unsigned int unsigned int d
Definition apr_siphash.h:72
APR Strings library.
APR Standard Headers Support.
static apr_size_t text_size(const apr_text *t)
Definition apr_xml.c:654
static sem_id lock
Definition threadpriv.c:21
static void reverse(const char **argv, int start, int len)
Definition getopt.c:141
#define AP_INIT_TAKE1(directive, func, mconfig, where, help)
#define APLOG_USE_MODULE(foo)
#define ap_get_module_config(v, m)
void ap_hook_post_config(ap_HOOK_post_config_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:105
#define AP_INIT_FLAG(directive, func, mconfig, where, help)
ap_conf_vector_t * base
void ap_hook_handler(ap_HOOK_handler_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition config.c:170
request_rec * r
#define AP_FILTER_ERROR
Definition httpd.h:473
#define DECLINED
Definition httpd.h:457
#define OK
Definition httpd.h:456
#define DONE
Definition httpd.h:458
#define ap_fputs(f, bb, str)
#define ap_fputc(f, bb, c)
apr_status_t ap_pass_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket)
apr_status_t ap_fputstrs(ap_filter_t *f, apr_bucket_brigade *bb,...)
apr_status_t ap_fprintf(ap_filter_t *f, apr_bucket_brigade *bb, const char *fmt,...) __attribute__((format(printf
apr_status_t ap_get_brigade(ap_filter_t *filter, apr_bucket_brigade *bucket, ap_input_mode_t mode, apr_read_type_e block, apr_off_t readbytes)
#define ap_get_core_module_config(v)
Definition http_core.h:383
char * ap_construct_url(apr_pool_t *p, const char *uri, request_rec *r)
Definition core.c:1246
#define APLOGNO(n)
Definition http_log.h:117
#define ap_log_rerror
Definition http_log.h:454
#define APLOG_ERR
Definition http_log.h:67
#define ap_log_error
Definition http_log.h:370
#define APLOG_MARK
Definition http_log.h:283
#define APLOG_WARNING
Definition http_log.h:68
int ap_rvputs(request_rec *r,...)
Definition protocol.c:2220
int ap_map_http_request_error(apr_status_t rv, int status)
int ap_meets_conditions(request_rec *r)
int ap_method_register(apr_pool_t *p, const char *methname)
int ap_rprintf(request_rec *r, const char *fmt,...) __attribute__((format(printf
void ap_set_content_length(request_rec *r, apr_off_t length)
Definition protocol.c:160
static APR_INLINE int ap_rputs(const char *str, request_rec *r)
void ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
void ap_set_content_type(request_rec *r, const char *ct)
const char * ap_get_status_line(int status)
int ap_discard_request_body(request_rec *r)
apr_array_header_t * ap_list_provider_names(apr_pool_t *pool, const char *provider_group, const char *provider_version)
Definition provider.c:127
void ap_hook_fixups(ap_HOOK_fixups_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition request.c:87
void * dummy
Definition http_vhost.h:62
void const char * arg
Definition http_vhost.h:63
int ap_xml_parse_input(request_rec *r, apr_xml_doc **pdoc)
Definition util_xml.c:36
#define APR_BUCKET_IS_METADATA(e)
#define APR_BRIGADE_INSERT_TAIL(b, e)
#define APR_BUCKET_NEXT(e)
#define APR_BRIGADE_SENTINEL(b)
#define APR_BUCKET_IS_EOS(e)
apr_brigade_flush void * ctx
#define APR_BRIGADE_FIRST(b)
#define apr_bucket_read(e, str, len, block)
@ APR_BLOCK_READ
Definition apr_buckets.h:58
apr_pool_t const char apr_dbd_t const char ** error
Definition apr_dbd.h:143
apr_pool_t apr_dbd_t const char const char * label
Definition apr_dbd.h:397
apr_dbd_transaction_t int mode
Definition apr_dbd.h:261
const char * src
Definition apr_encode.h:167
const char apr_ssize_t int flags
Definition apr_encode.h:168
#define APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(ns, link, ret, name, args_decl, args_use, decline)
Definition apr_hooks.h:267
#define APR_IMPLEMENT_EXTERNAL_HOOK_VOID(ns, link, name, args_decl, args_use)
Definition apr_hooks.h:179
#define APR_HOOK_LINK(name)
Definition apr_hooks.h:139
#define APR_HOOK_LAST
Definition apr_hooks.h:305
#define APR_HOOK_STRUCT(members)
Definition apr_hooks.h:135
#define APR_HOOK_MIDDLE
Definition apr_hooks.h:303
apr_redis_t * rc
Definition apr_redis.h:173
void ** resource
const char * uri
Definition apr_uri.h:159
#define APR_XML_X2T_INNER
Definition apr_xml.h:293
apr_text_header * hdr
Definition apr_xml.h:77
#define APR_XML_NS_DAV_ID
Definition apr_xml.h:133
#define APR_XML_GET_URI_ITEM(ary, i)
Definition apr_xml.h:339
const apr_xml_elem int apr_array_header_t * namespaces
Definition apr_xml.h:287
#define ACCESS_CONF
#define RSRC_CONF
#define HTTP_OK
Definition httpd.h:490
#define HTTP_BAD_REQUEST
Definition httpd.h:508
#define ap_is_HTTP_VALID_RESPONSE(x)
Definition httpd.h:560
#define HTTP_MULTI_STATUS
Definition httpd.h:497
#define HTTP_PRECONDITION_FAILED
Definition httpd.h:520
#define HTTP_UNSUPPORTED_MEDIA_TYPE
Definition httpd.h:523
#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_METHOD_NOT_ALLOWED
Definition httpd.h:513
#define HTTP_FORBIDDEN
Definition httpd.h:511
#define HTTP_NOT_FOUND
Definition httpd.h:512
#define HTTP_CREATED
Definition httpd.h:491
#define HTTP_UNAUTHORIZED
Definition httpd.h:509
#define HTTP_NO_CONTENT
Definition httpd.h:494
#define HTTP_NOT_IMPLEMENTED
Definition httpd.h:536
#define HTTP_CONFLICT
Definition httpd.h:517
#define DAV_GET_HOOKS_LOCKS(r)
Definition mod_dav.h:808
int dav_handle_err(request_rec *r, dav_error *err, dav_response *response)
Definition mod_dav.c:637
#define DAV_RESOURCE_LOCK_NULL
Definition mod_dav.h:1671
void dav_hook_find_liveprop(dav_HOOK_find_liveprop_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_dav.c:5247
int dav_get_depth(request_rec *r, int def_depth)
Definition mod_dav.c:704
#define DAV_VALIDATE_RESOURCE
Definition mod_dav.h:1455
#define DAV_XML_HEADER
Definition mod_dav.h:48
void dav_core_insert_all_liveprops(request_rec *r, const dav_resource *resource, dav_prop_insert what, apr_text_header *phdr)
const dav_hooks_locks * dav_get_lock_hooks(request_rec *r)
Definition mod_dav.c:228
void dav_hook_gather_reports(dav_HOOK_gather_reports_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_dav.c:5264
#define DAV_GET_HOOKS_BINDING(r)
Definition mod_dav.h:810
#define DAV_DO_COPY
Definition mod_dav.h:59
#define DAV_RESPONSE_BODY_3
Definition mod_dav.h:55
apr_text * dav_failed_proppatch(apr_pool_t *p, apr_array_header_t *prop_ctx)
Definition mod_dav.c:2284
void dav_run_gather_reports(request_rec *r, const dav_resource *resource, apr_array_header_t *reports, dav_error **err)
Definition mod_dav.c:5264
dav_get_props_result dav_get_allprops(dav_propdb *db, dav_prop_insert what)
Definition props.c:595
dav_lookup_result dav_lookup_uri(const char *uri, request_rec *r, int must_be_absolute)
Definition util.c:190
#define DAV_PROPDB_RO
Definition mod_dav.h:1685
void dav_core_register_uris(apr_pool_t *p)
const char * dav_lock_get_activelock(request_rec *r, dav_lock *locks, dav_buffer *pbuf)
Definition util_lock.c:42
#define DAV_VALIDATE_NO_MODIFY
Definition mod_dav.h:1460
int dav_run_method_precondition(request_rec *r, dav_resource *src, const dav_resource *dest, const apr_xml_doc *doc, dav_error **err)
Definition mod_dav.c:5271
#define DAV_RESPONSE_BODY_2
Definition mod_dav.h:54
#define DAV_RESOURCE_NULL
Definition mod_dav.h:1672
void dav_prop_validate(dav_prop_ctx *ctx)
Definition props.c:962
const dav_hooks_binding * dav_get_binding_hooks(request_rec *r)
Definition mod_dav.c:243
dav_error * dav_lock_parse_lockinfo(request_rec *r, const dav_resource *resource, dav_lockdb *lockdb, const apr_xml_doc *doc, dav_lock **lock_request)
Definition util_lock.c:170
int dav_unlock(request_rec *r, const dav_resource *resource, const dav_locktoken *locktoken)
Definition util_lock.c:497
#define DAV_READ_BLOCKSIZE
Definition mod_dav.h:51
dav_prop_insert
Definition mod_dav.h:516
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
const dav_provider * dav_lookup_provider(const char *name)
Definition providers.c:30
void dav_hook_deliver_report(dav_HOOK_deliver_report_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_dav.c:5259
void dav_add_vary_header(request_rec *in_req, request_rec *out_req, const dav_resource *resource)
Definition util.c:1874
#define DAV_DO_MOVE
Definition mod_dav.h:60
#define DEBUG_CR
Definition mod_dav.h:65
void dav_close_propdb(dav_propdb *db)
Definition props.c:583
void dav_get_liveprop_supported(dav_propdb *propdb, const char *ns_uri, const char *propname, apr_text_header *body)
Definition props.c:929
dav_error * dav_add_lock(request_rec *r, const dav_resource *resource, dav_lockdb *lockdb, dav_lock *request, dav_response **response)
Definition util_lock.c:296
apr_text * dav_success_proppatch(apr_pool_t *p, apr_array_header_t *prop_ctx)
Definition mod_dav.c:2345
#define DAV_INFINITY
Definition mod_dav.h:79
dav_get_props_result dav_get_props(dav_propdb *db, apr_xml_doc *doc)
Definition props.c:723
const char * dav_get_provider_name(request_rec *r)
Definition mod_dav.c:209
#define DAV_RESPONSE_BODY_1
Definition mod_dav.h:53
#define DAV_GET_HOOKS_SEARCH(r)
Definition mod_dav.h:811
void dav_prop_exec(dav_prop_ctx *ctx)
Definition props.c:1053
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
dav_error * dav_popen_propdb(apr_pool_t *p, request_rec *r, dav_lockdb *lockdb, const dav_resource *resource, int flags, apr_array_header_t *ns_xlate, dav_propdb **propdb)
Definition props.c:540
#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
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_OPTIONS_EXTENSION_GROUP
Definition mod_dav.h:2620
#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_status_t dav_finish_multistatus(request_rec *r, apr_bucket_brigade *bb)
Definition mod_dav.c:566
void dav_prop_commit(dav_prop_ctx *ctx)
Definition props.c:1123
const dav_provider * dav_get_provider(request_rec *r)
Definition mod_dav.c:215
apr_xml_elem * dav_find_child(const apr_xml_elem *elem, const char *tagname)
Definition util.c:359
void dav_hook_insert_all_liveprops(dav_HOOK_insert_all_liveprops_t *pf, const char *const *aszPre, const char *const *aszSucc, int nOrder)
Definition mod_dav.c:5252
dav_stream_mode
Definition mod_dav.h:1899
void dav_send_one_response(dav_response *response, apr_bucket_brigade *bb, request_rec *r, apr_pool_t *pool)
Definition mod_dav.c:479
int dav_core_find_liveprop(const dav_resource *resource, const char *ns_uri, const char *name, const dav_hooks_liveprop **hooks)
dav_error * dav_open_lockdb(request_rec *r, int ro, dav_lockdb **lockdb)
Definition mod_dav.c:826
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_RESPONSE_BODY_5
Definition mod_dav.h:57
dav_error * dav_notify_created(request_rec *r, dav_lockdb *lockdb, const dav_resource *resource, int resource_state, int depth)
Definition util_lock.c:745
#define DAV_VALIDATE_PARENT
Definition mod_dav.h:1456
#define DAV_PROPDB_DISABLE_LOCKDISCOVERY
Definition mod_dav.h:1686
#define DAV_DECLARE(type)
Definition mod_dav.h:85
#define DAV_XML_CONTENT_TYPE
Definition mod_dav.h:49
#define DAV_TIMEOUT_INFINITE
Definition mod_dav.h:1314
#define DAV_RESPONSE_BODY_4
Definition mod_dav.h:56
#define DAV_GET_HOOKS_VSN(r)
Definition mod_dav.h:809
const dav_options_provider * dav_get_options_providers(const char *name)
Definition providers.c:42
void dav_begin_multistatus(apr_bucket_brigade *bb, request_rec *r, int status, apr_array_header_t *namespaces)
Definition mod_dav.c:541
void dav_prop_rollback(dav_prop_ctx *ctx)
Definition props.c:1140
dav_error * dav_open_propdb(request_rec *r, dav_lockdb *lockdb, const dav_resource *resource, int flags, apr_array_header_t *ns_xlate, dav_propdb **propdb)
Definition props.c:530
const dav_hooks_search * dav_get_search_hooks(request_rec *r)
Definition mod_dav.c:248
const dav_hooks_vsn * dav_get_vsn_hooks(request_rec *r)
Definition mod_dav.c:238
void dav_send_multistatus(request_rec *r, int status, dav_response *first, apr_array_header_t *namespaces)
Definition mod_dav.c:581
dav_error * dav_get_resource(request_rec *r, int label_allowed, int use_checked_in, dav_resource **res_p)
Definition mod_dav.c:761
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
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
void dav_close_lockdb(dav_lockdb *lockdb)
Definition mod_dav.c:841
const dav_hooks_propdb * dav_get_propdb_hooks(request_rec *r)
Definition mod_dav.c:233
#define DAV_PROPDB_NONE
Definition mod_dav.h:1684
int dav_run_deliver_report(request_rec *r, const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output, dav_error **err)
Definition mod_dav.c:5259
#define DAV_PROP_CTX_HAS_ERR(dpc)
Definition mod_dav.h:1786
void dav_run_gather_propsets(apr_array_header_t *uris)
Definition mod_dav.c:5241
@ DAV_PROP_INSERT_NAME
Definition mod_dav.h:525
@ DAV_PROP_INSERT_VALUE
Definition mod_dav.h:527
@ DAV_PROP_INSERT_SUPPORTED
Definition mod_dav.h:529
@ DAV_MODE_WRITE_SEEKABLE
Definition mod_dav.h:1901
@ DAV_MODE_WRITE_TRUNC
Definition mod_dav.h:1900
@ DAV_RESOURCE_TYPE_VERSION
Definition mod_dav.h:303
@ DAV_RESOURCE_TYPE_REGULAR
Definition mod_dav.h:299
@ DAV_RESOURCE_TYPE_WORKING
Definition mod_dav.h:307
#define M_PUT
Definition httpd.h:593
#define M_LOCK
Definition httpd.h:605
#define M_MKACTIVITY
Definition httpd.h:615
#define M_OPTIONS
Definition httpd.h:597
#define AP_METHOD_BIT
Definition httpd.h:629
#define M_VERSION_CONTROL
Definition httpd.h:607
#define M_CHECKOUT
Definition httpd.h:608
#define M_LABEL
Definition httpd.h:612
#define M_PROPFIND
Definition httpd.h:600
#define M_MOVE
Definition httpd.h:604
#define M_POST
Definition httpd.h:594
#define M_PROPPATCH
Definition httpd.h:601
#define M_GET
Definition httpd.h:592
#define M_COPY
Definition httpd.h:603
#define M_MKWORKSPACE
Definition httpd.h:614
#define M_UNLOCK
Definition httpd.h:606
#define M_BASELINE_CONTROL
Definition httpd.h:616
#define M_CHECKIN
Definition httpd.h:610
#define M_MERGE
Definition httpd.h:617
#define M_UPDATE
Definition httpd.h:611
#define M_DELETE
Definition httpd.h:595
#define M_INVALID
Definition httpd.h:618
#define M_REPORT
Definition httpd.h:613
#define M_MKCOL
Definition httpd.h:602
#define M_UNCHECKOUT
Definition httpd.h:609
#define ap_strchr(s, c)
Definition httpd.h:2351
int ap_cstr_casecmp(const char *s1, const char *s2)
Definition util.c:3542
const char * ap_psignature(const char *prefix, request_rec *r)
Definition core.c:3516
#define ap_escape_uri(ppool, path)
Definition httpd.h:1836
#define ap_strchr_c(s, c)
Definition httpd.h:2353
int ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
Definition util.c:3559
#define ap_escape_html(p, s)
Definition httpd.h:1860
int ap_parse_strict_length(apr_off_t *len, const char *str)
Definition util.c:2683
apr_size_t size
const char int apr_pool_t * pool
Definition apr_cstr.h:84
#define APR_SUCCESS
Definition apr_errno.h:225
int apr_status_t
Definition apr_errno.h:44
const char * key
void * data
apr_array_header_t ** result
int strcasecmp(const char *a, const char *b)
apr_sockaddr_t apr_sockaddr_t apr_sockaddr_t * source
apr_interval_time_t t
apr_interval_time_t apr_pollcb_cb_t func
Definition apr_poll.h:422
apr_pool_t * b
Definition apr_pools.h:529
apr_pool_t * parent
Definition apr_pools.h:197
#define apr_pool_create(newpool, parent)
Definition apr_pools.h:322
#define apr_pcalloc(p, size)
Definition apr_pools.h:465
apr_dir_t * dir
const char * s
Definition apr_strings.h:95
const apr_array_header_t * arr
Definition apr_tables.h:187
const apr_array_header_t * first
Definition apr_tables.h:207
apr_int32_t apr_int32_t apr_int32_t err
const char const char *const const char *const apr_procattr_t * attr
apr_cmdtype_e cmd
int int status
#define REQUEST_NO_BODY
Definition httpd.h:746
Apache Configuration.
CORE HTTP Daemon.
Apache Logging library.
Command line options.
HTTP protocol handling.
Apache Request library.
HTTP Daemon routines.
apr_pool_t * p
Definition md_event.c:32
static const char * dav_cmd_davmintimeout(cmd_parms *cmd, void *config, const char *arg1)
Definition mod_dav.c:332
@ DAV_ENABLED_UNSET
Definition mod_dav.c:74
@ DAV_ENABLED_OFF
Definition mod_dav.c:75
@ DAV_ENABLED_ON
Definition mod_dav.c:76
static int dav_created(request_rec *r, const char *locn, const char *what, int replaced)
Definition mod_dav.c:673
static void dav_stream_response(dav_walk_resource *wres, int status, dav_get_props_result *propstats, apr_pool_t *pool)
Definition mod_dav.c:1226
static int dav_method_search(request_rec *r)
Definition mod_dav.c:1637
static int dav_method_unlock(request_rec *r)
Definition mod_dav.c:3356
#define DAV_DEFAULT_PROVIDER
Definition mod_dav.c:66
static int dav_method_vsn_control(request_rec *r)
Definition mod_dav.c:3455
static int dav_method_copymove(request_rec *r, int is_move)
Definition mod_dav.c:2751
static dav_error * dav_gen_supported_reports(request_rec *r, const dav_resource *resource, const apr_xml_elem *elem, apr_text_header *body)
Definition mod_dav.c:1538
static void dav_core_gather_reports(request_rec *r, const dav_resource *resource, apr_array_header_t *reports, dav_error **err)
Definition mod_dav.c:4320
static int dav_method_update(request_rec *r)
Definition mod_dav.c:3946
static int dav_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
Definition mod_dav.c:113
static int dav_method_checkin(request_rec *r)
Definition mod_dav.c:3866
static const char * dav_cmd_davbasepath(cmd_parms *cmd, void *config, const char *arg1)
Definition mod_dav.c:290
static int dav_method_options(request_rec *r)
Definition mod_dav.c:1692
static int dav_method_label(request_rec *r)
Definition mod_dav.c:4170
static const char * dav_cmd_davlockdiscovery(cmd_parms *cmd, void *config, int arg)
Definition mod_dav.c:317
static int dav_method_lock(request_rec *r)
Definition mod_dav.c:3171
static int dav_fixups(request_rec *r)
Definition mod_dav.c:5113
#define DAV_LABEL_REMOVE
Definition mod_dav.c:4118
static int dav_error_response_tag(request_rec *r, dav_error *err)
Definition mod_dav.c:384
static int process_mkcol_body(request_rec *r)
Definition mod_dav.c:2579
static int dav_method_delete(request_rec *r)
Definition mod_dav.c:1264
static int dav_parse_range(request_rec *r, apr_off_t *range_start, apr_off_t *range_end)
Definition mod_dav.c:851
static int dav_method_merge(request_rec *r)
Definition mod_dav.c:4577
static dav_error * dav_label_walker(dav_walk_resource *wres, int calltype)
Definition mod_dav.c:4125
static const command_rec dav_cmds[]
Definition mod_dav.c:5191
static int dav_method_proppatch(request_rec *r)
Definition mod_dav.c:2415
static int dav_method_make_workspace(request_rec *r)
Definition mod_dav.c:4429
static dav_error * dav_gen_supported_live_props(request_rec *r, const dav_resource *resource, const apr_xml_elem *elem, apr_text_header *body)
Definition mod_dav.c:1449
static void register_hooks(apr_pool_t *p)
Definition mod_dav.c:5168
#define DAV_LABEL_SET
Definition mod_dav.c:4117
static const char * dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config, int arg)
Definition mod_dav.c:302
static int dav_get_overwrite(request_rec *r)
Definition mod_dav.c:729
static int dav_handler(request_rec *r)
Definition mod_dav.c:4933
static int dav_method_propfind(request_rec *r)
Definition mod_dav.c:2137
#define DAV_INHERIT_VALUE(parent, child, field)
Definition mod_dav.c:97
static void dav_prop_log_errors(dav_prop_ctx *ctx)
Definition mod_dav.c:2373
static int dav_core_deliver_report(request_rec *r, const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output, dav_error **err)
Definition mod_dav.c:4304
static int dav_method_checkout(request_rec *r)
Definition mod_dav.c:3648
static void dav_cache_badprops(dav_walker_ctx *ctx)
Definition mod_dav.c:2034
static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
Definition mod_dav.c:2062
static int dav_error_response(request_rec *r, int status, const char *body)
Definition mod_dav.c:353
#define DAV_LABEL_ADD
Definition mod_dav.c:4116
static int dav_method_post(request_rec *r)
Definition mod_dav.c:953
@ DAV_M_BIND
Definition mod_dav.c:106
@ DAV_M_LAST
Definition mod_dav.c:108
@ DAV_M_SEARCH
Definition mod_dav.c:107
static const char * dav_xml_escape_uri(apr_pool_t *p, const char *uri)
Definition mod_dav.c:453
static int dav_method_uncheckout(request_rec *r)
Definition mod_dav.c:3797
static int dav_method_make_activity(request_rec *r)
Definition mod_dav.c:4500
#define DAV_HANDLER_NAME
Definition mod_dav.c:69
static int dav_methods[DAV_M_LAST]
Definition mod_dav.c:110
static int dav_process_ctx_list(void(*func)(dav_prop_ctx *ctx), apr_array_header_t *ctx_list, int stop_on_error, int reverse)
Definition mod_dav.c:2388
static void * dav_merge_server_config(apr_pool_t *p, void *base, void *overrides)
Definition mod_dav.c:136
static void dav_log_err(request_rec *r, dav_error *err, int level)
Definition mod_dav.c:608
static int dav_method_mkcol(request_rec *r)
Definition mod_dav.c:2629
static void * dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
Definition mod_dav.c:173
static int dav_method_report(request_rec *r)
Definition mod_dav.c:4345
static void * dav_create_server_config(apr_pool_t *p, server_rec *s)
Definition mod_dav.c:125
static void * dav_create_dir_config(apr_pool_t *p, char *dir)
Definition mod_dav.c:150
static const char * dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
Definition mod_dav.c:256
static int dav_method_bind(request_rec *r)
Definition mod_dav.c:4724
static int dav_method_get(request_rec *r)
Definition mod_dav.c:896
static dav_error * dav_gen_supported_methods(request_rec *r, const apr_xml_elem *elem, const apr_table_t *methods, apr_text_header *body)
Definition mod_dav.c:1383
static int dav_method_put(request_rec *r)
Definition mod_dav.c:981
static int dav_method_baseline_control(request_rec *r)
Definition mod_dav.c:4571
DAV extension module for Apache 2.0.*.
#define DAV_PROPFIND_IS_ALLPROP
Definition mod_dav.h:1859
#define DAV_WALKTYPE_AUTH
Definition mod_dav.h:1819
#define DAV_PROPFIND_IS_PROPNAME
Definition mod_dav.h:1860
#define DAV_WALKTYPE_TOLERANT
Definition mod_dav.h:1822
#define DAV_PROP_OP_DELETE
Definition mod_dav.h:1766
#define DAV_PROPFIND_IS_PROP
Definition mod_dav.h:1861
#define DAV_WALKTYPE_LOCKNULL
Definition mod_dav.h:1821
#define DAV_PROP_OP_SET
Definition mod_dav.h:1765
#define DAV_WALKTYPE_NORMAL
Definition mod_dav.h:1820
static const char *const hooks[]
return NULL
Definition mod_so.c:359
int i
Definition mod_so.c:347
char * name
The representation of a filter chain.
const char * provider_name
Definition ap_provider.h:36
Definition apr_tables.h:81
char * key
Definition apr_tables.h:83
apr_text * last
Definition apr_xml.h:68
apr_text * first
Definition apr_xml.h:66
struct apr_text * next
Definition apr_xml.h:57
char * fragment
Definition apr_uri.h:103
apr_array_header_t * namespaces
Definition apr_xml.h:204
apr_xml_elem * root
Definition apr_xml.h:202
const char * name
Definition apr_xml.h:164
struct apr_xml_elem * first_child
Definition apr_xml.h:180
struct apr_xml_elem * next
Definition apr_xml.h:178
struct apr_xml_attr * attr
Definition apr_xml.h:182
struct apr_bucket_alloc_t * bucket_alloc
Definition httpd.h:1201
unsigned aborted
Definition httpd.h:1219
Per-directory configuration.
Definition http_core.h:527
int locktimeout
Definition mod_dav.c:85
int allow_lockdiscovery
Definition mod_dav.c:87
int allow_depthinfinity
Definition mod_dav.c:86
const dav_provider * provider
Definition mod_dav.c:82
const char * base
Definition mod_dav.c:84
const char * provider_name
Definition mod_dav.c:81
const char * dir
Definition mod_dav.c:83
apr_text * xmlns
Definition mod_dav.h:488
apr_text * propstats
Definition mod_dav.h:487
void(* close_lockdb)(dav_lockdb *lockdb)
Definition mod_dav.h:1531
dav_error *(* get_resource)(request_rec *r, const char *root_dir, const char *label, int use_checked_in, dav_resource **resource)
Definition mod_dav.h:1944
int(* versionable)(const dav_resource *resource)
Definition mod_dav.h:2284
int(* can_be_activity)(const dav_resource *resource)
Definition mod_dav.h:2503
dav_error *(* vsn_control)(dav_resource *resource, const char *target)
Definition mod_dav.h:2310
int(* can_be_workspace)(const dav_resource *resource)
Definition mod_dav.h:2479
dav_error *(* avail_reports)(const dav_resource *resource, const dav_report_elem **reports)
Definition mod_dav.h:2373
dav_error *(* checkin)(dav_resource *resource, int keep_checked_out, dav_resource **version_resource)
Definition mod_dav.h:2361
dav_error *(* make_workspace)(dav_resource *resource, apr_xml_doc *doc)
Definition mod_dav.h:2492
dav_error *(* merge)(dav_resource *target, dav_resource *source, int no_auto_merge, int no_checkout, apr_xml_elem *prop_elem, ap_filter_t *output)
Definition mod_dav.h:2525
dav_error *(* add_label)(const dav_resource *resource, const char *label, int replace)
Definition mod_dav.h:2453
dav_error *(* deliver_report)(request_rec *r, const dav_resource *resource, const apr_xml_doc *doc, ap_filter_t *output)
Definition mod_dav.h:2404
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
dav_error *(* update)(const dav_resource *resource, const dav_resource *version, const char *label, int depth, dav_response **response)
Definition mod_dav.h:2432
int(* report_label_header_allowed)(const apr_xml_doc *doc)
Definition mod_dav.h:2382
dav_error *(* get_option)(const dav_resource *resource, const apr_xml_elem *elem, apr_text_header *option)
Definition mod_dav.h:2277
void(* get_vsn_options)(apr_pool_t *p, apr_text_header *phdr)
Definition mod_dav.h:2270
dav_error *(* make_activity)(dav_resource *resource)
Definition mod_dav.h:2515
request_rec * r
Definition mod_dav.c:4109
const char * label
Definition mod_dav.c:4112
const dav_hooks_vsn * vsn_hooks
Definition mod_dav.c:4121
dav_walk_params w
Definition mod_dav.c:4106
const dav_hooks_locks * hooks
Definition mod_dav.h:1334
dav_error *(* dav_header)(request_rec *r, const dav_resource *resource, apr_text_header *phdr)
Definition mod_dav.h:2624
dav_error *(* dav_method)(request_rec *r, const dav_resource *resource, apr_text_header *phdr)
Definition mod_dav.h:2628
request_rec * r
Definition props.c:174
const dav_hooks_vsn * vsn
Definition mod_dav.h:677
const dav_hooks_propdb * propdb
Definition mod_dav.h:675
const dav_hooks_locks * locks
Definition mod_dav.h:676
const dav_hooks_repository * repos
Definition mod_dav.h:674
const dav_hooks_search * search
Definition mod_dav.h:679
const dav_hooks_binding * binding
Definition mod_dav.h:678
const char * uri
Definition mod_dav.h:408
int versioned
Definition mod_dav.h:396
dav_resource_type type
Definition mod_dav.h:388
dav_get_props_result propresult
Definition mod_dav.h:498
const char * desc
Definition mod_dav.h:495
const char * href
Definition mod_dav.h:494
dav_response * response
Definition mod_dav.h:1812
const dav_resource * resource
Definition mod_dav.h:1809
apr_pool_t * pool
Definition mod_dav.h:1806
A structure that represents the current request.
Definition httpd.h:845
char * user
Definition httpd.h:1005
int status
Definition httpd.h:891
char * uri
Definition httpd.h:1016
struct ap_filter_t * output_filters
Definition httpd.h:1070
int assbackwards
Definition httpd.h:868
int read_body
Definition httpd.h:947
int header_only
Definition httpd.h:875
const char * handler
Definition httpd.h:994
apr_table_t * notes
Definition httpd.h:985
int method_number
Definition httpd.h:898
apr_pool_t * pool
Definition httpd.h:847
char * filename
Definition httpd.h:1018
apr_off_t remaining
Definition httpd.h:959
apr_uri_t parsed_uri
Definition httpd.h:1092
conn_rec * connection
Definition httpd.h:849
apr_table_t * err_headers_out
Definition httpd.h:981
struct ap_filter_t * input_filters
Definition httpd.h:1072
apr_table_t * headers_in
Definition httpd.h:976
apr_int64_t allowed
Definition httpd.h:922
int read_chunked
Definition httpd.h:949
request_rec * main
Definition httpd.h:860
apr_off_t sent_bodyct
Definition httpd.h:929
struct ap_conf_vector_t * per_dir_config
Definition httpd.h:1047
const char * status_line
Definition httpd.h:889
apr_table_t * headers_out
Definition httpd.h:978
A structure to store information for each virtual server.
Definition httpd.h:1322
@ AP_MODE_READBYTES
Definition util_filter.h:43
Apache script tools.
static NAMED * lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize)
Definition xmlparse.c:7191