From: Greg Stein Date: Wed, 28 Mar 2001 07:37:25 +0000 (+0000) Subject: Patch to sync with some changes to mod_dav 1.1: X-Git-Tag: 2.0.16~61 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5ba260cc0c41d2f84d1a255973a9f1fbffbcf3b7;p=thirdparty%2Fapache%2Fhttpd.git 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 Reviewed by: Greg Stein git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@88599 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c index 9a80b1577a9..b703871c693 100644 --- a/modules/dav/main/mod_dav.c +++ b/modules/dav/main/mod_dav.c @@ -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, - ""); - } - 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() - */ - if (depth == 0) { - err = dav_new_error(r->pool, multi_status->status, 0, multi_status->desc); - multi_status = NULL; + /* ### need a general mechanism for reporting precondition violations + * ### (should be returning XML document for 403/409 responses) + */ + if (resource->type != DAV_RESOURCE_TYPE_REGULAR + || !resource->versioned || resource->working) { + return dav_error_response(r, HTTP_CONFLICT, + ""); + } + + /* 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; + } + + /* ### 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); } - else { - err = dav_new_error(r->pool, HTTP_MULTI_STATUS, 0, - "Errors occurred during the SET-TARGET operation."); + if (lookup.rnew->status != HTTP_OK) { + /* ### how best to report this... */ + return dav_error_response(r, lookup.rnew->status, + "Version URI had an error."); } - return dav_handle_err(r, err, multi_status); + /* 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")) { diff --git a/modules/dav/main/mod_dav.h b/modules/dav/main/mod_dav.h index e71c06462b8..6278624ce9d 100644 --- a/modules/dav/main/mod_dav.h +++ b/modules/dav/main/mod_dav.h @@ -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 diff --git a/modules/dav/main/props.c b/modules/dav/main/props.c index 9f2c6d7daa5..2929a9a2971 100644 --- a/modules/dav/main/props.c +++ b/modules/dav/main/props.c @@ -337,22 +337,22 @@ 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) { + + /* 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; + } + + /* check for core property */ + 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) { return propid; } - - /* didn't find it. fall thru. a provider can define DAV: props */ - } - - /* is there a liveprop provider for this property? */ - propid = dav_run_find_liveprop(propdb->resource, ns_uri, propname, - provider); - if (propid != 0) { - return propid; } /* no provider for this property */ diff --git a/modules/dav/main/util.c b/modules/dav/main/util.c index 09df3b429eb..7c3d844a8b7 100644 --- a/modules/dav/main/util.c +++ b/modules/dav/main/util.c @@ -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,