📄 version.c
字号:
/*
* version.c: mod_dav_svn versioning provider functions for Subversion
*
* ====================================================================
* Copyright (c) 2000-2004 CollabNet. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://subversion.tigris.org/license-1.html.
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
*
* This software consists of voluntary contributions made by many
* individuals. For exact contribution history, see the revision
* history and logs, available at http://subversion.tigris.org/.
* ====================================================================
*/
#include <httpd.h>
#include <http_log.h>
#include <mod_dav.h>
#include <apr_tables.h>
#include <apr_uuid.h>
#include "svn_fs.h"
#include "svn_xml.h"
#include "svn_repos.h"
#include "svn_dav.h"
#include "svn_time.h"
#include "svn_pools.h"
#include "svn_dav.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" },
{ NULL },
};
/* declare these static functions early, so we can use them anywhere. */
static dav_error *dav_svn_make_activity(dav_resource *resource);
/* Helper: attach an auto-generated svn:log property to a txn within
an auto-checked-out working resource. */
static dav_error *set_auto_log_message(dav_resource *resource)
{
const char *logmsg;
svn_string_t *logval;
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_log_message called on invalid resource.");
logmsg = apr_psprintf(resource->pool,
"Autoversioning commit: a non-deltaV client made "
"a change to\n%s", resource->info->repos_path);
logval = svn_string_create(logmsg, resource->pool);
serr = svn_repos_fs_change_txn_prop(resource->info->root.txn,
SVN_PROP_REVISION_LOG, logval,
resource->pool);
if (serr)
return dav_svn_convert_err(serr, HTTP_INTERNAL_SERVER_ERROR,
"Error setting auto-log-message on "
"auto-checked-out resource's transaction.",
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_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;
dav_error *derr;
dav_svn_uri_info parse;
/* Auto-Versioning Stuff */
if (auto_checkout)
{
dav_resource *res; /* ignored */
apr_uuid_t uuid;
char uuid_buf[APR_UUID_FORMATTED_LENGTH + 1];
/* 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_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_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);
/* Come up with a unique activity name, put it in the resource. */
apr_uuid_get(&uuid);
apr_uuid_format(uuid_buf, &uuid);
resource->info->root.activity_id = uuid_buf;
/* 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;
/* Create a txn based on youngest rev, and create an associated
activity id in the activity database. */
derr = dav_svn_make_activity(resource);
if (derr)
return derr;
/* Tweak the VCR in-place, making it into a WR. (Ignore the
NULL return value.) */
res = dav_svn_create_working_resource(resource, uuid_buf,
resource->info->root.txn_name,
TRUE /* tweak in place */);
/* Finally, be sure to open the txn and txn_root in the
resource. Normally we only get a PUT on a WR uri, and
prep_working() opens the txn automatically. We need to make
sure this WR is in the exact same state, ready for a PUT. */
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 the (transaction) root "
"of the repository",
resource->pool);
return NULL;
}
/* end of Auto-Versioning Stuff */
if (resource->type != DAV_RESOURCE_TYPE_VERSION)
{
return dav_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)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -