1251881Speter/* 2251881Speter * serve.c : Functions for serving the Subversion protocol 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter 27251881Speter#include <limits.h> /* for UINT_MAX */ 28251881Speter#include <stdarg.h> 29251881Speter 30251881Speter#define APR_WANT_STRFUNC 31251881Speter#include <apr_want.h> 32251881Speter#include <apr_general.h> 33251881Speter#include <apr_lib.h> 34251881Speter#include <apr_strings.h> 35251881Speter 36251881Speter#include "svn_compat.h" 37251881Speter#include "svn_private_config.h" /* For SVN_PATH_LOCAL_SEPARATOR */ 38251881Speter#include "svn_hash.h" 39251881Speter#include "svn_types.h" 40251881Speter#include "svn_string.h" 41251881Speter#include "svn_pools.h" 42251881Speter#include "svn_error.h" 43251881Speter#include "svn_ra.h" /* for SVN_RA_CAPABILITY_* */ 44251881Speter#include "svn_ra_svn.h" 45251881Speter#include "svn_repos.h" 46251881Speter#include "svn_dirent_uri.h" 47251881Speter#include "svn_path.h" 48251881Speter#include "svn_time.h" 49251881Speter#include "svn_config.h" 50251881Speter#include "svn_props.h" 51251881Speter#include "svn_mergeinfo.h" 52251881Speter#include "svn_user.h" 53251881Speter 54251881Speter#include "private/svn_log.h" 55251881Speter#include "private/svn_mergeinfo_private.h" 56251881Speter#include "private/svn_ra_svn_private.h" 57251881Speter#include "private/svn_fspath.h" 58251881Speter 59251881Speter#ifdef HAVE_UNISTD_H 60251881Speter#include <unistd.h> /* For getpid() */ 61251881Speter#endif 62251881Speter 63251881Speter#include "server.h" 64289180Speter#include "logger.h" 65251881Speter 66251881Spetertypedef struct commit_callback_baton_t { 67251881Speter apr_pool_t *pool; 68251881Speter svn_revnum_t *new_rev; 69251881Speter const char **date; 70251881Speter const char **author; 71251881Speter const char **post_commit_err; 72251881Speter} commit_callback_baton_t; 73251881Speter 74251881Spetertypedef struct report_driver_baton_t { 75251881Speter server_baton_t *sb; 76251881Speter const char *repos_url; /* Decoded repository URL. */ 77251881Speter void *report_baton; 78251881Speter svn_error_t *err; 79251881Speter /* so update() can distinguish checkout from update in logging */ 80251881Speter int entry_counter; 81251881Speter svn_boolean_t only_empty_entries; 82251881Speter /* for diff() logging */ 83251881Speter svn_revnum_t *from_rev; 84251881Speter} report_driver_baton_t; 85251881Speter 86251881Spetertypedef struct log_baton_t { 87251881Speter const char *fs_path; 88251881Speter svn_ra_svn_conn_t *conn; 89251881Speter int stack_depth; 90362181Sdim 91362181Sdim /* Set to TRUE when at least one changed path has been sent. */ 92362181Sdim svn_boolean_t started; 93251881Speter} log_baton_t; 94251881Speter 95251881Spetertypedef struct file_revs_baton_t { 96251881Speter svn_ra_svn_conn_t *conn; 97251881Speter apr_pool_t *pool; /* Pool provided in the handler call. */ 98251881Speter} file_revs_baton_t; 99251881Speter 100251881Spetertypedef struct fs_warning_baton_t { 101251881Speter server_baton_t *server; 102251881Speter svn_ra_svn_conn_t *conn; 103251881Speter} fs_warning_baton_t; 104251881Speter 105251881Spetertypedef struct authz_baton_t { 106251881Speter server_baton_t *server; 107251881Speter svn_ra_svn_conn_t *conn; 108251881Speter} authz_baton_t; 109251881Speter 110362181Sdim/* Log an error. */ 111251881Speterstatic void 112362181Sdimlog_error(const svn_error_t *err, server_baton_t *server) 113251881Speter{ 114289180Speter logger__log_error(server->logger, err, server->repository, 115289180Speter server->client_info); 116251881Speter} 117251881Speter 118362181Sdim/* Log a warning. */ 119362181Sdimstatic void 120362181Sdimlog_warning(const svn_error_t *err, server_baton_t *server) 121362181Sdim{ 122362181Sdim logger__log_warning(server->logger, err, server->repository, 123362181Sdim server->client_info); 124362181Sdim} 125362181Sdim 126251881Speter/* svn_error_create() a new error, log_server_error() it, and 127251881Speter return it. */ 128251881Speterstatic svn_error_t * 129251881Spetererror_create_and_log(apr_status_t apr_err, svn_error_t *child, 130289180Speter const char *message, server_baton_t *server) 131251881Speter{ 132251881Speter svn_error_t *err = svn_error_create(apr_err, child, message); 133289180Speter log_error(err, server); 134251881Speter return err; 135251881Speter} 136251881Speter 137251881Speter/* Log a failure ERR, transmit ERR back to the client (as part of a 138251881Speter "failure" notification), consume ERR, and flush the connection. */ 139251881Speterstatic svn_error_t * 140251881Speterlog_fail_and_flush(svn_error_t *err, server_baton_t *server, 141251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool) 142251881Speter{ 143251881Speter svn_error_t *io_err; 144251881Speter 145289180Speter log_error(err, server); 146251881Speter io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 147251881Speter svn_error_clear(err); 148251881Speter SVN_ERR(io_err); 149251881Speter return svn_ra_svn__flush(conn, pool); 150251881Speter} 151251881Speter 152251881Speter/* Log a client command. */ 153251881Speterstatic svn_error_t *log_command(server_baton_t *b, 154251881Speter svn_ra_svn_conn_t *conn, 155251881Speter apr_pool_t *pool, 156251881Speter const char *fmt, ...) 157251881Speter{ 158251881Speter const char *remote_host, *timestr, *log, *line; 159251881Speter va_list ap; 160251881Speter apr_size_t nbytes; 161251881Speter 162289180Speter if (b->logger == NULL) 163251881Speter return SVN_NO_ERROR; 164251881Speter 165251881Speter remote_host = svn_ra_svn_conn_remote_host(conn); 166251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 167251881Speter 168251881Speter va_start(ap, fmt); 169251881Speter log = apr_pvsprintf(pool, fmt, ap); 170251881Speter va_end(ap); 171251881Speter 172251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 173251881Speter " %s %s %s %s %s" APR_EOL_STR, 174251881Speter getpid(), timestr, 175251881Speter (remote_host ? remote_host : "-"), 176289180Speter (b->client_info->user ? b->client_info->user : "-"), 177289180Speter b->repository->repos_name, log); 178251881Speter nbytes = strlen(line); 179251881Speter 180289180Speter return logger__write(b->logger, line, nbytes); 181251881Speter} 182251881Speter 183251881Speter/* Log an authz failure */ 184251881Speterstatic svn_error_t * 185251881Speterlog_authz_denied(const char *path, 186251881Speter svn_repos_authz_access_t required, 187251881Speter server_baton_t *b, 188251881Speter apr_pool_t *pool) 189251881Speter{ 190251881Speter const char *timestr, *remote_host, *line; 191251881Speter 192289180Speter if (!b->logger) 193251881Speter return SVN_NO_ERROR; 194251881Speter 195289180Speter if (!b->client_info || !b->client_info->user) 196251881Speter return SVN_NO_ERROR; 197251881Speter 198251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 199289180Speter remote_host = b->client_info->remote_host; 200251881Speter 201251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 202251881Speter " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR, 203251881Speter getpid(), timestr, 204251881Speter (remote_host ? remote_host : "-"), 205289180Speter b->client_info->user, 206289180Speter b->repository->repos_name, 207251881Speter (required & svn_authz_recursive ? "recursive " : ""), 208251881Speter (required & svn_authz_write ? "write" : "read"), 209251881Speter (path && path[0] ? path : "/")); 210251881Speter 211289180Speter return logger__write(b->logger, line, strlen(line)); 212251881Speter} 213251881Speter 214289180Speter/* If CFG specifies a path to the password DB, read that DB through 215289180Speter * CONFIG_POOL and store it in REPOSITORY->PWDB. 216289180Speter */ 217289180Speterstatic svn_error_t * 218289180Speterload_pwdb_config(repository_t *repository, 219289180Speter svn_config_t *cfg, 220289180Speter svn_repos__config_pool_t *config_pool, 221289180Speter apr_pool_t *pool) 222251881Speter{ 223251881Speter const char *pwdb_path; 224251881Speter svn_error_t *err; 225251881Speter 226289180Speter svn_config_get(cfg, &pwdb_path, 227289180Speter SVN_CONFIG_SECTION_GENERAL, 228251881Speter SVN_CONFIG_OPTION_PASSWORD_DB, NULL); 229251881Speter 230289180Speter repository->pwdb = NULL; 231251881Speter if (pwdb_path) 232251881Speter { 233251881Speter pwdb_path = svn_dirent_internal_style(pwdb_path, pool); 234289180Speter pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool); 235251881Speter 236362181Sdim err = svn_repos__config_pool_get(&repository->pwdb, config_pool, 237362181Sdim pwdb_path, TRUE, 238289180Speter repository->repos, pool); 239251881Speter if (err) 240251881Speter { 241251881Speter /* Because it may be possible to read the pwdb file with some 242251881Speter access methods and not others, ignore errors reading the pwdb 243251881Speter file and just don't present password authentication as an 244251881Speter option. Also, some authentications (e.g. --tunnel) can 245251881Speter proceed without it anyway. 246251881Speter 247251881Speter ### Not entirely sure why SVN_ERR_BAD_FILENAME is checked 248251881Speter ### for here. That seems to have been introduced in r856914, 249251881Speter ### and only in r870942 was the APR_EACCES check introduced. */ 250251881Speter if (err->apr_err != SVN_ERR_BAD_FILENAME 251251881Speter && ! APR_STATUS_IS_EACCES(err->apr_err)) 252251881Speter { 253289180Speter return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL); 254251881Speter } 255251881Speter else 256251881Speter /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */ 257251881Speter svn_error_clear(err); 258251881Speter } 259251881Speter } 260251881Speter 261251881Speter return SVN_NO_ERROR; 262251881Speter} 263251881Speter 264251881Speter/* Canonicalize *ACCESS_FILE based on the type of argument. Results are 265289180Speter * placed in *ACCESS_FILE. REPOSITORY is used to convert relative paths to 266251881Speter * absolute paths rooted at the server root. REPOS_ROOT is used to calculate 267251881Speter * an absolute URL for repos-relative URLs. */ 268251881Speterstatic svn_error_t * 269289180Spetercanonicalize_access_file(const char **access_file, repository_t *repository, 270251881Speter const char *repos_root, apr_pool_t *pool) 271251881Speter{ 272251881Speter if (svn_path_is_url(*access_file)) 273251881Speter { 274362181Sdim const char *canonical_url; 275362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file, 276362181Sdim pool, pool)); 277362181Sdim *access_file = canonical_url; 278251881Speter } 279251881Speter else if (svn_path_is_repos_relative_url(*access_file)) 280251881Speter { 281251881Speter const char *repos_root_url; 282362181Sdim const char *canonical_url; 283251881Speter 284251881Speter SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root, 285251881Speter pool)); 286251881Speter SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file, 287251881Speter repos_root_url, pool)); 288362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, *access_file, 289362181Sdim pool, pool)); 290362181Sdim *access_file = canonical_url; 291251881Speter } 292251881Speter else 293251881Speter { 294251881Speter *access_file = svn_dirent_internal_style(*access_file, pool); 295289180Speter *access_file = svn_dirent_join(repository->base, *access_file, pool); 296251881Speter } 297251881Speter 298251881Speter return SVN_NO_ERROR; 299251881Speter} 300251881Speter 301362181Sdim/* Load the authz database for the listening server based on the entries 302362181Sdim in the SERVER struct. 303289180Speter 304289180Speter SERVER and CONN must not be NULL. The real errors will be logged with 305289180Speter SERVER and CONN but return generic errors to the client. */ 306289180Speterstatic svn_error_t * 307289180Speterload_authz_config(repository_t *repository, 308289180Speter const char *repos_root, 309289180Speter svn_config_t *cfg, 310362181Sdim svn_repos_authz_warning_func_t warning_func, 311362181Sdim void *warning_baton, 312362181Sdim apr_pool_t *result_pool, 313362181Sdim apr_pool_t *scratch_pool) 314251881Speter{ 315251881Speter const char *authzdb_path; 316251881Speter const char *groupsdb_path; 317251881Speter svn_error_t *err; 318251881Speter 319251881Speter /* Read authz configuration. */ 320289180Speter svn_config_get(cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL, 321251881Speter SVN_CONFIG_OPTION_AUTHZ_DB, NULL); 322251881Speter 323289180Speter svn_config_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL, 324251881Speter SVN_CONFIG_OPTION_GROUPS_DB, NULL); 325251881Speter 326251881Speter if (authzdb_path) 327251881Speter { 328251881Speter const char *case_force_val; 329251881Speter 330251881Speter /* Canonicalize and add the base onto the authzdb_path (if needed). */ 331289180Speter err = canonicalize_access_file(&authzdb_path, repository, 332362181Sdim repos_root, scratch_pool); 333251881Speter 334251881Speter /* Same for the groupsdb_path if it is present. */ 335251881Speter if (groupsdb_path && !err) 336289180Speter err = canonicalize_access_file(&groupsdb_path, repository, 337362181Sdim repos_root, scratch_pool); 338251881Speter 339251881Speter if (!err) 340362181Sdim err = svn_repos_authz_read4(&repository->authzdb, authzdb_path, 341362181Sdim groupsdb_path, TRUE, repository->repos, 342362181Sdim warning_func, warning_baton, 343362181Sdim result_pool, scratch_pool); 344251881Speter 345251881Speter if (err) 346289180Speter return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL); 347251881Speter 348251881Speter /* Are we going to be case-normalizing usernames when we consult 349251881Speter * this authz file? */ 350289180Speter svn_config_get(cfg, &case_force_val, 351289180Speter SVN_CONFIG_SECTION_GENERAL, 352251881Speter SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL); 353251881Speter if (case_force_val) 354251881Speter { 355251881Speter if (strcmp(case_force_val, "upper") == 0) 356289180Speter repository->username_case = CASE_FORCE_UPPER; 357251881Speter else if (strcmp(case_force_val, "lower") == 0) 358289180Speter repository->username_case = CASE_FORCE_LOWER; 359251881Speter else 360289180Speter repository->username_case = CASE_ASIS; 361251881Speter } 362251881Speter } 363251881Speter else 364251881Speter { 365289180Speter repository->authzdb = NULL; 366289180Speter repository->username_case = CASE_ASIS; 367251881Speter } 368251881Speter 369251881Speter return SVN_NO_ERROR; 370251881Speter} 371251881Speter 372289180Speter/* If ERROR is a AUTH* error as returned by load_pwdb_config or 373289180Speter * load_authz_config, write it to SERVER's log file. 374289180Speter * Return a sanitized version of ERROR. 375289180Speter */ 376289180Speterstatic svn_error_t * 377289180Speterhandle_config_error(svn_error_t *error, 378289180Speter server_baton_t *server) 379289180Speter{ 380289180Speter if ( error 381289180Speter && ( error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG 382289180Speter || error->apr_err == SVN_ERR_AUTHN_FAILED)) 383289180Speter { 384289180Speter apr_status_t apr_err = error->apr_err; 385289180Speter log_error(error, server); 386289180Speter 387289180Speter /* Now that we've logged the error, clear it and return a 388289180Speter * nice, generic error to the user: 389362181Sdim * https://issues.apache.org/jira/browse/SVN-2271 */ 390289180Speter svn_error_clear(error); 391289180Speter return svn_error_create(apr_err, NULL, NULL); 392289180Speter } 393289180Speter 394289180Speter return error; 395289180Speter} 396289180Speter 397251881Speter/* Set *FS_PATH to the portion of URL that is the path within the 398251881Speter repository, if URL is inside REPOS_URL (if URL is not inside 399251881Speter REPOS_URL, then error, with the effect on *FS_PATH undefined). 400251881Speter 401251881Speter If the resultant fs path would be the empty string (i.e., URL and 402251881Speter REPOS_URL are the same), then set *FS_PATH to "/". 403251881Speter 404251881Speter Assume that REPOS_URL and URL are already URI-decoded. */ 405251881Speterstatic svn_error_t *get_fs_path(const char *repos_url, const char *url, 406251881Speter const char **fs_path) 407251881Speter{ 408251881Speter apr_size_t len; 409251881Speter 410251881Speter len = strlen(repos_url); 411251881Speter if (strncmp(url, repos_url, len) != 0) 412251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 413251881Speter "'%s' is not the same repository as '%s'", 414251881Speter url, repos_url); 415251881Speter *fs_path = url + len; 416251881Speter if (! **fs_path) 417251881Speter *fs_path = "/"; 418251881Speter 419251881Speter return SVN_NO_ERROR; 420251881Speter} 421251881Speter 422251881Speter/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */ 423251881Speter 424251881Speter/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else 425251881Speter converts it to lower case. */ 426251881Speterstatic void convert_case(char *text, svn_boolean_t to_uppercase) 427251881Speter{ 428251881Speter char *c = text; 429251881Speter while (*c) 430251881Speter { 431251881Speter *c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c)); 432251881Speter ++c; 433251881Speter } 434251881Speter} 435251881Speter 436251881Speter/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to 437251881Speter the user described in BATON according to the authz rules in BATON. 438251881Speter Use POOL for temporary allocations only. If no authz rules are 439251881Speter present in BATON, grant access by default. */ 440251881Speterstatic svn_error_t *authz_check_access(svn_boolean_t *allowed, 441251881Speter const char *path, 442251881Speter svn_repos_authz_access_t required, 443251881Speter server_baton_t *b, 444251881Speter apr_pool_t *pool) 445251881Speter{ 446289180Speter repository_t *repository = b->repository; 447289180Speter client_info_t *client_info = b->client_info; 448289180Speter 449251881Speter /* If authz cannot be performed, grant access. This is NOT the same 450251881Speter as the default policy when authz is performed on a path with no 451251881Speter rules. In the latter case, the default is to deny access, and is 452251881Speter set by svn_repos_authz_check_access. */ 453289180Speter if (!repository->authzdb) 454251881Speter { 455251881Speter *allowed = TRUE; 456251881Speter return SVN_NO_ERROR; 457251881Speter } 458251881Speter 459251881Speter /* If the authz request is for the empty path (ie. ""), replace it 460251881Speter with the root path. This happens because of stripping done at 461251881Speter various levels in svnserve that remove the leading / on an 462251881Speter absolute path. Passing such a malformed path to the authz 463251881Speter routines throws them into an infinite loop and makes them miss 464251881Speter ACLs. */ 465362181Sdim if (path && *path != '/') 466251881Speter path = svn_fspath__canonicalize(path, pool); 467251881Speter 468251881Speter /* If we have a username, and we've not yet used it + any username 469251881Speter case normalization that might be requested to determine "the 470251881Speter username we used for authz purposes", do so now. */ 471289180Speter if (client_info->user && (! client_info->authz_user)) 472251881Speter { 473289180Speter char *authz_user = apr_pstrdup(b->pool, client_info->user); 474289180Speter if (repository->username_case == CASE_FORCE_UPPER) 475251881Speter convert_case(authz_user, TRUE); 476289180Speter else if (repository->username_case == CASE_FORCE_LOWER) 477251881Speter convert_case(authz_user, FALSE); 478289180Speter 479289180Speter client_info->authz_user = authz_user; 480251881Speter } 481251881Speter 482289180Speter SVN_ERR(svn_repos_authz_check_access(repository->authzdb, 483289180Speter repository->authz_repos_name, 484289180Speter path, client_info->authz_user, 485289180Speter required, allowed, pool)); 486251881Speter if (!*allowed) 487289180Speter SVN_ERR(log_authz_denied(path, required, b, pool)); 488251881Speter 489251881Speter return SVN_NO_ERROR; 490251881Speter} 491251881Speter 492251881Speter/* Set *ALLOWED to TRUE if PATH is readable by the user described in 493251881Speter * BATON. Use POOL for temporary allocations only. ROOT is not used. 494251881Speter * Implements the svn_repos_authz_func_t interface. 495251881Speter */ 496251881Speterstatic svn_error_t *authz_check_access_cb(svn_boolean_t *allowed, 497251881Speter svn_fs_root_t *root, 498251881Speter const char *path, 499251881Speter void *baton, 500251881Speter apr_pool_t *pool) 501251881Speter{ 502251881Speter authz_baton_t *sb = baton; 503251881Speter 504251881Speter return authz_check_access(allowed, path, svn_authz_read, 505289180Speter sb->server, pool); 506251881Speter} 507251881Speter 508251881Speter/* If authz is enabled in the specified BATON, return a read authorization 509251881Speter function. Otherwise, return NULL. */ 510251881Speterstatic svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton) 511251881Speter{ 512289180Speter if (baton->repository->authzdb) 513251881Speter return authz_check_access_cb; 514251881Speter return NULL; 515251881Speter} 516251881Speter 517251881Speter/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted, 518251881Speter * according to the state in BATON. Use POOL for temporary 519251881Speter * allocations only. ROOT is not used. Implements the 520251881Speter * svn_repos_authz_callback_t interface. 521251881Speter */ 522251881Speterstatic svn_error_t *authz_commit_cb(svn_repos_authz_access_t required, 523251881Speter svn_boolean_t *allowed, 524251881Speter svn_fs_root_t *root, 525251881Speter const char *path, 526251881Speter void *baton, 527251881Speter apr_pool_t *pool) 528251881Speter{ 529251881Speter authz_baton_t *sb = baton; 530251881Speter 531289180Speter return authz_check_access(allowed, path, required, sb->server, pool); 532251881Speter} 533251881Speter 534289180Speter/* Return the access level specified for OPTION in CFG. If no such 535289180Speter * setting exists, use DEF. If READ_ONLY is set, unconditionally disable 536289180Speter * write access. 537289180Speter */ 538289180Speterstatic enum access_type 539289180Speterget_access(svn_config_t *cfg, 540289180Speter const char *option, 541289180Speter const char *def, 542289180Speter svn_boolean_t read_only) 543251881Speter{ 544251881Speter enum access_type result; 545289180Speter const char *val; 546251881Speter 547289180Speter svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, def); 548251881Speter result = (strcmp(val, "write") == 0 ? WRITE_ACCESS : 549251881Speter strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS); 550289180Speter 551289180Speter return result == WRITE_ACCESS && read_only ? READ_ACCESS : result; 552251881Speter} 553251881Speter 554289180Speter/* Set the *_ACCESS members in REPOSITORY according to the settings in 555289180Speter * CFG. If READ_ONLY is set, unconditionally disable write access. 556289180Speter */ 557289180Speterstatic void 558289180Speterset_access(repository_t *repository, 559289180Speter svn_config_t *cfg, 560289180Speter svn_boolean_t read_only) 561251881Speter{ 562289180Speter repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS, 563289180Speter "write", read_only); 564289180Speter repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS, 565289180Speter "read", read_only); 566251881Speter} 567251881Speter 568289180Speter/* Return the access level for the user in B. 569289180Speter */ 570289180Speterstatic enum access_type 571289180Spetercurrent_access(server_baton_t *b) 572289180Speter{ 573289180Speter return b->client_info->user ? b->repository->auth_access 574289180Speter : b->repository->anon_access; 575289180Speter} 576289180Speter 577251881Speter/* Send authentication mechs for ACCESS_TYPE to the client. If NEEDS_USERNAME 578251881Speter is true, don't send anonymous mech even if that would give the desired 579251881Speter access. */ 580251881Speterstatic svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 581251881Speter server_baton_t *b, enum access_type required, 582251881Speter svn_boolean_t needs_username) 583251881Speter{ 584289180Speter if (!needs_username && b->repository->anon_access >= required) 585251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS")); 586289180Speter if (b->client_info->tunnel_user && b->repository->auth_access >= required) 587251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL")); 588289180Speter if (b->repository->pwdb && b->repository->auth_access >= required) 589251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5")); 590251881Speter return SVN_NO_ERROR; 591251881Speter} 592251881Speter 593251881Speter/* Context for cleanup handler. */ 594251881Speterstruct cleanup_fs_access_baton 595251881Speter{ 596251881Speter svn_fs_t *fs; 597251881Speter apr_pool_t *pool; 598251881Speter}; 599251881Speter 600251881Speter/* Pool cleanup handler. Make sure fs's access_t points to NULL when 601251881Speter the command pool is destroyed. */ 602251881Speterstatic apr_status_t cleanup_fs_access(void *data) 603251881Speter{ 604251881Speter svn_error_t *serr; 605251881Speter struct cleanup_fs_access_baton *baton = data; 606251881Speter 607251881Speter serr = svn_fs_set_access(baton->fs, NULL); 608251881Speter if (serr) 609251881Speter { 610251881Speter apr_status_t apr_err = serr->apr_err; 611251881Speter svn_error_clear(serr); 612251881Speter return apr_err; 613251881Speter } 614251881Speter 615251881Speter return APR_SUCCESS; 616251881Speter} 617251881Speter 618251881Speter 619251881Speter/* Create an svn_fs_access_t in POOL for USER and associate it with 620251881Speter B's filesystem. Also, register a cleanup handler with POOL which 621251881Speter de-associates the svn_fs_access_t from B's filesystem. */ 622251881Speterstatic svn_error_t * 623251881Spetercreate_fs_access(server_baton_t *b, apr_pool_t *pool) 624251881Speter{ 625251881Speter svn_fs_access_t *fs_access; 626251881Speter struct cleanup_fs_access_baton *cleanup_baton; 627251881Speter 628289180Speter if (!b->client_info->user) 629251881Speter return SVN_NO_ERROR; 630251881Speter 631289180Speter SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool)); 632289180Speter SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access)); 633251881Speter 634251881Speter cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton)); 635251881Speter cleanup_baton->pool = pool; 636289180Speter cleanup_baton->fs = b->repository->fs; 637251881Speter apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access, 638251881Speter apr_pool_cleanup_null); 639251881Speter 640251881Speter return SVN_NO_ERROR; 641251881Speter} 642251881Speter 643251881Speter/* Authenticate, once the client has chosen a mechanism and possibly 644251881Speter * sent an initial mechanism token. On success, set *success to true 645251881Speter * and b->user to the authenticated username (or NULL for anonymous). 646251881Speter * On authentication failure, report failure to the client and set 647251881Speter * *success to FALSE. On communications failure, return an error. 648251881Speter * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */ 649362181Sdimstatic svn_error_t *auth(svn_boolean_t *success, 650362181Sdim svn_ra_svn_conn_t *conn, 651251881Speter const char *mech, const char *mecharg, 652251881Speter server_baton_t *b, enum access_type required, 653251881Speter svn_boolean_t needs_username, 654362181Sdim apr_pool_t *scratch_pool) 655251881Speter{ 656251881Speter const char *user; 657251881Speter *success = FALSE; 658251881Speter 659289180Speter if (b->repository->auth_access >= required 660289180Speter && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0) 661251881Speter { 662289180Speter if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0) 663362181Sdim return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure", 664251881Speter "Requested username does not match"); 665289180Speter b->client_info->user = b->client_info->tunnel_user; 666362181Sdim SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success")); 667251881Speter *success = TRUE; 668251881Speter return SVN_NO_ERROR; 669251881Speter } 670251881Speter 671289180Speter if (b->repository->anon_access >= required 672251881Speter && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) 673251881Speter { 674362181Sdim SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w()", "success")); 675251881Speter *success = TRUE; 676251881Speter return SVN_NO_ERROR; 677251881Speter } 678251881Speter 679289180Speter if (b->repository->auth_access >= required 680289180Speter && b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0) 681251881Speter { 682362181Sdim SVN_ERR(svn_ra_svn_cram_server(conn, scratch_pool, b->repository->pwdb, 683289180Speter &user, success)); 684289180Speter b->client_info->user = apr_pstrdup(b->pool, user); 685251881Speter return SVN_NO_ERROR; 686251881Speter } 687251881Speter 688362181Sdim return svn_ra_svn__write_tuple(conn, scratch_pool, "w(c)", "failure", 689251881Speter "Must authenticate with listed mechanism"); 690251881Speter} 691251881Speter 692251881Speter/* Perform an authentication request using the built-in SASL implementation. */ 693251881Speterstatic svn_error_t * 694251881Speterinternal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 695251881Speter server_baton_t *b, enum access_type required, 696251881Speter svn_boolean_t needs_username) 697251881Speter{ 698251881Speter svn_boolean_t success; 699251881Speter const char *mech, *mecharg; 700362181Sdim apr_pool_t *iterpool; 701251881Speter 702251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 703251881Speter SVN_ERR(send_mechs(conn, pool, b, required, needs_username)); 704289180Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm)); 705362181Sdim 706362181Sdim iterpool = svn_pool_create(pool); 707251881Speter do 708251881Speter { 709362181Sdim svn_pool_clear(iterpool); 710362181Sdim 711251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); 712251881Speter if (!*mech) 713251881Speter break; 714362181Sdim SVN_ERR(auth(&success, conn, mech, mecharg, b, required, 715362181Sdim needs_username, iterpool)); 716251881Speter } 717251881Speter while (!success); 718362181Sdim svn_pool_destroy(iterpool); 719362181Sdim 720251881Speter return SVN_NO_ERROR; 721251881Speter} 722251881Speter 723251881Speter/* Perform an authentication request in order to get an access level of 724251881Speter * REQUIRED or higher. Since the client may escape the authentication 725251881Speter * exchange, the caller should check current_access(b) to see if 726251881Speter * authentication succeeded. */ 727251881Speterstatic svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 728251881Speter server_baton_t *b, enum access_type required, 729251881Speter svn_boolean_t needs_username) 730251881Speter{ 731251881Speter#ifdef SVN_HAVE_SASL 732289180Speter if (b->repository->use_sasl) 733251881Speter return cyrus_auth_request(conn, pool, b, required, needs_username); 734251881Speter#endif 735251881Speter 736251881Speter return internal_auth_request(conn, pool, b, required, needs_username); 737251881Speter} 738251881Speter 739251881Speter/* Send a trivial auth notification on CONN which lists no mechanisms, 740251881Speter * indicating that authentication is unnecessary. Usually called in 741251881Speter * response to invocation of a svnserve command. 742251881Speter */ 743251881Speterstatic svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn, 744251881Speter apr_pool_t *pool, server_baton_t *b) 745251881Speter{ 746251881Speter return svn_ra_svn__write_cmd_response(conn, pool, "()c", ""); 747251881Speter} 748251881Speter 749251881Speter/* Ensure that the client has the REQUIRED access by checking the 750251881Speter * access directives (both blanket and per-directory) in BATON. If 751251881Speter * PATH is NULL, then only the blanket access configuration will 752251881Speter * impact the result. 753251881Speter * 754251881Speter * If NEEDS_USERNAME is TRUE, then a lookup is only successful if the 755251881Speter * user described in BATON is authenticated and, well, has a username 756251881Speter * assigned to him. 757251881Speter * 758251881Speter * Use POOL for temporary allocations only. 759251881Speter */ 760251881Speterstatic svn_boolean_t lookup_access(apr_pool_t *pool, 761251881Speter server_baton_t *baton, 762251881Speter svn_repos_authz_access_t required, 763251881Speter const char *path, 764251881Speter svn_boolean_t needs_username) 765251881Speter{ 766251881Speter enum access_type req = (required & svn_authz_write) ? 767251881Speter WRITE_ACCESS : READ_ACCESS; 768251881Speter svn_boolean_t authorized; 769251881Speter svn_error_t *err; 770251881Speter 771251881Speter /* Get authz's opinion on the access. */ 772289180Speter err = authz_check_access(&authorized, path, required, baton, pool); 773251881Speter 774251881Speter /* If an error made lookup fail, deny access. */ 775251881Speter if (err) 776251881Speter { 777289180Speter log_error(err, baton); 778251881Speter svn_error_clear(err); 779251881Speter return FALSE; 780251881Speter } 781251881Speter 782251881Speter /* If the required access is blanket-granted AND granted by authz 783251881Speter AND we already have a username if one is required, then the 784251881Speter lookup has succeeded. */ 785251881Speter if (current_access(baton) >= req 786251881Speter && authorized 787289180Speter && (! needs_username || baton->client_info->user)) 788251881Speter return TRUE; 789251881Speter 790251881Speter return FALSE; 791251881Speter} 792251881Speter 793251881Speter/* Check that the client has the REQUIRED access by consulting the 794251881Speter * authentication and authorization states stored in BATON. If the 795251881Speter * client does not have the required access credentials, attempt to 796251881Speter * authenticate the client to get that access, using CONN for 797251881Speter * communication. 798251881Speter * 799251881Speter * This function is supposed to be called to handle the authentication 800251881Speter * half of a standard svn protocol reply. If an error is returned, it 801251881Speter * probably means that the server can terminate the client connection 802251881Speter * with an apologetic error, as it implies an authentication failure. 803251881Speter * 804251881Speter * PATH and NEEDS_USERNAME are passed along to lookup_access, their 805251881Speter * behaviour is documented there. 806251881Speter */ 807251881Speterstatic svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, 808251881Speter apr_pool_t *pool, 809251881Speter server_baton_t *b, 810251881Speter svn_repos_authz_access_t required, 811251881Speter const char *path, 812251881Speter svn_boolean_t needs_username) 813251881Speter{ 814251881Speter enum access_type req = (required & svn_authz_write) ? 815251881Speter WRITE_ACCESS : READ_ACCESS; 816251881Speter 817251881Speter /* See whether the user already has the required access. If so, 818251881Speter nothing needs to be done. Create the FS access and send a 819251881Speter trivial auth request. */ 820289180Speter if (lookup_access(pool, b, required, path, needs_username)) 821251881Speter { 822251881Speter SVN_ERR(create_fs_access(b, pool)); 823251881Speter return trivial_auth_request(conn, pool, b); 824251881Speter } 825251881Speter 826251881Speter /* If the required blanket access can be obtained by authenticating, 827251881Speter try that. Unfortunately, we can't tell until after 828251881Speter authentication whether authz will work or not. We force 829251881Speter requiring a username because we need one to be able to check 830251881Speter authz configuration again with a different user credentials than 831251881Speter the first time round. */ 832289180Speter if (b->client_info->user == NULL 833289180Speter && b->repository->auth_access >= req 834289180Speter && (b->client_info->tunnel_user || b->repository->pwdb 835289180Speter || b->repository->use_sasl)) 836251881Speter SVN_ERR(auth_request(conn, pool, b, req, TRUE)); 837251881Speter 838251881Speter /* Now that an authentication has been done get the new take of 839251881Speter authz on the request. */ 840289180Speter if (! lookup_access(pool, b, required, path, needs_username)) 841251881Speter return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, 842251881Speter error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 843289180Speter NULL, NULL, b), 844251881Speter NULL); 845251881Speter 846251881Speter /* Else, access is granted, and there is much rejoicing. */ 847251881Speter SVN_ERR(create_fs_access(b, pool)); 848251881Speter 849251881Speter return SVN_NO_ERROR; 850251881Speter} 851251881Speter 852251881Speter/* --- REPORTER COMMAND SET --- */ 853251881Speter 854251881Speter/* To allow for pipelining, reporter commands have no reponses. If we 855251881Speter * get an error, we ignore all subsequent reporter commands and return 856251881Speter * the error finish_report, to be handled by the calling command. 857251881Speter */ 858251881Speter 859251881Speterstatic svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 860362181Sdim svn_ra_svn__list_t *params, void *baton) 861251881Speter{ 862251881Speter report_driver_baton_t *b = baton; 863362181Sdim const char *path, *lock_token, *depth_word, *canonical_relpath; 864251881Speter svn_revnum_t rev; 865251881Speter /* Default to infinity, for old clients that don't send depth. */ 866251881Speter svn_depth_t depth = svn_depth_infinity; 867251881Speter svn_boolean_t start_empty; 868251881Speter 869362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "crb?(?c)?w", 870362181Sdim &path, &rev, &start_empty, &lock_token, 871362181Sdim &depth_word)); 872251881Speter if (depth_word) 873251881Speter depth = svn_depth_from_word(depth_word); 874362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path, 875362181Sdim pool, pool)); 876362181Sdim path = canonical_relpath; 877251881Speter if (b->from_rev && strcmp(path, "") == 0) 878251881Speter *b->from_rev = rev; 879251881Speter if (!b->err) 880251881Speter b->err = svn_repos_set_path3(b->report_baton, path, rev, depth, 881251881Speter start_empty, lock_token, pool); 882251881Speter b->entry_counter++; 883251881Speter if (!start_empty) 884251881Speter b->only_empty_entries = FALSE; 885251881Speter return SVN_NO_ERROR; 886251881Speter} 887251881Speter 888251881Speterstatic svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 889362181Sdim svn_ra_svn__list_t *params, void *baton) 890251881Speter{ 891251881Speter report_driver_baton_t *b = baton; 892362181Sdim const char *path, *canonical_relpath; 893251881Speter 894362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path)); 895362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_relpath, NULL, path, 896362181Sdim pool, pool)); 897362181Sdim path = canonical_relpath; 898251881Speter if (!b->err) 899251881Speter b->err = svn_repos_delete_path(b->report_baton, path, pool); 900251881Speter return SVN_NO_ERROR; 901251881Speter} 902251881Speter 903251881Speterstatic svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 904362181Sdim svn_ra_svn__list_t *params, void *baton) 905251881Speter{ 906251881Speter report_driver_baton_t *b = baton; 907362181Sdim const char *path, *url, *lock_token, *fs_path, *depth_word, *canonical_url; 908362181Sdim const char *canonical_path; 909251881Speter svn_revnum_t rev; 910251881Speter svn_boolean_t start_empty; 911251881Speter /* Default to infinity, for old clients that don't send depth. */ 912251881Speter svn_depth_t depth = svn_depth_infinity; 913251881Speter 914362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "ccrb?(?c)?w", 915251881Speter &path, &url, &rev, &start_empty, 916251881Speter &lock_token, &depth_word)); 917251881Speter 918251881Speter /* ### WHAT?! The link path is an absolute URL?! Didn't see that 919251881Speter coming... -- cmpilato */ 920362181Sdim 921362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 922362181Sdim pool, pool)); 923362181Sdim path = canonical_path; 924362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool)); 925362181Sdim url = canonical_url; 926251881Speter if (depth_word) 927251881Speter depth = svn_depth_from_word(depth_word); 928251881Speter if (!b->err) 929251881Speter b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool), 930251881Speter svn_path_uri_decode(url, pool), 931251881Speter &fs_path); 932251881Speter if (!b->err) 933251881Speter b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev, 934251881Speter depth, start_empty, lock_token, pool); 935251881Speter b->entry_counter++; 936251881Speter return SVN_NO_ERROR; 937251881Speter} 938251881Speter 939251881Speterstatic svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 940362181Sdim svn_ra_svn__list_t *params, void *baton) 941251881Speter{ 942251881Speter report_driver_baton_t *b = baton; 943251881Speter 944251881Speter /* No arguments to parse. */ 945251881Speter SVN_ERR(trivial_auth_request(conn, pool, b->sb)); 946251881Speter if (!b->err) 947251881Speter b->err = svn_repos_finish_report(b->report_baton, pool); 948251881Speter return SVN_NO_ERROR; 949251881Speter} 950251881Speter 951362181Sdimstatic svn_error_t * 952362181Sdimabort_report(svn_ra_svn_conn_t *conn, 953362181Sdim apr_pool_t *pool, 954362181Sdim svn_ra_svn__list_t *params, 955362181Sdim void *baton) 956251881Speter{ 957251881Speter report_driver_baton_t *b = baton; 958251881Speter 959251881Speter /* No arguments to parse. */ 960251881Speter svn_error_clear(svn_repos_abort_report(b->report_baton, pool)); 961251881Speter return SVN_NO_ERROR; 962251881Speter} 963251881Speter 964362181Sdimstatic const svn_ra_svn__cmd_entry_t report_commands[] = { 965251881Speter { "set-path", set_path }, 966251881Speter { "delete-path", delete_path }, 967251881Speter { "link-path", link_path }, 968362181Sdim { "finish-report", finish_report, NULL, TRUE }, 969362181Sdim { "abort-report", abort_report, NULL, TRUE }, 970251881Speter { NULL } 971251881Speter}; 972251881Speter 973251881Speter/* Accept a report from the client, drive the network editor with the 974251881Speter * result, and then write an empty command response. If there is a 975251881Speter * non-protocol failure, accept_report will abort the edit and return 976251881Speter * a command error to be reported by handle_commands(). 977251881Speter * 978251881Speter * If only_empty_entry is not NULL and the report contains only one 979251881Speter * item, and that item is empty, set *only_empty_entry to TRUE, else 980251881Speter * set it to FALSE. 981251881Speter * 982251881Speter * If from_rev is not NULL, set *from_rev to the revision number from 983251881Speter * the set-path on ""; if somehow set-path "" never happens, set 984251881Speter * *from_rev to SVN_INVALID_REVNUM. 985251881Speter */ 986251881Speterstatic svn_error_t *accept_report(svn_boolean_t *only_empty_entry, 987251881Speter svn_revnum_t *from_rev, 988251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool, 989251881Speter server_baton_t *b, svn_revnum_t rev, 990251881Speter const char *target, const char *tgt_path, 991251881Speter svn_boolean_t text_deltas, 992251881Speter svn_depth_t depth, 993251881Speter svn_boolean_t send_copyfrom_args, 994251881Speter svn_boolean_t ignore_ancestry) 995251881Speter{ 996251881Speter const svn_delta_editor_t *editor; 997251881Speter void *edit_baton, *report_baton; 998251881Speter report_driver_baton_t rb; 999251881Speter svn_error_t *err; 1000251881Speter authz_baton_t ab; 1001251881Speter 1002251881Speter ab.server = b; 1003251881Speter ab.conn = conn; 1004251881Speter 1005251881Speter /* Make an svn_repos report baton. Tell it to drive the network editor 1006251881Speter * when the report is complete. */ 1007251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 1008289180Speter SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, 1009289180Speter b->repository->repos, 1010289180Speter b->repository->fs_path->data, target, 1011289180Speter tgt_path, text_deltas, depth, 1012289180Speter ignore_ancestry, send_copyfrom_args, 1013251881Speter editor, edit_baton, 1014251881Speter authz_check_access_cb_func(b), 1015251881Speter &ab, svn_ra_svn_zero_copy_limit(conn), 1016251881Speter pool)); 1017251881Speter 1018251881Speter rb.sb = b; 1019289180Speter rb.repos_url = svn_path_uri_decode(b->repository->repos_url, pool); 1020251881Speter rb.report_baton = report_baton; 1021251881Speter rb.err = NULL; 1022251881Speter rb.entry_counter = 0; 1023251881Speter rb.only_empty_entries = TRUE; 1024251881Speter rb.from_rev = from_rev; 1025251881Speter if (from_rev) 1026251881Speter *from_rev = SVN_INVALID_REVNUM; 1027251881Speter err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE); 1028251881Speter if (err) 1029251881Speter { 1030251881Speter /* Network or protocol error while handling commands. */ 1031251881Speter svn_error_clear(rb.err); 1032251881Speter return err; 1033251881Speter } 1034251881Speter else if (rb.err) 1035251881Speter { 1036251881Speter /* Some failure during the reporting or editing operations. */ 1037251881Speter SVN_CMD_ERR(rb.err); 1038251881Speter } 1039251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1040251881Speter 1041251881Speter if (only_empty_entry) 1042251881Speter *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries; 1043251881Speter 1044251881Speter return SVN_NO_ERROR; 1045251881Speter} 1046251881Speter 1047251881Speter/* --- MAIN COMMAND SET --- */ 1048251881Speter 1049251881Speter/* Write out a list of property diffs. PROPDIFFS is an array of svn_prop_t 1050251881Speter * values. */ 1051251881Speterstatic svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn, 1052251881Speter apr_pool_t *pool, 1053251881Speter const apr_array_header_t *propdiffs) 1054251881Speter{ 1055251881Speter int i; 1056251881Speter 1057251881Speter for (i = 0; i < propdiffs->nelts; ++i) 1058251881Speter { 1059251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); 1060251881Speter 1061251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)", 1062251881Speter prop->name, prop->value)); 1063251881Speter } 1064251881Speter 1065251881Speter return SVN_NO_ERROR; 1066251881Speter} 1067251881Speter 1068251881Speter/* Write out a lock to the client. */ 1069251881Speterstatic svn_error_t *write_lock(svn_ra_svn_conn_t *conn, 1070251881Speter apr_pool_t *pool, 1071289180Speter const svn_lock_t *lock) 1072251881Speter{ 1073251881Speter const char *cdate, *edate; 1074251881Speter 1075251881Speter cdate = svn_time_to_cstring(lock->creation_date, pool); 1076251881Speter edate = lock->expiration_date 1077251881Speter ? svn_time_to_cstring(lock->expiration_date, pool) : NULL; 1078251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path, 1079251881Speter lock->token, lock->owner, lock->comment, 1080251881Speter cdate, edate)); 1081251881Speter 1082251881Speter return SVN_NO_ERROR; 1083251881Speter} 1084251881Speter 1085251881Speter/* ### This really belongs in libsvn_repos. */ 1086251881Speter/* Get the explicit properties and/or inherited properties for a PATH in 1087251881Speter ROOT, with hardcoded committed-info values. */ 1088251881Speterstatic svn_error_t * 1089251881Speterget_props(apr_hash_t **props, 1090251881Speter apr_array_header_t **iprops, 1091251881Speter authz_baton_t *b, 1092251881Speter svn_fs_root_t *root, 1093251881Speter const char *path, 1094251881Speter apr_pool_t *pool) 1095251881Speter{ 1096251881Speter /* Get the explicit properties. */ 1097251881Speter if (props) 1098251881Speter { 1099251881Speter svn_string_t *str; 1100251881Speter svn_revnum_t crev; 1101251881Speter const char *cdate, *cauthor, *uuid; 1102251881Speter 1103251881Speter SVN_ERR(svn_fs_node_proplist(props, root, path, pool)); 1104251881Speter 1105251881Speter /* Hardcode the values for the committed revision, date, and author. */ 1106251881Speter SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root, 1107251881Speter path, pool)); 1108289180Speter str = svn_string_createf(pool, "%ld", crev); 1109251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str); 1110251881Speter str = (cdate) ? svn_string_create(cdate, pool) : NULL; 1111251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str); 1112251881Speter str = (cauthor) ? svn_string_create(cauthor, pool) : NULL; 1113251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str); 1114251881Speter 1115251881Speter /* Hardcode the values for the UUID. */ 1116251881Speter SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool)); 1117251881Speter str = (uuid) ? svn_string_create(uuid, pool) : NULL; 1118251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str); 1119251881Speter } 1120251881Speter 1121251881Speter /* Get any inherited properties the user is authorized to. */ 1122251881Speter if (iprops) 1123251881Speter { 1124251881Speter SVN_ERR(svn_repos_fs_get_inherited_props( 1125251881Speter iprops, root, path, NULL, 1126251881Speter authz_check_access_cb_func(b->server), 1127251881Speter b, pool, pool)); 1128251881Speter } 1129251881Speter 1130251881Speter return SVN_NO_ERROR; 1131251881Speter} 1132251881Speter 1133251881Speter/* Set BATON->FS_PATH for the repository URL found in PARAMS. */ 1134362181Sdimstatic svn_error_t * 1135362181Sdimreparent(svn_ra_svn_conn_t *conn, 1136362181Sdim apr_pool_t *pool, 1137362181Sdim svn_ra_svn__list_t *params, 1138362181Sdim void *baton) 1139251881Speter{ 1140251881Speter server_baton_t *b = baton; 1141362181Sdim const char *url, *canonical_url; 1142251881Speter const char *fs_path; 1143251881Speter 1144362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &url)); 1145362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, url, pool, pool)); 1146362181Sdim url = canonical_url; 1147251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1148289180Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool), 1149251881Speter svn_path_uri_decode(url, pool), 1150251881Speter &fs_path)); 1151251881Speter SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool))); 1152289180Speter svn_stringbuf_set(b->repository->fs_path, fs_path); 1153251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1154251881Speter return SVN_NO_ERROR; 1155251881Speter} 1156251881Speter 1157362181Sdimstatic svn_error_t * 1158362181Sdimget_latest_rev(svn_ra_svn_conn_t *conn, 1159362181Sdim apr_pool_t *pool, 1160362181Sdim svn_ra_svn__list_t *params, 1161362181Sdim void *baton) 1162251881Speter{ 1163251881Speter server_baton_t *b = baton; 1164251881Speter svn_revnum_t rev; 1165251881Speter 1166251881Speter SVN_ERR(log_command(b, conn, pool, "get-latest-rev")); 1167251881Speter 1168251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1169289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1170251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); 1171251881Speter return SVN_NO_ERROR; 1172251881Speter} 1173251881Speter 1174362181Sdimstatic svn_error_t * 1175362181Sdimget_dated_rev(svn_ra_svn_conn_t *conn, 1176362181Sdim apr_pool_t *pool, 1177362181Sdim svn_ra_svn__list_t *params, 1178362181Sdim void *baton) 1179251881Speter{ 1180251881Speter server_baton_t *b = baton; 1181251881Speter svn_revnum_t rev; 1182251881Speter apr_time_t tm; 1183251881Speter const char *timestr; 1184251881Speter 1185362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c", ×tr)); 1186251881Speter SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr)); 1187251881Speter 1188251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1189251881Speter SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool)); 1190289180Speter SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool)); 1191251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); 1192251881Speter return SVN_NO_ERROR; 1193251881Speter} 1194251881Speter 1195251881Speter/* Common logic for change_rev_prop() and change_rev_prop2(). */ 1196251881Speterstatic svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn, 1197251881Speter server_baton_t *b, 1198251881Speter svn_revnum_t rev, 1199251881Speter const char *name, 1200251881Speter const svn_string_t *const *old_value_p, 1201251881Speter const svn_string_t *value, 1202251881Speter apr_pool_t *pool) 1203251881Speter{ 1204251881Speter authz_baton_t ab; 1205251881Speter 1206251881Speter ab.server = b; 1207251881Speter ab.conn = conn; 1208251881Speter 1209251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE)); 1210251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1211251881Speter svn_log__change_rev_prop(rev, name, pool))); 1212289180Speter SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev, 1213289180Speter b->client_info->user, 1214251881Speter name, old_value_p, value, 1215251881Speter TRUE, TRUE, 1216251881Speter authz_check_access_cb_func(b), &ab, 1217251881Speter pool)); 1218251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1219251881Speter 1220251881Speter return SVN_NO_ERROR; 1221251881Speter} 1222251881Speter 1223362181Sdimstatic svn_error_t * 1224362181Sdimchange_rev_prop2(svn_ra_svn_conn_t *conn, 1225362181Sdim apr_pool_t *pool, 1226362181Sdim svn_ra_svn__list_t *params, 1227362181Sdim void *baton) 1228251881Speter{ 1229251881Speter server_baton_t *b = baton; 1230251881Speter svn_revnum_t rev; 1231251881Speter const char *name; 1232251881Speter svn_string_t *value; 1233251881Speter const svn_string_t *const *old_value_p; 1234251881Speter svn_string_t *old_value; 1235251881Speter svn_boolean_t dont_care; 1236251881Speter 1237362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "rc(?s)(b?s)", 1238251881Speter &rev, &name, &value, 1239251881Speter &dont_care, &old_value)); 1240251881Speter 1241251881Speter /* Argument parsing. */ 1242251881Speter if (dont_care) 1243251881Speter old_value_p = NULL; 1244251881Speter else 1245251881Speter old_value_p = (const svn_string_t *const *)&old_value; 1246251881Speter 1247251881Speter /* Input validation. */ 1248251881Speter if (dont_care && old_value) 1249251881Speter { 1250251881Speter svn_error_t *err; 1251251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1252251881Speter "'previous-value' and 'dont-care' cannot both be " 1253251881Speter "set in 'change-rev-prop2' request"); 1254251881Speter return log_fail_and_flush(err, b, conn, pool); 1255251881Speter } 1256251881Speter 1257251881Speter /* Do it. */ 1258251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool)); 1259251881Speter 1260251881Speter return SVN_NO_ERROR; 1261251881Speter} 1262251881Speter 1263362181Sdimstatic svn_error_t * 1264362181Sdimchange_rev_prop(svn_ra_svn_conn_t *conn, 1265362181Sdim apr_pool_t *pool, 1266362181Sdim svn_ra_svn__list_t *params, 1267362181Sdim void *baton) 1268251881Speter{ 1269251881Speter server_baton_t *b = baton; 1270251881Speter svn_revnum_t rev; 1271251881Speter const char *name; 1272251881Speter svn_string_t *value; 1273251881Speter 1274251881Speter /* Because the revprop value was at one time mandatory, the usual 1275251881Speter optional element pattern "(?s)" isn't used. */ 1276362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "rc?s", &rev, &name, &value)); 1277251881Speter 1278251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool)); 1279251881Speter 1280251881Speter return SVN_NO_ERROR; 1281251881Speter} 1282251881Speter 1283362181Sdimstatic svn_error_t * 1284362181Sdimrev_proplist(svn_ra_svn_conn_t *conn, 1285362181Sdim apr_pool_t *pool, 1286362181Sdim svn_ra_svn__list_t *params, 1287362181Sdim void *baton) 1288251881Speter{ 1289251881Speter server_baton_t *b = baton; 1290251881Speter svn_revnum_t rev; 1291251881Speter apr_hash_t *props; 1292251881Speter authz_baton_t ab; 1293251881Speter 1294251881Speter ab.server = b; 1295251881Speter ab.conn = conn; 1296251881Speter 1297362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "r", &rev)); 1298251881Speter SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool))); 1299251881Speter 1300251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1301289180Speter SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->repos, 1302289180Speter rev, 1303289180Speter authz_check_access_cb_func(b), 1304289180Speter &ab, pool)); 1305251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 1306251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1307251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1308251881Speter return SVN_NO_ERROR; 1309251881Speter} 1310251881Speter 1311362181Sdimstatic svn_error_t * 1312362181Sdimrev_prop(svn_ra_svn_conn_t *conn, 1313362181Sdim apr_pool_t *pool, 1314362181Sdim svn_ra_svn__list_t *params, 1315362181Sdim void *baton) 1316251881Speter{ 1317251881Speter server_baton_t *b = baton; 1318251881Speter svn_revnum_t rev; 1319251881Speter const char *name; 1320251881Speter svn_string_t *value; 1321251881Speter authz_baton_t ab; 1322251881Speter 1323251881Speter ab.server = b; 1324251881Speter ab.conn = conn; 1325251881Speter 1326362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "rc", &rev, &name)); 1327251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1328251881Speter svn_log__rev_prop(rev, name, pool))); 1329251881Speter 1330251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1331289180Speter SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->repos, rev, 1332289180Speter name, authz_check_access_cb_func(b), 1333289180Speter &ab, pool)); 1334251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value)); 1335251881Speter return SVN_NO_ERROR; 1336251881Speter} 1337251881Speter 1338251881Speterstatic svn_error_t *commit_done(const svn_commit_info_t *commit_info, 1339251881Speter void *baton, apr_pool_t *pool) 1340251881Speter{ 1341251881Speter commit_callback_baton_t *ccb = baton; 1342251881Speter 1343251881Speter *ccb->new_rev = commit_info->revision; 1344251881Speter *ccb->date = commit_info->date 1345251881Speter ? apr_pstrdup(ccb->pool, commit_info->date): NULL; 1346251881Speter *ccb->author = commit_info->author 1347251881Speter ? apr_pstrdup(ccb->pool, commit_info->author) : NULL; 1348251881Speter *ccb->post_commit_err = commit_info->post_commit_err 1349251881Speter ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL; 1350251881Speter return SVN_NO_ERROR; 1351251881Speter} 1352251881Speter 1353251881Speter/* Add the LOCK_TOKENS (if any) to the filesystem access context, 1354251881Speter * checking path authorizations using the state in SB as we go. 1355362181Sdim * LOCK_TOKENS is an array of svn_ra_svn__item_t structs. Return a 1356251881Speter * client error if LOCK_TOKENS is not a list of lists. If a lock 1357251881Speter * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED 1358251881Speter * to the client. Use POOL for temporary allocations only. 1359251881Speter */ 1360362181Sdimstatic svn_error_t * 1361362181Sdimadd_lock_tokens(const svn_ra_svn__list_t *lock_tokens, 1362362181Sdim server_baton_t *sb, 1363362181Sdim apr_pool_t *pool) 1364251881Speter{ 1365362181Sdim const char *canonical_path; 1366251881Speter int i; 1367251881Speter svn_fs_access_t *fs_access; 1368251881Speter 1369289180Speter SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs)); 1370251881Speter 1371251881Speter /* If there is no access context, nowhere to add the tokens. */ 1372251881Speter if (! fs_access) 1373251881Speter return SVN_NO_ERROR; 1374251881Speter 1375251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1376251881Speter { 1377251881Speter const char *path, *token, *full_path; 1378362181Sdim svn_ra_svn__item_t *path_item, *token_item; 1379362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i); 1380251881Speter if (item->kind != SVN_RA_SVN_LIST) 1381251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1382251881Speter "Lock tokens aren't a list of lists"); 1383251881Speter 1384362181Sdim path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0); 1385251881Speter if (path_item->kind != SVN_RA_SVN_STRING) 1386251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1387251881Speter "Lock path isn't a string"); 1388251881Speter 1389362181Sdim token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1); 1390251881Speter if (token_item->kind != SVN_RA_SVN_STRING) 1391251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1392251881Speter "Lock token isn't a string"); 1393251881Speter 1394362181Sdim path = path_item->u.string.data; 1395362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 1396362181Sdim pool, pool)); 1397289180Speter full_path = svn_fspath__join(sb->repository->fs_path->data, 1398362181Sdim canonical_path, pool); 1399251881Speter 1400289180Speter if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE)) 1401251881Speter return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, 1402289180Speter sb); 1403251881Speter 1404362181Sdim token = token_item->u.string.data; 1405251881Speter SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token)); 1406251881Speter } 1407251881Speter 1408251881Speter return SVN_NO_ERROR; 1409251881Speter} 1410251881Speter 1411289180Speter/* Implements svn_fs_lock_callback_t. */ 1412289180Speterstatic svn_error_t * 1413289180Speterlock_cb(void *baton, 1414289180Speter const char *path, 1415289180Speter const svn_lock_t *lock, 1416289180Speter svn_error_t *fs_err, 1417289180Speter apr_pool_t *pool) 1418289180Speter{ 1419289180Speter server_baton_t *sb = baton; 1420289180Speter 1421289180Speter log_error(fs_err, sb); 1422289180Speter 1423289180Speter return SVN_NO_ERROR; 1424289180Speter} 1425289180Speter 1426251881Speter/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors. 1427362181Sdim LOCK_TOKENS contains svn_ra_svn__item_t elements, assumed to be lists. */ 1428362181Sdimstatic svn_error_t * 1429362181Sdimunlock_paths(const svn_ra_svn__list_t *lock_tokens, 1430362181Sdim server_baton_t *sb, 1431362181Sdim apr_pool_t *pool) 1432251881Speter{ 1433251881Speter int i; 1434289180Speter apr_pool_t *subpool = svn_pool_create(pool); 1435289180Speter apr_hash_t *targets = apr_hash_make(subpool); 1436362181Sdim const char *canonical_path; 1437289180Speter svn_error_t *err; 1438251881Speter 1439251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1440251881Speter { 1441362181Sdim svn_ra_svn__item_t *item, *path_item, *token_item; 1442251881Speter const char *path, *token, *full_path; 1443251881Speter 1444362181Sdim item = &SVN_RA_SVN__LIST_ITEM(lock_tokens, i); 1445362181Sdim path_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 0); 1446362181Sdim token_item = &SVN_RA_SVN__LIST_ITEM(&item->u.list, 1); 1447251881Speter 1448362181Sdim path = path_item->u.string.data; 1449362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 1450362181Sdim subpool, subpool)); 1451289180Speter full_path = svn_fspath__join(sb->repository->fs_path->data, 1452362181Sdim canonical_path, subpool); 1453362181Sdim token = token_item->u.string.data; 1454289180Speter svn_hash_sets(targets, full_path, token); 1455289180Speter } 1456251881Speter 1457251881Speter 1458289180Speter /* The lock may have become defunct after the commit, so ignore such 1459289180Speter errors. */ 1460289180Speter err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE, 1461289180Speter lock_cb, sb, subpool, subpool); 1462289180Speter log_error(err, sb); 1463289180Speter svn_error_clear(err); 1464251881Speter 1465289180Speter svn_pool_destroy(subpool); 1466251881Speter 1467251881Speter return SVN_NO_ERROR; 1468251881Speter} 1469251881Speter 1470362181Sdimstatic svn_error_t * 1471362181Sdimcommit(svn_ra_svn_conn_t *conn, 1472362181Sdim apr_pool_t *pool, 1473362181Sdim svn_ra_svn__list_t *params, 1474362181Sdim void *baton) 1475251881Speter{ 1476251881Speter server_baton_t *b = baton; 1477289180Speter const char *log_msg, 1478251881Speter *date = NULL, 1479251881Speter *author = NULL, 1480251881Speter *post_commit_err = NULL; 1481362181Sdim svn_ra_svn__list_t *lock_tokens; 1482251881Speter svn_boolean_t keep_locks; 1483362181Sdim svn_ra_svn__list_t *revprop_list; 1484251881Speter apr_hash_t *revprop_table; 1485251881Speter const svn_delta_editor_t *editor; 1486251881Speter void *edit_baton; 1487251881Speter svn_boolean_t aborted; 1488251881Speter commit_callback_baton_t ccb; 1489251881Speter svn_revnum_t new_rev; 1490251881Speter authz_baton_t ab; 1491251881Speter 1492251881Speter ab.server = b; 1493251881Speter ab.conn = conn; 1494251881Speter 1495251881Speter if (params->nelts == 1) 1496251881Speter { 1497251881Speter /* Clients before 1.2 don't send lock-tokens, keep-locks, 1498251881Speter and rev-props fields. */ 1499362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &log_msg)); 1500251881Speter lock_tokens = NULL; 1501251881Speter keep_locks = TRUE; 1502251881Speter revprop_list = NULL; 1503251881Speter } 1504251881Speter else 1505251881Speter { 1506251881Speter /* Clients before 1.5 don't send the rev-props field. */ 1507362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "clb?l", &log_msg, 1508251881Speter &lock_tokens, &keep_locks, 1509251881Speter &revprop_list)); 1510251881Speter } 1511251881Speter 1512251881Speter /* The handling for locks is a little problematic, because the 1513251881Speter protocol won't let us send several auth requests once one has 1514251881Speter succeeded. So we request write access and a username before 1515251881Speter adding tokens (if we have any), and subsequently fail if a lock 1516251881Speter violates authz. */ 1517251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 1518251881Speter NULL, 1519251881Speter (lock_tokens && lock_tokens->nelts))); 1520251881Speter 1521251881Speter /* Authorize the lock tokens and give them to the FS if we got 1522251881Speter any. */ 1523251881Speter if (lock_tokens && lock_tokens->nelts) 1524289180Speter SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool)); 1525251881Speter 1526253734Speter /* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */ 1527251881Speter if (revprop_list) 1528251881Speter SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table)); 1529251881Speter else 1530251881Speter { 1531251881Speter revprop_table = apr_hash_make(pool); 1532251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, 1533251881Speter svn_string_create(log_msg, pool)); 1534251881Speter } 1535251881Speter 1536251881Speter /* Get author from the baton, making sure clients can't circumvent 1537251881Speter the authentication via the revision props. */ 1538251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 1539289180Speter b->client_info->user 1540289180Speter ? svn_string_create(b->client_info->user, pool) 1541289180Speter : NULL); 1542251881Speter 1543251881Speter ccb.pool = pool; 1544251881Speter ccb.new_rev = &new_rev; 1545251881Speter ccb.date = &date; 1546251881Speter ccb.author = &author; 1547251881Speter ccb.post_commit_err = &post_commit_err; 1548251881Speter /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */ 1549251881Speter SVN_CMD_ERR(svn_repos_get_commit_editor5 1550289180Speter (&editor, &edit_baton, b->repository->repos, NULL, 1551289180Speter svn_path_uri_decode(b->repository->repos_url, pool), 1552289180Speter b->repository->fs_path->data, revprop_table, 1553251881Speter commit_done, &ccb, 1554251881Speter authz_commit_cb, &ab, pool)); 1555251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1556251881Speter SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton, 1557251881Speter &aborted, FALSE)); 1558251881Speter if (!aborted) 1559251881Speter { 1560251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1561251881Speter svn_log__commit(new_rev, pool))); 1562251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1563251881Speter 1564251881Speter /* In tunnel mode, deltify before answering the client, because 1565251881Speter answering may cause the client to terminate the connection 1566251881Speter and thus kill the server. But otherwise, deltify after 1567251881Speter answering the client, to avoid user-visible delay. */ 1568251881Speter 1569289180Speter if (b->client_info->tunnel) 1570289180Speter SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool)); 1571251881Speter 1572251881Speter /* Unlock the paths. */ 1573251881Speter if (! keep_locks && lock_tokens && lock_tokens->nelts) 1574289180Speter SVN_ERR(unlock_paths(lock_tokens, b, pool)); 1575251881Speter 1576251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)", 1577251881Speter new_rev, date, author, post_commit_err)); 1578251881Speter 1579289180Speter if (! b->client_info->tunnel) 1580289180Speter SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool)); 1581251881Speter } 1582251881Speter return SVN_NO_ERROR; 1583251881Speter} 1584251881Speter 1585362181Sdimstatic svn_error_t * 1586362181Sdimget_file(svn_ra_svn_conn_t *conn, 1587362181Sdim apr_pool_t *pool, 1588362181Sdim svn_ra_svn__list_t *params, 1589362181Sdim void *baton) 1590251881Speter{ 1591251881Speter server_baton_t *b = baton; 1592362181Sdim const char *path, *full_path, *hex_digest, *canonical_path; 1593251881Speter svn_revnum_t rev; 1594251881Speter svn_fs_root_t *root; 1595251881Speter svn_stream_t *contents; 1596251881Speter apr_hash_t *props = NULL; 1597251881Speter apr_array_header_t *inherited_props; 1598251881Speter svn_string_t write_str; 1599251881Speter char buf[4096]; 1600251881Speter apr_size_t len; 1601251881Speter svn_boolean_t want_props, want_contents; 1602251881Speter apr_uint64_t wants_inherited_props; 1603251881Speter svn_checksum_t *checksum; 1604251881Speter svn_error_t *err, *write_err; 1605251881Speter int i; 1606251881Speter authz_baton_t ab; 1607251881Speter 1608251881Speter ab.server = b; 1609251881Speter ab.conn = conn; 1610251881Speter 1611251881Speter /* Parse arguments. */ 1612362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?B", &path, &rev, 1613251881Speter &want_props, &want_contents, 1614251881Speter &wants_inherited_props)); 1615251881Speter 1616266731Speter if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1617266731Speter wants_inherited_props = FALSE; 1618266731Speter 1619362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool, 1620362181Sdim pool)); 1621362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path, 1622362181Sdim pool); 1623251881Speter 1624251881Speter /* Check authorizations */ 1625251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1626251881Speter full_path, FALSE)); 1627251881Speter 1628251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1629289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1630251881Speter 1631251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1632251881Speter svn_log__get_file(full_path, rev, 1633251881Speter want_contents, want_props, pool))); 1634251881Speter 1635251881Speter /* Fetch the properties and a stream for the contents. */ 1636289180Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 1637251881Speter SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root, 1638251881Speter full_path, TRUE, pool)); 1639251881Speter hex_digest = svn_checksum_to_cstring_display(checksum, pool); 1640266731Speter 1641266731Speter /* Fetch the file's explicit and/or inherited properties if 1642266731Speter requested. Although the wants-iprops boolean was added to the 1643266731Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1644251881Speter if (want_props || wants_inherited_props) 1645266731Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1646266731Speter wants_inherited_props ? &inherited_props : NULL, 1647266731Speter &ab, root, full_path, 1648251881Speter pool)); 1649251881Speter if (want_contents) 1650251881Speter SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool)); 1651251881Speter 1652251881Speter /* Send successful command response with revision and props. */ 1653251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success", 1654251881Speter hex_digest, rev)); 1655251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1656251881Speter 1657251881Speter if (wants_inherited_props) 1658251881Speter { 1659251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1660251881Speter 1661251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1662251881Speter for (i = 0; i < inherited_props->nelts; i++) 1663251881Speter { 1664251881Speter svn_prop_inherited_item_t *iprop = 1665251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1666251881Speter 1667251881Speter svn_pool_clear(iterpool); 1668251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1669251881Speter iprop->path_or_url)); 1670251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1671251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1672251881Speter iprop->path_or_url)); 1673251881Speter } 1674251881Speter svn_pool_destroy(iterpool); 1675251881Speter } 1676251881Speter 1677251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1678251881Speter 1679251881Speter /* Now send the file's contents. */ 1680251881Speter if (want_contents) 1681251881Speter { 1682251881Speter err = SVN_NO_ERROR; 1683251881Speter while (1) 1684251881Speter { 1685251881Speter len = sizeof(buf); 1686289180Speter err = svn_stream_read_full(contents, buf, &len); 1687251881Speter if (err) 1688251881Speter break; 1689251881Speter if (len > 0) 1690251881Speter { 1691251881Speter write_str.data = buf; 1692251881Speter write_str.len = len; 1693251881Speter SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str)); 1694251881Speter } 1695251881Speter if (len < sizeof(buf)) 1696251881Speter { 1697251881Speter err = svn_stream_close(contents); 1698251881Speter break; 1699251881Speter } 1700251881Speter } 1701251881Speter write_err = svn_ra_svn__write_cstring(conn, pool, ""); 1702251881Speter if (write_err) 1703251881Speter { 1704251881Speter svn_error_clear(err); 1705251881Speter return write_err; 1706251881Speter } 1707251881Speter SVN_CMD_ERR(err); 1708251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1709251881Speter } 1710251881Speter 1711251881Speter return SVN_NO_ERROR; 1712251881Speter} 1713251881Speter 1714362181Sdim/* Translate all the words in DIRENT_FIELDS_LIST into the flags in 1715362181Sdim * DIRENT_FIELDS_P. If DIRENT_FIELDS_LIST is NULL, set all flags. */ 1716362181Sdimstatic svn_error_t * 1717362181Sdimparse_dirent_fields(apr_uint32_t *dirent_fields_p, 1718362181Sdim svn_ra_svn__list_t *dirent_fields_list) 1719251881Speter{ 1720362181Sdim static const svn_string_t str_kind 1721362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_KIND); 1722362181Sdim static const svn_string_t str_size 1723362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_SIZE); 1724362181Sdim static const svn_string_t str_has_props 1725362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_HAS_PROPS); 1726362181Sdim static const svn_string_t str_created_rev 1727362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_CREATED_REV); 1728362181Sdim static const svn_string_t str_time 1729362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_TIME); 1730362181Sdim static const svn_string_t str_last_author 1731362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_DIRENT_LAST_AUTHOR); 1732251881Speter 1733362181Sdim apr_uint32_t dirent_fields; 1734251881Speter 1735251881Speter if (! dirent_fields_list) 1736251881Speter { 1737251881Speter dirent_fields = SVN_DIRENT_ALL; 1738251881Speter } 1739251881Speter else 1740251881Speter { 1741362181Sdim int i; 1742251881Speter dirent_fields = 0; 1743251881Speter 1744251881Speter for (i = 0; i < dirent_fields_list->nelts; ++i) 1745251881Speter { 1746362181Sdim svn_ra_svn__item_t *elt 1747362181Sdim = &SVN_RA_SVN__LIST_ITEM(dirent_fields_list, i); 1748251881Speter 1749251881Speter if (elt->kind != SVN_RA_SVN_WORD) 1750251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1751362181Sdim "Dirent field not a word"); 1752251881Speter 1753362181Sdim if (svn_string_compare(&str_kind, &elt->u.word)) 1754251881Speter dirent_fields |= SVN_DIRENT_KIND; 1755362181Sdim else if (svn_string_compare(&str_size, &elt->u.word)) 1756251881Speter dirent_fields |= SVN_DIRENT_SIZE; 1757362181Sdim else if (svn_string_compare(&str_has_props, &elt->u.word)) 1758251881Speter dirent_fields |= SVN_DIRENT_HAS_PROPS; 1759362181Sdim else if (svn_string_compare(&str_created_rev, &elt->u.word)) 1760251881Speter dirent_fields |= SVN_DIRENT_CREATED_REV; 1761362181Sdim else if (svn_string_compare(&str_time, &elt->u.word)) 1762251881Speter dirent_fields |= SVN_DIRENT_TIME; 1763362181Sdim else if (svn_string_compare(&str_last_author, &elt->u.word)) 1764251881Speter dirent_fields |= SVN_DIRENT_LAST_AUTHOR; 1765251881Speter } 1766251881Speter } 1767251881Speter 1768362181Sdim *dirent_fields_p = dirent_fields; 1769362181Sdim return SVN_NO_ERROR; 1770362181Sdim} 1771251881Speter 1772362181Sdimstatic svn_error_t * 1773362181Sdimget_dir(svn_ra_svn_conn_t *conn, 1774362181Sdim apr_pool_t *pool, 1775362181Sdim svn_ra_svn__list_t *params, 1776362181Sdim void *baton) 1777362181Sdim{ 1778362181Sdim server_baton_t *b = baton; 1779362181Sdim const char *path, *full_path, *canonical_path; 1780362181Sdim svn_revnum_t rev; 1781362181Sdim apr_hash_t *entries, *props = NULL; 1782362181Sdim apr_array_header_t *inherited_props; 1783362181Sdim apr_hash_index_t *hi; 1784362181Sdim svn_fs_root_t *root; 1785362181Sdim apr_pool_t *subpool; 1786362181Sdim svn_boolean_t want_props, want_contents; 1787362181Sdim apr_uint64_t wants_inherited_props; 1788362181Sdim apr_uint32_t dirent_fields; 1789362181Sdim svn_ra_svn__list_t *dirent_fields_list = NULL; 1790362181Sdim int i; 1791362181Sdim authz_baton_t ab; 1792362181Sdim 1793362181Sdim ab.server = b; 1794362181Sdim ab.conn = conn; 1795362181Sdim 1796362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)bb?l?B", &path, &rev, 1797362181Sdim &want_props, &want_contents, 1798362181Sdim &dirent_fields_list, 1799362181Sdim &wants_inherited_props)); 1800362181Sdim 1801362181Sdim if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1802362181Sdim wants_inherited_props = FALSE; 1803362181Sdim 1804362181Sdim SVN_ERR(parse_dirent_fields(&dirent_fields, dirent_fields_list)); 1805362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 1806362181Sdim pool, pool)); 1807362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path, 1808362181Sdim pool); 1809362181Sdim 1810251881Speter /* Check authorizations */ 1811251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1812251881Speter full_path, FALSE)); 1813251881Speter 1814251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1815289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1816251881Speter 1817251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1818251881Speter svn_log__get_dir(full_path, rev, 1819251881Speter want_contents, want_props, 1820251881Speter dirent_fields, pool))); 1821251881Speter 1822251881Speter /* Fetch the root of the appropriate revision. */ 1823289180Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 1824251881Speter 1825266731Speter /* Fetch the directory's explicit and/or inherited properties if 1826266731Speter requested. Although the wants-iprops boolean was added to the 1827266731Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1828251881Speter if (want_props || wants_inherited_props) 1829266731Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1830266731Speter wants_inherited_props ? &inherited_props : NULL, 1831266731Speter &ab, root, full_path, 1832251881Speter pool)); 1833251881Speter 1834286506Speter /* Fetch the directories' entries before starting the response, to allow 1835286506Speter proper error handling in cases like when FULL_PATH doesn't exist */ 1836286506Speter if (want_contents) 1837286506Speter SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); 1838286506Speter 1839251881Speter /* Begin response ... */ 1840251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev)); 1841251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1842251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!")); 1843251881Speter 1844251881Speter /* Fetch the directory entries if requested and send them immediately. */ 1845251881Speter if (want_contents) 1846251881Speter { 1847251881Speter /* Use epoch for a placeholder for a missing date. */ 1848251881Speter const char *missing_date = svn_time_to_cstring(0, pool); 1849251881Speter 1850251881Speter /* Transform the hash table's FS entries into dirents. This probably 1851251881Speter * belongs in libsvn_repos. */ 1852251881Speter subpool = svn_pool_create(pool); 1853251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1854251881Speter { 1855289180Speter const char *name = apr_hash_this_key(hi); 1856289180Speter svn_fs_dirent_t *fsent = apr_hash_this_val(hi); 1857251881Speter const char *file_path; 1858251881Speter 1859251881Speter /* The fields in the entry tuple. */ 1860251881Speter svn_node_kind_t entry_kind = svn_node_none; 1861251881Speter svn_filesize_t entry_size = 0; 1862251881Speter svn_boolean_t has_props = FALSE; 1863251881Speter /* If 'created rev' was not requested, send 0. We can't use 1864251881Speter * SVN_INVALID_REVNUM as the tuple field is not optional. 1865251881Speter * See the email thread on dev@, 2012-03-28, subject 1866251881Speter * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra", 1867251881Speter * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */ 1868251881Speter svn_revnum_t created_rev = 0; 1869251881Speter const char *cdate = NULL; 1870251881Speter const char *last_author = NULL; 1871251881Speter 1872251881Speter svn_pool_clear(subpool); 1873251881Speter 1874251881Speter file_path = svn_fspath__join(full_path, name, subpool); 1875289180Speter if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE)) 1876251881Speter continue; 1877251881Speter 1878251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1879251881Speter entry_kind = fsent->kind; 1880251881Speter 1881251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1882362181Sdim if (fsent->kind != svn_node_dir) 1883251881Speter SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, 1884251881Speter subpool)); 1885251881Speter 1886251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1887251881Speter { 1888251881Speter /* has_props */ 1889289180Speter SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path, 1890251881Speter subpool)); 1891251881Speter } 1892251881Speter 1893251881Speter if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1894251881Speter || (dirent_fields & SVN_DIRENT_TIME) 1895251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1896251881Speter { 1897251881Speter /* created_rev, last_author, time */ 1898251881Speter SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev, 1899251881Speter &cdate, 1900251881Speter &last_author, 1901251881Speter root, 1902251881Speter file_path, 1903251881Speter subpool)); 1904251881Speter } 1905251881Speter 1906251881Speter /* The client does not properly handle a missing CDATE. For 1907251881Speter interoperability purposes, we must fill in some junk. 1908251881Speter 1909251881Speter See libsvn_ra_svn/client.c:ra_svn_get_dir() */ 1910251881Speter if (cdate == NULL) 1911251881Speter cdate = missing_date; 1912251881Speter 1913251881Speter /* Send the entry. */ 1914251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name, 1915251881Speter svn_node_kind_to_word(entry_kind), 1916251881Speter (apr_uint64_t) entry_size, 1917251881Speter has_props, created_rev, 1918251881Speter cdate, last_author)); 1919251881Speter } 1920251881Speter svn_pool_destroy(subpool); 1921251881Speter } 1922251881Speter 1923251881Speter if (wants_inherited_props) 1924251881Speter { 1925251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1926251881Speter 1927251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1928251881Speter for (i = 0; i < inherited_props->nelts; i++) 1929251881Speter { 1930251881Speter svn_prop_inherited_item_t *iprop = 1931251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1932251881Speter 1933251881Speter svn_pool_clear(iterpool); 1934251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1935251881Speter iprop->path_or_url)); 1936251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1937251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1938251881Speter iprop->path_or_url)); 1939251881Speter } 1940251881Speter svn_pool_destroy(iterpool); 1941251881Speter } 1942251881Speter 1943251881Speter /* Finish response. */ 1944251881Speter return svn_ra_svn__write_tuple(conn, pool, "!))"); 1945251881Speter} 1946251881Speter 1947362181Sdimstatic svn_error_t * 1948362181Sdimupdate(svn_ra_svn_conn_t *conn, 1949362181Sdim apr_pool_t *pool, 1950362181Sdim svn_ra_svn__list_t *params, 1951362181Sdim void *baton) 1952251881Speter{ 1953251881Speter server_baton_t *b = baton; 1954251881Speter svn_revnum_t rev; 1955362181Sdim const char *target, *full_path, *depth_word, *canonical_target; 1956251881Speter svn_boolean_t recurse; 1957289180Speter svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ 1958289180Speter svn_tristate_t ignore_ancestry; /* Optional; default FALSE */ 1959251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1960251881Speter handle that by converting recurse if necessary. */ 1961251881Speter svn_depth_t depth = svn_depth_unknown; 1962251881Speter svn_boolean_t is_checkout; 1963251881Speter 1964251881Speter /* Parse the arguments. */ 1965362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cb?w3?3", &rev, &target, 1966251881Speter &recurse, &depth_word, 1967251881Speter &send_copyfrom_args, &ignore_ancestry)); 1968362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target, 1969362181Sdim pool, pool)); 1970362181Sdim target = canonical_target; 1971251881Speter 1972251881Speter if (depth_word) 1973251881Speter depth = svn_depth_from_word(depth_word); 1974251881Speter else 1975251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1976251881Speter 1977289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, target, pool); 1978251881Speter /* Check authorization and authenticate the user if necessary. */ 1979251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE)); 1980251881Speter 1981251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1982289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1983251881Speter 1984251881Speter SVN_ERR(accept_report(&is_checkout, NULL, 1985251881Speter conn, pool, b, rev, target, NULL, TRUE, 1986251881Speter depth, 1987289180Speter (send_copyfrom_args == svn_tristate_true), 1988289180Speter (ignore_ancestry == svn_tristate_true))); 1989251881Speter if (is_checkout) 1990251881Speter { 1991251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1992251881Speter svn_log__checkout(full_path, rev, 1993251881Speter depth, pool))); 1994251881Speter } 1995251881Speter else 1996251881Speter { 1997251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1998251881Speter svn_log__update(full_path, rev, depth, 1999289180Speter (send_copyfrom_args 2000289180Speter == svn_tristate_true), 2001289180Speter pool))); 2002251881Speter } 2003251881Speter 2004251881Speter return SVN_NO_ERROR; 2005251881Speter} 2006251881Speter 2007362181Sdimstatic svn_error_t * 2008362181Sdimswitch_cmd(svn_ra_svn_conn_t *conn, 2009362181Sdim apr_pool_t *pool, 2010362181Sdim svn_ra_svn__list_t *params, 2011362181Sdim void *baton) 2012251881Speter{ 2013251881Speter server_baton_t *b = baton; 2014251881Speter svn_revnum_t rev; 2015251881Speter const char *target, *depth_word; 2016362181Sdim const char *switch_url, *switch_path, *canonical_url, *canonical_target; 2017251881Speter svn_boolean_t recurse; 2018251881Speter /* Default to unknown. Old clients won't send depth, but we'll 2019251881Speter handle that by converting recurse if necessary. */ 2020251881Speter svn_depth_t depth = svn_depth_unknown; 2021289180Speter svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ 2022289180Speter svn_tristate_t ignore_ancestry; /* Optional; default TRUE */ 2023251881Speter 2024251881Speter /* Parse the arguments. */ 2025362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbc?w?33", &rev, &target, 2026251881Speter &recurse, &switch_url, &depth_word, 2027251881Speter &send_copyfrom_args, &ignore_ancestry)); 2028362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target, 2029362181Sdim pool, pool)); 2030362181Sdim target = canonical_target; 2031362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, switch_url, pool, 2032362181Sdim pool)); 2033362181Sdim switch_url = canonical_url; 2034251881Speter if (depth_word) 2035251881Speter depth = svn_depth_from_word(depth_word); 2036251881Speter else 2037251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 2038251881Speter 2039251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2040251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2041289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2042251881Speter 2043289180Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, 2044289180Speter pool), 2045251881Speter svn_path_uri_decode(switch_url, pool), 2046251881Speter &switch_path)); 2047251881Speter 2048251881Speter { 2049289180Speter const char *full_path = svn_fspath__join(b->repository->fs_path->data, 2050289180Speter target, pool); 2051251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2052251881Speter svn_log__switch(full_path, switch_path, rev, 2053251881Speter depth, pool))); 2054251881Speter } 2055251881Speter 2056251881Speter return accept_report(NULL, NULL, 2057251881Speter conn, pool, b, rev, target, switch_path, TRUE, 2058251881Speter depth, 2059289180Speter (send_copyfrom_args == svn_tristate_true), 2060289180Speter (ignore_ancestry != svn_tristate_false)); 2061251881Speter} 2062251881Speter 2063362181Sdimstatic svn_error_t * 2064362181Sdimstatus(svn_ra_svn_conn_t *conn, 2065362181Sdim apr_pool_t *pool, 2066362181Sdim svn_ra_svn__list_t *params, 2067362181Sdim void *baton) 2068251881Speter{ 2069251881Speter server_baton_t *b = baton; 2070251881Speter svn_revnum_t rev; 2071362181Sdim const char *target, *depth_word, *canonical_target; 2072251881Speter svn_boolean_t recurse; 2073251881Speter /* Default to unknown. Old clients won't send depth, but we'll 2074251881Speter handle that by converting recurse if necessary. */ 2075251881Speter svn_depth_t depth = svn_depth_unknown; 2076251881Speter 2077251881Speter /* Parse the arguments. */ 2078362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "cb?(?r)?w", 2079251881Speter &target, &recurse, &rev, &depth_word)); 2080362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target, 2081362181Sdim pool, pool)); 2082362181Sdim target = canonical_target; 2083251881Speter 2084251881Speter if (depth_word) 2085251881Speter depth = svn_depth_from_word(depth_word); 2086251881Speter else 2087251881Speter depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse); 2088251881Speter 2089251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2090251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2091289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2092251881Speter 2093251881Speter { 2094289180Speter const char *full_path = svn_fspath__join(b->repository->fs_path->data, 2095289180Speter target, pool); 2096251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2097251881Speter svn_log__status(full_path, rev, depth, pool))); 2098251881Speter } 2099251881Speter 2100251881Speter return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE, 2101251881Speter depth, FALSE, FALSE); 2102251881Speter} 2103251881Speter 2104362181Sdimstatic svn_error_t * 2105362181Sdimdiff(svn_ra_svn_conn_t *conn, 2106362181Sdim apr_pool_t *pool, 2107362181Sdim svn_ra_svn__list_t *params, 2108362181Sdim void *baton) 2109251881Speter{ 2110251881Speter server_baton_t *b = baton; 2111251881Speter svn_revnum_t rev; 2112362181Sdim const char *target, *versus_url, *versus_path, *depth_word, *canonical_url; 2113362181Sdim const char *canonical_target; 2114251881Speter svn_boolean_t recurse, ignore_ancestry; 2115251881Speter svn_boolean_t text_deltas; 2116251881Speter /* Default to unknown. Old clients won't send depth, but we'll 2117251881Speter handle that by converting recurse if necessary. */ 2118251881Speter svn_depth_t depth = svn_depth_unknown; 2119251881Speter 2120251881Speter /* Parse the arguments. */ 2121251881Speter if (params->nelts == 5) 2122251881Speter { 2123251881Speter /* Clients before 1.4 don't send the text_deltas boolean or depth. */ 2124362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbc", &rev, &target, 2125251881Speter &recurse, &ignore_ancestry, &versus_url)); 2126251881Speter text_deltas = TRUE; 2127251881Speter depth_word = NULL; 2128251881Speter } 2129251881Speter else 2130251881Speter { 2131362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "(?r)cbbcb?w", 2132251881Speter &rev, &target, &recurse, 2133251881Speter &ignore_ancestry, &versus_url, 2134251881Speter &text_deltas, &depth_word)); 2135251881Speter } 2136362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_target, NULL, target, 2137362181Sdim pool, pool)); 2138362181Sdim target = canonical_target; 2139362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, versus_url, 2140362181Sdim pool, pool)); 2141362181Sdim versus_url = canonical_url; 2142251881Speter 2143251881Speter if (depth_word) 2144251881Speter depth = svn_depth_from_word(depth_word); 2145251881Speter else 2146251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 2147251881Speter 2148251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2149251881Speter 2150251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2151289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2152289180Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, 2153289180Speter pool), 2154251881Speter svn_path_uri_decode(versus_url, pool), 2155251881Speter &versus_path)); 2156251881Speter 2157251881Speter { 2158289180Speter const char *full_path = svn_fspath__join(b->repository->fs_path->data, 2159289180Speter target, pool); 2160251881Speter svn_revnum_t from_rev; 2161251881Speter SVN_ERR(accept_report(NULL, &from_rev, 2162251881Speter conn, pool, b, rev, target, versus_path, 2163251881Speter text_deltas, depth, FALSE, ignore_ancestry)); 2164251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2165251881Speter svn_log__diff(full_path, from_rev, versus_path, 2166251881Speter rev, depth, ignore_ancestry, 2167251881Speter pool))); 2168251881Speter } 2169251881Speter return SVN_NO_ERROR; 2170251881Speter} 2171251881Speter 2172362181Sdim/* Baton type to be used with mergeinfo_receiver. */ 2173362181Sdimtypedef struct mergeinfo_receiver_baton_t 2174362181Sdim{ 2175362181Sdim /* Send the response over this connection. */ 2176362181Sdim svn_ra_svn_conn_t *conn; 2177362181Sdim 2178362181Sdim /* Start path of the query; report paths relative to this one. */ 2179362181Sdim const char *fs_path; 2180362181Sdim 2181362181Sdim /* Did we already send the opening sequence? */ 2182362181Sdim svn_boolean_t starting_tuple_sent; 2183362181Sdim} mergeinfo_receiver_baton_t; 2184362181Sdim 2185362181Sdim/* Utility method sending the start of the "get m/i" response once 2186362181Sdim over BATON->CONN. */ 2187362181Sdimstatic svn_error_t * 2188362181Sdimsend_mergeinfo_starting_tuple(mergeinfo_receiver_baton_t *baton, 2189362181Sdim apr_pool_t *scratch_pool) 2190362181Sdim{ 2191362181Sdim if (baton->starting_tuple_sent) 2192362181Sdim return SVN_NO_ERROR; 2193362181Sdim 2194362181Sdim SVN_ERR(svn_ra_svn__write_tuple(baton->conn, scratch_pool, 2195362181Sdim "w((!", "success")); 2196362181Sdim baton->starting_tuple_sent = TRUE; 2197362181Sdim 2198362181Sdim return SVN_NO_ERROR; 2199362181Sdim} 2200362181Sdim 2201362181Sdim/* Implements svn_repos_mergeinfo_receiver_t, sending the MERGEINFO 2202362181Sdim * out over the connection in the mergeinfo_receiver_baton_t * BATON. */ 2203362181Sdimstatic svn_error_t * 2204362181Sdimmergeinfo_receiver(const char *path, 2205362181Sdim svn_mergeinfo_t mergeinfo, 2206362181Sdim void *baton, 2207362181Sdim apr_pool_t *scratch_pool) 2208362181Sdim{ 2209362181Sdim mergeinfo_receiver_baton_t *b = baton; 2210362181Sdim svn_string_t *mergeinfo_string; 2211362181Sdim 2212362181Sdim /* Delay starting the response until we checked that the initial 2213362181Sdim request went through. We are at that point now b/c we've got 2214362181Sdim the first results in. */ 2215362181Sdim SVN_ERR(send_mergeinfo_starting_tuple(b, scratch_pool)); 2216362181Sdim 2217362181Sdim /* Adjust the path info and send the m/i. */ 2218362181Sdim path = svn_fspath__skip_ancestor(b->fs_path, path); 2219362181Sdim SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, mergeinfo, 2220362181Sdim scratch_pool)); 2221362181Sdim SVN_ERR(svn_ra_svn__write_tuple(b->conn, scratch_pool, "cs", path, 2222362181Sdim mergeinfo_string)); 2223362181Sdim 2224362181Sdim return SVN_NO_ERROR; 2225362181Sdim} 2226362181Sdim 2227251881Speter/* Regardless of whether a client's capabilities indicate an 2228251881Speter understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO), 2229251881Speter we provide a response. 2230251881Speter 2231251881Speter ASSUMPTION: When performing a 'merge' with two URLs at different 2232251881Speter revisions, the client will call this command more than once. */ 2233362181Sdimstatic svn_error_t * 2234362181Sdimget_mergeinfo(svn_ra_svn_conn_t *conn, 2235362181Sdim apr_pool_t *pool, 2236362181Sdim svn_ra_svn__list_t *params, 2237362181Sdim void *baton) 2238251881Speter{ 2239251881Speter server_baton_t *b = baton; 2240251881Speter svn_revnum_t rev; 2241362181Sdim svn_ra_svn__list_t *paths; 2242362181Sdim apr_array_header_t *canonical_paths; 2243251881Speter int i; 2244251881Speter const char *inherit_word; 2245251881Speter svn_mergeinfo_inheritance_t inherit; 2246251881Speter svn_boolean_t include_descendants; 2247251881Speter authz_baton_t ab; 2248362181Sdim mergeinfo_receiver_baton_t mergeinfo_baton; 2249251881Speter 2250251881Speter ab.server = b; 2251251881Speter ab.conn = conn; 2252251881Speter 2253362181Sdim mergeinfo_baton.conn = conn; 2254362181Sdim mergeinfo_baton.fs_path = b->repository->fs_path->data; 2255362181Sdim mergeinfo_baton.starting_tuple_sent = FALSE; 2256362181Sdim 2257362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)wb", &paths, &rev, 2258251881Speter &inherit_word, &include_descendants)); 2259251881Speter inherit = svn_inheritance_from_word(inherit_word); 2260251881Speter 2261251881Speter /* Canonicalize the paths which mergeinfo has been requested for. */ 2262251881Speter canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2263251881Speter for (i = 0; i < paths->nelts; i++) 2264251881Speter { 2265362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(paths, i); 2266362181Sdim const char *full_path, *canonical_path; 2267251881Speter 2268251881Speter if (item->kind != SVN_RA_SVN_STRING) 2269251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2270251881Speter _("Path is not a string")); 2271362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, 2272362181Sdim item->u.string.data, pool, pool)); 2273362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2274362181Sdim canonical_path, pool); 2275251881Speter APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; 2276251881Speter } 2277251881Speter 2278251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2279251881Speter svn_log__get_mergeinfo(canonical_paths, inherit, 2280251881Speter include_descendants, 2281251881Speter pool))); 2282251881Speter 2283251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2284251881Speter 2285362181Sdim SVN_CMD_ERR(svn_repos_fs_get_mergeinfo2(b->repository->repos, 2286362181Sdim canonical_paths, rev, 2287362181Sdim inherit, 2288362181Sdim include_descendants, 2289362181Sdim authz_check_access_cb_func(b), &ab, 2290362181Sdim mergeinfo_receiver, 2291362181Sdim &mergeinfo_baton, 2292362181Sdim pool)); 2293251881Speter 2294362181Sdim /* We might not have sent anything 2295362181Sdim => ensure to begin the response in any case. */ 2296362181Sdim SVN_ERR(send_mergeinfo_starting_tuple(&mergeinfo_baton, pool)); 2297251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2298251881Speter 2299251881Speter return SVN_NO_ERROR; 2300251881Speter} 2301251881Speter 2302362181Sdim/* Send a changed paths list entry to the client. 2303362181Sdim This implements svn_repos_path_change_receiver_t. */ 2304362181Sdimstatic svn_error_t * 2305362181Sdimpath_change_receiver(void *baton, 2306362181Sdim svn_repos_path_change_t *change, 2307362181Sdim apr_pool_t *scratch_pool) 2308251881Speter{ 2309362181Sdim const char symbol[] = "MADR"; 2310362181Sdim 2311251881Speter log_baton_t *b = baton; 2312251881Speter svn_ra_svn_conn_t *conn = b->conn; 2313362181Sdim 2314362181Sdim /* Sanitize and convert change kind to ra-svn level action. 2315362181Sdim 2316362181Sdim Pushing that conversion down into libsvn_ra_svn would add yet another 2317362181Sdim API dependency there. */ 2318362181Sdim char action = ( change->change_kind < svn_fs_path_change_modify 2319362181Sdim || change->change_kind > svn_fs_path_change_replace) 2320362181Sdim ? 0 2321362181Sdim : symbol[change->change_kind]; 2322362181Sdim 2323362181Sdim /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */ 2324362181Sdim if (!b->started) 2325362181Sdim { 2326362181Sdim SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); 2327362181Sdim SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); 2328362181Sdim b->started = TRUE; 2329362181Sdim } 2330362181Sdim 2331362181Sdim /* Serialize CHANGE. */ 2332362181Sdim SVN_ERR(svn_ra_svn__write_data_log_changed_path( 2333362181Sdim conn, scratch_pool, 2334362181Sdim &change->path, 2335362181Sdim action, 2336362181Sdim change->copyfrom_path, 2337362181Sdim change->copyfrom_rev, 2338362181Sdim change->node_kind, 2339362181Sdim change->text_mod, 2340362181Sdim change->prop_mod)); 2341362181Sdim 2342362181Sdim return SVN_NO_ERROR; 2343362181Sdim} 2344362181Sdim 2345362181Sdim/* Send a the meta data and the revpros for LOG_ENTRY to the client. 2346362181Sdim This implements svn_log_entry_receiver_t. */ 2347362181Sdimstatic svn_error_t * 2348362181Sdimrevision_receiver(void *baton, 2349362181Sdim svn_repos_log_entry_t *log_entry, 2350362181Sdim apr_pool_t *scratch_pool) 2351362181Sdim{ 2352362181Sdim log_baton_t *b = baton; 2353362181Sdim svn_ra_svn_conn_t *conn = b->conn; 2354251881Speter svn_boolean_t invalid_revnum = FALSE; 2355289180Speter const svn_string_t *author, *date, *message; 2356289180Speter unsigned revprop_count; 2357251881Speter 2358251881Speter if (log_entry->revision == SVN_INVALID_REVNUM) 2359251881Speter { 2360251881Speter /* If the stack depth is zero, we've seen the last revision, so don't 2361251881Speter send it, just return. */ 2362251881Speter if (b->stack_depth == 0) 2363251881Speter return SVN_NO_ERROR; 2364251881Speter 2365251881Speter /* Because the svn protocol won't let us send an invalid revnum, we have 2366251881Speter to fudge here and send an additional flag. */ 2367251881Speter log_entry->revision = 0; 2368251881Speter invalid_revnum = TRUE; 2369251881Speter b->stack_depth--; 2370251881Speter } 2371251881Speter 2372289180Speter svn_compat_log_revprops_out_string(&author, &date, &message, 2373289180Speter log_entry->revprops); 2374362181Sdim 2375362181Sdim /* Revprops list filtering is somewhat expensive. 2376362181Sdim Avoid doing that for the 90% case where only the standard revprops 2377362181Sdim have been requested and delivered. */ 2378362181Sdim if (author && date && message && apr_hash_count(log_entry->revprops) == 3) 2379362181Sdim { 2380362181Sdim revprop_count = 0; 2381362181Sdim } 2382289180Speter else 2383362181Sdim { 2384362181Sdim svn_compat_log_revprops_clear(log_entry->revprops); 2385362181Sdim if (log_entry->revprops) 2386362181Sdim revprop_count = apr_hash_count(log_entry->revprops); 2387362181Sdim else 2388362181Sdim revprop_count = 0; 2389362181Sdim } 2390289180Speter 2391362181Sdim /* Open lists once: LOG_ENTRY and LOG_ENTRY->CHANGED_PATHS. */ 2392362181Sdim if (!b->started) 2393251881Speter { 2394362181Sdim SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); 2395362181Sdim SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); 2396251881Speter } 2397251881Speter 2398362181Sdim /* Close LOG_ENTRY->CHANGED_PATHS. */ 2399362181Sdim SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); 2400362181Sdim b->started = FALSE; 2401362181Sdim 2402289180Speter /* send LOG_ENTRY main members */ 2403362181Sdim SVN_ERR(svn_ra_svn__write_data_log_entry(conn, scratch_pool, 2404289180Speter log_entry->revision, 2405289180Speter author, date, message, 2406289180Speter log_entry->has_children, 2407289180Speter invalid_revnum, revprop_count)); 2408289180Speter 2409289180Speter /* send LOG_ENTRY->REVPROPS */ 2410362181Sdim SVN_ERR(svn_ra_svn__start_list(conn, scratch_pool)); 2411289180Speter if (revprop_count) 2412362181Sdim SVN_ERR(svn_ra_svn__write_proplist(conn, scratch_pool, 2413362181Sdim log_entry->revprops)); 2414362181Sdim SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); 2415289180Speter 2416289180Speter /* send LOG_ENTRY members that were added in later SVN releases */ 2417362181Sdim SVN_ERR(svn_ra_svn__write_boolean(conn, scratch_pool, 2418362181Sdim log_entry->subtractive_merge)); 2419362181Sdim SVN_ERR(svn_ra_svn__end_list(conn, scratch_pool)); 2420289180Speter 2421251881Speter if (log_entry->has_children) 2422251881Speter b->stack_depth++; 2423251881Speter 2424251881Speter return SVN_NO_ERROR; 2425251881Speter} 2426251881Speter 2427362181Sdimstatic svn_error_t * 2428362181Sdimlog_cmd(svn_ra_svn_conn_t *conn, 2429362181Sdim apr_pool_t *pool, 2430362181Sdim svn_ra_svn__list_t *params, 2431362181Sdim void *baton) 2432251881Speter{ 2433251881Speter svn_error_t *err, *write_err; 2434251881Speter server_baton_t *b = baton; 2435251881Speter svn_revnum_t start_rev, end_rev; 2436362181Sdim const char *full_path, *canonical_path; 2437251881Speter svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; 2438362181Sdim apr_array_header_t *full_paths, *revprops; 2439362181Sdim svn_ra_svn__list_t *paths, *revprop_items; 2440251881Speter char *revprop_word; 2441362181Sdim svn_ra_svn__item_t *elt; 2442251881Speter int i; 2443251881Speter apr_uint64_t limit, include_merged_revs_param; 2444251881Speter log_baton_t lb; 2445251881Speter authz_baton_t ab; 2446251881Speter 2447251881Speter ab.server = b; 2448251881Speter ab.conn = conn; 2449251881Speter 2450362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "l(?r)(?r)bb?n?Bwl", &paths, 2451251881Speter &start_rev, &end_rev, &send_changed_paths, 2452251881Speter &strict_node, &limit, 2453251881Speter &include_merged_revs_param, 2454251881Speter &revprop_word, &revprop_items)); 2455251881Speter 2456251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2457251881Speter include_merged_revisions = FALSE; 2458251881Speter else 2459251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2460251881Speter 2461251881Speter if (revprop_word == NULL) 2462251881Speter /* pre-1.5 client */ 2463251881Speter revprops = svn_compat_log_revprops_in(pool); 2464251881Speter else if (strcmp(revprop_word, "all-revprops") == 0) 2465251881Speter revprops = NULL; 2466251881Speter else if (strcmp(revprop_word, "revprops") == 0) 2467251881Speter { 2468251881Speter SVN_ERR_ASSERT(revprop_items); 2469251881Speter 2470251881Speter revprops = apr_array_make(pool, revprop_items->nelts, 2471251881Speter sizeof(char *)); 2472251881Speter for (i = 0; i < revprop_items->nelts; i++) 2473251881Speter { 2474362181Sdim elt = &SVN_RA_SVN__LIST_ITEM(revprop_items, i); 2475251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2476251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2477251881Speter _("Log revprop entry not a string")); 2478362181Sdim APR_ARRAY_PUSH(revprops, const char *) = elt->u.string.data; 2479251881Speter } 2480251881Speter } 2481251881Speter else 2482251881Speter return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2483251881Speter _("Unknown revprop word '%s' in log command"), 2484251881Speter revprop_word); 2485251881Speter 2486251881Speter /* If we got an unspecified number then the user didn't send us anything, 2487251881Speter so we assume no limit. If it's larger than INT_MAX then someone is 2488251881Speter messing with us, since we know the svn client libraries will never send 2489251881Speter us anything that big, so play it safe and default to no limit. */ 2490251881Speter if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX) 2491251881Speter limit = 0; 2492251881Speter 2493251881Speter full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2494251881Speter for (i = 0; i < paths->nelts; i++) 2495251881Speter { 2496362181Sdim elt = &SVN_RA_SVN__LIST_ITEM(paths, i); 2497251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2498251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2499251881Speter _("Log path entry not a string")); 2500362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, 2501362181Sdim elt->u.string.data, pool, pool)); 2502362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2503362181Sdim canonical_path, pool); 2504251881Speter APR_ARRAY_PUSH(full_paths, const char *) = full_path; 2505251881Speter } 2506251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2507251881Speter 2508251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2509251881Speter svn_log__log(full_paths, start_rev, end_rev, 2510251881Speter (int) limit, send_changed_paths, 2511251881Speter strict_node, include_merged_revisions, 2512251881Speter revprops, pool))); 2513251881Speter 2514251881Speter /* Get logs. (Can't report errors back to the client at this point.) */ 2515289180Speter lb.fs_path = b->repository->fs_path->data; 2516251881Speter lb.conn = conn; 2517251881Speter lb.stack_depth = 0; 2518362181Sdim lb.started = FALSE; 2519362181Sdim err = svn_repos_get_logs5(b->repository->repos, full_paths, start_rev, 2520362181Sdim end_rev, (int) limit, 2521289180Speter strict_node, include_merged_revisions, 2522289180Speter revprops, authz_check_access_cb_func(b), &ab, 2523362181Sdim send_changed_paths ? path_change_receiver : NULL, 2524362181Sdim send_changed_paths ? &lb : NULL, 2525362181Sdim revision_receiver, &lb, pool); 2526251881Speter 2527251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2528251881Speter if (write_err) 2529251881Speter { 2530251881Speter svn_error_clear(err); 2531251881Speter return write_err; 2532251881Speter } 2533251881Speter SVN_CMD_ERR(err); 2534251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2535251881Speter return SVN_NO_ERROR; 2536251881Speter} 2537251881Speter 2538362181Sdimstatic svn_error_t * 2539362181Sdimcheck_path(svn_ra_svn_conn_t *conn, 2540362181Sdim apr_pool_t *pool, 2541362181Sdim svn_ra_svn__list_t *params, 2542362181Sdim void *baton) 2543251881Speter{ 2544251881Speter server_baton_t *b = baton; 2545251881Speter svn_revnum_t rev; 2546362181Sdim const char *path, *full_path, *canonical_path; 2547251881Speter svn_fs_root_t *root; 2548251881Speter svn_node_kind_t kind; 2549251881Speter 2550362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); 2551362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 2552362181Sdim pool, pool));; 2553289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 2554362181Sdim canonical_path, pool); 2555251881Speter 2556251881Speter /* Check authorizations */ 2557251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2558251881Speter full_path, FALSE)); 2559251881Speter 2560251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2561289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2562251881Speter 2563251881Speter SVN_ERR(log_command(b, conn, pool, "check-path %s@%d", 2564251881Speter svn_path_uri_encode(full_path, pool), rev)); 2565251881Speter 2566289180Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 2567251881Speter SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool)); 2568251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w", 2569251881Speter svn_node_kind_to_word(kind))); 2570251881Speter return SVN_NO_ERROR; 2571251881Speter} 2572251881Speter 2573362181Sdimstatic svn_error_t * 2574362181Sdimstat_cmd(svn_ra_svn_conn_t *conn, 2575362181Sdim apr_pool_t *pool, 2576362181Sdim svn_ra_svn__list_t *params, 2577362181Sdim void *baton) 2578251881Speter{ 2579251881Speter server_baton_t *b = baton; 2580251881Speter svn_revnum_t rev; 2581362181Sdim const char *path, *full_path, *cdate, *canonical_path; 2582251881Speter svn_fs_root_t *root; 2583251881Speter svn_dirent_t *dirent; 2584251881Speter 2585362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); 2586362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, pool, 2587362181Sdim pool)); 2588362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, canonical_path, 2589362181Sdim pool); 2590251881Speter 2591251881Speter /* Check authorizations */ 2592251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2593251881Speter full_path, FALSE)); 2594251881Speter 2595251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2596289180Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2597251881Speter 2598251881Speter SVN_ERR(log_command(b, conn, pool, "stat %s@%d", 2599251881Speter svn_path_uri_encode(full_path, pool), rev)); 2600251881Speter 2601289180Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 2602251881Speter SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool)); 2603251881Speter 2604251881Speter /* Need to return the equivalent of "(?l)", since that's what the 2605251881Speter client is reading. */ 2606251881Speter 2607251881Speter if (dirent == NULL) 2608251881Speter { 2609251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()")); 2610251881Speter return SVN_NO_ERROR; 2611251881Speter } 2612251881Speter 2613251881Speter cdate = (dirent->time == (time_t) -1) ? NULL 2614251881Speter : svn_time_to_cstring(dirent->time, pool); 2615251881Speter 2616251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", 2617251881Speter svn_node_kind_to_word(dirent->kind), 2618251881Speter (apr_uint64_t) dirent->size, 2619251881Speter dirent->has_props, dirent->created_rev, 2620251881Speter cdate, dirent->last_author)); 2621251881Speter 2622251881Speter return SVN_NO_ERROR; 2623251881Speter} 2624251881Speter 2625362181Sdimstatic svn_error_t * 2626362181Sdimget_locations(svn_ra_svn_conn_t *conn, 2627362181Sdim apr_pool_t *pool, 2628362181Sdim svn_ra_svn__list_t *params, 2629362181Sdim void *baton) 2630251881Speter{ 2631251881Speter svn_error_t *err, *write_err; 2632251881Speter server_baton_t *b = baton; 2633251881Speter svn_revnum_t revision; 2634362181Sdim apr_array_header_t *location_revisions; 2635362181Sdim svn_ra_svn__list_t *loc_revs_proto; 2636362181Sdim svn_ra_svn__item_t *elt; 2637251881Speter int i; 2638362181Sdim const char *relative_path, *canonical_path; 2639251881Speter svn_revnum_t peg_revision; 2640251881Speter apr_hash_t *fs_locations; 2641251881Speter const char *abs_path; 2642251881Speter authz_baton_t ab; 2643251881Speter 2644251881Speter ab.server = b; 2645251881Speter ab.conn = conn; 2646251881Speter 2647251881Speter /* Parse the arguments. */ 2648362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "crl", &relative_path, 2649251881Speter &peg_revision, 2650251881Speter &loc_revs_proto)); 2651362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path, 2652362181Sdim pool, pool)); 2653362181Sdim relative_path = canonical_path; 2654251881Speter 2655289180Speter abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, 2656289180Speter pool); 2657251881Speter 2658251881Speter location_revisions = apr_array_make(pool, loc_revs_proto->nelts, 2659251881Speter sizeof(svn_revnum_t)); 2660251881Speter for (i = 0; i < loc_revs_proto->nelts; i++) 2661251881Speter { 2662362181Sdim elt = &SVN_RA_SVN__LIST_ITEM(loc_revs_proto, i); 2663251881Speter if (elt->kind != SVN_RA_SVN_NUMBER) 2664251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2665251881Speter "Get-locations location revisions entry " 2666251881Speter "not a revision number"); 2667251881Speter revision = (svn_revnum_t)(elt->u.number); 2668251881Speter APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision; 2669251881Speter } 2670251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2671251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2672251881Speter svn_log__get_locations(abs_path, peg_revision, 2673251881Speter location_revisions, pool))); 2674251881Speter 2675251881Speter /* All the parameters are fine - let's perform the query against the 2676251881Speter * repository. */ 2677251881Speter 2678251881Speter /* We store both err and write_err here, so the client will get 2679251881Speter * the "done" even if there was an error in fetching the results. */ 2680251881Speter 2681289180Speter err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations, 2682289180Speter abs_path, peg_revision, 2683289180Speter location_revisions, 2684251881Speter authz_check_access_cb_func(b), &ab, 2685251881Speter pool); 2686251881Speter 2687251881Speter /* Now, write the results to the connection. */ 2688251881Speter if (!err) 2689251881Speter { 2690251881Speter if (fs_locations) 2691251881Speter { 2692251881Speter apr_hash_index_t *iter; 2693251881Speter 2694251881Speter for (iter = apr_hash_first(pool, fs_locations); iter; 2695251881Speter iter = apr_hash_next(iter)) 2696251881Speter { 2697289180Speter const svn_revnum_t *iter_key = apr_hash_this_key(iter); 2698289180Speter const char *iter_value = apr_hash_this_val(iter); 2699251881Speter 2700251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", 2701251881Speter *iter_key, iter_value)); 2702251881Speter } 2703251881Speter } 2704251881Speter } 2705251881Speter 2706251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2707251881Speter if (write_err) 2708251881Speter { 2709251881Speter svn_error_clear(err); 2710251881Speter return write_err; 2711251881Speter } 2712251881Speter SVN_CMD_ERR(err); 2713251881Speter 2714251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2715251881Speter 2716251881Speter return SVN_NO_ERROR; 2717251881Speter} 2718251881Speter 2719251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment, 2720251881Speter void *baton, 2721251881Speter apr_pool_t *pool) 2722251881Speter{ 2723251881Speter svn_ra_svn_conn_t *conn = baton; 2724251881Speter return svn_ra_svn__write_tuple(conn, pool, "rr(?c)", 2725251881Speter segment->range_start, 2726251881Speter segment->range_end, 2727251881Speter segment->path); 2728251881Speter} 2729251881Speter 2730362181Sdimstatic svn_error_t * 2731362181Sdimget_location_segments(svn_ra_svn_conn_t *conn, 2732362181Sdim apr_pool_t *pool, 2733362181Sdim svn_ra_svn__list_t *params, 2734362181Sdim void *baton) 2735251881Speter{ 2736251881Speter svn_error_t *err, *write_err; 2737251881Speter server_baton_t *b = baton; 2738251881Speter svn_revnum_t peg_revision, start_rev, end_rev; 2739362181Sdim const char *relative_path, *canonical_path; 2740251881Speter const char *abs_path; 2741251881Speter authz_baton_t ab; 2742251881Speter 2743251881Speter ab.server = b; 2744251881Speter ab.conn = conn; 2745251881Speter 2746251881Speter /* Parse the arguments. */ 2747362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)(?r)", 2748251881Speter &relative_path, &peg_revision, 2749251881Speter &start_rev, &end_rev)); 2750362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, relative_path, 2751362181Sdim pool, pool)); 2752362181Sdim relative_path = canonical_path; 2753251881Speter 2754289180Speter abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, 2755289180Speter pool); 2756251881Speter 2757286506Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2758286506Speter SVN_ERR(log_command(baton, conn, pool, "%s", 2759286506Speter svn_log__get_location_segments(abs_path, peg_revision, 2760286506Speter start_rev, end_rev, 2761286506Speter pool))); 2762286506Speter 2763286506Speter /* No START_REV or PEG_REVISION? We'll use HEAD. */ 2764286506Speter if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision)) 2765251881Speter { 2766286506Speter svn_revnum_t youngest; 2767286506Speter 2768289180Speter err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool); 2769286506Speter 2770289180Speter if (err) 2771289180Speter { 2772289180Speter err = svn_error_compose_create( 2773289180Speter svn_ra_svn__write_word(conn, pool, "done"), 2774289180Speter err); 2775289180Speter 2776289180Speter return log_fail_and_flush(err, b, conn, pool); 2777289180Speter } 2778289180Speter 2779286506Speter if (!SVN_IS_VALID_REVNUM(start_rev)) 2780286506Speter start_rev = youngest; 2781286506Speter if (!SVN_IS_VALID_REVNUM(peg_revision)) 2782286506Speter peg_revision = youngest; 2783286506Speter } 2784286506Speter 2785286506Speter /* No END_REV? We'll use 0. */ 2786286506Speter if (!SVN_IS_VALID_REVNUM(end_rev)) 2787286506Speter end_rev = 0; 2788286506Speter 2789286506Speter if (end_rev > start_rev) 2790286506Speter { 2791289180Speter err = svn_ra_svn__write_word(conn, pool, "done"); 2792362181Sdim err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err, 2793251881Speter "Get-location-segments end revision must not be " 2794251881Speter "younger than start revision"); 2795251881Speter return log_fail_and_flush(err, b, conn, pool); 2796251881Speter } 2797251881Speter 2798286506Speter if (start_rev > peg_revision) 2799251881Speter { 2800289180Speter err = svn_ra_svn__write_word(conn, pool, "done"); 2801362181Sdim err = svn_error_createf(SVN_ERR_FS_NO_SUCH_REVISION, err, 2802251881Speter "Get-location-segments start revision must not " 2803251881Speter "be younger than peg revision"); 2804251881Speter return log_fail_and_flush(err, b, conn, pool); 2805251881Speter } 2806251881Speter 2807251881Speter /* All the parameters are fine - let's perform the query against the 2808251881Speter * repository. */ 2809251881Speter 2810251881Speter /* We store both err and write_err here, so the client will get 2811251881Speter * the "done" even if there was an error in fetching the results. */ 2812251881Speter 2813289180Speter err = svn_repos_node_location_segments(b->repository->repos, abs_path, 2814251881Speter peg_revision, start_rev, end_rev, 2815251881Speter gls_receiver, (void *)conn, 2816251881Speter authz_check_access_cb_func(b), &ab, 2817251881Speter pool); 2818251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2819251881Speter if (write_err) 2820251881Speter { 2821289180Speter return svn_error_compose_create(write_err, err); 2822251881Speter } 2823251881Speter SVN_CMD_ERR(err); 2824251881Speter 2825251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2826251881Speter 2827251881Speter return SVN_NO_ERROR; 2828251881Speter} 2829251881Speter 2830251881Speter/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the 2831251881Speter client as a string. */ 2832251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data, 2833251881Speter apr_size_t *len) 2834251881Speter{ 2835251881Speter file_revs_baton_t *b = baton; 2836251881Speter svn_string_t str; 2837251881Speter 2838251881Speter str.data = data; 2839251881Speter str.len = *len; 2840251881Speter return svn_ra_svn__write_string(b->conn, b->pool, &str); 2841251881Speter} 2842251881Speter 2843251881Speter/* This implements svn_close_fn_t. Mark the end of the data by writing an 2844251881Speter empty string to the client. */ 2845251881Speterstatic svn_error_t *svndiff_close_handler(void *baton) 2846251881Speter{ 2847251881Speter file_revs_baton_t *b = baton; 2848251881Speter 2849251881Speter SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, "")); 2850251881Speter return SVN_NO_ERROR; 2851251881Speter} 2852251881Speter 2853251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */ 2854251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path, 2855251881Speter svn_revnum_t rev, apr_hash_t *rev_props, 2856251881Speter svn_boolean_t merged_revision, 2857251881Speter svn_txdelta_window_handler_t *d_handler, 2858251881Speter void **d_baton, 2859251881Speter apr_array_header_t *prop_diffs, 2860251881Speter apr_pool_t *pool) 2861251881Speter{ 2862251881Speter file_revs_baton_t *frb = baton; 2863251881Speter svn_stream_t *stream; 2864251881Speter 2865251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!", 2866251881Speter path, rev)); 2867251881Speter SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props)); 2868251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!")); 2869251881Speter SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs)); 2870251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision)); 2871251881Speter 2872251881Speter /* Store the pool for the delta stream. */ 2873251881Speter frb->pool = pool; 2874251881Speter 2875251881Speter /* Prepare for the delta or just write an empty string. */ 2876251881Speter if (d_handler) 2877251881Speter { 2878251881Speter stream = svn_stream_create(baton, pool); 2879251881Speter svn_stream_set_write(stream, svndiff_handler); 2880251881Speter svn_stream_set_close(stream, svndiff_close_handler); 2881251881Speter 2882362181Sdim svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 2883362181Sdim svn_ra_svn__svndiff_version(frb->conn), 2884362181Sdim svn_ra_svn_compression_level(frb->conn), pool); 2885251881Speter } 2886251881Speter else 2887251881Speter SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); 2888251881Speter 2889251881Speter return SVN_NO_ERROR; 2890251881Speter} 2891251881Speter 2892362181Sdimstatic svn_error_t * 2893362181Sdimget_file_revs(svn_ra_svn_conn_t *conn, 2894362181Sdim apr_pool_t *pool, 2895362181Sdim svn_ra_svn__list_t *params, 2896362181Sdim void *baton) 2897251881Speter{ 2898251881Speter server_baton_t *b = baton; 2899251881Speter svn_error_t *err, *write_err; 2900251881Speter file_revs_baton_t frb; 2901251881Speter svn_revnum_t start_rev, end_rev; 2902251881Speter const char *path; 2903251881Speter const char *full_path; 2904362181Sdim const char *canonical_path; 2905251881Speter apr_uint64_t include_merged_revs_param; 2906251881Speter svn_boolean_t include_merged_revisions; 2907251881Speter authz_baton_t ab; 2908251881Speter 2909251881Speter ab.server = b; 2910251881Speter ab.conn = conn; 2911251881Speter 2912251881Speter /* Parse arguments. */ 2913362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)(?r)?B", 2914251881Speter &path, &start_rev, &end_rev, 2915251881Speter &include_merged_revs_param)); 2916362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 2917362181Sdim pool, pool)); 2918362181Sdim path = canonical_path; 2919251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2920289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, path, pool); 2921251881Speter 2922251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2923251881Speter include_merged_revisions = FALSE; 2924251881Speter else 2925251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2926251881Speter 2927251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2928251881Speter svn_log__get_file_revs(full_path, start_rev, end_rev, 2929251881Speter include_merged_revisions, 2930251881Speter pool))); 2931251881Speter 2932251881Speter frb.conn = conn; 2933251881Speter frb.pool = NULL; 2934251881Speter 2935289180Speter err = svn_repos_get_file_revs2(b->repository->repos, full_path, start_rev, 2936289180Speter end_rev, include_merged_revisions, 2937251881Speter authz_check_access_cb_func(b), &ab, 2938251881Speter file_rev_handler, &frb, pool); 2939251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2940251881Speter if (write_err) 2941251881Speter { 2942251881Speter svn_error_clear(err); 2943251881Speter return write_err; 2944251881Speter } 2945251881Speter SVN_CMD_ERR(err); 2946251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2947251881Speter 2948251881Speter return SVN_NO_ERROR; 2949251881Speter} 2950251881Speter 2951362181Sdimstatic svn_error_t * 2952362181Sdimlock(svn_ra_svn_conn_t *conn, 2953362181Sdim apr_pool_t *pool, 2954362181Sdim svn_ra_svn__list_t *params, 2955362181Sdim void *baton) 2956251881Speter{ 2957251881Speter server_baton_t *b = baton; 2958251881Speter const char *path; 2959251881Speter const char *comment; 2960251881Speter const char *full_path; 2961362181Sdim const char *canonical_path; 2962251881Speter svn_boolean_t steal_lock; 2963251881Speter svn_revnum_t current_rev; 2964251881Speter svn_lock_t *l; 2965251881Speter 2966362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b(?r)", &path, &comment, 2967251881Speter &steal_lock, ¤t_rev)); 2968362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 2969362181Sdim pool, pool));; 2970289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 2971362181Sdim canonical_path, pool); 2972251881Speter 2973251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2974251881Speter full_path, TRUE)); 2975251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2976251881Speter svn_log__lock_one_path(full_path, steal_lock, pool))); 2977251881Speter 2978289180Speter SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->repos, full_path, NULL, 2979289180Speter comment, 0, 0, /* No expiration time. */ 2980251881Speter current_rev, steal_lock, pool)); 2981251881Speter 2982251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success")); 2983251881Speter SVN_ERR(write_lock(conn, pool, l)); 2984251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)")); 2985251881Speter 2986251881Speter return SVN_NO_ERROR; 2987251881Speter} 2988251881Speter 2989289180Speterstruct lock_result_t { 2990289180Speter const svn_lock_t *lock; 2991289180Speter svn_error_t *err; 2992289180Speter}; 2993289180Speter 2994289180Speterstruct lock_many_baton_t { 2995289180Speter apr_hash_t *results; 2996289180Speter apr_pool_t *pool; 2997289180Speter}; 2998289180Speter 2999289180Speter/* Implements svn_fs_lock_callback_t. */ 3000289180Speterstatic svn_error_t * 3001289180Speterlock_many_cb(void *baton, 3002289180Speter const char *path, 3003289180Speter const svn_lock_t *fs_lock, 3004289180Speter svn_error_t *fs_err, 3005289180Speter apr_pool_t *pool) 3006289180Speter{ 3007289180Speter struct lock_many_baton_t *b = baton; 3008289180Speter struct lock_result_t *result = apr_palloc(b->pool, 3009289180Speter sizeof(struct lock_result_t)); 3010289180Speter 3011289180Speter result->lock = fs_lock; 3012289180Speter result->err = svn_error_dup(fs_err); 3013289180Speter svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result); 3014289180Speter 3015289180Speter return SVN_NO_ERROR; 3016289180Speter} 3017289180Speter 3018289180Speterstatic void 3019289180Speterclear_lock_result_hash(apr_hash_t *results, 3020289180Speter apr_pool_t *scratch_pool) 3021289180Speter{ 3022289180Speter apr_hash_index_t *hi; 3023289180Speter 3024289180Speter for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi)) 3025289180Speter { 3026289180Speter struct lock_result_t *result = apr_hash_this_val(hi); 3027289180Speter svn_error_clear(result->err); 3028289180Speter } 3029289180Speter} 3030289180Speter 3031362181Sdimstatic svn_error_t * 3032362181Sdimlock_many(svn_ra_svn_conn_t *conn, 3033362181Sdim apr_pool_t *pool, 3034362181Sdim svn_ra_svn__list_t *params, 3035362181Sdim void *baton) 3036251881Speter{ 3037251881Speter server_baton_t *b = baton; 3038362181Sdim svn_ra_svn__list_t *path_revs; 3039251881Speter const char *comment; 3040251881Speter svn_boolean_t steal_lock; 3041251881Speter int i; 3042251881Speter apr_pool_t *subpool; 3043289180Speter svn_error_t *err, *write_err = SVN_NO_ERROR; 3044289180Speter apr_hash_t *targets = apr_hash_make(pool); 3045289180Speter apr_hash_t *authz_results = apr_hash_make(pool); 3046289180Speter apr_hash_index_t *hi; 3047289180Speter struct lock_many_baton_t lmb; 3048251881Speter 3049362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "(?c)bl", &comment, &steal_lock, 3050251881Speter &path_revs)); 3051251881Speter 3052251881Speter subpool = svn_pool_create(pool); 3053251881Speter 3054251881Speter /* Because we can only send a single auth reply per request, we send 3055251881Speter a reply before parsing the lock commands. This means an authz 3056251881Speter access denial will abort the processing of the locks and return 3057251881Speter an error. */ 3058251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE)); 3059251881Speter 3060289180Speter /* Parse the lock requests from PATH_REVS into TARGETS. */ 3061251881Speter for (i = 0; i < path_revs->nelts; ++i) 3062251881Speter { 3063362181Sdim const char *path, *full_path, *canonical_path; 3064289180Speter svn_revnum_t current_rev; 3065362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i); 3066289180Speter svn_fs_lock_target_t *target; 3067251881Speter 3068251881Speter svn_pool_clear(subpool); 3069251881Speter 3070251881Speter if (item->kind != SVN_RA_SVN_LIST) 3071251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 3072251881Speter "Lock requests should be list of lists"); 3073251881Speter 3074362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", &path, 3075251881Speter ¤t_rev)); 3076251881Speter 3077362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3078362181Sdim subpool, subpool)); 3079289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3080362181Sdim canonical_path, pool); 3081289180Speter target = svn_fs_lock_target_create(NULL, current_rev, pool); 3082251881Speter 3083289180Speter /* Any duplicate paths, once canonicalized, get collapsed into a 3084289180Speter single path that is processed once. The result is then 3085289180Speter returned multiple times. */ 3086289180Speter svn_hash_sets(targets, full_path, target); 3087289180Speter } 3088289180Speter 3089289180Speter SVN_ERR(log_command(b, conn, subpool, "%s", 3090289180Speter svn_log__lock(targets, steal_lock, subpool))); 3091289180Speter 3092289180Speter /* Check authz. 3093289180Speter 3094289180Speter Note: From here on we need to make sure any errors in authz_results, or 3095289180Speter results, are cleared before returning from this function. */ 3096289180Speter for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) 3097289180Speter { 3098289180Speter const char *full_path = apr_hash_this_key(hi); 3099289180Speter 3100289180Speter svn_pool_clear(subpool); 3101289180Speter 3102289180Speter if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE)) 3103251881Speter { 3104289180Speter struct lock_result_t *result 3105289180Speter = apr_palloc(pool, sizeof(struct lock_result_t)); 3106289180Speter 3107289180Speter result->lock = NULL; 3108289180Speter result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 3109289180Speter NULL, NULL, b); 3110289180Speter svn_hash_sets(authz_results, full_path, result); 3111289180Speter svn_hash_sets(targets, full_path, NULL); 3112251881Speter } 3113289180Speter } 3114251881Speter 3115289180Speter lmb.results = apr_hash_make(pool); 3116289180Speter lmb.pool = pool; 3117251881Speter 3118289180Speter err = svn_repos_fs_lock_many(b->repository->repos, targets, 3119289180Speter comment, FALSE, 3120289180Speter 0, /* No expiration time. */ 3121289180Speter steal_lock, lock_many_cb, &lmb, 3122289180Speter pool, subpool); 3123289180Speter 3124289180Speter /* Return results in the same order as the paths were supplied. */ 3125289180Speter for (i = 0; i < path_revs->nelts; ++i) 3126289180Speter { 3127362181Sdim const char *path, *full_path, *canonical_path; 3128289180Speter svn_revnum_t current_rev; 3129362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(path_revs, i); 3130289180Speter struct lock_result_t *result; 3131289180Speter 3132289180Speter svn_pool_clear(subpool); 3133289180Speter 3134362181Sdim write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?r)", 3135362181Sdim &path, ¤t_rev); 3136289180Speter if (write_err) 3137289180Speter break; 3138289180Speter 3139362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3140362181Sdim subpool, subpool)); 3141289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3142362181Sdim canonical_path, subpool); 3143289180Speter 3144289180Speter result = svn_hash_gets(lmb.results, full_path); 3145289180Speter if (!result) 3146289180Speter result = svn_hash_gets(authz_results, full_path); 3147289180Speter if (!result) 3148251881Speter { 3149289180Speter /* No result? Something really odd happened, create a 3150289180Speter placeholder error so that any other results can be 3151289180Speter reported in the correct order. */ 3152289180Speter result = apr_palloc(pool, sizeof(struct lock_result_t)); 3153289180Speter result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, 3154289180Speter _("No result for '%s'."), path); 3155289180Speter svn_hash_sets(lmb.results, full_path, result); 3156251881Speter } 3157289180Speter 3158289180Speter if (result->err) 3159289180Speter write_err = svn_ra_svn__write_cmd_failure(conn, subpool, 3160289180Speter result->err); 3161251881Speter else 3162251881Speter { 3163289180Speter write_err = svn_ra_svn__write_tuple(conn, subpool, 3164289180Speter "w!", "success"); 3165289180Speter if (!write_err) 3166289180Speter write_err = write_lock(conn, subpool, result->lock); 3167289180Speter if (!write_err) 3168289180Speter write_err = svn_ra_svn__write_tuple(conn, subpool, "!"); 3169251881Speter } 3170289180Speter if (write_err) 3171289180Speter break; 3172251881Speter } 3173251881Speter 3174289180Speter clear_lock_result_hash(authz_results, subpool); 3175289180Speter clear_lock_result_hash(lmb.results, subpool); 3176289180Speter 3177251881Speter svn_pool_destroy(subpool); 3178251881Speter 3179251881Speter if (!write_err) 3180289180Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 3181289180Speter if (!write_err) 3182251881Speter SVN_CMD_ERR(err); 3183251881Speter svn_error_clear(err); 3184251881Speter SVN_ERR(write_err); 3185251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3186251881Speter 3187251881Speter return SVN_NO_ERROR; 3188251881Speter} 3189251881Speter 3190362181Sdimstatic svn_error_t * 3191362181Sdimunlock(svn_ra_svn_conn_t *conn, 3192362181Sdim apr_pool_t *pool, 3193362181Sdim svn_ra_svn__list_t *params, 3194362181Sdim void *baton) 3195251881Speter{ 3196251881Speter server_baton_t *b = baton; 3197362181Sdim const char *path, *token, *full_path, *canonical_path; 3198251881Speter svn_boolean_t break_lock; 3199251881Speter 3200362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?c)b", &path, &token, 3201251881Speter &break_lock)); 3202251881Speter 3203362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3204362181Sdim pool, pool)); 3205289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3206362181Sdim canonical_path, pool); 3207251881Speter 3208251881Speter /* Username required unless break_lock was specified. */ 3209251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 3210251881Speter full_path, ! break_lock)); 3211251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 3212251881Speter svn_log__unlock_one_path(full_path, break_lock, pool))); 3213251881Speter 3214289180Speter SVN_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token, 3215289180Speter break_lock, pool)); 3216251881Speter 3217251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3218251881Speter 3219251881Speter return SVN_NO_ERROR; 3220251881Speter} 3221251881Speter 3222362181Sdimstatic svn_error_t * 3223362181Sdimunlock_many(svn_ra_svn_conn_t *conn, 3224362181Sdim apr_pool_t *pool, 3225362181Sdim svn_ra_svn__list_t *params, 3226362181Sdim void *baton) 3227251881Speter{ 3228251881Speter server_baton_t *b = baton; 3229251881Speter svn_boolean_t break_lock; 3230362181Sdim svn_ra_svn__list_t *unlock_tokens; 3231251881Speter int i; 3232251881Speter apr_pool_t *subpool; 3233289180Speter svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR; 3234289180Speter apr_hash_t *targets = apr_hash_make(pool); 3235289180Speter apr_hash_t *authz_results = apr_hash_make(pool); 3236289180Speter apr_hash_index_t *hi; 3237289180Speter struct lock_many_baton_t lmb; 3238251881Speter 3239362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "bl", &break_lock, &unlock_tokens)); 3240251881Speter 3241251881Speter /* Username required unless break_lock was specified. */ 3242251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); 3243251881Speter 3244251881Speter subpool = svn_pool_create(pool); 3245251881Speter 3246289180Speter /* Parse the unlock requests from PATH_REVS into TARGETS. */ 3247251881Speter for (i = 0; i < unlock_tokens->nelts; i++) 3248251881Speter { 3249362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i); 3250362181Sdim const char *path, *full_path, *token, *canonical_path; 3251251881Speter 3252251881Speter svn_pool_clear(subpool); 3253251881Speter 3254251881Speter if (item->kind != SVN_RA_SVN_LIST) 3255251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 3256251881Speter "Unlock request should be a list of lists"); 3257251881Speter 3258362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(&item->u.list, "c(?c)", &path, 3259251881Speter &token)); 3260289180Speter if (!token) 3261289180Speter token = ""; 3262251881Speter 3263362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3264362181Sdim subpool, subpool)); 3265289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3266362181Sdim canonical_path, pool); 3267251881Speter 3268289180Speter /* Any duplicate paths, once canonicalized, get collapsed into a 3269289180Speter single path that is processed once. The result is then 3270289180Speter returned multiple times. */ 3271289180Speter svn_hash_sets(targets, full_path, token); 3272289180Speter } 3273289180Speter 3274289180Speter SVN_ERR(log_command(b, conn, subpool, "%s", 3275289180Speter svn_log__unlock(targets, break_lock, subpool))); 3276289180Speter 3277289180Speter /* Check authz. 3278289180Speter 3279289180Speter Note: From here on we need to make sure any errors in authz_results, or 3280289180Speter results, are cleared before returning from this function. */ 3281289180Speter for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) 3282289180Speter { 3283289180Speter const char *full_path = apr_hash_this_key(hi); 3284289180Speter 3285289180Speter svn_pool_clear(subpool); 3286289180Speter 3287289180Speter if (! lookup_access(subpool, b, svn_authz_write, full_path, 3288251881Speter ! break_lock)) 3289289180Speter { 3290289180Speter struct lock_result_t *result 3291289180Speter = apr_palloc(pool, sizeof(struct lock_result_t)); 3292251881Speter 3293289180Speter result->lock = NULL; 3294289180Speter result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 3295289180Speter NULL, NULL, b); 3296289180Speter svn_hash_sets(authz_results, full_path, result); 3297289180Speter svn_hash_sets(targets, full_path, NULL); 3298289180Speter } 3299289180Speter } 3300289180Speter 3301289180Speter lmb.results = apr_hash_make(pool); 3302289180Speter lmb.pool = pool; 3303289180Speter 3304289180Speter err = svn_repos_fs_unlock_many(b->repository->repos, targets, 3305289180Speter break_lock, lock_many_cb, &lmb, 3306289180Speter pool, subpool); 3307289180Speter 3308289180Speter /* Return results in the same order as the paths were supplied. */ 3309289180Speter for (i = 0; i < unlock_tokens->nelts; ++i) 3310289180Speter { 3311362181Sdim const char *path, *token, *full_path, *canonical_path; 3312362181Sdim svn_ra_svn__item_t *item = &SVN_RA_SVN__LIST_ITEM(unlock_tokens, i); 3313289180Speter struct lock_result_t *result; 3314289180Speter 3315289180Speter svn_pool_clear(subpool); 3316289180Speter 3317362181Sdim write_err = svn_ra_svn__parse_tuple(&item->u.list, "c(?c)", 3318362181Sdim &path, &token); 3319289180Speter if (write_err) 3320289180Speter break; 3321289180Speter 3322362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3323362181Sdim subpool, subpool)); 3324289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3325362181Sdim canonical_path, pool); 3326289180Speter 3327289180Speter result = svn_hash_gets(lmb.results, full_path); 3328289180Speter if (!result) 3329289180Speter result = svn_hash_gets(authz_results, full_path); 3330289180Speter if (!result) 3331251881Speter { 3332289180Speter /* No result? Something really odd happened, create a 3333289180Speter placeholder error so that any other results can be 3334289180Speter reported in the correct order. */ 3335289180Speter result = apr_palloc(pool, sizeof(struct lock_result_t)); 3336289180Speter result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, 3337289180Speter _("No result for '%s'."), path); 3338289180Speter svn_hash_sets(lmb.results, full_path, result); 3339251881Speter } 3340289180Speter 3341289180Speter if (result->err) 3342289180Speter write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err); 3343251881Speter else 3344289180Speter write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", 3345289180Speter path); 3346289180Speter if (write_err) 3347289180Speter break; 3348251881Speter } 3349251881Speter 3350289180Speter clear_lock_result_hash(authz_results, subpool); 3351289180Speter clear_lock_result_hash(lmb.results, subpool); 3352289180Speter 3353251881Speter svn_pool_destroy(subpool); 3354251881Speter 3355289180Speter if (!write_err) 3356289180Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 3357251881Speter if (! write_err) 3358251881Speter SVN_CMD_ERR(err); 3359251881Speter svn_error_clear(err); 3360289180Speter SVN_ERR(write_err); 3361251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3362251881Speter 3363251881Speter return SVN_NO_ERROR; 3364251881Speter} 3365251881Speter 3366362181Sdimstatic svn_error_t * 3367362181Sdimget_lock(svn_ra_svn_conn_t *conn, 3368362181Sdim apr_pool_t *pool, 3369362181Sdim svn_ra_svn__list_t *params, 3370362181Sdim void *baton) 3371251881Speter{ 3372251881Speter server_baton_t *b = baton; 3373251881Speter const char *path; 3374251881Speter const char *full_path; 3375362181Sdim const char *canonical_path; 3376251881Speter svn_lock_t *l; 3377251881Speter 3378362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c", &path)); 3379251881Speter 3380362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3381362181Sdim pool, pool)); 3382289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3383362181Sdim canonical_path, pool); 3384251881Speter 3385251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 3386251881Speter full_path, FALSE)); 3387251881Speter SVN_ERR(log_command(b, conn, pool, "get-lock %s", 3388251881Speter svn_path_uri_encode(full_path, pool))); 3389251881Speter 3390289180Speter SVN_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool)); 3391251881Speter 3392251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 3393251881Speter if (l) 3394251881Speter SVN_ERR(write_lock(conn, pool, l)); 3395251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3396251881Speter 3397251881Speter return SVN_NO_ERROR; 3398251881Speter} 3399251881Speter 3400362181Sdimstatic svn_error_t * 3401362181Sdimget_locks(svn_ra_svn_conn_t *conn, 3402362181Sdim apr_pool_t *pool, 3403362181Sdim svn_ra_svn__list_t *params, 3404362181Sdim void *baton) 3405251881Speter{ 3406251881Speter server_baton_t *b = baton; 3407251881Speter const char *path; 3408251881Speter const char *full_path; 3409362181Sdim const char *canonical_path; 3410251881Speter const char *depth_word; 3411251881Speter svn_depth_t depth; 3412251881Speter apr_hash_t *locks; 3413251881Speter apr_hash_index_t *hi; 3414251881Speter svn_error_t *err; 3415251881Speter authz_baton_t ab; 3416251881Speter 3417251881Speter ab.server = b; 3418251881Speter ab.conn = conn; 3419251881Speter 3420362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c?(?w)", &path, &depth_word)); 3421251881Speter 3422251881Speter depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; 3423251881Speter if ((depth != svn_depth_empty) && 3424251881Speter (depth != svn_depth_files) && 3425251881Speter (depth != svn_depth_immediates) && 3426251881Speter (depth != svn_depth_infinity)) 3427251881Speter { 3428251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 3429251881Speter "Invalid 'depth' specified in get-locks request"); 3430251881Speter return log_fail_and_flush(err, b, conn, pool); 3431251881Speter } 3432251881Speter 3433362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3434362181Sdim pool, pool)); 3435289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3436362181Sdim canonical_path, pool); 3437251881Speter 3438251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3439251881Speter 3440251881Speter SVN_ERR(log_command(b, conn, pool, "get-locks %s", 3441251881Speter svn_path_uri_encode(full_path, pool))); 3442289180Speter SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->repos, 3443289180Speter full_path, depth, 3444251881Speter authz_check_access_cb_func(b), &ab, 3445251881Speter pool)); 3446251881Speter 3447251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 3448251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 3449251881Speter { 3450289180Speter svn_lock_t *l = apr_hash_this_val(hi); 3451251881Speter 3452251881Speter SVN_ERR(write_lock(conn, pool, l)); 3453251881Speter } 3454251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3455251881Speter 3456251881Speter return SVN_NO_ERROR; 3457251881Speter} 3458251881Speter 3459251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, 3460251881Speter server_baton_t *b, 3461251881Speter svn_revnum_t rev, 3462251881Speter svn_revnum_t low_water_mark, 3463251881Speter svn_boolean_t send_deltas, 3464251881Speter apr_pool_t *pool) 3465251881Speter{ 3466251881Speter const svn_delta_editor_t *editor; 3467251881Speter void *edit_baton; 3468251881Speter svn_fs_root_t *root; 3469251881Speter svn_error_t *err; 3470251881Speter authz_baton_t ab; 3471251881Speter 3472251881Speter ab.server = b; 3473251881Speter ab.conn = conn; 3474251881Speter 3475251881Speter SVN_ERR(log_command(b, conn, pool, 3476289180Speter svn_log__replay(b->repository->fs_path->data, rev, 3477289180Speter pool))); 3478251881Speter 3479251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 3480251881Speter 3481289180Speter err = svn_fs_revision_root(&root, b->repository->fs, rev, pool); 3482251881Speter 3483251881Speter if (! err) 3484289180Speter err = svn_repos_replay2(root, b->repository->fs_path->data, 3485289180Speter low_water_mark, send_deltas, editor, edit_baton, 3486251881Speter authz_check_access_cb_func(b), &ab, pool); 3487251881Speter 3488251881Speter if (err) 3489251881Speter svn_error_clear(editor->abort_edit(edit_baton, pool)); 3490251881Speter SVN_CMD_ERR(err); 3491251881Speter 3492251881Speter return svn_ra_svn__write_cmd_finish_replay(conn, pool); 3493251881Speter} 3494251881Speter 3495362181Sdimstatic svn_error_t * 3496362181Sdimreplay(svn_ra_svn_conn_t *conn, 3497362181Sdim apr_pool_t *pool, 3498362181Sdim svn_ra_svn__list_t *params, 3499362181Sdim void *baton) 3500251881Speter{ 3501251881Speter svn_revnum_t rev, low_water_mark; 3502251881Speter svn_boolean_t send_deltas; 3503251881Speter server_baton_t *b = baton; 3504251881Speter 3505362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "rrb", &rev, &low_water_mark, 3506251881Speter &send_deltas)); 3507251881Speter 3508251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3509251881Speter 3510251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3511251881Speter send_deltas, pool)); 3512251881Speter 3513251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3514251881Speter 3515251881Speter return SVN_NO_ERROR; 3516251881Speter} 3517251881Speter 3518362181Sdimstatic svn_error_t * 3519362181Sdimreplay_range(svn_ra_svn_conn_t *conn, 3520362181Sdim apr_pool_t *pool, 3521362181Sdim svn_ra_svn__list_t *params, 3522362181Sdim void *baton) 3523251881Speter{ 3524251881Speter svn_revnum_t start_rev, end_rev, rev, low_water_mark; 3525251881Speter svn_boolean_t send_deltas; 3526251881Speter server_baton_t *b = baton; 3527251881Speter apr_pool_t *iterpool; 3528251881Speter authz_baton_t ab; 3529251881Speter 3530251881Speter ab.server = b; 3531251881Speter ab.conn = conn; 3532251881Speter 3533362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "rrrb", &start_rev, 3534251881Speter &end_rev, &low_water_mark, 3535251881Speter &send_deltas)); 3536251881Speter 3537251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3538251881Speter 3539251881Speter iterpool = svn_pool_create(pool); 3540251881Speter for (rev = start_rev; rev <= end_rev; rev++) 3541251881Speter { 3542251881Speter apr_hash_t *props; 3543251881Speter 3544251881Speter svn_pool_clear(iterpool); 3545251881Speter 3546289180Speter SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, 3547289180Speter b->repository->repos, rev, 3548251881Speter authz_check_access_cb_func(b), 3549251881Speter &ab, 3550251881Speter iterpool)); 3551251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops")); 3552251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props)); 3553251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)")); 3554251881Speter 3555251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3556251881Speter send_deltas, iterpool)); 3557251881Speter 3558251881Speter } 3559251881Speter svn_pool_destroy(iterpool); 3560251881Speter 3561251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3562251881Speter 3563251881Speter return SVN_NO_ERROR; 3564251881Speter} 3565251881Speter 3566251881Speterstatic svn_error_t * 3567251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn, 3568251881Speter apr_pool_t *pool, 3569362181Sdim svn_ra_svn__list_t *params, 3570251881Speter void *baton) 3571251881Speter{ 3572251881Speter server_baton_t *b = baton; 3573362181Sdim const char *path, *full_path, *canonical_path; 3574251881Speter svn_revnum_t peg_revision; 3575251881Speter svn_revnum_t end_revision; 3576251881Speter svn_revnum_t revision_deleted; 3577251881Speter 3578362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "crr", 3579251881Speter &path, &peg_revision, &end_revision)); 3580362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3581362181Sdim pool, pool)); 3582289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3583362181Sdim canonical_path, pool); 3584251881Speter SVN_ERR(log_command(b, conn, pool, "get-deleted-rev")); 3585251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3586362181Sdim SVN_CMD_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision, 3587362181Sdim end_revision, &revision_deleted, pool)); 3588362181Sdim 3589362181Sdim /* The protocol does not allow for a reply of SVN_INVALID_REVNUM directly. 3590362181Sdim Instead, return SVN_ERR_ENTRY_MISSING_REVISION. A new enough client 3591362181Sdim knows that this means the answer to the query is SVN_INVALID_REVNUM. 3592362181Sdim (An older client reports this as an error.) */ 3593362181Sdim if (revision_deleted == SVN_INVALID_REVNUM) 3594362181Sdim SVN_CMD_ERR(svn_error_createf(SVN_ERR_ENTRY_MISSING_REVISION, NULL, 3595362181Sdim "svn protocol command 'get-deleted-rev': " 3596362181Sdim "path '%s' was not deleted in r%ld-%ld; " 3597362181Sdim "NOTE: newer clients handle this case " 3598362181Sdim "and do not report it as an error", 3599362181Sdim full_path, peg_revision, end_revision)); 3600362181Sdim 3601251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted)); 3602251881Speter return SVN_NO_ERROR; 3603251881Speter} 3604251881Speter 3605251881Speterstatic svn_error_t * 3606251881Speterget_inherited_props(svn_ra_svn_conn_t *conn, 3607251881Speter apr_pool_t *pool, 3608362181Sdim svn_ra_svn__list_t *params, 3609251881Speter void *baton) 3610251881Speter{ 3611251881Speter server_baton_t *b = baton; 3612362181Sdim const char *path, *full_path, *canonical_path; 3613251881Speter svn_revnum_t rev; 3614251881Speter svn_fs_root_t *root; 3615251881Speter apr_array_header_t *inherited_props; 3616251881Speter int i; 3617251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 3618251881Speter authz_baton_t ab; 3619362181Sdim svn_node_kind_t node_kind; 3620251881Speter 3621251881Speter ab.server = b; 3622251881Speter ab.conn = conn; 3623251881Speter 3624251881Speter /* Parse arguments. */ 3625362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)", &path, &rev)); 3626251881Speter 3627362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3628362181Sdim iterpool, iterpool)); 3629289180Speter full_path = svn_fspath__join(b->repository->fs_path->data, 3630362181Sdim canonical_path, pool); 3631251881Speter 3632251881Speter /* Check authorizations */ 3633251881Speter SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, 3634251881Speter full_path, FALSE)); 3635251881Speter 3636251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 3637251881Speter svn_log__get_inherited_props(full_path, rev, 3638251881Speter iterpool))); 3639251881Speter 3640251881Speter /* Fetch the properties and a stream for the contents. */ 3641289180Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool)); 3642362181Sdim SVN_CMD_ERR(svn_fs_check_path(&node_kind, root, full_path, pool)); 3643362181Sdim if (node_kind == svn_node_none) 3644362181Sdim { 3645362181Sdim SVN_CMD_ERR(svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 3646362181Sdim _("'%s' path not found"), full_path)); 3647362181Sdim } 3648251881Speter SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); 3649251881Speter 3650251881Speter /* Send successful command response with revision and props. */ 3651251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success")); 3652251881Speter 3653251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!")); 3654251881Speter 3655251881Speter for (i = 0; i < inherited_props->nelts; i++) 3656251881Speter { 3657251881Speter svn_prop_inherited_item_t *iprop = 3658251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 3659251881Speter 3660251881Speter svn_pool_clear(iterpool); 3661251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 3662251881Speter iprop->path_or_url)); 3663251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 3664251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 3665251881Speter iprop->path_or_url)); 3666251881Speter } 3667251881Speter 3668251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))")); 3669251881Speter svn_pool_destroy(iterpool); 3670251881Speter return SVN_NO_ERROR; 3671251881Speter} 3672251881Speter 3673362181Sdim/* Baton type to be used with list_receiver. */ 3674362181Sdimtypedef struct list_receiver_baton_t 3675362181Sdim{ 3676362181Sdim /* Send the data through this connection. */ 3677362181Sdim svn_ra_svn_conn_t *conn; 3678362181Sdim 3679362181Sdim /* Send the field selected by these flags. */ 3680362181Sdim apr_uint32_t dirent_fields; 3681362181Sdim} list_receiver_baton_t; 3682362181Sdim 3683362181Sdim/* Implements svn_repos_dirent_receiver_t, sending DIRENT and PATH to the 3684362181Sdim * client. BATON must be a list_receiver_baton_t. */ 3685362181Sdimstatic svn_error_t * 3686362181Sdimlist_receiver(const char *path, 3687362181Sdim svn_dirent_t *dirent, 3688362181Sdim void *baton, 3689362181Sdim apr_pool_t *pool) 3690362181Sdim{ 3691362181Sdim list_receiver_baton_t *b = baton; 3692362181Sdim return svn_error_trace(svn_ra_svn__write_dirent(b->conn, pool, path, dirent, 3693362181Sdim b->dirent_fields)); 3694362181Sdim} 3695362181Sdim 3696362181Sdimstatic svn_error_t * 3697362181Sdimlist(svn_ra_svn_conn_t *conn, 3698362181Sdim apr_pool_t *pool, 3699362181Sdim svn_ra_svn__list_t *params, 3700362181Sdim void *baton) 3701362181Sdim{ 3702362181Sdim server_baton_t *b = baton; 3703362181Sdim const char *path, *full_path, *canonical_path; 3704362181Sdim svn_revnum_t rev; 3705362181Sdim svn_depth_t depth; 3706362181Sdim apr_array_header_t *patterns = NULL; 3707362181Sdim svn_fs_root_t *root; 3708362181Sdim const char *depth_word; 3709362181Sdim svn_boolean_t path_info_only; 3710362181Sdim svn_ra_svn__list_t *dirent_fields_list = NULL; 3711362181Sdim svn_ra_svn__list_t *patterns_list = NULL; 3712362181Sdim int i; 3713362181Sdim list_receiver_baton_t rb; 3714362181Sdim svn_error_t *err, *write_err; 3715362181Sdim 3716362181Sdim authz_baton_t ab; 3717362181Sdim ab.server = b; 3718362181Sdim ab.conn = conn; 3719362181Sdim 3720362181Sdim /* Read the command parameters. */ 3721362181Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, "c(?r)w?l?l", &path, &rev, 3722362181Sdim &depth_word, &dirent_fields_list, 3723362181Sdim &patterns_list)); 3724362181Sdim 3725362181Sdim rb.conn = conn; 3726362181Sdim SVN_ERR(parse_dirent_fields(&rb.dirent_fields, dirent_fields_list)); 3727362181Sdim 3728362181Sdim depth = svn_depth_from_word(depth_word); 3729362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3730362181Sdim pool, pool)); 3731362181Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3732362181Sdim canonical_path, pool); 3733362181Sdim 3734362181Sdim /* Read the patterns list. */ 3735362181Sdim if (patterns_list) 3736362181Sdim { 3737362181Sdim patterns = apr_array_make(pool, 0, sizeof(const char *)); 3738362181Sdim for (i = 0; i < patterns_list->nelts; ++i) 3739362181Sdim { 3740362181Sdim svn_ra_svn__item_t *elt = &SVN_RA_SVN__LIST_ITEM(patterns_list, i); 3741362181Sdim 3742362181Sdim if (elt->kind != SVN_RA_SVN_STRING) 3743362181Sdim return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 3744362181Sdim "Pattern field not a string"); 3745362181Sdim 3746362181Sdim APR_ARRAY_PUSH(patterns, const char *) = elt->u.string.data; 3747362181Sdim } 3748362181Sdim } 3749362181Sdim 3750362181Sdim /* Check authorizations */ 3751362181Sdim SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 3752362181Sdim full_path, FALSE)); 3753362181Sdim 3754362181Sdim if (!SVN_IS_VALID_REVNUM(rev)) 3755362181Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 3756362181Sdim 3757362181Sdim SVN_ERR(log_command(b, conn, pool, "%s", 3758362181Sdim svn_log__list(full_path, rev, patterns, depth, 3759362181Sdim rb.dirent_fields, pool))); 3760362181Sdim 3761362181Sdim /* Fetch the root of the appropriate revision. */ 3762362181Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 3763362181Sdim 3764362181Sdim /* Fetch the directory entries if requested and send them immediately. */ 3765362181Sdim path_info_only = (rb.dirent_fields & ~SVN_DIRENT_KIND) == 0; 3766362181Sdim err = svn_repos_list(root, full_path, patterns, depth, path_info_only, 3767362181Sdim authz_check_access_cb_func(b), &ab, list_receiver, 3768362181Sdim &rb, NULL, NULL, pool); 3769362181Sdim 3770362181Sdim 3771362181Sdim /* Finish response. */ 3772362181Sdim write_err = svn_ra_svn__write_word(conn, pool, "done"); 3773362181Sdim if (write_err) 3774362181Sdim { 3775362181Sdim svn_error_clear(err); 3776362181Sdim return write_err; 3777362181Sdim } 3778362181Sdim SVN_CMD_ERR(err); 3779362181Sdim 3780362181Sdim return svn_error_trace(svn_ra_svn__write_cmd_response(conn, pool, "")); 3781362181Sdim} 3782362181Sdim 3783362181Sdimstatic const svn_ra_svn__cmd_entry_t main_commands[] = { 3784251881Speter { "reparent", reparent }, 3785251881Speter { "get-latest-rev", get_latest_rev }, 3786251881Speter { "get-dated-rev", get_dated_rev }, 3787251881Speter { "change-rev-prop", change_rev_prop }, 3788251881Speter { "change-rev-prop2",change_rev_prop2 }, 3789251881Speter { "rev-proplist", rev_proplist }, 3790251881Speter { "rev-prop", rev_prop }, 3791251881Speter { "commit", commit }, 3792251881Speter { "get-file", get_file }, 3793251881Speter { "get-dir", get_dir }, 3794251881Speter { "update", update }, 3795251881Speter { "switch", switch_cmd }, 3796251881Speter { "status", status }, 3797251881Speter { "diff", diff }, 3798251881Speter { "get-mergeinfo", get_mergeinfo }, 3799251881Speter { "log", log_cmd }, 3800251881Speter { "check-path", check_path }, 3801251881Speter { "stat", stat_cmd }, 3802251881Speter { "get-locations", get_locations }, 3803251881Speter { "get-location-segments", get_location_segments }, 3804251881Speter { "get-file-revs", get_file_revs }, 3805251881Speter { "lock", lock }, 3806251881Speter { "lock-many", lock_many }, 3807251881Speter { "unlock", unlock }, 3808251881Speter { "unlock-many", unlock_many }, 3809251881Speter { "get-lock", get_lock }, 3810251881Speter { "get-locks", get_locks }, 3811251881Speter { "replay", replay }, 3812251881Speter { "replay-range", replay_range }, 3813251881Speter { "get-deleted-rev", get_deleted_rev }, 3814251881Speter { "get-iprops", get_inherited_props }, 3815362181Sdim { "list", list }, 3816251881Speter { NULL } 3817251881Speter}; 3818251881Speter 3819251881Speter/* Skip past the scheme part of a URL, including the tunnel specification 3820251881Speter * if present. Return NULL if the scheme part is invalid for ra_svn. */ 3821251881Speterstatic const char *skip_scheme_part(const char *url) 3822251881Speter{ 3823251881Speter if (strncmp(url, "svn", 3) != 0) 3824251881Speter return NULL; 3825251881Speter url += 3; 3826251881Speter if (*url == '+') 3827251881Speter url += strcspn(url, ":"); 3828251881Speter if (strncmp(url, "://", 3) != 0) 3829251881Speter return NULL; 3830251881Speter return url + 3; 3831251881Speter} 3832251881Speter 3833251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any 3834251881Speter '..' path segments. 3835251881Speter NOTE: This is similar to svn_path_is_backpath_present, but that function 3836251881Speter assumes the path separator is '/'. This function also checks for 3837251881Speter segments delimited by the local path separator. */ 3838251881Speterstatic svn_boolean_t 3839251881Speterrepos_path_valid(const char *path) 3840251881Speter{ 3841251881Speter const char *s = path; 3842251881Speter 3843251881Speter while (*s) 3844251881Speter { 3845251881Speter /* Scan for the end of the segment. */ 3846251881Speter while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR) 3847251881Speter ++path; 3848251881Speter 3849251881Speter /* Check for '..'. */ 3850251881Speter#ifdef WIN32 3851251881Speter /* On Windows, don't allow sequences of more than one character 3852251881Speter consisting of just dots and spaces. Win32 functions treat 3853251881Speter paths such as ".. " and "......." inconsistently. Make sure 3854251881Speter no one can escape out of the root. */ 3855251881Speter if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s)) 3856251881Speter return FALSE; 3857251881Speter#else /* ! WIN32 */ 3858251881Speter if (path - s == 2 && s[0] == '.' && s[1] == '.') 3859251881Speter return FALSE; 3860251881Speter#endif 3861251881Speter 3862251881Speter /* Skip all separators. */ 3863251881Speter while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR)) 3864251881Speter ++path; 3865251881Speter s = path; 3866251881Speter } 3867251881Speter 3868251881Speter return TRUE; 3869251881Speter} 3870251881Speter 3871251881Speter/* Look for the repository given by URL, using ROOT as the virtual 3872289180Speter * repository root. If we find one, fill in the repos, fs, repos_url, 3873289180Speter * and fs_path fields of REPOSITORY. VHOST and READ_ONLY flags are the 3874289180Speter * same as in the server baton. 3875289180Speter * 3876362181Sdim * CONFIG_POOL shall be used to load config objects. 3877289180Speter * 3878289180Speter * Use SCRATCH_POOL for temporary allocations. 3879289180Speter * 3880251881Speter */ 3881289180Speterstatic svn_error_t * 3882289180Speterfind_repos(const char *url, 3883289180Speter const char *root, 3884289180Speter svn_boolean_t vhost, 3885289180Speter svn_boolean_t read_only, 3886289180Speter svn_config_t *cfg, 3887289180Speter repository_t *repository, 3888289180Speter svn_repos__config_pool_t *config_pool, 3889289180Speter apr_hash_t *fs_config, 3890362181Sdim svn_repos_authz_warning_func_t authz_warning_func, 3891362181Sdim void *authz_warning_baton, 3892289180Speter apr_pool_t *result_pool, 3893289180Speter apr_pool_t *scratch_pool) 3894251881Speter{ 3895362181Sdim const char *path, *full_path, *fs_path, *hooks_env, *canonical_path; 3896362181Sdim const char *canonical_root; 3897251881Speter svn_stringbuf_t *url_buf; 3898362181Sdim svn_boolean_t sasl_requested; 3899251881Speter 3900251881Speter /* Skip past the scheme and authority part. */ 3901251881Speter path = skip_scheme_part(url); 3902251881Speter if (path == NULL) 3903251881Speter return svn_error_createf(SVN_ERR_BAD_URL, NULL, 3904251881Speter "Non-svn URL passed to svn server: '%s'", url); 3905251881Speter 3906289180Speter if (! vhost) 3907251881Speter { 3908251881Speter path = strchr(path, '/'); 3909251881Speter if (path == NULL) 3910251881Speter path = ""; 3911251881Speter } 3912362181Sdim SVN_ERR(svn_relpath_canonicalize_safe(&canonical_path, NULL, path, 3913362181Sdim scratch_pool, scratch_pool)); 3914362181Sdim path = svn_path_uri_decode(canonical_path, scratch_pool); 3915251881Speter 3916251881Speter /* Ensure that it isn't possible to escape the root by disallowing 3917251881Speter '..' segments. */ 3918251881Speter if (!repos_path_valid(path)) 3919251881Speter return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 3920251881Speter "Couldn't determine repository path"); 3921251881Speter 3922251881Speter /* Join the server-configured root with the client path. */ 3923362181Sdim SVN_ERR(svn_dirent_canonicalize_safe(&canonical_root, NULL, root, 3924362181Sdim scratch_pool, scratch_pool)); 3925362181Sdim full_path = svn_dirent_join(canonical_root, path, scratch_pool); 3926251881Speter 3927251881Speter /* Search for a repository in the full path. */ 3928289180Speter repository->repos_root = svn_repos_find_root_path(full_path, result_pool); 3929289180Speter if (!repository->repos_root) 3930251881Speter return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL, 3931251881Speter "No repository found in '%s'", url); 3932251881Speter 3933251881Speter /* Open the repository and fill in b with the resulting information. */ 3934289180Speter SVN_ERR(svn_repos_open3(&repository->repos, repository->repos_root, 3935289180Speter fs_config, result_pool, scratch_pool)); 3936289180Speter SVN_ERR(svn_repos_remember_client_capabilities(repository->repos, 3937289180Speter repository->capabilities)); 3938289180Speter repository->fs = svn_repos_fs(repository->repos); 3939289180Speter fs_path = full_path + strlen(repository->repos_root); 3940289180Speter repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", 3941289180Speter result_pool); 3942289180Speter url_buf = svn_stringbuf_create(url, result_pool); 3943251881Speter svn_path_remove_components(url_buf, 3944289180Speter svn_path_component_count(repository->fs_path->data)); 3945289180Speter repository->repos_url = url_buf->data; 3946362181Sdim repository->authz_repos_name = svn_dirent_is_child(canonical_root, 3947289180Speter repository->repos_root, 3948289180Speter result_pool); 3949289180Speter if (repository->authz_repos_name == NULL) 3950289180Speter repository->repos_name = svn_dirent_basename(repository->repos_root, 3951289180Speter result_pool); 3952251881Speter else 3953289180Speter repository->repos_name = repository->authz_repos_name; 3954289180Speter repository->repos_name = svn_path_uri_encode(repository->repos_name, 3955289180Speter result_pool); 3956251881Speter 3957251881Speter /* If the svnserve configuration has not been loaded then load it from the 3958251881Speter * repository. */ 3959289180Speter if (NULL == cfg) 3960251881Speter { 3961289180Speter repository->base = svn_repos_conf_dir(repository->repos, result_pool); 3962251881Speter 3963362181Sdim SVN_ERR(svn_repos__config_pool_get(&cfg, config_pool, 3964289180Speter svn_repos_svnserve_conf 3965289180Speter (repository->repos, result_pool), 3966362181Sdim FALSE, repository->repos, 3967289180Speter result_pool)); 3968251881Speter } 3969289180Speter 3970289180Speter SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool)); 3971289180Speter SVN_ERR(load_authz_config(repository, repository->repos_root, cfg, 3972362181Sdim authz_warning_func, authz_warning_baton, 3973362181Sdim result_pool, scratch_pool)); 3974289180Speter 3975362181Sdim /* Should we use Cyrus SASL? */ 3976362181Sdim SVN_ERR(svn_config_get_bool(cfg, &sasl_requested, 3977362181Sdim SVN_CONFIG_SECTION_SASL, 3978362181Sdim SVN_CONFIG_OPTION_USE_SASL, FALSE)); 3979362181Sdim if (sasl_requested) 3980362181Sdim { 3981289180Speter#ifdef SVN_HAVE_SASL 3982289180Speter const char *val; 3983289180Speter 3984362181Sdim repository->use_sasl = sasl_requested; 3985289180Speter 3986289180Speter svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, 3987289180Speter SVN_CONFIG_OPTION_MIN_SSF, "0"); 3988289180Speter SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val)); 3989289180Speter 3990289180Speter svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, 3991289180Speter SVN_CONFIG_OPTION_MAX_SSF, "256"); 3992289180Speter SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val)); 3993362181Sdim#else /* !SVN_HAVE_SASL */ 3994362181Sdim return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 3995362181Sdim _("SASL requested but not compiled in; " 3996362181Sdim "set '%s' to 'false' or recompile " 3997362181Sdim "svnserve with SASL support"), 3998362181Sdim SVN_CONFIG_OPTION_USE_SASL); 3999362181Sdim#endif /* SVN_HAVE_SASL */ 4000251881Speter } 4001362181Sdim else 4002362181Sdim { 4003362181Sdim repository->use_sasl = FALSE; 4004362181Sdim } 4005251881Speter 4006251881Speter /* Use the repository UUID as the default realm. */ 4007289180Speter SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool)); 4008289180Speter svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL, 4009289180Speter SVN_CONFIG_OPTION_REALM, repository->realm); 4010289180Speter repository->realm = apr_pstrdup(result_pool, repository->realm); 4011251881Speter 4012251881Speter /* Make sure it's possible for the client to authenticate. Note 4013251881Speter that this doesn't take into account any authz configuration read 4014251881Speter above, because we can't know about access it grants until paths 4015251881Speter are given by the client. */ 4016289180Speter set_access(repository, cfg, read_only); 4017251881Speter 4018251881Speter /* Configure hook script environment variables. */ 4019289180Speter svn_config_get(cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, 4020251881Speter SVN_CONFIG_OPTION_HOOKS_ENV, NULL); 4021251881Speter if (hooks_env) 4022289180Speter hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool); 4023251881Speter 4024309511Speter SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, scratch_pool)); 4025289180Speter repository->hooks_env = apr_pstrdup(result_pool, hooks_env); 4026289180Speter 4027251881Speter return SVN_NO_ERROR; 4028251881Speter} 4029251881Speter 4030251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */ 4031251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool) 4032251881Speter{ 4033251881Speter /* Only offer EXTERNAL for connections tunneled over a login agent. */ 4034251881Speter if (!params->tunnel) 4035251881Speter return NULL; 4036251881Speter 4037251881Speter /* If a tunnel user was provided on the command line, use that. */ 4038251881Speter if (params->tunnel_user) 4039251881Speter return params->tunnel_user; 4040251881Speter 4041251881Speter return svn_user_get_name(pool); 4042251881Speter} 4043251881Speter 4044251881Speterstatic void 4045251881Speterfs_warning_func(void *baton, svn_error_t *err) 4046251881Speter{ 4047251881Speter fs_warning_baton_t *b = baton; 4048289180Speter log_error(err, b->server); 4049251881Speter} 4050251881Speter 4051251881Speter/* Return the normalized repository-relative path for the given PATH 4052251881Speter * (may be a URL, full path or relative path) and fs contained in the 4053251881Speter * server baton BATON. Allocate the result in POOL. 4054251881Speter */ 4055251881Speterstatic const char * 4056251881Speterget_normalized_repo_rel_path(void *baton, 4057251881Speter const char *path, 4058251881Speter apr_pool_t *pool) 4059251881Speter{ 4060251881Speter server_baton_t *sb = baton; 4061251881Speter 4062251881Speter if (svn_path_is_url(path)) 4063251881Speter { 4064251881Speter /* This is a copyfrom URL. */ 4065289180Speter path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool); 4066251881Speter path = svn_fspath__canonicalize(path, pool); 4067251881Speter } 4068251881Speter else 4069251881Speter { 4070251881Speter /* This is a base-relative path. */ 4071251881Speter if ((path)[0] != '/') 4072251881Speter /* Get an absolute path for use in the FS. */ 4073289180Speter path = svn_fspath__join(sb->repository->fs_path->data, path, pool); 4074251881Speter } 4075251881Speter 4076251881Speter return path; 4077251881Speter} 4078251881Speter 4079251881Speter/* Get the revision root for REVISION in fs given by server baton BATON 4080251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM. 4081251881Speter * Use POOL for allocations. 4082251881Speter */ 4083251881Speterstatic svn_error_t * 4084251881Speterget_revision_root(svn_fs_root_t **fs_root, 4085251881Speter void *baton, 4086251881Speter svn_revnum_t revision, 4087251881Speter apr_pool_t *pool) 4088251881Speter{ 4089251881Speter server_baton_t *sb = baton; 4090251881Speter 4091251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 4092289180Speter SVN_ERR(svn_fs_youngest_rev(&revision, sb->repository->fs, pool)); 4093251881Speter 4094289180Speter SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool)); 4095251881Speter 4096251881Speter return SVN_NO_ERROR; 4097251881Speter} 4098251881Speter 4099251881Speterstatic svn_error_t * 4100251881Speterfetch_props_func(apr_hash_t **props, 4101251881Speter void *baton, 4102251881Speter const char *path, 4103251881Speter svn_revnum_t base_revision, 4104251881Speter apr_pool_t *result_pool, 4105251881Speter apr_pool_t *scratch_pool) 4106251881Speter{ 4107251881Speter svn_fs_root_t *fs_root; 4108251881Speter svn_error_t *err; 4109251881Speter 4110251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 4111251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 4112251881Speter 4113251881Speter err = svn_fs_node_proplist(props, fs_root, path, result_pool); 4114251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 4115251881Speter { 4116251881Speter svn_error_clear(err); 4117251881Speter *props = apr_hash_make(result_pool); 4118251881Speter return SVN_NO_ERROR; 4119251881Speter } 4120251881Speter else if (err) 4121251881Speter return svn_error_trace(err); 4122251881Speter 4123251881Speter return SVN_NO_ERROR; 4124251881Speter} 4125251881Speter 4126251881Speterstatic svn_error_t * 4127251881Speterfetch_kind_func(svn_node_kind_t *kind, 4128251881Speter void *baton, 4129251881Speter const char *path, 4130251881Speter svn_revnum_t base_revision, 4131251881Speter apr_pool_t *scratch_pool) 4132251881Speter{ 4133251881Speter svn_fs_root_t *fs_root; 4134251881Speter 4135251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 4136251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 4137251881Speter 4138251881Speter SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); 4139251881Speter 4140251881Speter return SVN_NO_ERROR; 4141251881Speter} 4142251881Speter 4143251881Speterstatic svn_error_t * 4144251881Speterfetch_base_func(const char **filename, 4145251881Speter void *baton, 4146251881Speter const char *path, 4147251881Speter svn_revnum_t base_revision, 4148251881Speter apr_pool_t *result_pool, 4149251881Speter apr_pool_t *scratch_pool) 4150251881Speter{ 4151251881Speter svn_stream_t *contents; 4152251881Speter svn_stream_t *file_stream; 4153251881Speter const char *tmp_filename; 4154251881Speter svn_fs_root_t *fs_root; 4155251881Speter svn_error_t *err; 4156251881Speter 4157251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 4158251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 4159251881Speter 4160251881Speter err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); 4161251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 4162251881Speter { 4163251881Speter svn_error_clear(err); 4164251881Speter *filename = NULL; 4165251881Speter return SVN_NO_ERROR; 4166251881Speter } 4167251881Speter else if (err) 4168251881Speter return svn_error_trace(err); 4169251881Speter SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, 4170251881Speter svn_io_file_del_on_pool_cleanup, 4171251881Speter scratch_pool, scratch_pool)); 4172251881Speter SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); 4173251881Speter 4174251881Speter *filename = apr_pstrdup(result_pool, tmp_filename); 4175251881Speter 4176251881Speter return SVN_NO_ERROR; 4177251881Speter} 4178251881Speter 4179289180Speterclient_info_t * 4180289180Speterget_client_info(svn_ra_svn_conn_t *conn, 4181289180Speter serve_params_t *params, 4182289180Speter apr_pool_t *pool) 4183251881Speter{ 4184289180Speter client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info)); 4185289180Speter 4186289180Speter client_info->tunnel = params->tunnel; 4187289180Speter client_info->tunnel_user = get_tunnel_user(params, pool); 4188289180Speter client_info->user = NULL; 4189289180Speter client_info->authz_user = NULL; 4190289180Speter client_info->remote_host = svn_ra_svn_conn_remote_host(conn); 4191289180Speter 4192289180Speter return client_info; 4193289180Speter} 4194289180Speter 4195362181Sdimstatic void 4196362181Sdimhandle_authz_warning(void *baton, 4197362181Sdim const svn_error_t *err, 4198362181Sdim apr_pool_t *scratch_pool) 4199362181Sdim{ 4200362181Sdim server_baton_t *const server_baton = baton; 4201362181Sdim log_warning(err, server_baton); 4202362181Sdim SVN_UNUSED(scratch_pool); 4203362181Sdim} 4204362181Sdim 4205289180Speter/* Construct the server baton for CONN using PARAMS and return it in *BATON. 4206289180Speter * It's lifetime is the same as that of CONN. SCRATCH_POOL 4207289180Speter */ 4208289180Speterstatic svn_error_t * 4209289180Speterconstruct_server_baton(server_baton_t **baton, 4210289180Speter svn_ra_svn_conn_t *conn, 4211289180Speter serve_params_t *params, 4212289180Speter apr_pool_t *scratch_pool) 4213289180Speter{ 4214362181Sdim svn_error_t *err; 4215251881Speter apr_uint64_t ver; 4216362181Sdim const char *client_url, *ra_client_string, *client_string, *canonical_url; 4217362181Sdim svn_ra_svn__list_t *caplist; 4218289180Speter apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn); 4219289180Speter server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b)); 4220289180Speter fs_warning_baton_t *warn_baton; 4221289180Speter svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool); 4222251881Speter 4223289180Speter b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository)); 4224289180Speter b->repository->username_case = params->username_case; 4225289180Speter b->repository->base = params->base; 4226289180Speter b->repository->pwdb = NULL; 4227289180Speter b->repository->authzdb = NULL; 4228289180Speter b->repository->realm = NULL; 4229289180Speter b->repository->use_sasl = FALSE; 4230251881Speter 4231289180Speter b->read_only = params->read_only; 4232289180Speter b->pool = conn_pool; 4233289180Speter b->vhost = params->vhost; 4234251881Speter 4235289180Speter b->logger = params->logger; 4236289180Speter b->client_info = get_client_info(conn, params, conn_pool); 4237289180Speter 4238251881Speter /* Send greeting. We don't support version 1 any more, so we can 4239251881Speter * send an empty mechlist. */ 4240251881Speter if (params->compression_level > 0) 4241289180Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, 4242362181Sdim "nn()(wwwwwwwwwwwww)", 4243251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 4244251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 4245251881Speter SVN_RA_SVN_CAP_SVNDIFF1, 4246362181Sdim SVN_RA_SVN_CAP_SVNDIFF2_ACCEPTED, 4247251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 4248251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 4249251881Speter SVN_RA_SVN_CAP_DEPTH, 4250251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 4251251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 4252251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 4253251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 4254251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 4255362181Sdim SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE, 4256362181Sdim SVN_RA_SVN_CAP_LIST 4257251881Speter )); 4258251881Speter else 4259289180Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, 4260362181Sdim "nn()(wwwwwwwwwww)", 4261251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 4262251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 4263251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 4264251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 4265251881Speter SVN_RA_SVN_CAP_DEPTH, 4266251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 4267251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 4268251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 4269251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 4270251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 4271362181Sdim SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE, 4272362181Sdim SVN_RA_SVN_CAP_LIST 4273251881Speter )); 4274251881Speter 4275251881Speter /* Read client response, which we assume to be in version 2 format: 4276251881Speter * version, capability list, and client URL; then we do an auth 4277251881Speter * request. */ 4278289180Speter SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_pool, "nlc?c(?c)", 4279251881Speter &ver, &caplist, &client_url, 4280251881Speter &ra_client_string, 4281251881Speter &client_string)); 4282251881Speter if (ver != 2) 4283362181Sdim return svn_error_createf(SVN_ERR_RA_SVN_BAD_VERSION, NULL, 4284362181Sdim "Unsupported ra_svn protocol version" 4285362181Sdim " %"APR_UINT64_T_FMT 4286362181Sdim " (supported versions: [2])", ver); 4287251881Speter 4288362181Sdim SVN_ERR(svn_uri_canonicalize_safe(&canonical_url, NULL, client_url, 4289362181Sdim conn_pool, scratch_pool)); 4290362181Sdim client_url = canonical_url; 4291362181Sdim SVN_ERR(svn_ra_svn__set_capabilities(conn, caplist)); 4292251881Speter 4293251881Speter /* All released versions of Subversion support edit-pipeline, 4294251881Speter * so we do not accept connections from clients that do not. */ 4295251881Speter if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) 4296362181Sdim return svn_error_create(SVN_ERR_RA_SVN_BAD_VERSION, NULL, 4297362181Sdim "Missing edit-pipeline capability"); 4298251881Speter 4299251881Speter /* find_repos needs the capabilities as a list of words (eventually 4300251881Speter they get handed to the start-commit hook). While we could add a 4301251881Speter new interface to re-retrieve them from conn and convert the 4302251881Speter result to a list, it's simpler to just convert caplist by hand 4303362181Sdim here, since we already have it and turning 'svn_ra_svn__item_t's 4304251881Speter into 'const char *'s is pretty easy. 4305251881Speter 4306251881Speter We only record capabilities we care about. The client may report 4307251881Speter more (because it doesn't know what the server cares about). */ 4308251881Speter { 4309251881Speter int i; 4310362181Sdim svn_ra_svn__item_t *item; 4311251881Speter 4312289180Speter b->repository->capabilities = apr_array_make(conn_pool, 1, 4313289180Speter sizeof(const char *)); 4314251881Speter for (i = 0; i < caplist->nelts; i++) 4315251881Speter { 4316362181Sdim static const svn_string_t str_cap_mergeinfo 4317362181Sdim = SVN__STATIC_STRING(SVN_RA_SVN_CAP_MERGEINFO); 4318362181Sdim 4319362181Sdim item = &SVN_RA_SVN__LIST_ITEM(caplist, i); 4320251881Speter /* ra_svn_set_capabilities() already type-checked for us */ 4321362181Sdim if (svn_string_compare(&item->u.word, &str_cap_mergeinfo)) 4322251881Speter { 4323289180Speter APR_ARRAY_PUSH(b->repository->capabilities, const char *) 4324251881Speter = SVN_RA_CAPABILITY_MERGEINFO; 4325251881Speter } 4326251881Speter /* Save for operational log. */ 4327251881Speter if (cap_log->len > 0) 4328251881Speter svn_stringbuf_appendcstr(cap_log, " "); 4329362181Sdim svn_stringbuf_appendcstr(cap_log, item->u.word.data); 4330251881Speter } 4331251881Speter } 4332251881Speter 4333362181Sdim /* (*b) has the logger, repository and client_info set, so it can 4334362181Sdim be used as the authz_warning_baton that eventyally gets passed 4335362181Sdim to log_warning(). */ 4336289180Speter err = handle_config_error(find_repos(client_url, params->root, b->vhost, 4337289180Speter b->read_only, params->cfg, 4338289180Speter b->repository, params->config_pool, 4339362181Sdim params->fs_config, 4340362181Sdim handle_authz_warning, b, 4341289180Speter conn_pool, scratch_pool), 4342289180Speter b); 4343251881Speter if (!err) 4344251881Speter { 4345289180Speter if (b->repository->anon_access == NO_ACCESS 4346289180Speter && (b->repository->auth_access == NO_ACCESS 4347289180Speter || (!b->client_info->tunnel_user && !b->repository->pwdb 4348289180Speter && !b->repository->use_sasl))) 4349251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 4350289180Speter "No access allowed to this repository", 4351289180Speter b); 4352251881Speter } 4353289180Speter if (!err) 4354289180Speter { 4355289180Speter SVN_ERR(auth_request(conn, scratch_pool, b, READ_ACCESS, FALSE)); 4356289180Speter if (current_access(b) == NO_ACCESS) 4357289180Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 4358289180Speter "Not authorized for access", b); 4359289180Speter } 4360251881Speter if (err) 4361251881Speter { 4362362181Sdim /* Report these errors to the client before closing the connection. */ 4363362181Sdim err = svn_error_compose_create(err, 4364362181Sdim svn_ra_svn__write_cmd_failure(conn, scratch_pool, err)); 4365362181Sdim err = svn_error_compose_create(err, 4366362181Sdim svn_ra_svn__flush(conn, scratch_pool)); 4367362181Sdim return err; 4368251881Speter } 4369251881Speter 4370289180Speter SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid, 4371289180Speter conn_pool)); 4372251881Speter 4373251881Speter /* We can't claim mergeinfo capability until we know whether the 4374251881Speter repository supports mergeinfo (i.e., is not a 1.4 repository), 4375251881Speter but we don't get the repository url from the client until after 4376251881Speter we've already sent the initial list of server capabilities. So 4377251881Speter we list repository capabilities here, in our first response after 4378251881Speter the client has sent the url. */ 4379251881Speter { 4380251881Speter svn_boolean_t supports_mergeinfo; 4381289180Speter SVN_ERR(svn_repos_has_capability(b->repository->repos, 4382289180Speter &supports_mergeinfo, 4383289180Speter SVN_REPOS_CAPABILITY_MERGEINFO, 4384289180Speter scratch_pool)); 4385251881Speter 4386289180Speter SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!", 4387289180Speter "success", b->repository->uuid, 4388289180Speter b->repository->repos_url)); 4389251881Speter if (supports_mergeinfo) 4390289180Speter SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool, 4391289180Speter SVN_RA_SVN_CAP_MERGEINFO)); 4392289180Speter SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))")); 4393289180Speter SVN_ERR(svn_ra_svn__flush(conn, scratch_pool)); 4394251881Speter } 4395251881Speter 4396289180Speter /* Log the open. */ 4397289180Speter if (ra_client_string == NULL || ra_client_string[0] == '\0') 4398289180Speter ra_client_string = "-"; 4399289180Speter else 4400289180Speter ra_client_string = svn_path_uri_encode(ra_client_string, scratch_pool); 4401289180Speter if (client_string == NULL || client_string[0] == '\0') 4402289180Speter client_string = "-"; 4403289180Speter else 4404289180Speter client_string = svn_path_uri_encode(client_string, scratch_pool); 4405289180Speter SVN_ERR(log_command(b, conn, scratch_pool, 4406289180Speter "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s", 4407289180Speter ver, cap_log->data, 4408289180Speter svn_path_uri_encode(b->repository->fs_path->data, 4409289180Speter scratch_pool), 4410289180Speter ra_client_string, client_string)); 4411289180Speter 4412289180Speter warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton)); 4413289180Speter warn_baton->server = b; 4414289180Speter warn_baton->conn = conn; 4415289180Speter svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton); 4416289180Speter 4417251881Speter /* Set up editor shims. */ 4418251881Speter { 4419251881Speter svn_delta_shim_callbacks_t *callbacks = 4420289180Speter svn_delta_shim_callbacks_default(conn_pool); 4421251881Speter 4422251881Speter callbacks->fetch_base_func = fetch_base_func; 4423251881Speter callbacks->fetch_props_func = fetch_props_func; 4424251881Speter callbacks->fetch_kind_func = fetch_kind_func; 4425289180Speter callbacks->fetch_baton = b; 4426251881Speter 4427251881Speter SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks)); 4428251881Speter } 4429251881Speter 4430289180Speter *baton = b; 4431289180Speter 4432289180Speter return SVN_NO_ERROR; 4433251881Speter} 4434289180Speter 4435289180Spetersvn_error_t * 4436289180Speterserve_interruptable(svn_boolean_t *terminate_p, 4437289180Speter connection_t *connection, 4438289180Speter svn_boolean_t (* is_busy)(connection_t *), 4439289180Speter apr_pool_t *pool) 4440289180Speter{ 4441289180Speter svn_boolean_t terminate = FALSE; 4442289180Speter svn_error_t *err = NULL; 4443362181Sdim const svn_ra_svn__cmd_entry_t *command; 4444289180Speter apr_pool_t *iterpool = svn_pool_create(pool); 4445289180Speter 4446289180Speter /* Prepare command parser. */ 4447289180Speter apr_hash_t *cmd_hash = apr_hash_make(pool); 4448289180Speter for (command = main_commands; command->cmdname; command++) 4449289180Speter svn_hash_sets(cmd_hash, command->cmdname, command); 4450289180Speter 4451289180Speter /* Auto-initialize connection */ 4452289180Speter if (! connection->conn) 4453289180Speter { 4454289180Speter apr_status_t ar; 4455289180Speter 4456289180Speter /* Enable TCP keep-alives on the socket so we time out when 4457289180Speter * the connection breaks due to network-layer problems. 4458289180Speter * If the peer has dropped the connection due to a network partition 4459289180Speter * or a crash, or if the peer no longer considers the connection 4460289180Speter * valid because we are behind a NAT and our public IP has changed, 4461289180Speter * it will respond to the keep-alive probe with a RST instead of an 4462289180Speter * acknowledgment segment, which will cause svn to abort the session 4463289180Speter * even while it is currently blocked waiting for data from the peer. */ 4464289180Speter ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1); 4465289180Speter if (ar) 4466289180Speter { 4467289180Speter /* It's not a fatal error if we cannot enable keep-alives. */ 4468289180Speter } 4469289180Speter 4470289180Speter /* create the connection, configure ports etc. */ 4471289180Speter connection->conn 4472362181Sdim = svn_ra_svn_create_conn5(connection->usock, NULL, NULL, 4473289180Speter connection->params->compression_level, 4474289180Speter connection->params->zero_copy_limit, 4475289180Speter connection->params->error_check_interval, 4476362181Sdim connection->params->max_request_size, 4477362181Sdim connection->params->max_response_size, 4478289180Speter connection->pool); 4479289180Speter 4480289180Speter /* Construct server baton and open the repository for the first time. */ 4481289180Speter err = construct_server_baton(&connection->baton, connection->conn, 4482289180Speter connection->params, pool); 4483289180Speter } 4484289180Speter 4485289180Speter /* If we can't access the repo for some reason, end this connection. */ 4486289180Speter if (err) 4487289180Speter terminate = TRUE; 4488289180Speter 4489289180Speter /* Process incoming commands. */ 4490289180Speter while (!terminate && !err) 4491289180Speter { 4492289180Speter svn_pool_clear(iterpool); 4493289180Speter if (is_busy && is_busy(connection)) 4494289180Speter { 4495289180Speter svn_boolean_t has_command; 4496289180Speter 4497289180Speter /* If the server is busy, execute just one command and only if 4498289180Speter * there is one currently waiting in our receive buffers. 4499289180Speter */ 4500289180Speter err = svn_ra_svn__has_command(&has_command, &terminate, 4501289180Speter connection->conn, iterpool); 4502289180Speter if (!err && has_command) 4503289180Speter err = svn_ra_svn__handle_command(&terminate, cmd_hash, 4504289180Speter connection->baton, 4505289180Speter connection->conn, 4506289180Speter FALSE, iterpool); 4507289180Speter 4508289180Speter break; 4509289180Speter } 4510289180Speter else 4511289180Speter { 4512289180Speter /* The server is not busy, thus let's serve whichever command 4513289180Speter * comes in next and whenever it comes in. This requires the 4514289180Speter * busy() callback test to return TRUE while there are still some 4515289180Speter * resources left. 4516289180Speter */ 4517289180Speter err = svn_ra_svn__handle_command(&terminate, cmd_hash, 4518289180Speter connection->baton, 4519289180Speter connection->conn, 4520289180Speter FALSE, iterpool); 4521289180Speter } 4522289180Speter } 4523289180Speter 4524289180Speter /* error or normal end of session. Close the connection */ 4525289180Speter svn_pool_destroy(iterpool); 4526289180Speter if (terminate_p) 4527289180Speter *terminate_p = terminate; 4528289180Speter 4529289180Speter return svn_error_trace(err); 4530289180Speter} 4531289180Speter 4532289180Spetersvn_error_t *serve(svn_ra_svn_conn_t *conn, 4533289180Speter serve_params_t *params, 4534289180Speter apr_pool_t *pool) 4535289180Speter{ 4536289180Speter server_baton_t *baton = NULL; 4537289180Speter 4538289180Speter SVN_ERR(construct_server_baton(&baton, conn, params, pool)); 4539289180Speter return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE); 4540289180Speter} 4541