#include <limits.h>
#include <stdarg.h>
#define APR_WANT_STRFUNC
#include <apr_want.h>
#include <apr_general.h>
#include <apr_lib.h>
#include <apr_strings.h>
#include "svn_compat.h"
#include "svn_private_config.h"
#include "svn_hash.h"
#include "svn_types.h"
#include "svn_string.h"
#include "svn_pools.h"
#include "svn_error.h"
#include "svn_ra.h"
#include "svn_ra_svn.h"
#include "svn_repos.h"
#include "svn_dirent_uri.h"
#include "svn_path.h"
#include "svn_time.h"
#include "svn_config.h"
#include "svn_props.h"
#include "svn_mergeinfo.h"
#include "svn_user.h"
#include "private/svn_log.h"
#include "private/svn_mergeinfo_private.h"
#include "private/svn_ra_svn_private.h"
#include "private/svn_fspath.h"
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "server.h"
#include "logger.h"
typedef struct commit_callback_baton_t {
apr_pool_t *pool;
svn_revnum_t *new_rev;
const char **date;
const char **author;
const char **post_commit_err;
} commit_callback_baton_t;
typedef struct report_driver_baton_t {
server_baton_t *sb;
const char *repos_url;
void *report_baton;
svn_error_t *err;
int entry_counter;
svn_boolean_t only_empty_entries;
svn_revnum_t *from_rev;
} report_driver_baton_t;
typedef struct log_baton_t {
const char *fs_path;
svn_ra_svn_conn_t *conn;
int stack_depth;
} log_baton_t;
typedef struct file_revs_baton_t {
svn_ra_svn_conn_t *conn;
apr_pool_t *pool;
} file_revs_baton_t;
typedef struct fs_warning_baton_t {
server_baton_t *server;
svn_ra_svn_conn_t *conn;
} fs_warning_baton_t;
typedef struct authz_baton_t {
server_baton_t *server;
svn_ra_svn_conn_t *conn;
} authz_baton_t;
static void
log_error(svn_error_t *err, server_baton_t *server)
{
logger__log_error(server->logger, err, server->repository,
server->client_info);
}
static svn_error_t *
error_create_and_log(apr_status_t apr_err, svn_error_t *child,
const char *message, server_baton_t *server)
{
svn_error_t *err = svn_error_create(apr_err, child, message);
log_error(err, server);
return err;
}
static svn_error_t *
log_fail_and_flush(svn_error_t *err, server_baton_t *server,
svn_ra_svn_conn_t *conn, apr_pool_t *pool)
{
svn_error_t *io_err;
log_error(err, server);
io_err = svn_ra_svn__write_cmd_failure(conn, pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn__flush(conn, pool);
}
static svn_error_t *log_command(server_baton_t *b,
svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const char *fmt, ...)
{
const char *remote_host, *timestr, *log, *line;
va_list ap;
apr_size_t nbytes;
if (b->logger == NULL)
return SVN_NO_ERROR;
remote_host = svn_ra_svn_conn_remote_host(conn);
timestr = svn_time_to_cstring(apr_time_now(), pool);
va_start(ap, fmt);
log = apr_pvsprintf(pool, fmt, ap);
va_end(ap);
line = apr_psprintf(pool, "%" APR_PID_T_FMT
" %s %s %s %s %s" APR_EOL_STR,
getpid(), timestr,
(remote_host ? remote_host : "-"),
(b->client_info->user ? b->client_info->user : "-"),
b->repository->repos_name, log);
nbytes = strlen(line);
return logger__write(b->logger, line, nbytes);
}
static svn_error_t *
log_authz_denied(const char *path,
svn_repos_authz_access_t required,
server_baton_t *b,
apr_pool_t *pool)
{
const char *timestr, *remote_host, *line;
if (!b->logger)
return SVN_NO_ERROR;
if (!b->client_info || !b->client_info->user)
return SVN_NO_ERROR;
timestr = svn_time_to_cstring(apr_time_now(), pool);
remote_host = b->client_info->remote_host;
line = apr_psprintf(pool, "%" APR_PID_T_FMT
" %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR,
getpid(), timestr,
(remote_host ? remote_host : "-"),
b->client_info->user,
b->repository->repos_name,
(required & svn_authz_recursive ? "recursive " : ""),
(required & svn_authz_write ? "write" : "read"),
(path && path[0] ? path : "/"));
return logger__write(b->logger, line, strlen(line));
}
static svn_error_t *
load_pwdb_config(repository_t *repository,
svn_config_t *cfg,
svn_repos__config_pool_t *config_pool,
apr_pool_t *pool)
{
const char *pwdb_path;
svn_error_t *err;
svn_config_get(cfg, &pwdb_path,
SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_PASSWORD_DB, NULL);
repository->pwdb = NULL;
if (pwdb_path)
{
pwdb_path = svn_dirent_internal_style(pwdb_path, pool);
pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool);
err = svn_repos__config_pool_get(&repository->pwdb, NULL, config_pool,
pwdb_path, TRUE, FALSE,
repository->repos, pool);
if (err)
{
if (err->apr_err != SVN_ERR_BAD_FILENAME
&& ! APR_STATUS_IS_EACCES(err->apr_err))
{
return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL);
}
else
svn_error_clear(err);
}
}
return SVN_NO_ERROR;
}
static svn_error_t *
canonicalize_access_file(const char **access_file, repository_t *repository,
const char *repos_root, apr_pool_t *pool)
{
if (svn_path_is_url(*access_file))
{
*access_file = svn_uri_canonicalize(*access_file, pool);
}
else if (svn_path_is_repos_relative_url(*access_file))
{
const char *repos_root_url;
SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root,
pool));
SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file,
repos_root_url, pool));
*access_file = svn_uri_canonicalize(*access_file, pool);
}
else
{
*access_file = svn_dirent_internal_style(*access_file, pool);
*access_file = svn_dirent_join(repository->base, *access_file, pool);
}
return SVN_NO_ERROR;
}
static svn_error_t *
load_authz_config(repository_t *repository,
const char *repos_root,
svn_config_t *cfg,
svn_repos__authz_pool_t *authz_pool,
apr_pool_t *pool)
{
const char *authzdb_path;
const char *groupsdb_path;
svn_error_t *err;
svn_config_get(cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_AUTHZ_DB, NULL);
svn_config_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_GROUPS_DB, NULL);
if (authzdb_path)
{
const char *case_force_val;
err = canonicalize_access_file(&authzdb_path, repository,
repos_root, pool);
if (groupsdb_path && !err)
err = canonicalize_access_file(&groupsdb_path, repository,
repos_root, pool);
if (!err)
err = svn_repos__authz_pool_get(&repository->authzdb, authz_pool,
authzdb_path, groupsdb_path, TRUE,
repository->repos, pool);
if (err)
return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL);
svn_config_get(cfg, &case_force_val,
SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL);
if (case_force_val)
{
if (strcmp(case_force_val, "upper") == 0)
repository->username_case = CASE_FORCE_UPPER;
else if (strcmp(case_force_val, "lower") == 0)
repository->username_case = CASE_FORCE_LOWER;
else
repository->username_case = CASE_ASIS;
}
}
else
{
repository->authzdb = NULL;
repository->username_case = CASE_ASIS;
}
return SVN_NO_ERROR;
}
static svn_error_t *
handle_config_error(svn_error_t *error,
server_baton_t *server)
{
if ( error
&& ( error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG
|| error->apr_err == SVN_ERR_AUTHN_FAILED))
{
apr_status_t apr_err = error->apr_err;
log_error(error, server);
svn_error_clear(error);
return svn_error_create(apr_err, NULL, NULL);
}
return error;
}
static svn_error_t *get_fs_path(const char *repos_url, const char *url,
const char **fs_path)
{
apr_size_t len;
len = strlen(repos_url);
if (strncmp(url, repos_url, len) != 0)
return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
"'%s' is not the same repository as '%s'",
url, repos_url);
*fs_path = url + len;
if (! **fs_path)
*fs_path = "/";
return SVN_NO_ERROR;
}
static void convert_case(char *text, svn_boolean_t to_uppercase)
{
char *c = text;
while (*c)
{
*c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c));
++c;
}
}
static svn_error_t *authz_check_access(svn_boolean_t *allowed,
const char *path,
svn_repos_authz_access_t required,
server_baton_t *b,
apr_pool_t *pool)
{
repository_t *repository = b->repository;
client_info_t *client_info = b->client_info;
if (!repository->authzdb)
{
*allowed = TRUE;
return SVN_NO_ERROR;
}
if (path)
path = svn_fspath__canonicalize(path, pool);
if (client_info->user && (! client_info->authz_user))
{
char *authz_user = apr_pstrdup(b->pool, client_info->user);
if (repository->username_case == CASE_FORCE_UPPER)
convert_case(authz_user, TRUE);
else if (repository->username_case == CASE_FORCE_LOWER)
convert_case(authz_user, FALSE);
client_info->authz_user = authz_user;
}
SVN_ERR(svn_repos_authz_check_access(repository->authzdb,
repository->authz_repos_name,
path, client_info->authz_user,
required, allowed, pool));
if (!*allowed)
SVN_ERR(log_authz_denied(path, required, b, pool));
return SVN_NO_ERROR;
}
static svn_error_t *authz_check_access_cb(svn_boolean_t *allowed,
svn_fs_root_t *root,
const char *path,
void *baton,
apr_pool_t *pool)
{
authz_baton_t *sb = baton;
return authz_check_access(allowed, path, svn_authz_read,
sb->server, pool);
}
static svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton)
{
if (baton->repository->authzdb)
return authz_check_access_cb;
return NULL;
}
static svn_error_t *authz_commit_cb(svn_repos_authz_access_t required,
svn_boolean_t *allowed,
svn_fs_root_t *root,
const char *path,
void *baton,
apr_pool_t *pool)
{
authz_baton_t *sb = baton;
return authz_check_access(allowed, path, required, sb->server, pool);
}
static enum access_type
get_access(svn_config_t *cfg,
const char *option,
const char *def,
svn_boolean_t read_only)
{
enum access_type result;
const char *val;
svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, def);
result = (strcmp(val, "write") == 0 ? WRITE_ACCESS :
strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS);
return result == WRITE_ACCESS && read_only ? READ_ACCESS : result;
}
static void
set_access(repository_t *repository,
svn_config_t *cfg,
svn_boolean_t read_only)
{
repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS,
"write", read_only);
repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS,
"read", read_only);
}
static enum access_type
current_access(server_baton_t *b)
{
return b->client_info->user ? b->repository->auth_access
: b->repository->anon_access;
}
static svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
if (!needs_username && b->repository->anon_access >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS"));
if (b->client_info->tunnel_user && b->repository->auth_access >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL"));
if (b->repository->pwdb && b->repository->auth_access >= required)
SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5"));
return SVN_NO_ERROR;
}
struct cleanup_fs_access_baton
{
svn_fs_t *fs;
apr_pool_t *pool;
};
static apr_status_t cleanup_fs_access(void *data)
{
svn_error_t *serr;
struct cleanup_fs_access_baton *baton = data;
serr = svn_fs_set_access(baton->fs, NULL);
if (serr)
{
apr_status_t apr_err = serr->apr_err;
svn_error_clear(serr);
return apr_err;
}
return APR_SUCCESS;
}
static svn_error_t *
create_fs_access(server_baton_t *b, apr_pool_t *pool)
{
svn_fs_access_t *fs_access;
struct cleanup_fs_access_baton *cleanup_baton;
if (!b->client_info->user)
return SVN_NO_ERROR;
SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool));
SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access));
cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton));
cleanup_baton->pool = pool;
cleanup_baton->fs = b->repository->fs;
apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access,
apr_pool_cleanup_null);
return SVN_NO_ERROR;
}
static svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
const char *mech, const char *mecharg,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username,
svn_boolean_t *success)
{
const char *user;
*success = FALSE;
if (b->repository->auth_access >= required
&& b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0)
{
if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0)
return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
"Requested username does not match");
b->client_info->user = b->client_info->tunnel_user;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
*success = TRUE;
return SVN_NO_ERROR;
}
if (b->repository->anon_access >= required
&& strcmp(mech, "ANONYMOUS") == 0 && ! needs_username)
{
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success"));
*success = TRUE;
return SVN_NO_ERROR;
}
if (b->repository->auth_access >= required
&& b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0)
{
SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->repository->pwdb,
&user, success));
b->client_info->user = apr_pstrdup(b->pool, user);
return SVN_NO_ERROR;
}
return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure",
"Must authenticate with listed mechanism");
}
static svn_error_t *
internal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
svn_boolean_t success;
const char *mech, *mecharg;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
SVN_ERR(send_mechs(conn, pool, b, required, needs_username));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm));
do
{
SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg));
if (!*mech)
break;
SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username,
&success));
}
while (!success);
return SVN_NO_ERROR;
}
static svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, enum access_type required,
svn_boolean_t needs_username)
{
#ifdef SVN_HAVE_SASL
if (b->repository->use_sasl)
return cyrus_auth_request(conn, pool, b, required, needs_username);
#endif
return internal_auth_request(conn, pool, b, required, needs_username);
}
static svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn,
apr_pool_t *pool, server_baton_t *b)
{
return svn_ra_svn__write_cmd_response(conn, pool, "()c", "");
}
static svn_boolean_t lookup_access(apr_pool_t *pool,
server_baton_t *baton,
svn_repos_authz_access_t required,
const char *path,
svn_boolean_t needs_username)
{
enum access_type req = (required & svn_authz_write) ?
WRITE_ACCESS : READ_ACCESS;
svn_boolean_t authorized;
svn_error_t *err;
err = authz_check_access(&authorized, path, required, baton, pool);
if (err)
{
log_error(err, baton);
svn_error_clear(err);
return FALSE;
}
if (current_access(baton) >= req
&& authorized
&& (! needs_username || baton->client_info->user))
return TRUE;
return FALSE;
}
static svn_error_t *must_have_access(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
server_baton_t *b,
svn_repos_authz_access_t required,
const char *path,
svn_boolean_t needs_username)
{
enum access_type req = (required & svn_authz_write) ?
WRITE_ACCESS : READ_ACCESS;
if (lookup_access(pool, b, required, path, needs_username))
{
SVN_ERR(create_fs_access(b, pool));
return trivial_auth_request(conn, pool, b);
}
if (b->client_info->user == NULL
&& b->repository->auth_access >= req
&& (b->client_info->tunnel_user || b->repository->pwdb
|| b->repository->use_sasl))
SVN_ERR(auth_request(conn, pool, b, req, TRUE));
if (! lookup_access(pool, b, required, path, needs_username))
return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR,
error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
NULL, NULL, b),
NULL);
SVN_ERR(create_fs_access(b, pool));
return SVN_NO_ERROR;
}
static svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path, *lock_token, *depth_word;
svn_revnum_t rev;
svn_depth_t depth = svn_depth_infinity;
svn_boolean_t start_empty;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w",
&path, &rev, &start_empty, &lock_token,
&depth_word));
if (depth_word)
depth = svn_depth_from_word(depth_word);
path = svn_relpath_canonicalize(path, pool);
if (b->from_rev && strcmp(path, "") == 0)
*b->from_rev = rev;
if (!b->err)
b->err = svn_repos_set_path3(b->report_baton, path, rev, depth,
start_empty, lock_token, pool);
b->entry_counter++;
if (!start_empty)
b->only_empty_entries = FALSE;
return SVN_NO_ERROR;
}
static svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
path = svn_relpath_canonicalize(path, pool);
if (!b->err)
b->err = svn_repos_delete_path(b->report_baton, path, pool);
return SVN_NO_ERROR;
}
static svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
const char *path, *url, *lock_token, *fs_path, *depth_word;
svn_revnum_t rev;
svn_boolean_t start_empty;
svn_depth_t depth = svn_depth_infinity;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w",
&path, &url, &rev, &start_empty,
&lock_token, &depth_word));
path = svn_relpath_canonicalize(path, pool);
url = svn_uri_canonicalize(url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
if (!b->err)
b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool),
svn_path_uri_decode(url, pool),
&fs_path);
if (!b->err)
b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev,
depth, start_empty, lock_token, pool);
b->entry_counter++;
return SVN_NO_ERROR;
}
static svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
SVN_ERR(trivial_auth_request(conn, pool, b->sb));
if (!b->err)
b->err = svn_repos_finish_report(b->report_baton, pool);
return SVN_NO_ERROR;
}
static svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
report_driver_baton_t *b = baton;
svn_error_clear(svn_repos_abort_report(b->report_baton, pool));
return SVN_NO_ERROR;
}
static const svn_ra_svn_cmd_entry_t report_commands[] = {
{ "set-path", set_path },
{ "delete-path", delete_path },
{ "link-path", link_path },
{ "finish-report", finish_report, TRUE },
{ "abort-report", abort_report, TRUE },
{ NULL }
};
static svn_error_t *accept_report(svn_boolean_t *only_empty_entry,
svn_revnum_t *from_rev,
svn_ra_svn_conn_t *conn, apr_pool_t *pool,
server_baton_t *b, svn_revnum_t rev,
const char *target, const char *tgt_path,
svn_boolean_t text_deltas,
svn_depth_t depth,
svn_boolean_t send_copyfrom_args,
svn_boolean_t ignore_ancestry)
{
const svn_delta_editor_t *editor;
void *edit_baton, *report_baton;
report_driver_baton_t rb;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev,
b->repository->repos,
b->repository->fs_path->data, target,
tgt_path, text_deltas, depth,
ignore_ancestry, send_copyfrom_args,
editor, edit_baton,
authz_check_access_cb_func(b),
&ab, svn_ra_svn_zero_copy_limit(conn),
pool));
rb.sb = b;
rb.repos_url = svn_path_uri_decode(b->repository->repos_url, pool);
rb.report_baton = report_baton;
rb.err = NULL;
rb.entry_counter = 0;
rb.only_empty_entries = TRUE;
rb.from_rev = from_rev;
if (from_rev)
*from_rev = SVN_INVALID_REVNUM;
err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE);
if (err)
{
svn_error_clear(rb.err);
return err;
}
else if (rb.err)
{
SVN_CMD_ERR(rb.err);
}
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
if (only_empty_entry)
*only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries;
return SVN_NO_ERROR;
}
static svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const apr_array_header_t *propdiffs)
{
int i;
for (i = 0; i < propdiffs->nelts; ++i)
{
const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)",
prop->name, prop->value));
}
return SVN_NO_ERROR;
}
static svn_error_t *write_lock(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
const svn_lock_t *lock)
{
const char *cdate, *edate;
cdate = svn_time_to_cstring(lock->creation_date, pool);
edate = lock->expiration_date
? svn_time_to_cstring(lock->expiration_date, pool) : NULL;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path,
lock->token, lock->owner, lock->comment,
cdate, edate));
return SVN_NO_ERROR;
}
static svn_error_t *
get_props(apr_hash_t **props,
apr_array_header_t **iprops,
authz_baton_t *b,
svn_fs_root_t *root,
const char *path,
apr_pool_t *pool)
{
if (props)
{
svn_string_t *str;
svn_revnum_t crev;
const char *cdate, *cauthor, *uuid;
SVN_ERR(svn_fs_node_proplist(props, root, path, pool));
SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root,
path, pool));
str = svn_string_createf(pool, "%ld", crev);
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str);
str = (cdate) ? svn_string_create(cdate, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str);
str = (cauthor) ? svn_string_create(cauthor, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str);
SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool));
str = (uuid) ? svn_string_create(uuid, pool) : NULL;
svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str);
}
if (iprops)
{
SVN_ERR(svn_repos_fs_get_inherited_props(
iprops, root, path, NULL,
authz_check_access_cb_func(b->server),
b, pool, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *url;
const char *fs_path;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url));
url = svn_uri_canonicalize(url, pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool),
svn_path_uri_decode(url, pool),
&fs_path));
SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool)));
svn_stringbuf_set(b->repository->fs_path, fs_path);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
SVN_ERR(log_command(b, conn, pool, "get-latest-rev"));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
return SVN_NO_ERROR;
}
static svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_time_t tm;
const char *timestr;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", ×tr));
SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool));
SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev));
return SVN_NO_ERROR;
}
static svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn,
server_baton_t *b,
svn_revnum_t rev,
const char *name,
const svn_string_t *const *old_value_p,
const svn_string_t *value,
apr_pool_t *pool)
{
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__change_rev_prop(rev, name, pool)));
SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev,
b->client_info->user,
name, old_value_p, value,
TRUE, TRUE,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
const svn_string_t *const *old_value_p;
svn_string_t *old_value;
svn_boolean_t dont_care;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)",
&rev, &name, &value,
&dont_care, &old_value));
if (dont_care)
old_value_p = NULL;
else
old_value_p = (const svn_string_t *const *)&old_value;
if (dont_care && old_value)
{
svn_error_t *err;
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
"'previous-value' and 'dont-care' cannot both be "
"set in 'change-rev-prop2' request");
return log_fail_and_flush(err, b, conn, pool);
}
SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool));
return SVN_NO_ERROR;
}
static svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value));
SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool));
return SVN_NO_ERROR;
}
static svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_hash_t *props;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev));
SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->repos,
rev,
authz_check_access_cb_func(b),
&ab, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *name;
svn_string_t *value;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__rev_prop(rev, name, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->repos, rev,
name, authz_check_access_cb_func(b),
&ab, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value));
return SVN_NO_ERROR;
}
static svn_error_t *commit_done(const svn_commit_info_t *commit_info,
void *baton, apr_pool_t *pool)
{
commit_callback_baton_t *ccb = baton;
*ccb->new_rev = commit_info->revision;
*ccb->date = commit_info->date
? apr_pstrdup(ccb->pool, commit_info->date): NULL;
*ccb->author = commit_info->author
? apr_pstrdup(ccb->pool, commit_info->author) : NULL;
*ccb->post_commit_err = commit_info->post_commit_err
? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL;
return SVN_NO_ERROR;
}
static svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens,
server_baton_t *sb,
apr_pool_t *pool)
{
int i;
svn_fs_access_t *fs_access;
SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs));
if (! fs_access)
return SVN_NO_ERROR;
for (i = 0; i < lock_tokens->nelts; ++i)
{
const char *path, *token, *full_path;
svn_ra_svn_item_t *path_item, *token_item;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i,
svn_ra_svn_item_t);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock tokens aren't a list of lists");
path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
if (path_item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock path isn't a string");
token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
if (token_item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock token isn't a string");
path = path_item->u.string->data;
full_path = svn_fspath__join(sb->repository->fs_path->data,
svn_relpath_canonicalize(path, pool),
pool);
if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE))
return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL,
sb);
token = token_item->u.string->data;
SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token));
}
return SVN_NO_ERROR;
}
static svn_error_t *
lock_cb(void *baton,
const char *path,
const svn_lock_t *lock,
svn_error_t *fs_err,
apr_pool_t *pool)
{
server_baton_t *sb = baton;
log_error(fs_err, sb);
return SVN_NO_ERROR;
}
static svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens,
server_baton_t *sb,
apr_pool_t *pool)
{
int i;
apr_pool_t *subpool = svn_pool_create(pool);
apr_hash_t *targets = apr_hash_make(subpool);
svn_error_t *err;
for (i = 0; i < lock_tokens->nelts; ++i)
{
svn_ra_svn_item_t *item, *path_item, *token_item;
const char *path, *token, *full_path;
item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t);
path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t);
token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t);
path = path_item->u.string->data;
full_path = svn_fspath__join(sb->repository->fs_path->data,
svn_relpath_canonicalize(path, subpool),
subpool);
token = token_item->u.string->data;
svn_hash_sets(targets, full_path, token);
}
err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE,
lock_cb, sb, subpool, subpool);
log_error(err, sb);
svn_error_clear(err);
svn_pool_destroy(subpool);
return SVN_NO_ERROR;
}
static svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *log_msg,
*date = NULL,
*author = NULL,
*post_commit_err = NULL;
apr_array_header_t *lock_tokens;
svn_boolean_t keep_locks;
apr_array_header_t *revprop_list;
apr_hash_t *revprop_table;
const svn_delta_editor_t *editor;
void *edit_baton;
svn_boolean_t aborted;
commit_callback_baton_t ccb;
svn_revnum_t new_rev;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
if (params->nelts == 1)
{
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg));
lock_tokens = NULL;
keep_locks = TRUE;
revprop_list = NULL;
}
else
{
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg,
&lock_tokens, &keep_locks,
&revprop_list));
}
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
NULL,
(lock_tokens && lock_tokens->nelts)));
if (lock_tokens && lock_tokens->nelts)
SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool));
if (revprop_list)
SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table));
else
{
revprop_table = apr_hash_make(pool);
svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG,
svn_string_create(log_msg, pool));
}
svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR,
b->client_info->user
? svn_string_create(b->client_info->user, pool)
: NULL);
ccb.pool = pool;
ccb.new_rev = &new_rev;
ccb.date = &date;
ccb.author = &author;
ccb.post_commit_err = &post_commit_err;
SVN_CMD_ERR(svn_repos_get_commit_editor5
(&editor, &edit_baton, b->repository->repos, NULL,
svn_path_uri_decode(b->repository->repos_url, pool),
b->repository->fs_path->data, revprop_table,
commit_done, &ccb,
authz_commit_cb, &ab, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton,
&aborted, FALSE));
if (!aborted)
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__commit(new_rev, pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
if (b->client_info->tunnel)
SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
if (! keep_locks && lock_tokens && lock_tokens->nelts)
SVN_ERR(unlock_paths(lock_tokens, b, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)",
new_rev, date, author, post_commit_err));
if (! b->client_info->tunnel)
SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool));
}
return SVN_NO_ERROR;
}
static svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path, *hex_digest;
svn_revnum_t rev;
svn_fs_root_t *root;
svn_stream_t *contents;
apr_hash_t *props = NULL;
apr_array_header_t *inherited_props;
svn_string_t write_str;
char buf[4096];
apr_size_t len;
svn_boolean_t want_props, want_contents;
apr_uint64_t wants_inherited_props;
svn_checksum_t *checksum;
svn_error_t *err, *write_err;
int i;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev,
&want_props, &want_contents,
&wants_inherited_props));
if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
wants_inherited_props = FALSE;
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_file(full_path, rev,
want_contents, want_props, pool)));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root,
full_path, TRUE, pool));
hex_digest = svn_checksum_to_cstring_display(checksum, pool);
if (want_props || wants_inherited_props)
SVN_CMD_ERR(get_props(want_props ? &props : NULL,
wants_inherited_props ? &inherited_props : NULL,
&ab, root, full_path,
pool));
if (want_contents)
SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success",
hex_digest, rev));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
if (wants_inherited_props)
{
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
svn_pool_destroy(iterpool);
}
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
if (want_contents)
{
err = SVN_NO_ERROR;
while (1)
{
len = sizeof(buf);
err = svn_stream_read_full(contents, buf, &len);
if (err)
break;
if (len > 0)
{
write_str.data = buf;
write_str.len = len;
SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str));
}
if (len < sizeof(buf))
{
err = svn_stream_close(contents);
break;
}
}
write_err = svn_ra_svn__write_cstring(conn, pool, "");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
}
return SVN_NO_ERROR;
}
static svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t rev;
apr_hash_t *entries, *props = NULL;
apr_array_header_t *inherited_props;
apr_hash_index_t *hi;
svn_fs_root_t *root;
apr_pool_t *subpool;
svn_boolean_t want_props, want_contents;
apr_uint64_t wants_inherited_props;
apr_uint64_t dirent_fields;
apr_array_header_t *dirent_fields_list = NULL;
svn_ra_svn_item_t *elt;
int i;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev,
&want_props, &want_contents,
&dirent_fields_list,
&wants_inherited_props));
if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER)
wants_inherited_props = FALSE;
if (! dirent_fields_list)
{
dirent_fields = SVN_DIRENT_ALL;
}
else
{
dirent_fields = 0;
for (i = 0; i < dirent_fields_list->nelts; ++i)
{
elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_WORD)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Dirent field not a string");
if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_KIND;
else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_SIZE;
else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_HAS_PROPS;
else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_CREATED_REV;
else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_TIME;
else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0)
dirent_fields |= SVN_DIRENT_LAST_AUTHOR;
}
}
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_dir(full_path, rev,
want_contents, want_props,
dirent_fields, pool)));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
if (want_props || wants_inherited_props)
SVN_CMD_ERR(get_props(want_props ? &props : NULL,
wants_inherited_props ? &inherited_props : NULL,
&ab, root, full_path,
pool));
if (want_contents)
SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev));
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!"));
if (want_contents)
{
const char *missing_date = svn_time_to_cstring(0, pool);
subpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi))
{
const char *name = apr_hash_this_key(hi);
svn_fs_dirent_t *fsent = apr_hash_this_val(hi);
const char *file_path;
svn_node_kind_t entry_kind = svn_node_none;
svn_filesize_t entry_size = 0;
svn_boolean_t has_props = FALSE;
svn_revnum_t created_rev = 0;
const char *cdate = NULL;
const char *last_author = NULL;
svn_pool_clear(subpool);
file_path = svn_fspath__join(full_path, name, subpool);
if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE))
continue;
if (dirent_fields & SVN_DIRENT_KIND)
entry_kind = fsent->kind;
if (dirent_fields & SVN_DIRENT_SIZE)
if (entry_kind != svn_node_dir)
SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path,
subpool));
if (dirent_fields & SVN_DIRENT_HAS_PROPS)
{
SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path,
subpool));
}
if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR)
|| (dirent_fields & SVN_DIRENT_TIME)
|| (dirent_fields & SVN_DIRENT_CREATED_REV))
{
SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev,
&cdate,
&last_author,
root,
file_path,
subpool));
}
if (cdate == NULL)
cdate = missing_date;
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name,
svn_node_kind_to_word(entry_kind),
(apr_uint64_t) entry_size,
has_props, created_rev,
cdate, last_author));
}
svn_pool_destroy(subpool);
}
if (wants_inherited_props)
{
apr_pool_t *iterpool = svn_pool_create(pool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
svn_pool_destroy(iterpool);
}
return svn_ra_svn__write_tuple(conn, pool, "!))");
}
static svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *full_path, *depth_word;
svn_boolean_t recurse;
svn_tristate_t send_copyfrom_args;
svn_tristate_t ignore_ancestry;
svn_depth_t depth = svn_depth_unknown;
svn_boolean_t is_checkout;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?w3?3", &rev, &target,
&recurse, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
full_path = svn_fspath__join(b->repository->fs_path->data, target, pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(accept_report(&is_checkout, NULL,
conn, pool, b, rev, target, NULL, TRUE,
depth,
(send_copyfrom_args == svn_tristate_true),
(ignore_ancestry == svn_tristate_true)));
if (is_checkout)
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__checkout(full_path, rev,
depth, pool)));
}
else
{
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__update(full_path, rev, depth,
(send_copyfrom_args
== svn_tristate_true),
pool)));
}
return SVN_NO_ERROR;
}
static svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *depth_word;
const char *switch_url, *switch_path;
svn_boolean_t recurse;
svn_depth_t depth = svn_depth_unknown;
svn_tristate_t send_copyfrom_args;
svn_tristate_t ignore_ancestry;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?33", &rev, &target,
&recurse, &switch_url, &depth_word,
&send_copyfrom_args, &ignore_ancestry));
target = svn_relpath_canonicalize(target, pool);
switch_url = svn_uri_canonicalize(switch_url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
pool),
svn_path_uri_decode(switch_url, pool),
&switch_path));
{
const char *full_path = svn_fspath__join(b->repository->fs_path->data,
target, pool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__switch(full_path, switch_path, rev,
depth, pool)));
}
return accept_report(NULL, NULL,
conn, pool, b, rev, target, switch_path, TRUE,
depth,
(send_copyfrom_args == svn_tristate_true),
(ignore_ancestry != svn_tristate_false));
}
static svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *depth_word;
svn_boolean_t recurse;
svn_depth_t depth = svn_depth_unknown;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w",
&target, &recurse, &rev, &depth_word));
target = svn_relpath_canonicalize(target, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
{
const char *full_path = svn_fspath__join(b->repository->fs_path->data,
target, pool);
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__status(full_path, rev, depth, pool)));
}
return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE,
depth, FALSE, FALSE);
}
static svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *target, *versus_url, *versus_path, *depth_word;
svn_boolean_t recurse, ignore_ancestry;
svn_boolean_t text_deltas;
svn_depth_t depth = svn_depth_unknown;
if (params->nelts == 5)
{
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target,
&recurse, &ignore_ancestry, &versus_url));
text_deltas = TRUE;
depth_word = NULL;
}
else
{
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w",
&rev, &target, &recurse,
&ignore_ancestry, &versus_url,
&text_deltas, &depth_word));
}
target = svn_relpath_canonicalize(target, pool);
versus_url = svn_uri_canonicalize(versus_url, pool);
if (depth_word)
depth = svn_depth_from_word(depth_word);
else
depth = SVN_DEPTH_INFINITY_OR_FILES(recurse);
SVN_ERR(trivial_auth_request(conn, pool, b));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url,
pool),
svn_path_uri_decode(versus_url, pool),
&versus_path));
{
const char *full_path = svn_fspath__join(b->repository->fs_path->data,
target, pool);
svn_revnum_t from_rev;
SVN_ERR(accept_report(NULL, &from_rev,
conn, pool, b, rev, target, versus_path,
text_deltas, depth, FALSE, ignore_ancestry));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__diff(full_path, from_rev, versus_path,
rev, depth, ignore_ancestry,
pool)));
}
return SVN_NO_ERROR;
}
static svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
apr_array_header_t *paths, *canonical_paths;
svn_mergeinfo_catalog_t mergeinfo;
int i;
apr_hash_index_t *hi;
const char *inherit_word;
svn_mergeinfo_inheritance_t inherit;
svn_boolean_t include_descendants;
apr_pool_t *iterpool;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev,
&inherit_word, &include_descendants));
inherit = svn_inheritance_from_word(inherit_word);
canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
for (i = 0; i < paths->nelts; i++)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
const char *full_path;
if (item->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Path is not a string"));
full_path = svn_relpath_canonicalize(item->u.string->data, pool);
full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool);
APR_ARRAY_PUSH(canonical_paths, const char *) = full_path;
}
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_mergeinfo(canonical_paths, inherit,
include_descendants,
pool)));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repository->repos,
canonical_paths, rev,
inherit,
include_descendants,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo,
b->repository->fs_path->data, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
iterpool = svn_pool_create(pool);
for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi))
{
const char *key = apr_hash_this_key(hi);
svn_mergeinfo_t value = apr_hash_this_val(hi);
svn_string_t *mergeinfo_string;
svn_pool_clear(iterpool);
SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key,
mergeinfo_string));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *log_receiver(void *baton,
svn_log_entry_t *log_entry,
apr_pool_t *pool)
{
log_baton_t *b = baton;
svn_ra_svn_conn_t *conn = b->conn;
apr_hash_index_t *h;
svn_boolean_t invalid_revnum = FALSE;
const svn_string_t *author, *date, *message;
unsigned revprop_count;
if (log_entry->revision == SVN_INVALID_REVNUM)
{
if (b->stack_depth == 0)
return SVN_NO_ERROR;
log_entry->revision = 0;
invalid_revnum = TRUE;
b->stack_depth--;
}
svn_compat_log_revprops_out_string(&author, &date, &message,
log_entry->revprops);
svn_compat_log_revprops_clear(log_entry->revprops);
if (log_entry->revprops)
revprop_count = apr_hash_count(log_entry->revprops);
else
revprop_count = 0;
SVN_ERR(svn_ra_svn__start_list(conn, pool));
SVN_ERR(svn_ra_svn__start_list(conn, pool));
if (log_entry->changed_paths2)
{
for (h = apr_hash_first(pool, log_entry->changed_paths2); h;
h = apr_hash_next(h))
{
const char *path = apr_hash_this_key(h);
svn_log_changed_path2_t *change = apr_hash_this_val(h);
SVN_ERR(svn_ra_svn__write_data_log_changed_path(
conn, pool,
path,
change->action,
change->copyfrom_path,
change->copyfrom_rev,
change->node_kind,
change->text_modified == svn_tristate_true,
change->props_modified == svn_tristate_true));
}
}
SVN_ERR(svn_ra_svn__end_list(conn, pool));
SVN_ERR(svn_ra_svn__write_data_log_entry(conn, pool,
log_entry->revision,
author, date, message,
log_entry->has_children,
invalid_revnum, revprop_count));
SVN_ERR(svn_ra_svn__start_list(conn, pool));
if (revprop_count)
SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops));
SVN_ERR(svn_ra_svn__end_list(conn, pool));
SVN_ERR(svn_ra_svn__write_boolean(conn, pool, log_entry->subtractive_merge));
SVN_ERR(svn_ra_svn__end_list(conn, pool));
if (log_entry->has_children)
b->stack_depth++;
return SVN_NO_ERROR;
}
static svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t start_rev, end_rev;
const char *full_path;
svn_boolean_t send_changed_paths, strict_node, include_merged_revisions;
apr_array_header_t *paths, *full_paths, *revprop_items, *revprops;
char *revprop_word;
svn_ra_svn_item_t *elt;
int i;
apr_uint64_t limit, include_merged_revs_param;
log_baton_t lb;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths,
&start_rev, &end_rev, &send_changed_paths,
&strict_node, &limit,
&include_merged_revs_param,
&revprop_word, &revprop_items));
if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
include_merged_revisions = FALSE;
else
include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
if (revprop_word == NULL)
revprops = svn_compat_log_revprops_in(pool);
else if (strcmp(revprop_word, "all-revprops") == 0)
revprops = NULL;
else if (strcmp(revprop_word, "revprops") == 0)
{
SVN_ERR_ASSERT(revprop_items);
revprops = apr_array_make(pool, revprop_items->nelts,
sizeof(char *));
for (i = 0; i < revprop_items->nelts; i++)
{
elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Log revprop entry not a string"));
APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data;
}
}
else
return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Unknown revprop word '%s' in log command"),
revprop_word);
if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX)
limit = 0;
full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *));
for (i = 0; i < paths->nelts; i++)
{
elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_STRING)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
_("Log path entry not a string"));
full_path = svn_relpath_canonicalize(elt->u.string->data, pool),
full_path = svn_fspath__join(b->repository->fs_path->data, full_path,
pool);
APR_ARRAY_PUSH(full_paths, const char *) = full_path;
}
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__log(full_paths, start_rev, end_rev,
(int) limit, send_changed_paths,
strict_node, include_merged_revisions,
revprops, pool)));
lb.fs_path = b->repository->fs_path->data;
lb.conn = conn;
lb.stack_depth = 0;
err = svn_repos_get_logs4(b->repository->repos, full_paths, start_rev,
end_rev, (int) limit, send_changed_paths,
strict_node, include_merged_revisions,
revprops, authz_check_access_cb_func(b), &ab,
log_receiver, &lb, pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *path, *full_path;
svn_fs_root_t *root;
svn_node_kind_t kind;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(log_command(b, conn, pool, "check-path %s@%d",
svn_path_uri_encode(full_path, pool), rev));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w",
svn_node_kind_to_word(kind)));
return SVN_NO_ERROR;
}
static svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_revnum_t rev;
const char *path, *full_path, *cdate;
svn_fs_root_t *root;
svn_dirent_t *dirent;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(log_command(b, conn, pool, "stat %s@%d",
svn_path_uri_encode(full_path, pool), rev));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool));
SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool));
if (dirent == NULL)
{
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()"));
return SVN_NO_ERROR;
}
cdate = (dirent->time == (time_t) -1) ? NULL
: svn_time_to_cstring(dirent->time, pool);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))",
svn_node_kind_to_word(dirent->kind),
(apr_uint64_t) dirent->size,
dirent->has_props, dirent->created_rev,
cdate, dirent->last_author));
return SVN_NO_ERROR;
}
static svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t revision;
apr_array_header_t *location_revisions, *loc_revs_proto;
svn_ra_svn_item_t *elt;
int i;
const char *relative_path;
svn_revnum_t peg_revision;
apr_hash_t *fs_locations;
const char *abs_path;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path,
&peg_revision,
&loc_revs_proto));
relative_path = svn_relpath_canonicalize(relative_path, pool);
abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
pool);
location_revisions = apr_array_make(pool, loc_revs_proto->nelts,
sizeof(svn_revnum_t));
for (i = 0; i < loc_revs_proto->nelts; i++)
{
elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t);
if (elt->kind != SVN_RA_SVN_NUMBER)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Get-locations location revisions entry "
"not a revision number");
revision = (svn_revnum_t)(elt->u.number);
APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision;
}
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_locations(abs_path, peg_revision,
location_revisions, pool)));
err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations,
abs_path, peg_revision,
location_revisions,
authz_check_access_cb_func(b), &ab,
pool);
if (!err)
{
if (fs_locations)
{
apr_hash_index_t *iter;
for (iter = apr_hash_first(pool, fs_locations); iter;
iter = apr_hash_next(iter))
{
const svn_revnum_t *iter_key = apr_hash_this_key(iter);
const char *iter_value = apr_hash_this_val(iter);
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc",
*iter_key, iter_value));
}
}
}
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *gls_receiver(svn_location_segment_t *segment,
void *baton,
apr_pool_t *pool)
{
svn_ra_svn_conn_t *conn = baton;
return svn_ra_svn__write_tuple(conn, pool, "rr(?c)",
segment->range_start,
segment->range_end,
segment->path);
}
static svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
svn_error_t *err, *write_err;
server_baton_t *b = baton;
svn_revnum_t peg_revision, start_rev, end_rev;
const char *relative_path;
const char *abs_path;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)",
&relative_path, &peg_revision,
&start_rev, &end_rev));
relative_path = svn_relpath_canonicalize(relative_path, pool);
abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path,
pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(baton, conn, pool, "%s",
svn_log__get_location_segments(abs_path, peg_revision,
start_rev, end_rev,
pool)));
if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision))
{
svn_revnum_t youngest;
err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool);
if (err)
{
err = svn_error_compose_create(
svn_ra_svn__write_word(conn, pool, "done"),
err);
return log_fail_and_flush(err, b, conn, pool);
}
if (!SVN_IS_VALID_REVNUM(start_rev))
start_rev = youngest;
if (!SVN_IS_VALID_REVNUM(peg_revision))
peg_revision = youngest;
}
if (!SVN_IS_VALID_REVNUM(end_rev))
end_rev = 0;
if (end_rev > start_rev)
{
err = svn_ra_svn__write_word(conn, pool, "done");
err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err,
"Get-location-segments end revision must not be "
"younger than start revision");
return log_fail_and_flush(err, b, conn, pool);
}
if (start_rev > peg_revision)
{
err = svn_ra_svn__write_word(conn, pool, "done");
err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err,
"Get-location-segments start revision must not "
"be younger than peg revision");
return log_fail_and_flush(err, b, conn, pool);
}
err = svn_repos_node_location_segments(b->repository->repos, abs_path,
peg_revision, start_rev, end_rev,
gls_receiver, (void *)conn,
authz_check_access_cb_func(b), &ab,
pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
return svn_error_compose_create(write_err, err);
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *svndiff_handler(void *baton, const char *data,
apr_size_t *len)
{
file_revs_baton_t *b = baton;
svn_string_t str;
str.data = data;
str.len = *len;
return svn_ra_svn__write_string(b->conn, b->pool, &str);
}
static svn_error_t *svndiff_close_handler(void *baton)
{
file_revs_baton_t *b = baton;
SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *file_rev_handler(void *baton, const char *path,
svn_revnum_t rev, apr_hash_t *rev_props,
svn_boolean_t merged_revision,
svn_txdelta_window_handler_t *d_handler,
void **d_baton,
apr_array_header_t *prop_diffs,
apr_pool_t *pool)
{
file_revs_baton_t *frb = baton;
svn_stream_t *stream;
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!",
path, rev));
SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props));
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!"));
SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs));
SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision));
frb->pool = pool;
if (d_handler)
{
stream = svn_stream_create(baton, pool);
svn_stream_set_write(stream, svndiff_handler);
svn_stream_set_close(stream, svndiff_close_handler);
if ( svn_ra_svn_compression_level(frb->conn) > 0
&& svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1))
svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1,
svn_ra_svn_compression_level(frb->conn), pool);
else
svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0,
svn_ra_svn_compression_level(frb->conn), pool);
}
else
SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_error_t *err, *write_err;
file_revs_baton_t frb;
svn_revnum_t start_rev, end_rev;
const char *path;
const char *full_path;
apr_uint64_t include_merged_revs_param;
svn_boolean_t include_merged_revisions;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B",
&path, &start_rev, &end_rev,
&include_merged_revs_param));
path = svn_relpath_canonicalize(path, pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
full_path = svn_fspath__join(b->repository->fs_path->data, path, pool);
if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER)
include_merged_revisions = FALSE;
else
include_merged_revisions = (svn_boolean_t) include_merged_revs_param;
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_file_revs(full_path, start_rev, end_rev,
include_merged_revisions,
pool)));
frb.conn = conn;
frb.pool = NULL;
err = svn_repos_get_file_revs2(b->repository->repos, full_path, start_rev,
end_rev, include_merged_revisions,
authz_check_access_cb_func(b), &ab,
file_rev_handler, &frb, pool);
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (write_err)
{
svn_error_clear(err);
return write_err;
}
SVN_CMD_ERR(err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *comment;
const char *full_path;
svn_boolean_t steal_lock;
svn_revnum_t current_rev;
svn_lock_t *l;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment,
&steal_lock, ¤t_rev));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
full_path, TRUE));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__lock_one_path(full_path, steal_lock, pool)));
SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->repos, full_path, NULL,
comment, 0, 0,
current_rev, steal_lock, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success"));
SVN_ERR(write_lock(conn, pool, l));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)"));
return SVN_NO_ERROR;
}
struct lock_result_t {
const svn_lock_t *lock;
svn_error_t *err;
};
struct lock_many_baton_t {
apr_hash_t *results;
apr_pool_t *pool;
};
static svn_error_t *
lock_many_cb(void *baton,
const char *path,
const svn_lock_t *fs_lock,
svn_error_t *fs_err,
apr_pool_t *pool)
{
struct lock_many_baton_t *b = baton;
struct lock_result_t *result = apr_palloc(b->pool,
sizeof(struct lock_result_t));
result->lock = fs_lock;
result->err = svn_error_dup(fs_err);
svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
return SVN_NO_ERROR;
}
static void
clear_lock_result_hash(apr_hash_t *results,
apr_pool_t *scratch_pool)
{
apr_hash_index_t *hi;
for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi))
{
struct lock_result_t *result = apr_hash_this_val(hi);
svn_error_clear(result->err);
}
}
static svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
apr_array_header_t *path_revs;
const char *comment;
svn_boolean_t steal_lock;
int i;
apr_pool_t *subpool;
svn_error_t *err, *write_err = SVN_NO_ERROR;
apr_hash_t *targets = apr_hash_make(pool);
apr_hash_t *authz_results = apr_hash_make(pool);
apr_hash_index_t *hi;
struct lock_many_baton_t lmb;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock,
&path_revs));
subpool = svn_pool_create(pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE));
for (i = 0; i < path_revs->nelts; ++i)
{
const char *path, *full_path;
svn_revnum_t current_rev;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
svn_ra_svn_item_t);
svn_fs_lock_target_t *target;
svn_pool_clear(subpool);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Lock requests should be list of lists");
SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path,
¤t_rev));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, subpool),
pool);
target = svn_fs_lock_target_create(NULL, current_rev, pool);
svn_hash_sets(targets, full_path, target);
}
SVN_ERR(log_command(b, conn, subpool, "%s",
svn_log__lock(targets, steal_lock, subpool)));
for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
{
const char *full_path = apr_hash_this_key(hi);
svn_pool_clear(subpool);
if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE))
{
struct lock_result_t *result
= apr_palloc(pool, sizeof(struct lock_result_t));
result->lock = NULL;
result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
NULL, NULL, b);
svn_hash_sets(authz_results, full_path, result);
svn_hash_sets(targets, full_path, NULL);
}
}
lmb.results = apr_hash_make(pool);
lmb.pool = pool;
err = svn_repos_fs_lock_many(b->repository->repos, targets,
comment, FALSE,
0,
steal_lock, lock_many_cb, &lmb,
pool, subpool);
for (i = 0; i < path_revs->nelts; ++i)
{
const char *path, *full_path;
svn_revnum_t current_rev;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i,
svn_ra_svn_item_t);
struct lock_result_t *result;
svn_pool_clear(subpool);
write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path,
¤t_rev);
if (write_err)
break;
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, subpool),
subpool);
result = svn_hash_gets(lmb.results, full_path);
if (!result)
result = svn_hash_gets(authz_results, full_path);
if (!result)
{
result = apr_palloc(pool, sizeof(struct lock_result_t));
result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
_("No result for '%s'."), path);
svn_hash_sets(lmb.results, full_path, result);
}
if (result->err)
write_err = svn_ra_svn__write_cmd_failure(conn, subpool,
result->err);
else
{
write_err = svn_ra_svn__write_tuple(conn, subpool,
"w!", "success");
if (!write_err)
write_err = write_lock(conn, subpool, result->lock);
if (!write_err)
write_err = svn_ra_svn__write_tuple(conn, subpool, "!");
}
if (write_err)
break;
}
clear_lock_result_hash(authz_results, subpool);
clear_lock_result_hash(lmb.results, subpool);
svn_pool_destroy(subpool);
if (!write_err)
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (!write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
SVN_ERR(write_err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path, *token, *full_path;
svn_boolean_t break_lock;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token,
&break_lock));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write,
full_path, ! break_lock));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__unlock_one_path(full_path, break_lock, pool)));
SVN_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token,
break_lock, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
svn_boolean_t break_lock;
apr_array_header_t *unlock_tokens;
int i;
apr_pool_t *subpool;
svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR;
apr_hash_t *targets = apr_hash_make(pool);
apr_hash_t *authz_results = apr_hash_make(pool);
apr_hash_index_t *hi;
struct lock_many_baton_t lmb;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock,
&unlock_tokens));
SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock));
subpool = svn_pool_create(pool);
for (i = 0; i < unlock_tokens->nelts; i++)
{
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
svn_ra_svn_item_t);
const char *path, *full_path, *token;
svn_pool_clear(subpool);
if (item->kind != SVN_RA_SVN_LIST)
return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL,
"Unlock request should be a list of lists");
SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
&token));
if (!token)
token = "";
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, subpool),
pool);
svn_hash_sets(targets, full_path, token);
}
SVN_ERR(log_command(b, conn, subpool, "%s",
svn_log__unlock(targets, break_lock, subpool)));
for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi))
{
const char *full_path = apr_hash_this_key(hi);
svn_pool_clear(subpool);
if (! lookup_access(subpool, b, svn_authz_write, full_path,
! break_lock))
{
struct lock_result_t *result
= apr_palloc(pool, sizeof(struct lock_result_t));
result->lock = NULL;
result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED,
NULL, NULL, b);
svn_hash_sets(authz_results, full_path, result);
svn_hash_sets(targets, full_path, NULL);
}
}
lmb.results = apr_hash_make(pool);
lmb.pool = pool;
err = svn_repos_fs_unlock_many(b->repository->repos, targets,
break_lock, lock_many_cb, &lmb,
pool, subpool);
for (i = 0; i < unlock_tokens->nelts; ++i)
{
const char *path, *token, *full_path;
svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i,
svn_ra_svn_item_t);
struct lock_result_t *result;
svn_pool_clear(subpool);
write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path,
&token);
if (write_err)
break;
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, subpool),
pool);
result = svn_hash_gets(lmb.results, full_path);
if (!result)
result = svn_hash_gets(authz_results, full_path);
if (!result)
{
result = apr_palloc(pool, sizeof(struct lock_result_t));
result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0,
_("No result for '%s'."), path);
svn_hash_sets(lmb.results, full_path, result);
}
if (result->err)
write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err);
else
write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success",
path);
if (write_err)
break;
}
clear_lock_result_hash(authz_results, subpool);
clear_lock_result_hash(lmb.results, subpool);
svn_pool_destroy(subpool);
if (!write_err)
write_err = svn_ra_svn__write_word(conn, pool, "done");
if (! write_err)
SVN_CMD_ERR(err);
svn_error_clear(err);
SVN_ERR(write_err);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *full_path;
svn_lock_t *l;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(must_have_access(conn, pool, b, svn_authz_read,
full_path, FALSE));
SVN_ERR(log_command(b, conn, pool, "get-lock %s",
svn_path_uri_encode(full_path, pool)));
SVN_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
if (l)
SVN_ERR(write_lock(conn, pool, l));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
server_baton_t *b = baton;
const char *path;
const char *full_path;
const char *depth_word;
svn_depth_t depth;
apr_hash_t *locks;
apr_hash_index_t *hi;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word));
depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity;
if ((depth != svn_depth_empty) &&
(depth != svn_depth_files) &&
(depth != svn_depth_immediates) &&
(depth != svn_depth_infinity))
{
err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL,
"Invalid 'depth' specified in get-locks request");
return log_fail_and_flush(err, b, conn, pool);
}
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(log_command(b, conn, pool, "get-locks %s",
svn_path_uri_encode(full_path, pool)));
SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->repos,
full_path, depth,
authz_check_access_cb_func(b), &ab,
pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success"));
for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi))
{
svn_lock_t *l = apr_hash_this_val(hi);
SVN_ERR(write_lock(conn, pool, l));
}
SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))"));
return SVN_NO_ERROR;
}
static svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn,
server_baton_t *b,
svn_revnum_t rev,
svn_revnum_t low_water_mark,
svn_boolean_t send_deltas,
apr_pool_t *pool)
{
const svn_delta_editor_t *editor;
void *edit_baton;
svn_fs_root_t *root;
svn_error_t *err;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(log_command(b, conn, pool,
svn_log__replay(b->repository->fs_path->data, rev,
pool)));
svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL);
err = svn_fs_revision_root(&root, b->repository->fs, rev, pool);
if (! err)
err = svn_repos_replay2(root, b->repository->fs_path->data,
low_water_mark, send_deltas, editor, edit_baton,
authz_check_access_cb_func(b), &ab, pool);
if (err)
svn_error_clear(editor->abort_edit(edit_baton, pool));
SVN_CMD_ERR(err);
return svn_ra_svn__write_cmd_finish_replay(conn, pool);
}
static svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_revnum_t rev, low_water_mark;
svn_boolean_t send_deltas;
server_baton_t *b = baton;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark,
&send_deltas));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
send_deltas, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool,
apr_array_header_t *params, void *baton)
{
svn_revnum_t start_rev, end_rev, rev, low_water_mark;
svn_boolean_t send_deltas;
server_baton_t *b = baton;
apr_pool_t *iterpool;
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev,
&end_rev, &low_water_mark,
&send_deltas));
SVN_ERR(trivial_auth_request(conn, pool, b));
iterpool = svn_pool_create(pool);
for (rev = start_rev; rev <= end_rev; rev++)
{
apr_hash_t *props;
svn_pool_clear(iterpool);
SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props,
b->repository->repos, rev,
authz_check_access_cb_func(b),
&ab,
iterpool));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops"));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)"));
SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark,
send_deltas, iterpool));
}
svn_pool_destroy(iterpool);
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, ""));
return SVN_NO_ERROR;
}
static svn_error_t *
get_deleted_rev(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t peg_revision;
svn_revnum_t end_revision;
svn_revnum_t revision_deleted;
SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr",
&path, &peg_revision, &end_revision));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, pool), pool);
SVN_ERR(log_command(b, conn, pool, "get-deleted-rev"));
SVN_ERR(trivial_auth_request(conn, pool, b));
SVN_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision,
end_revision, &revision_deleted, pool));
SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted));
return SVN_NO_ERROR;
}
static svn_error_t *
get_inherited_props(svn_ra_svn_conn_t *conn,
apr_pool_t *pool,
apr_array_header_t *params,
void *baton)
{
server_baton_t *b = baton;
const char *path, *full_path;
svn_revnum_t rev;
svn_fs_root_t *root;
apr_array_header_t *inherited_props;
int i;
apr_pool_t *iterpool = svn_pool_create(pool);
authz_baton_t ab;
ab.server = b;
ab.conn = conn;
SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev));
full_path = svn_fspath__join(b->repository->fs_path->data,
svn_relpath_canonicalize(path, iterpool),
pool);
SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read,
full_path, FALSE));
if (!SVN_IS_VALID_REVNUM(rev))
SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool));
SVN_ERR(log_command(b, conn, pool, "%s",
svn_log__get_inherited_props(full_path, rev,
iterpool)));
SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool));
SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success"));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!"));
for (i = 0; i < inherited_props->nelts; i++)
{
svn_prop_inherited_item_t *iprop =
APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *);
svn_pool_clear(iterpool);
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!",
iprop->path_or_url));
SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash));
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!",
iprop->path_or_url));
}
SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))"));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
}
static const svn_ra_svn_cmd_entry_t main_commands[] = {
{ "reparent", reparent },
{ "get-latest-rev", get_latest_rev },
{ "get-dated-rev", get_dated_rev },
{ "change-rev-prop", change_rev_prop },
{ "change-rev-prop2",change_rev_prop2 },
{ "rev-proplist", rev_proplist },
{ "rev-prop", rev_prop },
{ "commit", commit },
{ "get-file", get_file },
{ "get-dir", get_dir },
{ "update", update },
{ "switch", switch_cmd },
{ "status", status },
{ "diff", diff },
{ "get-mergeinfo", get_mergeinfo },
{ "log", log_cmd },
{ "check-path", check_path },
{ "stat", stat_cmd },
{ "get-locations", get_locations },
{ "get-location-segments", get_location_segments },
{ "get-file-revs", get_file_revs },
{ "lock", lock },
{ "lock-many", lock_many },
{ "unlock", unlock },
{ "unlock-many", unlock_many },
{ "get-lock", get_lock },
{ "get-locks", get_locks },
{ "replay", replay },
{ "replay-range", replay_range },
{ "get-deleted-rev", get_deleted_rev },
{ "get-iprops", get_inherited_props },
{ NULL }
};
static const char *skip_scheme_part(const char *url)
{
if (strncmp(url, "svn", 3) != 0)
return NULL;
url += 3;
if (*url == '+')
url += strcspn(url, ":");
if (strncmp(url, "://", 3) != 0)
return NULL;
return url + 3;
}
static svn_boolean_t
repos_path_valid(const char *path)
{
const char *s = path;
while (*s)
{
while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR)
++path;
#ifdef WIN32
if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s))
return FALSE;
#else
if (path - s == 2 && s[0] == '.' && s[1] == '.')
return FALSE;
#endif
while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR))
++path;
s = path;
}
return TRUE;
}
static svn_error_t *
find_repos(const char *url,
const char *root,
svn_boolean_t vhost,
svn_boolean_t read_only,
svn_config_t *cfg,
repository_t *repository,
svn_repos__config_pool_t *config_pool,
svn_repos__authz_pool_t *authz_pool,
apr_hash_t *fs_config,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
const char *path, *full_path, *fs_path, *hooks_env;
svn_stringbuf_t *url_buf;
path = skip_scheme_part(url);
if (path == NULL)
return svn_error_createf(SVN_ERR_BAD_URL, NULL,
"Non-svn URL passed to svn server: '%s'", url);
if (! vhost)
{
path = strchr(path, '/');
if (path == NULL)
path = "";
}
path = svn_relpath_canonicalize(path, scratch_pool);
path = svn_path_uri_decode(path, scratch_pool);
if (!repos_path_valid(path))
return svn_error_create(SVN_ERR_BAD_FILENAME, NULL,
"Couldn't determine repository path");
full_path = svn_dirent_join(svn_dirent_canonicalize(root, scratch_pool),
path, scratch_pool);
repository->repos_root = svn_repos_find_root_path(full_path, result_pool);
if (!repository->repos_root)
return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL,
"No repository found in '%s'", url);
SVN_ERR(svn_repos_open3(&repository->repos, repository->repos_root,
fs_config, result_pool, scratch_pool));
SVN_ERR(svn_repos_remember_client_capabilities(repository->repos,
repository->capabilities));
repository->fs = svn_repos_fs(repository->repos);
fs_path = full_path + strlen(repository->repos_root);
repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/",
result_pool);
url_buf = svn_stringbuf_create(url, result_pool);
svn_path_remove_components(url_buf,
svn_path_component_count(repository->fs_path->data));
repository->repos_url = url_buf->data;
repository->authz_repos_name = svn_dirent_is_child(root,
repository->repos_root,
result_pool);
if (repository->authz_repos_name == NULL)
repository->repos_name = svn_dirent_basename(repository->repos_root,
result_pool);
else
repository->repos_name = repository->authz_repos_name;
repository->repos_name = svn_path_uri_encode(repository->repos_name,
result_pool);
if (NULL == cfg)
{
repository->base = svn_repos_conf_dir(repository->repos, result_pool);
SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool,
svn_repos_svnserve_conf
(repository->repos, result_pool),
FALSE, FALSE, repository->repos,
result_pool));
}
SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool));
SVN_ERR(load_authz_config(repository, repository->repos_root, cfg,
authz_pool, result_pool));
#ifdef SVN_HAVE_SASL
{
const char *val;
SVN_ERR(svn_config_get_bool(cfg, &repository->use_sasl,
SVN_CONFIG_SECTION_SASL,
SVN_CONFIG_OPTION_USE_SASL, FALSE));
svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
SVN_CONFIG_OPTION_MIN_SSF, "0");
SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val));
svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL,
SVN_CONFIG_OPTION_MAX_SSF, "256");
SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val));
}
#endif
SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool));
svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_REALM, repository->realm);
repository->realm = apr_pstrdup(result_pool, repository->realm);
set_access(repository, cfg, read_only);
svn_config_get(cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL,
SVN_CONFIG_OPTION_HOOKS_ENV, NULL);
if (hooks_env)
hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool);
SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, scratch_pool));
repository->hooks_env = apr_pstrdup(result_pool, hooks_env);
return SVN_NO_ERROR;
}
static const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool)
{
if (!params->tunnel)
return NULL;
if (params->tunnel_user)
return params->tunnel_user;
return svn_user_get_name(pool);
}
static void
fs_warning_func(void *baton, svn_error_t *err)
{
fs_warning_baton_t *b = baton;
log_error(err, b->server);
}
static const char *
get_normalized_repo_rel_path(void *baton,
const char *path,
apr_pool_t *pool)
{
server_baton_t *sb = baton;
if (svn_path_is_url(path))
{
path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool);
path = svn_fspath__canonicalize(path, pool);
}
else
{
if ((path)[0] != '/')
path = svn_fspath__join(sb->repository->fs_path->data, path, pool);
}
return path;
}
static svn_error_t *
get_revision_root(svn_fs_root_t **fs_root,
void *baton,
svn_revnum_t revision,
apr_pool_t *pool)
{
server_baton_t *sb = baton;
if (!SVN_IS_VALID_REVNUM(revision))
SVN_ERR(svn_fs_youngest_rev(&revision, sb->repository->fs, pool));
SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_props_func(apr_hash_t **props,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_fs_root_t *fs_root;
svn_error_t *err;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
err = svn_fs_node_proplist(props, fs_root, path, result_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*props = apr_hash_make(result_pool);
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_kind_func(svn_node_kind_t *kind,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *scratch_pool)
{
svn_fs_root_t *fs_root;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool));
return SVN_NO_ERROR;
}
static svn_error_t *
fetch_base_func(const char **filename,
void *baton,
const char *path,
svn_revnum_t base_revision,
apr_pool_t *result_pool,
apr_pool_t *scratch_pool)
{
svn_stream_t *contents;
svn_stream_t *file_stream;
const char *tmp_filename;
svn_fs_root_t *fs_root;
svn_error_t *err;
path = get_normalized_repo_rel_path(baton, path, scratch_pool);
SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool));
err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool);
if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND)
{
svn_error_clear(err);
*filename = NULL;
return SVN_NO_ERROR;
}
else if (err)
return svn_error_trace(err);
SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL,
svn_io_file_del_on_pool_cleanup,
scratch_pool, scratch_pool));
SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool));
*filename = apr_pstrdup(result_pool, tmp_filename);
return SVN_NO_ERROR;
}
client_info_t *
get_client_info(svn_ra_svn_conn_t *conn,
serve_params_t *params,
apr_pool_t *pool)
{
client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info));
client_info->tunnel = params->tunnel;
client_info->tunnel_user = get_tunnel_user(params, pool);
client_info->user = NULL;
client_info->authz_user = NULL;
client_info->remote_host = svn_ra_svn_conn_remote_host(conn);
return client_info;
}
static svn_error_t *
construct_server_baton(server_baton_t **baton,
svn_ra_svn_conn_t *conn,
serve_params_t *params,
apr_pool_t *scratch_pool)
{
svn_error_t *err, *io_err;
apr_uint64_t ver;
const char *client_url, *ra_client_string, *client_string;
apr_array_header_t *caplist;
apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn);
server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b));
fs_warning_baton_t *warn_baton;
svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool);
b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository));
b->repository->username_case = params->username_case;
b->repository->base = params->base;
b->repository->pwdb = NULL;
b->repository->authzdb = NULL;
b->repository->realm = NULL;
b->repository->use_sasl = FALSE;
b->read_only = params->read_only;
b->pool = conn_pool;
b->vhost = params->vhost;
b->logger = params->logger;
b->client_info = get_client_info(conn, params, conn_pool);
if (params->compression_level > 0)
SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
"nn()(wwwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_SVNDIFF1,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
SVN_RA_SVN_CAP_COMMIT_REVPROPS,
SVN_RA_SVN_CAP_DEPTH,
SVN_RA_SVN_CAP_LOG_REVPROPS,
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
));
else
SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool,
"nn()(wwwwwwwwww)",
(apr_uint64_t) 2, (apr_uint64_t) 2,
SVN_RA_SVN_CAP_EDIT_PIPELINE,
SVN_RA_SVN_CAP_ABSENT_ENTRIES,
SVN_RA_SVN_CAP_COMMIT_REVPROPS,
SVN_RA_SVN_CAP_DEPTH,
SVN_RA_SVN_CAP_LOG_REVPROPS,
SVN_RA_SVN_CAP_ATOMIC_REVPROPS,
SVN_RA_SVN_CAP_PARTIAL_REPLAY,
SVN_RA_SVN_CAP_INHERITED_PROPS,
SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS,
SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE
));
SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_pool, "nlc?c(?c)",
&ver, &caplist, &client_url,
&ra_client_string,
&client_string));
if (ver != 2)
return SVN_NO_ERROR;
client_url = svn_uri_canonicalize(client_url, conn_pool);
SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist));
if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE))
return SVN_NO_ERROR;
{
int i;
svn_ra_svn_item_t *item;
b->repository->capabilities = apr_array_make(conn_pool, 1,
sizeof(const char *));
for (i = 0; i < caplist->nelts; i++)
{
item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t);
if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0)
{
APR_ARRAY_PUSH(b->repository->capabilities, const char *)
= SVN_RA_CAPABILITY_MERGEINFO;
}
if (cap_log->len > 0)
svn_stringbuf_appendcstr(cap_log, " ");
svn_stringbuf_appendcstr(cap_log, item->u.word);
}
}
err = handle_config_error(find_repos(client_url, params->root, b->vhost,
b->read_only, params->cfg,
b->repository, params->config_pool,
params->authz_pool, params->fs_config,
conn_pool, scratch_pool),
b);
if (!err)
{
if (b->repository->anon_access == NO_ACCESS
&& (b->repository->auth_access == NO_ACCESS
|| (!b->client_info->tunnel_user && !b->repository->pwdb
&& !b->repository->use_sasl)))
err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"No access allowed to this repository",
b);
}
if (!err)
{
SVN_ERR(auth_request(conn, scratch_pool, b, READ_ACCESS, FALSE));
if (current_access(b) == NO_ACCESS)
err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL,
"Not authorized for access", b);
}
if (err)
{
log_error(err, b);
io_err = svn_ra_svn__write_cmd_failure(conn, scratch_pool, err);
svn_error_clear(err);
SVN_ERR(io_err);
return svn_ra_svn__flush(conn, scratch_pool);
}
SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid,
conn_pool));
{
svn_boolean_t supports_mergeinfo;
SVN_ERR(svn_repos_has_capability(b->repository->repos,
&supports_mergeinfo,
SVN_REPOS_CAPABILITY_MERGEINFO,
scratch_pool));
SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!",
"success", b->repository->uuid,
b->repository->repos_url));
if (supports_mergeinfo)
SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool,
SVN_RA_SVN_CAP_MERGEINFO));
SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))"));
SVN_ERR(svn_ra_svn__flush(conn, scratch_pool));
}
if (ra_client_string == NULL || ra_client_string[0] == '\0')
ra_client_string = "-";
else
ra_client_string = svn_path_uri_encode(ra_client_string, scratch_pool);
if (client_string == NULL || client_string[0] == '\0')
client_string = "-";
else
client_string = svn_path_uri_encode(client_string, scratch_pool);
SVN_ERR(log_command(b, conn, scratch_pool,
"open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s",
ver, cap_log->data,
svn_path_uri_encode(b->repository->fs_path->data,
scratch_pool),
ra_client_string, client_string));
warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton));
warn_baton->server = b;
warn_baton->conn = conn;
svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton);
{
svn_delta_shim_callbacks_t *callbacks =
svn_delta_shim_callbacks_default(conn_pool);
callbacks->fetch_base_func = fetch_base_func;
callbacks->fetch_props_func = fetch_props_func;
callbacks->fetch_kind_func = fetch_kind_func;
callbacks->fetch_baton = b;
SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks));
}
*baton = b;
return SVN_NO_ERROR;
}
svn_error_t *
serve_interruptable(svn_boolean_t *terminate_p,
connection_t *connection,
svn_boolean_t (* is_busy)(connection_t *),
apr_pool_t *pool)
{
svn_boolean_t terminate = FALSE;
svn_error_t *err = NULL;
const svn_ra_svn_cmd_entry_t *command;
apr_pool_t *iterpool = svn_pool_create(pool);
apr_hash_t *cmd_hash = apr_hash_make(pool);
for (command = main_commands; command->cmdname; command++)
svn_hash_sets(cmd_hash, command->cmdname, command);
if (! connection->conn)
{
apr_status_t ar;
ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1);
if (ar)
{
}
connection->conn
= svn_ra_svn_create_conn4(connection->usock, NULL, NULL,
connection->params->compression_level,
connection->params->zero_copy_limit,
connection->params->error_check_interval,
connection->pool);
err = construct_server_baton(&connection->baton, connection->conn,
connection->params, pool);
}
if (err)
terminate = TRUE;
while (!terminate && !err)
{
svn_pool_clear(iterpool);
if (is_busy && is_busy(connection))
{
svn_boolean_t has_command;
err = svn_ra_svn__has_command(&has_command, &terminate,
connection->conn, iterpool);
if (!err && has_command)
err = svn_ra_svn__handle_command(&terminate, cmd_hash,
connection->baton,
connection->conn,
FALSE, iterpool);
break;
}
else
{
err = svn_ra_svn__handle_command(&terminate, cmd_hash,
connection->baton,
connection->conn,
FALSE, iterpool);
}
}
svn_pool_destroy(iterpool);
if (terminate_p)
*terminate_p = terminate;
return svn_error_trace(err);
}
svn_error_t *serve(svn_ra_svn_conn_t *conn,
serve_params_t *params,
apr_pool_t *pool)
{
server_baton_t *baton = NULL;
SVN_ERR(construct_server_baton(&baton, conn, params, pool));
return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE);
}