Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/unitlib/data/128/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 12.5.2025 mit Größe 5 kB image not shown  

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* */

#define APR_WANT_STRFUNC
#include "apr_want.h"

#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_log.h"
#include "http_main.h"
#include "http_protocol.h"
#include "http_request.h"
#include "util_script.h"

#include "mod_dav.h"

#include "ap_provider.h"


/* ### what is the best way to set this? */
#define DAV_DEFAULT_PROVIDER    "filesystem"

/* used to denote that mod_dav will be handling this request */
#define DAV_HANDLER_NAME "dav-handler"

APLOG_USE_MODULE(dav);

enum {
    DAV_ENABLED_UNSET = 0,
    DAV_ENABLED_OFF,
    DAV_ENABLED_ON
};

/* per-dir configuration */
typedef struct {
    const char *provider_name;
    const dav_provider *provider;
    const char *dir;
    const char *base;
    int locktimeout;
    int allow_depthinfinity;
    int allow_lockdiscovery;

} dav_dir_conf;

/* per-server configuration */
typedef struct {
    int unused;

} dav_server_conf;

#define DAV_INHERIT_VALUE(parent, child, field) \
                ((child)->field ? (child)->field : (parent)->field)


/* forward-declare for use in configuration lookup */
extern module DAV_DECLARE_DATA dav_module;

/* DAV methods */
enum {
    DAV_M_BIND = 0,
    DAV_M_SEARCH,
    DAV_M_LAST
};
static int dav_methods[DAV_M_LAST];


static int dav_init_handler(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
                             server_rec *s)
{
    /* DBG0("dav_init_handler"); */

    /* Register DAV methods */
    dav_methods[DAV_M_BIND] = ap_method_register(p, "BIND");
    dav_methods[DAV_M_SEARCH] = ap_method_register(p, "SEARCH");

    return OK;
}

static void *dav_create_server_config(apr_pool_t *p, server_rec *s)
{
    dav_server_conf *newconf;

    newconf = (dav_server_conf *)apr_pcalloc(p, sizeof(*newconf));

    /* ### this isn't used at the moment... */

    return newconf;
}

static void *dav_merge_server_config(apr_pool_t *p, void *base, void *overrides)
{
#if 0
    dav_server_conf *child = overrides;
#endif
    dav_server_conf *newconf;

    newconf = (dav_server_conf *)apr_pcalloc(p, sizeof(*newconf));

    /* ### nothing to merge right now... */

    return newconf;
}

static void *dav_create_dir_config(apr_pool_t *p, char *dir)
{
    /* NOTE: dir==NULL creates the default per-dir config */

    dav_dir_conf *conf;

    conf = (dav_dir_conf *)apr_pcalloc(p, sizeof(*conf));

    /* clean up the directory to remove any trailing slash */
    if (dir != NULL) {
        char *d;
        apr_size_t l;

        l = strlen(dir);
        d = apr_pstrmemdup(p, dir, l);
        if (l > 1 && d[l - 1] == '/')
            d[l - 1] = '\0';
        conf->dir = d;
    }

    return conf;
}

static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
{
    dav_dir_conf *parent = base;
    dav_dir_conf *child = overrides;
    dav_dir_conf *newconf = (dav_dir_conf *)apr_pcalloc(p, sizeof(*newconf));

    /* DBG3("dav_merge_dir_config: new=%08lx  base=%08lx  overrides=%08lx",
       (long)newconf, (long)base, (long)overrides); */


    newconf->provider_name = DAV_INHERIT_VALUE(parent, child, provider_name);
    newconf->provider = DAV_INHERIT_VALUE(parent, child, provider);
    if (parent->provider_name != NULL) {
        if (child->provider_name == NULL) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00578)
                         "\"DAV Off\" cannot be used to turn off a subtree "
                         "of a DAV-enabled location.");
        }
        else if (strcasecmp(child->provider_name,
                            parent->provider_name) != 0) {
            ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00579)
                         "A subtree cannot specify a different DAV provider "
                         "than its parent.");
        }
    }

    newconf->locktimeout = DAV_INHERIT_VALUE(parent, child, locktimeout);
    newconf->dir = DAV_INHERIT_VALUE(parent, child, dir);
    newconf->base = DAV_INHERIT_VALUE(parent, child, base);
    newconf->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child,
                                                     allow_depthinfinity);
    newconf->allow_lockdiscovery = DAV_INHERIT_VALUE(parent, child,
                                                     allow_lockdiscovery);

    return newconf;
}

