📄 mod_dav.c
字号:
/* Copyright 2000-2005 The Apache Software Foundation or its licensors, as * applicable. * * Licensed 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"); /* 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, apr_xml_quote_string(r->pool, err->desc, 0)); } ap_rputs("</D:error>" DEBUG_CR, r); /* the response has been sent. */ /* * ### Use of DONE obviates logging..! */ return DONE;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -