[prev in list] [next in list] [prev in thread] [next in thread]
List: subversion-cvs
Subject: svn commit: r20907 - in trunk/subversion/mod_dav_svn: . reports
From: maxb () tigris ! org
Date: 2006-07-30 12:19:50
Message-ID: 20060730121950.5137734C09F () svn ! collab ! net
[Download RAW message or body]
Author: maxb
Date: Sun Jul 30 05:19:49 2006
New Revision: 20907
Added:
trunk/subversion/mod_dav_svn/reports/dated-rev.c
- copied, changed from r20904, /trunk/subversion/mod_dav_svn/version.c
trunk/subversion/mod_dav_svn/reports/get-locations.c
- copied, changed from r20904, /trunk/subversion/mod_dav_svn/version.c
trunk/subversion/mod_dav_svn/reports/get-locks.c
- copied, changed from r20904, /trunk/subversion/mod_dav_svn/version.c
Modified:
trunk/subversion/mod_dav_svn/dav_svn.h
trunk/subversion/mod_dav_svn/reports/file-revs.c
trunk/subversion/mod_dav_svn/version.c
Log:
mod_dav_svn: Fork the remaining REPORT implementations which were embedded
inline in version.c out into their own files in reports/.
Also, rename dav_svn__drev_report to dav_svn__dated_rev_report.
[in subversion/mod_dav_svn/]
* dav_svn.h: Consolidate all the report handler declarations.
Remove doc-comment for file-revs-report to the .c file, for parity with
the other reports.
* reports/file-revs.c: Re-add doc-comment here.
* version.c: Remove the inline report implementations.
Update name dav_svn__drev_report to dav_svn__dated_rev_report.
* reports/get-locks.c, reports/get-locations.c, reports/dated-rev.c:
New files, forked from version.c.
Modified: trunk/subversion/mod_dav_svn/dav_svn.h
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/dav_svn.h?pathrev=20907&r1=20906&r2=20907
==============================================================================
--- trunk/subversion/mod_dav_svn/dav_svn.h (original)
+++ trunk/subversion/mod_dav_svn/dav_svn.h Sun Jul 30 05:19:49 2006
@@ -548,29 +548,37 @@
svn_boolean_t disable_merge_response,
apr_pool_t *pool);
+
+/* The various report handlers, defined in reports/, and used by version.c. */
dav_error *
dav_svn__update_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output);
-
-/* ### todo: document this, as soon as understand what the heck it
- does :-). -kff */
dav_error *
dav_svn__log_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output);
-
-/* Respond to a client request for a REPORT of type file-revs-report for the
- RESOURCE. Get request body from DOC and send result to OUTPUT. */
+dav_error *
+dav_svn__dated_rev_report(const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output);
+dav_error *
+dav_svn__get_locations_report(const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output);
dav_error *
dav_svn__file_revs_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output);
-
dav_error *
dav_svn__replay_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output);
+dav_error *
+dav_svn__get_locks_report(const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output);
+
int dav_svn_find_ns(apr_array_header_t *namespaces, const char *uri);
@@ -604,11 +612,6 @@
enum dav_svn_time_format format,
apr_pool_t *pool);
-dav_error *
-dav_svn__get_locations_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output);
-
/* Return a writable generic stream that will encode its output to base64
and send it to the Apache filter OUTPUT using BB. Allocate the stream in
Copied: trunk/subversion/mod_dav_svn/reports/dated-rev.c (from r20904, \
/trunk/subversion/mod_dav_svn/version.c)
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/reports/dated-rev.c?pathrev=20907&r1=20904&r2=20907
==============================================================================
--- /trunk/subversion/mod_dav_svn/version.c (original)
+++ trunk/subversion/mod_dav_svn/reports/dated-rev.c Sun Jul 30 05:19:49 2006
@@ -33,937 +33,17 @@
#include "svn_dav.h"
#include "svn_base64.h"
-#include "dav_svn.h"
-
-
-/* ### should move these report names to a public header to share with
- ### the client (and third parties). */
-static const dav_report_elem avail_reports[] = {
- { SVN_XML_NAMESPACE, "update-report" },
- { SVN_XML_NAMESPACE, "log-report" },
- { SVN_XML_NAMESPACE, "dated-rev-report" },
- { SVN_XML_NAMESPACE, "get-locations" },
- { SVN_XML_NAMESPACE, "file-revs-report" },
- { SVN_XML_NAMESPACE, "get-locks-report" },
- { SVN_XML_NAMESPACE, "replay-report" },
- { NULL },
-};
-
-
-/* declare these static functions early, so we can use them anywhere. */
-static dav_error *dav_svn_make_activity(dav_resource *resource);
-
-
-svn_error_t *
-dav_svn_attach_auto_revprops(svn_fs_txn_t *txn,
- const char *fs_path,
- apr_pool_t *pool)
-{
- const char *logmsg;
- svn_string_t *logval;
- svn_error_t *serr;
-
- logmsg = apr_psprintf(pool,
- "Autoversioning commit: a non-deltaV client made "
- "a change to\n%s", fs_path);
-
- logval = svn_string_create(logmsg, pool);
- if ((serr = svn_repos_fs_change_txn_prop(txn, SVN_PROP_REVISION_LOG, logval,
- pool)))
- return serr;
-
- /* Notate that this revision was created by autoversioning. (Tools
- like post-commit email scripts might not care to send an email
- for every autoversioning change.) */
- if ((serr = svn_repos_fs_change_txn_prop(txn,
- SVN_PROP_REVISION_AUTOVERSIONED,
- svn_string_create("*", pool),
- pool)))
- return serr;
-
- return SVN_NO_ERROR;
-}
-
-
-/* Helper: attach an auto-generated svn:log property to a txn within
- an auto-checked-out working resource. */
-static dav_error *
-set_auto_revprops(dav_resource *resource)
-{
- svn_error_t *serr;
-
- if (! (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Set_auto_revprops called on invalid resource.");
-
- if ((serr = dav_svn_attach_auto_revprops(resource->info->root.txn,
- resource->info->repos_path,
- resource->pool)))
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error setting a revision property "
- " on auto-checked-out resource's txn. ",
- resource->pool);
- return NULL;
-}
-
-
-static dav_error *
-open_txn(svn_fs_txn_t **ptxn,
- svn_fs_t *fs,
- const char *txn_name,
- apr_pool_t *pool)
-{
- svn_error_t *serr;
-
- serr = svn_fs_open_txn(ptxn, fs, txn_name, pool);
- if (serr != NULL)
- {
- if (serr->apr_err == SVN_ERR_FS_NO_SUCH_TRANSACTION)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "The transaction specified by the "
- "activity does not exist",
- pool);
- }
-
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "There was a problem opening the "
- "transaction specified by this "
- "activity.",
- pool);
- }
-
- return NULL;
-}
-
-
-static void
-dav_svn_get_vsn_options(apr_pool_t *p, apr_text_header *phdr)
-{
- /* Note: we append pieces with care for Web Folders's 63-char limit
- on the DAV: header */
-
- apr_text_append(p, phdr,
- "version-control,checkout,working-resource");
- apr_text_append(p, phdr,
- "merge,baseline,activity,version-controlled-collection");
-
- /* ### fork-control? */
-}
-
-
-static dav_error *
-dav_svn_get_option(const dav_resource *resource,
- const apr_xml_elem *elem,
- apr_text_header *option)
-{
- /* ### DAV:version-history-collection-set */
-
- if (elem->ns == APR_XML_NS_DAV_ID)
- {
- if (strcmp(elem->name, "activity-collection-set") == 0)
- {
- apr_text_append(resource->pool, option,
- "<D:activity-collection-set>");
- apr_text_append(resource->pool, option,
- dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_ACT_COLLECTION,
- SVN_INVALID_REVNUM, NULL,
- 1 /* add_href */, resource->pool));
- apr_text_append(resource->pool, option,
- "</D:activity-collection-set>");
- }
- }
-
- return NULL;
-}
-
-
-static int
-dav_svn_versionable(const dav_resource *resource)
-{
- return 0;
-}
-
-
-static dav_auto_version
-dav_svn_auto_versionable(const dav_resource *resource)
-{
- /* The svn client attempts to proppatch a baseline when changing
- unversioned revision props. Thus we allow baselines to be
- "auto-checked-out" by mod_dav. See issue #916. */
- if (resource->type == DAV_RESOURCE_TYPE_VERSION
- && resource->baselined)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* No other autoversioning is allowed unless the SVNAutoversioning
- directive is used. */
- if (resource->info->repos->autoversioning)
- {
- /* This allows a straight-out PUT on a public file or collection
- VCR. mod_dav's auto-versioning subsystem will check to see if
- it's possible to auto-checkout a regular resource. */
- if (resource->type == DAV_RESOURCE_TYPE_REGULAR)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* mod_dav's auto-versioning subsystem will also check to see if
- it's possible to auto-checkin a working resource that was
- auto-checked-out. We *only* allow auto-versioning on a working
- resource if it was auto-checked-out. */
- if (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out)
- return DAV_AUTO_VERSION_ALWAYS;
- }
-
- /* Default: whatever it is, assume it's not auto-versionable */
- return DAV_AUTO_VERSION_NEVER;
-}
-
-
-static dav_error *
-dav_svn_vsn_control(dav_resource *resource, const char *target)
-{
- /* All mod_dav_svn resources are versioned objects; so it doesn't
- make sense to call vsn_control on a resource that exists . */
- if (resource->exists)
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "vsn_control called on already-versioned resource.");
-
- /* Only allow a NULL target, which means an create an 'empty' VCR. */
- if (target != NULL)
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "vsn_control called with non-null target.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* This is kind of silly. The docstring for this callback says it's
- supposed to "put a resource under version control". But in
- Subversion, all REGULAR resources (bc's or public URIs) are
- already under version control. So we don't need to do a thing to
- the resource, just return. */
- return NULL;
-}
-
-
-dav_error *
-dav_svn_checkout(dav_resource *resource,
- int auto_checkout,
- int is_unreserved,
- int is_fork_ok,
- int create_activity,
- apr_array_header_t *activities,
- dav_resource **working_resource)
-{
- const char *txn_name;
- svn_error_t *serr;
- apr_status_t apr_err;
- dav_error *derr;
- dav_svn_uri_info parse;
-
- /* Auto-Versioning Stuff */
- if (auto_checkout)
- {
- dav_resource *res; /* ignored */
- const char *uuid_buf;
- void *data;
- const char *shared_activity, *shared_txn_name = NULL;
-
- /* Baselines can be auto-checked-out -- grudgingly -- so we can
- allow clients to proppatch unversioned rev props. See issue
- #916. */
- if ((resource->type == DAV_RESOURCE_TYPE_VERSION)
- && resource->baselined)
- /* ### We're violating deltaV big time here, by allowing a
- dav_auto_checkout() on something that mod_dav assumes is a
- VCR, not a VR. Anyway, mod_dav thinks we're checking out the
- resource 'in place', so that no working resource is returned.
- (It passes NULL as **working_resource.) */
- return NULL;
-
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on non-regular "
- "version-controlled resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- if (resource->baselined)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on baseline "
- "collection, which is not supported.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* See if the shared activity already exists. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- if (! shared_activity)
- {
- /* Build a shared activity for all auto-checked-out resources. */
- uuid_buf = svn_uuid_generate(resource->info->r->pool);
- shared_activity = apr_pstrdup(resource->info->r->pool, uuid_buf);
-
- derr = dav_svn_create_activity(resource->info->repos,
- &shared_txn_name,
- resource->info->r->pool);
- if (derr) return derr;
-
- derr = dav_svn_store_activity(resource->info->repos,
- shared_activity, shared_txn_name);
- if (derr) return derr;
-
- /* Save the shared activity in r->pool for others to use. */
- apr_err = apr_pool_userdata_set(shared_activity,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error setting pool userdata.",
- resource->pool);
- }
-
- if (! shared_txn_name)
- {
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
- }
-
- /* Tweak the VCR in-place, making it into a WR. (Ignore the
- NULL return value.) */
- res = dav_svn_create_working_resource(resource,
- shared_activity, shared_txn_name,
- TRUE /* tweak in place */);
-
- /* Remember that this resource was auto-checked-out, so that
- dav_svn_auto_versionable allows us to do an auto-checkin and
- dav_svn_can_be_activity will allow this resource to be an
- activity. */
- resource->info->auto_checked_out = TRUE;
-
- /* The txn and txn_root must be open and ready to go in the
- resource's root object. Normally prep_resource() will do
- this automatically on a WR's root object. We're
- converting a VCR to WR forcibly, so it's now our job to
- make sure it happens. */
- derr = open_txn(&resource->info->root.txn, resource->info->repos->fs,
- resource->info->root.txn_name, resource->pool);
- if (derr) return derr;
-
- serr = svn_fs_txn_root(&resource->info->root.root,
- resource->info->root.txn, resource->pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open a (transaction) root "
- "in the repository",
- resource->pool);
- return NULL;
- }
- /* end of Auto-Versioning Stuff */
-
- if (resource->type != DAV_RESOURCE_TYPE_VERSION)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can only be performed on a "
- "version resource [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (create_activity)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can not create an activity at "
- "this time. Use MKACTIVITY first.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (is_unreserved)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "Unreserved checkouts are not yet "
- "available. A version history may not be "
- "checked out more than once, into a "
- "specific activity.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (activities == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCOMPLETE_DATA,
- "An activity must be provided for "
- "checkout.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- /* assert: nelts > 0. the below check effectively means > 1. */
- if (activities->nelts != 1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "Only one activity may be specified within "
- "the CHECKOUT.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- serr = dav_svn_simple_parse_uri(&parse, resource,
- APR_ARRAY_IDX(activities, 0, const char *),
- resource->pool);
- if (serr != NULL)
- {
- /* ### is BAD_REQUEST proper? */
- return dav_svn_convert_err(serr, HTTP_CONFLICT,
- "The activity href could not be parsed "
- "properly.",
- resource->pool);
- }
- if (parse.activity_id == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "The provided href is not an activity URI.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- if ((txn_name = dav_svn_get_txn(resource->info->repos,
- parse.activity_id)) == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_ACTIVITY_NOT_FOUND,
- "The specified activity does not exist.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* verify the specified version resource is the "latest", thus allowing
- changes to be made. */
- if (resource->baselined || resource->info->root.rev == SVN_INVALID_REVNUM)
- {
- /* a Baseline, or a standard Version Resource which was accessed
- via a Label against a VCR within a Baseline Collection. */
- /* ### at the moment, this branch is only reached for baselines */
-
- svn_revnum_t youngest;
-
- /* make sure the baseline being checked out is the latest */
- serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not determine the youngest "
- "revision for verification against "
- "the baseline being checked out.",
- resource->pool);
- }
-
- if (resource->info->root.rev != youngest)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_BAD_BASELINE,
- "The specified baseline is not the "
- "latest baseline, so it may not be "
- "checked out.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* ### hmm. what if the transaction root's revision is different
- ### from this baseline? i.e. somebody created a new revision while
- ### we are processing this commit.
- ###
- ### first question: what does the client *do* with a working
- ### baseline? knowing that, and how it maps to our backend, then
- ### we can figure out what to do here. */
- }
- else
- {
- /* standard Version Resource */
-
- svn_fs_txn_t *txn;
- svn_fs_root_t *txn_root;
- svn_revnum_t txn_created_rev;
- dav_error *err;
-
- /* open the specified transaction so that we can verify this version
- resource corresponds to the current/latest in the transaction. */
- if ((err = open_txn(&txn, resource->info->repos->fs, txn_name,
- resource->pool)) != NULL)
- return err;
-
- serr = svn_fs_txn_root(&txn_root, txn, resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open the transaction tree.",
- resource->pool);
- }
-
- /* assert: repos_path != NULL (for this type of resource) */
-
-
- /* Out-of-dateness check: compare the created-rev of the item
- in the txn against the created-rev of the version resource
- being changed. */
- serr = svn_fs_node_created_rev(&txn_created_rev,
- txn_root, resource->info->repos_path,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not get created-rev of "
- "transaction node.",
- resource->pool);
- }
-
- /* If txn_created_rev is invalid, that means it's already
- mutable in the txn... which means it has already passed this
- out-of-dateness check. (Usually, this happens when looking
- at a parent directory of an already checked-out
- resource.)
-
- Now, we come down to it. If the created revision of the node
- in the transaction is different from the revision parsed from
- the version resource URL, we're in a bit of a quandry, and
- one of a few things could be true.
-
- - The client is trying to modify an old (out of date)
- revision of the resource. This is, of course,
- unacceptable!
-
- - The client is trying to modify a *newer* revision. If the
- version resource is *newer* than the transaction root, then
- the client started a commit, a new revision was created
- within the repository, the client fetched the new resource
- from that new revision, changed it (or merged in a prior
- change), and then attempted to incorporate that into the
- commit that was initially started. We could copy that new
- node into our transaction and then modify it, but why
- bother? We can stop the commit, and everything will be
- fine again if the user simply restarts it (because we'll
- use that new revision as the transaction root, thus
- incorporating the new resource, which they will then
- modify).
-
- - The path/revision that client is wishing to edit and the
- path/revision in the current transaction are actually the
- same node, and thus this created-rev comparison didn't
- really solidify anything after all. :-)
- */
-
- if (SVN_IS_VALID_REVNUM( txn_created_rev ))
- {
- int errorful = 0;
-
- if (resource->info->root.rev < txn_created_rev)
- {
- /* The item being modified is older than the one in the
- transaction. The client is out of date. */
- errorful = 1;
- }
- else if (resource->info->root.rev > txn_created_rev)
- {
- /* The item being modified is being accessed via a newer
- revision than the one in the transaction. We'll
- check to see if they are still the same node, and if
- not, return an error. */
- const svn_fs_id_t *url_noderev_id, *txn_noderev_id;
-
- if ((serr = svn_fs_node_id(&txn_noderev_id, txn_root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the transaction.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if ((serr = svn_fs_node_id(&url_noderev_id,
- resource->info->root.root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the revision.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if (svn_fs_compare_ids(url_noderev_id, txn_noderev_id) != 0)
- {
- errorful = 1;
- }
- }
- if (errorful)
- {
-#if 1
- return dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, SVN_ERR_FS_CONFLICT,
- "The version resource does not correspond to the resource "
- "within the transaction. Either the requested version "
- "resource is out of date (needs to be updated), or the "
- "requested version resource is newer than the transaction "
- "root (restart the commit).",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
-#else
- /* ### some debugging code */
- const char *msg;
-
- msg = apr_psprintf(resource->pool,
- "created-rev mismatch: r=%ld, t=%ld",
- resource->info->root.rev, txn_created_rev);
-
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_FS_CONFLICT, msg,
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-#endif
- }
- }
- }
- *working_resource = dav_svn_create_working_resource(resource,
- parse.activity_id,
- txn_name,
- FALSE);
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_uncheckout(dav_resource *resource)
-{
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "UNCHECKOUT called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* Try to abort the txn if it exists; but don't try too hard. :-) */
- if (resource->info->root.txn)
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- /* Attempt to destroy the shared activity. */
- if (resource->info->root.activity_id)
- {
- dav_svn_delete_activity(resource->info->repos,
- resource->info->root.activity_id);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- }
-
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* We're no longer checked out. */
- resource->info->auto_checked_out = FALSE;
-
- /* Convert the working resource back into a regular one, in-place. */
- return dav_svn_working_to_regular_resource(resource);
-}
-
-
-/* Closure object for cleanup_deltify. */
-struct cleanup_deltify_baton
-{
- /* The repository in which to deltify. We use a path instead of an
- object, because it's difficult to obtain a repos or fs object
- with the right lifetime guarantees. */
- const char *repos_path;
-
- /* The revision number against which to deltify. */
- svn_revnum_t revision;
-
- /* The pool to use for all temporary allocation while working. This
- may or may not be the same as the pool on which the cleanup is
- registered, but obviously it must have a lifetime at least as
- long as that pool. */
- apr_pool_t *pool;
-};
-
-
-/* APR pool cleanup function to deltify against a just-committed
- revision. DATA is a 'struct cleanup_deltify_baton *'.
-
- If any errors occur, log them in the httpd server error log, but
- return APR_SUCCESS no matter what, as this is a pool cleanup
- function and deltification is not a matter of correctness
- anyway. */
-static apr_status_t
-cleanup_deltify(void *data)
-{
- struct cleanup_deltify_baton *cdb = data;
- svn_repos_t *repos;
- svn_error_t *err;
-
- /* It's okay to allocate in the pool that's being cleaned up, and
- it's also okay to register new cleanups against that pool. But
- if you create subpools of it, you must make sure to destroy them
- at the end of the cleanup. So we do all our work in this
- subpool, then destroy it before exiting. */
- apr_pool_t *subpool = svn_pool_create(cdb->pool);
-
- err = svn_repos_open(&repos, cdb->repos_path, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error opening repository '%s'",
- cdb->repos_path);
- svn_error_clear(err);
- goto cleanup;
- }
-
- err = svn_fs_deltify_revision(svn_repos_fs(repos),
- cdb->revision, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error deltifying against revision %ld"
- " in repository '%s'",
- cdb->revision, cdb->repos_path);
- svn_error_clear(err);
- }
-
- cleanup:
- svn_pool_destroy(subpool);
-
- return APR_SUCCESS;
-}
-
-
-/* Register the cleanup_deltify function on POOL, which should be the
- connection pool for the request. This way the time needed for
- deltification won't delay the response to the client.
-
- REPOS is the repository in which deltify, and REVISION is the
- revision against which to deltify. POOL is both the pool on which
- to register the cleanup function and the pool that will be used for
- temporary allocations while deltifying. */
-static void
-register_deltification_cleanup(svn_repos_t *repos,
- svn_revnum_t revision,
- apr_pool_t *pool)
-{
- struct cleanup_deltify_baton *cdb = apr_palloc(pool, sizeof(*cdb));
-
- cdb->repos_path = svn_repos_path(repos, pool);
- cdb->revision = revision;
- cdb->pool = pool;
-
- apr_pool_cleanup_register(pool, cdb, cleanup_deltify, apr_pool_cleanup_null);
-}
-
-
-dav_error *
-dav_svn_checkin(dav_resource *resource,
- int keep_checked_out,
- dav_resource **version_resource)
-{
- svn_error_t *serr;
- dav_error *err;
- apr_status_t apr_err;
- const char *uri;
- const char *shared_activity;
- void *data;
-
- /* ### mod_dav has a flawed architecture, in the sense that it first
- tries to auto-checkin the modified resource, then attempts to
- auto-checkin the parent resource (if the parent resource was
- auto-checked-out). Instead, the provider should be in charge:
- mod_dav should provide a *set* of resources that need
- auto-checkin, and the provider can decide how to do it. (One
- txn? Many txns? Etc.) */
-
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKIN called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* If the global autoversioning activity still exists, that means
- nobody's committed it yet. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- /* Try to commit the txn if it exists. */
- if (shared_activity
- && (strcmp(shared_activity, resource->info->root.activity_id) == 0))
- {
- const char *shared_txn_name;
- const char *conflict_msg;
- svn_revnum_t new_rev;
-
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
-
- /* Sanity checks */
- if (resource->info->root.txn_name
- && (strcmp(shared_txn_name, resource->info->root.txn_name) != 0))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Internal txn_name doesn't match"
- " autoversioning transaction.");
-
- if (! resource->info->root.txn)
- /* should already be open by dav_svn_checkout */
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Autoversioning txn isn't open "
- "when it should be.");
-
- err = set_auto_revprops(resource);
- if (err)
- return err;
-
- serr = svn_repos_fs_commit_txn(&conflict_msg,
- resource->info->repos->repos,
- &new_rev,
- resource->info->root.txn,
- resource->pool);
-
- if (serr != NULL)
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- msg = apr_psprintf(resource->pool,
- "A conflict occurred during the CHECKIN "
- "processing. The problem occurred with "
- "the \"%s\" resource.",
- conflict_msg);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg,
- resource->pool);
- }
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(resource->info->repos->repos,
- new_rev,
- resource->info->r->connection->pool);
-
- /* If caller wants it, return the new VR that was created by
- the checkin. */
- if (version_resource)
- {
- uri = dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_VERSION,
- new_rev, resource->info->repos_path,
- 0, resource->pool);
-
- err = dav_svn_create_version_resource(version_resource, uri,
- resource->pool);
- if (err)
- return err;
- }
- } /* end of commit stuff */
-
- /* The shared activity was either nonexistent to begin with, or it's
- been committed and is only now nonexistent. The resource needs
- to forget about it. */
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* Convert the working resource back into an regular one. */
- if (! keep_checked_out)
- {
- resource->info->auto_checked_out = FALSE;
- return dav_svn_working_to_regular_resource(resource);
- }
-
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_avail_reports(const dav_resource *resource,
- const dav_report_elem **reports)
-{
- /* ### further restrict to the public space? */
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
- *reports = NULL;
- return NULL;
- }
-
- *reports = avail_reports;
- return NULL;
-}
-
-
-static int
-dav_svn_report_label_header_allowed(const apr_xml_doc *doc)
-{
- return 0;
-}
+#include "../dav_svn.h"
/* Respond to a S:dated-rev-report request. The request contains a
* DAV:creationdate element giving the requested date; the response
* contains a DAV:version-name element giving the most recent revision
* as of that date. */
-static dav_error *
-dav_svn__drev_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
+dav_error *
+dav_svn__dated_rev_report(const dav_resource *resource,
+ const apr_xml_doc *doc,
+ ap_filter_t *output)
{
apr_xml_elem *child;
int ns;
@@ -1029,795 +109,3 @@
return derr;
}
-
-
-/* Respond to a get-locks-report request. See description of this
- report in libsvn_ra_dav/fetch.c. */
-static dav_error *
-dav_svn__get_locks_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- apr_hash_t *locks;
- dav_svn__authz_read_baton arb;
- apr_hash_index_t *hi;
- apr_pool_t *subpool;
-
- /* The request URI should be a public one representing an fs path. */
- if ((! resource->info->repos_path)
- || (! resource->info->repos->repos))
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "get-locks-report run on resource which doesn't "
- "represent a path within a repository.");
-
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- /* Fetch the locks, but allow authz_read checks to happen on each. */
- if ((err = svn_repos_fs_get_locks(&locks,
- resource->info->repos->repos,
- resource->info->repos_path,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool)) != SVN_NO_ERROR)
- return dav_svn_convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
- err->message, resource->pool);
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- /* start sending report */
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* stream the locks */
- subpool = svn_pool_create(resource->pool);
- for (hi = apr_hash_first(resource->pool, locks); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- const svn_lock_t *lock;
- const char *path_quoted, *token_quoted;
- const char *creation_str, *expiration_str;
- const char *owner_to_send, *comment_to_send;
- svn_boolean_t owner_base64 = FALSE, comment_base64 = FALSE;
-
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
- lock = val;
-
- path_quoted = apr_xml_quote_string(subpool, lock->path, 1);
- token_quoted = apr_xml_quote_string(subpool, lock->token, 1);
- creation_str = svn_time_to_cstring(lock->creation_date, subpool);
-
- apr_err = ap_fprintf(output, bb,
- "<S:lock>" DEBUG_CR
- "<S:path>%s</S:path>" DEBUG_CR
- "<S:token>%s</S:token>" DEBUG_CR
- "<S:creationdate>%s</S:creationdate>" DEBUG_CR,
- path_quoted, token_quoted, creation_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->expiration_date)
- {
- expiration_str = svn_time_to_cstring(lock->expiration_date, subpool);
- apr_err = ap_fprintf(output, bb,
- "<S:expirationdate>%s</S:expirationdate>"
- DEBUG_CR, expiration_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner)))
- {
- owner_to_send = apr_xml_quote_string(subpool, lock->owner, 1);
- }
- else
- {
- svn_string_t owner_string;
- const svn_string_t *encoded_owner;
-
- owner_string.data = lock->owner;
- owner_string.len = strlen(lock->owner);
- encoded_owner = svn_base64_encode_string(&owner_string, subpool);
- owner_to_send = encoded_owner->data;
- owner_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:owner %s>%s</S:owner>" DEBUG_CR,
- owner_base64 ? "encoding=\"base64\"" : "",
- owner_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->comment)
- {
- if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment)))
- {
- comment_to_send = apr_xml_quote_string(subpool,
- lock->comment, 1);
- }
- else
- {
- svn_string_t comment_string;
- const svn_string_t *encoded_comment;
-
- comment_string.data = lock->comment;
- comment_string.len = strlen(lock->comment);
- encoded_comment = svn_base64_encode_string(&comment_string,
- subpool);
- comment_to_send = encoded_comment->data;
- comment_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:comment %s>%s</S:comment>" DEBUG_CR,
- comment_base64 ? "encoding=\"base64\"" : "",
- comment_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- apr_err = ap_fprintf(output, bb, "</S:lock>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- } /* end of hash loop */
- svn_pool_destroy(subpool);
-
- /* finish the report */
- apr_err = ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if ((apr_err = ap_fflush(output, bb)))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return NULL;
-}
-
-
-static apr_status_t
-send_get_locations_report(ap_filter_t *output,
- apr_bucket_brigade *bb,
- const dav_resource *resource,
- apr_hash_t *fs_locations)
-{
- apr_hash_index_t *hi;
- apr_pool_t *pool;
- apr_status_t apr_err;
-
- pool = resource->pool;
-
- apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR
- "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE
- "\" xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return apr_err;
-
- for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *value;
- const char *path_quoted;
-
- apr_hash_this(hi, &key, NULL, &value);
- path_quoted = apr_xml_quote_string(pool, value, 1);
- apr_err = ap_fprintf(output, bb, "<S:location "
- "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR,
- *(const svn_revnum_t *)key, path_quoted);
- if (apr_err)
- return apr_err;
- }
- return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR);
-}
-
-
-dav_error *
-dav_svn__get_locations_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- svn_error_t *serr;
- dav_error *derr = NULL;
- apr_status_t apr_err;
- apr_bucket_brigade *bb;
- dav_svn__authz_read_baton arb;
-
- /* The parameters to do the operation on. */
- const char *relative_path = NULL;
- const char *abs_path;
- svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
- apr_array_header_t *location_revisions;
-
- /* XML Parsing Variables */
- int ns;
- apr_xml_elem *child;
-
- apr_hash_t *fs_locations;
-
- location_revisions = apr_array_make(resource->pool, 0,
- sizeof(svn_revnum_t));
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain the 'svn:' "
- "namespace, so it is not going to have "
- "certain required elements.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Gather the parameters. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* If this element isn't one of ours, then skip it. */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "peg-revision") == 0)
- peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
- resource->pool, 1));
- else if (strcmp(child->name, "location-revision") == 0)
- {
- svn_revnum_t revision
- = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
- APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
- }
- else if (strcmp(child->name, "path") == 0)
- {
- relative_path = dav_xml_get_cdata(child, resource->pool, 0);
- if ((derr = dav_svn__test_canonical(relative_path, resource->pool)))
- return derr;
- }
- }
-
- /* Now we should have the parameters ready - let's
- check if they are all present. */
- if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision)))
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "Not all parameters passed.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Append the relative path to the base FS path to get an absolute
- repository path. */
- abs_path = svn_path_join(resource->info->repos_path, relative_path,
- resource->pool);
-
- /* Build an authz read baton */
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- serr = svn_repos_trace_node_locations(resource->info->repos->fs,
- &fs_locations, abs_path, peg_revision,
- location_revisions,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool);
-
- if (serr)
- {
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- serr->message, resource->pool);
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- apr_err = send_get_locations_report(output, bb, resource, fs_locations);
-
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
-
-
-static dav_error *
-dav_svn_deliver_report(request_rec *r,
- const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- int ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
-
- if (doc->root->ns == ns)
- {
- /* ### note that these report names should have symbols... */
-
- if (strcmp(doc->root->name, "update-report") == 0)
- {
- return dav_svn__update_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "log-report") == 0)
- {
- return dav_svn__log_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "dated-rev-report") == 0)
- {
- return dav_svn__drev_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locations") == 0)
- {
- return dav_svn__get_locations_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "file-revs-report") == 0)
- {
- return dav_svn__file_revs_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locks-report") == 0)
- {
- return dav_svn__get_locks_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "replay-report") == 0)
- {
- return dav_svn__replay_report(resource, doc, output);
- }
-
- /* NOTE: if you add a report, don't forget to add it to the
- * avail_reports[] array at the top of this file.
- */
- }
-
- /* ### what is a good error for an unknown report? */
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "The requested report is unknown.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-}
-
-
-static int
-dav_svn_can_be_activity(const dav_resource *resource)
-{
- /* If our resource is marked as auto_checked_out'd, then we allow this to
- * be an activity URL. Otherwise, it must be a real activity URL that
- * doesn't already exist.
- */
- return (resource->info->auto_checked_out == TRUE ||
- (resource->type == DAV_RESOURCE_TYPE_ACTIVITY &&
- !resource->exists));
-}
-
-
-static dav_error *
-dav_svn_make_activity(dav_resource *resource)
-{
- const char *activity_id = resource->info->root.activity_id;
- const char *txn_name;
- dav_error *err;
-
- /* sanity check: make sure the resource is a valid activity, in
- case an older mod_dav doesn't do the check for us. */
- if (! dav_svn_can_be_activity(resource))
- return dav_svn__new_error_tag(resource->pool, HTTP_FORBIDDEN,
- SVN_ERR_APMOD_MALFORMED_URI,
- "Activities cannot be created at that "
- "location; query the "
- "DAV:activity-collection-set property.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- err = dav_svn_create_activity(resource->info->repos, &txn_name,
- resource->pool);
- if (err != NULL)
- return err;
-
- err = dav_svn_store_activity(resource->info->repos, activity_id, txn_name);
- if (err != NULL)
- return err;
-
- /* everything is happy. update the resource */
- resource->info->root.txn_name = txn_name;
- resource->exists = 1;
- return NULL;
-}
-
-
-dav_error *
-dav_svn__build_lock_hash(apr_hash_t **locks,
- request_rec *r,
- const char *path_prefix,
- apr_pool_t *pool)
-{
- apr_status_t apr_err;
- dav_error *derr;
- void *data = NULL;
- apr_xml_doc *doc = NULL;
- apr_xml_elem *child, *lockchild;
- int ns;
- apr_hash_t *hash = apr_hash_make(pool);
-
- /* Grab the request body out of r->pool, as it contains all of the
- lock tokens. It should have been stashed already by our custom
- input filter. */
- apr_err = apr_pool_userdata_get(&data, "svn-request-body", r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- pool);
- doc = data;
- if (! doc)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- /* If there's no svn: namespace in the body, then there are
- definitely no lock-tokens to harvest. This is likely a
- request from an old client. */
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- if ((doc->root->ns == ns)
- && (strcmp(doc->root->name, "lock-token-list") == 0))
- {
- child = doc->root;
- }
- else
- {
- /* Search doc's children until we find the <lock-token-list>. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* if this element isn't one of ours, then skip it */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "lock-token-list") == 0)
- break;
- }
- }
-
- /* Did we find what we were looking for? */
- if (! child)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Then look for N different <lock> structures within. */
- for (lockchild = child->first_child; lockchild != NULL;
- lockchild = lockchild->next)
- {
- const char *lockpath = NULL, *locktoken = NULL;
- apr_xml_elem *lfchild;
-
- if (strcmp(lockchild->name, "lock") != 0)
- continue;
-
- for (lfchild = lockchild->first_child; lfchild != NULL;
- lfchild = lfchild->next)
- {
- if (strcmp(lfchild->name, "lock-path") == 0)
- {
- const char *cdata = dav_xml_get_cdata(lfchild, pool, 0);
- if ((derr = dav_svn__test_canonical(cdata, pool)))
- return derr;
-
- /* Create an absolute fs-path */
- lockpath = svn_path_join(path_prefix, cdata, pool);
- if (lockpath && locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- else if (strcmp(lfchild->name, "lock-token") == 0)
- {
- locktoken = dav_xml_get_cdata(lfchild, pool, 1);
- if (lockpath && *locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- }
- }
-
- *locks = hash;
- return SVN_NO_ERROR;
-}
-
-
-dav_error *
-dav_svn__push_locks(dav_resource *resource,
- apr_hash_t *locks,
- apr_pool_t *pool)
-{
- svn_fs_access_t *fsaccess;
- apr_hash_index_t *hi;
- svn_error_t *serr;
-
- serr = svn_fs_get_access(&fsaccess, resource->info->repos->fs);
- if (serr)
- {
- /* If an authenticated user name was attached to the request,
- then dav_svn_get_resource() should have already noticed and
- created an fs_access_t in the filesystem. */
- return dav_svn__sanitize_error(serr, "Lock token(s) in request, but "
- "missing an user name", HTTP_BAD_REQUEST,
- resource->info->r);
- }
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- const char *token;
- void *val;
- apr_hash_this(hi, NULL, NULL, &val);
- token = val;
-
- serr = svn_fs_access_add_lock_token(fsaccess, token);
- if (serr)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error pushing token into filesystem.",
- pool);
- }
-
- return NULL;
-}
-
-
-/* Helper for dav_svn_merge(). Free every lock in LOCKS. The locks
- live in REPOS. Log any errors for REQUEST. Use POOL for temporary
- work.*/
-static svn_error_t *
-release_locks(apr_hash_t *locks,
- svn_repos_t *repos,
- request_rec *r,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- const void *key;
- void *val;
- apr_pool_t *subpool = svn_pool_create(pool);
- svn_error_t *err;
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
-
- /* The lock may be stolen or broken sometime between
- svn_fs_commit_txn() and this post-commit cleanup. So ignore
- any errors from this command; just free as many locks as we can. */
- err = svn_repos_fs_unlock(repos, key, val, FALSE, subpool);
-
- if (err) /* If we got an error, just log it and move along. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, err->apr_err, r,
- "%s", err->message);
-
- svn_error_clear(err);
- }
-
- svn_pool_destroy(subpool);
-
- return SVN_NO_ERROR;
-}
-
-
-static dav_error *
-dav_svn_merge(dav_resource *target,
- dav_resource *source,
- int no_auto_merge,
- int no_checkout,
- apr_xml_elem *prop_elem,
- ap_filter_t *output)
-{
- apr_pool_t *pool;
- dav_error *err;
- svn_fs_txn_t *txn;
- const char *conflict;
- svn_error_t *serr;
- char *post_commit_err = NULL;
- svn_revnum_t new_rev;
- apr_hash_t *locks;
- svn_boolean_t disable_merge_response = FALSE;
-
- /* We'll use the target's pool for our operation. We happen to know that
- it matches the request pool, which (should) have the proper lifetime. */
- pool = target->pool;
-
- /* ### what to verify on the target? */
-
- /* ### anything else for the source? */
- if (source->type != DAV_RESOURCE_TYPE_ACTIVITY)
- {
- return dav_svn__new_error_tag(pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_INCORRECT_PARAMS,
- "MERGE can only be performed using an "
- "activity as the source [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Before attempting the final commit, we need to push any incoming
- lock-tokens into the filesystem's access_t. Normally they come
- in via 'If:' header, and dav_svn_get_resource() automatically
- notices them and does this work for us. In the case of MERGE,
- however, svn clients are sending them in the request body. */
-
- err = dav_svn__build_lock_hash(&locks, target->info->r,
- target->info->repos_path,
- pool);
- if (err != NULL)
- return err;
-
- if (apr_hash_count(locks))
- {
- err = dav_svn__push_locks(source, locks, pool);
- if (err != NULL)
- return err;
- }
-
- /* We will ignore no_auto_merge and no_checkout. We can't do those, but the
- client has no way to assert that we *should* do them. This should be fine
- because, presumably, the client has no way to do the various checkouts
- and things that would necessitate an auto-merge or checkout during the
- MERGE processing. */
-
- /* open the transaction that we're going to commit. */
- if ((err = open_txn(&txn, source->info->repos->fs,
- source->info->root.txn_name, pool)) != NULL)
- return err;
-
- /* all righty... commit the bugger. */
- serr = svn_repos_fs_commit_txn(&conflict, source->info->repos->repos,
- &new_rev, txn, pool);
-
- /* If the error was just a post-commit hook failure, we ignore it.
- Otherwise, we deal with it.
- ### TODO: Figure out if the MERGE response can grow a means by
- which to marshal back both the success of the commit (and its
- commit info) and the failure of the post-commit hook. */
- if (serr && (serr->apr_err != SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(txn, pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- /* ### we need to convert the conflict path into a URI */
- msg = apr_psprintf(pool,
- "A conflict occurred during the MERGE "
- "processing. The problem occurred with the "
- "\"%s\" resource.",
- conflict);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg, pool);
- }
- else if (serr)
- {
- if (serr->child && serr->child->message)
- post_commit_err = apr_pstrdup(pool, serr->child->message);
- svn_error_clear(serr);
- }
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(source->info->repos->repos, new_rev,
- source->info->r->connection->pool);
-
- /* We've detected a 'high level' svn action to log. */
- apr_table_set(target->info->r->subprocess_env, "SVN-ACTION",
- apr_psprintf(target->info->r->pool,
- "commit r%" SVN_REVNUM_T_FMT, new_rev));
-
- /* Since the commit was successful, the txn ID is no longer valid.
- Store an empty txn ID in the activity database so that when the
- client deletes the activity, we don't try to open and abort the
- transaction. */
- err = dav_svn_store_activity(source->info->repos,
- source->info->root.activity_id, "");
- if (err != NULL)
- return err;
-
- /* Check the dav_resource->info area for information about the
- special X-SVN-Options: header that may have come in the http
- request. */
- if (source->info->svn_client_options != NULL)
- {
- /* The client might want us to release all locks sent in the
- MERGE request. */
- if ((NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_RELEASE_LOCKS)))
- && apr_hash_count(locks))
- {
- serr = release_locks(locks, source->info->repos->repos,
- source->info->r, pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error releasing locks", pool);
- }
-
- /* The client might want us to disable the merge response altogether. */
- if (NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_NO_MERGE_RESPONSE)))
- disable_merge_response = TRUE;
- }
-
- /* process the response for the new revision. */
- return dav_svn__merge_response(output, source->info->repos, new_rev,
- post_commit_err, prop_elem,
- disable_merge_response, pool);
-}
-
-
-const dav_hooks_vsn dav_svn_hooks_vsn = {
- dav_svn_get_vsn_options,
- dav_svn_get_option,
- dav_svn_versionable,
- dav_svn_auto_versionable,
- dav_svn_vsn_control,
- dav_svn_checkout,
- dav_svn_uncheckout,
- dav_svn_checkin,
- dav_svn_avail_reports,
- dav_svn_report_label_header_allowed,
- dav_svn_deliver_report,
- NULL, /* update */
- NULL, /* add_label */
- NULL, /* remove_label */
- NULL, /* can_be_workspace */
- NULL, /* make_workspace */
- dav_svn_can_be_activity,
- dav_svn_make_activity,
- dav_svn_merge,
-};
Modified: trunk/subversion/mod_dav_svn/reports/file-revs.c
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/reports/file-revs.c?pathrev=20907&r1=20906&r2=20907
==============================================================================
--- trunk/subversion/mod_dav_svn/reports/file-revs.c (original)
+++ trunk/subversion/mod_dav_svn/reports/file-revs.c Sun Jul 30 05:19:49 2006
@@ -204,6 +204,8 @@
}
+/* Respond to a client request for a REPORT of type file-revs-report for the
+ RESOURCE. Get request body from DOC and send result to OUTPUT. */
dav_error *
dav_svn__file_revs_report(const dav_resource *resource,
const apr_xml_doc *doc,
Copied: trunk/subversion/mod_dav_svn/reports/get-locations.c (from r20904, \
/trunk/subversion/mod_dav_svn/version.c)
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/reports/get-locations.c?pathrev=20907&r1=20904&r2=20907
==============================================================================
--- /trunk/subversion/mod_dav_svn/version.c (original)
+++ trunk/subversion/mod_dav_svn/reports/get-locations.c Sun Jul 30 05:19:49 2006
@@ -33,1180 +33,7 @@
#include "svn_dav.h"
#include "svn_base64.h"
-#include "dav_svn.h"
-
-
-/* ### should move these report names to a public header to share with
- ### the client (and third parties). */
-static const dav_report_elem avail_reports[] = {
- { SVN_XML_NAMESPACE, "update-report" },
- { SVN_XML_NAMESPACE, "log-report" },
- { SVN_XML_NAMESPACE, "dated-rev-report" },
- { SVN_XML_NAMESPACE, "get-locations" },
- { SVN_XML_NAMESPACE, "file-revs-report" },
- { SVN_XML_NAMESPACE, "get-locks-report" },
- { SVN_XML_NAMESPACE, "replay-report" },
- { NULL },
-};
-
-
-/* declare these static functions early, so we can use them anywhere. */
-static dav_error *dav_svn_make_activity(dav_resource *resource);
-
-
-svn_error_t *
-dav_svn_attach_auto_revprops(svn_fs_txn_t *txn,
- const char *fs_path,
- apr_pool_t *pool)
-{
- const char *logmsg;
- svn_string_t *logval;
- svn_error_t *serr;
-
- logmsg = apr_psprintf(pool,
- "Autoversioning commit: a non-deltaV client made "
- "a change to\n%s", fs_path);
-
- logval = svn_string_create(logmsg, pool);
- if ((serr = svn_repos_fs_change_txn_prop(txn, SVN_PROP_REVISION_LOG, logval,
- pool)))
- return serr;
-
- /* Notate that this revision was created by autoversioning. (Tools
- like post-commit email scripts might not care to send an email
- for every autoversioning change.) */
- if ((serr = svn_repos_fs_change_txn_prop(txn,
- SVN_PROP_REVISION_AUTOVERSIONED,
- svn_string_create("*", pool),
- pool)))
- return serr;
-
- return SVN_NO_ERROR;
-}
-
-
-/* Helper: attach an auto-generated svn:log property to a txn within
- an auto-checked-out working resource. */
-static dav_error *
-set_auto_revprops(dav_resource *resource)
-{
- svn_error_t *serr;
-
- if (! (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Set_auto_revprops called on invalid resource.");
-
- if ((serr = dav_svn_attach_auto_revprops(resource->info->root.txn,
- resource->info->repos_path,
- resource->pool)))
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error setting a revision property "
- " on auto-checked-out resource's txn. ",
- resource->pool);
- return NULL;
-}
-
-
-static dav_error *
-open_txn(svn_fs_txn_t **ptxn,
- svn_fs_t *fs,
- const char *txn_name,
- apr_pool_t *pool)
-{
- svn_error_t *serr;
-
- serr = svn_fs_open_txn(ptxn, fs, txn_name, pool);
- if (serr != NULL)
- {
- if (serr->apr_err == SVN_ERR_FS_NO_SUCH_TRANSACTION)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "The transaction specified by the "
- "activity does not exist",
- pool);
- }
-
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "There was a problem opening the "
- "transaction specified by this "
- "activity.",
- pool);
- }
-
- return NULL;
-}
-
-
-static void
-dav_svn_get_vsn_options(apr_pool_t *p, apr_text_header *phdr)
-{
- /* Note: we append pieces with care for Web Folders's 63-char limit
- on the DAV: header */
-
- apr_text_append(p, phdr,
- "version-control,checkout,working-resource");
- apr_text_append(p, phdr,
- "merge,baseline,activity,version-controlled-collection");
-
- /* ### fork-control? */
-}
-
-
-static dav_error *
-dav_svn_get_option(const dav_resource *resource,
- const apr_xml_elem *elem,
- apr_text_header *option)
-{
- /* ### DAV:version-history-collection-set */
-
- if (elem->ns == APR_XML_NS_DAV_ID)
- {
- if (strcmp(elem->name, "activity-collection-set") == 0)
- {
- apr_text_append(resource->pool, option,
- "<D:activity-collection-set>");
- apr_text_append(resource->pool, option,
- dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_ACT_COLLECTION,
- SVN_INVALID_REVNUM, NULL,
- 1 /* add_href */, resource->pool));
- apr_text_append(resource->pool, option,
- "</D:activity-collection-set>");
- }
- }
-
- return NULL;
-}
-
-
-static int
-dav_svn_versionable(const dav_resource *resource)
-{
- return 0;
-}
-
-
-static dav_auto_version
-dav_svn_auto_versionable(const dav_resource *resource)
-{
- /* The svn client attempts to proppatch a baseline when changing
- unversioned revision props. Thus we allow baselines to be
- "auto-checked-out" by mod_dav. See issue #916. */
- if (resource->type == DAV_RESOURCE_TYPE_VERSION
- && resource->baselined)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* No other autoversioning is allowed unless the SVNAutoversioning
- directive is used. */
- if (resource->info->repos->autoversioning)
- {
- /* This allows a straight-out PUT on a public file or collection
- VCR. mod_dav's auto-versioning subsystem will check to see if
- it's possible to auto-checkout a regular resource. */
- if (resource->type == DAV_RESOURCE_TYPE_REGULAR)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* mod_dav's auto-versioning subsystem will also check to see if
- it's possible to auto-checkin a working resource that was
- auto-checked-out. We *only* allow auto-versioning on a working
- resource if it was auto-checked-out. */
- if (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out)
- return DAV_AUTO_VERSION_ALWAYS;
- }
-
- /* Default: whatever it is, assume it's not auto-versionable */
- return DAV_AUTO_VERSION_NEVER;
-}
-
-
-static dav_error *
-dav_svn_vsn_control(dav_resource *resource, const char *target)
-{
- /* All mod_dav_svn resources are versioned objects; so it doesn't
- make sense to call vsn_control on a resource that exists . */
- if (resource->exists)
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "vsn_control called on already-versioned resource.");
-
- /* Only allow a NULL target, which means an create an 'empty' VCR. */
- if (target != NULL)
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "vsn_control called with non-null target.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* This is kind of silly. The docstring for this callback says it's
- supposed to "put a resource under version control". But in
- Subversion, all REGULAR resources (bc's or public URIs) are
- already under version control. So we don't need to do a thing to
- the resource, just return. */
- return NULL;
-}
-
-
-dav_error *
-dav_svn_checkout(dav_resource *resource,
- int auto_checkout,
- int is_unreserved,
- int is_fork_ok,
- int create_activity,
- apr_array_header_t *activities,
- dav_resource **working_resource)
-{
- const char *txn_name;
- svn_error_t *serr;
- apr_status_t apr_err;
- dav_error *derr;
- dav_svn_uri_info parse;
-
- /* Auto-Versioning Stuff */
- if (auto_checkout)
- {
- dav_resource *res; /* ignored */
- const char *uuid_buf;
- void *data;
- const char *shared_activity, *shared_txn_name = NULL;
-
- /* Baselines can be auto-checked-out -- grudgingly -- so we can
- allow clients to proppatch unversioned rev props. See issue
- #916. */
- if ((resource->type == DAV_RESOURCE_TYPE_VERSION)
- && resource->baselined)
- /* ### We're violating deltaV big time here, by allowing a
- dav_auto_checkout() on something that mod_dav assumes is a
- VCR, not a VR. Anyway, mod_dav thinks we're checking out the
- resource 'in place', so that no working resource is returned.
- (It passes NULL as **working_resource.) */
- return NULL;
-
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on non-regular "
- "version-controlled resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- if (resource->baselined)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on baseline "
- "collection, which is not supported.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* See if the shared activity already exists. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- if (! shared_activity)
- {
- /* Build a shared activity for all auto-checked-out resources. */
- uuid_buf = svn_uuid_generate(resource->info->r->pool);
- shared_activity = apr_pstrdup(resource->info->r->pool, uuid_buf);
-
- derr = dav_svn_create_activity(resource->info->repos,
- &shared_txn_name,
- resource->info->r->pool);
- if (derr) return derr;
-
- derr = dav_svn_store_activity(resource->info->repos,
- shared_activity, shared_txn_name);
- if (derr) return derr;
-
- /* Save the shared activity in r->pool for others to use. */
- apr_err = apr_pool_userdata_set(shared_activity,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error setting pool userdata.",
- resource->pool);
- }
-
- if (! shared_txn_name)
- {
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
- }
-
- /* Tweak the VCR in-place, making it into a WR. (Ignore the
- NULL return value.) */
- res = dav_svn_create_working_resource(resource,
- shared_activity, shared_txn_name,
- TRUE /* tweak in place */);
-
- /* Remember that this resource was auto-checked-out, so that
- dav_svn_auto_versionable allows us to do an auto-checkin and
- dav_svn_can_be_activity will allow this resource to be an
- activity. */
- resource->info->auto_checked_out = TRUE;
-
- /* The txn and txn_root must be open and ready to go in the
- resource's root object. Normally prep_resource() will do
- this automatically on a WR's root object. We're
- converting a VCR to WR forcibly, so it's now our job to
- make sure it happens. */
- derr = open_txn(&resource->info->root.txn, resource->info->repos->fs,
- resource->info->root.txn_name, resource->pool);
- if (derr) return derr;
-
- serr = svn_fs_txn_root(&resource->info->root.root,
- resource->info->root.txn, resource->pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open a (transaction) root "
- "in the repository",
- resource->pool);
- return NULL;
- }
- /* end of Auto-Versioning Stuff */
-
- if (resource->type != DAV_RESOURCE_TYPE_VERSION)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can only be performed on a "
- "version resource [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (create_activity)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can not create an activity at "
- "this time. Use MKACTIVITY first.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (is_unreserved)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "Unreserved checkouts are not yet "
- "available. A version history may not be "
- "checked out more than once, into a "
- "specific activity.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (activities == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCOMPLETE_DATA,
- "An activity must be provided for "
- "checkout.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- /* assert: nelts > 0. the below check effectively means > 1. */
- if (activities->nelts != 1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "Only one activity may be specified within "
- "the CHECKOUT.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- serr = dav_svn_simple_parse_uri(&parse, resource,
- APR_ARRAY_IDX(activities, 0, const char *),
- resource->pool);
- if (serr != NULL)
- {
- /* ### is BAD_REQUEST proper? */
- return dav_svn_convert_err(serr, HTTP_CONFLICT,
- "The activity href could not be parsed "
- "properly.",
- resource->pool);
- }
- if (parse.activity_id == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "The provided href is not an activity URI.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- if ((txn_name = dav_svn_get_txn(resource->info->repos,
- parse.activity_id)) == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_ACTIVITY_NOT_FOUND,
- "The specified activity does not exist.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* verify the specified version resource is the "latest", thus allowing
- changes to be made. */
- if (resource->baselined || resource->info->root.rev == SVN_INVALID_REVNUM)
- {
- /* a Baseline, or a standard Version Resource which was accessed
- via a Label against a VCR within a Baseline Collection. */
- /* ### at the moment, this branch is only reached for baselines */
-
- svn_revnum_t youngest;
-
- /* make sure the baseline being checked out is the latest */
- serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not determine the youngest "
- "revision for verification against "
- "the baseline being checked out.",
- resource->pool);
- }
-
- if (resource->info->root.rev != youngest)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_BAD_BASELINE,
- "The specified baseline is not the "
- "latest baseline, so it may not be "
- "checked out.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* ### hmm. what if the transaction root's revision is different
- ### from this baseline? i.e. somebody created a new revision while
- ### we are processing this commit.
- ###
- ### first question: what does the client *do* with a working
- ### baseline? knowing that, and how it maps to our backend, then
- ### we can figure out what to do here. */
- }
- else
- {
- /* standard Version Resource */
-
- svn_fs_txn_t *txn;
- svn_fs_root_t *txn_root;
- svn_revnum_t txn_created_rev;
- dav_error *err;
-
- /* open the specified transaction so that we can verify this version
- resource corresponds to the current/latest in the transaction. */
- if ((err = open_txn(&txn, resource->info->repos->fs, txn_name,
- resource->pool)) != NULL)
- return err;
-
- serr = svn_fs_txn_root(&txn_root, txn, resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open the transaction tree.",
- resource->pool);
- }
-
- /* assert: repos_path != NULL (for this type of resource) */
-
-
- /* Out-of-dateness check: compare the created-rev of the item
- in the txn against the created-rev of the version resource
- being changed. */
- serr = svn_fs_node_created_rev(&txn_created_rev,
- txn_root, resource->info->repos_path,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not get created-rev of "
- "transaction node.",
- resource->pool);
- }
-
- /* If txn_created_rev is invalid, that means it's already
- mutable in the txn... which means it has already passed this
- out-of-dateness check. (Usually, this happens when looking
- at a parent directory of an already checked-out
- resource.)
-
- Now, we come down to it. If the created revision of the node
- in the transaction is different from the revision parsed from
- the version resource URL, we're in a bit of a quandry, and
- one of a few things could be true.
-
- - The client is trying to modify an old (out of date)
- revision of the resource. This is, of course,
- unacceptable!
-
- - The client is trying to modify a *newer* revision. If the
- version resource is *newer* than the transaction root, then
- the client started a commit, a new revision was created
- within the repository, the client fetched the new resource
- from that new revision, changed it (or merged in a prior
- change), and then attempted to incorporate that into the
- commit that was initially started. We could copy that new
- node into our transaction and then modify it, but why
- bother? We can stop the commit, and everything will be
- fine again if the user simply restarts it (because we'll
- use that new revision as the transaction root, thus
- incorporating the new resource, which they will then
- modify).
-
- - The path/revision that client is wishing to edit and the
- path/revision in the current transaction are actually the
- same node, and thus this created-rev comparison didn't
- really solidify anything after all. :-)
- */
-
- if (SVN_IS_VALID_REVNUM( txn_created_rev ))
- {
- int errorful = 0;
-
- if (resource->info->root.rev < txn_created_rev)
- {
- /* The item being modified is older than the one in the
- transaction. The client is out of date. */
- errorful = 1;
- }
- else if (resource->info->root.rev > txn_created_rev)
- {
- /* The item being modified is being accessed via a newer
- revision than the one in the transaction. We'll
- check to see if they are still the same node, and if
- not, return an error. */
- const svn_fs_id_t *url_noderev_id, *txn_noderev_id;
-
- if ((serr = svn_fs_node_id(&txn_noderev_id, txn_root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the transaction.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if ((serr = svn_fs_node_id(&url_noderev_id,
- resource->info->root.root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the revision.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if (svn_fs_compare_ids(url_noderev_id, txn_noderev_id) != 0)
- {
- errorful = 1;
- }
- }
- if (errorful)
- {
-#if 1
- return dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, SVN_ERR_FS_CONFLICT,
- "The version resource does not correspond to the resource "
- "within the transaction. Either the requested version "
- "resource is out of date (needs to be updated), or the "
- "requested version resource is newer than the transaction "
- "root (restart the commit).",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
-#else
- /* ### some debugging code */
- const char *msg;
-
- msg = apr_psprintf(resource->pool,
- "created-rev mismatch: r=%ld, t=%ld",
- resource->info->root.rev, txn_created_rev);
-
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_FS_CONFLICT, msg,
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-#endif
- }
- }
- }
- *working_resource = dav_svn_create_working_resource(resource,
- parse.activity_id,
- txn_name,
- FALSE);
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_uncheckout(dav_resource *resource)
-{
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "UNCHECKOUT called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* Try to abort the txn if it exists; but don't try too hard. :-) */
- if (resource->info->root.txn)
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- /* Attempt to destroy the shared activity. */
- if (resource->info->root.activity_id)
- {
- dav_svn_delete_activity(resource->info->repos,
- resource->info->root.activity_id);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- }
-
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* We're no longer checked out. */
- resource->info->auto_checked_out = FALSE;
-
- /* Convert the working resource back into a regular one, in-place. */
- return dav_svn_working_to_regular_resource(resource);
-}
-
-
-/* Closure object for cleanup_deltify. */
-struct cleanup_deltify_baton
-{
- /* The repository in which to deltify. We use a path instead of an
- object, because it's difficult to obtain a repos or fs object
- with the right lifetime guarantees. */
- const char *repos_path;
-
- /* The revision number against which to deltify. */
- svn_revnum_t revision;
-
- /* The pool to use for all temporary allocation while working. This
- may or may not be the same as the pool on which the cleanup is
- registered, but obviously it must have a lifetime at least as
- long as that pool. */
- apr_pool_t *pool;
-};
-
-
-/* APR pool cleanup function to deltify against a just-committed
- revision. DATA is a 'struct cleanup_deltify_baton *'.
-
- If any errors occur, log them in the httpd server error log, but
- return APR_SUCCESS no matter what, as this is a pool cleanup
- function and deltification is not a matter of correctness
- anyway. */
-static apr_status_t
-cleanup_deltify(void *data)
-{
- struct cleanup_deltify_baton *cdb = data;
- svn_repos_t *repos;
- svn_error_t *err;
-
- /* It's okay to allocate in the pool that's being cleaned up, and
- it's also okay to register new cleanups against that pool. But
- if you create subpools of it, you must make sure to destroy them
- at the end of the cleanup. So we do all our work in this
- subpool, then destroy it before exiting. */
- apr_pool_t *subpool = svn_pool_create(cdb->pool);
-
- err = svn_repos_open(&repos, cdb->repos_path, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error opening repository '%s'",
- cdb->repos_path);
- svn_error_clear(err);
- goto cleanup;
- }
-
- err = svn_fs_deltify_revision(svn_repos_fs(repos),
- cdb->revision, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error deltifying against revision %ld"
- " in repository '%s'",
- cdb->revision, cdb->repos_path);
- svn_error_clear(err);
- }
-
- cleanup:
- svn_pool_destroy(subpool);
-
- return APR_SUCCESS;
-}
-
-
-/* Register the cleanup_deltify function on POOL, which should be the
- connection pool for the request. This way the time needed for
- deltification won't delay the response to the client.
-
- REPOS is the repository in which deltify, and REVISION is the
- revision against which to deltify. POOL is both the pool on which
- to register the cleanup function and the pool that will be used for
- temporary allocations while deltifying. */
-static void
-register_deltification_cleanup(svn_repos_t *repos,
- svn_revnum_t revision,
- apr_pool_t *pool)
-{
- struct cleanup_deltify_baton *cdb = apr_palloc(pool, sizeof(*cdb));
-
- cdb->repos_path = svn_repos_path(repos, pool);
- cdb->revision = revision;
- cdb->pool = pool;
-
- apr_pool_cleanup_register(pool, cdb, cleanup_deltify, apr_pool_cleanup_null);
-}
-
-
-dav_error *
-dav_svn_checkin(dav_resource *resource,
- int keep_checked_out,
- dav_resource **version_resource)
-{
- svn_error_t *serr;
- dav_error *err;
- apr_status_t apr_err;
- const char *uri;
- const char *shared_activity;
- void *data;
-
- /* ### mod_dav has a flawed architecture, in the sense that it first
- tries to auto-checkin the modified resource, then attempts to
- auto-checkin the parent resource (if the parent resource was
- auto-checked-out). Instead, the provider should be in charge:
- mod_dav should provide a *set* of resources that need
- auto-checkin, and the provider can decide how to do it. (One
- txn? Many txns? Etc.) */
-
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKIN called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* If the global autoversioning activity still exists, that means
- nobody's committed it yet. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- /* Try to commit the txn if it exists. */
- if (shared_activity
- && (strcmp(shared_activity, resource->info->root.activity_id) == 0))
- {
- const char *shared_txn_name;
- const char *conflict_msg;
- svn_revnum_t new_rev;
-
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
-
- /* Sanity checks */
- if (resource->info->root.txn_name
- && (strcmp(shared_txn_name, resource->info->root.txn_name) != 0))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Internal txn_name doesn't match"
- " autoversioning transaction.");
-
- if (! resource->info->root.txn)
- /* should already be open by dav_svn_checkout */
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Autoversioning txn isn't open "
- "when it should be.");
-
- err = set_auto_revprops(resource);
- if (err)
- return err;
-
- serr = svn_repos_fs_commit_txn(&conflict_msg,
- resource->info->repos->repos,
- &new_rev,
- resource->info->root.txn,
- resource->pool);
-
- if (serr != NULL)
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- msg = apr_psprintf(resource->pool,
- "A conflict occurred during the CHECKIN "
- "processing. The problem occurred with "
- "the \"%s\" resource.",
- conflict_msg);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg,
- resource->pool);
- }
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(resource->info->repos->repos,
- new_rev,
- resource->info->r->connection->pool);
-
- /* If caller wants it, return the new VR that was created by
- the checkin. */
- if (version_resource)
- {
- uri = dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_VERSION,
- new_rev, resource->info->repos_path,
- 0, resource->pool);
-
- err = dav_svn_create_version_resource(version_resource, uri,
- resource->pool);
- if (err)
- return err;
- }
- } /* end of commit stuff */
-
- /* The shared activity was either nonexistent to begin with, or it's
- been committed and is only now nonexistent. The resource needs
- to forget about it. */
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* Convert the working resource back into an regular one. */
- if (! keep_checked_out)
- {
- resource->info->auto_checked_out = FALSE;
- return dav_svn_working_to_regular_resource(resource);
- }
-
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_avail_reports(const dav_resource *resource,
- const dav_report_elem **reports)
-{
- /* ### further restrict to the public space? */
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
- *reports = NULL;
- return NULL;
- }
-
- *reports = avail_reports;
- return NULL;
-}
-
-
-static int
-dav_svn_report_label_header_allowed(const apr_xml_doc *doc)
-{
- return 0;
-}
-
-
-/* Respond to a S:dated-rev-report request. The request contains a
- * DAV:creationdate element giving the requested date; the response
- * contains a DAV:version-name element giving the most recent revision
- * as of that date. */
-static dav_error *
-dav_svn__drev_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_xml_elem *child;
- int ns;
- apr_time_t tm = (apr_time_t) -1;
- svn_revnum_t rev;
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- dav_error *derr = NULL;
-
- /* Find the DAV:creationdate element and get the requested time from it. */
- ns = dav_svn_find_ns(doc->namespaces, "DAV:");
- if (ns != -1)
- {
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- if (child->ns != ns || strcmp(child->name, "creationdate") != 0)
- continue;
- /* If this fails, we'll notice below, so ignore any error for now. */
- svn_error_clear
- (svn_time_from_cstring(&tm, dav_xml_get_cdata(child,
- resource->pool, 1),
- resource->pool));
- }
- }
-
- if (tm == (apr_time_t) -1)
- {
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain a valid "
- "'DAV:creationdate' element.");
- }
-
- /* Do the actual work of finding the revision by date. */
- if ((err = svn_repos_dated_revision(&rev, resource->info->repos->repos, tm,
- resource->pool)) != SVN_NO_ERROR)
- {
- svn_error_clear(err);
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Could not access revision times.");
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR
- "<D:version-name>%ld</D:version-name>"
- "</S:dated-rev-report>", rev);
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
-
-
-/* Respond to a get-locks-report request. See description of this
- report in libsvn_ra_dav/fetch.c. */
-static dav_error *
-dav_svn__get_locks_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- apr_hash_t *locks;
- dav_svn__authz_read_baton arb;
- apr_hash_index_t *hi;
- apr_pool_t *subpool;
-
- /* The request URI should be a public one representing an fs path. */
- if ((! resource->info->repos_path)
- || (! resource->info->repos->repos))
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "get-locks-report run on resource which doesn't "
- "represent a path within a repository.");
-
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- /* Fetch the locks, but allow authz_read checks to happen on each. */
- if ((err = svn_repos_fs_get_locks(&locks,
- resource->info->repos->repos,
- resource->info->repos_path,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool)) != SVN_NO_ERROR)
- return dav_svn_convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
- err->message, resource->pool);
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- /* start sending report */
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* stream the locks */
- subpool = svn_pool_create(resource->pool);
- for (hi = apr_hash_first(resource->pool, locks); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- const svn_lock_t *lock;
- const char *path_quoted, *token_quoted;
- const char *creation_str, *expiration_str;
- const char *owner_to_send, *comment_to_send;
- svn_boolean_t owner_base64 = FALSE, comment_base64 = FALSE;
-
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
- lock = val;
-
- path_quoted = apr_xml_quote_string(subpool, lock->path, 1);
- token_quoted = apr_xml_quote_string(subpool, lock->token, 1);
- creation_str = svn_time_to_cstring(lock->creation_date, subpool);
-
- apr_err = ap_fprintf(output, bb,
- "<S:lock>" DEBUG_CR
- "<S:path>%s</S:path>" DEBUG_CR
- "<S:token>%s</S:token>" DEBUG_CR
- "<S:creationdate>%s</S:creationdate>" DEBUG_CR,
- path_quoted, token_quoted, creation_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->expiration_date)
- {
- expiration_str = svn_time_to_cstring(lock->expiration_date, subpool);
- apr_err = ap_fprintf(output, bb,
- "<S:expirationdate>%s</S:expirationdate>"
- DEBUG_CR, expiration_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner)))
- {
- owner_to_send = apr_xml_quote_string(subpool, lock->owner, 1);
- }
- else
- {
- svn_string_t owner_string;
- const svn_string_t *encoded_owner;
-
- owner_string.data = lock->owner;
- owner_string.len = strlen(lock->owner);
- encoded_owner = svn_base64_encode_string(&owner_string, subpool);
- owner_to_send = encoded_owner->data;
- owner_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:owner %s>%s</S:owner>" DEBUG_CR,
- owner_base64 ? "encoding=\"base64\"" : "",
- owner_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->comment)
- {
- if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment)))
- {
- comment_to_send = apr_xml_quote_string(subpool,
- lock->comment, 1);
- }
- else
- {
- svn_string_t comment_string;
- const svn_string_t *encoded_comment;
-
- comment_string.data = lock->comment;
- comment_string.len = strlen(lock->comment);
- encoded_comment = svn_base64_encode_string(&comment_string,
- subpool);
- comment_to_send = encoded_comment->data;
- comment_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:comment %s>%s</S:comment>" DEBUG_CR,
- comment_base64 ? "encoding=\"base64\"" : "",
- comment_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- apr_err = ap_fprintf(output, bb, "</S:lock>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- } /* end of hash loop */
- svn_pool_destroy(subpool);
-
- /* finish the report */
- apr_err = ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if ((apr_err = ap_fflush(output, bb)))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return NULL;
-}
+#include "../dav_svn.h"
static apr_status_t
@@ -1358,466 +185,3 @@
return derr;
}
-
-
-static dav_error *
-dav_svn_deliver_report(request_rec *r,
- const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- int ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
-
- if (doc->root->ns == ns)
- {
- /* ### note that these report names should have symbols... */
-
- if (strcmp(doc->root->name, "update-report") == 0)
- {
- return dav_svn__update_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "log-report") == 0)
- {
- return dav_svn__log_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "dated-rev-report") == 0)
- {
- return dav_svn__drev_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locations") == 0)
- {
- return dav_svn__get_locations_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "file-revs-report") == 0)
- {
- return dav_svn__file_revs_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locks-report") == 0)
- {
- return dav_svn__get_locks_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "replay-report") == 0)
- {
- return dav_svn__replay_report(resource, doc, output);
- }
-
- /* NOTE: if you add a report, don't forget to add it to the
- * avail_reports[] array at the top of this file.
- */
- }
-
- /* ### what is a good error for an unknown report? */
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "The requested report is unknown.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-}
-
-
-static int
-dav_svn_can_be_activity(const dav_resource *resource)
-{
- /* If our resource is marked as auto_checked_out'd, then we allow this to
- * be an activity URL. Otherwise, it must be a real activity URL that
- * doesn't already exist.
- */
- return (resource->info->auto_checked_out == TRUE ||
- (resource->type == DAV_RESOURCE_TYPE_ACTIVITY &&
- !resource->exists));
-}
-
-
-static dav_error *
-dav_svn_make_activity(dav_resource *resource)
-{
- const char *activity_id = resource->info->root.activity_id;
- const char *txn_name;
- dav_error *err;
-
- /* sanity check: make sure the resource is a valid activity, in
- case an older mod_dav doesn't do the check for us. */
- if (! dav_svn_can_be_activity(resource))
- return dav_svn__new_error_tag(resource->pool, HTTP_FORBIDDEN,
- SVN_ERR_APMOD_MALFORMED_URI,
- "Activities cannot be created at that "
- "location; query the "
- "DAV:activity-collection-set property.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- err = dav_svn_create_activity(resource->info->repos, &txn_name,
- resource->pool);
- if (err != NULL)
- return err;
-
- err = dav_svn_store_activity(resource->info->repos, activity_id, txn_name);
- if (err != NULL)
- return err;
-
- /* everything is happy. update the resource */
- resource->info->root.txn_name = txn_name;
- resource->exists = 1;
- return NULL;
-}
-
-
-dav_error *
-dav_svn__build_lock_hash(apr_hash_t **locks,
- request_rec *r,
- const char *path_prefix,
- apr_pool_t *pool)
-{
- apr_status_t apr_err;
- dav_error *derr;
- void *data = NULL;
- apr_xml_doc *doc = NULL;
- apr_xml_elem *child, *lockchild;
- int ns;
- apr_hash_t *hash = apr_hash_make(pool);
-
- /* Grab the request body out of r->pool, as it contains all of the
- lock tokens. It should have been stashed already by our custom
- input filter. */
- apr_err = apr_pool_userdata_get(&data, "svn-request-body", r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- pool);
- doc = data;
- if (! doc)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- /* If there's no svn: namespace in the body, then there are
- definitely no lock-tokens to harvest. This is likely a
- request from an old client. */
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- if ((doc->root->ns == ns)
- && (strcmp(doc->root->name, "lock-token-list") == 0))
- {
- child = doc->root;
- }
- else
- {
- /* Search doc's children until we find the <lock-token-list>. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* if this element isn't one of ours, then skip it */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "lock-token-list") == 0)
- break;
- }
- }
-
- /* Did we find what we were looking for? */
- if (! child)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Then look for N different <lock> structures within. */
- for (lockchild = child->first_child; lockchild != NULL;
- lockchild = lockchild->next)
- {
- const char *lockpath = NULL, *locktoken = NULL;
- apr_xml_elem *lfchild;
-
- if (strcmp(lockchild->name, "lock") != 0)
- continue;
-
- for (lfchild = lockchild->first_child; lfchild != NULL;
- lfchild = lfchild->next)
- {
- if (strcmp(lfchild->name, "lock-path") == 0)
- {
- const char *cdata = dav_xml_get_cdata(lfchild, pool, 0);
- if ((derr = dav_svn__test_canonical(cdata, pool)))
- return derr;
-
- /* Create an absolute fs-path */
- lockpath = svn_path_join(path_prefix, cdata, pool);
- if (lockpath && locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- else if (strcmp(lfchild->name, "lock-token") == 0)
- {
- locktoken = dav_xml_get_cdata(lfchild, pool, 1);
- if (lockpath && *locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- }
- }
-
- *locks = hash;
- return SVN_NO_ERROR;
-}
-
-
-dav_error *
-dav_svn__push_locks(dav_resource *resource,
- apr_hash_t *locks,
- apr_pool_t *pool)
-{
- svn_fs_access_t *fsaccess;
- apr_hash_index_t *hi;
- svn_error_t *serr;
-
- serr = svn_fs_get_access(&fsaccess, resource->info->repos->fs);
- if (serr)
- {
- /* If an authenticated user name was attached to the request,
- then dav_svn_get_resource() should have already noticed and
- created an fs_access_t in the filesystem. */
- return dav_svn__sanitize_error(serr, "Lock token(s) in request, but "
- "missing an user name", HTTP_BAD_REQUEST,
- resource->info->r);
- }
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- const char *token;
- void *val;
- apr_hash_this(hi, NULL, NULL, &val);
- token = val;
-
- serr = svn_fs_access_add_lock_token(fsaccess, token);
- if (serr)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error pushing token into filesystem.",
- pool);
- }
-
- return NULL;
-}
-
-
-/* Helper for dav_svn_merge(). Free every lock in LOCKS. The locks
- live in REPOS. Log any errors for REQUEST. Use POOL for temporary
- work.*/
-static svn_error_t *
-release_locks(apr_hash_t *locks,
- svn_repos_t *repos,
- request_rec *r,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- const void *key;
- void *val;
- apr_pool_t *subpool = svn_pool_create(pool);
- svn_error_t *err;
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
-
- /* The lock may be stolen or broken sometime between
- svn_fs_commit_txn() and this post-commit cleanup. So ignore
- any errors from this command; just free as many locks as we can. */
- err = svn_repos_fs_unlock(repos, key, val, FALSE, subpool);
-
- if (err) /* If we got an error, just log it and move along. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, err->apr_err, r,
- "%s", err->message);
-
- svn_error_clear(err);
- }
-
- svn_pool_destroy(subpool);
-
- return SVN_NO_ERROR;
-}
-
-
-static dav_error *
-dav_svn_merge(dav_resource *target,
- dav_resource *source,
- int no_auto_merge,
- int no_checkout,
- apr_xml_elem *prop_elem,
- ap_filter_t *output)
-{
- apr_pool_t *pool;
- dav_error *err;
- svn_fs_txn_t *txn;
- const char *conflict;
- svn_error_t *serr;
- char *post_commit_err = NULL;
- svn_revnum_t new_rev;
- apr_hash_t *locks;
- svn_boolean_t disable_merge_response = FALSE;
-
- /* We'll use the target's pool for our operation. We happen to know that
- it matches the request pool, which (should) have the proper lifetime. */
- pool = target->pool;
-
- /* ### what to verify on the target? */
-
- /* ### anything else for the source? */
- if (source->type != DAV_RESOURCE_TYPE_ACTIVITY)
- {
- return dav_svn__new_error_tag(pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_INCORRECT_PARAMS,
- "MERGE can only be performed using an "
- "activity as the source [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Before attempting the final commit, we need to push any incoming
- lock-tokens into the filesystem's access_t. Normally they come
- in via 'If:' header, and dav_svn_get_resource() automatically
- notices them and does this work for us. In the case of MERGE,
- however, svn clients are sending them in the request body. */
-
- err = dav_svn__build_lock_hash(&locks, target->info->r,
- target->info->repos_path,
- pool);
- if (err != NULL)
- return err;
-
- if (apr_hash_count(locks))
- {
- err = dav_svn__push_locks(source, locks, pool);
- if (err != NULL)
- return err;
- }
-
- /* We will ignore no_auto_merge and no_checkout. We can't do those, but the
- client has no way to assert that we *should* do them. This should be fine
- because, presumably, the client has no way to do the various checkouts
- and things that would necessitate an auto-merge or checkout during the
- MERGE processing. */
-
- /* open the transaction that we're going to commit. */
- if ((err = open_txn(&txn, source->info->repos->fs,
- source->info->root.txn_name, pool)) != NULL)
- return err;
-
- /* all righty... commit the bugger. */
- serr = svn_repos_fs_commit_txn(&conflict, source->info->repos->repos,
- &new_rev, txn, pool);
-
- /* If the error was just a post-commit hook failure, we ignore it.
- Otherwise, we deal with it.
- ### TODO: Figure out if the MERGE response can grow a means by
- which to marshal back both the success of the commit (and its
- commit info) and the failure of the post-commit hook. */
- if (serr && (serr->apr_err != SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(txn, pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- /* ### we need to convert the conflict path into a URI */
- msg = apr_psprintf(pool,
- "A conflict occurred during the MERGE "
- "processing. The problem occurred with the "
- "\"%s\" resource.",
- conflict);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg, pool);
- }
- else if (serr)
- {
- if (serr->child && serr->child->message)
- post_commit_err = apr_pstrdup(pool, serr->child->message);
- svn_error_clear(serr);
- }
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(source->info->repos->repos, new_rev,
- source->info->r->connection->pool);
-
- /* We've detected a 'high level' svn action to log. */
- apr_table_set(target->info->r->subprocess_env, "SVN-ACTION",
- apr_psprintf(target->info->r->pool,
- "commit r%" SVN_REVNUM_T_FMT, new_rev));
-
- /* Since the commit was successful, the txn ID is no longer valid.
- Store an empty txn ID in the activity database so that when the
- client deletes the activity, we don't try to open and abort the
- transaction. */
- err = dav_svn_store_activity(source->info->repos,
- source->info->root.activity_id, "");
- if (err != NULL)
- return err;
-
- /* Check the dav_resource->info area for information about the
- special X-SVN-Options: header that may have come in the http
- request. */
- if (source->info->svn_client_options != NULL)
- {
- /* The client might want us to release all locks sent in the
- MERGE request. */
- if ((NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_RELEASE_LOCKS)))
- && apr_hash_count(locks))
- {
- serr = release_locks(locks, source->info->repos->repos,
- source->info->r, pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error releasing locks", pool);
- }
-
- /* The client might want us to disable the merge response altogether. */
- if (NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_NO_MERGE_RESPONSE)))
- disable_merge_response = TRUE;
- }
-
- /* process the response for the new revision. */
- return dav_svn__merge_response(output, source->info->repos, new_rev,
- post_commit_err, prop_elem,
- disable_merge_response, pool);
-}
-
-
-const dav_hooks_vsn dav_svn_hooks_vsn = {
- dav_svn_get_vsn_options,
- dav_svn_get_option,
- dav_svn_versionable,
- dav_svn_auto_versionable,
- dav_svn_vsn_control,
- dav_svn_checkout,
- dav_svn_uncheckout,
- dav_svn_checkin,
- dav_svn_avail_reports,
- dav_svn_report_label_header_allowed,
- dav_svn_deliver_report,
- NULL, /* update */
- NULL, /* add_label */
- NULL, /* remove_label */
- NULL, /* can_be_workspace */
- NULL, /* make_workspace */
- dav_svn_can_be_activity,
- dav_svn_make_activity,
- dav_svn_merge,
-};
Copied: trunk/subversion/mod_dav_svn/reports/get-locks.c (from r20904, \
/trunk/subversion/mod_dav_svn/version.c)
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/reports/get-locks.c?pathrev=20907&r1=20904&r2=20907
==============================================================================
--- /trunk/subversion/mod_dav_svn/version.c (original)
+++ trunk/subversion/mod_dav_svn/reports/get-locks.c Sun Jul 30 05:19:49 2006
@@ -33,1007 +33,12 @@
#include "svn_dav.h"
#include "svn_base64.h"
-#include "dav_svn.h"
-
-
-/* ### should move these report names to a public header to share with
- ### the client (and third parties). */
-static const dav_report_elem avail_reports[] = {
- { SVN_XML_NAMESPACE, "update-report" },
- { SVN_XML_NAMESPACE, "log-report" },
- { SVN_XML_NAMESPACE, "dated-rev-report" },
- { SVN_XML_NAMESPACE, "get-locations" },
- { SVN_XML_NAMESPACE, "file-revs-report" },
- { SVN_XML_NAMESPACE, "get-locks-report" },
- { SVN_XML_NAMESPACE, "replay-report" },
- { NULL },
-};
-
-
-/* declare these static functions early, so we can use them anywhere. */
-static dav_error *dav_svn_make_activity(dav_resource *resource);
-
-
-svn_error_t *
-dav_svn_attach_auto_revprops(svn_fs_txn_t *txn,
- const char *fs_path,
- apr_pool_t *pool)
-{
- const char *logmsg;
- svn_string_t *logval;
- svn_error_t *serr;
-
- logmsg = apr_psprintf(pool,
- "Autoversioning commit: a non-deltaV client made "
- "a change to\n%s", fs_path);
-
- logval = svn_string_create(logmsg, pool);
- if ((serr = svn_repos_fs_change_txn_prop(txn, SVN_PROP_REVISION_LOG, logval,
- pool)))
- return serr;
-
- /* Notate that this revision was created by autoversioning. (Tools
- like post-commit email scripts might not care to send an email
- for every autoversioning change.) */
- if ((serr = svn_repos_fs_change_txn_prop(txn,
- SVN_PROP_REVISION_AUTOVERSIONED,
- svn_string_create("*", pool),
- pool)))
- return serr;
-
- return SVN_NO_ERROR;
-}
-
-
-/* Helper: attach an auto-generated svn:log property to a txn within
- an auto-checked-out working resource. */
-static dav_error *
-set_auto_revprops(dav_resource *resource)
-{
- svn_error_t *serr;
-
- if (! (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Set_auto_revprops called on invalid resource.");
-
- if ((serr = dav_svn_attach_auto_revprops(resource->info->root.txn,
- resource->info->repos_path,
- resource->pool)))
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error setting a revision property "
- " on auto-checked-out resource's txn. ",
- resource->pool);
- return NULL;
-}
-
-
-static dav_error *
-open_txn(svn_fs_txn_t **ptxn,
- svn_fs_t *fs,
- const char *txn_name,
- apr_pool_t *pool)
-{
- svn_error_t *serr;
-
- serr = svn_fs_open_txn(ptxn, fs, txn_name, pool);
- if (serr != NULL)
- {
- if (serr->apr_err == SVN_ERR_FS_NO_SUCH_TRANSACTION)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "The transaction specified by the "
- "activity does not exist",
- pool);
- }
-
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "There was a problem opening the "
- "transaction specified by this "
- "activity.",
- pool);
- }
-
- return NULL;
-}
-
-
-static void
-dav_svn_get_vsn_options(apr_pool_t *p, apr_text_header *phdr)
-{
- /* Note: we append pieces with care for Web Folders's 63-char limit
- on the DAV: header */
-
- apr_text_append(p, phdr,
- "version-control,checkout,working-resource");
- apr_text_append(p, phdr,
- "merge,baseline,activity,version-controlled-collection");
-
- /* ### fork-control? */
-}
-
-
-static dav_error *
-dav_svn_get_option(const dav_resource *resource,
- const apr_xml_elem *elem,
- apr_text_header *option)
-{
- /* ### DAV:version-history-collection-set */
-
- if (elem->ns == APR_XML_NS_DAV_ID)
- {
- if (strcmp(elem->name, "activity-collection-set") == 0)
- {
- apr_text_append(resource->pool, option,
- "<D:activity-collection-set>");
- apr_text_append(resource->pool, option,
- dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_ACT_COLLECTION,
- SVN_INVALID_REVNUM, NULL,
- 1 /* add_href */, resource->pool));
- apr_text_append(resource->pool, option,
- "</D:activity-collection-set>");
- }
- }
-
- return NULL;
-}
-
-
-static int
-dav_svn_versionable(const dav_resource *resource)
-{
- return 0;
-}
-
-
-static dav_auto_version
-dav_svn_auto_versionable(const dav_resource *resource)
-{
- /* The svn client attempts to proppatch a baseline when changing
- unversioned revision props. Thus we allow baselines to be
- "auto-checked-out" by mod_dav. See issue #916. */
- if (resource->type == DAV_RESOURCE_TYPE_VERSION
- && resource->baselined)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* No other autoversioning is allowed unless the SVNAutoversioning
- directive is used. */
- if (resource->info->repos->autoversioning)
- {
- /* This allows a straight-out PUT on a public file or collection
- VCR. mod_dav's auto-versioning subsystem will check to see if
- it's possible to auto-checkout a regular resource. */
- if (resource->type == DAV_RESOURCE_TYPE_REGULAR)
- return DAV_AUTO_VERSION_ALWAYS;
-
- /* mod_dav's auto-versioning subsystem will also check to see if
- it's possible to auto-checkin a working resource that was
- auto-checked-out. We *only* allow auto-versioning on a working
- resource if it was auto-checked-out. */
- if (resource->type == DAV_RESOURCE_TYPE_WORKING
- && resource->info->auto_checked_out)
- return DAV_AUTO_VERSION_ALWAYS;
- }
-
- /* Default: whatever it is, assume it's not auto-versionable */
- return DAV_AUTO_VERSION_NEVER;
-}
-
-
-static dav_error *
-dav_svn_vsn_control(dav_resource *resource, const char *target)
-{
- /* All mod_dav_svn resources are versioned objects; so it doesn't
- make sense to call vsn_control on a resource that exists . */
- if (resource->exists)
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "vsn_control called on already-versioned resource.");
-
- /* Only allow a NULL target, which means an create an 'empty' VCR. */
- if (target != NULL)
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "vsn_control called with non-null target.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* This is kind of silly. The docstring for this callback says it's
- supposed to "put a resource under version control". But in
- Subversion, all REGULAR resources (bc's or public URIs) are
- already under version control. So we don't need to do a thing to
- the resource, just return. */
- return NULL;
-}
-
-
-dav_error *
-dav_svn_checkout(dav_resource *resource,
- int auto_checkout,
- int is_unreserved,
- int is_fork_ok,
- int create_activity,
- apr_array_header_t *activities,
- dav_resource **working_resource)
-{
- const char *txn_name;
- svn_error_t *serr;
- apr_status_t apr_err;
- dav_error *derr;
- dav_svn_uri_info parse;
-
- /* Auto-Versioning Stuff */
- if (auto_checkout)
- {
- dav_resource *res; /* ignored */
- const char *uuid_buf;
- void *data;
- const char *shared_activity, *shared_txn_name = NULL;
-
- /* Baselines can be auto-checked-out -- grudgingly -- so we can
- allow clients to proppatch unversioned rev props. See issue
- #916. */
- if ((resource->type == DAV_RESOURCE_TYPE_VERSION)
- && resource->baselined)
- /* ### We're violating deltaV big time here, by allowing a
- dav_auto_checkout() on something that mod_dav assumes is a
- VCR, not a VR. Anyway, mod_dav thinks we're checking out the
- resource 'in place', so that no working resource is returned.
- (It passes NULL as **working_resource.) */
- return NULL;
-
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on non-regular "
- "version-controlled resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- if (resource->baselined)
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "auto-checkout attempted on baseline "
- "collection, which is not supported.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* See if the shared activity already exists. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- if (! shared_activity)
- {
- /* Build a shared activity for all auto-checked-out resources. */
- uuid_buf = svn_uuid_generate(resource->info->r->pool);
- shared_activity = apr_pstrdup(resource->info->r->pool, uuid_buf);
-
- derr = dav_svn_create_activity(resource->info->repos,
- &shared_txn_name,
- resource->info->r->pool);
- if (derr) return derr;
-
- derr = dav_svn_store_activity(resource->info->repos,
- shared_activity, shared_txn_name);
- if (derr) return derr;
-
- /* Save the shared activity in r->pool for others to use. */
- apr_err = apr_pool_userdata_set(shared_activity,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error setting pool userdata.",
- resource->pool);
- }
-
- if (! shared_txn_name)
- {
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
- }
-
- /* Tweak the VCR in-place, making it into a WR. (Ignore the
- NULL return value.) */
- res = dav_svn_create_working_resource(resource,
- shared_activity, shared_txn_name,
- TRUE /* tweak in place */);
-
- /* Remember that this resource was auto-checked-out, so that
- dav_svn_auto_versionable allows us to do an auto-checkin and
- dav_svn_can_be_activity will allow this resource to be an
- activity. */
- resource->info->auto_checked_out = TRUE;
-
- /* The txn and txn_root must be open and ready to go in the
- resource's root object. Normally prep_resource() will do
- this automatically on a WR's root object. We're
- converting a VCR to WR forcibly, so it's now our job to
- make sure it happens. */
- derr = open_txn(&resource->info->root.txn, resource->info->repos->fs,
- resource->info->root.txn_name, resource->pool);
- if (derr) return derr;
-
- serr = svn_fs_txn_root(&resource->info->root.root,
- resource->info->root.txn, resource->pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open a (transaction) root "
- "in the repository",
- resource->pool);
- return NULL;
- }
- /* end of Auto-Versioning Stuff */
-
- if (resource->type != DAV_RESOURCE_TYPE_VERSION)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can only be performed on a "
- "version resource [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (create_activity)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKOUT can not create an activity at "
- "this time. Use MKACTIVITY first.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (is_unreserved)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "Unreserved checkouts are not yet "
- "available. A version history may not be "
- "checked out more than once, into a "
- "specific activity.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- if (activities == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCOMPLETE_DATA,
- "An activity must be provided for "
- "checkout.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
- /* assert: nelts > 0. the below check effectively means > 1. */
- if (activities->nelts != 1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "Only one activity may be specified within "
- "the CHECKOUT.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- serr = dav_svn_simple_parse_uri(&parse, resource,
- APR_ARRAY_IDX(activities, 0, const char *),
- resource->pool);
- if (serr != NULL)
- {
- /* ### is BAD_REQUEST proper? */
- return dav_svn_convert_err(serr, HTTP_CONFLICT,
- "The activity href could not be parsed "
- "properly.",
- resource->pool);
- }
- if (parse.activity_id == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_INCORRECT_PARAMS,
- "The provided href is not an activity URI.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- if ((txn_name = dav_svn_get_txn(resource->info->repos,
- parse.activity_id)) == NULL)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_ACTIVITY_NOT_FOUND,
- "The specified activity does not exist.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* verify the specified version resource is the "latest", thus allowing
- changes to be made. */
- if (resource->baselined || resource->info->root.rev == SVN_INVALID_REVNUM)
- {
- /* a Baseline, or a standard Version Resource which was accessed
- via a Label against a VCR within a Baseline Collection. */
- /* ### at the moment, this branch is only reached for baselines */
-
- svn_revnum_t youngest;
-
- /* make sure the baseline being checked out is the latest */
- serr = svn_fs_youngest_rev(&youngest, resource->info->repos->fs,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not determine the youngest "
- "revision for verification against "
- "the baseline being checked out.",
- resource->pool);
- }
-
- if (resource->info->root.rev != youngest)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_APMOD_BAD_BASELINE,
- "The specified baseline is not the "
- "latest baseline, so it may not be "
- "checked out.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* ### hmm. what if the transaction root's revision is different
- ### from this baseline? i.e. somebody created a new revision while
- ### we are processing this commit.
- ###
- ### first question: what does the client *do* with a working
- ### baseline? knowing that, and how it maps to our backend, then
- ### we can figure out what to do here. */
- }
- else
- {
- /* standard Version Resource */
-
- svn_fs_txn_t *txn;
- svn_fs_root_t *txn_root;
- svn_revnum_t txn_created_rev;
- dav_error *err;
-
- /* open the specified transaction so that we can verify this version
- resource corresponds to the current/latest in the transaction. */
- if ((err = open_txn(&txn, resource->info->repos->fs, txn_name,
- resource->pool)) != NULL)
- return err;
-
- serr = svn_fs_txn_root(&txn_root, txn, resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not open the transaction tree.",
- resource->pool);
- }
-
- /* assert: repos_path != NULL (for this type of resource) */
-
-
- /* Out-of-dateness check: compare the created-rev of the item
- in the txn against the created-rev of the version resource
- being changed. */
- serr = svn_fs_node_created_rev(&txn_created_rev,
- txn_root, resource->info->repos_path,
- resource->pool);
- if (serr != NULL)
- {
- /* ### correct HTTP error? */
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Could not get created-rev of "
- "transaction node.",
- resource->pool);
- }
-
- /* If txn_created_rev is invalid, that means it's already
- mutable in the txn... which means it has already passed this
- out-of-dateness check. (Usually, this happens when looking
- at a parent directory of an already checked-out
- resource.)
-
- Now, we come down to it. If the created revision of the node
- in the transaction is different from the revision parsed from
- the version resource URL, we're in a bit of a quandry, and
- one of a few things could be true.
-
- - The client is trying to modify an old (out of date)
- revision of the resource. This is, of course,
- unacceptable!
-
- - The client is trying to modify a *newer* revision. If the
- version resource is *newer* than the transaction root, then
- the client started a commit, a new revision was created
- within the repository, the client fetched the new resource
- from that new revision, changed it (or merged in a prior
- change), and then attempted to incorporate that into the
- commit that was initially started. We could copy that new
- node into our transaction and then modify it, but why
- bother? We can stop the commit, and everything will be
- fine again if the user simply restarts it (because we'll
- use that new revision as the transaction root, thus
- incorporating the new resource, which they will then
- modify).
-
- - The path/revision that client is wishing to edit and the
- path/revision in the current transaction are actually the
- same node, and thus this created-rev comparison didn't
- really solidify anything after all. :-)
- */
-
- if (SVN_IS_VALID_REVNUM( txn_created_rev ))
- {
- int errorful = 0;
-
- if (resource->info->root.rev < txn_created_rev)
- {
- /* The item being modified is older than the one in the
- transaction. The client is out of date. */
- errorful = 1;
- }
- else if (resource->info->root.rev > txn_created_rev)
- {
- /* The item being modified is being accessed via a newer
- revision than the one in the transaction. We'll
- check to see if they are still the same node, and if
- not, return an error. */
- const svn_fs_id_t *url_noderev_id, *txn_noderev_id;
-
- if ((serr = svn_fs_node_id(&txn_noderev_id, txn_root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the transaction.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if ((serr = svn_fs_node_id(&url_noderev_id,
- resource->info->root.root,
- resource->info->repos_path,
- resource->pool)))
- {
- err = dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, serr->apr_err,
- "Unable to fetch the node revision id of the version "
- "resource within the revision.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- svn_error_clear(serr);
- return err;
- }
- if (svn_fs_compare_ids(url_noderev_id, txn_noderev_id) != 0)
- {
- errorful = 1;
- }
- }
- if (errorful)
- {
-#if 1
- return dav_svn__new_error_tag
- (resource->pool, HTTP_CONFLICT, SVN_ERR_FS_CONFLICT,
- "The version resource does not correspond to the resource "
- "within the transaction. Either the requested version "
- "resource is out of date (needs to be updated), or the "
- "requested version resource is newer than the transaction "
- "root (restart the commit).",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
-#else
- /* ### some debugging code */
- const char *msg;
-
- msg = apr_psprintf(resource->pool,
- "created-rev mismatch: r=%ld, t=%ld",
- resource->info->root.rev, txn_created_rev);
-
- return dav_svn__new_error_tag(resource->pool, HTTP_CONFLICT,
- SVN_ERR_FS_CONFLICT, msg,
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-#endif
- }
- }
- }
- *working_resource = dav_svn_create_working_resource(resource,
- parse.activity_id,
- txn_name,
- FALSE);
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_uncheckout(dav_resource *resource)
-{
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "UNCHECKOUT called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* Try to abort the txn if it exists; but don't try too hard. :-) */
- if (resource->info->root.txn)
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- /* Attempt to destroy the shared activity. */
- if (resource->info->root.activity_id)
- {
- dav_svn_delete_activity(resource->info->repos,
- resource->info->root.activity_id);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
- }
-
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* We're no longer checked out. */
- resource->info->auto_checked_out = FALSE;
-
- /* Convert the working resource back into a regular one, in-place. */
- return dav_svn_working_to_regular_resource(resource);
-}
-
-
-/* Closure object for cleanup_deltify. */
-struct cleanup_deltify_baton
-{
- /* The repository in which to deltify. We use a path instead of an
- object, because it's difficult to obtain a repos or fs object
- with the right lifetime guarantees. */
- const char *repos_path;
-
- /* The revision number against which to deltify. */
- svn_revnum_t revision;
-
- /* The pool to use for all temporary allocation while working. This
- may or may not be the same as the pool on which the cleanup is
- registered, but obviously it must have a lifetime at least as
- long as that pool. */
- apr_pool_t *pool;
-};
-
-
-/* APR pool cleanup function to deltify against a just-committed
- revision. DATA is a 'struct cleanup_deltify_baton *'.
-
- If any errors occur, log them in the httpd server error log, but
- return APR_SUCCESS no matter what, as this is a pool cleanup
- function and deltification is not a matter of correctness
- anyway. */
-static apr_status_t
-cleanup_deltify(void *data)
-{
- struct cleanup_deltify_baton *cdb = data;
- svn_repos_t *repos;
- svn_error_t *err;
-
- /* It's okay to allocate in the pool that's being cleaned up, and
- it's also okay to register new cleanups against that pool. But
- if you create subpools of it, you must make sure to destroy them
- at the end of the cleanup. So we do all our work in this
- subpool, then destroy it before exiting. */
- apr_pool_t *subpool = svn_pool_create(cdb->pool);
-
- err = svn_repos_open(&repos, cdb->repos_path, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error opening repository '%s'",
- cdb->repos_path);
- svn_error_clear(err);
- goto cleanup;
- }
-
- err = svn_fs_deltify_revision(svn_repos_fs(repos),
- cdb->revision, subpool);
- if (err)
- {
- ap_log_perror(APLOG_MARK, APLOG_ERR, err->apr_err, cdb->pool,
- "cleanup_deltify: error deltifying against revision %ld"
- " in repository '%s'",
- cdb->revision, cdb->repos_path);
- svn_error_clear(err);
- }
-
- cleanup:
- svn_pool_destroy(subpool);
-
- return APR_SUCCESS;
-}
-
-
-/* Register the cleanup_deltify function on POOL, which should be the
- connection pool for the request. This way the time needed for
- deltification won't delay the response to the client.
-
- REPOS is the repository in which deltify, and REVISION is the
- revision against which to deltify. POOL is both the pool on which
- to register the cleanup function and the pool that will be used for
- temporary allocations while deltifying. */
-static void
-register_deltification_cleanup(svn_repos_t *repos,
- svn_revnum_t revision,
- apr_pool_t *pool)
-{
- struct cleanup_deltify_baton *cdb = apr_palloc(pool, sizeof(*cdb));
-
- cdb->repos_path = svn_repos_path(repos, pool);
- cdb->revision = revision;
- cdb->pool = pool;
-
- apr_pool_cleanup_register(pool, cdb, cleanup_deltify, apr_pool_cleanup_null);
-}
-
-
-dav_error *
-dav_svn_checkin(dav_resource *resource,
- int keep_checked_out,
- dav_resource **version_resource)
-{
- svn_error_t *serr;
- dav_error *err;
- apr_status_t apr_err;
- const char *uri;
- const char *shared_activity;
- void *data;
-
- /* ### mod_dav has a flawed architecture, in the sense that it first
- tries to auto-checkin the modified resource, then attempts to
- auto-checkin the parent resource (if the parent resource was
- auto-checked-out). Instead, the provider should be in charge:
- mod_dav should provide a *set* of resources that need
- auto-checkin, and the provider can decide how to do it. (One
- txn? Many txns? Etc.) */
-
- if (resource->type != DAV_RESOURCE_TYPE_WORKING)
- return dav_svn__new_error_tag(resource->pool, HTTP_INTERNAL_SERVER_ERROR,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "CHECKIN called on non-working resource.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- /* If the global autoversioning activity still exists, that means
- nobody's committed it yet. */
- apr_err = apr_pool_userdata_get(&data,
- DAV_SVN_AUTOVERSIONING_ACTIVITY,
- resource->info->r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- resource->pool);
- shared_activity = data;
-
- /* Try to commit the txn if it exists. */
- if (shared_activity
- && (strcmp(shared_activity, resource->info->root.activity_id) == 0))
- {
- const char *shared_txn_name;
- const char *conflict_msg;
- svn_revnum_t new_rev;
-
- shared_txn_name = dav_svn_get_txn(resource->info->repos,
- shared_activity);
- if (! shared_txn_name)
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Cannot look up a txn_name by activity");
-
- /* Sanity checks */
- if (resource->info->root.txn_name
- && (strcmp(shared_txn_name, resource->info->root.txn_name) != 0))
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Internal txn_name doesn't match"
- " autoversioning transaction.");
-
- if (! resource->info->root.txn)
- /* should already be open by dav_svn_checkout */
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Autoversioning txn isn't open "
- "when it should be.");
-
- err = set_auto_revprops(resource);
- if (err)
- return err;
-
- serr = svn_repos_fs_commit_txn(&conflict_msg,
- resource->info->repos->repos,
- &new_rev,
- resource->info->root.txn,
- resource->pool);
-
- if (serr != NULL)
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(resource->info->root.txn,
- resource->pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- msg = apr_psprintf(resource->pool,
- "A conflict occurred during the CHECKIN "
- "processing. The problem occurred with "
- "the \"%s\" resource.",
- conflict_msg);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg,
- resource->pool);
- }
-
- /* Attempt to destroy the shared activity. */
- dav_svn_delete_activity(resource->info->repos, shared_activity);
- apr_pool_userdata_set(NULL, DAV_SVN_AUTOVERSIONING_ACTIVITY,
- NULL, resource->info->r->pool);
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(resource->info->repos->repos,
- new_rev,
- resource->info->r->connection->pool);
-
- /* If caller wants it, return the new VR that was created by
- the checkin. */
- if (version_resource)
- {
- uri = dav_svn_build_uri(resource->info->repos,
- DAV_SVN_BUILD_URI_VERSION,
- new_rev, resource->info->repos_path,
- 0, resource->pool);
-
- err = dav_svn_create_version_resource(version_resource, uri,
- resource->pool);
- if (err)
- return err;
- }
- } /* end of commit stuff */
-
- /* The shared activity was either nonexistent to begin with, or it's
- been committed and is only now nonexistent. The resource needs
- to forget about it. */
- resource->info->root.txn_name = NULL;
- resource->info->root.txn = NULL;
-
- /* Convert the working resource back into an regular one. */
- if (! keep_checked_out)
- {
- resource->info->auto_checked_out = FALSE;
- return dav_svn_working_to_regular_resource(resource);
- }
-
- return NULL;
-}
-
-
-static dav_error *
-dav_svn_avail_reports(const dav_resource *resource,
- const dav_report_elem **reports)
-{
- /* ### further restrict to the public space? */
- if (resource->type != DAV_RESOURCE_TYPE_REGULAR) {
- *reports = NULL;
- return NULL;
- }
-
- *reports = avail_reports;
- return NULL;
-}
-
-
-static int
-dav_svn_report_label_header_allowed(const apr_xml_doc *doc)
-{
- return 0;
-}
-
-
-/* Respond to a S:dated-rev-report request. The request contains a
- * DAV:creationdate element giving the requested date; the response
- * contains a DAV:version-name element giving the most recent revision
- * as of that date. */
-static dav_error *
-dav_svn__drev_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_xml_elem *child;
- int ns;
- apr_time_t tm = (apr_time_t) -1;
- svn_revnum_t rev;
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- dav_error *derr = NULL;
-
- /* Find the DAV:creationdate element and get the requested time from it. */
- ns = dav_svn_find_ns(doc->namespaces, "DAV:");
- if (ns != -1)
- {
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- if (child->ns != ns || strcmp(child->name, "creationdate") != 0)
- continue;
- /* If this fails, we'll notice below, so ignore any error for now. */
- svn_error_clear
- (svn_time_from_cstring(&tm, dav_xml_get_cdata(child,
- resource->pool, 1),
- resource->pool));
- }
- }
-
- if (tm == (apr_time_t) -1)
- {
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain a valid "
- "'DAV:creationdate' element.");
- }
-
- /* Do the actual work of finding the revision by date. */
- if ((err = svn_repos_dated_revision(&rev, resource->info->repos->repos, tm,
- resource->pool)) != SVN_NO_ERROR)
- {
- svn_error_clear(err);
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Could not access revision times.");
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR
- "<D:version-name>%ld</D:version-name>"
- "</S:dated-rev-report>", rev);
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
+#include "../dav_svn.h"
/* Respond to a get-locks-report request. See description of this
report in libsvn_ra_dav/fetch.c. */
-static dav_error *
+dav_error *
dav_svn__get_locks_report(const dav_resource *resource,
const apr_xml_doc *doc,
ap_filter_t *output)
@@ -1207,617 +212,3 @@
return NULL;
}
-
-
-static apr_status_t
-send_get_locations_report(ap_filter_t *output,
- apr_bucket_brigade *bb,
- const dav_resource *resource,
- apr_hash_t *fs_locations)
-{
- apr_hash_index_t *hi;
- apr_pool_t *pool;
- apr_status_t apr_err;
-
- pool = resource->pool;
-
- apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR
- "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE
- "\" xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return apr_err;
-
- for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *value;
- const char *path_quoted;
-
- apr_hash_this(hi, &key, NULL, &value);
- path_quoted = apr_xml_quote_string(pool, value, 1);
- apr_err = ap_fprintf(output, bb, "<S:location "
- "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR,
- *(const svn_revnum_t *)key, path_quoted);
- if (apr_err)
- return apr_err;
- }
- return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR);
-}
-
-
-dav_error *
-dav_svn__get_locations_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- svn_error_t *serr;
- dav_error *derr = NULL;
- apr_status_t apr_err;
- apr_bucket_brigade *bb;
- dav_svn__authz_read_baton arb;
-
- /* The parameters to do the operation on. */
- const char *relative_path = NULL;
- const char *abs_path;
- svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
- apr_array_header_t *location_revisions;
-
- /* XML Parsing Variables */
- int ns;
- apr_xml_elem *child;
-
- apr_hash_t *fs_locations;
-
- location_revisions = apr_array_make(resource->pool, 0,
- sizeof(svn_revnum_t));
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain the 'svn:' "
- "namespace, so it is not going to have "
- "certain required elements.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Gather the parameters. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* If this element isn't one of ours, then skip it. */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "peg-revision") == 0)
- peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
- resource->pool, 1));
- else if (strcmp(child->name, "location-revision") == 0)
- {
- svn_revnum_t revision
- = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
- APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
- }
- else if (strcmp(child->name, "path") == 0)
- {
- relative_path = dav_xml_get_cdata(child, resource->pool, 0);
- if ((derr = dav_svn__test_canonical(relative_path, resource->pool)))
- return derr;
- }
- }
-
- /* Now we should have the parameters ready - let's
- check if they are all present. */
- if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision)))
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "Not all parameters passed.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Append the relative path to the base FS path to get an absolute
- repository path. */
- abs_path = svn_path_join(resource->info->repos_path, relative_path,
- resource->pool);
-
- /* Build an authz read baton */
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- serr = svn_repos_trace_node_locations(resource->info->repos->fs,
- &fs_locations, abs_path, peg_revision,
- location_revisions,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool);
-
- if (serr)
- {
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- serr->message, resource->pool);
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- apr_err = send_get_locations_report(output, bb, resource, fs_locations);
-
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
-
-
-static dav_error *
-dav_svn_deliver_report(request_rec *r,
- const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- int ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
-
- if (doc->root->ns == ns)
- {
- /* ### note that these report names should have symbols... */
-
- if (strcmp(doc->root->name, "update-report") == 0)
- {
- return dav_svn__update_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "log-report") == 0)
- {
- return dav_svn__log_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "dated-rev-report") == 0)
- {
- return dav_svn__drev_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locations") == 0)
- {
- return dav_svn__get_locations_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "file-revs-report") == 0)
- {
- return dav_svn__file_revs_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "get-locks-report") == 0)
- {
- return dav_svn__get_locks_report(resource, doc, output);
- }
- else if (strcmp(doc->root->name, "replay-report") == 0)
- {
- return dav_svn__replay_report(resource, doc, output);
- }
-
- /* NOTE: if you add a report, don't forget to add it to the
- * avail_reports[] array at the top of this file.
- */
- }
-
- /* ### what is a good error for an unknown report? */
- return dav_svn__new_error_tag(resource->pool, HTTP_NOT_IMPLEMENTED,
- SVN_ERR_UNSUPPORTED_FEATURE,
- "The requested report is unknown.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-}
-
-
-static int
-dav_svn_can_be_activity(const dav_resource *resource)
-{
- /* If our resource is marked as auto_checked_out'd, then we allow this to
- * be an activity URL. Otherwise, it must be a real activity URL that
- * doesn't already exist.
- */
- return (resource->info->auto_checked_out == TRUE ||
- (resource->type == DAV_RESOURCE_TYPE_ACTIVITY &&
- !resource->exists));
-}
-
-
-static dav_error *
-dav_svn_make_activity(dav_resource *resource)
-{
- const char *activity_id = resource->info->root.activity_id;
- const char *txn_name;
- dav_error *err;
-
- /* sanity check: make sure the resource is a valid activity, in
- case an older mod_dav doesn't do the check for us. */
- if (! dav_svn_can_be_activity(resource))
- return dav_svn__new_error_tag(resource->pool, HTTP_FORBIDDEN,
- SVN_ERR_APMOD_MALFORMED_URI,
- "Activities cannot be created at that "
- "location; query the "
- "DAV:activity-collection-set property.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
-
- err = dav_svn_create_activity(resource->info->repos, &txn_name,
- resource->pool);
- if (err != NULL)
- return err;
-
- err = dav_svn_store_activity(resource->info->repos, activity_id, txn_name);
- if (err != NULL)
- return err;
-
- /* everything is happy. update the resource */
- resource->info->root.txn_name = txn_name;
- resource->exists = 1;
- return NULL;
-}
-
-
-dav_error *
-dav_svn__build_lock_hash(apr_hash_t **locks,
- request_rec *r,
- const char *path_prefix,
- apr_pool_t *pool)
-{
- apr_status_t apr_err;
- dav_error *derr;
- void *data = NULL;
- apr_xml_doc *doc = NULL;
- apr_xml_elem *child, *lockchild;
- int ns;
- apr_hash_t *hash = apr_hash_make(pool);
-
- /* Grab the request body out of r->pool, as it contains all of the
- lock tokens. It should have been stashed already by our custom
- input filter. */
- apr_err = apr_pool_userdata_get(&data, "svn-request-body", r->pool);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error fetching pool userdata.",
- pool);
- doc = data;
- if (! doc)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- /* If there's no svn: namespace in the body, then there are
- definitely no lock-tokens to harvest. This is likely a
- request from an old client. */
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- if ((doc->root->ns == ns)
- && (strcmp(doc->root->name, "lock-token-list") == 0))
- {
- child = doc->root;
- }
- else
- {
- /* Search doc's children until we find the <lock-token-list>. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* if this element isn't one of ours, then skip it */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "lock-token-list") == 0)
- break;
- }
- }
-
- /* Did we find what we were looking for? */
- if (! child)
- {
- *locks = hash;
- return SVN_NO_ERROR;
- }
-
- /* Then look for N different <lock> structures within. */
- for (lockchild = child->first_child; lockchild != NULL;
- lockchild = lockchild->next)
- {
- const char *lockpath = NULL, *locktoken = NULL;
- apr_xml_elem *lfchild;
-
- if (strcmp(lockchild->name, "lock") != 0)
- continue;
-
- for (lfchild = lockchild->first_child; lfchild != NULL;
- lfchild = lfchild->next)
- {
- if (strcmp(lfchild->name, "lock-path") == 0)
- {
- const char *cdata = dav_xml_get_cdata(lfchild, pool, 0);
- if ((derr = dav_svn__test_canonical(cdata, pool)))
- return derr;
-
- /* Create an absolute fs-path */
- lockpath = svn_path_join(path_prefix, cdata, pool);
- if (lockpath && locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- else if (strcmp(lfchild->name, "lock-token") == 0)
- {
- locktoken = dav_xml_get_cdata(lfchild, pool, 1);
- if (lockpath && *locktoken)
- {
- apr_hash_set(hash, lockpath, APR_HASH_KEY_STRING, locktoken);
- lockpath = NULL;
- locktoken = NULL;
- }
- }
- }
- }
-
- *locks = hash;
- return SVN_NO_ERROR;
-}
-
-
-dav_error *
-dav_svn__push_locks(dav_resource *resource,
- apr_hash_t *locks,
- apr_pool_t *pool)
-{
- svn_fs_access_t *fsaccess;
- apr_hash_index_t *hi;
- svn_error_t *serr;
-
- serr = svn_fs_get_access(&fsaccess, resource->info->repos->fs);
- if (serr)
- {
- /* If an authenticated user name was attached to the request,
- then dav_svn_get_resource() should have already noticed and
- created an fs_access_t in the filesystem. */
- return dav_svn__sanitize_error(serr, "Lock token(s) in request, but "
- "missing an user name", HTTP_BAD_REQUEST,
- resource->info->r);
- }
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- const char *token;
- void *val;
- apr_hash_this(hi, NULL, NULL, &val);
- token = val;
-
- serr = svn_fs_access_add_lock_token(fsaccess, token);
- if (serr)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error pushing token into filesystem.",
- pool);
- }
-
- return NULL;
-}
-
-
-/* Helper for dav_svn_merge(). Free every lock in LOCKS. The locks
- live in REPOS. Log any errors for REQUEST. Use POOL for temporary
- work.*/
-static svn_error_t *
-release_locks(apr_hash_t *locks,
- svn_repos_t *repos,
- request_rec *r,
- apr_pool_t *pool)
-{
- apr_hash_index_t *hi;
- const void *key;
- void *val;
- apr_pool_t *subpool = svn_pool_create(pool);
- svn_error_t *err;
-
- for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
- {
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
-
- /* The lock may be stolen or broken sometime between
- svn_fs_commit_txn() and this post-commit cleanup. So ignore
- any errors from this command; just free as many locks as we can. */
- err = svn_repos_fs_unlock(repos, key, val, FALSE, subpool);
-
- if (err) /* If we got an error, just log it and move along. */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, err->apr_err, r,
- "%s", err->message);
-
- svn_error_clear(err);
- }
-
- svn_pool_destroy(subpool);
-
- return SVN_NO_ERROR;
-}
-
-
-static dav_error *
-dav_svn_merge(dav_resource *target,
- dav_resource *source,
- int no_auto_merge,
- int no_checkout,
- apr_xml_elem *prop_elem,
- ap_filter_t *output)
-{
- apr_pool_t *pool;
- dav_error *err;
- svn_fs_txn_t *txn;
- const char *conflict;
- svn_error_t *serr;
- char *post_commit_err = NULL;
- svn_revnum_t new_rev;
- apr_hash_t *locks;
- svn_boolean_t disable_merge_response = FALSE;
-
- /* We'll use the target's pool for our operation. We happen to know that
- it matches the request pool, which (should) have the proper lifetime. */
- pool = target->pool;
-
- /* ### what to verify on the target? */
-
- /* ### anything else for the source? */
- if (source->type != DAV_RESOURCE_TYPE_ACTIVITY)
- {
- return dav_svn__new_error_tag(pool, HTTP_METHOD_NOT_ALLOWED,
- SVN_ERR_INCORRECT_PARAMS,
- "MERGE can only be performed using an "
- "activity as the source [at this time].",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Before attempting the final commit, we need to push any incoming
- lock-tokens into the filesystem's access_t. Normally they come
- in via 'If:' header, and dav_svn_get_resource() automatically
- notices them and does this work for us. In the case of MERGE,
- however, svn clients are sending them in the request body. */
-
- err = dav_svn__build_lock_hash(&locks, target->info->r,
- target->info->repos_path,
- pool);
- if (err != NULL)
- return err;
-
- if (apr_hash_count(locks))
- {
- err = dav_svn__push_locks(source, locks, pool);
- if (err != NULL)
- return err;
- }
-
- /* We will ignore no_auto_merge and no_checkout. We can't do those, but the
- client has no way to assert that we *should* do them. This should be fine
- because, presumably, the client has no way to do the various checkouts
- and things that would necessitate an auto-merge or checkout during the
- MERGE processing. */
-
- /* open the transaction that we're going to commit. */
- if ((err = open_txn(&txn, source->info->repos->fs,
- source->info->root.txn_name, pool)) != NULL)
- return err;
-
- /* all righty... commit the bugger. */
- serr = svn_repos_fs_commit_txn(&conflict, source->info->repos->repos,
- &new_rev, txn, pool);
-
- /* If the error was just a post-commit hook failure, we ignore it.
- Otherwise, we deal with it.
- ### TODO: Figure out if the MERGE response can grow a means by
- which to marshal back both the success of the commit (and its
- commit info) and the failure of the post-commit hook. */
- if (serr && (serr->apr_err != SVN_ERR_REPOS_POST_COMMIT_HOOK_FAILED))
- {
- const char *msg;
- svn_error_clear(svn_fs_abort_txn(txn, pool));
-
- if (serr->apr_err == SVN_ERR_FS_CONFLICT)
- {
- /* ### we need to convert the conflict path into a URI */
- msg = apr_psprintf(pool,
- "A conflict occurred during the MERGE "
- "processing. The problem occurred with the "
- "\"%s\" resource.",
- conflict);
- }
- else
- msg = "An error occurred while committing the transaction.";
-
- return dav_svn_convert_err(serr, HTTP_CONFLICT, msg, pool);
- }
- else if (serr)
- {
- if (serr->child && serr->child->message)
- post_commit_err = apr_pstrdup(pool, serr->child->message);
- svn_error_clear(serr);
- }
-
- /* Commit was successful, so schedule deltification. */
- register_deltification_cleanup(source->info->repos->repos, new_rev,
- source->info->r->connection->pool);
-
- /* We've detected a 'high level' svn action to log. */
- apr_table_set(target->info->r->subprocess_env, "SVN-ACTION",
- apr_psprintf(target->info->r->pool,
- "commit r%" SVN_REVNUM_T_FMT, new_rev));
-
- /* Since the commit was successful, the txn ID is no longer valid.
- Store an empty txn ID in the activity database so that when the
- client deletes the activity, we don't try to open and abort the
- transaction. */
- err = dav_svn_store_activity(source->info->repos,
- source->info->root.activity_id, "");
- if (err != NULL)
- return err;
-
- /* Check the dav_resource->info area for information about the
- special X-SVN-Options: header that may have come in the http
- request. */
- if (source->info->svn_client_options != NULL)
- {
- /* The client might want us to release all locks sent in the
- MERGE request. */
- if ((NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_RELEASE_LOCKS)))
- && apr_hash_count(locks))
- {
- serr = release_locks(locks, source->info->repos->repos,
- source->info->r, pool);
- if (serr != NULL)
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- "Error releasing locks", pool);
- }
-
- /* The client might want us to disable the merge response altogether. */
- if (NULL != (ap_strstr_c(source->info->svn_client_options,
- SVN_DAV_OPTION_NO_MERGE_RESPONSE)))
- disable_merge_response = TRUE;
- }
-
- /* process the response for the new revision. */
- return dav_svn__merge_response(output, source->info->repos, new_rev,
- post_commit_err, prop_elem,
- disable_merge_response, pool);
-}
-
-
-const dav_hooks_vsn dav_svn_hooks_vsn = {
- dav_svn_get_vsn_options,
- dav_svn_get_option,
- dav_svn_versionable,
- dav_svn_auto_versionable,
- dav_svn_vsn_control,
- dav_svn_checkout,
- dav_svn_uncheckout,
- dav_svn_checkin,
- dav_svn_avail_reports,
- dav_svn_report_label_header_allowed,
- dav_svn_deliver_report,
- NULL, /* update */
- NULL, /* add_label */
- NULL, /* remove_label */
- NULL, /* can_be_workspace */
- NULL, /* make_workspace */
- dav_svn_can_be_activity,
- dav_svn_make_activity,
- dav_svn_merge,
-};
Modified: trunk/subversion/mod_dav_svn/version.c
URL: http://svn.collab.net/viewvc/svn/trunk/subversion/mod_dav_svn/version.c?pathrev=20907&r1=20906&r2=20907
==============================================================================
--- trunk/subversion/mod_dav_svn/version.c (original)
+++ trunk/subversion/mod_dav_svn/version.c Sun Jul 30 05:19:49 2006
@@ -956,410 +956,6 @@
}
-/* Respond to a S:dated-rev-report request. The request contains a
- * DAV:creationdate element giving the requested date; the response
- * contains a DAV:version-name element giving the most recent revision
- * as of that date. */
-static dav_error *
-dav_svn__drev_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_xml_elem *child;
- int ns;
- apr_time_t tm = (apr_time_t) -1;
- svn_revnum_t rev;
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- dav_error *derr = NULL;
-
- /* Find the DAV:creationdate element and get the requested time from it. */
- ns = dav_svn_find_ns(doc->namespaces, "DAV:");
- if (ns != -1)
- {
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- if (child->ns != ns || strcmp(child->name, "creationdate") != 0)
- continue;
- /* If this fails, we'll notice below, so ignore any error for now. */
- svn_error_clear
- (svn_time_from_cstring(&tm, dav_xml_get_cdata(child,
- resource->pool, 1),
- resource->pool));
- }
- }
-
- if (tm == (apr_time_t) -1)
- {
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain a valid "
- "'DAV:creationdate' element.");
- }
-
- /* Do the actual work of finding the revision by date. */
- if ((err = svn_repos_dated_revision(&rev, resource->info->repos->repos, tm,
- resource->pool)) != SVN_NO_ERROR)
- {
- svn_error_clear(err);
- return dav_new_error(resource->pool, HTTP_INTERNAL_SERVER_ERROR, 0,
- "Could not access revision times.");
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:dated-rev-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR
- "<D:version-name>%ld</D:version-name>"
- "</S:dated-rev-report>", rev);
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
-
-
-/* Respond to a get-locks-report request. See description of this
- report in libsvn_ra_dav/fetch.c. */
-static dav_error *
-dav_svn__get_locks_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- apr_bucket_brigade *bb;
- svn_error_t *err;
- apr_status_t apr_err;
- apr_hash_t *locks;
- dav_svn__authz_read_baton arb;
- apr_hash_index_t *hi;
- apr_pool_t *subpool;
-
- /* The request URI should be a public one representing an fs path. */
- if ((! resource->info->repos_path)
- || (! resource->info->repos->repos))
- return dav_new_error(resource->pool, HTTP_BAD_REQUEST, 0,
- "get-locks-report run on resource which doesn't "
- "represent a path within a repository.");
-
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- /* Fetch the locks, but allow authz_read checks to happen on each. */
- if ((err = svn_repos_fs_get_locks(&locks,
- resource->info->repos->repos,
- resource->info->repos_path,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool)) != SVN_NO_ERROR)
- return dav_svn_convert_err(err, HTTP_INTERNAL_SERVER_ERROR,
- err->message, resource->pool);
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- /* start sending report */
- apr_err = ap_fprintf(output, bb,
- DAV_XML_HEADER DEBUG_CR
- "<S:get-locks-report xmlns:S=\"" SVN_XML_NAMESPACE "\" "
- "xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* stream the locks */
- subpool = svn_pool_create(resource->pool);
- for (hi = apr_hash_first(resource->pool, locks); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *val;
- const svn_lock_t *lock;
- const char *path_quoted, *token_quoted;
- const char *creation_str, *expiration_str;
- const char *owner_to_send, *comment_to_send;
- svn_boolean_t owner_base64 = FALSE, comment_base64 = FALSE;
-
- svn_pool_clear(subpool);
- apr_hash_this(hi, &key, NULL, &val);
- lock = val;
-
- path_quoted = apr_xml_quote_string(subpool, lock->path, 1);
- token_quoted = apr_xml_quote_string(subpool, lock->token, 1);
- creation_str = svn_time_to_cstring(lock->creation_date, subpool);
-
- apr_err = ap_fprintf(output, bb,
- "<S:lock>" DEBUG_CR
- "<S:path>%s</S:path>" DEBUG_CR
- "<S:token>%s</S:token>" DEBUG_CR
- "<S:creationdate>%s</S:creationdate>" DEBUG_CR,
- path_quoted, token_quoted, creation_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->expiration_date)
- {
- expiration_str = svn_time_to_cstring(lock->expiration_date, subpool);
- apr_err = ap_fprintf(output, bb,
- "<S:expirationdate>%s</S:expirationdate>"
- DEBUG_CR, expiration_str);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner)))
- {
- owner_to_send = apr_xml_quote_string(subpool, lock->owner, 1);
- }
- else
- {
- svn_string_t owner_string;
- const svn_string_t *encoded_owner;
-
- owner_string.data = lock->owner;
- owner_string.len = strlen(lock->owner);
- encoded_owner = svn_base64_encode_string(&owner_string, subpool);
- owner_to_send = encoded_owner->data;
- owner_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:owner %s>%s</S:owner>" DEBUG_CR,
- owner_base64 ? "encoding=\"base64\"" : "",
- owner_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- if (lock->comment)
- {
- if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment)))
- {
- comment_to_send = apr_xml_quote_string(subpool,
- lock->comment, 1);
- }
- else
- {
- svn_string_t comment_string;
- const svn_string_t *encoded_comment;
-
- comment_string.data = lock->comment;
- comment_string.len = strlen(lock->comment);
- encoded_comment = svn_base64_encode_string(&comment_string,
- subpool);
- comment_to_send = encoded_comment->data;
- comment_base64 = TRUE;
- }
-
- apr_err = ap_fprintf(output, bb,
- "<S:comment %s>%s</S:comment>" DEBUG_CR,
- comment_base64 ? "encoding=\"base64\"" : "",
- comment_to_send);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- }
-
- apr_err = ap_fprintf(output, bb, "</S:lock>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
- } /* end of hash loop */
- svn_pool_destroy(subpool);
-
- /* finish the report */
- apr_err = ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR);
- if (apr_err)
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if ((apr_err = ap_fflush(output, bb)))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return NULL;
-}
-
-
-static apr_status_t
-send_get_locations_report(ap_filter_t *output,
- apr_bucket_brigade *bb,
- const dav_resource *resource,
- apr_hash_t *fs_locations)
-{
- apr_hash_index_t *hi;
- apr_pool_t *pool;
- apr_status_t apr_err;
-
- pool = resource->pool;
-
- apr_err = ap_fprintf(output, bb, DAV_XML_HEADER DEBUG_CR
- "<S:get-locations-report xmlns:S=\"" SVN_XML_NAMESPACE
- "\" xmlns:D=\"DAV:\">" DEBUG_CR);
- if (apr_err)
- return apr_err;
-
- for (hi = apr_hash_first(pool, fs_locations); hi; hi = apr_hash_next(hi))
- {
- const void *key;
- void *value;
- const char *path_quoted;
-
- apr_hash_this(hi, &key, NULL, &value);
- path_quoted = apr_xml_quote_string(pool, value, 1);
- apr_err = ap_fprintf(output, bb, "<S:location "
- "rev=\"%ld\" path=\"%s\"/>" DEBUG_CR,
- *(const svn_revnum_t *)key, path_quoted);
- if (apr_err)
- return apr_err;
- }
- return ap_fprintf(output, bb, "</S:get-locations-report>" DEBUG_CR);
-}
-
-
-dav_error *
-dav_svn__get_locations_report(const dav_resource *resource,
- const apr_xml_doc *doc,
- ap_filter_t *output)
-{
- svn_error_t *serr;
- dav_error *derr = NULL;
- apr_status_t apr_err;
- apr_bucket_brigade *bb;
- dav_svn__authz_read_baton arb;
-
- /* The parameters to do the operation on. */
- const char *relative_path = NULL;
- const char *abs_path;
- svn_revnum_t peg_revision = SVN_INVALID_REVNUM;
- apr_array_header_t *location_revisions;
-
- /* XML Parsing Variables */
- int ns;
- apr_xml_elem *child;
-
- apr_hash_t *fs_locations;
-
- location_revisions = apr_array_make(resource->pool, 0,
- sizeof(svn_revnum_t));
-
- /* Sanity check. */
- ns = dav_svn_find_ns(doc->namespaces, SVN_XML_NAMESPACE);
- if (ns == -1)
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "The request does not contain the 'svn:' "
- "namespace, so it is not going to have "
- "certain required elements.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Gather the parameters. */
- for (child = doc->root->first_child; child != NULL; child = child->next)
- {
- /* If this element isn't one of ours, then skip it. */
- if (child->ns != ns)
- continue;
-
- if (strcmp(child->name, "peg-revision") == 0)
- peg_revision = SVN_STR_TO_REV(dav_xml_get_cdata(child,
- resource->pool, 1));
- else if (strcmp(child->name, "location-revision") == 0)
- {
- svn_revnum_t revision
- = SVN_STR_TO_REV(dav_xml_get_cdata(child, resource->pool, 1));
- APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
- }
- else if (strcmp(child->name, "path") == 0)
- {
- relative_path = dav_xml_get_cdata(child, resource->pool, 0);
- if ((derr = dav_svn__test_canonical(relative_path, resource->pool)))
- return derr;
- }
- }
-
- /* Now we should have the parameters ready - let's
- check if they are all present. */
- if (! (relative_path && SVN_IS_VALID_REVNUM(peg_revision)))
- {
- return dav_svn__new_error_tag(resource->pool, HTTP_BAD_REQUEST, 0,
- "Not all parameters passed.",
- SVN_DAV_ERROR_NAMESPACE,
- SVN_DAV_ERROR_TAG);
- }
-
- /* Append the relative path to the base FS path to get an absolute
- repository path. */
- abs_path = svn_path_join(resource->info->repos_path, relative_path,
- resource->pool);
-
- /* Build an authz read baton */
- arb.r = resource->info->r;
- arb.repos = resource->info->repos;
-
- serr = svn_repos_trace_node_locations(resource->info->repos->fs,
- &fs_locations, abs_path, peg_revision,
- location_revisions,
- dav_svn__authz_read_func(&arb), &arb,
- resource->pool);
-
- if (serr)
- {
- return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
- serr->message, resource->pool);
- }
-
- bb = apr_brigade_create(resource->pool, output->c->bucket_alloc);
-
- apr_err = send_get_locations_report(output, bb, resource, fs_locations);
-
- if (apr_err)
- derr = dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error writing REPORT response.",
- resource->pool);
-
- /* Flush the contents of the brigade (returning an error only if we
- don't already have one). */
- if (((apr_err = ap_fflush(output, bb))) && (! derr))
- return dav_svn_convert_err(svn_error_create(apr_err, 0, NULL),
- HTTP_INTERNAL_SERVER_ERROR,
- "Error flushing brigade.",
- resource->pool);
-
- return derr;
-}
-
-
static dav_error *
dav_svn_deliver_report(request_rec *r,
const dav_resource *resource,
@@ -1382,7 +978,7 @@
}
else if (strcmp(doc->root->name, "dated-rev-report") == 0)
{
- return dav_svn__drev_report(resource, doc, output);
+ return dav_svn__dated_rev_report(resource, doc, output);
}
else if (strcmp(doc->root->name, "get-locations") == 0)
{
---------------------------------------------------------------------
To unsubscribe, e-mail: svn-unsubscribe@subversion.tigris.org
For additional commands, e-mail: svn-help@subversion.tigris.org
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic