blob: da9456e41eef68e10074ccbed5d878c0f576f139 [file] [log] [blame]
/*
* get-locks.c: mod_dav_svn REPORT handler for querying filesystem locks
*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
* ====================================================================
*/
#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_neon/get_locks.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 *iterpool;
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 */
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
const svn_lock_t *lock;
svn_pool_clear(iterpool);
lock = apr_hash_this_val(hi);
/* 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(iterpool, lock->path, 1),
apr_xml_quote_string(iterpool, lock->token, 1),
svn_time_to_cstring(lock->creation_date,
iterpool)));
/* 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,
iterpool)));
/* 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(iterpool, 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,
iterpool);
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(iterpool, 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, iterpool);
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(iterpool);
/* 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;
svn_depth_t depth = svn_depth_unknown;
apr_xml_attr *this_attr;
/* The request URI should be a public one representing an fs path. */
if ((! resource->info->repos_path)
|| (! resource->info->repos->repos))
return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, 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;
/* See if the client provided additional information for this request. */
for (this_attr = doc->root->attr; this_attr; this_attr = this_attr->next)
{
if (strcmp(this_attr->name, "depth") == 0)
{
depth = svn_depth_from_word(this_attr->value);
if ((depth != svn_depth_empty) &&
(depth != svn_depth_files) &&
(depth != svn_depth_immediates) &&
(depth != svn_depth_infinity))
return dav_svn__new_error(resource->pool, HTTP_BAD_REQUEST, 0, 0,
"Invalid 'depth' specified in "
"get-locks-report request.");
continue;
}
}
/* For compatibility, our default depth is infinity. */
if (depth == svn_depth_unknown)
depth = svn_depth_infinity;
/* Fetch the locks, but allow authz_read checks to happen on each. */
if ((err = svn_repos_fs_get_locks2(&locks,
resource->info->repos->repos,
resource->info->repos_path, depth,
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);
}