blob: 395f7e8ba2c2ea62aea86811b88d3777fb7b26c2 [file] [log] [blame]
/*
* liveprops.c: mod_dav_svn live property provider functions for Subversion
*
* ====================================================================
* Copyright (c) 2000-2002 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 <util_xml.h>
#include <apr_tables.h>
#include <mod_dav.h>
#include "dav_svn.h"
/*
** The namespace URIs that we use. This list and the enumeration must
** stay in sync.
*/
static const char * const dav_svn_namespace_uris[] =
{
"DAV:",
SVN_PROP_PREFIX, /* ### need to get this approved from IANA */
NULL /* sentinel */
};
enum {
DAV_SVN_NAMESPACE_URI_DAV, /* the DAV: namespace URI */
DAV_SVN_NAMESPACE_URI /* the svn: namespace URI */
};
#define SVN_RO_DAV_PROP(name) \
{ DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##name, 0 }
#define SVN_RW_DAV_PROP(name) \
{ DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##name, 1 }
#define SVN_RO_DAV_PROP2(sym,name) \
{ DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##sym, 0 }
#define SVN_RW_DAV_PROP2(sym,name) \
{ DAV_SVN_NAMESPACE_URI_DAV, #name, DAV_PROPID_##sym, 1 }
#define SVN_RO_SVN_PROP(sym,name) \
{ DAV_SVN_NAMESPACE_URI, #name, SVN_PROPID_##sym, 0 }
#define SVN_RW_SVN_PROP(sym,name) \
{ DAV_SVN_NAMESPACE_URI, #name, SVN_PROPID_##sym, 1 }
enum {
SVN_PROPID_baseline_relative_path = 1
};
static const dav_liveprop_spec dav_svn_props[] =
{
/* ### don't worry about these for a bit */
#if 0
/* WebDAV properties */
SVN_RO_DAV_PROP(getcontentlanguage), /* ### make this r/w? */
SVN_RO_DAV_PROP(getcontentlength),
SVN_RO_DAV_PROP(getcontenttype), /* ### make this r/w? */
#endif
SVN_RO_DAV_PROP(getetag),
SVN_RO_DAV_PROP(creationdate),
#if 0
SVN_RO_DAV_PROP(getlastmodified),
#endif
/* DeltaV properties */
SVN_RO_DAV_PROP2(baseline_collection, baseline-collection),
SVN_RO_DAV_PROP2(checked_in, checked-in),
SVN_RO_DAV_PROP2(version_controlled_configuration,
version-controlled-configuration),
SVN_RO_DAV_PROP2(version_name, version-name),
SVN_RO_DAV_PROP2(creator_displayname, creator-displayname),
/* SVN properties */
SVN_RO_SVN_PROP(baseline_relative_path, baseline-relative-path),
{ 0 } /* sentinel */
};
static const dav_liveprop_group dav_svn_liveprop_group =
{
dav_svn_props,
dav_svn_namespace_uris,
&dav_svn_hooks_liveprop
};
static dav_prop_insert dav_svn_insert_prop(const dav_resource *resource,
int propid, dav_prop_insert what,
ap_text_header *phdr)
{
const char *value;
const char *s;
apr_pool_t *p = resource->pool;
const dav_liveprop_spec *info;
int global_ns;
svn_error_t *serr;
/*
** None of SVN provider properties are defined if the resource does not
** exist. Just bail for this case.
**
** Even though we state that the SVN properties are not defined, the
** client cannot store dead values -- we deny that thru the is_writable
** hook function.
*/
if (!resource->exists)
return DAV_PROP_INSERT_NOTSUPP;
/* ### we may want to respond to DAV_PROPID_resourcetype for PRIVATE
### resources. need to think on "proper" interaction with mod_dav */
switch (propid)
{
case DAV_PROPID_creationdate:
{
svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
svn_string_t *committed_date = NULL;
/* Get the CR field out of the node's skel. Notice that the
root object might be an ID root -or- a revision root. */
serr = svn_fs_node_created_rev(&committed_rev,
resource->info->root.root,
DAV_SVN_REPOS_PATH(resource), p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
/* Get the date property of the created revision. */
serr = svn_fs_revision_prop(&committed_date,
resource->info->repos->fs,
committed_rev,
SVN_PROP_REVISION_DATE, p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
if (committed_date == NULL)
return DAV_PROP_INSERT_NOTDEF;
value = apr_xml_quote_string(p, committed_date->data, 1);
break;
}
case DAV_PROPID_creator_displayname:
{
svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
svn_string_t *last_author = NULL;
/* Get the CR field out of the node's skel. Notice that the
root object might be an ID root -or- a revision root. */
serr = svn_fs_node_created_rev(&committed_rev,
resource->info->root.root,
DAV_SVN_REPOS_PATH(resource), p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
/* Get the date property of the created revision. */
serr = svn_fs_revision_prop(&last_author,
resource->info->repos->fs,
committed_rev,
SVN_PROP_REVISION_AUTHOR, p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
if (last_author == NULL)
return DAV_PROP_INSERT_NOTDEF;
value = apr_xml_quote_string(p, last_author->data, 1);
break;
}
case DAV_PROPID_getcontentlanguage:
/* ### need something here */
return DAV_PROP_INSERT_NOTSUPP;
break;
case DAV_PROPID_getcontentlength:
/* our property, but not defined on collection resources */
if (resource->collection)
return DAV_PROP_INSERT_NOTSUPP;
/* ### call svn_fs_file_length() */
value = "0";
break;
case DAV_PROPID_getcontenttype:
/* ### need something here */
/* ### maybe application/octet-stream and text/plain? */
return DAV_PROP_INSERT_NOTSUPP;
break;
case DAV_PROPID_getetag:
value = dav_svn_getetag(resource);
break;
case DAV_PROPID_getlastmodified:
/* ### need a modified date */
return DAV_PROP_INSERT_NOTSUPP;
break;
case DAV_PROPID_baseline_collection:
/* only defined for Baselines */
/* ### whoops. also defined for a VCC. deal with it later. */
if (resource->type != DAV_RESOURCE_TYPE_VERSION || !resource->baselined)
return DAV_PROP_INSERT_NOTSUPP;
value = dav_svn_build_uri(resource->info->repos, DAV_SVN_BUILD_URI_BC,
resource->info->root.rev, NULL,
1 /* add_href */, p);
break;
case DAV_PROPID_checked_in:
/* only defined for VCRs (in the public space and in a BC space) */
/* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
if (resource->type == DAV_RESOURCE_TYPE_PRIVATE
&& resource->info->restype == DAV_SVN_RESTYPE_VCC)
{
svn_revnum_t revnum;
serr = svn_fs_youngest_rev(&revnum, resource->info->repos->fs, p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
s = dav_svn_build_uri(resource->info->repos,
DAV_SVN_BUILD_URI_BASELINE,
revnum, NULL,
0 /* add_href */, p);
value = apr_psprintf(p, "<D:href>%s</D:href>",
apr_xml_quote_string(p, s, 1));
}
else if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
{
/* not defined for this resource type */
return DAV_PROP_INSERT_NOTSUPP;
}
else
{
svn_fs_id_t *id;
svn_stringbuf_t *stable_id;
serr = svn_fs_node_id(&id, resource->info->root.root,
resource->info->repos_path, p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
stable_id = svn_fs_unparse_id(id, p);
svn_stringbuf_appendcstr(stable_id, resource->info->repos_path);
s = dav_svn_build_uri(resource->info->repos,
DAV_SVN_BUILD_URI_VERSION,
SVN_INVALID_REVNUM, stable_id->data,
0 /* add_href */, p);
value = apr_psprintf(p, "<D:href>%s</D:href>",
apr_xml_quote_string(p, s, 1));
}
break;
case DAV_PROPID_version_controlled_configuration:
/* only defined for VCRs */
/* ### VCRs within the BC should not have this property! */
/* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
return DAV_PROP_INSERT_NOTSUPP;
value = dav_svn_build_uri(resource->info->repos, DAV_SVN_BUILD_URI_VCC,
SVN_IGNORED_REVNUM, NULL,
1 /* add_href */, p);
break;
case DAV_PROPID_version_name:
/* only defined for Version Resources and Baselines */
/* ### whoops. also defined for VCRs. deal with it later. */
if ((resource->type != DAV_RESOURCE_TYPE_VERSION)
&& (! resource->versioned))
return DAV_PROP_INSERT_NOTSUPP;
if (resource->baselined)
{
/* just the revision number for baselines */
value = apr_psprintf(p, "%ld", resource->info->root.rev);
}
else
{
svn_revnum_t committed_rev = SVN_INVALID_REVNUM;
/* Get the CR field out of the node's skel. Notice that the
root object might be an ID root -or- a revision root. */
serr = svn_fs_node_created_rev(&committed_rev,
resource->info->root.root,
DAV_SVN_REPOS_PATH(resource), p);
if (serr != NULL)
{
/* ### what to do? */
value = "###error###";
break;
}
/* Convert the revision into a quoted string */
s = apr_psprintf(p, "%ld", committed_rev);
value = apr_xml_quote_string(p, s, 1);
}
break;
case SVN_PROPID_baseline_relative_path:
/* only defined for VCRs */
/* ### VCRs within the BC should not have this property! */
/* ### note that a VCC (a special VCR) is defined as _PRIVATE for now */
if (resource->type != DAV_RESOURCE_TYPE_REGULAR)
return DAV_PROP_INSERT_NOTSUPP;
/* drop the leading slash, so it is relative */
s = resource->info->repos_path + 1;
value = apr_xml_quote_string(p, s, 1);
break;
default:
/* ### what the heck was this property? */
return DAV_PROP_INSERT_NOTDEF;
}
/* assert: value != NULL */
/* get the information and global NS index for the property */
global_ns = dav_get_liveprop_info(propid, &dav_svn_liveprop_group, &info);
/* assert: info != NULL && info->name != NULL */
if (what == DAV_PROP_INSERT_NAME
|| (what == DAV_PROP_INSERT_VALUE && *value == '\0')) {
s = apr_psprintf(p, "<lp%d:%s/>" DEBUG_CR, global_ns, info->name);
}
else if (what == DAV_PROP_INSERT_VALUE) {
s = apr_psprintf(p, "<lp%d:%s>%s</lp%d:%s>" DEBUG_CR,
global_ns, info->name, value, global_ns, info->name);
}
else {
/* assert: what == DAV_PROP_INSERT_SUPPORTED */
s = apr_psprintf(p,
"<D:supported-live-property D:name=\"%s\" "
"D:namespace=\"%s\"/>" DEBUG_CR,
info->name, dav_svn_namespace_uris[info->ns]);
}
ap_text_append(p, phdr, s);
/* we inserted whatever was asked for */
return what;
}
static int dav_svn_is_writable(const dav_resource *resource, int propid)
{
const dav_liveprop_spec *info;
(void) dav_get_liveprop_info(propid, &dav_svn_liveprop_group, &info);
return info->is_writable;
}
static dav_error * dav_svn_patch_validate(const dav_resource *resource,
const ap_xml_elem *elem,
int operation, void **context,
int *defer_to_dead)
{
/* NOTE: this function will not be called unless/until we have
modifiable (writable) live properties. */
return NULL;
}
static dav_error * dav_svn_patch_exec(const dav_resource *resource,
const ap_xml_elem *elem,
int operation, void *context,
dav_liveprop_rollback **rollback_ctx)
{
/* NOTE: this function will not be called unless/until we have
modifiable (writable) live properties. */
return NULL;
}
static void dav_svn_patch_commit(const dav_resource *resource,
int operation, void *context,
dav_liveprop_rollback *rollback_ctx)
{
/* NOTE: this function will not be called unless/until we have
modifiable (writable) live properties. */
}
static dav_error * dav_svn_patch_rollback(const dav_resource *resource,
int operation, void *context,
dav_liveprop_rollback *rollback_ctx)
{
/* NOTE: this function will not be called unless/until we have
modifiable (writable) live properties. */
return NULL;
}
const dav_hooks_liveprop dav_svn_hooks_liveprop = {
dav_svn_insert_prop,
dav_svn_is_writable,
dav_svn_namespace_uris,
dav_svn_patch_validate,
dav_svn_patch_exec,
dav_svn_patch_commit,
dav_svn_patch_rollback,
};
void dav_svn_gather_propsets(apr_array_header_t *uris)
{
/* ### what should we use for a URL to describe the available prop set? */
/* ### for now... nothing. we will *only* have DAV properties */
#if 0
*(const char **)apr_array_push(uris) =
"<http://subversion.tigris.org/dav/propset/svn/1>";
#endif
}
int dav_svn_find_liveprop(const dav_resource *resource,
const char *ns_uri, const char *name,
const dav_hooks_liveprop **hooks)
{
/* don't try to find any liveprops if this isn't "our" resource */
if (resource->hooks != &dav_svn_hooks_repos)
return 0;
return dav_do_find_liveprop(ns_uri, name, &dav_svn_liveprop_group, hooks);
}
void dav_svn_insert_all_liveprops(request_rec *r, const dav_resource *resource,
dav_prop_insert what, ap_text_header *phdr)
{
const dav_liveprop_spec *spec;
/* don't insert any liveprops if this isn't "our" resource */
if (resource->hooks != &dav_svn_hooks_repos)
return;
if (!resource->exists) {
/* a lock-null resource */
/*
** ### technically, we should insert empty properties. dunno offhand
** ### what part of the spec said this, but it was essentially thus:
** ### "the properties should be defined, but may have no value".
*/
return;
}
for (spec = dav_svn_props; spec->name != NULL; ++spec)
{
(void) dav_svn_insert_prop(resource, spec->propid, what, phdr);
}
/* ### we know the others aren't defined as liveprops */
}
void dav_svn_register_uris(apr_pool_t *p)
{
/* register the namespace URIs */
dav_register_liveprop_group(p, &dav_svn_liveprop_group);
}
/*
* local variables:
* eval: (load-file "../../tools/dev/svn-dev.el")
* end:
*/