Commit 9b94525b authored by Greg Stein's avatar Greg Stein
Browse files

Patch to sync with some changes to mod_dav 1.1:

*) revamp the set_target stuff -- latest draft calls this UPDATE
*) update the CHECKIN method handling
*) liveprop providers can catch/define "core" properties before the core
   gets a chance.

Submitted by: John Vasta <jvasta@rational.com>
Reviewed by: Greg Stein


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88599 13f79535-47bb-0310-9956-ffa450edef68
parent 28a46a6d
Loading
Loading
Loading
Loading
+106 −120
Original line number Diff line number Diff line
@@ -517,6 +517,7 @@ static void dav_log_err(request_rec *r, dav_error *err, int level)
**   - repos_hooks->remove_resource
**   - repos_hooks->move_resource
**   - repos_hooks->copy_resource
**   - vsn_hooks->update
*/
static int dav_handle_err(request_rec *r, dav_error *err,
			  dav_response *response)
@@ -2848,13 +2849,8 @@ static int dav_method_lock(request_rec *r)
	return HTTP_BAD_REQUEST;
    }

    /* Ask repository module to resolve the resource.
     * DeltaV says result of target selector is undefined,
     * so allow it, and let provider reject the lock attempt
     * on a version if it wants to.
     */
    /* ### gjs: I'm not sure we want to allow for locking a version... */
    err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
    /* Ask repository module to resolve the resource */
    err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
                           &resource);
    if (err != NULL)
        return dav_handle_err(r, err, NULL);
@@ -3035,13 +3031,8 @@ static int dav_method_unlock(request_rec *r)
	return dav_handle_err(r, err, NULL);
    }

    /* Ask repository module to resolve the resource.
     * DeltaV says result of target selector is undefined,
     * so allow it, and let provider reject the unlock attempt
     * on a version if it wants to.
     */
    /* ### gjs: I'm not sure we want to allow for locking a version... */
    err = dav_get_resource(r, 1 /* label_allowed */, 0 /* use_checked_in */,
    /* Ask repository module to resolve the resource */
    err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
                           &resource);
    if (err != NULL)
        return dav_handle_err(r, err, NULL);
@@ -3475,13 +3466,26 @@ static int dav_method_checkin(request_rec *r)
    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
    dav_error *err;
    int result;
    ap_xml_doc *doc;
    int keep_checked_out = 0;

    /* If no versioning provider, decline the request */
    if (vsn_hooks == NULL)
        return DECLINED;

    if ((result = ap_discard_request_body(r)) != OK) {
    if ((result = ap_xml_parse_input(r, &doc)) != OK)
	return result;

    if (doc != NULL) {
        if (!dav_validate_root(doc, "checkin")) {
            /* This supplies additional information for the default msg. */
            ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
                          "The request body, if present, must be a "
                          "DAV:checkin element.");
            return HTTP_BAD_REQUEST;
        }

        keep_checked_out = dav_find_child(doc->root, "keep-checked-out") != NULL;
    }

    /* Ask repository module to resolve the resource */