DAV_DECLARE(const char *) dav_get_provider_name(request_rec *r)
{
    dav_dir_conf *conf = ap_get_module_config(r->per_dir_config, &dav_module);
    return conf ? conf->provider_name : NULL;
}

DAV_DECLARE(const dav_provider *) dav_get_provider(request_rec *r)
{
    dav_dir_conf *conf;

    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;
}

DAV_DECLARE(const dav_hooks_locks *) dav_get_lock_hooks(request_rec *r)
{
    return dav_get_provider(r)->locks;
}

DAV_DECLARE(const dav_hooks_propdb *) dav_get_propdb_hooks(request_rec *r)
{
    return dav_get_provider(r)->propdb;
}

DAV_DECLARE(const dav_hooks_vsn *) dav_get_vsn_hooks(request_rec *r)
{
    return dav_get_provider(r)->vsn;
}

DAV_DECLARE(const dav_hooks_binding *) dav_get_binding_hooks(request_rec *r)
{
    return dav_get_provider(r)->binding;
}

DAV_DECLARE(const dav_hooks_search *) dav_get_search_hooks(request_rec *r)
{
    return dav_get_provider(r)->search;
}

DAV_DECLARE(const char *) dav_get_base_path(request_rec *r)
{
    dav_dir_conf *conf = ap_get_module_config(r->per_dir_config, &dav_module);

    return conf && conf->base ? conf->base : NULL;
}

/*
 * Command handler for the DAV directive, which is TAKE1.
 */

static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
{
    dav_dir_conf *conf = (dav_dir_conf *)config;

    if (strcasecmp(arg1, "on") == 0) {
        conf->provider_name = DAV_DEFAULT_PROVIDER;
    }
    else if (strcasecmp(arg1, "off") == 0) {
        conf->provider_name = NULL;
        conf->provider = NULL;
    }
    else {
        conf->provider_name = arg1;
    }

    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
 */

static const char *dav_cmd_davbasepath(cmd_parms *cmd, void *config, const char *arg1)
{
    dav_dir_conf *conf = config;

    conf->base = arg1;

    return NULL;
}

/*
 * Command handler for the DAVDepthInfinity directive, which is FLAG.
 */

static const char *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config,
                                            int arg)
{
    dav_dir_conf *conf = (dav_dir_conf *)config;

    if (arg)
        conf->allow_depthinfinity = DAV_ENABLED_ON;
    else
        conf->allow_depthinfinity = DAV_ENABLED_OFF;
    return NULL;
}

/*
 * Command handler for the DAVLockDiscovery directive, which is FLAG.
 */

static const char *dav_cmd_davlockdiscovery(cmd_parms *cmd, void *config,
                                            int arg)
{
    dav_dir_conf *conf = (dav_dir_conf *)config;

    if (arg)
        conf->allow_lockdiscovery = DAV_ENABLED_ON;
    else
        conf->allow_lockdiscovery = DAV_ENABLED_OFF;
    return NULL;
}

/*
 * Command handler for DAVMinTimeout directive, which is TAKE1
 */

static const char *dav_cmd_davmintimeout(cmd_parms *cmd, void *config,
                                         const char *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)
*/

static int dav_error_response(request_rec *r, int status, const char *body)
{
    r->status = status;
    r->status_line = ap_get_status_line(status);

    ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);

    /* 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
 */

static int dav_error_response_tag(request_rec *r,
                                  dav_error *err)
{
    r->status = err->status;

    ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);

    ap_rputs(DAV_XML_HEADER DEBUG_CR
             "DAV:\"", r);

    if (err->desc != NULL) {
        /* ### should move this namespace somewhere (with the others!) */
        ap_rputs(" xmlns:m=\"http://apache.org/dav/xmlns\"", r);
    }

    if (err->childtags) {
        if (err->namespace != NULL) {
            ap_rprintf(r,
                    " xmlns:C=\"%s\">" DEBUG_CR
                    "%s" DEBUG_CR,
                    err->namespace,
                    err->tagname, err->childtags, err->tagname);
        }
        else {
            ap_rprintf(r,
                    ">" DEBUG_CR
                    "%s" DEBUG_CR,
                    err->tagname, err->childtags, err->tagname);
        }
    }
    else {
        if (err->namespace != NULL) {
            ap_rprintf(r,
                    " xmlns:C=\"%s\">" DEBUG_CR
                    "" DEBUG_CR,
                    err->namespace, err->tagname);
        }
        else {
            ap_rprintf(r,
                    ">" DEBUG_CR
                    "" DEBUG_CR, err->tagname);
        }
    }

    /* here's our mod_dav specific tag: */
    if (err->desc != NULL) {
        ap_rprintf(r,
                   "%d\">" DEBUG_CR
                   "%s" DEBUG_CR
                   "
"
 DEBUG_CR,
                   err->error_id,
                   apr_xml_quote_string(r->pool, err->desc, 0));
    }

    ap_rputs("
"
 DEBUG_CR, r);

    /* 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.
 */

static const char *dav_xml_escape_uri(apr_pool_t *p, const char *uri)
{
    const char *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.xmlns == NULL) {
      ap_fputs(r->output_filters, bb, "");
    }
    else {
      ap_fputs(r->output_filters, bb, ");
      for (t = response->propresult.xmlns; t; t = t->next) {
        ap_fputs(r->output_filters, bb, t->text);
      }
      ap_fputc(r->output_filters, bb, '>');
    }

    ap_fputstrs(r->output_filters, bb,
                DEBUG_CR "",
                dav_xml_escape_uri(pool, response->href),
                "" DEBUG_CR,
                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);
}

DAV_DECLARE(void) dav_send_multistatus(request_rec *r, int status,
                                       dav_response *first,
                                       apr_array_header_t *namespaces)
{
    apr_pool_t *subpool;
    apr_bucket_brigade *bb = apr_brigade_create(r->pool,
                                                r->connection->bucket_alloc);

    dav_begin_multistatus(bb, r, status, namespaces);

    apr_pool_create(&subpool, r->pool);
    apr_pool_tag(subpool, "mod_dav-multistatus");

    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.
 */

static void 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;

        /* Intentional no APLOGNO */
        ap_log_rerror(APLOG_MARK, level, errscan->aprerr, r, "%s [%d, #%d]",
                      errscan->desc, errscan->status, errscan->error_id);
    }
}

/*
 * 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;

        if (stackerr != NULL && stackerr->tagname != NULL)
            return dav_error_response_tag(r, stackerr);

        return err->status;
    }

    /* 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. */

static int dav_created(request_rec *r, const char *locn, const char *what,
                       int replaced)
{
    const char *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)
{
    const char *depth = apr_table_get(r->headers_in, "Depth");

    if (depth == NULL) {
        return def_depth;
    }

    if (ap_cstr_casecmp(depth, "infinity") == 0) {
        return DAV_INFINITY;
    }
    else if (strcmp(depth, "0") == 0) {
        return 0;
    }
    else if (strcmp(depth, "1") == 0) {
        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(00580)
                  "An invalid Depth header was specified.");
    return -1;
}

static int dav_get_overwrite(request_rec *r)
{
    const char *overwrite = apr_table_get(r->headers_in, "Overwrite");

    if (overwrite == NULL) {
        return 1; /* default is "T" */
    }

    if ((*overwrite == 'F' || *overwrite == 'f') && overwrite[1] == '\0') {
        return 0;
    }

    if ((*overwrite == 'T' || *overwrite == 't') && overwrite[1] == '\0') {
        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;
    const char *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?");
            }
        }

        err = dav_push_error(r->pool, err->status, 0,
                             "Could not fetch resource information.", err);
        return err;
    }

    /* 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);

    return NULL;
}

DAV_DECLARE(dav_error *) dav_open_lockdb(request_rec *r,
                                         int ro,
                                         dav_lockdb **lockdb)
{
    const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);

    if (hooks == NULL) {
        *lockdb = NULL;
        return NULL;
    }

    /* open the thing lazily */
    return (*hooks->open_lockdb)(r, ro, 0, lockdb);
}

DAV_DECLARE(void) dav_close_lockdb(dav_lockdb *lockdb)
{
    (lockdb->hooks->close_lockdb)(lockdb);
}

/**
 * @return  1 if valid content-range,
 *          0 if no content-range,
 *         -1 if malformed content-range
 */

static int dav_parse_range(request_rec *r,
                           apr_off_t *range_start, apr_off_t *range_end)
{
    const char *range_c;
    char *range;
    char *dash;
    char *slash;

    range_c = apr_table_get(r->headers_in, "content-range");
    if (range_c == NULL)
        return 0;

    range = apr_pstrdup(r->pool, range_c);
    if (ap_cstr_casecmpn(range, "bytes ", 6) != 0
        || (dash = ap_strchr(range + 6, '-')) == NULL
        || (slash = ap_strchr(range + 6, '/')) == NULL) {
        /* malformed header */
        return -1;
    }

    *dash++ = *slash++ = '\0';

    /* detect invalid ranges */
    if (!ap_parse_strict_length(range_start, range + 6)) {
        return -1;
    }
    if (!ap_parse_strict_length(range_end, dash)
            || *range_end < *range_start) {
        return -1;
    }

    if (*slash != '*') {
        apr_off_t dummy;

        if (!ap_parse_strict_length(&dummy, slash)
                || dummy <= *range_end) {
            return -1;
        }
    }

    /* we now have a valid range */
    return 1;
}

/* handle the GET method */
static int 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 */
static int 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 */
static int 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);
    const char *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.");

    }

    resource_state = dav_get_resource_state(r, resource);

    /*
     * 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);
    } else if (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;

        bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);

        do {
            apr_status_t rc;

            rc = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
                                APR_BLOCK_READ, DAV_READ_BLOCKSIZE);

            if (rc != APR_SUCCESS) {
                int http_err;
                char *msg = ap_escape_html(r->pool, r->uri);
                http_err = ap_map_http_request_error(rc, HTTP_BAD_REQUEST);
                msg = apr_psprintf(r->pool, "An error occurred while reading "
                                            "the request body (URI: %s)",
                                            msg);
                err = dav_new_error(r->pool, http_err, 0, rc, msg);
                break;
            }

            for (b = APR_BRIGADE_FIRST(bb);
                 b != APR_BRIGADE_SENTINEL(bb);
                 b = APR_BUCKET_NEXT(b))
            {
                const char *data;
                apr_size_t len;

                if (APR_BUCKET_IS_EOS(b)) {
                    seen_eos = 1;
                    break;
                }

                if (APR_BUCKET_IS_METADATA(b)) {
                    continue;
                }

                if (err == NULL) {
                    /* write whatever we read, until we see an error */
                    rc = apr_bucket_read(b, &data, &len, APR_BLOCK_READ);
                    if (rc != APR_SUCCESS) {
                       err = dav_new_error(r->pool, HTTP_BAD_REQUEST, 0, rc,
                                           apr_psprintf(r->pool,
                                                        "An error occurred while"
                                                        " reading the request body"
                                                        " from the bucket (URI: %s)",
                                                        ap_escape_html(r->pool, r->uri)));
                        break;
                    }

                    err = (*resource->hooks->write_stream)(stream, data, len);
                }
            }

            apr_brigade_cleanup(bb);
        } while (!seen_eos);

        apr_brigade_destroy(bb);

        err2 = (*resource->hooks->close_stream)(stream,
                                                err == NULL /* commit */);
        err = dav_join_error(err, err2);
    }

    /*
     * 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. */

static void 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;

    resp.href = wres->resource->uri;
    resp.status = status;
    if (propstats) {
        resp.propresult = *propstats;
    }

    dav_send_one_response(&resp, ctx->bb, ctx->r, pool);
}


/* ### 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;
    }

    resp->next = wres->response;
    wres->response = resp;
}


/* handle the DELETE method */
static int 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;
}

/* generate DAV:supported-method-set OPTIONS response */
static dav_error *dav_gen_supported_methods(request_rec *r,
                                            const apr_xml_elem *elem,
                                            const apr_table_t *methods,
                                            apr_text_header *body)
{
    const apr_array_header_t *arr;
    const apr_table_entry_t *elts;
    apr_xml_elem *child;
    apr_xml_attr *attr;
    char *s;
    int i;

    apr_text_append(r->pool, body, "" DEBUG_CR);

    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) {
                const char *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);
                }
            }
        }
    }

    apr_text_append(r->pool, body, "" DEBUG_CR);
    return NULL;
}

/* generate DAV:supported-live-property-set OPTIONS response */
static dav_error *dav_gen_supported_live_props(request_rec *r,
                                               const dav_resource *resource,
                                               const apr_xml_elem *elem,
                                               apr_text_header *body)
{
    dav_lockdb *lockdb;
    dav_propdb *propdb;
    apr_xml_elem *child;
    apr_xml_attr *attr;
    dav_error *err;

    /* 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) {
                const char *name = NULL;
                const char *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;
                        else if (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);

    return err;
}


/* generate DAV:supported-report-set OPTIONS response */
static dav_error *dav_gen_supported_reports(request_rec *r,
                                            const dav_resource *resource,
                                            const apr_xml_elem *elem,
                                            apr_text_header *body)
{
    apr_xml_elem *child;
    apr_xml_attr *attr;
    dav_error *err = NULL;
    char *s;
    apr_array_header_t *reports;
    const dav_report_elem *rp;

    apr_text_append(r->pool, body, "" DEBUG_CR);

    reports = apr_array_make(r->pool, 5, sizeof(const char *));
    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) {
                const char *name = NULL;
                const char *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;
                        else if (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;
                    }
                }
            }
        }
    }

    apr_text_append(r->pool, body, "" DEBUG_CR);
    return NULL;
}


