summaryrefslogtreecommitdiffstats
path: root/modules/dav/main/mod_dav.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-25 04:41:26 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-25 04:41:26 +0000
commit7b31d4f4901cdb89a79f2f7de4a6b8bb637b523b (patch)
treefdeb0b5ff80273f95ce61607fc3613dff0b9a235 /modules/dav/main/mod_dav.c
parentAdding upstream version 2.4.38. (diff)
downloadapache2-upstream.tar.xz
apache2-upstream.zip
Adding upstream version 2.4.59.upstream/2.4.59upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/dav/main/mod_dav.c')
-rw-r--r--modules/dav/main/mod_dav.c635
1 files changed, 480 insertions, 155 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)