@@ -3509,13 +3513,14 @@ static int dav_method_checkin(request_rec *r)

    if (!resource->working) {
	return dav_error_response(r, HTTP_CONFLICT,
				  "The resource is not checked out to the workspace.");
				  "The resource is not checked out.");
    }

    /* ### do lock checks, once behavior is defined */

    /* Do the checkin */
    if ((err = (*vsn_hooks->checkin)(resource, &new_version)) != NULL) {
    if ((err = (*vsn_hooks->checkin)(resource, keep_checked_out, &new_version))
        != NULL) {
	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
			     apr_psprintf(r->pool,
					 "Could not CHECKIN resource %s.",
@@ -3527,80 +3532,27 @@ static int dav_method_checkin(request_rec *r)
    return dav_created(r, new_version->uri, "Version", 0);
}

/* context maintained during SET-TARGET treewalk */
typedef struct dav_set_target_walker_ctx
{
    /* input: */
    dav_walk_params w;

    /* target specifier */
    const char *target;

    /* flag for whether target is version URI or label */
    int is_label;

    /* version provider hooks */
    const dav_hooks_vsn *vsn_hooks;

} dav_set_target_walker_ctx;

static dav_error * dav_set_target_walker(dav_walk_resource *wres, int calltype)
{
    dav_set_target_walker_ctx *ctx = wres->walk_ctx;
    dav_error *err = NULL;

    /* Check the state of the resource: must be a checked-in version
     * or baseline 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_REGULAR
        || !wres->resource->versioned || wres->resource->working) {
	err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0,
			    "<DAV:must-be-checked-in-version-selector/>");
    }
    else {
        /* do the set-target operation */
        err = (*ctx->vsn_hooks->set_target)(wres->resource, ctx->target, ctx->is_label);
    }

    if (err != NULL) {
        /* ### need utility routine to add response with description? */
        dav_add_response(wres, err->status, NULL);
        wres->response->desc = err->desc;
    }

    return NULL;
}

static int dav_method_set_target(request_rec *r)
static int dav_method_update(request_rec *r)
{
    dav_resource *resource;
    dav_resource *version = NULL;
    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
    ap_xml_doc *doc;
    ap_xml_elem *child;
    int is_label = 0;
    int depth;
    int result;
    apr_size_t tsize;
    int tsize;
    const char *target;
    dav_response *multi_response;
    dav_error *err;
    dav_set_target_walker_ctx ctx = { { 0 } };
    dav_response *multi_status;
    dav_lookup_result lookup;

    /* If no versioning provider, decline the request */
    if (vsn_hooks == NULL)
    /* If no versioning provider, or UPDATE not supported,
     * decline the request */
    if (vsn_hooks == NULL || vsn_hooks->update == NULL)
        return DECLINED;

    /* Ask repository module to resolve the resource */
    err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
                           &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 ((depth = dav_get_depth(r, 0)) < 0) {
	/* dav_get_depth() supplies additional information for the
	 * default message. */
@@ -3612,21 +3564,18 @@ static int dav_method_set_target(request_rec *r)
	return result;
    }

    if (doc == NULL || !dav_validate_root(doc, "set-target")) {
    if (doc == NULL || !dav_validate_root(doc, "update")) {
	/* This supplies additional information for the default message. */
	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
		      "The request body does not contain "
		      "a \"set-target\" element.");
		      "an \"update\" element.");
	return HTTP_BAD_REQUEST;
    }

    /* check for label-name or version element */
    if ((child = dav_find_child(doc->root, "label-name")) != NULL) {
        ctx.is_label = 1;
    }
    /* check for label-name or version element, but not both */
    if ((child = dav_find_child(doc->root, "label-name")) != NULL)
        is_label = 1;
    else if ((child = dav_find_child(doc->root, "version")) != NULL) {
        ctx.is_label = 0;

        /* get the href element */
        if ((child = dav_find_child(child, "href")) == NULL) {
	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
@@ -3637,14 +3586,21 @@ static int dav_method_set_target(request_rec *r)
    }
    else {
	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
		      "The \"set-target\" element does not contain "
		      "The \"update\" element does not contain "
		      "a \"label-name\" or \"version\" element.");
	return HTTP_BAD_REQUEST;
    }

    /* get the target value (a label or a version URI */
    /* a depth greater than zero is only allowed for a label */
    if (!is_label && depth != 0) {
	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
		      "Depth must be zero for UPDATE with a version");
	return HTTP_BAD_REQUEST;
    }

    /* get the target value (a label or a version URI) */
    ap_xml_to_text(r->pool, child, AP_XML_X2T_INNER, NULL, NULL,
                   &ctx.target, &tsize);
                   &target, &tsize);
    if (tsize == 0) {
	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
		      "A \"label-name\" or \"href\" element does not contain "
@@ -3652,39 +3608,69 @@ static int dav_method_set_target(request_rec *r)
	return HTTP_BAD_REQUEST;
    }

    /* do the set-target operation walk */
    ctx.w.walk_type = DAV_WALKTYPE_NORMAL;
    ctx.w.func = dav_set_target_walker;
    ctx.w.walk_ctx = &ctx;
    ctx.w.pool = r->pool;
    ctx.w.root = resource;
    ctx.vsn_hooks = vsn_hooks;

    err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
    /* Ask repository module to resolve the resource */
    err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
                           &resource);
    if (err != NULL)
        return dav_handle_err(r, err, NULL);

    if (err != NULL) {
        /* some sort of error occurred which terminated the walk */
        err = dav_push_error(r->pool, err->status, 0,
                             "The SET-TARGET operation was terminated prematurely.",
                             err);
        return dav_handle_err(r, err, multi_status);
    if (!resource->exists) {
        /* Apache will supply a default error for this. */
        return HTTP_NOT_FOUND;
    }

    if (multi_status != NULL) {
        /* One or more resources had errors. If depth was zero, convert
         * response to simple error, else make sure there is an
         * overall error to pass to dav_handle_err()
    /* ### need a general mechanism for reporting precondition violations
     * ### (should be returning XML document for 403/409 responses)
     */
        if (depth == 0) {
            err = dav_new_error(r->pool, multi_status->status, 0, multi_status->desc);
            multi_status = NULL;
    if (resource->type != DAV_RESOURCE_TYPE_REGULAR
        || !resource->versioned || resource->working) {
	return dav_error_response(r, HTTP_CONFLICT,
				  "<DAV:must-be-checked-in-version-controlled-resource>");
    }
        else {
            err = dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
                                "Errors occurred during the SET-TARGET operation.");

    /* if target is a version, resolve the version resource */
    /* ### dav_lookup_uri only allows absolute URIs; is that OK? */
    if (!is_label) {
        lookup = dav_lookup_uri(target, r);
        if (lookup.rnew == NULL) {
	    if (lookup.err.status == HTTP_BAD_REQUEST) {
	        /* This supplies additional information for the default message. */
	        ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
			      lookup.err.desc);
	        return HTTP_BAD_REQUEST;
	    }

        return dav_handle_err(r, err, multi_status);
	    /* ### this assumes that dav_lookup_uri() only generates a status
	     * ### that Apache can provide a status line for!! */

	    return dav_error_response(r, lookup.err.status, lookup.err.desc);
        }
        if (lookup.rnew->status != HTTP_OK) {
	    /* ### how best to report this... */
	    return dav_error_response(r, lookup.rnew->status,
				      "Version URI had an error.");
        }

        /* resolve version resource */
        err = dav_get_resource(lookup.rnew, 0 /* label_allowed */,
                               0 /* use_checked_in */, &version);
        if (err != NULL)
            return dav_handle_err(r, err, NULL);

        /* NULL out target, since we're using a version resource */
        target = NULL;
    }

    /* do the UPDATE operation */
    err = (*vsn_hooks->update)(resource, version, target, depth, &multi_response);

    if (err != NULL) {
        err = dav_push_error(r->pool, err->status, 0,
			     ap_psprintf(r->pool,
					 "Could not UPDATE %s.",
					 ap_escape_html(r->pool, r->uri)),
                             err);
        return dav_handle_err(r, err, multi_response);
    }

    /* set the Cache-Control header, per the spec */
@@ -4402,8 +4388,8 @@ static int dav_handler(request_rec *r)
	return dav_method_checkin(r);
    }

    if (!strcmp(r->method, "SET-TARGET")) {
	return dav_method_set_target(r);
    if (!strcmp(r->method, "UPDATE")) {
	return dav_method_update(r);
    }

    if (!strcmp(r->method, "LABEL")) {
+37 −19
Original line number Diff line number Diff line
@@ -321,12 +321,12 @@ typedef struct dav_resource_private dav_resource_private;
**     baselined  = 0
**     working    = 0
**
** version/baseline selector:
** version-controlled resource or configuration:
**     type       = DAV_RESOURCE_TYPE_REGULAR
**     exists     = 1
**     collection = ? (1 if collection)
**     versioned  = 1
**     baselined  = ? (1 if baseline selector)
**     baselined  = ? (1 if configuration)
**     working    = ? (1 if checked out)
**
** version/baseline history:
@@ -357,9 +357,9 @@ typedef struct dav_resource_private dav_resource_private;
**     type       = DAV_RESOURCE_TYPE_WORKSPACE
**     exists     = ? (1 if exists)
**     collection = 1
**     versioned  = * (jvasta: I'm seeking clarification on whether a
**     baselined  = *  workspace can be versioned or baselined)
**     working    = *
**     versioned  = ? (1 if version-controlled)
**     baselined  = ? (1 if baseline-controlled)
**     working    = ? (1 if checked out)
**
** activity:
**     type       = DAV_RESOURCE_TYPE_ACTIVITY
@@ -383,7 +383,7 @@ typedef struct dav_resource {
                         * and is always 1 for VERSION and WORKING */

    int baselined;      /* 0 => not baselined; can be 1 for
                         * REGULAR and VERSION resources;
                         * REGULAR, VERSION, and WORKSPACE resources;
                         * versioned == 1 when baselined == 1 */

    int working;	/* 0 => not checked out; can be 1 for
@@ -1937,31 +1937,26 @@ struct dav_hooks_vsn
                            apr_array_header_t *activities,
                            dav_resource **working_resource);

    /* Uncheckout a resource. If successful, the resource
    /* Uncheckout a checked-out resource. If successful, the resource
     * object state is updated appropriately.
     */
    dav_error * (*uncheckout)(dav_resource *resource);

    /* Checkin a working resource. If successful, the resource
    /* Checkin a checked-out resource. If successful, the resource
     * object state is updated appropriately, and the
     * version_resource descriptor will refer to the new version.
     * The version_resource argument can be NULL if the caller
     * is not interested in the new version resource.
     *
     * If the client has specified DAV:keep-checked-out in the checkin
     * request, then the keep_checked_out flag is set. The provider
     * should create a new version, but keep the resource in the
     * checked-out state.
     */
    dav_error * (*checkin)(dav_resource *resource,
                           int keep_checked_out,
                           dav_resource **version_resource);

    /*
    ** Set the default target of a version selector or
    ** baseline selector resource.
    ** The target argument specifies the new target, which
    ** can be a label, if is_label != 0, or a version URI,
    ** if is_label == 0.
    */
    dav_error * (*set_target)(const dav_resource *resource,
	                      const char *target,
                              int is_label);

    /* Determine whether a non-versioned (or non-existent) resource
     * is versionable. Returns != 0 if resource can be versioned.
     */
@@ -2015,6 +2010,29 @@ struct dav_hooks_vsn
    ** corresponding protocol methods will be unsupported.
    */

    /*
    ** Set the state of a checked-in version-controlled resource.
    **
    ** If the request specified a version, the version resource
    ** represents that version. If the request specified a label,
    ** then "version" is NULL, and "label" is the label.
    **
    ** The depth argument is ignored for a file, and can be 0, 1, or
    ** DAV_INFINITY for a collection. The depth argument only applies
    ** with a label, not a version.
    **
    ** If an error occurs in a child resource, then the return value is
    ** non-NULL, and *response is set to a multistatus response.
    **
    ** This hook is optional; if not defined, then the UPDATE method
    ** will not be supported.
    */
    dav_error * (*update)(const dav_resource *resource,
                          const dav_resource *version,
                          const char *label,
                          int depth,
                          dav_response **response);

    /*
    ** Add a label to a version. The resource is either a specific
    ** version, or a version selector, in which case the label should
+10 −10
Original line number Diff line number Diff line
@@ -337,23 +337,23 @@ static int dav_find_liveprop_provider(dav_propdb *propdb,
	/* policy: liveprop providers cannot define no-namespace properties */
	return DAV_PROPID_CORE_UNKNOWN;
    }
    else if (strcmp(ns_uri, "DAV:") == 0) {
	const char * const *p = dav_core_props;

	for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
	    if (strcmp(propname, *p) == 0) {
    /* check liveprop providers first, so they can define core properties */
    propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
                                   provider);
    if (propid != 0) {
        return propid;
    }

	/* didn't find it. fall thru. a provider can define DAV: props */
    }
    /* check for core property */
    if (strcmp(ns_uri, "DAV:") == 0) {
	const char * const *p = dav_core_props;

    /* is there a liveprop provider for this property? */
    propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname,
                                   provider);
    if (propid != 0) {
	for (propid = DAV_PROPID_CORE; *p != NULL; ++p, ++propid)
	    if (strcmp(propname, *p) == 0) {
		return propid;
	    }
    }

    /* no provider for this property */
    return DAV_PROPID_CORE_UNKNOWN;
+4 −2
Original line number Diff line number Diff line
@@ -1739,7 +1739,8 @@ dav_error *dav_revert_resource_writability(
            if (undo)
                err = (*vsn_hooks->uncheckout)(resource);
            else
                err = (*vsn_hooks->checkin)(resource, NULL);
                err = (*vsn_hooks->checkin)(resource,
                                            0 /*keep_checked_out*/, NULL);

            if (err != NULL) {
	        body = apr_psprintf(r->pool,
@@ -1774,7 +1775,8 @@ dav_error *dav_revert_resource_writability(
	if (undo)
	    err = (*vsn_hooks->uncheckout)(av_info->parent_resource);
	else
	    err = (*vsn_hooks->checkin)(av_info->parent_resource, NULL);
	    err = (*vsn_hooks->checkin)(av_info->parent_resource,
                                        0 /*keep_checked_out*/, NULL);

	if (err != NULL) {
	    body = apr_psprintf(r->pool,