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);