Impressum mod_dav.c
Interaktion und PortierbarkeitC
/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * DAV extension module for Apache 2.0.* * * This module is repository-independent. It depends on hooks provided by a * repository implementation. * * APACHE ISSUES: * - within a DAV hierarchy, if an unknown method is used and we default * to Apache's implementation, it sends back an OPTIONS with the wrong * set of methods -- there is NO HOOK for us. * therefore: we need to manually handle the HTTP_METHOD_NOT_ALLOWED * and HTTP_NOT_IMPLEMENTED responses (not ap_send_error_response). * - process_mkcol_body() had to dup code from ap_setup_client_block(). * - it would be nice to get status lines from Apache for arbitrary * status codes * - it would be nice to be able to extend Apache's set of response * codes so that it doesn't return 500 when an unknown code is placed * into r->status. * - http_vhost functions should apply "const" to their params * * DESIGN NOTES: * - For PROPFIND, we batch up the entire response in memory before * sending it. We may want to reorganize around sending the information * as we suck it in from the propdb. Alternatively, we should at least * generate a total Content-Length if we're going to buffer in memory * so that we can keep the connection open.
*/
#include"apr_strings.h" #include"apr_lib.h"/* for apr_is* */
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);
}
}
return NULL;
}
/* * Command handler for the DAVBasePath directive, which is TAKE1
*/ staticconstchar *dav_cmd_davbasepath(cmd_parms *cmd, void *config, constchar *arg1)
{
dav_dir_conf *conf = config;
conf->base = arg1;
return NULL;
}
/* * Command handler for the DAVDepthInfinity directive, which is FLAG.
*/ staticconstchar *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config, int arg)
{
dav_dir_conf *conf = (dav_dir_conf *)config;
/* * Command handler for the DAVLockDiscovery directive, which is FLAG.
*/ staticconstchar *dav_cmd_davlockdiscovery(cmd_parms *cmd, void *config, int arg)
{
dav_dir_conf *conf = (dav_dir_conf *)config;
/* * Command handler for DAVMinTimeout directive, which is TAKE1
*/ staticconstchar *dav_cmd_davmintimeout(cmd_parms *cmd, void *config, constchar *arg1)
{
dav_dir_conf *conf = (dav_dir_conf *)config;
conf->locktimeout = atoi(arg1); if (conf->locktimeout < 0) return"DAVMinTimeout requires a non-negative integer.";
return NULL;
}
/* ** dav_error_response() ** ** Send a nice response back to the user. In most cases, Apache doesn't ** allow us to provide details in the body about what happened. This ** function allows us to completely specify the response body. ** ** ### this function is not logging any errors! (e.g. the body)
*/ staticint dav_error_response(request_rec *r, int status, constchar *body)
{
r->status = status;
r->status_line = ap_get_status_line(status);
/* begin the response now... */
ap_rvputs(r,
DAV_RESPONSE_BODY_1,
r->status_line,
DAV_RESPONSE_BODY_2,
&r->status_line[4],
DAV_RESPONSE_BODY_3,
body,
DAV_RESPONSE_BODY_4,
ap_psignature("\n", r),
DAV_RESPONSE_BODY_5,
NULL);
/* the response has been sent. */ /* * ### Use of DONE obviates logging..!
*/ return DONE;
}
/* * Send a "standardized" error response based on the error's namespace & tag
*/ staticint dav_error_response_tag(request_rec *r,
dav_error *err)
{
r->status = err->status;
/* the response has been sent. */ /* * ### Use of DONE obviates logging..!
*/ return DONE;
}
/* * Apache's URI escaping does not replace '&' since that is a valid character * in a URI (to form a query section). We must explicitly handle it so that * we can embed the URI into an XML document.
*/ staticconstchar *dav_xml_escape_uri(apr_pool_t *p, constchar *uri)
{ constchar *e_uri = ap_escape_uri(p, uri);
/* check the easy case... */ if (ap_strchr_c(e_uri, '&') == NULL) return e_uri;
/* there was a '&', so more work is needed... sigh. */
/* * Note: this is a teeny bit of overkill since we know there are no * '<' or '>' characters, but who cares.
*/ return apr_xml_quote_string(p, e_uri, 0);
}
/* Write a complete RESPONSE object out as a <DAV:response> xml element. Data is sent into brigade BB, which is auto-flushed into the output filter stack for request R. Use POOL for any temporary allocations.
[Presumably the <multistatus> tag has already been written; this routine is shared by dav_send_multistatus and dav_stream_response.]
*/
DAV_DECLARE(void) dav_send_one_response(dav_response *response,
apr_bucket_brigade *bb,
request_rec *r,
apr_pool_t *pool)
{
apr_text *t = NULL;
if (response->propresult.propstats == NULL) { /* use the Status-Line text from Apache. Note, this will * default to 500 Internal Server Error if first->status * is not a known (or valid) status code.
*/
ap_fputstrs(r->output_filters, bb, "HTTP/1.1 ",
ap_get_status_line(response->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);
}
}
if (response->desc != NULL) { /* * We supply the description, so we know it doesn't have to * have any escaping/encoding applied to it.
*/
ap_fputstrs(r->output_filters, bb, "",
response->desc, "" DEBUG_CR,
NULL);
}
ap_fputs(r->output_filters, bb, "" DEBUG_CR);
}
/* Factorized helper function: prep request_rec R for a multistatus response and write <multistatus> tag into BB, destined for R->output_filters. Use xml NAMESPACES in initial tag, 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 "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;
ap_fputs(r->output_filters, bb, "" DEBUG_CR);
/* indicate the end of the response body */
b = apr_bucket_eos_create(r->connection->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
/* deliver whatever might be remaining in the brigade */ return ap_pass_brigade(r->output_filters, bb);
}
for (; first != NULL; first = first->next) {
apr_pool_clear(subpool);
dav_send_one_response(first, bb, r, subpool);
}
apr_pool_destroy(subpool);
dav_finish_multistatus(r, bb);
}
/* * dav_log_err() * * Write error information to the log.
*/ staticvoid dav_log_err(request_rec *r, dav_error *err, int level)
{
dav_error *errscan;
/* 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;
/* * dav_handle_err() * * Handle the standard error processing. <err> must be non-NULL. * * <response> is set by the following: * - dav_validate_request() * - dav_add_lock() * - repos_hooks->remove_resource * - repos_hooks->move_resource * - repos_hooks->copy_resource * - vsn_hooks->update
*/
DAV_DECLARE(int) dav_handle_err(request_rec *r, dav_error *err,
dav_response *response)
{ /* log the errors */
dav_log_err(r, err, APLOG_ERR);
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 might be able to generate a standard <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. * * If label_allowed != 0, then allow the request target to be altered by * a Label: header. * * If use_checked_in is true, then the repository provider should return * the resource identified by the DAV:checked-in property of the resource * identified by the Request-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 * visible to Apache. We will fetch the resource from the repository, * then create a subrequest for Apache to handle.
*/
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.");
/* * Note: depth == 0 normally requires no multistatus response. However, * if we pass DAV_VALIDATE_PARENT, then we could get an error on a URI * other than the Request-URI, thereby requiring a multistatus. * * If the resource does not exist (DAV_RESOURCE_NULL), then we must * check the resource *and* its parent. If the resource exists or is * a locknull resource, then we check only the resource.
*/ if ((err = dav_validate_request(r, resource, 0, NULL, &multi_response,
resource_state == DAV_RESOURCE_NULL ?
DAV_VALIDATE_PARENT :
DAV_VALIDATE_RESOURCE, NULL)) != NULL) { /* ### add a higher-level description? */ return dav_handle_err(r, err, multi_response);
}
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 * not use the content.
*/
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;
/* * Ensure that we think the resource exists now. * ### eek. if an error occurred during the write and we did not commit, * ### then the resource might NOT exist (e.g. dav_fs_repos.c)
*/ if (err == NULL) {
resource->exists = 1;
}
/* 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. * For non-collections, depth is ignored, unless it is an illegal value (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;
}
/* ** If any resources fail the lock/If: conditions, then we must fail ** the delete. Each of the failing resources will be listed within ** a DAV:multistatus body, wrapped into a 424 response. ** ** Note that a failure on the resource itself does not generate a ** multistatus response -- only internal members/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 * locked by the token(s) in the if_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, "",
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, "",
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);
}
apr_text_append(r->pool, body, "" DEBUG_CR);
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);
}
}
}
apr_text_append(r->pool, body, "" DEBUG_CR);
dav_close_propdb(propdb);
if (lockdb != NULL)
(*lockdb->hooks->close_lockdb)(lockdb);
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, "",
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 * properly XML/URL quoted
*/
s = apr_pstrcat(r->pool, " "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 * visible to Apache. We will fetch the resource from the repository, * then create a subrequest for Apache to handle.
*/
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 * in the hook function
*/ 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);
options->dav_header(r, resource, &hoptions); for (t = hoptions.first; t && t->text; t = t->next)
dav_level = apr_pstrcat(r->pool, dav_level, ",", t->text, NULL);
}
}
/* ### * MSFT Web Folders chokes if length of DAV header value > 63 characters! * To workaround that, we use separate DAV headers for versioning and * live prop provider namespace URIs. * ###
*/
apr_table_setn(r->headers_out, "DAV", dav_level);
/* * If there is a versioning provider, generate DAV headers * for versioning options.
*/ if (vsn_hooks != NULL) {
(*vsn_hooks->get_vsn_options)(r->pool, &vsn_options);
for (t = vsn_options.first; t != NULL; t = t->next)
apr_table_addn(r->headers_out, "DAV", t->text);
}
/* * Gather property set URIs from all the liveprop providers, * and generate a separate DAV header for each URI, to avoid * problems with long header lengths.
*/
uri_ary = apr_array_make(r->pool, 5, sizeof(constchar *));
dav_run_gather_propsets(uri_ary); for (i = 0; i < uri_ary->nelts; ++i) { if (((char **)uri_ary->elts)[i] != NULL)
apr_table_addn(r->headers_out, "DAV", ((char **)uri_ary->elts)[i]);
}
/* this tells MSFT products to skip looking for FrontPage extensions */
apr_table_setn(r->headers_out, "MS-Author-Via", "DAV");
/* * Determine which methods are allowed on the resource. * Three cases: resource is null (3), is lock-null (7.4), or exists. * * All cases support OPTIONS, and if there is a lock provider, LOCK. * (Lock-) null resources also support MKCOL and PUT. * Lock-null supports PROPFIND and UNLOCK. * Existing resources support lots of stuff.
*/
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", "");
}
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 ist noch experimentell.