/* handle the SEARCH method */
static int dav_method_search(request_rec *r)
{
    const dav_hooks_search *search_hooks = DAV_GET_HOOKS_SEARCH(r);
    dav_resource *resource;
    dav_error *err;
    dav_response *multi_status;

    /* 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);

    return DONE;
}


/* handle the OPTIONS method */
static int dav_method_options(request_rec *r)
{
    const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
    const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r);
    const dav_hooks_search *search_hooks = DAV_GET_HOOKS_SEARCH(r);
    dav_resource *resource;
    const char *dav_level;
    char *allow;
    char *s;
    const apr_array_header_t *arr;
    const apr_table_entry_t *elts;
    apr_table_t *methods = apr_table_make(r->pool, 12);
    apr_text_header vsn_options = { 0 };
    apr_text_header body = { 0 };
    apr_text *t;
    int text_size;
    int result;
    int i;
    apr_array_header_t *uri_ary;
    apr_xml_doc *doc;
    const apr_xml_elem *elem;
    dav_error *err;

    apr_array_header_t *extensions;
    ap_list_provider_names_t *entry;

    /* 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 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);

    /* DAV header additions registered by external modules */
    extensions = ap_list_provider_names(r->pool, DAV_OPTIONS_EXTENSION_GROUP, "0");
    entry = (ap_list_provider_names_t *)extensions->elts;

    for (i = 0; i < extensions->nelts; i++, entry++) {
        const dav_options_provider *options =
            dav_get_options_providers(entry->provider_name);

        if (options && options->dav_header) {
            apr_text_header hoptions = { 0 };

            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(const char *));
    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.
     */


    apr_table_addn(methods, "OPTIONS""");

    /* ### take into account resource type */
    switch (dav_get_resource_state(r, resource))
    {
    case DAV_RESOURCE_EXISTS:
        /* resource exists */
        apr_table_addn(methods, "GET""");
        apr_table_addn(methods, "HEAD""");
        apr_table_addn(methods, "POST""");
        apr_table_addn(methods, "DELETE""");
        apr_table_addn(methods, "TRACE""");
        apr_table_addn(methods, "PROPFIND""");
        apr_table_addn(methods, "PROPPATCH""");
        apr_table_addn(methods, "COPY""");
        apr_table_addn(methods, "MOVE""");

        if (!resource->collection)
            apr_table_addn(methods, "PUT""");

        if (locks_hooks != NULL) {
            apr_table_addn(methods, "LOCK""");
            apr_table_addn(methods, "UNLOCK""");
        }

        break;

    case DAV_RESOURCE_LOCK_NULL:
        /* resource is lock-null. */
        apr_table_addn(methods, "MKCOL""");
        apr_table_addn(methods, "PROPFIND""");
        apr_table_addn(methods, "PUT""");

        if (locks_hooks != NULL) {
            apr_table_addn(methods, "LOCK""");
            apr_table_addn(methods, "UNLOCK""");
        }

        break;

    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""");

            if (vsn_hooks->can_be_activity != NULL
                && (*vsn_hooks->can_be_activity)(resource))
                apr_table_addn(methods, "MKACTIVITY""");
        }
        else if (!resource->versioned) {
            if ((*vsn_hooks->versionable)(resource))
                apr_table_addn(methods, "VERSION-CONTROL""");
        }
        else if (resource->working) {
            apr_table_addn(methods, "CHECKIN""");

            /* ### we might not support this DeltaV option */
            apr_table_addn(methods, "UNCHECKOUT""");
        }
        else if (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""");
    }

    /* additional methods registered by external modules */
    extensions = ap_list_provider_names(r->pool, DAV_OPTIONS_EXTENSION_GROUP, "0");
    entry = (ap_list_provider_names_t *)extensions->elts;

    for (i = 0; i < extensions->nelts; i++, entry++) {
        const dav_options_provider *options =
            dav_get_options_providers(entry->provider_name);

        if (options && options->dav_method) {
            apr_text_header hoptions = { 0 };

            options->dav_method(r, resource, &hoptions);
            for (t = hoptions.first; t && t->text; t = t->next)
                apr_table_addn(methods, t->text, "");
        }
    }

    /* Generate the Allow header */
    arr = apr_table_elts(methods);
    elts = (const apr_table_entry_t *)arr->elts;
    text_size = 0;

    /* 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) {
--> --------------------

--> maximum size reached

--> --------------------

91%


¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.67Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.