You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by gs...@locus.apache.org on 2000/11/15 03:05:12 UTC

cvs commit: apache-2.0/src/modules/dav/main mod_dav.c mod_dav.h props.c util.c

gstein      00/11/14 18:05:12

  Modified:    src/modules/dav/fs repos.c
               src/modules/dav/main mod_dav.c mod_dav.h props.c util.c
  Log:
  Apply a patch from John Vasta for adding (some/more) DeltaV support to
  mod_dav. The patch applied and compiled cleanly, so I'm committing. Any
  necessary changes from a review will come later, so that we can easily track
  what needed to change (and can be back-ported to mod_dav 1.1).
  
  Submitted by: John Vasta <jv...@rational.com>
  
  Revision  Changes    Path
  1.28      +0 -1      apache-2.0/src/modules/dav/fs/repos.c
  
  Index: repos.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/modules/dav/fs/repos.c,v
  retrieving revision 1.27
  retrieving revision 1.28
  diff -u -r1.27 -r1.28
  --- repos.c	2000/11/09 13:08:05	1.27
  +++ repos.c	2000/11/15 02:05:11	1.28
  @@ -591,7 +591,6 @@
   static dav_resource * dav_fs_get_resource(
       request_rec *r,
       const char *root_dir,
  -    const char *workspace,
       const char *target,
       int is_label)
   {
  
  
  
  1.29      +880 -112  apache-2.0/src/modules/dav/main/mod_dav.c
  
  Index: mod_dav.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/modules/dav/main/mod_dav.c,v
  retrieving revision 1.28
  retrieving revision 1.29
  diff -u -r1.28 -r1.29
  --- mod_dav.c	2000/11/09 13:08:06	1.28
  +++ mod_dav.c	2000/11/15 02:05:12	1.29
  @@ -258,6 +258,11 @@
       return dav_get_provider(r)->vsn;
   }
   
  +const dav_hooks_binding *dav_get_binding_hooks(request_rec *r)
  +{
  +    return dav_get_provider(r)->binding;
  +}
  +
   /*
    * Command handler for the DAV directive, which is TAKE1.
    */
  @@ -618,7 +623,6 @@
   {
       void *data;
       dav_dir_conf *conf;
  -    const char *workspace = NULL;
       const char *target_selector = NULL;
       int is_label = 0;
       int result;
  @@ -630,10 +634,6 @@
           return OK;
       }
   
  -    /* get any workspace header */
  -    if ((result = dav_get_workspace(r, &workspace)) != OK)
  -        return result;
  -
       /* if the request target can be overridden, get any target selector */
       if (target_allowed) {
           if ((result = dav_get_target_selector(r, target,
  @@ -646,7 +646,7 @@
       /* assert: conf->provider != NULL */
   
       /* resolve the resource */
  -    *res_p = (*conf->provider->repos->get_resource)(r, conf->dir, workspace,
  +    *res_p = (*conf->provider->repos->get_resource)(r, conf->dir,
                                                       target_selector, is_label);
       if (*res_p == NULL) {
           /* Apache will supply a default error for this. */
  @@ -710,46 +710,6 @@
       return 1;
   }
   
  -static int available_report(request_rec *r, const dav_resource *resource)
  -{
  -    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  -    dav_error *err;
  -    const dav_report_elem *reports;
  -
  -    if ((err = (*vsn_hooks->avail_reports)(resource, &reports)) != NULL) {
  -	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  -			     apr_psprintf(r->pool,
  -                                          "Could not fetch a list of the "
  -                                          "available reports."),
  -			     err);
  -        return dav_handle_err(r, err, NULL);
  -    }
  -
  -    /* set the correct status and Content-Type */
  -    r->status = 200;
  -    r->content_type = DAV_XML_CONTENT_TYPE;
  -
  -    /* send all the headers now */
  -    ap_send_http_header(r);
  -
  -    /* send the response... */
  -    ap_rputs(DAV_XML_HEADER DEBUG_CR
  -             "<D:report-set xmlns:D=\"DAV:\">" DEBUG_CR
  -             "<D:available-report/>" DEBUG_CR,
  -             r);
  -
  -    for (; reports->nmspace != NULL; ++reports) {
  -        /* Note: we presume reports->namespace is propertly XML/URL quoted */
  -        ap_rprintf(r, "<%s xmlns=\"%s\"/>" DEBUG_CR,
  -                   reports->name, reports->nmspace);
  -    }
  -
  -    ap_rputs("</D:report-set>" DEBUG_CR, r);
  -
  -    /* the response has been sent. */
  -    return DONE;
  -}
  -
   /* handle the GET method */
   static int dav_method_get(request_rec *r)
   {
  @@ -1170,7 +1130,7 @@
       /* NOTE: WebDAV spec, S8.7.1 states properties should be unaffected */
   
       /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
  -    return dav_created(r, NULL, "Resource", !av_info.resource_created);
  +    return dav_created(r, NULL, "Resource", resource_state == DAV_RESOURCE_EXISTS);
   }
   
   /* ### move this to dav_util? */
  @@ -1307,6 +1267,7 @@
   {
       const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
       const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  +    const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r);
       dav_resource *resource;
       const char *options;
       const char *dav_level;
  @@ -1335,8 +1296,10 @@
       if (locks_hooks != NULL) {
           dav_level = "1,2";
       }
  -    if (vsn_hooks != NULL) {
  -        vsn_level = (*vsn_hooks->get_vsn_header)();
  +
  +    if (vsn_hooks != NULL
  +        && (vsn_level = (*vsn_hooks->get_vsn_header)()) != NULL) {
  +	dav_level = apr_pstrcat(r->pool, dav_level, ",", vsn_level, NULL);
       }
   
       /* gather property set URIs from all the liveprop providers */
  @@ -1407,17 +1370,29 @@
       if (vsn_hooks != NULL) {
           const char *vsn_options = NULL;
   
  -        /* ### take into account resource type */
           if (!resource->exists) {
  -            if ((*vsn_hooks->versionable)(resource))
  -                vsn_options = ", MKRESOURCE";
  +            int vsn_control = (*vsn_hooks->versionable)(resource);
  +            int mkworkspace = vsn_hooks->can_be_workspace != NULL
  +                              && (*vsn_hooks->can_be_workspace)(resource);
  +
  +            if (vsn_control && mkworkspace) {
  +                vsn_options = ", VERSION-CONTROL, MKWORKSPACE";
  +            }
  +            else if (vsn_control)
  +                vsn_options = ", VERSION-CONTROL";
  +            else if (mkworkspace) {
  +                vsn_options = ", MKWORKSPACE";
  +            }
           }
           else if (!resource->versioned) {
  -            if ((*vsn_hooks->versionable)(resource))
  -                vsn_options = ", CHECKIN";
  +            if ((*vsn_hooks->versionable)(resource)) {
  +                vsn_options = ", VERSION-CONTROL";
  +            }
           }
           else if (resource->working)
               vsn_options = ", CHECKIN, UNCHECKOUT";
  +        else if (vsn_hooks->add_label != NULL)
  +            vsn_options = ", CHECKOUT, LABEL";
           else
               vsn_options = ", CHECKOUT";
   
  @@ -1425,12 +1400,16 @@
               options = apr_pstrcat(r->pool, options, vsn_options, NULL);
       }
   
  +    /* If there is a bindings provider, see if resource is bindable */
  +    if (binding_hooks != NULL) {
  +	dav_level = apr_pstrcat(r->pool, dav_level, ",bindings", NULL);
  +        if ((*binding_hooks->is_bindable)(resource))
  +            options = apr_pstrcat(r->pool, options, ", BIND", NULL);
  +    }
  +
       apr_table_setn(r->headers_out, "Allow", options);
       apr_table_setn(r->headers_out, "DAV", dav_level);
   
  -    if (vsn_level != NULL)
  -        apr_table_setn(r->headers_out, "Versioning", vsn_level);
  -
       /* ### this will send a Content-Type. the default OPTIONS does not. */
       ap_send_http_header(r);
   
  @@ -2730,8 +2709,185 @@
   
   static int dav_method_vsn_control(request_rec *r)
   {
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  +    dav_resource *resource;
  +    int resource_state;
  +    dav_auto_version_info av_info;
  +    const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
  +    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  +    dav_error *err;
  +    ap_xml_doc *doc;
  +    ap_xml_elem *child;
  +    const char *target = NULL;
  +    int result;
  +
  +    /* if no versioning provider, decline the request */
  +    if (vsn_hooks == NULL)
  +        return DECLINED;
  +
  +    /* ask repository module to resolve the resource */
  +    result = dav_get_resource(r, 0 /*target_allowed*/, NULL, &resource);
  +    if (result != OK)
  +        return result;
  +
  +    /* 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 */
  +
  +    if (doc != NULL) {
  +        if (!dav_validate_root(doc, "version-control")) {
  +	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		          "The request body does not contain "
  +		          "a \"version-control\" element.");
  +	    return HTTP_BAD_REQUEST;
  +        }
  +
  +        /* get the version URI */
  +        if ((child = dav_find_child(doc->root, "version")) == NULL) {
  +	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		          "The \"version-control\" element does not contain "
  +		          "a \"version\" element.");
  +	    return HTTP_BAD_REQUEST;
  +        }
  +
  +        if ((child = dav_find_child(child, "href")) == NULL) {
  +	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		          "The \"version\" element does not contain "
  +		          "an \"href\" element.");
  +	    return HTTP_BAD_REQUEST;
  +        }
  +
  +        /* get version URI */
  +        ap_xml_to_text(r->pool, child, AP_XML_X2T_INNER, NULL, NULL, &target, NULL);
  +        if (strlen(target) == 0) {
  +	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		          "An \"href\" element does not contain a URI.");
  +	    return HTTP_BAD_REQUEST;
  +        }
  +    }
  +
  +    /* Check request preconditions */
  +
  +    /* ### need a general mechanism for reporting precondition violations
  +     * ### (should be returning XML document for 403/409 responses)
  +     */
  +
  +    /* if not versioning existing resource, must specify version to select */
  +    if (!resource->exists && target == NULL) {
  +        err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +                            "<DAV:initial-version-required/>");
  +	return dav_handle_err(r, err, NULL);
  +    }
  +    else if (resource->exists) {
  +        /* cannot add resource to existing version history */
  +        if (target != NULL) {
  +            err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +                                "<DAV:cannot-add-to-existing-history/>");
  +	    return dav_handle_err(r, err, NULL);
  +        }
  +
  +        /* resource must be unversioned and versionable, or version selector */
  +        if (resource->type != DAV_RESOURCE_TYPE_REGULAR
  +            || (!resource->versioned && !(vsn_hooks->versionable)(resource))) {
  +            err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +                                "<DAV:must-be-versionable/>");
  +	    return dav_handle_err(r, err, NULL);
  +        }
  +
  +        /* the DeltaV spec says if resource is a version selector,
  +         * then VERSION-CONTROL is a no-op
  +         */
  +        if (resource->versioned) {
  +            /* set the Cache-Control header, per the spec */
  +            apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
  +
  +            /* no body */
  +            ap_set_content_length(r, 0);
  +            ap_send_http_header(r);
  +
  +            return DONE;
  +        }
  +    }
  +
  +    /* Check If-Headers and existing locks */
  +    /* Note: depth == 0. Implies no need for a multistatus response. */
  +    if ((err = dav_validate_request(r, resource, 0, NULL, NULL,
  +				    resource_state == DAV_RESOURCE_NULL ?
  +				    DAV_VALIDATE_PARENT :
  +				    DAV_VALIDATE_RESOURCE, NULL)) != NULL) {
  +	return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* if in versioned collection, make sure parent is checked out */
  +    if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
  +					    &av_info)) != NULL) {
  +	return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* attempt to version-control the resource */
  +    if ((err = (*vsn_hooks->vsn_control)(resource, target)) != NULL) {
  +        dav_revert_resource_writability(r, resource, 1 /*undo*/, &av_info);
  +	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  +			     apr_psprintf(r->pool,
  +					 "Could not VERSION-CONTROL resource %s.",
  +					 ap_escape_html(r->pool, r->uri)),
  +			     err);
  +        return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* revert writability of parent directory */
  +    err = dav_revert_resource_writability(r, resource, 0 /*undo*/, &av_info);
  +    if (err != NULL) {
  +        /* just log a warning */
  +	err = dav_push_error(r->pool, err->status, 0,
  +			     "The VERSION-CONTROL was successful, but there "
  +			     "was a problem reverting the writability of "
  +			     "the parent collection.",
  +			     err);
  +        dav_log_err(r, err, APLOG_WARNING);
  +    }
  +
  +    /* if the resource is lockable, let lock system know of new resource */
  +    if (locks_hooks != NULL
  +	&& (*locks_hooks->get_supportedlock)(resource) != NULL) {
  +	dav_lockdb *lockdb;
  +
  +	if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, &lockdb)) != NULL) {
  +	    /* The resource creation was successful, but the locking failed. */
  +	    err = dav_push_error(r->pool, err->status, 0,
  +				 "The VERSION-CONTROL was successful, but there "
  +				 "was a problem opening the lock database "
  +				 "which prevents inheriting locks from the "
  +				 "parent resources.",
  +				 err);
  +	    return dav_handle_err(r, err, NULL);
  +	}
  +
  +	/* notify lock system that we have created/replaced a resource */
  +	err = dav_notify_created(r, lockdb, resource, resource_state, 0);
  +
  +	(*locks_hooks->close_lockdb)(lockdb);
  +
  +	if (err != NULL) {
  +	    /* The dir creation was successful, but the locking failed. */
  +	    err = dav_push_error(r->pool, err->status, 0,
  +				 "The VERSION-CONTROL was successful, but there "
  +				 "was a problem updating its lock "
  +				 "information.",
  +				 err);
  +	    return dav_handle_err(r, err, NULL);
  +	}
  +    }
  +
  +    /* set the Cache-Control header, per the spec */
  +    apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
  +
  +    /* return an appropriate response (HTTP_CREATED) */
  +    return dav_created(r, resource->uri, "Version selector", 0 /*replaced*/);
   }
   
   /* handle the CHECKOUT method */
  @@ -2935,43 +3091,72 @@
       return dav_created(r, new_version->uri, "Version", 0);
   }
   
  -static int dav_method_set_target(request_rec *r)
  +/* context maintained during SET-TARGET treewalk */
  +typedef struct dav_set_target_walker_ctx
   {
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  -}
  +    /* input: */
  +    dav_walk_params w;
   
  -static int dav_method_label(request_rec *r)
  +    /* 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)
   {
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  +    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_report(request_rec *r)
  +static int dav_method_set_target(request_rec *r)
   {
       dav_resource *resource;
       const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  -    int result;
  -    int target_allowed;
       ap_xml_doc *doc;
  +    ap_xml_elem *child;
  +    int depth;
  +    int result;
  +    int tsize;
  +    dav_error *err;
  +    dav_set_target_walker_ctx ctx = { { 0 } };
  +    dav_response *multi_status;
   
       /* If no versioning provider, decline the request */
       if (vsn_hooks == NULL)
           return DECLINED;
   
  -    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 | APLOG_NOERRNO, 0, r,
  -                      "The request body must specify a report.");
  -        return HTTP_BAD_REQUEST;
  -    }
  -
       /* Ask repository module to resolve the resource */
  -    /* ### need to compute target_allowed based on report type */
  -    target_allowed = 0;
  -    result = dav_get_resource(r, target_allowed, NULL, &resource);
  +    result = dav_get_resource(r, 0 /*target_allowed*/, NULL, &resource);
       if (result != OK)
           return result;
       if (!resource->exists) {
  @@ -2979,51 +3164,630 @@
           return HTTP_NOT_FOUND;
       }
   
  -    if (dav_validate_root(doc, "available-report")) {
  -        return available_report(r, resource);
  +    if ((depth = dav_get_depth(r, 0)) < 0) {
  +	/* dav_get_depth() supplies additional information for the
  +	 * default message. */
  +	return HTTP_BAD_REQUEST;
       }
   
  -    /* ### run report hook */
  -    return DECLINED;
  -}
  +    /* parse the request body */
  +    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
  +	return result;
  +    }
   
  -static int dav_method_make_workspace(request_rec *r)
  -{
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  -}
  +    if (doc == NULL || !dav_validate_root(doc, "set-target")) {
  +	/* 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.");
  +	return HTTP_BAD_REQUEST;
  +    }
   
  -static int dav_method_make_activity(request_rec *r)
  -{
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  -}
  +    /* check for label-name or version element */
  +    if ((child = dav_find_child(doc->root, "label-name")) != NULL) {
  +        ctx.is_label = 1;
  +    }
  +    else if ((child = dav_find_child(doc->root, "version")) != NULL) {
  +        ctx.is_label = 0;
   
  -static int dav_method_baseline_control(request_rec *r)
  -{
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  -}
  +        /* get the href element */
  +        if ((child = dav_find_child(child, "href")) == NULL) {
  +	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		          "The version element does not contain "
  +		          "an \"href\" element.");
  +	    return HTTP_BAD_REQUEST;
  +        }
  +    }
  +    else {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "The \"set-target\" element does not contain "
  +		      "a \"label-name\" or \"version\" element.");
  +	return HTTP_BAD_REQUEST;
  +    }
   
  -static int dav_method_merge(request_rec *r)
  -{
  -    /* ### */
  -    return HTTP_METHOD_NOT_ALLOWED;
  -}
  +    /* 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);
  +    if (tsize == 0) {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "A \"label-name\" or \"href\" element does not contain "
  +		      "any content.");
  +	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;
   
  -/*
  - * Response handler for DAV resources
  - */
  -static int dav_handler(request_rec *r)
  -{
  -    dav_dir_conf *conf;
  +    err = (*resource->hooks->walk)(&ctx.w, depth, &multi_status);
   
  -    /* quickly ignore any HTTP/0.9 requests */
  -    if (r->assbackwards) {
  -	return DECLINED;
  +    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 (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;
  +        }
  +        else {
  +            err = dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
  +                                "Errors occurred during the SET-TARGET operation.");
  +        }
  +
  +        return dav_handle_err(r, err, multi_status);
  +    }
  +
  +    /* set the Cache-Control header, per the spec */
  +    apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
  +
  +    /* no body */
  +    ap_set_content_length(r, 0);
  +    ap_send_http_header(r);
  +
  +    return DONE;
  +}
  +
  +/* context maintained during LABEL treewalk */
  +typedef struct dav_label_walker_ctx
  +{
  +    /* input: */
  +    dav_walk_params w;
  +
  +    /* label being manipulated */
  +    const char *label;
  +
  +    /* label operation */
  +    int label_op;
  +#define DAV_LABEL_ADD           1
  +#define DAV_LABEL_SET           2
  +#define DAV_LABEL_REMOVE        3
  +
  +    /* version provider hooks */
  +    const dav_hooks_vsn *vsn_hooks;
  +
  +} dav_label_walker_ctx;
  +
  +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 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 &&
  +        (wres->resource->type != DAV_RESOURCE_TYPE_REGULAR
  +         || !wres->resource->versioned)) {
  +	err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0,
  +			    "<DAV:must-be-version-or-version-selector/>");
  +    }
  +    else if (wres->resource->working) {
  +	err = dav_new_error(ctx->w.pool, HTTP_CONFLICT, 0,
  +			    "<DAV:must-not-be-checked-out/>");
  +    }
  +    else {
  +        /* do the label operation */
  +        if (ctx->label_op == DAV_LABEL_REMOVE)
  +	    err = (*ctx->vsn_hooks->remove_label)(wres->resource, ctx->label);
  +        else
  +	    err = (*ctx->vsn_hooks->add_label)(wres->resource, ctx->label,
  +                                               ctx->label_op == DAV_LABEL_SET);
  +    }
  +
  +    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_label(request_rec *r)
  +{
  +    dav_resource *resource;
  +    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  +    ap_xml_doc *doc;
  +    ap_xml_elem *child;
  +    int depth;
  +    int result;
  +    int tsize;
  +    dav_error *err;
  +    dav_label_walker_ctx ctx = { { 0 } };
  +    dav_response *multi_status;
  +
  +    /* If no versioning provider, or the provider doesn't support
  +     * labels, decline the request */
  +    if (vsn_hooks == NULL || vsn_hooks->add_label == NULL)
  +        return DECLINED;
  +
  +    /* Ask repository module to resolve the resource */
  +    result = dav_get_resource(r, 1 /*target_allowed*/, NULL, &resource);
  +    if (result != OK)
  +        return result;
  +    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. */
  +	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 | APLOG_NOERRNO, 0, r,
  +		      "The request body does not contain "
  +		      "a \"label\" element.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* check for add, set, or remove element */
  +    if ((child = dav_find_child(doc->root, "add")) != NULL) {
  +        ctx.label_op = DAV_LABEL_ADD;
  +    }
  +    else if ((child = dav_find_child(doc->root, "set")) != NULL) {
  +        ctx.label_op = DAV_LABEL_SET;
  +    }
  +    else if ((child = dav_find_child(doc->root, "remove")) != NULL) {
  +        ctx.label_op = DAV_LABEL_REMOVE;
  +    }
  +    else {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "The \"label\" element does not contain "
  +		      "an \"add\", \"set\", or \"remove\" element.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* get the label string */
  +    if ((child = dav_find_child(child, "label-name")) == NULL) {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "The label command element does not contain "
  +		      "a \"label-name\" element.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    ap_xml_to_text(r->pool, child, AP_XML_X2T_INNER, NULL, NULL, &ctx.label, &tsize);
  +    if (tsize == 0) {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "A \"label-name\" element does not contain "
  +		      "a label name.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* do the label operation walk */
  +    ctx.w.walk_type = DAV_WALKTYPE_NORMAL;
  +    ctx.w.func = dav_label_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);
  +
  +    if (err != NULL) {
  +        /* some sort of error occurred which terminated the walk */
  +        err = dav_push_error(r->pool, err->status, 0,
  +                             "The LABEL operation was terminated prematurely.",
  +                             err);
  +        return dav_handle_err(r, err, multi_status);
  +    }
  +
  +    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;
  +        }
  +        else {
  +            err = dav_new_error(r->pool, HTTP_MULTI_STATUS, 0,
  +                                "Errors occurred during the LABEL operation.");
  +        }
  +
  +        return dav_handle_err(r, err, multi_status);
  +    }
  +
  +    /* set the Cache-Control header, per the spec */
  +    apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
  +
  +    /* no body */
  +    ap_set_content_length(r, 0);
  +    ap_send_http_header(r);
  +
  +    return DONE;
  +}
  +
  +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 target_allowed;
  +    ap_xml_doc *doc;
  +    ap_text_header hdr = { 0 };
  +    ap_text *t;
  +    dav_error *err;
  +
  +    /* If no versioning provider, decline the request */
  +    if (vsn_hooks == NULL)
  +        return DECLINED;
  +
  +    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 | APLOG_NOERRNO, 0, r,
  +                      "The request body must specify a report.");
  +        return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* Ask repository module to resolve the resource.
  +     * First determine whether a Target-Selector header is allowed
  +     * for this report.
  +     */
  +    target_allowed = (*vsn_hooks->report_target_selector_allowed)(doc);
  +    result = dav_get_resource(r, target_allowed, NULL, &resource);
  +    if (result != OK)
  +        return result;
  +    if (!resource->exists) {
  +        /* Apache will supply a default error for this. */
  +        return HTTP_NOT_FOUND;
  +    }
  +
  +    /* run report hook */
  +    /* ### writing large reports to memory could be bad...
  +     * ### but if provider generated output directly, it would
  +     * ### have to handle error responses as well.
  +     */
  +    if ((err = (*vsn_hooks->get_report)(r, resource, doc, &hdr)) != NULL)
  +	return dav_handle_err(r, err, NULL);
  +
  +    /* send the report response */
  +    r->status = HTTP_OK;
  +    r->content_type = DAV_XML_CONTENT_TYPE;
  +
  +    /* send the headers and start a timeout */
  +    ap_send_http_header(r);
  +
  +    /* send the response body */
  +    ap_rputs(DAV_XML_HEADER DEBUG_CR, r);
  +
  +    for (t = hdr.first; t != NULL; t = t->next)
  +        ap_rputs(t->text, r);
  +
  +    return DONE;
  +}
  +
  +static int dav_method_make_workspace(request_rec *r)
  +{
  +    dav_resource *resource;
  +    const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  +    dav_error *err;
  +    ap_xml_doc *doc;
  +    int result;
  +
  +    /* if no versioning provider, or the provider does not support workspaces,
  +     * decline the request
  +     */
  +    if (vsn_hooks == NULL || vsn_hooks->make_workspace == NULL)
  +        return DECLINED;
  +
  +    /* ask repository module to resolve the resource */
  +    result = dav_get_resource(r, 0 /*target_allowed*/, NULL, &resource);
  +    if (result != OK)
  +        return result;
  +
  +    /* parse the request body (must be a mkworkspace element) */
  +    if ((result = ap_xml_parse_input(r, &doc)) != OK) {
  +	return result;
  +    }
  +
  +    if (doc == NULL
  +        || !dav_validate_root(doc, "mkworkspace")) {
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "The request body does not contain "
  +		      "a \"mkworkspace\" element.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* Check request preconditions */
  +
  +    /* ### need a general mechanism for reporting precondition violations
  +     * ### (should be returning XML document for 403/409 responses)
  +     */
  +
  +    /* resource must not already exist */
  +    if (resource->exists) {
  +        err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +                            "<DAV:resource-must-be-null/>");
  +	return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* ### what about locking? */
  +
  +    /* attempt to create the workspace */
  +    if ((err = (*vsn_hooks->make_workspace)(resource, doc)) != NULL) {
  +	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  +			     apr_psprintf(r->pool,
  +					 "Could not create workspace %s.",
  +					 ap_escape_html(r->pool, r->uri)),
  +			     err);
  +        return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* set the Cache-Control header, per the spec */
  +    apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
  +
  +    /* return an appropriate response (HTTP_CREATED) */
  +    return dav_created(r, resource->uri, "Workspace", 0 /*replaced*/);
  +}
  +
  +static int dav_method_make_activity(request_rec *r)
  +{
  +    /* ### */
  +    return HTTP_METHOD_NOT_ALLOWED;
  +}
  +
  +static int dav_method_baseline_control(request_rec *r)
  +{
  +    /* ### */
  +    return HTTP_METHOD_NOT_ALLOWED;
  +}
  +
  +static int dav_method_merge(request_rec *r)
  +{
  +    /* ### */
  +    return HTTP_METHOD_NOT_ALLOWED;
  +}
  +
  +static int dav_method_bind(request_rec *r)
  +{
  +    dav_resource *resource;
  +    dav_resource *binding;
  +    dav_auto_version_info av_info;
  +    const dav_hooks_binding *binding_hooks = DAV_GET_HOOKS_BINDING(r);
  +    const char *dest;
  +    dav_error *err;
  +    dav_error *err2;
  +    dav_response *multi_response = NULL;
  +    dav_lookup_result lookup;
  +    int overwrite;
  +    int result;
  +
  +    /* If no bindings provider, decline the request */
  +    if (binding_hooks == NULL)
  +        return DECLINED;
  +
  +    /* Ask repository module to resolve the resource */
  +    result = dav_get_resource(r, 0 /*!target_allowed*/, NULL, &resource);
  +    if (result != OK)
  +        return result;
  +    if (!resource->exists) {
  +	/* Apache will supply a default error for this. */
  +	return HTTP_NOT_FOUND;
  +    }
  +
  +    /* get the destination URI */
  +    dest = apr_table_get(r->headers_in, "Destination");
  +    if (dest == NULL) {
  +	/* This supplies additional information for the default message. */
  +	ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
  +		      "The request is missing a Destination header.");
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    lookup = dav_lookup_uri(dest, 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;
  +	}
  +        else if (lookup.err.status == HTTP_BAD_GATEWAY) {
  +            /* ### Bindings protocol draft 02 says to return 507
  +             * ### (Cross Server Binding Forbidden); Apache already defines 507
  +             * ### as HTTP_INSUFFICIENT_STORAGE. So, for now, we'll return
  +             * ### HTTP_FORBIDDEN
  +             */
  +             return dav_error_response(r, HTTP_FORBIDDEN,
  +                                       "Cross server bindings are not allowed by this server.");
  +        }
  +
  +	/* ### 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,
  +				  "Destination URI had an error.");
       }
   
  +    /* resolve binding resource */
  +    result = dav_get_resource(lookup.rnew, 0 /*!target_allowed*/, NULL, &binding);
  +    if (result != OK)
  +        return result;
  +
  +    /* are the two resources handled by the same repository? */
  +    if (resource->hooks != binding->hooks) {
  +	/* ### this message exposes some backend config, but screw it... */
  +	return dav_error_response(r, HTTP_BAD_GATEWAY,
  +				  "Destination URI is handled by a "
  +				  "different repository than the source URI. "
  +				  "BIND between repositories is not possible.");
  +    }
  +
  +    /* get and parse the overwrite header value */
  +    if ((overwrite = dav_get_overwrite(r)) < 0) {
  +	/* dav_get_overwrite() supplies additional information for the
  +	 * default message. */
  +	return HTTP_BAD_REQUEST;
  +    }
  +
  +    /* quick failure test: if dest exists and overwrite is false. */
  +    if (binding->exists && !overwrite) {
  +	return dav_error_response(r, HTTP_PRECONDITION_FAILED,
  +			          "Destination is not empty and "
  +			          "Overwrite is not \"T\"");
  +    }
  +
  +    /* are the source and destination the same? */
  +    if ((*resource->hooks->is_same_resource)(resource, binding)) {
  +	return dav_error_response(r, HTTP_FORBIDDEN,
  +			          "Source and Destination URIs are the same.");
  +    }
  +
  +    /*
  +    ** Check If-Headers and existing locks for destination. Note that we
  +    ** use depth==infinity since the target (hierarchy) will be deleted
  +    ** before the move/copy is completed.
  +    **
  +    ** Note that we are overwriting the target, which implies a DELETE, so
  +    ** we are subject to the error/response rules as a DELETE. Namely, we
  +    ** will return a 424 error if any of the validations fail.
  +    ** (see dav_method_delete() for more information)
  +    */
  +    if ((err = dav_validate_request(lookup.rnew, binding, DAV_INFINITY, NULL,
  +				    &multi_response,
  +				    DAV_VALIDATE_PARENT
  +                                    | DAV_VALIDATE_USE_424, NULL)) != NULL) {
  +	err = dav_push_error(r->pool, err->status, 0,
  +			     apr_psprintf(r->pool,
  +					 "Could not BIND %s due to a "
  +					 "failed precondition on the "
  +					 "destination (e.g. locks).",
  +					 ap_escape_html(r->pool, r->uri)),
  +			     err);
  +	return dav_handle_err(r, err, multi_response);
  +    }
  +
  +    /* guard against creating circular bindings */
  +    if (resource->collection
  +	&& (*resource->hooks->is_parent_resource)(resource, binding)) {
  +	return dav_error_response(r, HTTP_FORBIDDEN,
  +			          "Source collection contains the Destination.");
  +    }
  +    if (resource->collection
  +	&& (*resource->hooks->is_parent_resource)(binding, resource)) {
  +	/* The destination must exist (since it contains the source), and
  +	 * a condition above implies Overwrite==T. Obviously, we cannot
  +	 * delete the Destination before the BIND, as that would
  +	 * delete the Source.
  +	 */
  +
  +	return dav_error_response(r, HTTP_FORBIDDEN,
  +			          "Destination collection contains the Source and "
  +			          "Overwrite has been specified.");
  +    }
  +
  +    /* prepare the destination collection for modification */
  +    if ((err = dav_ensure_resource_writable(r, binding, 1 /* parent_only */,
  +					    &av_info)) != NULL) {
  +        /* could not make destination writable */
  +	return dav_handle_err(r, err, NULL);
  +    }
  +
  +    /* If target exists, remove it first (we know Ovewrite must be TRUE).
  +     * Then try to bind to the resource.
  +     */
  +    if (binding->exists)
  +	err = (*resource->hooks->remove_resource)(binding, &multi_response);
  +
  +    if (err == NULL) {
  +	err = (*binding_hooks->bind_resource)(resource, binding);
  +    }
  +
  +    /* restore parent collection states */
  +    err2 = dav_revert_resource_writability(r, NULL,
  +					   err != NULL /* undo if error */,
  +					   &av_info);
  +
  +    /* check for error from remove/bind operations */
  +    if (err != NULL) {
  +	err = dav_push_error(r->pool, err->status, 0,
  +			     apr_psprintf(r->pool,
  +					 "Could not BIND %s.",
  +					 ap_escape_html(r->pool, r->uri)),
  +			     err);
  +	return dav_handle_err(r, err, multi_response);
  +    }
  +
  +    /* check for errors from reverting writability */
  +    if (err2 != NULL) {
  +	/* just log a warning */
  +	err = dav_push_error(r->pool, err2->status, 0,
  +			     "The BIND was successful, but there was a "
  +			     "problem reverting the writability of the "
  +			     "source parent collection.",
  +			     err2);
  +	dav_log_err(r, err, APLOG_WARNING);
  +    }
  +
  +    /* return an appropriate response (HTTP_CREATED) */
  +    /* ### spec doesn't say what happens when destination was replaced */
  +    return dav_created(r, lookup.rnew->uri, "Binding", 0);
  +}
  +
  +
  +/*
  + * Response handler for DAV resources
  + */
  +static int dav_handler(request_rec *r)
  +{
  +    dav_dir_conf *conf;
  +
  +    /* quickly ignore any HTTP/0.9 requests */
  +    if (r->assbackwards) {
  +	return DECLINED;
  +    }
  +
       /* ### do we need to do anything with r->proxyreq ?? */
   
       conf = (dav_dir_conf *) ap_get_module_config(r->per_dir_config,
  @@ -3172,6 +3936,10 @@
   
       if (!strcmp(r->method, "MERGE")) {
   	return dav_method_merge(r);
  +    }
  +
  +    if (!strcmp(r->method, "BIND")) {
  +	return dav_method_bind(r);
       }
   
       /* ### add'l methods for Advanced Collections, ACLs, DASL */
  
  
  
  1.25      +160 -26   apache-2.0/src/modules/dav/main/mod_dav.h
  
  Index: mod_dav.h
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/modules/dav/main/mod_dav.h,v
  retrieving revision 1.24
  retrieving revision 1.25
  diff -u -r1.24 -r1.25
  --- mod_dav.h	2000/11/09 13:08:06	1.24
  +++ mod_dav.h	2000/11/15 02:05:12	1.25
  @@ -252,6 +252,7 @@
   typedef struct dav_hooks_vsn dav_hooks_vsn;
   typedef struct dav_hooks_repository dav_hooks_repository;
   typedef struct dav_hooks_liveprop dav_hooks_liveprop;
  +typedef struct dav_hooks_binding dav_hooks_binding;
   
   /* ### deprecated name */
   typedef dav_hooks_propdb dav_hooks_db;
  @@ -278,11 +279,12 @@
       DAV_RESOURCE_TYPE_UNKNOWN,
   
       DAV_RESOURCE_TYPE_REGULAR,          /* file or collection; could be
  -                                           unversioned or version selector */
  +                                         * unversioned, or version selector,
  +                                         * or baseline selector */
   
  -    DAV_RESOURCE_TYPE_VERSION,          /* version URL */
  +    DAV_RESOURCE_TYPE_VERSION,          /* version or baseline URL */
   
  -    DAV_RESOURCE_TYPE_HISTORY,          /* version history URL */
  +    DAV_RESOURCE_TYPE_HISTORY,          /* version or baseline history URL */
   
       DAV_RESOURCE_TYPE_WORKING,          /* working resource URL */
   
  @@ -290,8 +292,6 @@
   
       DAV_RESOURCE_TYPE_ACTIVITY,         /* activity URL */
   
  -    DAV_RESOURCE_TYPE_BASELINE,         /* baseline URL */
  -
       DAV_RESOURCE_TYPE_PRIVATE           /* repository-private type */
   
   } dav_resource_type;
  @@ -317,11 +317,14 @@
   
       int versioned;	/* 0 => unversioned; can be 1 for
                            * REGULAR and WORKSPACE resources,
  -                         * and is always 1 for VERSION, WORKING,
  -                         * and BASELINE */
  +                         * and is always 1 for VERSION and WORKING */
  +
  +    int baselined;      /* 0 => not baselined; can be 1 for
  +                         * REGULAR and VERSION resources;
  +                         * versioned == 1 when baselined == 1 */
   
       int working;	/* 0 => not checked out; can be 1 for
  -                         * REGULAR, WORKSPACE, and BASELINE,
  +                         * REGULAR and WORKSPACE resources,
                            * and is always 1 for WORKING */
   
       const char *uri;	/* the URI for this resource */
  @@ -456,6 +459,7 @@
       const dav_hooks_locks *locks;
       const dav_hooks_liveprop *liveprop;
       const dav_hooks_vsn *vsn;
  +    const dav_hooks_binding *binding;
   
   } dav_provider;
   
  @@ -474,6 +478,7 @@
   const dav_hooks_locks *dav_get_lock_hooks(request_rec *r);
   const dav_hooks_propdb *dav_get_propdb_hooks(request_rec *r);
   const dav_hooks_vsn *dav_get_vsn_hooks(request_rec *r);
  +const dav_hooks_binding *dav_get_binding_hooks(request_rec *r);
   
   DAV_DECLARE(void) dav_register_provider(apr_pool_t *p, const char *name,
                                           const dav_provider *hooks);
  @@ -490,6 +495,7 @@
   #define DAV_GET_HOOKS_PROPDB(r)         dav_get_propdb_hooks(r)
   #define DAV_GET_HOOKS_LOCKS(r)          dav_get_lock_hooks(r)
   #define DAV_GET_HOOKS_VSN(r)            dav_get_vsn_hooks(r)
  +#define DAV_GET_HOOKS_BINDING(r)        dav_get_binding_hooks(r)
   
   
   /* --------------------------------------------------------------------
  @@ -1341,8 +1347,6 @@
        *
        * The root_dir is the root of the directory for which this repository
        * is configured.
  -     * The workspace is the value of any Workspace header, or NULL
  -     * if there is none.
        * The target is either a label, or a version URI, or NULL. If there
        * is a target, then is_label specifies whether the target is a label
        * or a URI.
  @@ -1353,7 +1357,6 @@
       dav_resource * (*get_resource)(
           request_rec *r,
           const char *root_dir,
  -        const char *workspace,
   	const char *target,
           int is_label
       );
  @@ -1549,13 +1552,6 @@
   */
   
   
  -/* dav_get_workspace:
  - *
  - * Returns the value of any Workspace header in a request
  - * (used by versioning clients)
  - */
  -int dav_get_workspace(request_rec *r, const char **workspace);
  -
   /*
    * dav_get_target_selector
    *
  @@ -1647,15 +1643,32 @@
   /* Versioning provider hooks */
   struct dav_hooks_vsn
   {
  +    /*
  +    ** MANDATORY HOOKS
  +    ** The following hooks are mandatory for all versioning providers;
  +    ** they define the functionality needed to implement "core" versioning.
  +    */
  +
       /* Return supported versioning level
        * for the Versioning header
        */
       const char * (*get_vsn_header)(void);
   
  -    /* Create a new (empty) resource. If successful,
  -     * the resource object state is updated appropriately.
  +    /* Put a resource under version control. If the resource already
  +     * exists unversioned, then it becomes the initial version of the
  +     * new version history, and it is replaced by a version selector
  +     * which targets the new version.
  +     *
  +     * If the resource does not exist, then a new version selector
  +     * is created which either targets an existing version (if the
  +     * "target" argument is not NULL), or the initial, empty version
  +     * in a new history resource (if the "target" argument is NULL).
  +     *
  +     * If successful, the resource object state is updated appropriately
  +     * (that is, changed to refer to the new version selector resource).
        */
  -    dav_error * (*mkresource)(dav_resource *resource);
  +    dav_error * (*vsn_control)(dav_resource *resource,
  +                               const char *target);
   
       /* Checkout a resource. If successful, the resource
        * object state is updated appropriately.
  @@ -1684,6 +1697,17 @@
       dav_error * (*checkin)(dav_resource *resource,
                              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.
        */
  @@ -1699,14 +1723,124 @@
       ** Return the set of reports available at this resource.
       **
       ** An array of report elements should be returned, with an end-marker
  -    ** element containing namespace==NULL. The report response will be
  -    ** constructed and returned.
  -    **
  -    ** DAV:available-report should not be returned; the mod_dav core will
  -    ** handle that.
  +    ** element containing namespace==NULL. The value of the
  +    ** DAV:supported-report-set property will be constructed and
  +    ** returned.
       */
       dav_error * (*avail_reports)(const dav_resource *resource,
                                    const dav_report_elem **reports);
  +
  +    /*
  +    ** Determine whether a Target-Selector header can be used
  +    ** with a particular report. The dav_xml_doc structure
  +    ** contains the parsed report request body.
  +    ** Returns 0 if Target-Selector is not allowed.
  +    */
  +    int (*report_target_selector_allowed)(const ap_xml_doc *doc);
  +
  +    /*
  +    ** Generate a report on a resource. Since a provider is free
  +    ** to define its own reports, and the value of request headers
  +    ** may affect the interpretation of a report, the request record
  +    ** must be passed to this routine.
  +    **
  +    ** The dav_xml_doc structure contains the parsed report request
  +    ** body. The report response is generated into the dav_text_header
  +    ** structure.
  +    **
  +    ** ### shouldn't generate large responses to memory ###
  +    */
  +    dav_error * (*get_report)(request_rec *r,
  +                              const dav_resource *resource,
  +                              const ap_xml_doc *doc,
  +                              ap_text_header *report);
  +
  +    /*
  +    ** OPTIONAL HOOKS
  +    ** The following hooks are optional; if not defined, then the
  +    ** corresponding protocol methods will be unsupported.
  +    */
  +
  +    /*
  +    ** Add a label to a version. The resource is either a specific
  +    ** version, or a version selector, in which case the label should
  +    ** be added to the current target of the version selector. The
  +    ** version selector cannot be checked out.
  +    **
  +    ** If replace != 0, any existing label by the same name is
  +    ** effectively deleted first. Otherwise, it is an error to
  +    ** attempt to add a label which already exists on some version
  +    ** of the same history resource.
  +    **
  +    ** This hook is optional; if not defined, then the LABEL method
  +    ** will not be supported. If it is defined, then the remove_label
  +    ** hook must be defined also.
  +    */
  +    dav_error * (*add_label)(const dav_resource *resource,
  +                             const char *label,
  +                             int replace);
  +
  +    /*
  +    ** Remove a label from a version. The resource is either a specific
  +    ** version, or a version selector, in which case the label should
  +    ** be added to the current target of the version selector. The
  +    ** version selector cannot be checked out.
  +    **
  +    ** It is an error if no such label exists on the specified version.
  +    **
  +    ** This hook is optional, but if defined, the add_label hook
  +    ** must be defined also.
  +    */
  +    dav_error * (*remove_label)(const dav_resource *resource,
  +                                const char *label);
  +
  +    /*
  +    ** Determine whether a null resource can be created as a workspace.
  +    ** The provider may restrict workspaces to certain locations.
  +    ** Returns 0 if the resource cannot be a workspace.
  +    **
  +    ** This hook is optional; if the provider does not support workspaces,
  +    ** it should be set to NULL.
  +    */
  +    int (*can_be_workspace)(const dav_resource *resource);
  +
  +    /*
  +    ** Create a workspace resource. The resource must not already
  +    ** exist. Any <DAV:mkworkspace> element is passed to the provider
  +    ** in the "doc" structure; it may be empty.
  +    **
  +    ** If workspace creation is succesful, the state of the resource
  +    ** object is updated appropriately.
  +    **
  +    ** This hook is optional; if the provider does not support workspaces,
  +    ** it should be set to NULL.
  +    */
  +    dav_error * (*make_workspace)(dav_resource *resource,
  +                                  ap_xml_doc *doc);
  +};
  +
  +
  +/* --------------------------------------------------------------------
  +**
  +** BINDING FUNCTIONS
  +*/
  +
  +/* binding provider hooks */
  +struct dav_hooks_binding {
  +
  +    /* Determine whether a resource can be the target of a binding.
  +     * Returns 0 if the resource cannot be a binding target.
  +     */
  +    int (*is_bindable)(const dav_resource *resource);
  +
  +    /* Create a binding to a resource.
  +     * The resource argument is the target of the binding;
  +     * the binding argument must be a resource which does not already
  +     * exist.
  +     */
  +    dav_error * (*bind_resource)(const dav_resource *resource,
  +				 dav_resource *binding);
  +
   };
   
   
  
  
  
  1.15      +63 -6     apache-2.0/src/modules/dav/main/props.c
  
  Index: props.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/modules/dav/main/props.c,v
  retrieving revision 1.14
  retrieving revision 1.15
  diff -u -r1.14 -r1.15
  --- props.c	2000/10/07 00:50:42	1.14
  +++ props.c	2000/11/15 02:05:12	1.15
  @@ -279,6 +279,9 @@
       "lockdiscovery",
       "resourcetype",
       "supportedlock",
  +    "supported-method-set",
  +    "supported-live-property-set",
  +    "supported-report-set",
   
       NULL	/* sentinel */
   };
  @@ -288,6 +291,9 @@
       DAV_PROPID_CORE_lockdiscovery,
       DAV_PROPID_CORE_resourcetype,
       DAV_PROPID_CORE_supportedlock,
  +    DAV_PROPID_CORE_supported_method_set,
  +    DAV_PROPID_CORE_supported_live_property_set,
  +    DAV_PROPID_CORE_supported_report_set,
   
       DAV_PROPID_CORE_UNKNOWN
   };
  @@ -372,7 +378,10 @@
   	|| propid == DAV_PROPID_CORE_getcontenttype
   	|| propid == DAV_PROPID_CORE_getcontentlanguage
   #endif
  -	|| propid == DAV_PROPID_CORE_supportedlock) {
  +	|| propid == DAV_PROPID_CORE_supportedlock
  +	|| propid == DAV_PROPID_CORE_supported_method_set
  +	|| propid == DAV_PROPID_CORE_supported_live_property_set
  +	|| propid == DAV_PROPID_CORE_supported_report_set) {
   
   	return 0;
       }
  @@ -419,6 +428,7 @@
   				       int *inserted)
   {
       const char *value = NULL;
  +    dav_error *err;
   
       *inserted = 0;
   
  @@ -430,8 +440,13 @@
   
       case DAV_PROPID_CORE_resourcetype:
           switch (propdb->resource->type) {
  -        case DAV_RESOURCE_TYPE_REGULAR:
           case DAV_RESOURCE_TYPE_VERSION:
  +            if (propdb->resource->baselined) {
  +	        value = "<D:baseline/>";
  +                break;
  +            }
  +            /* fall through */
  +        case DAV_RESOURCE_TYPE_REGULAR:
           case DAV_RESOURCE_TYPE_WORKING:
               if (propdb->resource->collection) {
   	        value = "<D:collection/>";
  @@ -451,9 +466,6 @@
           case DAV_RESOURCE_TYPE_ACTIVITY:
   	    value = "<D:activity/>";
               break;
  -        case DAV_RESOURCE_TYPE_BASELINE:
  -	    value = "<D:baseline/>";
  -            break;
   
           default:
   	    /* ### bad juju */
  @@ -463,7 +475,6 @@
   
       case DAV_PROPID_CORE_lockdiscovery:
           if (propdb->lockdb != NULL) {
  -	    dav_error *err;
   	    dav_lock *locks;
   
   	    if ((err = dav_lock_query(propdb->lockdb, propdb->resource,
  @@ -499,6 +510,43 @@
           }
   	break;
   
  +    case DAV_PROPID_CORE_supported_method_set:
  +        /* ### leverage code from dav_method_options ### */
  +        break;
  +
  +    case DAV_PROPID_CORE_supported_live_property_set:
  +        /* ### insert all live property names ### */
  +        break;
  +
  +    case DAV_PROPID_CORE_supported_report_set:
  +        if (propdb->vsn_hooks != NULL) {
  +            const dav_report_elem *reports;
  +
  +            if ((err = (*propdb->vsn_hooks->avail_reports)(propdb->resource, &reports)) != NULL) {
  +	        return dav_push_error(propdb->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 (reports == NULL)
  +                break;
  +
  +            value = "";
  +
  +            for (; reports->nmspace != NULL; ++reports) {
  +                /* Note: we presume reports->namespace is properly XML/URL quoted */
  +                char *v = apr_psprintf(propdb->p, "<%s xmlns=\"%s\"/>" DEBUG_CR,
  +                                       reports->name, reports->nmspace);
  +                /* This isn't very memory-efficient, but there should only be a small
  +                 * number of reports
  +                 */
  +                value = apr_pstrcat(propdb->p, value, v, NULL);
  +            }
  +        }
  +        break;
  +
       case DAV_PROPID_CORE_getcontenttype:
   	if (propdb->subreq == NULL) {
   	    dav_do_prop_subreq(propdb);
  @@ -1128,6 +1176,15 @@
   			      getvals, &hdr, &unused_inserted);
       (void)dav_insert_coreprop(propdb,
   			      DAV_PROPID_CORE_lockdiscovery, "lockdiscovery",
  +			      getvals, &hdr, &unused_inserted);
  +    (void)dav_insert_coreprop(propdb,
  +			      DAV_PROPID_CORE_supported_method_set, "supported-method-set",
  +			      getvals, &hdr, &unused_inserted);
  +    (void)dav_insert_coreprop(propdb,
  +			      DAV_PROPID_CORE_supported_live_property_set, "supported-live-property-set",
  +			      getvals, &hdr, &unused_inserted);
  +    (void)dav_insert_coreprop(propdb,
  +			      DAV_PROPID_CORE_supported_report_set, "supported-report-set",
   			      getvals, &hdr, &unused_inserted);
   
       /* if the resourcetype wasn't stored, then prepare one */
  
  
  
  1.14      +2 -47     apache-2.0/src/modules/dav/main/util.c
  
  Index: util.c
  ===================================================================
  RCS file: /home/cvs/apache-2.0/src/modules/dav/main/util.c,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- util.c	2000/11/09 13:08:06	1.13
  +++ util.c	2000/11/15 02:05:12	1.14
  @@ -1511,40 +1511,9 @@
       return s;
   }
   
  -#define DAV_WORKSPACE_HDR "Workspace"
   #define DAV_TARGET_SELECTOR_HDR "Target-Selector"
   
   /* see mod_dav.h for docco */
  -int dav_get_workspace(request_rec *r, const char **workspace)
  -{
  -    const char *ws_uri;
  -
  -    *workspace = NULL;
  -    ws_uri = apr_table_get(r->headers_in, DAV_WORKSPACE_HDR);
  -
  -    if (ws_uri != NULL) {
  -	dav_lookup_result lookup;
  -
  -	/* make the URI server-relative */
  -	lookup = dav_lookup_uri(ws_uri, 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 lookup.err.status;
  -	}
  -
  -	*workspace = lookup.rnew->uri;
  -    }
  -
  -    return OK;
  -}
  -
  -/* see mod_dav.h for docco */
   int dav_get_target_selector(request_rec *r,
                               const ap_xml_elem *version,
                               const char **target,
  @@ -1603,28 +1572,18 @@
       /* Only versioning headers require a Vary response header,
        * so only do this check if there is a versioning provider */
       if (vsn_hooks != NULL) {
  -	const char *workspace = apr_table_get(in_req->headers_in, DAV_WORKSPACE_HDR);
   	const char *target = apr_table_get(in_req->headers_in, DAV_TARGET_SELECTOR_HDR);
   	const char *vary = apr_table_get(out_req->headers_out, "Vary");
   
  -        /* If Workspace header specified, add it to Vary header */
  -	if (workspace != NULL) {
  -	    if (vary == NULL)
  -		vary = DAV_WORKSPACE_HDR;
  -	    else
  -		vary = apr_pstrcat(out_req->pool, vary, "," DAV_WORKSPACE_HDR, NULL);
  -	}
  -
           /* If Target-Selector specified, add it to the Vary header */
   	if (target != NULL) {
   	    if (vary == NULL)
   		vary = DAV_TARGET_SELECTOR_HDR;
   	    else
   		vary = apr_pstrcat(out_req->pool, vary, "," DAV_TARGET_SELECTOR_HDR, NULL);
  -	}
   
  -	if (workspace != NULL || target != NULL)
   	    apr_table_setn(out_req->headers_out, "Vary", vary);
  +	}
       }
   }
   
  @@ -1641,10 +1600,6 @@
       /* Initialize results */
       memset(av_info, 0, sizeof(*av_info));
   
  -    if (!parent_only) {
  -        av_info->resource_created = !resource->exists;
  -    }
  -
       /* check parent resource if requested or if resource must be created */
       if (!resource->exists || parent_only) {
   	dav_resource *parent = (*resource->hooks->get_parent_resource)(resource);
  @@ -1702,7 +1657,7 @@
   
   	/* if not just checking parent, create new child resource */
           if (!parent_only) {
  -	    if ((err = (*vsn_hooks->mkresource)(resource)) != NULL) {
  +	    if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) {
   	        body = apr_psprintf(r->pool,
                                       "Unable to create versioned resource %s.",
                                       ap_escape_html(r->pool, resource->uri));