⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mod_dav.c

📁 Apache 2.0.63 is the current stable version of the 2.0 series, and is recommended over any previous
💻 C
📖 第 1 页 / 共 5 页
字号:
/* 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"


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

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;
    int locktimeout;
    int allow_depthinfinity;

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

    ap_add_version_component(p, "DAV/2");

    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;

        d = apr_pstrdup(p, dir);
        l = strlen(d);
        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,
                         "\"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,
                         "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->allow_depthinfinity = DAV_INHERIT_VALUE(parent, child,
                                                     allow_depthinfinity);

    return newconf;
}

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

/*
 * 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 = apr_pstrdup(cmd->pool, 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 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 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;

    /* ### I really don't think this is needed; gotta test */
    r->status_line = ap_get_status_line(status);

    ap_set_content_type(r, "text/html; charset=ISO-8859-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("<hr />\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;

    /* ### I really don't think this is needed; gotta test */
    r->status_line = ap_get_status_line(err->status);

    ap_set_content_type(r, DAV_XML_CONTENT_TYPE);

    ap_rputs(DAV_XML_HEADER DEBUG_CR
             "<D:error xmlns:D=\"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->namespace != NULL) {
        ap_rprintf(r,
                   " xmlns:C=\"%s\">" DEBUG_CR
                   "<C:%s/>" DEBUG_CR,
                   err->namespace, err->tagname);
    }
    else {
        ap_rprintf(r,
                   ">" DEBUG_CR
                   "<D:%s/>" DEBUG_CR, err->tagname);
    }

    /* here's our mod_dav specific tag: */
    if (err->desc != NULL) {
        ap_rprintf(r,
                   "<m:human-readable errcode=\"%d\">" DEBUG_CR
                   "%s" DEBUG_CR
                   "</m:human-readable>" DEBUG_CR,
                   err->error_id,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -