📄 mod_dav.c
字号:
/* 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 + -