/*
* version.c: mod_dav_svn versioning provider functions for Subversion
*
* ====================================================================
* Copyright (c) 2000-2006 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 <apr_tables.h>
#include <apr_uuid.h>
#include <httpd.h>
#include <http_log.h>
#include <mod_dav.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_props.h"
#include "svn_dav.h"
#include "svn_base64.h"
#include "../dav_svn.h"
/* Respond to a get-locks-report request. See description of this
report in libsvn_ra_dav/fetch.c. */
#define SVN_APR_ERR(expr) \
do { \
apr_status_t apr_status__temp = (expr); \
if (apr_status__temp) \
return apr_status__temp; \
} while (0)
/* Transmit LOCKS (a hash of Subversion filesystem locks keyed by
path) across OUTPUT using BB. Use POOL for necessary allocations.
NOTE: As written, this function currently returns one of only two
status values -- "success", and "we had trouble writing out to the
output stream". If you need to return something more interesting,
you'll probably want to generate dav_error's here instead of
passing back only apr_status_t's. */
static apr_status_t
send_get_lock_response(apr_hash_t *locks,
ap_filter_t *output,
apr_bucket_brigade *bb,
apr_pool_t *pool)
{
apr_pool_t *subpool;
apr_hash_index_t *hi;
/* start sending report */
SVN_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));
/* stream the locks */
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
void *val;
const svn_lock_t *lock;
svn_pool_clear(subpool);
apr_hash_this(hi, NULL, NULL, &val);
lock = val;
/* Begin the <S:lock> tag, transmitting the path, token, and
creation date. */
SVN_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,
apr_xml_quote_string(subpool, lock->path, 1),
apr_xml_quote_string(subpool, lock->token, 1),
svn_time_to_cstring(lock->creation_date,
subpool)));
/* Got expiration date? Tell the client. */
if (lock->expiration_date)
SVN_APR_ERR(ap_fprintf(output, bb,
"<S:expirationdate>%s</S:expirationdate>"
DEBUG_CR,
svn_time_to_cstring(lock->expiration_date,
subpool)));
/* Transmit the lock ownership information. */
if (lock->owner)
{
const char *owner;
svn_boolean_t owner_base64 = FALSE;
if (svn_xml_is_xml_safe(lock->owner, strlen(lock->owner)))
{
owner = 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_string2(&owner_string, TRUE,
subpool);
owner = encoded_owner->data;
owner_base64 = TRUE;
}
SVN_APR_ERR(ap_fprintf(output, bb,
"<S:owner %s>%s</S:owner>" DEBUG_CR,
owner_base64 ? "encoding=\"base64\"" : "",
owner));
}
/* If the lock carries a comment, transmit that, too. */
if (lock->comment)
{
const char *comment;
svn_boolean_t comment_base64 = FALSE;
if (svn_xml_is_xml_safe(lock->comment, strlen(lock->comment)))
{
comment = 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_string2(&comment_string,
TRUE, subpool);
comment = encoded_comment->data;
comment_base64 = TRUE;
}
SVN_APR_ERR(ap_fprintf(output, bb,
"<S:comment %s>%s</S:comment>" DEBUG_CR,
comment_base64 ? "encoding=\"base64\"" : "",
comment));
}
/* Okay, finish up this lock by closing the <S:lock> tag. */
SVN_APR_ERR(ap_fprintf(output, bb, "</S:lock>" DEBUG_CR));
}
svn_pool_destroy(subpool);
/* Finish the report */
SVN_APR_ERR(ap_fprintf(output, bb, "</S:get-locks-report>" DEBUG_CR));
return APR_SUCCESS;
}
#undef SVN_APR_ERR
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;
dav_error *derr = NULL;
apr_status_t apr_err;
apr_hash_t *locks;
dav_svn__authz_read_baton arb;
/* 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);
if ((apr_err = send_get_lock_response(locks, output, bb, resource->pool)))
derr = dav_svn__convert_err(svn_error_create(apr_err, 0, NULL),
HTTP_INTERNAL_SERVER_ERROR,
"Error writing REPORT response.",
resource->pool);
return dav_svn__final_flush_or_error(resource->info->r, bb, output,
derr, resource->pool);
}