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...@apache.org on 2001/04/14 15:10:23 UTC

cvs commit: httpd-2.0/modules/dav/main mod_dav.c mod_dav.h util.c util_lock.c

gstein      01/04/14 06:10:23

  Modified:    modules/dav/main mod_dav.c mod_dav.h util.c util_lock.c
  Log:
  Fix up the auto-versioning stuff. The new scheme more closely matches the
  intent of DeltaV draft 14, simplifying some previous assumptions.
  
  Includes some heavy fixes to MOVE/COPY in a versioning world.
  
  Fix to CHECKOUT when a working resource is not created (checkout in place)
  
  Submitted by: John Vasta <jv...@rational.com>
  
  Revision  Changes    Path
  1.55      +133 -78   httpd-2.0/modules/dav/main/mod_dav.c
  
  Index: mod_dav.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/mod_dav.c,v
  retrieving revision 1.54
  retrieving revision 1.55
  diff -u -u -r1.54 -r1.55
  --- mod_dav.c	2001/04/14 12:45:30	1.54
  +++ mod_dav.c	2001/04/14 13:10:22	1.55
  @@ -1001,9 +1001,9 @@
       }
   
       /* make sure the resource can be modified (if versioning repository) */
  -    if ((err = dav_ensure_resource_writable(r, resource,
  -					    0 /* not parent_only */,
  -					    &av_info)) != NULL) {
  +    if ((err = dav_auto_checkout(r, resource,
  +				 0 /* not parent_only */,
  +				 &av_info)) != NULL) {
   	/* ### add a higher-level description? */
   	return dav_handle_err(r, err, NULL);
       }
  @@ -1086,8 +1086,8 @@
       }
   
       /* restore modifiability of resources back to what they were */
  -    err2 = dav_revert_resource_writability(r, resource, err != NULL /* undo if error */,
  -                                           &av_info);
  +    err2 = dav_auto_checkin(r, resource, err != NULL /* undo if error */,
  +                            0 /*unlock*/, &av_info);
   
       /* check for errors now */
       if (err != NULL) {
  @@ -1097,7 +1097,7 @@
   	/* just log a warning */
   	err2 = dav_push_error(r->pool, err->status, 0,
   			      "The PUT was successful, but there "
  -			      "was a problem reverting the writability of "
  +			      "was a problem automatically checking in "
   			      "the resource or its parent collection.",
   			      err2);
   	dav_log_err(r, err2, APLOG_WARNING);
  @@ -1232,8 +1232,8 @@
       }
   
       /* if versioned resource, make sure parent is checked out */
  -    if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
  -					    &av_info)) != NULL) {
  +    if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
  +				 &av_info)) != NULL) {
   	/* ### add a higher-level description? */
   	return dav_handle_err(r, err, NULL);
       }
  @@ -1242,8 +1242,8 @@
       err = (*resource->hooks->remove_resource)(resource, &multi_response);
   
       /* restore writability of parent back to what it was */
  -    err2 = dav_revert_resource_writability(r, NULL, err != NULL /* undo if error */,
  -					   &av_info);
  +    err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
  +			    0 /*unlock*/, &av_info);
   
       /* check for errors now */
       if (err != NULL) {
  @@ -1258,8 +1258,8 @@
   	/* just log a warning */
   	err = dav_push_error(r->pool, err2->status, 0,
   			     "The DELETE was successful, but there "
  -			     "was a problem reverting the writability of "
  -			     "its parent collection.",
  +			     "was a problem automatically checking in "
  +			     "the parent collection.",
   			     err2);
   	dav_log_err(r, err, APLOG_WARNING);
       }
  @@ -2146,6 +2146,7 @@
       ap_text *propstat_text;
       apr_array_header_t *ctx_list;
       dav_prop_ctx *ctx;
  +    dav_auto_version_info av_info;
   
       /* Ask repository module to resolve the resource */
       err = dav_get_resource(r, 0 /* label_allowed */, 0 /* use_checked_in */,
  @@ -2178,8 +2179,19 @@
   	return dav_handle_err(r, err, NULL);
       }
   
  +    /* make sure the resource can be modified (if versioning repository) */
  +    if ((err = dav_auto_checkout(r, resource,
  +				 0 /* not parent_only */,
  +				 &av_info)) != NULL) {
  +	/* ### add a higher-level description? */
  +	return dav_handle_err(r, err, NULL);
  +    }
  +
       if ((err = dav_open_propdb(r, NULL, resource, 0, doc->namespaces,
   			       &propdb)) != NULL) {
  +        /* undo any auto-checkout */
  +        dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
  +
   	err = dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
   			     apr_psprintf(r->pool,
   					 "Could not open the property "
  @@ -2212,6 +2224,9 @@
   	if ((prop_group = dav_find_child(child, "prop")) == NULL) {
   	    dav_close_propdb(propdb);
   
  +            /* undo any auto-checkout */
  +            dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
  +
   	    /* This supplies additional information for the default message. */
   	    ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
   			  "A \"prop\" element is missing inside "
  @@ -2257,6 +2272,9 @@
       /* make sure this gets closed! */
       dav_close_propdb(propdb);
   
  +    /* complete any auto-versioning */
  +    dav_auto_checkin(r, resource, failure, 0 /*unlock*/, &av_info);
  +
       /* log any errors that occurred */
       (void)dav_process_ctx_list(dav_prop_log_errors, ctx_list, 0, 0);
   
  @@ -2384,8 +2402,8 @@
       }
   
       /* if versioned resource, make sure parent is checked out */
  -    if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
  -					    &av_info)) != NULL) {
  +    if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
  +				 &av_info)) != NULL) {
   	/* ### add a higher-level description? */
   	return dav_handle_err(r, err, NULL);
       }
  @@ -2395,8 +2413,8 @@
       err = (*resource->hooks->create_collection)(resource);
   
       /* restore modifiability of parent back to what it was */
  -    err2 = dav_revert_resource_writability(r, NULL, err != NULL /* undo if error */,
  -					   &av_info);
  +    err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
  +			    0 /*unlock*/, &av_info);
   
       /* check for errors now */
       if (err != NULL) {
  @@ -2406,8 +2424,8 @@
   	/* just log a warning */
   	err = dav_push_error(r->pool, err->status, 0,
   			     "The MKCOL was successful, but there "
  -			     "was a problem reverting the writability of "
  -			     "its parent collection.",
  +			     "was a problem automatically checking in "
  +			     "the parent collection.",
   			     err2);
   	dav_log_err(r, err, APLOG_WARNING);
       }
  @@ -2450,9 +2468,9 @@
   static int dav_method_copymove(request_rec *r, int is_move)
   {
       dav_resource *resource;
  -    dav_auto_version_info src_av_info = { 0 };
       dav_resource *resnew;
  -    dav_auto_version_info dst_av_info;
  +    dav_auto_version_info src_av_info = { 0 };
  +    dav_auto_version_info dst_av_info = { 0 };
       const char *body;
       const char *dest;
       dav_error *err;
  @@ -2465,8 +2483,8 @@
       int depth;
       int result;
       dav_lockdb *lockdb;
  -    int replaced;
  -    int resource_state;
  +    int replace_dest;
  +    int resnew_state;
   
       /* Ask repository module to resolve the resource */
       err = dav_get_resource(r, !is_move /* label_allowed */,
  @@ -2479,6 +2497,7 @@
       }
   
       /* If not a file or collection resource, COPY/MOVE not allowed */
  +    /* ### allow COPY/MOVE of DeltaV resource types */
       if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
           body = apr_psprintf(r->pool,
                              "Cannot COPY/MOVE resource %s.",
  @@ -2686,13 +2705,10 @@
   	(void)dav_unlock(r, resource, NULL);
       }
   
  -    /* remember whether target resource existed */
  -    replaced = resnew->exists;
  -
       /* if this is a move, then the source parent collection will be modified */
       if (is_move) {
  -        if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
  -						&src_av_info)) != NULL) {
  +        if ((err = dav_auto_checkout(r, resource, 1 /* parent_only */,
  +				     &src_av_info)) != NULL) {
   	    if (lockdb != NULL)
   		(*lockdb->hooks->close_lockdb)(lockdb);
   
  @@ -2701,44 +2717,76 @@
           }
       }
   
  -    /* prepare the destination collection for modification */
  -    if ((err = dav_ensure_resource_writable(r, resnew, 1 /* parent_only */,
  -					    &dst_av_info)) != NULL) {
  -        /* could not make destination writable:
  -	 * if move, restore state of source parent
  -	 */
  -        if (is_move) {
  -            (void) dav_revert_resource_writability(r, NULL, 1 /* undo */,
  -						   &src_av_info);
  -        }
  +    /*
  +     * Remember the initial state of the destination, so the lock system
  +     * can be notified as to how it changed.
  +     */
  +    resnew_state = dav_get_resource_state(lookup.rnew, resnew);
   
  -	if (lockdb != NULL)
  -	    (*lockdb->hooks->close_lockdb)(lockdb);
  +    /* If destination does not exist, initialize resource object
  +     * to be same type as the source.
  +     */
  +    if (!resnew->exists) {
  +        resnew->type = resource->type;
  +        resnew->collection = resource->collection;
  +    }
   
  -	/* ### add a higher-level description? */
  -	return dav_handle_err(r, err, NULL);
  +    /* In a MOVE operation, the destination is replaced by the source.
  +     * In a COPY operation, if the destination exists, is under version
  +     * control, and is the same resource type as the source,
  +     * then it should not be replaced, but modified to be a copy of
  +     * the source.
  +     */
  +    if (!resnew->exists)
  +        replace_dest = 0;
  +    else if (is_move || !resource->versioned)
  +        replace_dest = 1;
  +    else if (resource->type != resnew->type)
  +        replace_dest = 1;
  +    else if ((resource->collection == 0) != (resnew->collection == 0))
  +        replace_dest = 1;
  +    else
  +        replace_dest = 0;
  +
  +    /* If the destination must be created or replaced,
  +     * make sure the parent collection is writable
  +     */
  +    if (!resnew->exists || replace_dest) {
  +        if ((err = dav_auto_checkout(r, resnew, 1 /*parent_only*/,
  +				     &dst_av_info)) != NULL) {
  +            /* could not make destination writable:
  +	     * if move, restore state of source parent
  +	     */
  +            if (is_move) {
  +                (void) dav_auto_checkin(r, NULL, 1 /* undo */,
  +				        0 /*unlock*/, &src_av_info);
  +            }
  +
  +	    if (lockdb != NULL)
  +	        (*lockdb->hooks->close_lockdb)(lockdb);
  +
  +	    /* ### add a higher-level description? */
  +	    return dav_handle_err(r, err, NULL);
  +        }
       }
   
       /* If source and destination parents are the same, then
  -     * use the same object, so status updates to one are reflected
  -     * in the other, when reverting their writable states.
  +     * use the same resource object, so status updates to one are reflected
  +     * in the other, when doing auto-versioning. Otherwise,
  +     * we may try to checkin the parent twice.
        */
       if (src_av_info.parent_resource != NULL
  +        && dst_av_info.parent_resource != NULL
           && (*src_av_info.parent_resource->hooks->is_same_resource)
               (src_av_info.parent_resource, dst_av_info.parent_resource)) {
   
           dst_av_info.parent_resource = src_av_info.parent_resource;
       }
  -
  -    /* New resource will be same kind as source */
  -    resnew->collection = resource->collection;
   
  -    resource_state = dav_get_resource_state(lookup.rnew, resnew);
  -
  -    /* If target exists, remove it first (we know Ovewrite must be TRUE).
  -     * Then try to copy/move the resource.
  +    /* If destination is being replaced, remove it first
  +     * (we know Ovewrite must be TRUE). Then try to copy/move the resource.
        */
  -    if (resnew->exists)
  +    if (replace_dest)
   	err = (*resnew->hooks->remove_resource)(resnew, &multi_response);
   
       if (err == NULL) {
  @@ -2750,13 +2798,13 @@
                                                       &multi_response);
       }
   
  -    /* restore parent collection states */
  -    err2 = dav_revert_resource_writability(r, NULL, err != NULL /* undo if error */,
  -					   &dst_av_info);
  +    /* perform any auto-versioning cleanup */
  +    err2 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
  +			    0 /*unlock*/, &dst_av_info);
   
       if (is_move) {
  -        err3 = dav_revert_resource_writability(r, NULL, err != NULL /* undo if error */,
  -					       &src_av_info);
  +        err3 = dav_auto_checkin(r, NULL, err != NULL /* undo if error */,
  +				0 /*unlock*/, &src_av_info);
       }
       else
   	err3 = NULL;
  @@ -2774,12 +2822,12 @@
   	return dav_handle_err(r, err, multi_response);
       }
   
  -    /* check for errors from reverting writability */
  +    /* check for errors from auto-versioning */
       if (err2 != NULL) {
   	/* just log a warning */
   	err = dav_push_error(r->pool, err2->status, 0,
   			     "The MOVE/COPY was successful, but there was a "
  -			     "problem reverting the writability of the "
  +			     "problem automatically checking in the "
   			     "source parent collection.",
   			     err2);
   	dav_log_err(r, err, APLOG_WARNING);
  @@ -2788,8 +2836,8 @@
   	/* just log a warning */
   	err = dav_push_error(r->pool, err3->status, 0,
   			     "The MOVE/COPY was successful, but there was a "
  -			     "problem reverting the writability of the "
  -			     "destination parent collection.",
  +			     "problem automatically checking in the "
  +			     "destination or its parent collection.",
   			     err3);
   	dav_log_err(r, err, APLOG_WARNING);
       }
  @@ -2798,7 +2846,7 @@
       if (lockdb != NULL) {
   
   	/* notify lock system that we have created/replaced a resource */
  -	err = dav_notify_created(r, lockdb, resnew, resource_state, depth);
  +	err = dav_notify_created(r, lockdb, resnew, resnew_state, depth);
   
   	(*lockdb->hooks->close_lockdb)(lockdb);
   
  @@ -2814,7 +2862,8 @@
       }
   
       /* return an appropriate response (HTTP_CREATED or HTTP_NO_CONTENT) */
  -    return dav_created(r, lookup.rnew->uri, "Destination", replaced);
  +    return dav_created(r, lookup.rnew->uri, "Destination",
  +                       resnew_state == DAV_RESOURCE_EXISTS);
   }
   
   /* dav_method_lock:  Handler to implement the DAV LOCK method
  @@ -3193,14 +3242,14 @@
       }
   
       /* if in versioned collection, make sure parent is checked out */
  -    if ((err = dav_ensure_resource_writable(r, resource, 1 /* parent_only */,
  -					    &av_info)) != NULL) {
  +    if ((err = dav_auto_checkout(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);
  +        dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, &av_info);
   	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
   			     apr_psprintf(r->pool,
   					 "Could not VERSION-CONTROL resource %s.",
  @@ -3210,12 +3259,12 @@
       }
   
       /* revert writability of parent directory */
  -    err = dav_revert_resource_writability(r, resource, 0 /*undo*/, &av_info);
  +    err = dav_auto_checkin(r, resource, 0 /*undo*/, 0 /*unlock*/, &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 "
  +			     "was a problem automatically checking in "
   			     "the parent collection.",
   			     err);
           dav_log_err(r, err, APLOG_WARNING);
  @@ -3375,7 +3424,8 @@
       /* ### do lock checks, once behavior is defined */
   
       /* Do the checkout */
  -    if ((err = (*vsn_hooks->checkout)(resource, is_unreserved, is_fork_ok,
  +    if ((err = (*vsn_hooks->checkout)(resource, 0 /*auto_checkout*/,
  +                                      is_unreserved, is_fork_ok,
                                         create_activity, activities,
                                         &working_resource)) != NULL) {
   	err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  @@ -3389,9 +3439,14 @@
       /* set the Cache-Control header, per the spec */
       apr_table_setn(r->headers_out, "Cache-Control", "no-cache");
   
  -    /* use appropriate URI for Location header */
  -    if (working_resource == NULL)
  -        working_resource = resource;
  +    /* if no working resource created, return OK,
  +     * else return CREATED with working resource URL in Location header
  +     */
  +    if (working_resource == NULL) {
  +        /* no body */
  +        ap_set_content_length(r, 0);
  +        return DONE;
  +    }
   
       return dav_created(r, working_resource->uri, "Checked-out resource", 0);
   }
  @@ -3894,7 +3949,7 @@
        * First determine whether a Target-Selector header is allowed
        * for this report.
        */
  -    label_allowed = (*vsn_hooks->report_target_selector_allowed)(doc);
  +    label_allowed = (*vsn_hooks->report_label_header_allowed)(doc);
       err = dav_get_resource(r, label_allowed, 0 /* use_checked_in */,
                              &resource);
       if (err != NULL)
  @@ -4331,8 +4386,8 @@
       }
   
       /* prepare the destination collection for modification */
  -    if ((err = dav_ensure_resource_writable(r, binding, 1 /* parent_only */,
  -					    &av_info)) != NULL) {
  +    if ((err = dav_auto_checkout(r, binding, 1 /* parent_only */,
  +				 &av_info)) != NULL) {
           /* could not make destination writable */
   	return dav_handle_err(r, err, NULL);
       }
  @@ -4348,9 +4403,9 @@
       }
   
       /* restore parent collection states */
  -    err2 = dav_revert_resource_writability(r, NULL,
  -					   err != NULL /* undo if error */,
  -					   &av_info);
  +    err2 = dav_auto_checkin(r, NULL,
  +			    err != NULL /* undo if error */,
  +			    0 /*unlock*/, &av_info);
   
       /* check for error from remove/bind operations */
       if (err != NULL) {
  @@ -4367,7 +4422,7 @@
   	/* 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 "
  +			     "problem automatically checking in the "
   			     "source parent collection.",
   			     err2);
   	dav_log_err(r, err, APLOG_WARNING);
  
  
  
  1.48      +83 -29    httpd-2.0/modules/dav/main/mod_dav.h
  
  Index: mod_dav.h
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/mod_dav.h,v
  retrieving revision 1.47
  retrieving revision 1.48
  diff -u -u -r1.47 -r1.48
  --- mod_dav.h	2001/04/14 12:45:30	1.47
  +++ mod_dav.h	2001/04/14 13:10:23	1.48
  @@ -1737,8 +1737,12 @@
           dav_resource *resource
       );
   
  -    /* Copy one resource to another. The destination must not exist.
  +    /* Copy one resource to another. The destination may exist, if it is
  +     * versioned.
        * Handles both files and collections. Properties are copied as well.
  +     * If the destination exists and is versioned, the provider must update
  +     * the destination to have identical content to the source,
  +     * recursively for collections.
        * The depth argument is ignored for a file, and can be either 0 or
        * DAV_INFINITY for a collection.
        * If an error occurs in a child resource, then the return value is
  @@ -1814,14 +1818,41 @@
   			 const dav_resource *resource);
   
   /*
  +** Flags specifying auto-versioning behavior, returned by
  +** the auto_versionable hook. The value returned depends
  +** on both the state of the resource and the value of the
  +** DAV:auto-versioning property for the resource.
  +**
  +** If the resource does not exist (null or lock-null),
  +** DAV_AUTO_VERSION_ALWAYS causes creation of a new version-controlled resource
  +**
  +** If the resource is checked in,
  +** DAV_AUTO_VERSION_ALWAYS causes it to be checked out always,
  +** DAV_AUTO_VERSION_LOCKED causes it to be checked out only when locked
  +**
  +** If the resource is checked out,
  +** DAV_AUTO_VERSION_ALWAYS causes it to be checked in always,
  +** DAV_AUTO_VERSION_LOCKED causes it to be checked in when unlocked
  +** (note: a provider should allow auto-checkin only for resources which
  +** were automatically checked out)
  +**
  +** In all cases, DAV_AUTO_VERSION_NEVER results in no auto-versioning behavior.
  +*/
  +typedef enum {
  +    DAV_AUTO_VERSION_NEVER,
  +    DAV_AUTO_VERSION_ALWAYS,
  +    DAV_AUTO_VERSION_LOCKED
  +} dav_auto_version;
  +
  +/*
   ** This structure is used to record what auto-versioning operations
   ** were done to make a resource writable, so that they can be undone
   ** at the end of a request.
   */
   typedef struct {
  -    int resource_created;               /* 0 => resource existed previously */
  -    int resource_checkedout;            /* 0 => resource was checked out */
  -    int parent_checkedout;              /* 0 => parent was checked out */
  +    int resource_versioned;             /* 1 => resource was auto-version-controlled */
  +    int resource_checkedout;            /* 1 => resource was auto-checked-out */
  +    int parent_checkedout;              /* 1 => parent was auto-checked-out */
       dav_resource *parent_resource;      /* parent resource, if it was needed */
   } dav_auto_version_info;
   
  @@ -1836,14 +1867,18 @@
    * child does not exist, then a new versioned resource is created and
    * checked out.
    *
  + * If auto-versioning is not enabled for a versioned resource, then an error is
  + * returned, since the resource cannot be modified.
  + *
    * The dav_auto_version_info structure is filled in with enough information
    * to restore both parent and child resources to the state they were in
    * before the auto-versioning operations occurred.
    */
  -dav_error *dav_ensure_resource_writable(request_rec *r,
  -					dav_resource *resource,
  -                                        int parent_only,
  -                                        dav_auto_version_info *av_info);
  +dav_error *dav_auto_checkout(
  +    request_rec *r,
  +    dav_resource *resource,
  +    int parent_only,
  +    dav_auto_version_info *av_info);
   
   /* Revert the writability of resources back to what they were
    * before they were modified. If undo == 0, then the resource
  @@ -1851,15 +1886,21 @@
    * If undo != 0, then resource modifications are discarded
    * (i.e. they are unchecked out).
    *
  + * Set the unlock flag to indicate that the resource is about
  + * to be unlocked; it will be checked in if the resource
  + * auto-versioning property indicates it should be. In this case,
  + * av_info is ignored, so it can be NULL.
  + *
    * The resource argument may be NULL if only the parent resource
  - * was made writable (i.e. the parent_only was != 0 in the
  - * dav_ensure_resource_writable call).
  + * was checked out (i.e. the parent_only was != 0 in the
  + * dav_auto_checkout call).
    */
  -dav_error *dav_revert_resource_writability(
  +dav_error *dav_auto_checkin(
       request_rec *r,
       dav_resource *resource,
       int undo,
  -    const dav_auto_version_info *av_info);
  +    int unlock,
  +    dav_auto_version_info *av_info);
   
   /*
   ** This structure is used to describe available reports
  @@ -1899,18 +1940,34 @@
                                 const ap_xml_elem *elem,
                                 ap_text_header *option);
   
  +    /* Determine whether a non-versioned (or non-existent) resource
  +     * is versionable. Returns != 0 if resource can be versioned.
  +     */
  +    int (*versionable)(const dav_resource *resource);
  +
  +    /* Determine whether auto-versioning is enabled for a resource
  +     * (which may not exist, or may not be versioned). If the resource
  +     * is a checked-out resource, the provider must only enable
  +     * auto-checkin if the resource was automatically checked out.
  +     *
  +     * The value returned depends on both the state of the resource
  +     * and the value of its DAV:auto-version property. See the description
  +     * of the dav_auto_version enumeration above for the details.
  +     */
  +    dav_auto_version (*auto_versionable)(const dav_resource *resource);
  +
       /* 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
  +     * If the resource does not exist, then a new version-controlled
  +     * resource 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).
  +     * (that is, changed to refer to the new version-controlled resource).
        */
       dav_error * (*vsn_control)(dav_resource *resource,
                                  const char *target);
  @@ -1918,6 +1975,13 @@
       /* Checkout a resource. If successful, the resource
        * object state is updated appropriately.
        *
  +     * The auto_checkout flag will be set if this checkout is being
  +     * done automatically, as part of some method which modifies
  +     * the resource. The provider must remember that the resource
  +     * was automatically checked out, so it can determine whether it
  +     * can be automatically checked in. (Auto-checkin should only be
  +     * enabled for resources which were automatically checked out.)
  +     *
        * If the working resource has a different URL from the
        * target resource, a dav_resource descriptor is returned
        * for the new working resource. Otherwise, the original
  @@ -1934,6 +1998,7 @@
        * no DAV:activity-set was provided or when create_activity is set.
        */
       dav_error * (*checkout)(dav_resource *resource,
  +                            int auto_checkout,
                               int is_unreserved, int is_fork_ok,
                               int create_activity,
                               apr_array_header_t *activities,
  @@ -1959,17 +2024,6 @@
                              int keep_checked_out,
                              dav_resource **version_resource);
   
  -    /* Determine whether a non-versioned (or non-existent) resource
  -     * is versionable. Returns != 0 if resource can be versioned.
  -     */
  -    int (*versionable)(const dav_resource *resource);
  -
  -    /* Determine whether auto-versioning is enabled for a resource
  -     * (which may not exist, or may not be versioned).
  -     * Returns != 0 if auto-versioning is enabled.
  -     */
  -    int (*auto_version_enabled)(const dav_resource *resource);
  -
       /*
       ** Return the set of reports available at this resource.
       **
  @@ -1982,12 +2036,12 @@
                                    const dav_report_elem **reports);
   
       /*
  -    ** Determine whether a Target-Selector header can be used
  +    ** Determine whether a Label 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.
  +    ** Returns 0 if the Label header is not allowed.
       */
  -    int (*report_target_selector_allowed)(const ap_xml_doc *doc);
  +    int (*report_label_header_allowed)(const ap_xml_doc *doc);
   
       /*
       ** Generate a report on a resource. Since a provider is free
  
  
  
  1.27      +241 -111  httpd-2.0/modules/dav/main/util.c
  
  Index: util.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/util.c,v
  retrieving revision 1.26
  retrieving revision 1.27
  diff -u -u -r1.26 -r1.27
  --- util.c	2001/04/08 07:13:42	1.26
  +++ util.c	2001/04/14 13:10:23	1.27
  @@ -1609,183 +1609,313 @@
       }
   }
   
  +/* dav_can_auto_checkout
  + *
  + * Determine whether auto-checkout is enabled for a resource.
  + * r - the request_rec
  + * resource - the resource
  + * auto_version - the value of the auto_versionable hook for the resource
  + * lockdb - pointer to lock database (opened if necessary)
  + * auto_checkout - set to 1 if auto-checkout enabled
  + */
  +static dav_error * dav_can_auto_checkout(
  +    request_rec *r,                                         
  +    dav_resource *resource,
  +    dav_auto_version auto_version,
  +    dav_lockdb **lockdb,
  +    int *auto_checkout)
  +{
  +    dav_error *err;
  +    dav_lock *lock_list;
  +
  +    *auto_checkout = 0;
  +
  +    if (auto_version == DAV_AUTO_VERSION_ALWAYS) {
  +        *auto_checkout = 1;
  +    }
  +    else if (auto_version == DAV_AUTO_VERSION_LOCKED) {
  +        if (*lockdb == NULL) {
  +            const dav_hooks_locks *locks_hooks = DAV_GET_HOOKS_LOCKS(r);
  +
  +            if (locks_hooks == NULL) {
  +                return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +                                     "Auto-checkout is only enabled for locked resources, "
  +                                     "but there is no lock provider.");
  +            }
  +
  +            if ((err = (*locks_hooks->open_lockdb)(r, 0, 0, lockdb)) != NULL) {
  +                return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +                                      "Cannot open lock database to determine "
  +                                      "auto-versioning behavior.",
  +                                      err);
  +            }
  +        }
  +
  +        if ((err = dav_lock_query(*lockdb, resource, &lock_list)) != NULL) {
  +	    return dav_push_error(r->pool,
  +				  HTTP_INTERNAL_SERVER_ERROR, 0,
  +				  "The locks could not be queried for "
  +				  "determining auto-versioning behavior.",
  +				  err);
  +        }
  +
  +        if (lock_list != NULL)
  +            *auto_checkout = 1;
  +    }
  +
  +    return NULL;
  +}
  +
   /* see mod_dav.h for docco */
  -dav_error *dav_ensure_resource_writable(request_rec *r,
  -					dav_resource *resource,
  -                                        int parent_only,
  -                                        dav_auto_version_info *av_info)
  +dav_error *dav_auto_checkout(
  +    request_rec *r,
  +    dav_resource *resource,
  +    int parent_only,
  +    dav_auto_version_info *av_info)
   {
       const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  -    const char *body;
  -    dav_error *err;
  +    dav_lockdb *lockdb = NULL;
  +    dav_error *err = NULL;
   
       /* Initialize results */
       memset(av_info, 0, sizeof(*av_info));
   
  +    /* if no versioning provider, just return */
  +    if (vsn_hooks == NULL)
  +        return NULL;
  +
       /* check parent resource if requested or if resource must be created */
       if (!resource->exists || parent_only) {
   	dav_resource *parent;
   
           if ((err = (*resource->hooks->get_parent_resource)(resource,
                                                              &parent)) != NULL)
  -            return err;
  +            goto done;
   
           if (parent == NULL || !parent->exists) {
  -	    body = apr_psprintf(r->pool,
  -                                "Missing one or more intermediate collections. "
  -                                "Cannot create resource %s.",
  -                                ap_escape_html(r->pool, resource->uri));
  -	    return dav_new_error(r->pool, HTTP_CONFLICT, 0, body);
  +	    err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +	                        apr_psprintf(r->pool,
  +		                            "Missing one or more intermediate "
  +                                            "collections. Cannot create resource %s.",
  +			                    ap_escape_html(r->pool, resource->uri)));
  +            goto done;
           }
   
           av_info->parent_resource = parent;
  -
  -	/* if parent not versioned, assume child can be created */
  -	if (!parent->versioned) {
  -	    return NULL;
  -	}
   
  -	/* if no versioning provider, something is terribly wrong */
  -	if (vsn_hooks == NULL) {
  -	    return dav_new_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  -				 "INTERNAL ERROR: "
  -                                 "versioned resource with no versioning "
  -				 "provider?");
  -	}
  +        /* if parent versioned and not checked out, see if it can be */
  +	if (parent->versioned && !parent->working) {
  +            int checkout_parent;
  +
  +            if ((err = dav_can_auto_checkout(r, parent,
  +                                             (*vsn_hooks->auto_versionable)(parent),
  +                                             &lockdb, &checkout_parent))
  +                != NULL) {
  +                goto done;
  +            }
   
  -	/* parent must be checked out */
  -	if (!parent->working) {
  -            /* if parent cannot be automatically checked out, fail */
  -            if (!(*vsn_hooks->auto_version_enabled)(parent)) {
  -		body = apr_psprintf(r->pool,
  -                                    "Parent collection must be checked out. "
  -                                    "Cannot create resource %s.",
  -                                    ap_escape_html(r->pool, resource->uri));
  -		return dav_new_error(r->pool, HTTP_CONFLICT, 0, body);
  +            if (!checkout_parent) {
  +		err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +		                    "<DAV:cannot-modify-checked-in-parent>");
  +                goto done;
               }
   
               /* Try to checkout the parent collection.
                * Note that auto-versioning can only be applied to a version selector,
                * so no separate working resource will be created.
                */
  -	    if ((err = (*vsn_hooks->checkout)(parent, 0, 0, 0, NULL, NULL))
  +	    if ((err = (*vsn_hooks->checkout)(parent, 1 /*auto_checkout*/,
  +                                              0, 0, 0, NULL, NULL))
                   != NULL)
               {
  -		body = apr_psprintf(r->pool,
  -                                    "Unable to checkout parent collection. "
  -                                    "Cannot create resource %s.",
  -                                    ap_escape_html(r->pool, resource->uri));
  -		return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
  +		err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  +		                     apr_psprintf(r->pool,
  +				                 "Unable to auto-checkout parent collection. "
  +				                 "Cannot create resource %s.",
  +				                 ap_escape_html(r->pool, resource->uri)),
  +                                     err);
  +                goto done;
   	    }
   
               /* remember that parent was checked out */
               av_info->parent_checkedout = 1;
   	}
  +    }
   
  -	/* if not just checking parent, create new child resource */
  -        if (!parent_only) {
  -	    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));
  -	        return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
  -	    }
  +    /* if only checking parent, we're done */
  +    if (parent_only)
  +        goto done;
  +
  +    /* if creating a new resource, see if it should be version-controlled */
  +    if (!resource->exists
  +        && (*vsn_hooks->auto_versionable)(resource) == DAV_AUTO_VERSION_ALWAYS) {
  +
  +	if ((err = (*vsn_hooks->vsn_control)(resource, NULL)) != NULL) {
  +	    err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  +	                         apr_psprintf(r->pool,
  +		                             "Unable to create versioned resource %s.",
  +			                     ap_escape_html(r->pool, resource->uri)),
  +                                 err);
  +            goto done;
  +	}
   
  -            /* remember that resource was created */
  -            av_info->resource_created = 1;
  -        }
  +        /* remember that resource was created */
  +        av_info->resource_versioned = 1;
       }
  -    else if (!resource->versioned) {
  -	/* resource exists and is not versioned; assume it is writable */
  -	return NULL;
  -    }
  +
  +    /* if resource is versioned, make sure it is checked out */
  +    if (resource->versioned && !resource->working) {
  +        int checkout_resource;
  +
  +        if ((err = dav_can_auto_checkout(r, resource,
  +                                         (*vsn_hooks->auto_versionable)(resource),
  +                                         &lockdb, &checkout_resource)) != NULL) {
  +            goto done;
  +        }
   
  -    /* if not just checking parent, make sure child resource is checked out */
  -    if (!parent_only && !resource->working) {
  +        if (!checkout_resource) {
  +	    err = dav_new_error(r->pool, HTTP_CONFLICT, 0,
  +		                "<DAV:cannot-modify-version-controlled-content>");
  +            goto done;
  +        }
  +
           /* Auto-versioning can only be applied to version selectors, so
            * no separate working resource will be created. */
  -	if ((err = (*vsn_hooks->checkout)(resource, 0, 0, 0, NULL, NULL))
  +	if ((err = (*vsn_hooks->checkout)(resource, 1 /*auto_checkout*/,
  +                                          0, 0, 0, NULL, NULL))
               != NULL)
           {
  -	    body = apr_psprintf(r->pool,
  -                                "Unable to checkout resource %s.",
  -                                ap_escape_html(r->pool, resource->uri));
  -	    return dav_push_error(r->pool, HTTP_CONFLICT, 0, body, err);
  +            err = dav_push_error(r->pool, HTTP_CONFLICT, 0,
  +	                         apr_psprintf(r->pool,
  +		                             "Unable to checkout resource %s.",
  +			                     ap_escape_html(r->pool, resource->uri)),
  +                                 err);
  +            goto done;
   	}
   
           /* remember that resource was checked out */
           av_info->resource_checkedout = 1;
       }
   
  +done:
  +
  +    /* make sure lock database is closed */
  +    if (lockdb != NULL)
  +        (*lockdb->hooks->close_lockdb)(lockdb);
  +
  +    /* if an error occurred, undo any auto-versioning operations already done */
  +    if (err != NULL) {
  +        dav_auto_checkin(r, resource, 1 /*undo*/, 0 /*unlock*/, av_info);
  +        return err;
  +    }
  +
       return NULL;
   }
   
   /* see mod_dav.h for docco */
  -dav_error *dav_revert_resource_writability(
  +dav_error *dav_auto_checkin(
       request_rec *r,
       dav_resource *resource,
       int undo,
  -    const dav_auto_version_info *av_info)
  +    int unlock,
  +    dav_auto_version_info *av_info)
   {
       const dav_hooks_vsn *vsn_hooks = DAV_GET_HOOKS_VSN(r);
  -    const char *body;
  -    dav_error *err;
  +    dav_error *err = NULL;
  +    dav_auto_version auto_version;
   
  -    /* If a resource was provided, restore its writable state.
  -     * Otherwise, only the parent must have been modified */
  -    if (resource != NULL) {
  -        if (av_info->resource_checkedout) {
  -
  -            if (undo)
  -                err = (*vsn_hooks->uncheckout)(resource);
  -            else
  -                err = (*vsn_hooks->checkin)(resource,
  -                                            0 /*keep_checked_out*/, NULL);
  -
  -            if (err != NULL) {
  -	        body = apr_psprintf(r->pool,
  -                                    "Unable to %s resource %s.",
  -                                    undo ? "uncheckout" : "checkin",
  -                                    ap_escape_html(r->pool, resource->uri));
  -                return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  -				      body, err);
  +    /* If no versioning provider, this is a no-op */
  +    if (vsn_hooks == NULL)
  +        return NULL;
  +
  +    /* If undoing auto-checkouts, then do uncheckouts */
  +    if (undo) {
  +        if (resource != NULL) {
  +            if (av_info->resource_checkedout) {
  +                if ((err = (*vsn_hooks->uncheckout)(resource)) != NULL) {
  +                    return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +	                                  apr_psprintf(r->pool,
  +		                                      "Unable to undo auto-checkout "
  +                                                      "of resource %s.",
  +			                              ap_escape_html(r->pool, resource->uri)),
  +                                          err);
  +                }
  +            }
  +
  +            if (av_info->resource_versioned) {
  +	        dav_response *response;
  +
  +	        /* ### should we do anything with the response? */
  +                if ((err = (*resource->hooks->remove_resource)(resource,
  +							       &response)) != NULL) {
  +                    return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +	                                  apr_psprintf(r->pool,
  +			                              "Unable to undo auto-version-control "
  +                                                      "of resource %s.",
  +			                              ap_escape_html(r->pool, resource->uri)),
  +                                          err);
  +                }
               }
           }
   
  -        /* If undoing because of an error, and the resource was created,
  -         * then remove it */
  -        if (undo && av_info->resource_created) {
  -	    dav_response *response;
  +        if (av_info->parent_resource != NULL && av_info->parent_checkedout) {
  +            if ((err = (*vsn_hooks->uncheckout)(av_info->parent_resource)) != NULL) {
  +	        return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +	                              apr_psprintf(r->pool,
  +		                                  "Unable to undo auto-checkout "
  +                                                  "of parent collection %s.",
  +			                          ap_escape_html(r->pool, av_info->parent_resource->uri)),
  +				      err);
  +	    }
  +        }
   
  -	    /* ### should we do anything with the response? */
  -            if ((err = (*resource->hooks->remove_resource)(resource,
  -							   &response)) != NULL) {
  -	        body = apr_psprintf(r->pool,
  -                                    "Unable to undo creation of resource %s.",
  -                                    ap_escape_html(r->pool, resource->uri));
  +        return NULL;
  +    }
  +
  +    /* If the resource was checked out, and auto-checkin is enabled,
  +     * then check it in.
  +     */
  +    if (resource != NULL && resource->working
  +        && (unlock || av_info->resource_checkedout)) {
  +
  +        auto_version = (*vsn_hooks->auto_versionable)(resource);
  +
  +        if (auto_version == DAV_AUTO_VERSION_ALWAYS ||
  +            (unlock && (auto_version == DAV_AUTO_VERSION_LOCKED))) {
  +
  +            if ((err = (*vsn_hooks->checkin)(resource,
  +                                             0 /*keep_checked_out*/, NULL))
  +                != NULL) {
                   return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  -				      body, err);
  +	                              apr_psprintf(r->pool,
  +			                          "Unable to auto-checkin resource %s.",
  +			                          ap_escape_html(r->pool, resource->uri)),
  +				      err);
               }
           }
       }
  -
  -    /* If parent resource was made writable, restore its state */
  -    if (av_info->parent_resource != NULL && av_info->parent_checkedout) {
  -
  -	if (undo)
  -	    err = (*vsn_hooks->uncheckout)(av_info->parent_resource);
  -	else
  -	    err = (*vsn_hooks->checkin)(av_info->parent_resource,
  -                                        0 /*keep_checked_out*/, NULL);
   
  -	if (err != NULL) {
  -	    body = apr_psprintf(r->pool,
  -                                "Unable to %s parent collection %s.",
  -                                undo ? "uncheckout" : "checkin",
  -                                ap_escape_html(r->pool, av_info->parent_resource->uri));
  -	    return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  -				  body, err);
  -	}
  +    /* If parent resource was checked out, and auto-checkin is enabled,
  +     * then check it in.
  +     */
  +    if (av_info->parent_resource != NULL && av_info->parent_resource->working
  +        && (unlock || av_info->parent_checkedout)) {
  +
  +        auto_version = (*vsn_hooks->auto_versionable)(av_info->parent_resource);
  +
  +        if (auto_version == DAV_AUTO_VERSION_ALWAYS ||
  +            (unlock && (auto_version == DAV_AUTO_VERSION_LOCKED))) {
  +
  +	    if ((err = (*vsn_hooks->checkin)(av_info->parent_resource,
  +                                             0 /*keep_checked_out*/, NULL))
  +                != NULL) {
  +	        return dav_push_error(r->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
  +	                              apr_psprintf(r->pool,
  +			                          "Unable to auto-checkin parent collection %s.",
  +			                          ap_escape_html(r->pool, av_info->parent_resource->uri)),
  +				                  err);
  +	    }
  +        }
       }
   
       return NULL;
  
  
  
  1.17      +27 -29    httpd-2.0/modules/dav/main/util_lock.c
  
  Index: util_lock.c
  ===================================================================
  RCS file: /home/cvs/httpd-2.0/modules/dav/main/util_lock.c,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -u -r1.16 -r1.17
  --- util_lock.c	2001/03/28 16:34:05	1.16
  +++ util_lock.c	2001/04/14 13:10:23	1.17
  @@ -417,6 +417,16 @@
       dav_walker_ctx *ctx = wres->walk_ctx;
       dav_error *err;
   
  +    /* Before removing the lock, do any auto-checkin required */
  +    if (wres->resource->working) {
  +        /* ### get rid of this typecast */
  +        if ((err = dav_auto_checkin(ctx->r, (dav_resource *) wres->resource,
  +                                    0 /*undo*/, 1 /*unlock*/, NULL))
  +            != NULL) {
  +            return err;
  +        }
  +    }
  +
       if ((err = (*ctx->w.lockdb->hooks->remove_lock)(ctx->w.lockdb,
                                                       wres->resource,
                                                       ctx->locktoken)) != NULL) {
  @@ -521,6 +531,8 @@
       const dav_resource *lock_resource = resource;
       const dav_hooks_locks *hooks = DAV_GET_HOOKS_LOCKS(r);
       const dav_hooks_repository *repos_hooks = resource->hooks;
  +    dav_walker_ctx ctx = { { 0 } };
  +    dav_response *multi_status;
       dav_error *err;
   
       /* If no locks provider, then there is nothing to unlock. */
  @@ -558,35 +570,21 @@
       /* At this point, lock_resource/locktoken refers to a direct lock (key), ie
        * the root of a depth > 0 lock, or locktoken is null.
        */
  -    if ((err = (*hooks->remove_lock)(lockdb, lock_resource,
  -				     locktoken)) != NULL) {
  -	/* ### add a higher-level desc? */
  -	/* ### return err! */
  -	return HTTP_INTERNAL_SERVER_ERROR;
  -    }
  -
  -    if (lock_resource->collection) {
  -        dav_walker_ctx ctx = { { 0 } };
  -        dav_response *multi_status;
  -
  -	ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
  -	ctx.w.func = dav_unlock_walker;
  -        ctx.w.walk_ctx = &ctx;
  -	ctx.w.pool = r->pool;
  -        ctx.w.root = lock_resource;
  -	ctx.w.lockdb = lockdb;
  -
  -	ctx.r = r;
  -	ctx.locktoken = locktoken;
  -
  -	err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
  -
  -	/* ### fix this! */
  -        /* ### do something with multi_status */
  -	result = err == NULL ? OK : err->status;
  -    }
  -    else
  -	result = OK;
  +    ctx.w.walk_type = DAV_WALKTYPE_NORMAL | DAV_WALKTYPE_LOCKNULL;
  +    ctx.w.func = dav_unlock_walker;
  +    ctx.w.walk_ctx = &ctx;
  +    ctx.w.pool = r->pool;
  +    ctx.w.root = lock_resource;
  +    ctx.w.lockdb = lockdb;
  +
  +    ctx.r = r;
  +    ctx.locktoken = locktoken;
  +
  +    err = (*repos_hooks->walk)(&ctx.w, DAV_INFINITY, &multi_status);
  +
  +    /* ### fix this! */
  +    /* ### do something with multi_status */
  +    result = err == NULL ? OK : err->status;
   
       (*hooks->close_lockdb)(lockdb);