/* Licensed to the Apache Software Foundation (ASF) under one or more *contributorlicenseagreements.SeetheNOTICEfiledistributedwith *thisworkforadditionalinformationregardingcopyrightownership. *TheASFlicensesthisfiletoYouundertheApacheLicense,Version2.0 *(the"License");youmaynotusethisfileexceptincompliancewith *theLicense.YoumayobtainacopyoftheLicenseat * *http://www.apache.org/licenses/LICENSE-2.0 * *Unlessrequiredbyapplicablelaworagreedtoinwriting,software *distributedundertheLicenseisdistributedonan"ASIS"BASIS, *WITHOUTWARRANTIESORCONDITIONSOFANYKIND,eitherexpressorimplied. *SeetheLicenseforthespecificlanguagegoverningpermissionsand *limitationsundertheLicense.
*/
conf = ap_get_module_config(r->per_dir_config, &dav_module); /* assert: conf->provider_name != NULL
(otherwise, DAV is disabled, and we wouldn't be here) */
/* assert: conf->provider != NULL
(checked when conf->provider_name is set) */ return conf->provider;
}
if (conf->provider_name != NULL) { /* lookup and cache the actual provider now */
conf->provider = dav_lookup_provider(conf->provider_name);
if (conf->provider == NULL) { /* by the time they use it, the provider should be loaded and
registered with us. */ return apr_psprintf(cmd->pool, "Unknown DAV provider: %s",
conf->provider_name);
}
}
/* Write a complete RESPONSE object out as a <DAV:response> xml element.DataissentintobrigadeBB,whichisauto-flushedinto theoutputfilterstackforrequestR.UsePOOLforanytemporary allocations.
if (response->propresult.propstats == NULL) { /* use the Status-Line text from Apache. Note, this will *defaultto500InternalServerErroriffirst->status *isnotaknown(orvalid)statuscode.
*/
ap_fputstrs(r->output_filters, bb, "<D:status>HTTP/1.1 ",
ap_get_status_line(response->status), "</D:status>" DEBUG_CR,
NULL);
} else { /* assume this includes <propstat> and is quoted properly */ for (t = response->propresult.propstats; t; t = t->next) {
ap_fputs(r->output_filters, bb, t->text);
}
}
/* Factorized helper function: prep request_rec R for a multistatus responseandwrite<multistatus>tagintoBB,destinedfor R->output_filters.UsexmlNAMESPACESininitialtag,if
non-NULL. */
DAV_DECLARE(void) dav_begin_multistatus(apr_bucket_brigade *bb,
request_rec *r, int status,
apr_array_header_t *namespaces)
{ /* Set the correct status and Content-Type */
r->status = status;
ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
/* Send the headers and actual multistatus response now... */
ap_fputs(r->output_filters, bb, DAV_XML_HEADER DEBUG_CR "<D:multistatus xmlns:D=\"DAV:\"");
if (namespaces != NULL) { int i;
for (i = namespaces->nelts; i--; ) {
ap_fprintf(r->output_filters, bb, " xmlns:ns%d=\"%s\"", i,
APR_XML_GET_URI_ITEM(namespaces, i));
}
}
ap_fputs(r->output_filters, bb, ">" DEBUG_CR);
}
/* Finish a multistatus response started by dav_begin_multistatus: */
DAV_DECLARE(apr_status_t) dav_finish_multistatus(request_rec *r,
apr_bucket_brigade *bb)
{
apr_bucket *b;
/* Log the errors */ /* ### should have a directive to log the first or all */ for (errscan = err; errscan != NULL; errscan = errscan->prev) { if (errscan->desc == NULL) continue;
if (!ap_is_HTTP_VALID_RESPONSE(err->status)) { /* we have responded already */ return AP_FILTER_ERROR;
}
if (response == NULL) {
dav_error *stackerr = err;
/* our error messages are safe; tell Apache this */
apr_table_setn(r->notes, "verbose-error-to", "*");
/* Didn't get a multistatus response passed in, but we still mightbeabletogenerateastandard<D:error>response.
Search the error stack for an errortag. */ while (stackerr != NULL && stackerr->tagname == NULL)
stackerr = stackerr->prev;
/* send the multistatus and tell Apache the request/response is DONE. */
dav_send_multistatus(r, err->status, response, NULL); return DONE;
}
/* handy function for return values of methods that (may) create things.
* locn if provided is assumed to be escaped. */ staticint dav_created(request_rec *r, constchar *locn, constchar *what, int replaced)
{ constchar *body;
if (locn == NULL) {
locn = ap_escape_uri(r->pool, r->uri);
}
/* did the target resource already exist? */ if (replaced) { /* Apache will supply a default message */ return HTTP_NO_CONTENT;
}
/* Per HTTP/1.1, S10.2.2: add a Location header to contain the
* URI that was created. */
/* Convert locn to an absolute URI, and return in Location header */
apr_table_setn(r->headers_out, "Location", ap_construct_url(r->pool, locn, r));
/* ### insert an ETag header? see HTTP/1.1 S10.2.2 */
/* Apache doesn't allow us to set a variable body for HTTP_CREATED, so
* we must manufacture the entire response. */
body = apr_pstrcat(r->pool, what, " ", ap_escape_html(r->pool, locn), " has been created.", NULL); return dav_error_response(r, HTTP_CREATED, body);
}
/* ### move to dav_util? */
DAV_DECLARE(int) dav_get_depth(request_rec *r, int def_depth)
{ constchar *depth = apr_table_get(r->headers_in, "Depth");
/* The caller will return an HTTP_BAD_REQUEST. This will augment the
* default message that Apache provides. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00580) "An invalid Depth header was specified."); return -1;
}
/* The caller will return an HTTP_BAD_REQUEST. This will augment the
* default message that Apache provides. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00581) "An invalid Overwrite header was specified."); return -1;
}
/* resolve a request URI to a resource descriptor. * *Iflabel_allowed!=0,thenallowtherequesttargettobealteredby *aLabel:header. * *Ifuse_checked_inistrue,thentherepositoryprovidershouldreturn *theresourceidentifiedbytheDAV:checked-inpropertyoftheresource *identifiedbytheRequest-URI.
*/
DAV_DECLARE(dav_error *) dav_get_resource(request_rec *r, int label_allowed, int use_checked_in, dav_resource **res_p)
{
dav_dir_conf *conf; constchar *label = NULL, *base;
dav_error *err;
/* if the request target can be overridden, get any target selector */ if (label_allowed) {
label = apr_table_get(r->headers_in, "label");
}
conf = ap_get_module_config(r->per_dir_config, &dav_module); /* assert: conf->provider != NULL */ if (conf->provider == NULL) { return dav_new_error(r->pool, HTTP_METHOD_NOT_ALLOWED, 0, 0,
apr_psprintf(r->pool, "DAV not enabled for %s",
ap_escape_html(r->pool, r->uri)));
}
/* Take the repos root from DAVBasePath if configured, else the
* path of the enclosing section. */
base = conf->base ? conf->base : conf->dir;
/* resolve the resource */
err = (*conf->provider->repos->get_resource)(r, base,
label, use_checked_in,
res_p); if (err != NULL) { /* In the error path, give a hint that DavBasePath needs to be
* used if the location was configured via a regex match. */ if (!conf->base) {
core_dir_config *cdc = ap_get_core_module_config(r->per_dir_config);
if (cdc->r) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(10484) "failed to find repository for location configured " "via regex match - missing DAVBasePath?");
}
}
/* Note: this shouldn't happen, but just be sure... */ if (*res_p == NULL) { /* ### maybe use HTTP_INTERNAL_SERVER_ERROR */ return dav_new_error(r->pool, HTTP_NOT_FOUND, 0, 0,
apr_psprintf(r->pool, "The provider did not define a " "resource for %s.",
ap_escape_html(r->pool, r->uri)));
}
/* ### hmm. this doesn't feel like the right place or thing to do */ /* if there were any input headers requiring a Vary header in the response,
* add it now */
dav_add_vary_header(r, r, *res_p);
/* handle the GET method */ staticint dav_method_get(request_rec *r)
{
dav_resource *resource;
dav_error *err; int status;
/* This method should only be called when the resource is not *visibletoApache.Wewillfetchtheresourcefromtherepository, *thencreateasubrequestforApachetohandle.
*/
err = dav_get_resource(r, 1/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
if (!resource->exists) { /* Apache will supply a default error for this. */ return HTTP_NOT_FOUND;
}
/* set up the HTTP headers for the response */ if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
err = dav_push_error(r->pool, err->status, 0, "Unable to set up HTTP headers.",
err); return dav_handle_err(r, err, NULL);
}
/* Handle conditional requests */
status = ap_meets_conditions(r); if (status) { return status;
}
if (r->header_only) { return DONE;
}
/* okay... time to deliver the content */ if ((err = (*resource->hooks->deliver)(resource,
r->output_filters)) != NULL) {
err = dav_push_error(r->pool, err->status, 0, "Unable to deliver content.",
err); return dav_handle_err(r, err, NULL);
}
return DONE;
}
/* validate resource/locks on POST, then pass to the default handler */ staticint dav_method_post(request_rec *r)
{
dav_resource *resource;
dav_error *err;
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 0/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
/* Note: depth == 0. Implies no need for a multistatus response. */ if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
DAV_VALIDATE_RESOURCE, NULL)) != NULL) { /* ### add a higher-level description? */ return dav_handle_err(r, err, NULL);
}
return DECLINED;
}
/* handle the PUT method */ staticint dav_method_put(request_rec *r)
{
dav_resource *resource; int resource_state;
dav_auto_version_info av_info; const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r); constchar *body;
dav_error *err;
dav_error *err2;
dav_stream_mode mode;
dav_stream *stream;
dav_response *multi_response; int has_range;
apr_off_t range_start;
apr_off_t range_end;
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 0/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
/* If not a file or collection resource, PUT not allowed */ if (resource->type != DAV_RESOURCE_TYPE_REGULAR
&& resource->type != DAV_RESOURCE_TYPE_WORKING) {
body = apr_psprintf(r->pool, "Cannot create resource %s with PUT.",
ap_escape_html(r->pool, r->uri)); return dav_error_response(r, HTTP_CONFLICT, body);
}
/* Cannot PUT a collection */ if (resource->collection) { return dav_error_response(r, HTTP_CONFLICT, "Cannot PUT to a collection.");
has_range = dav_parse_range(r, &range_start, &range_end); if (has_range < 0) { /* RFC 2616 14.16: If we receive an invalid Content-Range we must *notusethecontent.
*/
body = apr_psprintf(r->pool, "Malformed Content-Range header for PUT %s.",
ap_escape_html(r->pool, r->uri)); return dav_error_response(r, HTTP_BAD_REQUEST, body);
} elseif (has_range) {
mode = DAV_MODE_WRITE_SEEKABLE;
} else {
mode = DAV_MODE_WRITE_TRUNC;
}
/* make sure the resource can be modified (if versioning repository) */ if ((err = dav_auto_checkout(r, resource, 0/* not parent_only */,
&av_info)) != NULL) { /* ### add a higher-level description? */ return dav_handle_err(r, err, NULL);
}
/* Create the new file in the repository */ if ((err = (*resource->hooks->open_stream)(resource, mode,
&stream)) != NULL) { int status = err->status ? err->status : HTTP_FORBIDDEN; if (status > 299) {
err = dav_push_error(r->pool, status, 0,
apr_psprintf(r->pool, "Unable to PUT new contents for %s.",
ap_escape_html(r->pool, r->uri)),
err);
} else {
err = NULL;
}
}
if (err == NULL && has_range) { /* a range was provided. seek to the start */
err = (*resource->hooks->seek_stream)(stream, range_start);
}
if (err == NULL) {
apr_bucket_brigade *bb;
apr_bucket *b; int seen_eos = 0;
/* restore modifiability of resources back to what they were */
err2 = dav_auto_checkin(r, resource, err != NULL /* undo if error */, 0/*unlock*/, &av_info);
/* check for errors now */ if (err != NULL) {
err = dav_join_error(err, err2); /* don't forget err2 */ return dav_handle_err(r, err, NULL);
}
if (err2 != NULL) { /* just log a warning */
err2 = dav_push_error(r->pool, err2->status, 0, "The PUT was successful, but there " "was a problem automatically checking in " "the resource or its parent collection.",
err2);
dav_log_err(r, err2, APLOG_WARNING);
}
/* ### place the Content-Type and Content-Language into the propdb */
if (locks_hooks != NULL) {
dav_lockdb *lockdb;
if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) { /* The file creation was successful, but the locking failed. */
err = dav_push_error(r->pool, err->status, 0, "The file was PUT successfully, but there " "was a problem opening the lock database " "which prevents inheriting locks from the " "parent resources.",
err); return dav_handle_err(r, err, NULL);
}
/* notify lock system that we have created/replaced a resource */
err = dav_notify_created(r, lockdb, resource, resource_state, 0);
(*locks_hooks->close_lockdb)(lockdb);
if (err != NULL) { /* The file creation was successful, but the locking failed. */
err = dav_push_error(r->pool, err->status, 0, "The file was PUT successfully, but there " "was a problem updating its lock " "information.",
err); return dav_handle_err(r, err, NULL);
}
}
/* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
/* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */ return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
}
/* Use POOL to temporarily construct a dav_response object (from WRES
STATUS, and PROPSTATS) and stream it via WRES's ctx->brigade. */ staticvoid dav_stream_response(dav_walk_resource *wres, int status,
dav_get_props_result *propstats,
apr_pool_t *pool)
{
dav_response resp = { 0 };
dav_walker_ctx *ctx = wres->walk_ctx;
/* ### move this to dav_util? */
DAV_DECLARE(void) dav_add_response(dav_walk_resource *wres, int status, dav_get_props_result *propstats)
{
dav_response *resp;
/* just drop some data into an dav_response */
resp = apr_pcalloc(wres->pool, sizeof(*resp));
resp->href = apr_pstrdup(wres->pool, wres->resource->uri);
resp->status = status; if (propstats) {
resp->propresult = *propstats;
}
/* handle the DELETE method */ staticint dav_method_delete(request_rec *r)
{
dav_resource *resource;
dav_auto_version_info av_info;
dav_error *err;
dav_error *err2;
dav_response *multi_response; int result; int depth;
/* We don't use the request body right now, so torch it. */ if ((result = ap_discard_request_body(r)) != OK) { return result;
}
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 0/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
if (!resource->exists) { /* Apache will supply a default error for this. */ return HTTP_NOT_FOUND;
}
/* 2518 says that depth must be infinity only for collections. *Fornon-collections,depthisignored,unlessitisanillegalvalue(1).
*/
depth = dav_get_depth(r, DAV_INFINITY);
if (resource->collection && depth != DAV_INFINITY) { /* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00582) "Depth must be \"infinity\" for DELETE of a collection."); return HTTP_BAD_REQUEST;
}
if (!resource->collection && depth == 1) { /* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00583) "Depth of \"1\" is not allowed for DELETE."); return HTTP_BAD_REQUEST;
}
/* **Ifanyresourcesfailthelock/If:conditions,thenwemustfail **thedelete.Eachofthefailingresourceswillbelistedwithin **aDAV:multistatusbody,wrappedintoa424response. ** **Notethatafailureontheresourceitselfdoesnotgeneratea **multistatusresponse--onlyinternalmembers/collections.
*/ if ((err = dav_validate_request(r, resource, depth, NULL,
&multi_response,
DAV_VALIDATE_PARENT
| DAV_VALIDATE_USE_424, NULL)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not DELETE %s due to a failed " "precondition (e.g. locks).",
ap_escape_html(r->pool, r->uri)),
err); return dav_handle_err(r, err, multi_response);
}
/* ### RFC 2518 s. 8.10.5 says to remove _all_ locks, not just those *lockedbythetoken(s)intheif_header.
*/ if ((result = dav_unlock(r, resource, NULL)) != OK) { return result;
}
/* if versioned resource, make sure parent is checked out */ if ((err = dav_auto_checkout(r, resource, 1/* parent_only */,
&av_info)) != NULL) { /* ### add a higher-level description? */ return dav_handle_err(r, err, NULL);
}
/* try to remove the resource */
err = (*resource->hooks->remove_resource)(resource, &multi_response);
/* restore writability of parent back to what it was */
err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */, 0/*unlock*/, &av_info);
/* check for errors now */ if (err != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not DELETE %s.",
ap_escape_html(r->pool, r->uri)),
err); return dav_handle_err(r, err, multi_response);
} if (err2 != NULL) { /* just log a warning */
err = dav_push_error(r->pool, err2->status, 0, "The DELETE was successful, but there " "was a problem automatically checking in " "the parent collection.",
err2);
dav_log_err(r, err, APLOG_WARNING);
}
/* ### HTTP_NO_CONTENT if no body, HTTP_OK if there is a body (some day) */
/* Apache will supply a default error for this. */ return HTTP_NO_CONTENT;
}
if (elem->first_child == NULL) { /* show all supported methods */
arr = apr_table_elts(methods);
elts = (const apr_table_entry_t *)arr->elts;
for (i = 0; i < arr->nelts; ++i) { if (elts[i].key == NULL) continue;
s = apr_pstrcat(r->pool, "<D:supported-method D:name=\"",
elts[i].key, "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s);
}
} else { /* check for support of specific methods */ for (child = elem->first_child; child != NULL; child = child->next) { if (child->ns == APR_XML_NS_DAV_ID
&& strcmp(child->name, "supported-method") == 0) { constchar *name = NULL;
/* go through attributes to find method name */ for (attr = child->attr; attr != NULL; attr = attr->next) { if (attr->ns == APR_XML_NS_DAV_ID
&& strcmp(attr->name, "name") == 0)
name = attr->value;
}
if (name == NULL) { return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0, "A DAV:supported-method element " "does not have a \"name\" attribute");
}
/* see if method is supported */ if (apr_table_get(methods, name) != NULL) {
s = apr_pstrcat(r->pool, "<D:supported-method D:name=\"",
name, "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s);
}
}
}
}
/* open lock database, to report on supported lock properties */ if ((err = dav_open_lockdb(r, 1, &lockdb)) != NULL) { return dav_push_error(r->pool, err->status, 0, "The lock database could not be opened, " "preventing the reporting of supported lock " "properties.",
err);
}
/* open the property database (readonly) for the resource */ if ((err = dav_open_propdb(r, lockdb, resource, DAV_PROPDB_RO, NULL,
&propdb)) != NULL) { if (lockdb != NULL)
(*lockdb->hooks->close_lockdb)(lockdb);
return dav_push_error(r->pool, err->status, 0, "The property database could not be opened, " "preventing report of supported properties.",
err);
}
if (elem->first_child == NULL) { /* show all supported live properties */
dav_get_props_result props = dav_get_allprops(propdb, DAV_PROP_INSERT_SUPPORTED);
body->last->next = props.propstats; while (body->last->next != NULL)
body->last = body->last->next;
} else { /* check for support of specific live property */ for (child = elem->first_child; child != NULL; child = child->next) { if (child->ns == APR_XML_NS_DAV_ID
&& strcmp(child->name, "supported-live-property") == 0) { constchar *name = NULL; constchar *nmspace = NULL;
/* go through attributes to find name and namespace */ for (attr = child->attr; attr != NULL; attr = attr->next) { if (attr->ns == APR_XML_NS_DAV_ID) { if (strcmp(attr->name, "name") == 0)
name = attr->value; elseif (strcmp(attr->name, "namespace") == 0)
nmspace = attr->value;
}
}
if (name == NULL) {
err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0, "A DAV:supported-live-property " "element does not have a \"name\" " "attribute"); break;
}
/* default namespace to DAV: */ if (nmspace == NULL)
nmspace = "DAV:";
/* check for support of property */
dav_get_liveprop_supported(propdb, nmspace, name, body);
}
}
}
reports = apr_array_make(r->pool, 5, sizeof(constchar *));
dav_run_gather_reports(r, resource, reports, &err); if (err != NULL) { return dav_push_error(r->pool, err->status, 0, "DAV:supported-report-set could not be " "determined due to a problem fetching the " "available reports for this resource.",
err);
}
if (elem->first_child == NULL) { int i;
/* show all supported reports */
rp = (const dav_report_elem *)reports->elts; for (i = 0; i < reports->nelts; i++, rp++) { /* Note: we presume reports->namespace is
* properly XML/URL quoted */
s = apr_pstrcat(r->pool, "<D:supported-report D:name=\"",
rp->name, "\" D:namespace=\"",
rp->nmspace, "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s);
}
} else { /* check for support of specific report */ for (child = elem->first_child; child != NULL; child = child->next) { if (child->ns == APR_XML_NS_DAV_ID
&& strcmp(child->name, "supported-report") == 0) { constchar *name = NULL; constchar *nmspace = NULL; int i;
/* go through attributes to find name and namespace */ for (attr = child->attr; attr != NULL; attr = attr->next) { if (attr->ns == APR_XML_NS_DAV_ID) { if (strcmp(attr->name, "name") == 0)
name = attr->value; elseif (strcmp(attr->name, "namespace") == 0)
nmspace = attr->value;
}
}
if (name == NULL) { return dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, 0, "A DAV:supported-report element " "does not have a \"name\" attribute");
}
/* default namespace to DAV: */ if (nmspace == NULL) {
nmspace = "DAV:";
}
rp = (const dav_report_elem *)reports->elts; for (i = 0; i < reports->nelts; i++, rp++) { if (strcmp(name, rp->name) == 0
&& strcmp(nmspace, rp->nmspace) == 0) { /* Note: we presume reports->nmspace is *properlyXML/URLquoted
*/
s = apr_pstrcat(r->pool, "<D:supported-report " "D:name=\"",
rp->name, "\" D:namespace=\"",
rp->nmspace, "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s); break;
}
}
}
}
}
/* If no search provider, decline the request */ if (search_hooks == NULL) return DECLINED;
/* This method should only be called when the resource is not *visibletoApache.Wewillfetchtheresourcefromtherepository, *thencreateasubrequestforApachetohandle.
*/
err = dav_get_resource(r, 1/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
if (!resource->exists) { /* Apache will supply a default error for this. */ return HTTP_NOT_FOUND;
}
/* set up the HTTP headers for the response */ if ((err = (*resource->hooks->set_headers)(r, resource)) != NULL) {
err = dav_push_error(r->pool, err->status, 0, "Unable to set up HTTP headers.",
err); return dav_handle_err(r, err, NULL);
}
if (r->header_only) { return DONE;
}
/* okay... time to search the content */ /* Let's validate XML and process walk function *inthehookfunction
*/ if ((err = (*search_hooks->search_resource)(r, &multi_status)) != NULL) { /* ### add a higher-level description? */ return dav_handle_err(r, err, NULL);
}
/* We have results in multi_status */ /* Should I pass namespace?? */
dav_send_multistatus(r, HTTP_MULTI_STATUS, multi_status, NULL);
/* parse any request body */ if ((result = ap_xml_parse_input(r, &doc)) != OK) { return result;
} /* note: doc == NULL if no request body */
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
if (doc && !dav_validate_root(doc, "options")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00584) "The \"options\" element was not found."); return HTTP_BAD_REQUEST;
}
/* determine which providers are available */
dav_level = "1";
if (locks_hooks != NULL) {
dav_level = "1,2";
}
if (binding_hooks != NULL)
dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
case DAV_RESOURCE_NULL: /* resource is null. */
apr_table_addn(methods, "MKCOL", "");
apr_table_addn(methods, "PUT", "");
if (locks_hooks != NULL)
apr_table_addn(methods, "LOCK", "");
break;
default: /* ### internal error! */ break;
}
/* If there is a versioning provider, add versioning methods */ if (vsn_hooks != NULL) { if (!resource->exists) { if ((*vsn_hooks->versionable)(resource))
apr_table_addn(methods, "VERSION-CONTROL", "");
if (vsn_hooks->can_be_workspace != NULL
&& (*vsn_hooks->can_be_workspace)(resource))
apr_table_addn(methods, "MKWORKSPACE", "");
/* ### we might not support this DeltaV option */
apr_table_addn(methods, "UNCHECKOUT", "");
} elseif (vsn_hooks->add_label != NULL) {
apr_table_addn(methods, "CHECKOUT", "");
apr_table_addn(methods, "LABEL", "");
} else {
apr_table_addn(methods, "CHECKOUT", "");
}
}
/* If there is a bindings provider, see if resource is bindable */ if (binding_hooks != NULL
&& (*binding_hooks->is_bindable)(resource)) {
apr_table_addn(methods, "BIND", "");
}
/* If there is a search provider, set SEARCH in option */ if (search_hooks != NULL) {
apr_table_addn(methods, "SEARCH", "");
}
/* first, compute total length */ for (i = 0; i < arr->nelts; ++i) { if (elts[i].key == NULL) continue;
/* add 1 for comma or null */
text_size += strlen(elts[i].key) + 1;
}
s = allow = apr_palloc(r->pool, text_size);
for (i = 0; i < arr->nelts; ++i) { if (elts[i].key == NULL) continue;
if (s != allow)
*s++ = ',';
strcpy(s, elts[i].key);
s += strlen(s);
}
apr_table_setn(r->headers_out, "Allow", allow);
/* If there is search set_option_head function, set head */ /* DASL: <DAV:basicsearch> *DASL:<http://foo.bar.com/syntax1> *DASL:<http://akuma.com/syntax2>
*/ if (search_hooks != NULL
&& *search_hooks->set_option_head != NULL) { if ((err = (*search_hooks->set_option_head)(r)) != NULL) { return dav_handle_err(r, err, NULL);
}
}
/* if there was no request body, then there is no response body */ if (doc == NULL) {
ap_set_content_length(r, 0);
/* ### this sends a Content-Type. the default OPTIONS does not. */
/* ### the default (ap_send_http_options) returns OK, but I believe *###thatisbecauseitisthedefaulthandlerandnothingelse
* ### will run after the thing. */ return DONE;
}
/* handle each options request */ for (elem = doc->root->first_child; elem != NULL; elem = elem->next) { /* check for something we recognize first */ int core_option = 0;
dav_error *err = NULL;
/* some props were expected on this collection/resource */
dav_cache_badprops(ctx);
badprops.propstats = ctx->propstat_404;
dav_stream_response(wres, 0, &badprops, ctx->scratchpool);
} else { /* no props on this collection/resource */
dav_stream_response(wres, HTTP_OK, NULL, ctx->scratchpool);
}
apr_pool_clear(ctx->scratchpool); return NULL;
} /* ### what to do about closing the propdb on server failure? */
/* at this point, ctx->scratchpool has been used to stream a singleresponse.thisfunctionfullycontrolsthepool,and thushastherighttoclearitforthenextiterationofthis
callback. */
apr_pool_clear(ctx->scratchpool);
return NULL;
}
/* handle the PROPFIND method */ staticint dav_method_propfind(request_rec *r)
{
dav_resource *resource; int depth;
dav_error *err; int result;
apr_xml_doc *doc;
dav_walker_ctx ctx = { { 0 } };
dav_response *multi_status;
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 1/* label_allowed */, 0 /* use_checked_in */,
&resource); if (err != NULL) return dav_handle_err(r, err, NULL);
if ((result = ap_xml_parse_input(r, &doc)) != OK) { return result;
} /* note: doc == NULL if no request body */
/* check for any method preconditions */ if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
&& err) { return dav_handle_err(r, err, NULL);
}
if (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) { /* Apache will supply a default error for this. */ return HTTP_NOT_FOUND;
}
if ((depth = dav_get_depth(r, DAV_INFINITY)) < 0) { /* dav_get_depth() supplies additional information for the
* default message. */ return HTTP_BAD_REQUEST;
}
if (depth == DAV_INFINITY && resource->collection) {
dav_dir_conf *conf;
conf = (dav_dir_conf *)ap_get_module_config(r->per_dir_config,
&dav_module); /* default is to DISALLOW these requests */ if (conf->allow_depthinfinity != DAV_ENABLED_ON) { return dav_error_response(r, HTTP_FORBIDDEN,
apr_psprintf(r->pool, "PROPFIND requests with a " "Depth of \"infinity\" are " "not allowed for %s.",
ap_escape_html(r->pool,
r->uri)));
}
}
if (doc && !dav_validate_root(doc, "propfind")) { /* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00585) "The \"propfind\" element was not found."); return HTTP_BAD_REQUEST;
}
/* ### validate that only one of these three elements is present */
if (doc == NULL || dav_find_child(doc->root, "allprop") != NULL) { /* note: no request body implies allprop */
ctx.propfind_type = DAV_PROPFIND_IS_ALLPROP;
} elseif (dav_find_child(doc->root, "propname") != NULL) {
ctx.propfind_type = DAV_PROPFIND_IS_PROPNAME;
} elseif (dav_find_child(doc->root, "prop") != NULL) {
ctx.propfind_type = DAV_PROPFIND_IS_PROP;
} else { /* "propfind" element must have one of the above three children */
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00586) "The \"propfind\" element does not contain one of " "the required child elements (the specific command)."); return HTTP_BAD_REQUEST;
}
if ((err = dav_open_lockdb(r, 1, &ctx.w.lockdb)) != NULL) {
err = dav_push_error(r->pool, err->status, 0, "The lock database could not be opened, " "preventing access to the various lock " "properties for the PROPFIND.",
err); return dav_handle_err(r, err, NULL);
} if (ctx.w.lockdb != NULL) { /* if we have a lock database, then we can walk locknull resources */
ctx.w.walk_type |= DAV_WALKTYPE_LOCKNULL;
}
/* send <multistatus> tag, with all doc->namespaces attached. */
/* NOTE: we *cannot* leave out the doc's namespaces from the initial<multistatus>tag.ifa404wasgeneratedforanHREF, thenweneedtospitoutthedoc'snamespacesforusebythe 404.Notethat<response>elementswilloverridethesens0, ns1,etc,butNOTwithinthe<response>scopeforthe
badprops. */
dav_begin_multistatus(ctx.bb, r, HTTP_MULTI_STATUS,
doc ? doc->namespaces : NULL);
/* Have the provider walk the resource. */
err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
if (ctx.w.lockdb != NULL) {
(*ctx.w.lockdb->hooks->close_lockdb)(ctx.w.lockdb);
}
if (err != NULL) { /* If an error occurred during the resource walk, there's basicallynothingwecandobutaborttheconnectionand loganerror.ThisisoneofthelimitationsofHTTP;it needsto"know"theentirestatusoftheresponsebefore generatingit,whichisjustimpossibleinthesestreamy
response situations. */
err = dav_push_error(r->pool, err->status, 0, "Provider encountered an error while streaming" " a multistatus PROPFIND response.", err);
dav_log_err(r, err, APLOG_ERR);
r->connection->aborted = 1; return DONE;
}
err=dav_push_error(r->pool,HTTP_INTERNAL_SERVER_ERROR,0, apr_psprintf(r->pool, "Couldnotopentheproperty" "databasefor%s.", ap_escape_html(r->pool,r->uri)), err); returndav_handle_err(r,err,NULL); } /*### what to do about closing the propdb on server failure? */
/*### for now, we don't need anything in the body */ if((result=ap_discard_request_body(r))!=OK){ returnresult; }
if((err=dav_open_lockdb(r,0,&lockdb))!=NULL){ /*### add a higher-level description? */ returndav_handle_err(r,err,NULL); }
/*removeanylocksfromtheoldresources*/ /* *### this is Yet Another Traversal. if we do a rename(), then we *### really don't have to do this in some cases since the inode *### values will remain constant across the move. but we can't *### know that fact from outside the provider :-( * *### note that we now have a problem atomicity in the move/copy *### since a failure after this would have removed locks (technically, *### this is okay to do, but really...) */ if(is_move&&lockdb!=NULL){ /*### this is wrong! it blasts direct locks on parent resources */ /*### pass lockdb! */ (void)dav_unlock(r,resource,NULL); }
/* *Refreshrequest *### Assumption: We can renew multiple locks on the same resource *### at once. First harvest all the positive lock-tokens given in *### the If header. Then modify the lock entries for this resource *### with the new Timeout val. */
locktoken_txt=apr_pstrdup(r->pool,const_locktoken_txt); if(locktoken_txt[0]!='<'){ /*### should provide more specifics... */ returnHTTP_BAD_REQUEST; } locktoken_txt++;
if(locktoken_txt[strlen(locktoken_txt)-1]!='>'){ /*### should provide more specifics... */ returnHTTP_BAD_REQUEST; } locktoken_txt[strlen(locktoken_txt)-1]='\0';
/* If an error occurred during the report delivery, there's
basically nothing we can do but abort the connection and
log an error. This is one of the limitations of HTTP; it
needs to "know" the entire status of the response before
generating it, which is just impossible in these streamy
response situations. */
err = dav_push_error(r->pool, err->status, 0, "Provider encountered an error while streaming" " a REPORT response.", err);
dav_log_err(r, err, APLOG_ERR);
r->connection->aborted = 1;
return DONE;
}
switch (result) {
case OK:
return DONE;
case DECLINED:
/* No one handled the report */
return HTTP_NOT_IMPLEMENTED;
default:
return DONE;
}
return DONE;
}
static int dav_method_make_workspace(request_rec *r)
{
dav_resource *resource;
const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
dav_error *err;
apr_xml_doc *doc;
int result;
/* if no versioning provider, or the provider does not support workspaces,
* decline the request
*/
if (vsn_hooks == NULL || vsn_hooks->make_workspace == NULL)
return DECLINED;
/* ask repository module to resolve the resource */
err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* parse the request body (must be a mkworkspace element) */
if ((result = ap_xml_parse_input(r, &doc)) != OK) {
return result;
}
/* check for any method preconditions */
if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
if (doc == NULL
|| !dav_validate_root(doc, "mkworkspace")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00615) "The request body does not contain " "a \"mkworkspace\" element.");
return HTTP_BAD_REQUEST;
}
/* Check request preconditions */
/* ### need a general mechanism for reporting precondition violations
* ### (should be returning XML document for 403/409 responses)
*/
/* resource must not already exist */
if (resource->exists) {
err = dav_new_error(r->pool, HTTP_CONFLICT, 0, 0, "<DAV:resource-must-be-null/>");
return dav_handle_err(r, err, NULL);
}
/* ### what about locking? */
/* attempt to create the workspace */
if ((err = (*vsn_hooks->make_workspace)(resource, doc)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not create workspace %s.",
ap_escape_html(r->pool, r->uri)),
err);
return dav_handle_err(r, err, NULL);
}
/* set the Cache-Control header, per the spec */
apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
static int dav_method_make_activity(request_rec *r)
{
dav_resource *resource;
const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
dav_error *err;
int result;
/* if no versioning provider, or the provider does not support activities,
* decline the request
*/
if (vsn_hooks == NULL || vsn_hooks->make_activity == NULL)
return DECLINED;
/* ask repository module to resolve the resource */
err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* check for any method preconditions */
if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
/* MKACTIVITY does not have a defined request body. */
if ((result = ap_discard_request_body(r)) != OK) {
return result;
}
/* Check request preconditions */
/* ### need a general mechanism for reporting precondition violations
* ### (should be returning XML document for 403/409 responses)
*/
/* resource must not already exist */
if (resource->exists) {
err = dav_new_error(r->pool, HTTP_CONFLICT, 0, 0, "<DAV:resource-must-be-null/>");
return dav_handle_err(r, err, NULL);
}
/* the provider must say whether the resource can be created as
an activity, i.e. whether the location is ok. */
if (vsn_hooks->can_be_activity != NULL
&& !(*vsn_hooks->can_be_activity)(resource)) {
err = dav_new_error(r->pool, HTTP_FORBIDDEN, 0, 0, "<DAV:activity-location-ok/>");
return dav_handle_err(r, err, NULL);
}
/* ### what about locking? */
/* attempt to create the activity */
if ((err = (*vsn_hooks->make_activity)(resource)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not create activity %s.",
ap_escape_html(r->pool, r->uri)),
err);
return dav_handle_err(r, err, NULL);
}
/* set the Cache-Control header, per the spec */
apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
static int dav_method_merge(request_rec *r)
{
dav_resource *resource;
dav_resource *source_resource;
const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
dav_error *err;
int result;
apr_xml_doc *doc;
apr_xml_elem *source_elem;
apr_xml_elem *href_elem;
apr_xml_elem *prop_elem;
const char *source;
int no_auto_merge;
int no_checkout;
dav_lookup_result lookup;
/* If no versioning provider, decline the request */
if (vsn_hooks == NULL)
return DECLINED;
if ((result = ap_xml_parse_input(r, &doc)) != OK)
return result;
if (doc == NULL || !dav_validate_root(doc, "merge")) {
/* This supplies additional information for the default msg. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00616) "The request body must be present and must be a " "DAV:merge element.");
return HTTP_BAD_REQUEST;
}
if ((source_elem = dav_find_child(doc->root, "source")) == NULL) {
/* This supplies additional information for the default msg. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00617) "The DAV:merge element must contain a DAV:source " "element.");
return HTTP_BAD_REQUEST;
}
if ((href_elem = dav_find_child(source_elem, "href")) == NULL) {
/* This supplies additional information for the default msg. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00618) "The DAV:source element must contain a DAV:href " "element.");
return HTTP_BAD_REQUEST;
} source = dav_xml_get_cdata(href_elem, r->pool, 1 /* strip_white */);
/* get a subrequest for the source, so that we can get a dav_resource
for that source. */
lookup = dav_lookup_uri(source, r, 0 /* must_be_absolute */);
if (lookup.rnew == NULL) {
if (lookup.err.status == HTTP_BAD_REQUEST) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00619) "%s", lookup.err.desc);
return HTTP_BAD_REQUEST;
}
/* ### this assumes that dav_lookup_uri() only generates a status
* ### that Apache can provide a status line for!! */
return dav_error_response(r, lookup.err.status, lookup.err.desc);
}
if (lookup.rnew->status != HTTP_OK) {
/* ### how best to report this... */
return dav_error_response(r, lookup.rnew->status, "Merge source URI had an error.");
}
err = dav_get_resource(lookup.rnew, 0 /* label_allowed */, 0 /* use_checked_in */, &source_resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* check for any method preconditions */
if (dav_run_method_precondition(r, source_resource, NULL, doc, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
/* ### check RFC. I believe the DAV:merge element may contain any
### element also allowed within DAV:checkout. need to extract them
### here, and pass them along.
### if so, then refactor the CHECKOUT method handling so we can reuse
### the code. maybe create a structure to hold CHECKOUT parameters
### which can be passed to the checkout() and merge() hooks. */
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* check for any method preconditions */
if (dav_run_method_precondition(r, source_resource, resource, doc, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
if (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
}
/* ### check the source and target resources flags/types */
/* ### do lock checks, once behavior is defined */
/* set the Cache-Control header, per the spec */
/* ### correct? */
apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
/* Initialize these values for a standard MERGE response. If the MERGE
is going to do something different (i.e. an error), then it must
return a dav_error, and we'll reset these values properly. */
r->status = HTTP_OK;
ap_set_content_type(r, "text/xml");
/* ### should we do any preliminary response generation? probably not,
### because we may have an error, thus demanding something else in
### the response body. */
/* Do the merge, including any response generation. */
if ((err = (*vsn_hooks->merge)(resource, source_resource,
no_auto_merge, no_checkout,
prop_elem,
r->output_filters)) != NULL) {
/* ### is err->status the right error here? */
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not MERGE resource \"%s\" " "into \"%s\".",
ap_escape_html(r->pool, source),
ap_escape_html(r->pool, r->uri)),
err);
return dav_handle_err(r, err, NULL);
}
/* the response was fully generated by the merge() hook. */
/* ### urk. does this prevent logging? need to check... */
return DONE;
}
/* If no bindings provider, decline the request */
if (binding_hooks == NULL)
return DECLINED;
/* Ask repository module to resolve the resource */
err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
/* check for any method preconditions */
if (dav_run_method_precondition(r, resource, NULL, NULL, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
if (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
}
/* get the destination URI */
dest = apr_table_get(r->headers_in, "Destination");
if (dest == NULL) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00620) "The request is missing a Destination header.");
return HTTP_BAD_REQUEST;
}
lookup = dav_lookup_uri(dest, r, 0 /* must_be_absolute */);
if (lookup.rnew == NULL) {
if (lookup.err.status == HTTP_BAD_REQUEST) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00621) "%s", lookup.err.desc);
return HTTP_BAD_REQUEST;
}
else if (lookup.err.status == HTTP_BAD_GATEWAY) {
/* ### Bindings protocol draft 02 says to return 507
* ### (Cross Server Binding Forbidden); Apache already defines 507
* ### as HTTP_INSUFFICIENT_STORAGE. So, for now, we'll return
* ### HTTP_FORBIDDEN
*/
return dav_error_response(r, HTTP_FORBIDDEN, "Cross server bindings are not " "allowed by this server.");
}
/* ### this assumes that dav_lookup_uri() only generates a status
* ### that Apache can provide a status line for!! */
return dav_error_response(r, lookup.err.status, lookup.err.desc);
}
if (lookup.rnew->status != HTTP_OK) {
/* ### how best to report this... */
return dav_error_response(r, lookup.rnew->status, "Destination URI had an error.");
}
/* check for any method preconditions */
if (dav_run_method_precondition(r, resource, binding, NULL, &err) != DECLINED
&& err) {
return dav_handle_err(r, err, NULL);
}
/* are the two resources handled by the same repository? */
if (resource->hooks != binding->hooks) {
/* ### this message exposes some backend config, but screw it... */
return dav_error_response(r, HTTP_BAD_GATEWAY, "Destination URI is handled by a " "different repository than the source URI. " "BIND between repositories is not possible.");
}
/* get and parse the overwrite header value */
if ((overwrite = dav_get_overwrite(r)) < 0) {
/* dav_get_overwrite() supplies additional information for the
* default message. */
return HTTP_BAD_REQUEST;
}
/* quick failure test: if dest exists and overwrite is false. */
if (binding->exists && !overwrite) {
return dav_error_response(r, HTTP_PRECONDITION_FAILED, "Destination is not empty and " "Overwrite is not \"T\"");
}
/* are the source and destination the same? */
if ((*resource->hooks->is_same_resource)(resource, binding)) {
return dav_error_response(r, HTTP_FORBIDDEN, "Source and Destination URIs are the same.");
}
/*
* Check If-Headers and existing locks for destination. Note that we
* use depth==infinity since the target (hierarchy) will be deleted
* before the move/copy is completed.
*
* Note that we are overwriting the target, which implies a DELETE, so
* we are subject to the error/response rules as a DELETE. Namely, we
* will return a424 error if any of the validations fail.
* (see dav_method_delete() for more information)
*/
if ((err = dav_validate_request(lookup.rnew, binding, DAV_INFINITY, NULL,
&multi_response,
DAV_VALIDATE_PARENT
| DAV_VALIDATE_USE_424, NULL)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not BIND %s due to a " "failed precondition on the " "destination (e.g. locks).",
ap_escape_html(r->pool, r->uri)),
err);
return dav_handle_err(r, err, multi_response);
}
/* guard against creating circular bindings */
if (resource->collection
&& (*resource->hooks->is_parent_resource)(resource, binding)) {
return dav_error_response(r, HTTP_FORBIDDEN, "Source collection contains the Destination.");
}
if (resource->collection
&& (*resource->hooks->is_parent_resource)(binding, resource)) {
/* The destination must exist (since it contains the source), and
* a condition above implies Overwrite==T. Obviously, we cannot
* delete the Destination before the BIND, as that would
* delete the Source.
*/
return dav_error_response(r, HTTP_FORBIDDEN, "Destination collection contains the Source and " "Overwrite has been specified.");
}
/* prepare the destination collection for modification */
if ((err = dav_auto_checkout(r, binding, 1 /* parent_only */,
&av_info)) != NULL) {
/* could not make destination writable */
return dav_handle_err(r, err, NULL);
}
/* If target exists, remove it first (we know Ovewrite must be TRUE).
* Then try to bind to the resource.
*/
if (binding->exists)
err = (*resource->hooks->remove_resource)(binding, &multi_response);
if (err == NULL) {
err = (*binding_hooks->bind_resource)(resource, binding);
}
/* check for error from remove/bind operations */
if (err != NULL) {
err = dav_push_error(r->pool, err->status, 0,
apr_psprintf(r->pool, "Could not BIND %s.",
ap_escape_html(r->pool, r->uri)),
err);
return dav_handle_err(r, err, multi_response);
}
/* check for errors from reverting writability */
if (err2 != NULL) {
/* just log a warning */
err = dav_push_error(r->pool, err2->status, 0, "The BIND was successful, but there was a " "problem automatically checking in the " "source parent collection.",
err2);
dav_log_err(r, err, APLOG_WARNING);
}
/* return an appropriate response (HTTP_CREATED) */
/* ### spec doesn't say what happens when destination was replaced */
return dav_created(r, lookup.rnew->unparsed_uri, "Binding", 0);
}
/*
* Response handler for DAV resources
*/
static int dav_handler(request_rec *r)
{
if (strcmp(r->handler, DAV_HANDLER_NAME) != 0)
return DECLINED;
/* Reject requests with an unescaped hash character, as these may
* be more destructive than the user intended. */
if (r->parsed_uri.fragment != NULL) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00622) "buggy client used un-escaped hash in Request-URI");
return dav_error_response(r, HTTP_BAD_REQUEST, "The request was invalid: the URI included " "an un-escaped hash character");
}
/* ### do we need to do anything with r->proxyreq ?? */
/*
* ### anything else to do here? could another module and/or
* ### config option"take over" the handler here? i.e. how do
* ### we lock down this hierarchy so that we are the ultimate
* ### arbiter? (or do we simply depend on the administrator
* ### to avoid conflicting configurations?)
*/
/*
* Set up the methods mask, since that's one of the reasons this handler
* gets called, and lower-level things may need the info.
*
* First, set the mask to the methods we handle directly. Since by
* definition we own our managed space, we unconditionally set
* the r->allowed field rather than ORing our values with anything
* any other module may have put in there.
*
* These are the HTTP-defined methods that we handle directly.
*/
r->allowed = 0
| (AP_METHOD_BIT << M_GET)
| (AP_METHOD_BIT << M_PUT)
| (AP_METHOD_BIT << M_DELETE)
| (AP_METHOD_BIT << M_OPTIONS)
| (AP_METHOD_BIT << M_INVALID);
/*
* These are methods that we don't handle directly, but let the
* server's default handler do for us as our agent.
*/
r->allowed |= 0
| (AP_METHOD_BIT << M_POST);
/* ### hrm. if we return HTTP_METHOD_NOT_ALLOWED, then an Allow header
* ### is sent; it will need the other allowed states; since the default
* ### handler is not called on error, then it doesn't add the other
* ### allowed states, so we must
*/
/* ### we might need to refine this for just where we return the error.
* ### also, there is the issue with other methods (see ISSUES)
*/
/* dispatch the appropriate method handler */
if (r->method_number == M_GET) {
return dav_method_get(r);
}
if (r->method_number == M_PUT) {
return dav_method_put(r);
}
if (r->method_number == M_POST) {
return dav_method_post(r);
}
if (r->method_number == M_DELETE) {
return dav_method_delete(r);
}
if (r->method_number == M_OPTIONS) {
return dav_method_options(r);
}
if (r->method_number == M_PROPFIND) {
return dav_method_propfind(r);
}
if (r->method_number == M_PROPPATCH) {
return dav_method_proppatch(r);
}
if (r->method_number == M_MKCOL) {
return dav_method_mkcol(r);
}
if (r->method_number == M_COPY) {
return dav_method_copymove(r, DAV_DO_COPY);
}
if (r->method_number == M_MOVE) {
return dav_method_copymove(r, DAV_DO_MOVE);
}
if (r->method_number == M_LOCK) {
return dav_method_lock(r);
}
if (r->method_number == M_UNLOCK) {
return dav_method_unlock(r);
}
if (r->method_number == M_VERSION_CONTROL) {
return dav_method_vsn_control(r);
}
if (r->method_number == M_CHECKOUT) {
return dav_method_checkout(r);
}
if (r->method_number == M_UNCHECKOUT) {
return dav_method_uncheckout(r);
}
if (r->method_number == M_CHECKIN) {
return dav_method_checkin(r);
}
if (r->method_number == M_UPDATE) {
return dav_method_update(r);
}
if (r->method_number == M_LABEL) {
return dav_method_label(r);
}
if (r->method_number == M_REPORT) {
return dav_method_report(r);
}
if (r->method_number == M_MKWORKSPACE) {
return dav_method_make_workspace(r);
}
if (r->method_number == M_MKACTIVITY) {
return dav_method_make_activity(r);
}
if (r->method_number == M_BASELINE_CONTROL) {
return dav_method_baseline_control(r);
}
if (r->method_number == M_MERGE) {
return dav_method_merge(r);
}
/* if DAV is not enabled, then we've got nothing to do */
if (conf->provider == NULL) {
return DECLINED;
}
/* We are going to handle almost every request. In certain cases,
the provider maps to the filesystem (thus, handle_get is
FALSE), and core Apache will handle it. a For that case, we
just return right away. */
if (r->method_number == M_GET) {
/*
* ### need some work to pull Content-Type and Content-Language
* ### from the property database.
*/
/*
* If the repository hasn't indicated that it will handle the
* GET method, then just punt.
*
* ### this isn't quite right... taking over the response can break
* ### things like mod_negotiation. need to look into this some more.
*/
if (!conf->provider->repos->handle_get) {
return DECLINED;
}
}
/* ### this is wrong. We should only be setting the r->handler for the
* requests that mod_dav knows about. If we set the handler for M_POST
* requests, then CGI scripts that use POST will return the source for the
* script. However, mod_dav DOES handle POST, so something else needs
* to be fixed.
*/
if (r->method_number != M_POST) {
/* We are going to be handling the response for this resource. */
r->handler = DAV_HANDLER_NAME;
return OK;
}
/*---------------------------------------------------------------------------
*
* Configuration info for the module
*/
static const command_rec dav_cmds[] =
{
/* per directory/location */
AP_INIT_TAKE1("DAV", dav_cmd_dav, NULL, ACCESS_CONF, "specify the DAV provider for a directory or location"),
/* per directory/location */
AP_INIT_TAKE1("DAVBasePath", dav_cmd_davbasepath, NULL, ACCESS_CONF, "specify the DAV repository base URL"),
/* per directory/location, or per server */
AP_INIT_TAKE1("DAVMinTimeout", dav_cmd_davmintimeout, NULL,
ACCESS_CONF|RSRC_CONF, "specify minimum allowed timeout"),
/* per directory/location, or per server */
AP_INIT_FLAG("DAVDepthInfinity", dav_cmd_davdepthinfinity, NULL,
ACCESS_CONF|RSRC_CONF, "allow Depth infinity PROPFIND requests"),
/* per directory/location, or per server */
AP_INIT_FLAG("DAVLockDiscovery", dav_cmd_davlockdiscovery, NULL,
ACCESS_CONF|RSRC_CONF, "allow lock discovery by PROPFIND requests"),
{ NULL }
};
module DAV_DECLARE_DATA dav_module =
{
STANDARD20_MODULE_STUFF,
dav_create_dir_config, /* dir config creater */
dav_merge_dir_config, /* dir merger --- default is to override */
dav_create_server_config, /* server config */
dav_merge_server_config, /* merge server config */
dav_cmds, /* command table */
register_hooks, /* register hooks */
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.