summaryrefslogtreecommitdiffstats
path: root/modules/dav/main
diff options
context:
space:
mode:
Diffstat (limited to 'modules/dav/main')
-rw-r--r--modules/dav/main/mod_dav.c635
-rw-r--r--modules/dav/main/mod_dav.h132
-rw-r--r--modules/dav/main/props.c88
-rw-r--r--modules/dav/main/std_liveprop.c8
-rw-r--r--modules/dav/main/util.c87
5 files changed, 754 insertions, 196 deletions
diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c
index 84012f2..dea3f18 100644
--- a/modules/dav/main/mod_dav.c
+++ b/modules/dav/main/mod_dav.c
@@ -81,8 +81,10 @@ 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;
@@ -195,8 +197,11 @@ static void *dav_merge_dir_config(apr_pool_t *p, void *base, void *overrides)
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;
}
@@ -207,7 +212,7 @@ DAV_DECLARE(const char *) dav_get_provider_name(request_rec *r)
return conf ? conf->provider_name : NULL;
}
-static const dav_provider *dav_get_provider(request_rec *r)
+DAV_DECLARE(const dav_provider *) dav_get_provider(request_rec *r)
{
dav_dir_conf *conf;
@@ -280,6 +285,18 @@ static const char *dav_cmd_dav(cmd_parms *cmd, void *config, const char *arg1)
}
/*
+ * 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,
@@ -295,6 +312,21 @@ static const char *dav_cmd_davdepthinfinity(cmd_parms *cmd, void *config,
}
/*
+ * 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,
@@ -436,7 +468,7 @@ static const char *dav_xml_escape_uri(apr_pool_t *p, const char *uri)
}
-/* Write a complete RESPONSE object out as a <DAV:repsonse> xml
+/* 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.
@@ -557,6 +589,7 @@ DAV_DECLARE(void) dav_send_multistatus(request_rec *r, int status,
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);
@@ -662,8 +695,8 @@ static int dav_created(request_rec *r, const char *locn, const char *what,
/* Apache doesn't allow us to set a variable body for HTTP_CREATED, so
* we must manufacture the entire response. */
- body = apr_psprintf(r->pool, "%s %s has been created.",
- what, ap_escape_html(r->pool, locn));
+ body = apr_pstrcat(r->pool, what, " ", ap_escape_html(r->pool, locn),
+ " has been created.", NULL);
return dav_error_response(r, HTTP_CREATED, body);
}
@@ -676,7 +709,7 @@ DAV_DECLARE(int) dav_get_depth(request_rec *r, int def_depth)
return def_depth;
}
- if (strcasecmp(depth, "infinity") == 0) {
+ if (ap_cstr_casecmp(depth, "infinity") == 0) {
return DAV_INFINITY;
}
else if (strcmp(depth, "0") == 0) {
@@ -725,11 +758,11 @@ static int dav_get_overwrite(request_rec *r)
* the resource identified by the DAV:checked-in property of the resource
* identified by the Request-URI.
*/
-static dav_error *dav_get_resource(request_rec *r, int label_allowed,
+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;
+ const char *label = NULL, *base;
dav_error *err;
/* if the request target can be overridden, get any target selector */
@@ -746,11 +779,27 @@ static dav_error *dav_get_resource(request_rec *r, int label_allowed,
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, conf->dir,
+ 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;
@@ -774,7 +823,9 @@ static dav_error *dav_get_resource(request_rec *r, int label_allowed,
return NULL;
}
-static dav_error * dav_open_lockdb(request_rec *r, int ro, dav_lockdb **lockdb)
+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);
@@ -787,6 +838,11 @@ static dav_error * dav_open_lockdb(request_rec *r, int ro, dav_lockdb **lockdb)
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,
@@ -799,16 +855,15 @@ static int dav_parse_range(request_rec *r,
char *range;
char *dash;
char *slash;
- char *errp;
range_c = apr_table_get(r->headers_in, "content-range");
if (range_c == NULL)
return 0;
range = apr_pstrdup(r->pool, range_c);
- if (strncasecmp(range, "bytes ", 6) != 0
- || (dash = ap_strchr(range, '-')) == NULL
- || (slash = ap_strchr(range, '/')) == NULL) {
+ if (ap_cstr_casecmpn(range, "bytes ", 6) != 0
+ || (dash = ap_strchr(range + 6, '-')) == NULL
+ || (slash = ap_strchr(range + 6, '/')) == NULL) {
/* malformed header */
return -1;
}
@@ -816,20 +871,19 @@ static int dav_parse_range(request_rec *r,
*dash++ = *slash++ = '\0';
/* detect invalid ranges */
- if (apr_strtoff(range_start, range + 6, &errp, 10)
- || *errp || *range_start < 0) {
+ if (!ap_parse_strict_length(range_start, range + 6)) {
return -1;
}
- if (apr_strtoff(range_end, dash, &errp, 10)
- || *errp || *range_end < 0 || *range_end < *range_start) {
+ if (!ap_parse_strict_length(range_end, dash)
+ || *range_end < *range_start) {
return -1;
}
if (*slash != '*') {
apr_off_t dummy;
- if (apr_strtoff(&dummy, slash, &errp, 10)
- || *errp || dummy <= *range_end) {
+ if (!ap_parse_strict_length(&dummy, slash)
+ || dummy <= *range_end) {
return -1;
}
}
@@ -854,6 +908,12 @@ static int dav_method_get(request_rec *r)
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;
@@ -901,6 +961,12 @@ static int dav_method_post(request_rec *r)
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) {
@@ -934,6 +1000,12 @@ static int dav_method_put(request_rec *r)
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) {
@@ -996,12 +1068,17 @@ static int dav_method_put(request_rec *r)
/* Create the new file in the repository */
if ((err = (*resource->hooks->open_stream)(resource, mode,
&stream)) != NULL) {
- /* ### assuming FORBIDDEN is probably not quite right... */
- err = dav_push_error(r->pool, HTTP_FORBIDDEN, 0,
- apr_psprintf(r->pool,
- "Unable to PUT new contents for %s.",
- ap_escape_html(r->pool, r->uri)),
- err);
+ 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) {
@@ -1204,6 +1281,13 @@ static int dav_method_delete(request_rec *r)
&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;
@@ -1319,10 +1403,10 @@ static dav_error *dav_gen_supported_methods(request_rec *r,
if (elts[i].key == NULL)
continue;
- s = apr_psprintf(r->pool,
- "<D:supported-method D:name=\"%s\"/>"
- DEBUG_CR,
- elts[i].key);
+ s = apr_pstrcat(r->pool,
+ "<D:supported-method D:name=\"",
+ elts[i].key,
+ "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s);
}
}
@@ -1348,10 +1432,9 @@ static dav_error *dav_gen_supported_methods(request_rec *r,
/* see if method is supported */
if (apr_table_get(methods, name) != NULL) {
- s = apr_psprintf(r->pool,
- "<D:supported-method D:name=\"%s\"/>"
- DEBUG_CR,
- name);
+ s = apr_pstrcat(r->pool,
+ "<D:supported-method D:name=\"",
+ name, "\"/>" DEBUG_CR, NULL);
apr_text_append(r->pool, body, s);
}
}
@@ -1375,8 +1458,7 @@ static dav_error *dav_gen_supported_live_props(request_rec *r,
dav_error *err;
/* open lock database, to report on supported lock properties */
- /* ### should open read-only */
- if ((err = dav_open_lockdb(r, 0, &lockdb)) != NULL) {
+ 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 "
@@ -1385,7 +1467,7 @@ static dav_error *dav_gen_supported_live_props(request_rec *r,
}
/* open the property database (readonly) for the resource */
- if ((err = dav_open_propdb(r, lockdb, resource, 1, NULL,
+ if ((err = dav_open_propdb(r, lockdb, resource, DAV_PROPDB_RO, NULL,
&propdb)) != NULL) {
if (lockdb != NULL)
(*lockdb->hooks->close_lockdb)(lockdb);
@@ -1451,89 +1533,95 @@ static dav_error *dav_gen_supported_live_props(request_rec *r,
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,
- const dav_hooks_vsn *vsn_hooks,
apr_text_header *body)
{
apr_xml_elem *child;
apr_xml_attr *attr;
- dav_error *err;
+ dav_error *err = NULL;
char *s;
+ apr_array_header_t *reports;
+ const dav_report_elem *rp;
apr_text_append(r->pool, body, "<D:supported-report-set>" DEBUG_CR);
- if (vsn_hooks != NULL) {
- const dav_report_elem *reports;
- const dav_report_elem *rp;
+ 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 ((err = (*vsn_hooks->avail_reports)(resource, &reports)) != 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,
+ "<D:supported-report D:name=\"",
+ 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;
- if (reports != NULL) {
- if (elem->first_child == NULL) {
- /* show all supported reports */
- for (rp = reports; rp->nmspace != NULL; ++rp) {
- /* Note: we presume reports->namespace is
- * properly XML/URL quoted */
- s = apr_psprintf(r->pool,
- "<D:supported-report D:name=\"%s\" "
- "D:namespace=\"%s\"/>" DEBUG_CR,
- rp->name, rp->nmspace);
- apr_text_append(r->pool, body, s);
+ /* 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;
+ }
}
- }
- 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;
-
- /* 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:";
-
- for (rp = reports; rp->nmspace != NULL; ++rp) {
- if (strcmp(name, rp->name) == 0
- && strcmp(nmspace, rp->nmspace) == 0) {
- /* Note: we presume reports->nmspace is
- * properly XML/URL quoted
- */
- s = apr_psprintf(r->pool,
- "<D:supported-report "
- "D:name=\"%s\" "
- "D:namespace=\"%s\"/>"
- DEBUG_CR,
- rp->name, rp->nmspace);
- apr_text_append(r->pool, body, s);
- break;
- }
- }
+
+ 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:supported-report "
+ "D:name=\"",
+ rp->name,
+ "\" D:namespace=\"",
+ rp->nmspace,
+ "\"/>" DEBUG_CR, NULL);
+ apr_text_append(r->pool, body, s);
+ break;
}
}
}
@@ -1640,6 +1728,12 @@ static int dav_method_options(request_rec *r)
}
/* 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.");
@@ -1903,7 +1997,7 @@ static int dav_method_options(request_rec *r)
core_option = 1;
}
else if (strcmp(elem->name, "supported-report-set") == 0) {
- err = dav_gen_supported_reports(r, resource, elem, vsn_hooks, &body);
+ err = dav_gen_supported_reports(r, resource, elem, &body);
core_option = 1;
}
}
@@ -1968,10 +2062,23 @@ static void dav_cache_badprops(dav_walker_ctx *ctx)
static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
{
dav_walker_ctx *ctx = wres->walk_ctx;
+ dav_dir_conf *conf;
+ int flags = DAV_PROPDB_RO;
dav_error *err;
dav_propdb *propdb;
dav_get_props_result propstats = { 0 };
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(ctx->r, NULL, wres->resource, ctx->doc, &err) != DECLINED
+ && err) {
+ apr_pool_clear(ctx->scratchpool);
+ return NULL;
+ }
+
+ conf = ap_get_module_config(ctx->r->per_dir_config, &dav_module);
+ if (conf && conf->allow_lockdiscovery == DAV_ENABLED_OFF)
+ flags |= DAV_PROPDB_DISABLE_LOCKDISCOVERY;
+
/*
** Note: ctx->doc can only be NULL for DAV_PROPFIND_IS_ALLPROP. Since
** dav_get_allprops() does not need to do namespace translation,
@@ -1980,8 +2087,9 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
** Note: we cast to lose the "const". The propdb won't try to change
** the resource, however, since we are opening readonly.
*/
- err = dav_open_propdb(ctx->r, ctx->w.lockdb, wres->resource, 1,
- ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
+ err = dav_popen_propdb(ctx->scratchpool,
+ ctx->r, ctx->w.lockdb, wres->resource, flags,
+ ctx->doc ? ctx->doc->namespaces : NULL, &propdb);
if (err != NULL) {
/* ### do something with err! */
@@ -2012,10 +2120,10 @@ static dav_error * dav_propfind_walker(dav_walk_resource *wres, int calltype)
: DAV_PROP_INSERT_NAME;
propstats = dav_get_allprops(propdb, what);
}
- dav_close_propdb(propdb);
-
dav_stream_response(wres, 0, &propstats, ctx->scratchpool);
+ dav_close_propdb(propdb);
+
/* at this point, ctx->scratchpool has been used to stream a
single response. this function fully controls the pool, and
thus has the right to clear it for the next iteration of this
@@ -2042,6 +2150,17 @@ static int dav_method_propfind(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ 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 (dav_get_resource_state(r, resource) == DAV_RESOURCE_NULL) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
@@ -2069,11 +2188,6 @@ static int dav_method_propfind(request_rec *r)
}
}
- if ((result = ap_xml_parse_input(r, &doc)) != OK) {
- return result;
- }
- /* note: doc == NULL if no request body */
-
if (doc && !dav_validate_root(doc, "propfind")) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00585)
@@ -2103,7 +2217,7 @@ static int dav_method_propfind(request_rec *r)
return HTTP_BAD_REQUEST;
}
- ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH;
+ ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_AUTH | DAV_WALKTYPE_TOLERANT;
ctx.w.func = dav_propfind_walker;
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
@@ -2113,9 +2227,9 @@ static int dav_method_propfind(request_rec *r)
ctx.r = r;
ctx.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
apr_pool_create(&ctx.scratchpool, r->pool);
+ apr_pool_tag(ctx.scratchpool, "mod_dav-scratch");
- /* ### should open read-only */
- if ((err = dav_open_lockdb(r, 0, &ctx.w.lockdb)) != NULL) {
+ if ((err = dav_open_lockdb(r, 1, &ctx.w.lockdb)) != NULL) {
err = dav_push_error(r->pool, err->status, 0,
"The lock database could not be opened, "
"preventing access to the various lock "
@@ -2318,16 +2432,23 @@ static int dav_method_proppatch(request_rec *r)
&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;
- }
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 (!resource->exists) {
+ /* Apache will supply a default error for this. */
+ return HTTP_NOT_FOUND;
+ }
+
if (doc == NULL || !dav_validate_root(doc, "propertyupdate")) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00587)
@@ -2352,7 +2473,8 @@ static int dav_method_proppatch(request_rec *r)
return dav_handle_err(r, err, NULL);
}
- if ((err = dav_open_propdb(r, NULL, resource, 0, doc->namespaces,
+ if ((err = dav_open_propdb(r, NULL, resource,
+ DAV_PROPDB_NONE, doc->namespaces,
&propdb)) != NULL) {
/* undo any auto-checkout */
dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
@@ -2470,7 +2592,7 @@ static int process_mkcol_body(request_rec *r)
r->remaining = 0;
if (tenc) {
- if (strcasecmp(tenc, "chunked")) {
+ if (ap_cstr_casecmp(tenc, "chunked")) {
/* Use this instead of Apache's default error string */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00589)
"Unknown Transfer-Encoding %s", tenc);
@@ -2480,20 +2602,13 @@ static int process_mkcol_body(request_rec *r)
r->read_chunked = 1;
}
else if (lenp) {
- const char *pos = lenp;
-
- while (apr_isdigit(*pos) || apr_isspace(*pos)) {
- ++pos;
- }
-
- if (*pos != '\0') {
+ if (!ap_parse_strict_length(&r->remaining, lenp)) {
+ r->remaining = 0;
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00590)
"Invalid Content-Length %s", lenp);
return HTTP_BAD_REQUEST;
}
-
- r->remaining = apr_atoi64(lenp);
}
if (r->read_chunked || r->remaining > 0) {
@@ -2534,6 +2649,12 @@ static int dav_method_mkcol(request_rec *r)
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) {
/* oops. something was already there! */
@@ -2654,6 +2775,12 @@ static int dav_method_copymove(request_rec *r, int is_move)
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;
@@ -2676,7 +2803,7 @@ static int dav_method_copymove(request_rec *r, int is_move)
const char *nscp_path = apr_table_get(r->headers_in, "New-uri");
if (nscp_host != NULL && nscp_path != NULL)
- dest = apr_psprintf(r->pool, "http://%s%s", nscp_host, nscp_path);
+ dest = apr_pstrcat(r->pool, "http://", nscp_host, nscp_path, NULL);
}
if (dest == NULL) {
/* This supplies additional information for the default message. */
@@ -2720,6 +2847,12 @@ static int dav_method_copymove(request_rec *r, int is_move)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(r, resource, resnew, NULL, &err) != DECLINED
+ && err) {
+ return dav_handle_err(r, err, NULL);
+ }
+
/* are the two resources handled by the same repository? */
if (resource->hooks != resnew->hooks) {
/* ### this message exposes some backend config, but screw it... */
@@ -3071,6 +3204,12 @@ static int dav_method_lock(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(r, resource, NULL, doc, &err) != DECLINED
+ && err) {
+ return dav_handle_err(r, err, NULL);
+ }
+
/* Check if parent collection exists */
if ((err = resource->hooks->get_parent_resource(resource, &parent)) != NULL) {
/* ### add a higher-level description? */
@@ -3270,6 +3409,12 @@ static int dav_method_unlock(request_rec *r)
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);
+ }
+
resource_state = dav_get_resource_state(r, resource);
/*
@@ -3294,8 +3439,8 @@ static int dav_method_unlock(request_rec *r)
/* ### RFC 2518 s. 8.11: If this resource is locked by locktoken,
* _all_ resources locked by locktoken are released. It does not say
- * resource has to be the root of an infinte lock. Thus, an UNLOCK
- * on any part of an infinte lock will remove the lock on all resources.
+ * resource has to be the root of an infinite lock. Thus, an UNLOCK
+ * on any part of an infinite lock will remove the lock on all resources.
*
* For us, if r->filename represents an indirect lock (part of an infinity lock),
* we must actually perform an UNLOCK on the direct lock for this resource.
@@ -3329,15 +3474,21 @@ static int dav_method_vsn_control(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
- /* remember the pre-creation resource state */
- resource_state = dav_get_resource_state(r, resource);
-
/* parse the request body (may be a version-control element) */
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);
+ }
+
+ /* remember the pre-creation resource state */
+ resource_state = dav_get_resource_state(r, resource);
+
if (doc != NULL) {
const apr_xml_elem *child;
apr_size_t tsize;
@@ -3582,6 +3733,12 @@ static int dav_method_checkout(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* 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 (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
@@ -3658,6 +3815,12 @@ static int dav_method_uncheckout(request_rec *r)
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;
@@ -3735,6 +3898,12 @@ static int dav_method_checkin(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* 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 (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
@@ -3856,6 +4025,12 @@ static int dav_method_update(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* 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 (!resource->exists) {
/* Apache will supply a default error for this. */
return HTTP_NOT_FOUND;
@@ -3930,6 +4105,9 @@ typedef struct dav_label_walker_ctx
/* input: */
dav_walk_params w;
+ /* original request */
+ request_rec *r;
+
/* label being manipulated */
const char *label;
@@ -3949,13 +4127,19 @@ static dav_error * dav_label_walker(dav_walk_resource *wres, int calltype)
dav_label_walker_ctx *ctx = wres->walk_ctx;
dav_error *err = NULL;
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(ctx->r, NULL, wres->resource, NULL, &err) != DECLINED
+ && err) {
+ /* precondition failed, dropping through */
+ }
+
/* Check the state of the resource: must be a version or
* non-checkedout version selector
*/
/* ### need a general mechanism for reporting precondition violations
* ### (should be returning XML document for 403/409 responses)
*/
- if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION &&
+ else if (wres->resource->type != DAV_RESOURCE_TYPE_VERSION &&
(wres->resource->type != DAV_RESOURCE_TYPE_REGULAR
|| !wres->resource->versioned)) {
err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0, 0,
@@ -4001,11 +4185,23 @@ static int dav_method_label(request_rec *r)
if (vsn_hooks == NULL || vsn_hooks->add_label == NULL)
return DECLINED;
+ /* parse the request body */
+ if ((result = ap_xml_parse_input(r, &doc)) != OK) {
+ return result;
+ }
+
/* Ask repository module to resolve the resource */
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, doc, &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;
@@ -4017,11 +4213,6 @@ static int dav_method_label(request_rec *r)
return HTTP_BAD_REQUEST;
}
- /* parse the request body */
- if ((result = ap_xml_parse_input(r, &doc)) != OK) {
- return result;
- }
-
if (doc == NULL || !dav_validate_root(doc, "label")) {
/* This supplies additional information for the default message. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00610)
@@ -4070,6 +4261,7 @@ static int dav_method_label(request_rec *r)
ctx.w.walk_ctx = &ctx;
ctx.w.pool = r->pool;
ctx.w.root = resource;
+ ctx.r = r;
ctx.vsn_hooks = vsn_hooks;
err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
@@ -4109,21 +4301,60 @@ static int dav_method_label(request_rec *r)
return DONE;
}
+static int dav_core_deliver_report(request_rec *r,
+ const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output, dav_error **err)
+{
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+
+ if (vsn_hooks) {
+ *err = (*vsn_hooks->deliver_report)(r, resource, doc,
+ r->output_filters);
+ return OK;
+ }
+
+ return DECLINED;
+}
+
+static void dav_core_gather_reports(
+ request_rec *r,
+ const dav_resource *resource,
+ apr_array_header_t *reports,
+ dav_error **err)
+{
+ const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
+
+ if (vsn_hooks) {
+ const dav_report_elem *rp;
+
+ (*err) = (*vsn_hooks->avail_reports)(resource, &rp);
+ while (rp && rp->name) {
+
+ dav_report_elem *report = apr_array_push(reports);
+
+ report->nmspace = rp->nmspace;
+ report->name = rp->name;
+
+ rp++;
+ }
+ }
+
+}
+
static int dav_method_report(request_rec *r)
{
dav_resource *resource;
const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
- int result;
- int label_allowed;
apr_xml_doc *doc;
- dav_error *err;
+ dav_error *err = NULL;
- /* If no versioning provider, decline the request */
- if (vsn_hooks == NULL)
- return DECLINED;
+ int result;
+ int label_allowed;
- if ((result = ap_xml_parse_input(r, &doc)) != OK)
+ if ((result = ap_xml_parse_input(r, &doc)) != OK) {
return result;
+ }
if (doc == NULL) {
/* This supplies additional information for the default msg. */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00614)
@@ -4135,11 +4366,18 @@ static int dav_method_report(request_rec *r)
* First determine whether a Target-Selector header is allowed
* for this report.
*/
- label_allowed = (*vsn_hooks->report_label_header_allowed)(doc);
+ label_allowed = vsn_hooks ? (*vsn_hooks->report_label_header_allowed)(doc) : 0;
err = dav_get_resource(r, label_allowed, 0 /* use_checked_in */,
&resource);
- if (err != NULL)
+ if (err != NULL) {
+ return dav_handle_err(r, err, NULL);
+ }
+
+ /* 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 (!resource->exists) {
/* Apache will supply a default error for this. */
@@ -4149,13 +4387,17 @@ static int dav_method_report(request_rec *r)
/* set up defaults for the report response */
r->status = HTTP_OK;
ap_set_content_type(r, DAV_XML_CONTENT_TYPE);
+ err = NULL;
/* run report hook */
- if ((err = (*vsn_hooks->deliver_report)(r, resource, doc,
- r->output_filters)) != NULL) {
- if (! r->sent_bodyct)
+ result = dav_run_deliver_report(r, resource, doc,
+ r->output_filters, &err);
+ if (err != NULL) {
+
+ if (! r->sent_bodyct) {
/* No data has been sent to client yet; throw normal error. */
return dav_handle_err(r, err, NULL);
+ }
/* If an error occurred during the report delivery, there's
basically nothing we can do but abort the connection and
@@ -4168,6 +4410,16 @@ static int dav_method_report(request_rec *r)
" a REPORT response.", err);
dav_log_err(r, err, APLOG_ERR);
r->connection->aborted = 1;
+
+ return DONE;
+ }
+ switch (result) {
+ case OK:
+ return DONE;
+ case DECLINED:
+ /* No one handled the report */
+ return HTTP_NOT_IMPLEMENTED;
+ default:
return DONE;
}
@@ -4199,6 +4451,12 @@ static int dav_method_make_workspace(request_rec *r)
return result;
}
+ /* 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 == NULL
|| !dav_validate_root(doc, "mkworkspace")) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00615)
@@ -4258,6 +4516,12 @@ static int dav_method_make_activity(request_rec *r)
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);
+ }
+
/* MKACTIVITY does not have a defined request body. */
if ((result = ap_discard_request_body(r)) != OK) {
return result;
@@ -4383,6 +4647,12 @@ static int dav_method_merge(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(r, source_resource, NULL, doc, &err) != DECLINED
+ && err) {
+ return dav_handle_err(r, err, NULL);
+ }
+
no_auto_merge = dav_find_child(doc->root, "no-auto-merge") != NULL;
no_checkout = dav_find_child(doc->root, "no-checkout") != NULL;
@@ -4400,6 +4670,13 @@ static int dav_method_merge(request_rec *r)
&resource);
if (err != NULL)
return dav_handle_err(r, err, NULL);
+
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(r, source_resource, resource, doc, &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;
@@ -4467,6 +4744,12 @@ static int dav_method_bind(request_rec *r)
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;
@@ -4517,6 +4800,12 @@ static int dav_method_bind(request_rec *r)
if (err != NULL)
return dav_handle_err(r, err, NULL);
+ /* check for any method preconditions */
+ if (dav_run_method_precondition(r, resource, binding, NULL, &err) != DECLINED
+ && err) {
+ return dav_handle_err(r, err, NULL);
+ }
+
/* are the two resources handled by the same repository? */
if (resource->hooks != binding->hooks) {
/* ### this message exposes some backend config, but screw it... */
@@ -4886,6 +5175,11 @@ static void register_hooks(apr_pool_t *p)
dav_hook_insert_all_liveprops(dav_core_insert_all_liveprops,
NULL, NULL, APR_HOOK_MIDDLE);
+ dav_hook_deliver_report(dav_core_deliver_report,
+ NULL, NULL, APR_HOOK_LAST);
+ dav_hook_gather_reports(dav_core_gather_reports,
+ NULL, NULL, APR_HOOK_LAST);
+
dav_core_register_uris(p);
}
@@ -4900,6 +5194,10 @@ static const command_rec dav_cmds[] =
AP_INIT_TAKE1("DAV", dav_cmd_dav, NULL, ACCESS_CONF,
"specify the DAV provider for a directory or location"),
+ /* per directory/location */
+ AP_INIT_TAKE1("DAVBasePath", dav_cmd_davbasepath, NULL, ACCESS_CONF,
+ "specify the DAV repository base URL"),
+
/* per directory/location, or per server */
AP_INIT_TAKE1("DAVMinTimeout", dav_cmd_davmintimeout, NULL,
ACCESS_CONF|RSRC_CONF,
@@ -4910,6 +5208,11 @@ static const command_rec dav_cmds[] =
ACCESS_CONF|RSRC_CONF,
"allow Depth infinity PROPFIND requests"),
+ /* per directory/location, or per server */
+ AP_INIT_FLAG("DAVLockDiscovery", dav_cmd_davlockdiscovery, NULL,
+ ACCESS_CONF|RSRC_CONF,
+ "allow lock discovery by PROPFIND requests"),
+
{ NULL }
};
@@ -4928,6 +5231,9 @@ APR_HOOK_STRUCT(
APR_HOOK_LINK(gather_propsets)
APR_HOOK_LINK(find_liveprop)
APR_HOOK_LINK(insert_all_liveprops)
+ APR_HOOK_LINK(deliver_report)
+ APR_HOOK_LINK(gather_reports)
+ APR_HOOK_LINK(method_precondition)
)
APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, gather_propsets,
@@ -4944,3 +5250,22 @@ APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, insert_all_liveprops,
(request_rec *r, const dav_resource *resource,
dav_prop_insert what, apr_text_header *phdr),
(r, resource, what, phdr))
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, deliver_report,
+ (request_rec *r,
+ const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output, dav_error **err),
+ (r, resource, doc, output, err), DECLINED)
+
+APR_IMPLEMENT_EXTERNAL_HOOK_VOID(dav, DAV, gather_reports,
+ (request_rec *r, const dav_resource *resource,
+ apr_array_header_t *reports, dav_error **err),
+ (r, resource, reports, err))
+
+APR_IMPLEMENT_EXTERNAL_HOOK_RUN_FIRST(dav, DAV, int, method_precondition,
+ (request_rec *r,
+ dav_resource *src, const dav_resource *dest,
+ const apr_xml_doc *doc,
+ dav_error **err),
+ (r, src, dest, doc, err), DECLINED)
diff --git a/modules/dav/main/mod_dav.h b/modules/dav/main/mod_dav.h
index 80ad117..c8c54f3 100644
--- a/modules/dav/main/mod_dav.h
+++ b/modules/dav/main/mod_dav.h
@@ -50,7 +50,7 @@ extern "C" {
#define DAV_READ_BLOCKSIZE 2048 /* used for reading input blocks */
-#define DAV_RESPONSE_BODY_1 "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\n<html><head>\n<title>"
+#define DAV_RESPONSE_BODY_1 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">\n<html>\n<head>\n<title>"
#define DAV_RESPONSE_BODY_2 "</title>\n</head><body>\n<h1>"
#define DAV_RESPONSE_BODY_3 "</h1>\n<p>"
#define DAV_RESPONSE_BODY_4 "</p>\n"
@@ -427,6 +427,9 @@ typedef struct dav_resource {
*/
typedef struct dav_locktoken dav_locktoken;
+DAV_DECLARE(dav_error *) dav_get_resource(request_rec *r, int label_allowed,
+ int use_checked_in, dav_resource **res_p);
+
/* --------------------------------------------------------------------
**
@@ -574,8 +577,22 @@ DAV_DECLARE(int) dav_get_depth(request_rec *r, int def_depth);
DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc,
const char *tagname);
+DAV_DECLARE(int) dav_validate_root_ns(const apr_xml_doc *doc,
+ int ns, const char *tagname);
DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem,
const char *tagname);
+DAV_DECLARE(apr_xml_elem *) dav_find_child_ns(const apr_xml_elem *elem,
+ int ns, const char *tagname);
+DAV_DECLARE(apr_xml_elem *) dav_find_next_ns(const apr_xml_elem *elem,
+ int ns, const char *tagname);
+
+/* find and return the attribute with a name in the given namespace */
+DAV_DECLARE(apr_xml_attr *) dav_find_attr_ns(const apr_xml_elem *elem,
+ int ns, const char *attrname);
+
+/* find and return the attribute with a given DAV: tagname */
+DAV_DECLARE(apr_xml_attr *) dav_find_attr(const apr_xml_elem *elem,
+ const char *attrname);
/* gather up all the CDATA into a single string */
DAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool,
@@ -644,10 +661,10 @@ DAV_DECLARE(void) dav_xmlns_generate(dav_xmlns_info *xi,
** mod_dav 1.0). There are too many dependencies between a dav_resource
** (defined by <repos>) and the other functionality.
**
-** Live properties are not part of the dav_provider structure because they
-** are handled through the APR_HOOK interface (to allow for multiple liveprop
-** providers). The core always provides some properties, and then a given
-** provider will add more properties.
+** Live properties and report extensions are not part of the dav_provider
+** structure because they are handled through the APR_HOOK interface (to
+** allow for multiple providers). The core always provides some
+** properties, and then a given provider will add more properties.
**
** Some providers may need to associate a context with the dav_provider
** structure -- the ctx field is available for storing this context. Just
@@ -711,6 +728,68 @@ APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, insert_all_liveprops,
(request_rec *r, const dav_resource *resource,
dav_prop_insert what, apr_text_header *phdr))
+/*
+** deliver_report: given a parsed report request, process the request
+** an deliver the resulting report.
+**
+** The hook implementer should decide whether it should handle the given
+** report, and if so, write the response to the output filter. If the
+** report is not relevant, return DECLINED.
+*/
+APR_DECLARE_EXTERNAL_HOOK(dav, DAV, int, deliver_report,
+ (request_rec *r,
+ const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output, dav_error **err))
+
+/*
+** gather_reports: get all reports.
+**
+** The hook implementor should push one or more dav_report_elem structures
+** containing report names into the specified array. These names are returned
+** in the DAV:supported-reports-set property to let clients know
+** what reports are supported by the installation.
+**
+*/
+APR_DECLARE_EXTERNAL_HOOK(dav, DAV, void, gather_reports,
+ (request_rec *r, const dav_resource *resource,
+ apr_array_header_t *reports, dav_error **err))
+
+/*
+ ** method_precondition: check method preconditions.
+ **
+ ** If a WebDAV extension needs to set any preconditions on a method, this
+ ** hook is where to do it. If the precondition fails, return an error
+ ** response with the tagname set to the value of the failed precondition.
+ **
+ ** If the method requires an XML body, this will be read and provided as
+ ** the doc value. If not, doc is NULL. An extension that needs to verify
+ ** the non-XML body of a request should register an input filter to do so
+ ** within this hook.
+ **
+ ** Methods like PUT will supply a single src resource, and the dst will
+ ** be NULL.
+ **
+ ** Methods like COPY or MOVE will trigger this hook twice. The first
+ ** invocation will supply just the source resource. The second invocation
+ ** will supply a source and destination. This allows preconditions on the
+ ** source resource to be verified before making an attempt to get the
+ ** destination resource.
+ **
+ ** Methods like PROPFIND and LABEL will trigger this hook initially for
+ ** the src resource, and then subsequently for each resource that has
+ ** been walked during processing, with the walked resource passed in dst,
+ ** and NULL passed in src.
+ **
+ ** As a rule, the src resource originates from a request that has passed
+ ** through httpd's authn/authz hooks, while the dst resource has not.
+ */
+APR_DECLARE_EXTERNAL_HOOK(dav, DAV, int, method_precondition,
+ (request_rec *r,
+ dav_resource *src, const dav_resource *dst,
+ const apr_xml_doc *doc, dav_error **err))
+
+
DAV_DECLARE(const dav_hooks_locks *) dav_get_lock_hooks(request_rec *r);
DAV_DECLARE(const dav_hooks_propdb *) dav_get_propdb_hooks(request_rec *r);
DAV_DECLARE(const dav_hooks_vsn *) dav_get_vsn_hooks(request_rec *r);
@@ -721,6 +800,7 @@ DAV_DECLARE(void) dav_register_provider(apr_pool_t *p, const char *name,
const dav_provider *hooks);
DAV_DECLARE(const dav_provider *) dav_lookup_provider(const char *name);
DAV_DECLARE(const char *) dav_get_provider_name(request_rec *r);
+DAV_DECLARE(const dav_provider *) dav_get_provider(request_rec *r);
/* ### deprecated */
@@ -827,6 +907,14 @@ struct dav_hooks_liveprop
** property, and does not want it handled as a dead property, it should
** return DAV_PROP_INSERT_NOTSUPP.
**
+ ** Some DAV extensions, like CalDAV, specify both document elements
+ ** and property elements that need to be taken into account when
+ ** generating a property. The document element and property element
+ ** are made available in the dav_liveprop_elem structure under the
+ ** resource, accessible as follows:
+ **
+ ** dav_get_liveprop_element(resource);
+ **
** Returns one of DAV_PROP_INSERT_* based on what happened.
**
** ### we may need more context... ie. the lock database
@@ -974,6 +1062,18 @@ DAV_DECLARE(long) dav_get_liveprop_ns_count(void);
DAV_DECLARE(void) dav_add_all_liveprop_xmlns(apr_pool_t *p,
apr_text_header *phdr);
+typedef struct {
+ const apr_xml_doc *doc;
+ const apr_xml_elem *elem;
+} dav_liveprop_elem;
+
+/*
+ ** When calling insert_prop(), the associated request element and
+ ** document is accessible using the following call.
+ */
+DAV_DECLARE(dav_liveprop_elem *) dav_get_liveprop_element(const dav_resource
+ *resource);
+
/*
** The following three functions are part of mod_dav's internal handling
** for the core WebDAV properties. They are not part of mod_dav's API.
@@ -1314,8 +1414,12 @@ DAV_DECLARE(const char *)dav_lock_get_activelock(request_rec *r,
dav_buffer *pbuf);
/* LockDB-related public lock functions */
+DAV_DECLARE(dav_error *) dav_open_lockdb(request_rec *r,
+ int ro,
+ dav_lockdb **lockdb);
+DAV_DECLARE(void) dav_close_lockdb(dav_lockdb *lockdb);
DAV_DECLARE(dav_error *) dav_lock_parse_lockinfo(request_rec *r,
- const dav_resource *resrouce,
+ const dav_resource *resource,
dav_lockdb *lockdb,
const apr_xml_doc *doc,
dav_lock **lock_request);
@@ -1581,15 +1685,28 @@ struct dav_hooks_locks
typedef struct dav_propdb dav_propdb;
+#define DAV_PROPDB_NONE 0
+#define DAV_PROPDB_RO 1
+#define DAV_PROPDB_DISABLE_LOCKDISCOVERY 2
DAV_DECLARE(dav_error *) dav_open_propdb(
request_rec *r,
dav_lockdb *lockdb,
const dav_resource *resource,
- int ro,
+ int flags,
apr_array_header_t *ns_xlate,
dav_propdb **propdb);
+DAV_DECLARE(dav_error *) dav_popen_propdb(
+ apr_pool_t *p,
+ request_rec *r,
+ dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int flags,
+ apr_array_header_t *ns_xlate,
+ dav_propdb **propdb);
+
+
DAV_DECLARE(void) dav_close_propdb(dav_propdb *db);
DAV_DECLARE(dav_get_props_result) dav_get_props(
@@ -1706,6 +1823,7 @@ typedef struct
#define DAV_WALKTYPE_AUTH 0x0001 /* limit to authorized files */
#define DAV_WALKTYPE_NORMAL 0x0002 /* walk normal files */
#define DAV_WALKTYPE_LOCKNULL 0x0004 /* walk locknull resources */
+#define DAV_WALKTYPE_TOLERANT 0x0008 /* tolerate non-fatal errors */
/* callback function and a client context for the walk */
dav_error * (*func)(dav_walk_resource *wres, int calltype);
diff --git a/modules/dav/main/props.c b/modules/dav/main/props.c
index f64878e..c320f8a 100644
--- a/modules/dav/main/props.c
+++ b/modules/dav/main/props.c
@@ -167,6 +167,8 @@
#define DAV_EMPTY_VALUE "\0" /* TWO null terms */
+#define DAV_PROP_ELEMENT "mod_dav-element"
+
struct dav_propdb {
apr_pool_t *p; /* the pool we should use */
request_rec *r; /* the request record */
@@ -183,6 +185,8 @@ struct dav_propdb {
dav_buffer wb_lock; /* work buffer for lockdiscovery property */
+ int flags; /* ro, disable lock discovery */
+
/* if we ever run a GET subreq, it will be stored here */
request_rec *subreq;
@@ -323,7 +327,7 @@ static void dav_do_prop_subreq(dav_propdb *propdb)
{
/* need to escape the uri that's in the resource struct because during
* the property walker it's not encoded. */
- const char *e_uri = ap_escape_uri(propdb->resource->pool,
+ const char *e_uri = ap_escape_uri(propdb->p,
propdb->resource->uri);
/* perform a "GET" on the resource's URI (note that the resource
@@ -349,6 +353,11 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
switch (propid) {
case DAV_PROPID_CORE_lockdiscovery:
+ if (propdb->flags & DAV_PROPDB_DISABLE_LOCKDISCOVERY) {
+ value = "";
+ break;
+ }
+
if (propdb->lockdb != NULL) {
dav_lock *locks;
@@ -421,18 +430,18 @@ static dav_error * dav_insert_coreprop(dav_propdb *propdb,
/* use D: prefix to refer to the DAV: namespace URI,
* and let the namespace attribute default to "DAV:"
*/
- s = apr_psprintf(propdb->p,
- "<D:supported-live-property D:name=\"%s\"/>" DEBUG_CR,
- name);
+ s = apr_pstrcat(propdb->p,
+ "<D:supported-live-property D:name=\"",
+ name, "\"/>" DEBUG_CR, NULL);
}
else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
/* use D: prefix to refer to the DAV: namespace URI */
- s = apr_psprintf(propdb->p, "<D:%s>%s</D:%s>" DEBUG_CR,
- name, value, name);
+ s = apr_pstrcat(propdb->p, "<D:", name, ">", value, "</D:", name,
+ ">" DEBUG_CR, NULL);
}
else {
/* use D: prefix to refer to the DAV: namespace URI */
- s = apr_psprintf(propdb->p, "<D:%s/>" DEBUG_CR, name);
+ s = apr_pstrcat(propdb->p, "<D:", name, "/>" DEBUG_CR, NULL);
}
apr_text_append(propdb->p, phdr, s);
@@ -473,11 +482,11 @@ static void dav_output_prop_name(apr_pool_t *pool,
const char *s;
if (*name->ns == '\0')
- s = apr_psprintf(pool, "<%s/>" DEBUG_CR, name->name);
+ s = apr_pstrcat(pool, "<", name->name, "/>" DEBUG_CR, NULL);
else {
const char *prefix = dav_xmlns_add_uri(xi, name->ns);
- s = apr_psprintf(pool, "<%s:%s/>" DEBUG_CR, prefix, name->name);
+ s = apr_pstrcat(pool, "<", prefix, ":", name->name, "/>" DEBUG_CR, NULL);
}
apr_text_append(pool, phdr, s);
@@ -520,11 +529,25 @@ static dav_error *dav_really_open_db(dav_propdb *propdb, int ro)
DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
const dav_resource *resource,
- int ro,
+ int flags,
apr_array_header_t * ns_xlate,
dav_propdb **p_propdb)
{
- dav_propdb *propdb = apr_pcalloc(r->pool, sizeof(*propdb));
+ return dav_popen_propdb(r->pool, r, lockdb, resource,
+ flags, ns_xlate, p_propdb);
+}
+
+DAV_DECLARE(dav_error *)dav_popen_propdb(apr_pool_t *p,
+ request_rec *r, dav_lockdb *lockdb,
+ const dav_resource *resource,
+ int flags,
+ apr_array_header_t * ns_xlate,
+ dav_propdb **p_propdb)
+{
+ dav_propdb *propdb = NULL;
+
+ propdb = apr_pcalloc(p, sizeof(*propdb));
+ propdb->p = p;
*p_propdb = NULL;
@@ -537,7 +560,6 @@ DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
#endif
propdb->r = r;
- apr_pool_create(&propdb->p, r->pool);
propdb->resource = resource;
propdb->ns_xlate = ns_xlate;
@@ -545,6 +567,8 @@ DAV_DECLARE(dav_error *)dav_open_propdb(request_rec *r, dav_lockdb *lockdb,
propdb->lockdb = lockdb;
+ propdb->flags = flags;
+
/* always defer actual open, to avoid expense of accessing db
* when only live properties are involved
*/
@@ -562,10 +586,10 @@ DAV_DECLARE(void) dav_close_propdb(dav_propdb *propdb)
(*propdb->db_hooks->close)(propdb->db);
}
- /* Currently, mod_dav's pool usage doesn't allow clearing this pool. */
-#if 0
- apr_pool_destroy(propdb->p);
-#endif
+ if (propdb->subreq) {
+ ap_destroy_sub_req(propdb->subreq);
+ propdb->subreq = NULL;
+ }
}
DAV_DECLARE(dav_get_props_result) dav_get_allprops(dav_propdb *propdb,
@@ -706,10 +730,24 @@ DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
apr_text_header hdr_ns = { 0 };
int have_good = 0;
dav_get_props_result result = { 0 };
+ dav_liveprop_elem *element;
char *marks_liveprop;
dav_xmlns_info *xi;
int xi_filled = 0;
+ /* we lose both the document and the element when calling (insert_prop),
+ * make these available in the pool.
+ */
+ element = dav_get_liveprop_element(propdb->resource);
+ if (!element) {
+ element = apr_pcalloc(propdb->resource->pool, sizeof(dav_liveprop_elem));
+ apr_pool_userdata_setn(element, DAV_PROP_ELEMENT, NULL, propdb->resource->pool);
+ }
+ else {
+ memset(element, 0, sizeof(dav_liveprop_elem));
+ }
+ element->doc = doc;
+
/* ### NOTE: we should pass in TWO buffers -- one for keys, one for
the marks */
@@ -733,13 +771,16 @@ DAV_DECLARE(dav_get_props_result) dav_get_props(dav_propdb *propdb,
dav_prop_insert inserted;
dav_prop_name name;
+ element->elem = elem;
+
/*
** First try live property providers; if they don't handle
** the property, then try looking it up in the propdb.
*/
if (elem->priv == NULL) {
- elem->priv = apr_pcalloc(propdb->p, sizeof(*priv));
+ /* elem->priv outlives propdb->p. Hence use the request pool */
+ elem->priv = apr_pcalloc(propdb->r->pool, sizeof(*priv));
}
priv = elem->priv;
@@ -909,6 +950,15 @@ DAV_DECLARE(void) dav_get_liveprop_supported(dav_propdb *propdb,
}
}
+DAV_DECLARE(dav_liveprop_elem *) dav_get_liveprop_element(const dav_resource *resource)
+{
+ dav_liveprop_elem *element;
+
+ apr_pool_userdata_get((void **)&element, DAV_PROP_ELEMENT, resource->pool);
+
+ return element;
+}
+
DAV_DECLARE_NONSTD(void) dav_prop_validate(dav_prop_ctx *ctx)
{
dav_propdb *propdb = ctx->propdb;
@@ -1047,6 +1097,10 @@ DAV_DECLARE_NONSTD(void) dav_prop_exec(dav_prop_ctx *ctx)
/*
** Delete the property. Ignore errors -- the property is there, or
** we are deleting it for a second time.
+ **
+ ** http://tools.ietf.org/html/rfc4918#section-14.23 says
+ ** "Specifying the removal of a property that does not exist is
+ ** not an error"
*/
/* ### but what about other errors? */
(void) (*propdb->db_hooks->remove)(propdb->db, &name);
diff --git a/modules/dav/main/std_liveprop.c b/modules/dav/main/std_liveprop.c
index e760c65..cb46b65 100644
--- a/modules/dav/main/std_liveprop.c
+++ b/modules/dav/main/std_liveprop.c
@@ -154,10 +154,10 @@ static dav_prop_insert dav_core_insert_prop(const dav_resource *resource,
/* assert: info != NULL && info->name != NULL */
if (what == DAV_PROP_INSERT_SUPPORTED) {
- s = apr_psprintf(p,
- "<D:supported-live-property D:name=\"%s\" "
- "D:namespace=\"%s\"/>" DEBUG_CR,
- info->name, dav_core_namespace_uris[info->ns]);
+ s = apr_pstrcat(p,
+ "<D:supported-live-property D:name=\"", info->name,
+ "\" D:namespace=\"", dav_core_namespace_uris[info->ns],
+ "\"/>" DEBUG_CR, NULL);
}
else if (what == DAV_PROP_INSERT_VALUE && *value != '\0') {
s = apr_psprintf(p, "<lp%ld:%s>%s</lp%ld:%s>" DEBUG_CR,
diff --git a/modules/dav/main/util.c b/modules/dav/main/util.c
index 9f24604..3f7822f 100644
--- a/modules/dav/main/util.c
+++ b/modules/dav/main/util.c
@@ -101,6 +101,9 @@ DAV_DECLARE(dav_error*) dav_join_error(dav_error *dest, dav_error *src)
return dest;
}
+/* ### Unclear if this was designed to be used with an uninitialized
+ * dav_buffer struct, but is used on by dav_lock_get_activelock().
+ * Hence check for pbuf->buf. */
DAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf,
apr_size_t extra_needed)
{
@@ -110,7 +113,8 @@ DAV_DECLARE(void) dav_check_bufsize(apr_pool_t * p, dav_buffer *pbuf,
pbuf->alloc_len += extra_needed + DAV_BUFFER_PAD;
newbuf = apr_palloc(p, pbuf->alloc_len);
- memcpy(newbuf, pbuf->buf, pbuf->cur_len);
+ if (pbuf->buf)
+ memcpy(newbuf, pbuf->buf, pbuf->cur_len);
pbuf->buf = newbuf;
}
}
@@ -240,7 +244,7 @@ DAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri,
request. the port must match our port.
*/
port = r->connection->local_addr->port;
- if (strcasecmp(comp.scheme, scheme) != 0
+ if (ap_cstr_casecmp(comp.scheme, scheme) != 0
#ifdef APACHE_PORT_HANDLING_IS_BUSTED
|| comp.port != port
#endif
@@ -312,26 +316,71 @@ DAV_DECLARE(dav_lookup_result) dav_lookup_uri(const char *uri,
*/
/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
-DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc,
- const char *tagname)
+DAV_DECLARE(int) dav_validate_root_ns(const apr_xml_doc *doc,
+ int ns, const char *tagname)
{
return doc->root &&
- doc->root->ns == APR_XML_NS_DAV_ID &&
+ doc->root->ns == ns &&
strcmp(doc->root->name, tagname) == 0;
}
-/* find and return the (unique) child with a given DAV: tagname */
-DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem,
- const char *tagname)
+/* validate that the root element uses a given DAV: tagname (TRUE==valid) */
+DAV_DECLARE(int) dav_validate_root(const apr_xml_doc *doc,
+ const char *tagname)
+{
+ return dav_validate_root_ns(doc, APR_XML_NS_DAV_ID, tagname);
+}
+
+/* find and return the next child with a tagname in the given namespace */
+DAV_DECLARE(apr_xml_elem *) dav_find_next_ns(const apr_xml_elem *elem,
+ int ns, const char *tagname)
+{
+ apr_xml_elem *child = elem->next;
+
+ for (; child; child = child->next)
+ if (child->ns == ns && !strcmp(child->name, tagname))
+ return child;
+ return NULL;
+}
+
+/* find and return the (unique) child with a tagname in the given namespace */
+DAV_DECLARE(apr_xml_elem *) dav_find_child_ns(const apr_xml_elem *elem,
+ int ns, const char *tagname)
{
apr_xml_elem *child = elem->first_child;
for (; child; child = child->next)
- if (child->ns == APR_XML_NS_DAV_ID && !strcmp(child->name, tagname))
+ if (child->ns == ns && !strcmp(child->name, tagname))
return child;
return NULL;
}
+/* find and return the (unique) child with a given DAV: tagname */
+DAV_DECLARE(apr_xml_elem *) dav_find_child(const apr_xml_elem *elem,
+ const char *tagname)
+{
+ return dav_find_child_ns(elem, APR_XML_NS_DAV_ID, tagname);
+}
+
+/* find and return the attribute with a name in the given namespace */
+DAV_DECLARE(apr_xml_attr *) dav_find_attr_ns(const apr_xml_elem *elem,
+ int ns, const char *attrname)
+{
+ apr_xml_attr *attr = elem->attr;
+
+ for (; attr; attr = attr->next)
+ if (attr->ns == ns && !strcmp(attr->name, attrname))
+ return attr;
+ return NULL;
+}
+
+/* find and return the attribute with a given DAV: tagname */
+DAV_DECLARE(apr_xml_attr *) dav_find_attr(const apr_xml_elem *elem,
+ const char *attrname)
+{
+ return dav_find_attr_ns(elem, APR_XML_NS_DAV_ID, attrname);
+}
+
/* gather up all the CDATA into a single string */
DAV_DECLARE(const char *) dav_xml_get_cdata(const apr_xml_elem *elem, apr_pool_t *pool,
int strip_white)
@@ -470,8 +519,8 @@ DAV_DECLARE(void) dav_xmlns_generate(dav_xmlns_info *xi,
apr_hash_this(hi, &prefix, NULL, &uri);
- s = apr_psprintf(xi->pool, " xmlns:%s=\"%s\"",
- (const char *)prefix, (const char *)uri);
+ s = apr_pstrcat(xi->pool, " xmlns:", (const char *)prefix, "=\"",
+ (const char *)uri, "\"", NULL);
apr_text_append(xi->pool, phdr, s);
}
}
@@ -660,7 +709,13 @@ static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
/* note that parsed_uri.path is allocated; we can trash it */
/* clean up the URI a bit */
- ap_getparents(parsed_uri.path);
+ if (!ap_normalize_path(parsed_uri.path,
+ AP_NORMALIZE_NOT_ABOVE_ROOT |
+ AP_NORMALIZE_DECODE_UNRESERVED)) {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_TAGGED, rv,
+ "Invalid URI path tagged If-header.");
+ }
/* the resources we will compare to have unencoded paths */
if (ap_unescape_url(parsed_uri.path) != OK) {
@@ -746,8 +801,14 @@ static dav_error * dav_process_if_header(request_rec *r, dav_if_header **p_ih)
"for the same state.");
}
condition = DAV_IF_COND_NOT;
+ list += 2;
+ }
+ else {
+ return dav_new_error(r->pool, HTTP_BAD_REQUEST,
+ DAV_ERR_IF_UNK_CHAR, 0,
+ "Invalid \"If:\" header: "
+ "Unexpected character in List");
}
- list += 2;
break;
case ' ':