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" 64299742Sdim#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; 90251881Speter} log_baton_t; 91251881Speter 92251881Spetertypedef struct file_revs_baton_t { 93251881Speter svn_ra_svn_conn_t *conn; 94251881Speter apr_pool_t *pool; /* Pool provided in the handler call. */ 95251881Speter} file_revs_baton_t; 96251881Speter 97251881Spetertypedef struct fs_warning_baton_t { 98251881Speter server_baton_t *server; 99251881Speter svn_ra_svn_conn_t *conn; 100251881Speter} fs_warning_baton_t; 101251881Speter 102251881Spetertypedef struct authz_baton_t { 103251881Speter server_baton_t *server; 104251881Speter svn_ra_svn_conn_t *conn; 105251881Speter} authz_baton_t; 106251881Speter 107299742Sdim/* svn_error_create() a new error, log_server_error() it, and 108299742Sdim return it. */ 109251881Speterstatic void 110299742Sdimlog_error(svn_error_t *err, server_baton_t *server) 111251881Speter{ 112299742Sdim logger__log_error(server->logger, err, server->repository, 113299742Sdim server->client_info); 114251881Speter} 115251881Speter 116251881Speter/* svn_error_create() a new error, log_server_error() it, and 117251881Speter return it. */ 118251881Speterstatic svn_error_t * 119251881Spetererror_create_and_log(apr_status_t apr_err, svn_error_t *child, 120299742Sdim const char *message, server_baton_t *server) 121251881Speter{ 122251881Speter svn_error_t *err = svn_error_create(apr_err, child, message); 123299742Sdim log_error(err, server); 124251881Speter return err; 125251881Speter} 126251881Speter 127251881Speter/* Log a failure ERR, transmit ERR back to the client (as part of a 128251881Speter "failure" notification), consume ERR, and flush the connection. */ 129251881Speterstatic svn_error_t * 130251881Speterlog_fail_and_flush(svn_error_t *err, server_baton_t *server, 131251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool) 132251881Speter{ 133251881Speter svn_error_t *io_err; 134251881Speter 135299742Sdim log_error(err, server); 136251881Speter io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 137251881Speter svn_error_clear(err); 138251881Speter SVN_ERR(io_err); 139251881Speter return svn_ra_svn__flush(conn, pool); 140251881Speter} 141251881Speter 142251881Speter/* Log a client command. */ 143251881Speterstatic svn_error_t *log_command(server_baton_t *b, 144251881Speter svn_ra_svn_conn_t *conn, 145251881Speter apr_pool_t *pool, 146251881Speter const char *fmt, ...) 147251881Speter{ 148251881Speter const char *remote_host, *timestr, *log, *line; 149251881Speter va_list ap; 150251881Speter apr_size_t nbytes; 151251881Speter 152299742Sdim if (b->logger == NULL) 153251881Speter return SVN_NO_ERROR; 154251881Speter 155251881Speter remote_host = svn_ra_svn_conn_remote_host(conn); 156251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 157251881Speter 158251881Speter va_start(ap, fmt); 159251881Speter log = apr_pvsprintf(pool, fmt, ap); 160251881Speter va_end(ap); 161251881Speter 162251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 163251881Speter " %s %s %s %s %s" APR_EOL_STR, 164251881Speter getpid(), timestr, 165251881Speter (remote_host ? remote_host : "-"), 166299742Sdim (b->client_info->user ? b->client_info->user : "-"), 167299742Sdim b->repository->repos_name, log); 168251881Speter nbytes = strlen(line); 169251881Speter 170299742Sdim return logger__write(b->logger, line, nbytes); 171251881Speter} 172251881Speter 173251881Speter/* Log an authz failure */ 174251881Speterstatic svn_error_t * 175251881Speterlog_authz_denied(const char *path, 176251881Speter svn_repos_authz_access_t required, 177251881Speter server_baton_t *b, 178251881Speter apr_pool_t *pool) 179251881Speter{ 180251881Speter const char *timestr, *remote_host, *line; 181251881Speter 182299742Sdim if (!b->logger) 183251881Speter return SVN_NO_ERROR; 184251881Speter 185299742Sdim if (!b->client_info || !b->client_info->user) 186251881Speter return SVN_NO_ERROR; 187251881Speter 188251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 189299742Sdim remote_host = b->client_info->remote_host; 190251881Speter 191251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 192251881Speter " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR, 193251881Speter getpid(), timestr, 194251881Speter (remote_host ? remote_host : "-"), 195299742Sdim b->client_info->user, 196299742Sdim b->repository->repos_name, 197251881Speter (required & svn_authz_recursive ? "recursive " : ""), 198251881Speter (required & svn_authz_write ? "write" : "read"), 199251881Speter (path && path[0] ? path : "/")); 200251881Speter 201299742Sdim return logger__write(b->logger, line, strlen(line)); 202251881Speter} 203251881Speter 204299742Sdim/* If CFG specifies a path to the password DB, read that DB through 205299742Sdim * CONFIG_POOL and store it in REPOSITORY->PWDB. 206299742Sdim */ 207299742Sdimstatic svn_error_t * 208299742Sdimload_pwdb_config(repository_t *repository, 209299742Sdim svn_config_t *cfg, 210299742Sdim svn_repos__config_pool_t *config_pool, 211299742Sdim apr_pool_t *pool) 212251881Speter{ 213251881Speter const char *pwdb_path; 214251881Speter svn_error_t *err; 215251881Speter 216299742Sdim svn_config_get(cfg, &pwdb_path, 217299742Sdim SVN_CONFIG_SECTION_GENERAL, 218251881Speter SVN_CONFIG_OPTION_PASSWORD_DB, NULL); 219251881Speter 220299742Sdim repository->pwdb = NULL; 221251881Speter if (pwdb_path) 222251881Speter { 223251881Speter pwdb_path = svn_dirent_internal_style(pwdb_path, pool); 224299742Sdim pwdb_path = svn_dirent_join(repository->base, pwdb_path, pool); 225251881Speter 226299742Sdim err = svn_repos__config_pool_get(&repository->pwdb, NULL, config_pool, 227299742Sdim pwdb_path, TRUE, FALSE, 228299742Sdim repository->repos, pool); 229251881Speter if (err) 230251881Speter { 231251881Speter /* Because it may be possible to read the pwdb file with some 232251881Speter access methods and not others, ignore errors reading the pwdb 233251881Speter file and just don't present password authentication as an 234251881Speter option. Also, some authentications (e.g. --tunnel) can 235251881Speter proceed without it anyway. 236251881Speter 237251881Speter ### Not entirely sure why SVN_ERR_BAD_FILENAME is checked 238251881Speter ### for here. That seems to have been introduced in r856914, 239251881Speter ### and only in r870942 was the APR_EACCES check introduced. */ 240251881Speter if (err->apr_err != SVN_ERR_BAD_FILENAME 241251881Speter && ! APR_STATUS_IS_EACCES(err->apr_err)) 242251881Speter { 243299742Sdim return svn_error_create(SVN_ERR_AUTHN_FAILED, err, NULL); 244251881Speter } 245251881Speter else 246251881Speter /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */ 247251881Speter svn_error_clear(err); 248251881Speter } 249251881Speter } 250251881Speter 251251881Speter return SVN_NO_ERROR; 252251881Speter} 253251881Speter 254251881Speter/* Canonicalize *ACCESS_FILE based on the type of argument. Results are 255299742Sdim * placed in *ACCESS_FILE. REPOSITORY is used to convert relative paths to 256251881Speter * absolute paths rooted at the server root. REPOS_ROOT is used to calculate 257251881Speter * an absolute URL for repos-relative URLs. */ 258251881Speterstatic svn_error_t * 259299742Sdimcanonicalize_access_file(const char **access_file, repository_t *repository, 260251881Speter const char *repos_root, apr_pool_t *pool) 261251881Speter{ 262251881Speter if (svn_path_is_url(*access_file)) 263251881Speter { 264251881Speter *access_file = svn_uri_canonicalize(*access_file, pool); 265251881Speter } 266251881Speter else if (svn_path_is_repos_relative_url(*access_file)) 267251881Speter { 268251881Speter const char *repos_root_url; 269251881Speter 270251881Speter SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root, 271251881Speter pool)); 272251881Speter SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file, 273251881Speter repos_root_url, pool)); 274251881Speter *access_file = svn_uri_canonicalize(*access_file, pool); 275251881Speter } 276251881Speter else 277251881Speter { 278251881Speter *access_file = svn_dirent_internal_style(*access_file, pool); 279299742Sdim *access_file = svn_dirent_join(repository->base, *access_file, pool); 280251881Speter } 281251881Speter 282251881Speter return SVN_NO_ERROR; 283251881Speter} 284251881Speter 285299742Sdim/* Load the authz database for the listening server through AUTHZ_POOL 286299742Sdim based on the entries in the SERVER struct. 287299742Sdim 288299742Sdim SERVER and CONN must not be NULL. The real errors will be logged with 289299742Sdim SERVER and CONN but return generic errors to the client. */ 290299742Sdimstatic svn_error_t * 291299742Sdimload_authz_config(repository_t *repository, 292299742Sdim const char *repos_root, 293299742Sdim svn_config_t *cfg, 294299742Sdim svn_repos__authz_pool_t *authz_pool, 295299742Sdim apr_pool_t *pool) 296251881Speter{ 297251881Speter const char *authzdb_path; 298251881Speter const char *groupsdb_path; 299251881Speter svn_error_t *err; 300251881Speter 301251881Speter /* Read authz configuration. */ 302299742Sdim svn_config_get(cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL, 303251881Speter SVN_CONFIG_OPTION_AUTHZ_DB, NULL); 304251881Speter 305299742Sdim svn_config_get(cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL, 306251881Speter SVN_CONFIG_OPTION_GROUPS_DB, NULL); 307251881Speter 308251881Speter if (authzdb_path) 309251881Speter { 310251881Speter const char *case_force_val; 311251881Speter 312251881Speter /* Canonicalize and add the base onto the authzdb_path (if needed). */ 313299742Sdim err = canonicalize_access_file(&authzdb_path, repository, 314251881Speter repos_root, pool); 315251881Speter 316251881Speter /* Same for the groupsdb_path if it is present. */ 317251881Speter if (groupsdb_path && !err) 318299742Sdim err = canonicalize_access_file(&groupsdb_path, repository, 319251881Speter repos_root, pool); 320251881Speter 321251881Speter if (!err) 322299742Sdim err = svn_repos__authz_pool_get(&repository->authzdb, authz_pool, 323299742Sdim authzdb_path, groupsdb_path, TRUE, 324299742Sdim repository->repos, pool); 325251881Speter 326251881Speter if (err) 327299742Sdim return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, err, NULL); 328251881Speter 329251881Speter /* Are we going to be case-normalizing usernames when we consult 330251881Speter * this authz file? */ 331299742Sdim svn_config_get(cfg, &case_force_val, 332299742Sdim SVN_CONFIG_SECTION_GENERAL, 333251881Speter SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL); 334251881Speter if (case_force_val) 335251881Speter { 336251881Speter if (strcmp(case_force_val, "upper") == 0) 337299742Sdim repository->username_case = CASE_FORCE_UPPER; 338251881Speter else if (strcmp(case_force_val, "lower") == 0) 339299742Sdim repository->username_case = CASE_FORCE_LOWER; 340251881Speter else 341299742Sdim repository->username_case = CASE_ASIS; 342251881Speter } 343251881Speter } 344251881Speter else 345251881Speter { 346299742Sdim repository->authzdb = NULL; 347299742Sdim repository->username_case = CASE_ASIS; 348251881Speter } 349251881Speter 350251881Speter return SVN_NO_ERROR; 351251881Speter} 352251881Speter 353299742Sdim/* If ERROR is a AUTH* error as returned by load_pwdb_config or 354299742Sdim * load_authz_config, write it to SERVER's log file. 355299742Sdim * Return a sanitized version of ERROR. 356299742Sdim */ 357299742Sdimstatic svn_error_t * 358299742Sdimhandle_config_error(svn_error_t *error, 359299742Sdim server_baton_t *server) 360299742Sdim{ 361299742Sdim if ( error 362299742Sdim && ( error->apr_err == SVN_ERR_AUTHZ_INVALID_CONFIG 363299742Sdim || error->apr_err == SVN_ERR_AUTHN_FAILED)) 364299742Sdim { 365299742Sdim apr_status_t apr_err = error->apr_err; 366299742Sdim log_error(error, server); 367299742Sdim 368299742Sdim /* Now that we've logged the error, clear it and return a 369299742Sdim * nice, generic error to the user: 370299742Sdim * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */ 371299742Sdim svn_error_clear(error); 372299742Sdim return svn_error_create(apr_err, NULL, NULL); 373299742Sdim } 374299742Sdim 375299742Sdim return error; 376299742Sdim} 377299742Sdim 378251881Speter/* Set *FS_PATH to the portion of URL that is the path within the 379251881Speter repository, if URL is inside REPOS_URL (if URL is not inside 380251881Speter REPOS_URL, then error, with the effect on *FS_PATH undefined). 381251881Speter 382251881Speter If the resultant fs path would be the empty string (i.e., URL and 383251881Speter REPOS_URL are the same), then set *FS_PATH to "/". 384251881Speter 385251881Speter Assume that REPOS_URL and URL are already URI-decoded. */ 386251881Speterstatic svn_error_t *get_fs_path(const char *repos_url, const char *url, 387251881Speter const char **fs_path) 388251881Speter{ 389251881Speter apr_size_t len; 390251881Speter 391251881Speter len = strlen(repos_url); 392251881Speter if (strncmp(url, repos_url, len) != 0) 393251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 394251881Speter "'%s' is not the same repository as '%s'", 395251881Speter url, repos_url); 396251881Speter *fs_path = url + len; 397251881Speter if (! **fs_path) 398251881Speter *fs_path = "/"; 399251881Speter 400251881Speter return SVN_NO_ERROR; 401251881Speter} 402251881Speter 403251881Speter/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */ 404251881Speter 405251881Speter/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else 406251881Speter converts it to lower case. */ 407251881Speterstatic void convert_case(char *text, svn_boolean_t to_uppercase) 408251881Speter{ 409251881Speter char *c = text; 410251881Speter while (*c) 411251881Speter { 412251881Speter *c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c)); 413251881Speter ++c; 414251881Speter } 415251881Speter} 416251881Speter 417251881Speter/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to 418251881Speter the user described in BATON according to the authz rules in BATON. 419251881Speter Use POOL for temporary allocations only. If no authz rules are 420251881Speter present in BATON, grant access by default. */ 421251881Speterstatic svn_error_t *authz_check_access(svn_boolean_t *allowed, 422251881Speter const char *path, 423251881Speter svn_repos_authz_access_t required, 424251881Speter server_baton_t *b, 425251881Speter apr_pool_t *pool) 426251881Speter{ 427299742Sdim repository_t *repository = b->repository; 428299742Sdim client_info_t *client_info = b->client_info; 429299742Sdim 430251881Speter /* If authz cannot be performed, grant access. This is NOT the same 431251881Speter as the default policy when authz is performed on a path with no 432251881Speter rules. In the latter case, the default is to deny access, and is 433251881Speter set by svn_repos_authz_check_access. */ 434299742Sdim if (!repository->authzdb) 435251881Speter { 436251881Speter *allowed = TRUE; 437251881Speter return SVN_NO_ERROR; 438251881Speter } 439251881Speter 440251881Speter /* If the authz request is for the empty path (ie. ""), replace it 441251881Speter with the root path. This happens because of stripping done at 442251881Speter various levels in svnserve that remove the leading / on an 443251881Speter absolute path. Passing such a malformed path to the authz 444251881Speter routines throws them into an infinite loop and makes them miss 445251881Speter ACLs. */ 446251881Speter if (path) 447251881Speter path = svn_fspath__canonicalize(path, pool); 448251881Speter 449251881Speter /* If we have a username, and we've not yet used it + any username 450251881Speter case normalization that might be requested to determine "the 451251881Speter username we used for authz purposes", do so now. */ 452299742Sdim if (client_info->user && (! client_info->authz_user)) 453251881Speter { 454299742Sdim char *authz_user = apr_pstrdup(b->pool, client_info->user); 455299742Sdim if (repository->username_case == CASE_FORCE_UPPER) 456251881Speter convert_case(authz_user, TRUE); 457299742Sdim else if (repository->username_case == CASE_FORCE_LOWER) 458251881Speter convert_case(authz_user, FALSE); 459299742Sdim 460299742Sdim client_info->authz_user = authz_user; 461251881Speter } 462251881Speter 463299742Sdim SVN_ERR(svn_repos_authz_check_access(repository->authzdb, 464299742Sdim repository->authz_repos_name, 465299742Sdim path, client_info->authz_user, 466299742Sdim required, allowed, pool)); 467251881Speter if (!*allowed) 468299742Sdim SVN_ERR(log_authz_denied(path, required, b, pool)); 469251881Speter 470251881Speter return SVN_NO_ERROR; 471251881Speter} 472251881Speter 473251881Speter/* Set *ALLOWED to TRUE if PATH is readable by the user described in 474251881Speter * BATON. Use POOL for temporary allocations only. ROOT is not used. 475251881Speter * Implements the svn_repos_authz_func_t interface. 476251881Speter */ 477251881Speterstatic svn_error_t *authz_check_access_cb(svn_boolean_t *allowed, 478251881Speter svn_fs_root_t *root, 479251881Speter const char *path, 480251881Speter void *baton, 481251881Speter apr_pool_t *pool) 482251881Speter{ 483251881Speter authz_baton_t *sb = baton; 484251881Speter 485251881Speter return authz_check_access(allowed, path, svn_authz_read, 486299742Sdim sb->server, pool); 487251881Speter} 488251881Speter 489251881Speter/* If authz is enabled in the specified BATON, return a read authorization 490251881Speter function. Otherwise, return NULL. */ 491251881Speterstatic svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton) 492251881Speter{ 493299742Sdim if (baton->repository->authzdb) 494251881Speter return authz_check_access_cb; 495251881Speter return NULL; 496251881Speter} 497251881Speter 498251881Speter/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted, 499251881Speter * according to the state in BATON. Use POOL for temporary 500251881Speter * allocations only. ROOT is not used. Implements the 501251881Speter * svn_repos_authz_callback_t interface. 502251881Speter */ 503251881Speterstatic svn_error_t *authz_commit_cb(svn_repos_authz_access_t required, 504251881Speter svn_boolean_t *allowed, 505251881Speter svn_fs_root_t *root, 506251881Speter const char *path, 507251881Speter void *baton, 508251881Speter apr_pool_t *pool) 509251881Speter{ 510251881Speter authz_baton_t *sb = baton; 511251881Speter 512299742Sdim return authz_check_access(allowed, path, required, sb->server, pool); 513251881Speter} 514251881Speter 515299742Sdim/* Return the access level specified for OPTION in CFG. If no such 516299742Sdim * setting exists, use DEF. If READ_ONLY is set, unconditionally disable 517299742Sdim * write access. 518299742Sdim */ 519299742Sdimstatic enum access_type 520299742Sdimget_access(svn_config_t *cfg, 521299742Sdim const char *option, 522299742Sdim const char *def, 523299742Sdim svn_boolean_t read_only) 524251881Speter{ 525251881Speter enum access_type result; 526299742Sdim const char *val; 527251881Speter 528299742Sdim svn_config_get(cfg, &val, SVN_CONFIG_SECTION_GENERAL, option, def); 529251881Speter result = (strcmp(val, "write") == 0 ? WRITE_ACCESS : 530251881Speter strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS); 531299742Sdim 532299742Sdim return result == WRITE_ACCESS && read_only ? READ_ACCESS : result; 533251881Speter} 534251881Speter 535299742Sdim/* Set the *_ACCESS members in REPOSITORY according to the settings in 536299742Sdim * CFG. If READ_ONLY is set, unconditionally disable write access. 537299742Sdim */ 538299742Sdimstatic void 539299742Sdimset_access(repository_t *repository, 540299742Sdim svn_config_t *cfg, 541299742Sdim svn_boolean_t read_only) 542251881Speter{ 543299742Sdim repository->auth_access = get_access(cfg, SVN_CONFIG_OPTION_AUTH_ACCESS, 544299742Sdim "write", read_only); 545299742Sdim repository->anon_access = get_access(cfg, SVN_CONFIG_OPTION_ANON_ACCESS, 546299742Sdim "read", read_only); 547251881Speter} 548251881Speter 549299742Sdim/* Return the access level for the user in B. 550299742Sdim */ 551299742Sdimstatic enum access_type 552299742Sdimcurrent_access(server_baton_t *b) 553299742Sdim{ 554299742Sdim return b->client_info->user ? b->repository->auth_access 555299742Sdim : b->repository->anon_access; 556299742Sdim} 557299742Sdim 558251881Speter/* Send authentication mechs for ACCESS_TYPE to the client. If NEEDS_USERNAME 559251881Speter is true, don't send anonymous mech even if that would give the desired 560251881Speter access. */ 561251881Speterstatic svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 562251881Speter server_baton_t *b, enum access_type required, 563251881Speter svn_boolean_t needs_username) 564251881Speter{ 565299742Sdim if (!needs_username && b->repository->anon_access >= required) 566251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS")); 567299742Sdim if (b->client_info->tunnel_user && b->repository->auth_access >= required) 568251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL")); 569299742Sdim if (b->repository->pwdb && b->repository->auth_access >= required) 570251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5")); 571251881Speter return SVN_NO_ERROR; 572251881Speter} 573251881Speter 574251881Speter/* Context for cleanup handler. */ 575251881Speterstruct cleanup_fs_access_baton 576251881Speter{ 577251881Speter svn_fs_t *fs; 578251881Speter apr_pool_t *pool; 579251881Speter}; 580251881Speter 581251881Speter/* Pool cleanup handler. Make sure fs's access_t points to NULL when 582251881Speter the command pool is destroyed. */ 583251881Speterstatic apr_status_t cleanup_fs_access(void *data) 584251881Speter{ 585251881Speter svn_error_t *serr; 586251881Speter struct cleanup_fs_access_baton *baton = data; 587251881Speter 588251881Speter serr = svn_fs_set_access(baton->fs, NULL); 589251881Speter if (serr) 590251881Speter { 591251881Speter apr_status_t apr_err = serr->apr_err; 592251881Speter svn_error_clear(serr); 593251881Speter return apr_err; 594251881Speter } 595251881Speter 596251881Speter return APR_SUCCESS; 597251881Speter} 598251881Speter 599251881Speter 600251881Speter/* Create an svn_fs_access_t in POOL for USER and associate it with 601251881Speter B's filesystem. Also, register a cleanup handler with POOL which 602251881Speter de-associates the svn_fs_access_t from B's filesystem. */ 603251881Speterstatic svn_error_t * 604251881Spetercreate_fs_access(server_baton_t *b, apr_pool_t *pool) 605251881Speter{ 606251881Speter svn_fs_access_t *fs_access; 607251881Speter struct cleanup_fs_access_baton *cleanup_baton; 608251881Speter 609299742Sdim if (!b->client_info->user) 610251881Speter return SVN_NO_ERROR; 611251881Speter 612299742Sdim SVN_ERR(svn_fs_create_access(&fs_access, b->client_info->user, pool)); 613299742Sdim SVN_ERR(svn_fs_set_access(b->repository->fs, fs_access)); 614251881Speter 615251881Speter cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton)); 616251881Speter cleanup_baton->pool = pool; 617299742Sdim cleanup_baton->fs = b->repository->fs; 618251881Speter apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access, 619251881Speter apr_pool_cleanup_null); 620251881Speter 621251881Speter return SVN_NO_ERROR; 622251881Speter} 623251881Speter 624251881Speter/* Authenticate, once the client has chosen a mechanism and possibly 625251881Speter * sent an initial mechanism token. On success, set *success to true 626251881Speter * and b->user to the authenticated username (or NULL for anonymous). 627251881Speter * On authentication failure, report failure to the client and set 628251881Speter * *success to FALSE. On communications failure, return an error. 629251881Speter * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */ 630251881Speterstatic svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 631251881Speter const char *mech, const char *mecharg, 632251881Speter server_baton_t *b, enum access_type required, 633251881Speter svn_boolean_t needs_username, 634251881Speter svn_boolean_t *success) 635251881Speter{ 636251881Speter const char *user; 637251881Speter *success = FALSE; 638251881Speter 639299742Sdim if (b->repository->auth_access >= required 640299742Sdim && b->client_info->tunnel_user && strcmp(mech, "EXTERNAL") == 0) 641251881Speter { 642299742Sdim if (*mecharg && strcmp(mecharg, b->client_info->tunnel_user) != 0) 643251881Speter return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", 644251881Speter "Requested username does not match"); 645299742Sdim b->client_info->user = b->client_info->tunnel_user; 646251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); 647251881Speter *success = TRUE; 648251881Speter return SVN_NO_ERROR; 649251881Speter } 650251881Speter 651299742Sdim if (b->repository->anon_access >= required 652251881Speter && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) 653251881Speter { 654251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); 655251881Speter *success = TRUE; 656251881Speter return SVN_NO_ERROR; 657251881Speter } 658251881Speter 659299742Sdim if (b->repository->auth_access >= required 660299742Sdim && b->repository->pwdb && strcmp(mech, "CRAM-MD5") == 0) 661251881Speter { 662299742Sdim SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->repository->pwdb, 663299742Sdim &user, success)); 664299742Sdim b->client_info->user = apr_pstrdup(b->pool, user); 665251881Speter return SVN_NO_ERROR; 666251881Speter } 667251881Speter 668251881Speter return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", 669251881Speter "Must authenticate with listed mechanism"); 670251881Speter} 671251881Speter 672251881Speter/* Perform an authentication request using the built-in SASL implementation. */ 673251881Speterstatic svn_error_t * 674251881Speterinternal_auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 675251881Speter server_baton_t *b, enum access_type required, 676251881Speter svn_boolean_t needs_username) 677251881Speter{ 678251881Speter svn_boolean_t success; 679251881Speter const char *mech, *mecharg; 680251881Speter 681251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 682251881Speter SVN_ERR(send_mechs(conn, pool, b, required, needs_username)); 683299742Sdim SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->repository->realm)); 684251881Speter do 685251881Speter { 686251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "w(?c)", &mech, &mecharg)); 687251881Speter if (!*mech) 688251881Speter break; 689251881Speter SVN_ERR(auth(conn, pool, mech, mecharg, b, required, needs_username, 690251881Speter &success)); 691251881Speter } 692251881Speter while (!success); 693251881Speter return SVN_NO_ERROR; 694251881Speter} 695251881Speter 696251881Speter/* Perform an authentication request in order to get an access level of 697251881Speter * REQUIRED or higher. Since the client may escape the authentication 698251881Speter * exchange, the caller should check current_access(b) to see if 699251881Speter * authentication succeeded. */ 700251881Speterstatic svn_error_t *auth_request(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 701251881Speter server_baton_t *b, enum access_type required, 702251881Speter svn_boolean_t needs_username) 703251881Speter{ 704251881Speter#ifdef SVN_HAVE_SASL 705299742Sdim if (b->repository->use_sasl) 706251881Speter return cyrus_auth_request(conn, pool, b, required, needs_username); 707251881Speter#endif 708251881Speter 709251881Speter return internal_auth_request(conn, pool, b, required, needs_username); 710251881Speter} 711251881Speter 712251881Speter/* Send a trivial auth notification on CONN which lists no mechanisms, 713251881Speter * indicating that authentication is unnecessary. Usually called in 714251881Speter * response to invocation of a svnserve command. 715251881Speter */ 716251881Speterstatic svn_error_t *trivial_auth_request(svn_ra_svn_conn_t *conn, 717251881Speter apr_pool_t *pool, server_baton_t *b) 718251881Speter{ 719251881Speter return svn_ra_svn__write_cmd_response(conn, pool, "()c", ""); 720251881Speter} 721251881Speter 722251881Speter/* Ensure that the client has the REQUIRED access by checking the 723251881Speter * access directives (both blanket and per-directory) in BATON. If 724251881Speter * PATH is NULL, then only the blanket access configuration will 725251881Speter * impact the result. 726251881Speter * 727251881Speter * If NEEDS_USERNAME is TRUE, then a lookup is only successful if the 728251881Speter * user described in BATON is authenticated and, well, has a username 729251881Speter * assigned to him. 730251881Speter * 731251881Speter * Use POOL for temporary allocations only. 732251881Speter */ 733251881Speterstatic svn_boolean_t lookup_access(apr_pool_t *pool, 734251881Speter server_baton_t *baton, 735251881Speter svn_repos_authz_access_t required, 736251881Speter const char *path, 737251881Speter svn_boolean_t needs_username) 738251881Speter{ 739251881Speter enum access_type req = (required & svn_authz_write) ? 740251881Speter WRITE_ACCESS : READ_ACCESS; 741251881Speter svn_boolean_t authorized; 742251881Speter svn_error_t *err; 743251881Speter 744251881Speter /* Get authz's opinion on the access. */ 745299742Sdim err = authz_check_access(&authorized, path, required, baton, pool); 746251881Speter 747251881Speter /* If an error made lookup fail, deny access. */ 748251881Speter if (err) 749251881Speter { 750299742Sdim log_error(err, baton); 751251881Speter svn_error_clear(err); 752251881Speter return FALSE; 753251881Speter } 754251881Speter 755251881Speter /* If the required access is blanket-granted AND granted by authz 756251881Speter AND we already have a username if one is required, then the 757251881Speter lookup has succeeded. */ 758251881Speter if (current_access(baton) >= req 759251881Speter && authorized 760299742Sdim && (! needs_username || baton->client_info->user)) 761251881Speter return TRUE; 762251881Speter 763251881Speter return FALSE; 764251881Speter} 765251881Speter 766251881Speter/* Check that the client has the REQUIRED access by consulting the 767251881Speter * authentication and authorization states stored in BATON. If the 768251881Speter * client does not have the required access credentials, attempt to 769251881Speter * authenticate the client to get that access, using CONN for 770251881Speter * communication. 771251881Speter * 772251881Speter * This function is supposed to be called to handle the authentication 773251881Speter * half of a standard svn protocol reply. If an error is returned, it 774251881Speter * probably means that the server can terminate the client connection 775251881Speter * with an apologetic error, as it implies an authentication failure. 776251881Speter * 777251881Speter * PATH and NEEDS_USERNAME are passed along to lookup_access, their 778251881Speter * behaviour is documented there. 779251881Speter */ 780251881Speterstatic svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, 781251881Speter apr_pool_t *pool, 782251881Speter server_baton_t *b, 783251881Speter svn_repos_authz_access_t required, 784251881Speter const char *path, 785251881Speter svn_boolean_t needs_username) 786251881Speter{ 787251881Speter enum access_type req = (required & svn_authz_write) ? 788251881Speter WRITE_ACCESS : READ_ACCESS; 789251881Speter 790251881Speter /* See whether the user already has the required access. If so, 791251881Speter nothing needs to be done. Create the FS access and send a 792251881Speter trivial auth request. */ 793299742Sdim if (lookup_access(pool, b, required, path, needs_username)) 794251881Speter { 795251881Speter SVN_ERR(create_fs_access(b, pool)); 796251881Speter return trivial_auth_request(conn, pool, b); 797251881Speter } 798251881Speter 799251881Speter /* If the required blanket access can be obtained by authenticating, 800251881Speter try that. Unfortunately, we can't tell until after 801251881Speter authentication whether authz will work or not. We force 802251881Speter requiring a username because we need one to be able to check 803251881Speter authz configuration again with a different user credentials than 804251881Speter the first time round. */ 805299742Sdim if (b->client_info->user == NULL 806299742Sdim && b->repository->auth_access >= req 807299742Sdim && (b->client_info->tunnel_user || b->repository->pwdb 808299742Sdim || b->repository->use_sasl)) 809251881Speter SVN_ERR(auth_request(conn, pool, b, req, TRUE)); 810251881Speter 811251881Speter /* Now that an authentication has been done get the new take of 812251881Speter authz on the request. */ 813299742Sdim if (! lookup_access(pool, b, required, path, needs_username)) 814251881Speter return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, 815251881Speter error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 816299742Sdim NULL, NULL, b), 817251881Speter NULL); 818251881Speter 819251881Speter /* Else, access is granted, and there is much rejoicing. */ 820251881Speter SVN_ERR(create_fs_access(b, pool)); 821251881Speter 822251881Speter return SVN_NO_ERROR; 823251881Speter} 824251881Speter 825251881Speter/* --- REPORTER COMMAND SET --- */ 826251881Speter 827251881Speter/* To allow for pipelining, reporter commands have no reponses. If we 828251881Speter * get an error, we ignore all subsequent reporter commands and return 829251881Speter * the error finish_report, to be handled by the calling command. 830251881Speter */ 831251881Speter 832251881Speterstatic svn_error_t *set_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 833251881Speter apr_array_header_t *params, void *baton) 834251881Speter{ 835251881Speter report_driver_baton_t *b = baton; 836251881Speter const char *path, *lock_token, *depth_word; 837251881Speter svn_revnum_t rev; 838251881Speter /* Default to infinity, for old clients that don't send depth. */ 839251881Speter svn_depth_t depth = svn_depth_infinity; 840251881Speter svn_boolean_t start_empty; 841251881Speter 842251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crb?(?c)?w", 843251881Speter &path, &rev, &start_empty, &lock_token, 844251881Speter &depth_word)); 845251881Speter if (depth_word) 846251881Speter depth = svn_depth_from_word(depth_word); 847251881Speter path = svn_relpath_canonicalize(path, pool); 848251881Speter if (b->from_rev && strcmp(path, "") == 0) 849251881Speter *b->from_rev = rev; 850251881Speter if (!b->err) 851251881Speter b->err = svn_repos_set_path3(b->report_baton, path, rev, depth, 852251881Speter start_empty, lock_token, pool); 853251881Speter b->entry_counter++; 854251881Speter if (!start_empty) 855251881Speter b->only_empty_entries = FALSE; 856251881Speter return SVN_NO_ERROR; 857251881Speter} 858251881Speter 859251881Speterstatic svn_error_t *delete_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 860251881Speter apr_array_header_t *params, void *baton) 861251881Speter{ 862251881Speter report_driver_baton_t *b = baton; 863251881Speter const char *path; 864251881Speter 865251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); 866251881Speter path = svn_relpath_canonicalize(path, pool); 867251881Speter if (!b->err) 868251881Speter b->err = svn_repos_delete_path(b->report_baton, path, pool); 869251881Speter return SVN_NO_ERROR; 870251881Speter} 871251881Speter 872251881Speterstatic svn_error_t *link_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 873251881Speter apr_array_header_t *params, void *baton) 874251881Speter{ 875251881Speter report_driver_baton_t *b = baton; 876251881Speter const char *path, *url, *lock_token, *fs_path, *depth_word; 877251881Speter svn_revnum_t rev; 878251881Speter svn_boolean_t start_empty; 879251881Speter /* Default to infinity, for old clients that don't send depth. */ 880251881Speter svn_depth_t depth = svn_depth_infinity; 881251881Speter 882251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "ccrb?(?c)?w", 883251881Speter &path, &url, &rev, &start_empty, 884251881Speter &lock_token, &depth_word)); 885251881Speter 886251881Speter /* ### WHAT?! The link path is an absolute URL?! Didn't see that 887251881Speter coming... -- cmpilato */ 888251881Speter path = svn_relpath_canonicalize(path, pool); 889251881Speter url = svn_uri_canonicalize(url, pool); 890251881Speter if (depth_word) 891251881Speter depth = svn_depth_from_word(depth_word); 892251881Speter if (!b->err) 893251881Speter b->err = get_fs_path(svn_path_uri_decode(b->repos_url, pool), 894251881Speter svn_path_uri_decode(url, pool), 895251881Speter &fs_path); 896251881Speter if (!b->err) 897251881Speter b->err = svn_repos_link_path3(b->report_baton, path, fs_path, rev, 898251881Speter depth, start_empty, lock_token, pool); 899251881Speter b->entry_counter++; 900251881Speter return SVN_NO_ERROR; 901251881Speter} 902251881Speter 903251881Speterstatic svn_error_t *finish_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 904251881Speter apr_array_header_t *params, void *baton) 905251881Speter{ 906251881Speter report_driver_baton_t *b = baton; 907251881Speter 908251881Speter /* No arguments to parse. */ 909251881Speter SVN_ERR(trivial_auth_request(conn, pool, b->sb)); 910251881Speter if (!b->err) 911251881Speter b->err = svn_repos_finish_report(b->report_baton, pool); 912251881Speter return SVN_NO_ERROR; 913251881Speter} 914251881Speter 915251881Speterstatic svn_error_t *abort_report(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 916251881Speter apr_array_header_t *params, void *baton) 917251881Speter{ 918251881Speter report_driver_baton_t *b = baton; 919251881Speter 920251881Speter /* No arguments to parse. */ 921251881Speter svn_error_clear(svn_repos_abort_report(b->report_baton, pool)); 922251881Speter return SVN_NO_ERROR; 923251881Speter} 924251881Speter 925251881Speterstatic const svn_ra_svn_cmd_entry_t report_commands[] = { 926251881Speter { "set-path", set_path }, 927251881Speter { "delete-path", delete_path }, 928251881Speter { "link-path", link_path }, 929251881Speter { "finish-report", finish_report, TRUE }, 930251881Speter { "abort-report", abort_report, TRUE }, 931251881Speter { NULL } 932251881Speter}; 933251881Speter 934251881Speter/* Accept a report from the client, drive the network editor with the 935251881Speter * result, and then write an empty command response. If there is a 936251881Speter * non-protocol failure, accept_report will abort the edit and return 937251881Speter * a command error to be reported by handle_commands(). 938251881Speter * 939251881Speter * If only_empty_entry is not NULL and the report contains only one 940251881Speter * item, and that item is empty, set *only_empty_entry to TRUE, else 941251881Speter * set it to FALSE. 942251881Speter * 943251881Speter * If from_rev is not NULL, set *from_rev to the revision number from 944251881Speter * the set-path on ""; if somehow set-path "" never happens, set 945251881Speter * *from_rev to SVN_INVALID_REVNUM. 946251881Speter */ 947251881Speterstatic svn_error_t *accept_report(svn_boolean_t *only_empty_entry, 948251881Speter svn_revnum_t *from_rev, 949251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool, 950251881Speter server_baton_t *b, svn_revnum_t rev, 951251881Speter const char *target, const char *tgt_path, 952251881Speter svn_boolean_t text_deltas, 953251881Speter svn_depth_t depth, 954251881Speter svn_boolean_t send_copyfrom_args, 955251881Speter svn_boolean_t ignore_ancestry) 956251881Speter{ 957251881Speter const svn_delta_editor_t *editor; 958251881Speter void *edit_baton, *report_baton; 959251881Speter report_driver_baton_t rb; 960251881Speter svn_error_t *err; 961251881Speter authz_baton_t ab; 962251881Speter 963251881Speter ab.server = b; 964251881Speter ab.conn = conn; 965251881Speter 966251881Speter /* Make an svn_repos report baton. Tell it to drive the network editor 967251881Speter * when the report is complete. */ 968251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 969299742Sdim SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, 970299742Sdim b->repository->repos, 971299742Sdim b->repository->fs_path->data, target, 972299742Sdim tgt_path, text_deltas, depth, 973299742Sdim ignore_ancestry, send_copyfrom_args, 974251881Speter editor, edit_baton, 975251881Speter authz_check_access_cb_func(b), 976251881Speter &ab, svn_ra_svn_zero_copy_limit(conn), 977251881Speter pool)); 978251881Speter 979251881Speter rb.sb = b; 980299742Sdim rb.repos_url = svn_path_uri_decode(b->repository->repos_url, pool); 981251881Speter rb.report_baton = report_baton; 982251881Speter rb.err = NULL; 983251881Speter rb.entry_counter = 0; 984251881Speter rb.only_empty_entries = TRUE; 985251881Speter rb.from_rev = from_rev; 986251881Speter if (from_rev) 987251881Speter *from_rev = SVN_INVALID_REVNUM; 988251881Speter err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE); 989251881Speter if (err) 990251881Speter { 991251881Speter /* Network or protocol error while handling commands. */ 992251881Speter svn_error_clear(rb.err); 993251881Speter return err; 994251881Speter } 995251881Speter else if (rb.err) 996251881Speter { 997251881Speter /* Some failure during the reporting or editing operations. */ 998251881Speter SVN_CMD_ERR(rb.err); 999251881Speter } 1000251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1001251881Speter 1002251881Speter if (only_empty_entry) 1003251881Speter *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries; 1004251881Speter 1005251881Speter return SVN_NO_ERROR; 1006251881Speter} 1007251881Speter 1008251881Speter/* --- MAIN COMMAND SET --- */ 1009251881Speter 1010251881Speter/* Write out a list of property diffs. PROPDIFFS is an array of svn_prop_t 1011251881Speter * values. */ 1012251881Speterstatic svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn, 1013251881Speter apr_pool_t *pool, 1014251881Speter const apr_array_header_t *propdiffs) 1015251881Speter{ 1016251881Speter int i; 1017251881Speter 1018251881Speter for (i = 0; i < propdiffs->nelts; ++i) 1019251881Speter { 1020251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); 1021251881Speter 1022251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)", 1023251881Speter prop->name, prop->value)); 1024251881Speter } 1025251881Speter 1026251881Speter return SVN_NO_ERROR; 1027251881Speter} 1028251881Speter 1029251881Speter/* Write out a lock to the client. */ 1030251881Speterstatic svn_error_t *write_lock(svn_ra_svn_conn_t *conn, 1031251881Speter apr_pool_t *pool, 1032299742Sdim const svn_lock_t *lock) 1033251881Speter{ 1034251881Speter const char *cdate, *edate; 1035251881Speter 1036251881Speter cdate = svn_time_to_cstring(lock->creation_date, pool); 1037251881Speter edate = lock->expiration_date 1038251881Speter ? svn_time_to_cstring(lock->expiration_date, pool) : NULL; 1039251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path, 1040251881Speter lock->token, lock->owner, lock->comment, 1041251881Speter cdate, edate)); 1042251881Speter 1043251881Speter return SVN_NO_ERROR; 1044251881Speter} 1045251881Speter 1046251881Speter/* ### This really belongs in libsvn_repos. */ 1047251881Speter/* Get the explicit properties and/or inherited properties for a PATH in 1048251881Speter ROOT, with hardcoded committed-info values. */ 1049251881Speterstatic svn_error_t * 1050251881Speterget_props(apr_hash_t **props, 1051251881Speter apr_array_header_t **iprops, 1052251881Speter authz_baton_t *b, 1053251881Speter svn_fs_root_t *root, 1054251881Speter const char *path, 1055251881Speter apr_pool_t *pool) 1056251881Speter{ 1057251881Speter /* Get the explicit properties. */ 1058251881Speter if (props) 1059251881Speter { 1060251881Speter svn_string_t *str; 1061251881Speter svn_revnum_t crev; 1062251881Speter const char *cdate, *cauthor, *uuid; 1063251881Speter 1064251881Speter SVN_ERR(svn_fs_node_proplist(props, root, path, pool)); 1065251881Speter 1066251881Speter /* Hardcode the values for the committed revision, date, and author. */ 1067251881Speter SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root, 1068251881Speter path, pool)); 1069299742Sdim str = svn_string_createf(pool, "%ld", crev); 1070251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_REV, str); 1071251881Speter str = (cdate) ? svn_string_create(cdate, pool) : NULL; 1072251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_COMMITTED_DATE, str); 1073251881Speter str = (cauthor) ? svn_string_create(cauthor, pool) : NULL; 1074251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_LAST_AUTHOR, str); 1075251881Speter 1076251881Speter /* Hardcode the values for the UUID. */ 1077251881Speter SVN_ERR(svn_fs_get_uuid(svn_fs_root_fs(root), &uuid, pool)); 1078251881Speter str = (uuid) ? svn_string_create(uuid, pool) : NULL; 1079251881Speter svn_hash_sets(*props, SVN_PROP_ENTRY_UUID, str); 1080251881Speter } 1081251881Speter 1082251881Speter /* Get any inherited properties the user is authorized to. */ 1083251881Speter if (iprops) 1084251881Speter { 1085251881Speter SVN_ERR(svn_repos_fs_get_inherited_props( 1086251881Speter iprops, root, path, NULL, 1087251881Speter authz_check_access_cb_func(b->server), 1088251881Speter b, pool, pool)); 1089251881Speter } 1090251881Speter 1091251881Speter return SVN_NO_ERROR; 1092251881Speter} 1093251881Speter 1094251881Speter/* Set BATON->FS_PATH for the repository URL found in PARAMS. */ 1095251881Speterstatic svn_error_t *reparent(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1096251881Speter apr_array_header_t *params, void *baton) 1097251881Speter{ 1098251881Speter server_baton_t *b = baton; 1099251881Speter const char *url; 1100251881Speter const char *fs_path; 1101251881Speter 1102251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &url)); 1103251881Speter url = svn_uri_canonicalize(url, pool); 1104251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1105299742Sdim SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, pool), 1106251881Speter svn_path_uri_decode(url, pool), 1107251881Speter &fs_path)); 1108251881Speter SVN_ERR(log_command(b, conn, pool, "%s", svn_log__reparent(fs_path, pool))); 1109299742Sdim svn_stringbuf_set(b->repository->fs_path, fs_path); 1110251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1111251881Speter return SVN_NO_ERROR; 1112251881Speter} 1113251881Speter 1114251881Speterstatic svn_error_t *get_latest_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1115251881Speter apr_array_header_t *params, void *baton) 1116251881Speter{ 1117251881Speter server_baton_t *b = baton; 1118251881Speter svn_revnum_t rev; 1119251881Speter 1120251881Speter SVN_ERR(log_command(b, conn, pool, "get-latest-rev")); 1121251881Speter 1122251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1123299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1124251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); 1125251881Speter return SVN_NO_ERROR; 1126251881Speter} 1127251881Speter 1128251881Speterstatic svn_error_t *get_dated_rev(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1129251881Speter apr_array_header_t *params, void *baton) 1130251881Speter{ 1131251881Speter server_baton_t *b = baton; 1132251881Speter svn_revnum_t rev; 1133251881Speter apr_time_t tm; 1134251881Speter const char *timestr; 1135251881Speter 1136251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", ×tr)); 1137251881Speter SVN_ERR(log_command(b, conn, pool, "get-dated-rev %s", timestr)); 1138251881Speter 1139251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1140251881Speter SVN_CMD_ERR(svn_time_from_cstring(&tm, timestr, pool)); 1141299742Sdim SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->repository->repos, tm, pool)); 1142251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", rev)); 1143251881Speter return SVN_NO_ERROR; 1144251881Speter} 1145251881Speter 1146251881Speter/* Common logic for change_rev_prop() and change_rev_prop2(). */ 1147251881Speterstatic svn_error_t *do_change_rev_prop(svn_ra_svn_conn_t *conn, 1148251881Speter server_baton_t *b, 1149251881Speter svn_revnum_t rev, 1150251881Speter const char *name, 1151251881Speter const svn_string_t *const *old_value_p, 1152251881Speter const svn_string_t *value, 1153251881Speter apr_pool_t *pool) 1154251881Speter{ 1155251881Speter authz_baton_t ab; 1156251881Speter 1157251881Speter ab.server = b; 1158251881Speter ab.conn = conn; 1159251881Speter 1160251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, FALSE)); 1161251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1162251881Speter svn_log__change_rev_prop(rev, name, pool))); 1163299742Sdim SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repository->repos, rev, 1164299742Sdim b->client_info->user, 1165251881Speter name, old_value_p, value, 1166251881Speter TRUE, TRUE, 1167251881Speter authz_check_access_cb_func(b), &ab, 1168251881Speter pool)); 1169251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1170251881Speter 1171251881Speter return SVN_NO_ERROR; 1172251881Speter} 1173251881Speter 1174251881Speterstatic svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1175251881Speter apr_array_header_t *params, void *baton) 1176251881Speter{ 1177251881Speter server_baton_t *b = baton; 1178251881Speter svn_revnum_t rev; 1179251881Speter const char *name; 1180251881Speter svn_string_t *value; 1181251881Speter const svn_string_t *const *old_value_p; 1182251881Speter svn_string_t *old_value; 1183251881Speter svn_boolean_t dont_care; 1184251881Speter 1185251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)", 1186251881Speter &rev, &name, &value, 1187251881Speter &dont_care, &old_value)); 1188251881Speter 1189251881Speter /* Argument parsing. */ 1190251881Speter if (dont_care) 1191251881Speter old_value_p = NULL; 1192251881Speter else 1193251881Speter old_value_p = (const svn_string_t *const *)&old_value; 1194251881Speter 1195251881Speter /* Input validation. */ 1196251881Speter if (dont_care && old_value) 1197251881Speter { 1198251881Speter svn_error_t *err; 1199251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1200251881Speter "'previous-value' and 'dont-care' cannot both be " 1201251881Speter "set in 'change-rev-prop2' request"); 1202251881Speter return log_fail_and_flush(err, b, conn, pool); 1203251881Speter } 1204251881Speter 1205251881Speter /* Do it. */ 1206251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool)); 1207251881Speter 1208251881Speter return SVN_NO_ERROR; 1209251881Speter} 1210251881Speter 1211251881Speterstatic svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1212251881Speter apr_array_header_t *params, void *baton) 1213251881Speter{ 1214251881Speter server_baton_t *b = baton; 1215251881Speter svn_revnum_t rev; 1216251881Speter const char *name; 1217251881Speter svn_string_t *value; 1218251881Speter 1219251881Speter /* Because the revprop value was at one time mandatory, the usual 1220251881Speter optional element pattern "(?s)" isn't used. */ 1221251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value)); 1222251881Speter 1223251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool)); 1224251881Speter 1225251881Speter return SVN_NO_ERROR; 1226251881Speter} 1227251881Speter 1228251881Speterstatic svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1229251881Speter apr_array_header_t *params, void *baton) 1230251881Speter{ 1231251881Speter server_baton_t *b = baton; 1232251881Speter svn_revnum_t rev; 1233251881Speter apr_hash_t *props; 1234251881Speter authz_baton_t ab; 1235251881Speter 1236251881Speter ab.server = b; 1237251881Speter ab.conn = conn; 1238251881Speter 1239251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); 1240251881Speter SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool))); 1241251881Speter 1242251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1243299742Sdim SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repository->repos, 1244299742Sdim rev, 1245299742Sdim authz_check_access_cb_func(b), 1246299742Sdim &ab, pool)); 1247251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 1248251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1249251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1250251881Speter return SVN_NO_ERROR; 1251251881Speter} 1252251881Speter 1253251881Speterstatic svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1254251881Speter apr_array_header_t *params, void *baton) 1255251881Speter{ 1256251881Speter server_baton_t *b = baton; 1257251881Speter svn_revnum_t rev; 1258251881Speter const char *name; 1259251881Speter svn_string_t *value; 1260251881Speter authz_baton_t ab; 1261251881Speter 1262251881Speter ab.server = b; 1263251881Speter ab.conn = conn; 1264251881Speter 1265251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name)); 1266251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1267251881Speter svn_log__rev_prop(rev, name, pool))); 1268251881Speter 1269251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1270299742Sdim SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repository->repos, rev, 1271299742Sdim name, authz_check_access_cb_func(b), 1272299742Sdim &ab, pool)); 1273251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value)); 1274251881Speter return SVN_NO_ERROR; 1275251881Speter} 1276251881Speter 1277251881Speterstatic svn_error_t *commit_done(const svn_commit_info_t *commit_info, 1278251881Speter void *baton, apr_pool_t *pool) 1279251881Speter{ 1280251881Speter commit_callback_baton_t *ccb = baton; 1281251881Speter 1282251881Speter *ccb->new_rev = commit_info->revision; 1283251881Speter *ccb->date = commit_info->date 1284251881Speter ? apr_pstrdup(ccb->pool, commit_info->date): NULL; 1285251881Speter *ccb->author = commit_info->author 1286251881Speter ? apr_pstrdup(ccb->pool, commit_info->author) : NULL; 1287251881Speter *ccb->post_commit_err = commit_info->post_commit_err 1288251881Speter ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL; 1289251881Speter return SVN_NO_ERROR; 1290251881Speter} 1291251881Speter 1292251881Speter/* Add the LOCK_TOKENS (if any) to the filesystem access context, 1293251881Speter * checking path authorizations using the state in SB as we go. 1294251881Speter * LOCK_TOKENS is an array of svn_ra_svn_item_t structs. Return a 1295251881Speter * client error if LOCK_TOKENS is not a list of lists. If a lock 1296251881Speter * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED 1297251881Speter * to the client. Use POOL for temporary allocations only. 1298251881Speter */ 1299299742Sdimstatic svn_error_t *add_lock_tokens(const apr_array_header_t *lock_tokens, 1300251881Speter server_baton_t *sb, 1301251881Speter apr_pool_t *pool) 1302251881Speter{ 1303251881Speter int i; 1304251881Speter svn_fs_access_t *fs_access; 1305251881Speter 1306299742Sdim SVN_ERR(svn_fs_get_access(&fs_access, sb->repository->fs)); 1307251881Speter 1308251881Speter /* If there is no access context, nowhere to add the tokens. */ 1309251881Speter if (! fs_access) 1310251881Speter return SVN_NO_ERROR; 1311251881Speter 1312251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1313251881Speter { 1314251881Speter const char *path, *token, *full_path; 1315251881Speter svn_ra_svn_item_t *path_item, *token_item; 1316251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i, 1317251881Speter svn_ra_svn_item_t); 1318251881Speter if (item->kind != SVN_RA_SVN_LIST) 1319251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1320251881Speter "Lock tokens aren't a list of lists"); 1321251881Speter 1322251881Speter path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); 1323251881Speter if (path_item->kind != SVN_RA_SVN_STRING) 1324251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1325251881Speter "Lock path isn't a string"); 1326251881Speter 1327251881Speter token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); 1328251881Speter if (token_item->kind != SVN_RA_SVN_STRING) 1329251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1330251881Speter "Lock token isn't a string"); 1331251881Speter 1332251881Speter path = path_item->u.string->data; 1333299742Sdim full_path = svn_fspath__join(sb->repository->fs_path->data, 1334251881Speter svn_relpath_canonicalize(path, pool), 1335251881Speter pool); 1336251881Speter 1337299742Sdim if (! lookup_access(pool, sb, svn_authz_write, full_path, TRUE)) 1338251881Speter return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, 1339299742Sdim sb); 1340251881Speter 1341251881Speter token = token_item->u.string->data; 1342251881Speter SVN_ERR(svn_fs_access_add_lock_token2(fs_access, path, token)); 1343251881Speter } 1344251881Speter 1345251881Speter return SVN_NO_ERROR; 1346251881Speter} 1347251881Speter 1348299742Sdim/* Implements svn_fs_lock_callback_t. */ 1349299742Sdimstatic svn_error_t * 1350299742Sdimlock_cb(void *baton, 1351299742Sdim const char *path, 1352299742Sdim const svn_lock_t *lock, 1353299742Sdim svn_error_t *fs_err, 1354299742Sdim apr_pool_t *pool) 1355299742Sdim{ 1356299742Sdim server_baton_t *sb = baton; 1357299742Sdim 1358299742Sdim log_error(fs_err, sb); 1359299742Sdim 1360299742Sdim return SVN_NO_ERROR; 1361299742Sdim} 1362299742Sdim 1363251881Speter/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors. 1364251881Speter LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */ 1365251881Speterstatic svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens, 1366251881Speter server_baton_t *sb, 1367251881Speter apr_pool_t *pool) 1368251881Speter{ 1369251881Speter int i; 1370299742Sdim apr_pool_t *subpool = svn_pool_create(pool); 1371299742Sdim apr_hash_t *targets = apr_hash_make(subpool); 1372299742Sdim svn_error_t *err; 1373251881Speter 1374251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1375251881Speter { 1376251881Speter svn_ra_svn_item_t *item, *path_item, *token_item; 1377251881Speter const char *path, *token, *full_path; 1378251881Speter 1379251881Speter item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t); 1380251881Speter path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); 1381251881Speter token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); 1382251881Speter 1383251881Speter path = path_item->u.string->data; 1384299742Sdim full_path = svn_fspath__join(sb->repository->fs_path->data, 1385299742Sdim svn_relpath_canonicalize(path, subpool), 1386299742Sdim subpool); 1387251881Speter token = token_item->u.string->data; 1388299742Sdim svn_hash_sets(targets, full_path, token); 1389299742Sdim } 1390251881Speter 1391251881Speter 1392299742Sdim /* The lock may have become defunct after the commit, so ignore such 1393299742Sdim errors. */ 1394299742Sdim err = svn_repos_fs_unlock_many(sb->repository->repos, targets, FALSE, 1395299742Sdim lock_cb, sb, subpool, subpool); 1396299742Sdim log_error(err, sb); 1397299742Sdim svn_error_clear(err); 1398251881Speter 1399299742Sdim svn_pool_destroy(subpool); 1400251881Speter 1401251881Speter return SVN_NO_ERROR; 1402251881Speter} 1403251881Speter 1404251881Speterstatic svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1405251881Speter apr_array_header_t *params, void *baton) 1406251881Speter{ 1407251881Speter server_baton_t *b = baton; 1408299742Sdim const char *log_msg, 1409251881Speter *date = NULL, 1410251881Speter *author = NULL, 1411251881Speter *post_commit_err = NULL; 1412251881Speter apr_array_header_t *lock_tokens; 1413251881Speter svn_boolean_t keep_locks; 1414299742Sdim apr_array_header_t *revprop_list; 1415251881Speter apr_hash_t *revprop_table; 1416251881Speter const svn_delta_editor_t *editor; 1417251881Speter void *edit_baton; 1418251881Speter svn_boolean_t aborted; 1419251881Speter commit_callback_baton_t ccb; 1420251881Speter svn_revnum_t new_rev; 1421251881Speter authz_baton_t ab; 1422251881Speter 1423251881Speter ab.server = b; 1424251881Speter ab.conn = conn; 1425251881Speter 1426251881Speter if (params->nelts == 1) 1427251881Speter { 1428251881Speter /* Clients before 1.2 don't send lock-tokens, keep-locks, 1429251881Speter and rev-props fields. */ 1430251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg)); 1431251881Speter lock_tokens = NULL; 1432251881Speter keep_locks = TRUE; 1433251881Speter revprop_list = NULL; 1434251881Speter } 1435251881Speter else 1436251881Speter { 1437251881Speter /* Clients before 1.5 don't send the rev-props field. */ 1438251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg, 1439251881Speter &lock_tokens, &keep_locks, 1440251881Speter &revprop_list)); 1441251881Speter } 1442251881Speter 1443251881Speter /* The handling for locks is a little problematic, because the 1444251881Speter protocol won't let us send several auth requests once one has 1445251881Speter succeeded. So we request write access and a username before 1446251881Speter adding tokens (if we have any), and subsequently fail if a lock 1447251881Speter violates authz. */ 1448251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 1449251881Speter NULL, 1450251881Speter (lock_tokens && lock_tokens->nelts))); 1451251881Speter 1452251881Speter /* Authorize the lock tokens and give them to the FS if we got 1453251881Speter any. */ 1454251881Speter if (lock_tokens && lock_tokens->nelts) 1455299742Sdim SVN_CMD_ERR(add_lock_tokens(lock_tokens, b, pool)); 1456251881Speter 1457253734Speter /* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */ 1458251881Speter if (revprop_list) 1459251881Speter SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table)); 1460251881Speter else 1461251881Speter { 1462251881Speter revprop_table = apr_hash_make(pool); 1463251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, 1464251881Speter svn_string_create(log_msg, pool)); 1465251881Speter } 1466251881Speter 1467251881Speter /* Get author from the baton, making sure clients can't circumvent 1468251881Speter the authentication via the revision props. */ 1469251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 1470299742Sdim b->client_info->user 1471299742Sdim ? svn_string_create(b->client_info->user, pool) 1472299742Sdim : NULL); 1473251881Speter 1474251881Speter ccb.pool = pool; 1475251881Speter ccb.new_rev = &new_rev; 1476251881Speter ccb.date = &date; 1477251881Speter ccb.author = &author; 1478251881Speter ccb.post_commit_err = &post_commit_err; 1479251881Speter /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */ 1480251881Speter SVN_CMD_ERR(svn_repos_get_commit_editor5 1481299742Sdim (&editor, &edit_baton, b->repository->repos, NULL, 1482299742Sdim svn_path_uri_decode(b->repository->repos_url, pool), 1483299742Sdim b->repository->fs_path->data, revprop_table, 1484251881Speter commit_done, &ccb, 1485251881Speter authz_commit_cb, &ab, pool)); 1486251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1487251881Speter SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton, 1488251881Speter &aborted, FALSE)); 1489251881Speter if (!aborted) 1490251881Speter { 1491251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1492251881Speter svn_log__commit(new_rev, pool))); 1493251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1494251881Speter 1495251881Speter /* In tunnel mode, deltify before answering the client, because 1496251881Speter answering may cause the client to terminate the connection 1497251881Speter and thus kill the server. But otherwise, deltify after 1498251881Speter answering the client, to avoid user-visible delay. */ 1499251881Speter 1500299742Sdim if (b->client_info->tunnel) 1501299742Sdim SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool)); 1502251881Speter 1503251881Speter /* Unlock the paths. */ 1504251881Speter if (! keep_locks && lock_tokens && lock_tokens->nelts) 1505299742Sdim SVN_ERR(unlock_paths(lock_tokens, b, pool)); 1506251881Speter 1507251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)", 1508251881Speter new_rev, date, author, post_commit_err)); 1509251881Speter 1510299742Sdim if (! b->client_info->tunnel) 1511299742Sdim SVN_ERR(svn_fs_deltify_revision(b->repository->fs, new_rev, pool)); 1512251881Speter } 1513251881Speter return SVN_NO_ERROR; 1514251881Speter} 1515251881Speter 1516251881Speterstatic svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1517251881Speter apr_array_header_t *params, void *baton) 1518251881Speter{ 1519251881Speter server_baton_t *b = baton; 1520251881Speter const char *path, *full_path, *hex_digest; 1521251881Speter svn_revnum_t rev; 1522251881Speter svn_fs_root_t *root; 1523251881Speter svn_stream_t *contents; 1524251881Speter apr_hash_t *props = NULL; 1525251881Speter apr_array_header_t *inherited_props; 1526251881Speter svn_string_t write_str; 1527251881Speter char buf[4096]; 1528251881Speter apr_size_t len; 1529251881Speter svn_boolean_t want_props, want_contents; 1530251881Speter apr_uint64_t wants_inherited_props; 1531251881Speter svn_checksum_t *checksum; 1532251881Speter svn_error_t *err, *write_err; 1533251881Speter int i; 1534251881Speter authz_baton_t ab; 1535251881Speter 1536251881Speter ab.server = b; 1537251881Speter ab.conn = conn; 1538251881Speter 1539251881Speter /* Parse arguments. */ 1540251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev, 1541251881Speter &want_props, &want_contents, 1542251881Speter &wants_inherited_props)); 1543251881Speter 1544269847Speter if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1545269847Speter wants_inherited_props = FALSE; 1546269847Speter 1547299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 1548251881Speter svn_relpath_canonicalize(path, pool), pool); 1549251881Speter 1550251881Speter /* Check authorizations */ 1551251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1552251881Speter full_path, FALSE)); 1553251881Speter 1554251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1555299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1556251881Speter 1557251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1558251881Speter svn_log__get_file(full_path, rev, 1559251881Speter want_contents, want_props, pool))); 1560251881Speter 1561251881Speter /* Fetch the properties and a stream for the contents. */ 1562299742Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 1563251881Speter SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root, 1564251881Speter full_path, TRUE, pool)); 1565251881Speter hex_digest = svn_checksum_to_cstring_display(checksum, pool); 1566269847Speter 1567269847Speter /* Fetch the file's explicit and/or inherited properties if 1568269847Speter requested. Although the wants-iprops boolean was added to the 1569269847Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1570251881Speter if (want_props || wants_inherited_props) 1571269847Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1572269847Speter wants_inherited_props ? &inherited_props : NULL, 1573269847Speter &ab, root, full_path, 1574251881Speter pool)); 1575251881Speter if (want_contents) 1576251881Speter SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool)); 1577251881Speter 1578251881Speter /* Send successful command response with revision and props. */ 1579251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success", 1580251881Speter hex_digest, rev)); 1581251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1582251881Speter 1583251881Speter if (wants_inherited_props) 1584251881Speter { 1585251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1586251881Speter 1587251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1588251881Speter for (i = 0; i < inherited_props->nelts; i++) 1589251881Speter { 1590251881Speter svn_prop_inherited_item_t *iprop = 1591251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1592251881Speter 1593251881Speter svn_pool_clear(iterpool); 1594251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1595251881Speter iprop->path_or_url)); 1596251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1597251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1598251881Speter iprop->path_or_url)); 1599251881Speter } 1600251881Speter svn_pool_destroy(iterpool); 1601251881Speter } 1602251881Speter 1603251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1604251881Speter 1605251881Speter /* Now send the file's contents. */ 1606251881Speter if (want_contents) 1607251881Speter { 1608251881Speter err = SVN_NO_ERROR; 1609251881Speter while (1) 1610251881Speter { 1611251881Speter len = sizeof(buf); 1612299742Sdim err = svn_stream_read_full(contents, buf, &len); 1613251881Speter if (err) 1614251881Speter break; 1615251881Speter if (len > 0) 1616251881Speter { 1617251881Speter write_str.data = buf; 1618251881Speter write_str.len = len; 1619251881Speter SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str)); 1620251881Speter } 1621251881Speter if (len < sizeof(buf)) 1622251881Speter { 1623251881Speter err = svn_stream_close(contents); 1624251881Speter break; 1625251881Speter } 1626251881Speter } 1627251881Speter write_err = svn_ra_svn__write_cstring(conn, pool, ""); 1628251881Speter if (write_err) 1629251881Speter { 1630251881Speter svn_error_clear(err); 1631251881Speter return write_err; 1632251881Speter } 1633251881Speter SVN_CMD_ERR(err); 1634251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1635251881Speter } 1636251881Speter 1637251881Speter return SVN_NO_ERROR; 1638251881Speter} 1639251881Speter 1640251881Speterstatic svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1641251881Speter apr_array_header_t *params, void *baton) 1642251881Speter{ 1643251881Speter server_baton_t *b = baton; 1644251881Speter const char *path, *full_path; 1645251881Speter svn_revnum_t rev; 1646251881Speter apr_hash_t *entries, *props = NULL; 1647251881Speter apr_array_header_t *inherited_props; 1648251881Speter apr_hash_index_t *hi; 1649251881Speter svn_fs_root_t *root; 1650251881Speter apr_pool_t *subpool; 1651251881Speter svn_boolean_t want_props, want_contents; 1652251881Speter apr_uint64_t wants_inherited_props; 1653251881Speter apr_uint64_t dirent_fields; 1654251881Speter apr_array_header_t *dirent_fields_list = NULL; 1655251881Speter svn_ra_svn_item_t *elt; 1656251881Speter int i; 1657251881Speter authz_baton_t ab; 1658251881Speter 1659251881Speter ab.server = b; 1660251881Speter ab.conn = conn; 1661251881Speter 1662251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev, 1663251881Speter &want_props, &want_contents, 1664251881Speter &dirent_fields_list, 1665251881Speter &wants_inherited_props)); 1666251881Speter 1667269847Speter if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1668269847Speter wants_inherited_props = FALSE; 1669269847Speter 1670251881Speter if (! dirent_fields_list) 1671251881Speter { 1672251881Speter dirent_fields = SVN_DIRENT_ALL; 1673251881Speter } 1674251881Speter else 1675251881Speter { 1676251881Speter dirent_fields = 0; 1677251881Speter 1678251881Speter for (i = 0; i < dirent_fields_list->nelts; ++i) 1679251881Speter { 1680251881Speter elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t); 1681251881Speter 1682251881Speter if (elt->kind != SVN_RA_SVN_WORD) 1683251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1684251881Speter "Dirent field not a string"); 1685251881Speter 1686251881Speter if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0) 1687251881Speter dirent_fields |= SVN_DIRENT_KIND; 1688251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0) 1689251881Speter dirent_fields |= SVN_DIRENT_SIZE; 1690251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0) 1691251881Speter dirent_fields |= SVN_DIRENT_HAS_PROPS; 1692251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0) 1693251881Speter dirent_fields |= SVN_DIRENT_CREATED_REV; 1694251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0) 1695251881Speter dirent_fields |= SVN_DIRENT_TIME; 1696251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0) 1697251881Speter dirent_fields |= SVN_DIRENT_LAST_AUTHOR; 1698251881Speter } 1699251881Speter } 1700251881Speter 1701299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 1702251881Speter svn_relpath_canonicalize(path, pool), pool); 1703251881Speter 1704251881Speter /* Check authorizations */ 1705251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1706251881Speter full_path, FALSE)); 1707251881Speter 1708251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1709299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1710251881Speter 1711251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1712251881Speter svn_log__get_dir(full_path, rev, 1713251881Speter want_contents, want_props, 1714251881Speter dirent_fields, pool))); 1715251881Speter 1716251881Speter /* Fetch the root of the appropriate revision. */ 1717299742Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 1718251881Speter 1719269847Speter /* Fetch the directory's explicit and/or inherited properties if 1720269847Speter requested. Although the wants-iprops boolean was added to the 1721269847Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1722251881Speter if (want_props || wants_inherited_props) 1723269847Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1724269847Speter wants_inherited_props ? &inherited_props : NULL, 1725269847Speter &ab, root, full_path, 1726251881Speter pool)); 1727251881Speter 1728289166Speter /* Fetch the directories' entries before starting the response, to allow 1729289166Speter proper error handling in cases like when FULL_PATH doesn't exist */ 1730289166Speter if (want_contents) 1731289166Speter SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); 1732289166Speter 1733251881Speter /* Begin response ... */ 1734251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev)); 1735251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1736251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!")); 1737251881Speter 1738251881Speter /* Fetch the directory entries if requested and send them immediately. */ 1739251881Speter if (want_contents) 1740251881Speter { 1741251881Speter /* Use epoch for a placeholder for a missing date. */ 1742251881Speter const char *missing_date = svn_time_to_cstring(0, pool); 1743251881Speter 1744251881Speter /* Transform the hash table's FS entries into dirents. This probably 1745251881Speter * belongs in libsvn_repos. */ 1746251881Speter subpool = svn_pool_create(pool); 1747251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1748251881Speter { 1749299742Sdim const char *name = apr_hash_this_key(hi); 1750299742Sdim svn_fs_dirent_t *fsent = apr_hash_this_val(hi); 1751251881Speter const char *file_path; 1752251881Speter 1753251881Speter /* The fields in the entry tuple. */ 1754251881Speter svn_node_kind_t entry_kind = svn_node_none; 1755251881Speter svn_filesize_t entry_size = 0; 1756251881Speter svn_boolean_t has_props = FALSE; 1757251881Speter /* If 'created rev' was not requested, send 0. We can't use 1758251881Speter * SVN_INVALID_REVNUM as the tuple field is not optional. 1759251881Speter * See the email thread on dev@, 2012-03-28, subject 1760251881Speter * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra", 1761251881Speter * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */ 1762251881Speter svn_revnum_t created_rev = 0; 1763251881Speter const char *cdate = NULL; 1764251881Speter const char *last_author = NULL; 1765251881Speter 1766251881Speter svn_pool_clear(subpool); 1767251881Speter 1768251881Speter file_path = svn_fspath__join(full_path, name, subpool); 1769299742Sdim if (! lookup_access(subpool, b, svn_authz_read, file_path, FALSE)) 1770251881Speter continue; 1771251881Speter 1772251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1773251881Speter entry_kind = fsent->kind; 1774251881Speter 1775251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1776251881Speter if (entry_kind != svn_node_dir) 1777251881Speter SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, 1778251881Speter subpool)); 1779251881Speter 1780251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1781251881Speter { 1782251881Speter /* has_props */ 1783299742Sdim SVN_CMD_ERR(svn_fs_node_has_props(&has_props, root, file_path, 1784251881Speter subpool)); 1785251881Speter } 1786251881Speter 1787251881Speter if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1788251881Speter || (dirent_fields & SVN_DIRENT_TIME) 1789251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1790251881Speter { 1791251881Speter /* created_rev, last_author, time */ 1792251881Speter SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev, 1793251881Speter &cdate, 1794251881Speter &last_author, 1795251881Speter root, 1796251881Speter file_path, 1797251881Speter subpool)); 1798251881Speter } 1799251881Speter 1800251881Speter /* The client does not properly handle a missing CDATE. For 1801251881Speter interoperability purposes, we must fill in some junk. 1802251881Speter 1803251881Speter See libsvn_ra_svn/client.c:ra_svn_get_dir() */ 1804251881Speter if (cdate == NULL) 1805251881Speter cdate = missing_date; 1806251881Speter 1807251881Speter /* Send the entry. */ 1808251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name, 1809251881Speter svn_node_kind_to_word(entry_kind), 1810251881Speter (apr_uint64_t) entry_size, 1811251881Speter has_props, created_rev, 1812251881Speter cdate, last_author)); 1813251881Speter } 1814251881Speter svn_pool_destroy(subpool); 1815251881Speter } 1816251881Speter 1817251881Speter if (wants_inherited_props) 1818251881Speter { 1819251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1820251881Speter 1821251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1822251881Speter for (i = 0; i < inherited_props->nelts; i++) 1823251881Speter { 1824251881Speter svn_prop_inherited_item_t *iprop = 1825251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1826251881Speter 1827251881Speter svn_pool_clear(iterpool); 1828251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1829251881Speter iprop->path_or_url)); 1830251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1831251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1832251881Speter iprop->path_or_url)); 1833251881Speter } 1834251881Speter svn_pool_destroy(iterpool); 1835251881Speter } 1836251881Speter 1837251881Speter /* Finish response. */ 1838251881Speter return svn_ra_svn__write_tuple(conn, pool, "!))"); 1839251881Speter} 1840251881Speter 1841251881Speterstatic svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1842251881Speter apr_array_header_t *params, void *baton) 1843251881Speter{ 1844251881Speter server_baton_t *b = baton; 1845251881Speter svn_revnum_t rev; 1846251881Speter const char *target, *full_path, *depth_word; 1847251881Speter svn_boolean_t recurse; 1848299742Sdim svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ 1849299742Sdim svn_tristate_t ignore_ancestry; /* Optional; default FALSE */ 1850251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1851251881Speter handle that by converting recurse if necessary. */ 1852251881Speter svn_depth_t depth = svn_depth_unknown; 1853251881Speter svn_boolean_t is_checkout; 1854251881Speter 1855251881Speter /* Parse the arguments. */ 1856299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?w3?3", &rev, &target, 1857251881Speter &recurse, &depth_word, 1858251881Speter &send_copyfrom_args, &ignore_ancestry)); 1859251881Speter target = svn_relpath_canonicalize(target, pool); 1860251881Speter 1861251881Speter if (depth_word) 1862251881Speter depth = svn_depth_from_word(depth_word); 1863251881Speter else 1864251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1865251881Speter 1866299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, target, pool); 1867251881Speter /* Check authorization and authenticate the user if necessary. */ 1868251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE)); 1869251881Speter 1870251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1871299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1872251881Speter 1873251881Speter SVN_ERR(accept_report(&is_checkout, NULL, 1874251881Speter conn, pool, b, rev, target, NULL, TRUE, 1875251881Speter depth, 1876299742Sdim (send_copyfrom_args == svn_tristate_true), 1877299742Sdim (ignore_ancestry == svn_tristate_true))); 1878251881Speter if (is_checkout) 1879251881Speter { 1880251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1881251881Speter svn_log__checkout(full_path, rev, 1882251881Speter depth, pool))); 1883251881Speter } 1884251881Speter else 1885251881Speter { 1886251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1887251881Speter svn_log__update(full_path, rev, depth, 1888299742Sdim (send_copyfrom_args 1889299742Sdim == svn_tristate_true), 1890299742Sdim pool))); 1891251881Speter } 1892251881Speter 1893251881Speter return SVN_NO_ERROR; 1894251881Speter} 1895251881Speter 1896251881Speterstatic svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1897251881Speter apr_array_header_t *params, void *baton) 1898251881Speter{ 1899251881Speter server_baton_t *b = baton; 1900251881Speter svn_revnum_t rev; 1901251881Speter const char *target, *depth_word; 1902251881Speter const char *switch_url, *switch_path; 1903251881Speter svn_boolean_t recurse; 1904251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1905251881Speter handle that by converting recurse if necessary. */ 1906251881Speter svn_depth_t depth = svn_depth_unknown; 1907299742Sdim svn_tristate_t send_copyfrom_args; /* Optional; default FALSE */ 1908299742Sdim svn_tristate_t ignore_ancestry; /* Optional; default TRUE */ 1909251881Speter 1910251881Speter /* Parse the arguments. */ 1911299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?33", &rev, &target, 1912251881Speter &recurse, &switch_url, &depth_word, 1913251881Speter &send_copyfrom_args, &ignore_ancestry)); 1914251881Speter target = svn_relpath_canonicalize(target, pool); 1915251881Speter switch_url = svn_uri_canonicalize(switch_url, pool); 1916251881Speter 1917251881Speter if (depth_word) 1918251881Speter depth = svn_depth_from_word(depth_word); 1919251881Speter else 1920251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1921251881Speter 1922251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1923251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1924299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1925251881Speter 1926299742Sdim SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, 1927299742Sdim pool), 1928251881Speter svn_path_uri_decode(switch_url, pool), 1929251881Speter &switch_path)); 1930251881Speter 1931251881Speter { 1932299742Sdim const char *full_path = svn_fspath__join(b->repository->fs_path->data, 1933299742Sdim target, pool); 1934251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1935251881Speter svn_log__switch(full_path, switch_path, rev, 1936251881Speter depth, pool))); 1937251881Speter } 1938251881Speter 1939251881Speter return accept_report(NULL, NULL, 1940251881Speter conn, pool, b, rev, target, switch_path, TRUE, 1941251881Speter depth, 1942299742Sdim (send_copyfrom_args == svn_tristate_true), 1943299742Sdim (ignore_ancestry != svn_tristate_false)); 1944251881Speter} 1945251881Speter 1946251881Speterstatic svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1947251881Speter apr_array_header_t *params, void *baton) 1948251881Speter{ 1949251881Speter server_baton_t *b = baton; 1950251881Speter svn_revnum_t rev; 1951251881Speter const char *target, *depth_word; 1952251881Speter svn_boolean_t recurse; 1953251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1954251881Speter handle that by converting recurse if necessary. */ 1955251881Speter svn_depth_t depth = svn_depth_unknown; 1956251881Speter 1957251881Speter /* Parse the arguments. */ 1958251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w", 1959251881Speter &target, &recurse, &rev, &depth_word)); 1960251881Speter target = svn_relpath_canonicalize(target, pool); 1961251881Speter 1962251881Speter if (depth_word) 1963251881Speter depth = svn_depth_from_word(depth_word); 1964251881Speter else 1965251881Speter depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse); 1966251881Speter 1967251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1968251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1969299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 1970251881Speter 1971251881Speter { 1972299742Sdim const char *full_path = svn_fspath__join(b->repository->fs_path->data, 1973299742Sdim target, pool); 1974251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1975251881Speter svn_log__status(full_path, rev, depth, pool))); 1976251881Speter } 1977251881Speter 1978251881Speter return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE, 1979251881Speter depth, FALSE, FALSE); 1980251881Speter} 1981251881Speter 1982251881Speterstatic svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1983251881Speter apr_array_header_t *params, void *baton) 1984251881Speter{ 1985251881Speter server_baton_t *b = baton; 1986251881Speter svn_revnum_t rev; 1987251881Speter const char *target, *versus_url, *versus_path, *depth_word; 1988251881Speter svn_boolean_t recurse, ignore_ancestry; 1989251881Speter svn_boolean_t text_deltas; 1990251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1991251881Speter handle that by converting recurse if necessary. */ 1992251881Speter svn_depth_t depth = svn_depth_unknown; 1993251881Speter 1994251881Speter /* Parse the arguments. */ 1995251881Speter if (params->nelts == 5) 1996251881Speter { 1997251881Speter /* Clients before 1.4 don't send the text_deltas boolean or depth. */ 1998251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target, 1999251881Speter &recurse, &ignore_ancestry, &versus_url)); 2000251881Speter text_deltas = TRUE; 2001251881Speter depth_word = NULL; 2002251881Speter } 2003251881Speter else 2004251881Speter { 2005251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w", 2006251881Speter &rev, &target, &recurse, 2007251881Speter &ignore_ancestry, &versus_url, 2008251881Speter &text_deltas, &depth_word)); 2009251881Speter } 2010251881Speter target = svn_relpath_canonicalize(target, pool); 2011251881Speter versus_url = svn_uri_canonicalize(versus_url, pool); 2012251881Speter 2013251881Speter if (depth_word) 2014251881Speter depth = svn_depth_from_word(depth_word); 2015251881Speter else 2016251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 2017251881Speter 2018251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2019251881Speter 2020251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2021299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2022299742Sdim SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repository->repos_url, 2023299742Sdim pool), 2024251881Speter svn_path_uri_decode(versus_url, pool), 2025251881Speter &versus_path)); 2026251881Speter 2027251881Speter { 2028299742Sdim const char *full_path = svn_fspath__join(b->repository->fs_path->data, 2029299742Sdim target, pool); 2030251881Speter svn_revnum_t from_rev; 2031251881Speter SVN_ERR(accept_report(NULL, &from_rev, 2032251881Speter conn, pool, b, rev, target, versus_path, 2033251881Speter text_deltas, depth, FALSE, ignore_ancestry)); 2034251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2035251881Speter svn_log__diff(full_path, from_rev, versus_path, 2036251881Speter rev, depth, ignore_ancestry, 2037251881Speter pool))); 2038251881Speter } 2039251881Speter return SVN_NO_ERROR; 2040251881Speter} 2041251881Speter 2042251881Speter/* Regardless of whether a client's capabilities indicate an 2043251881Speter understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO), 2044251881Speter we provide a response. 2045251881Speter 2046251881Speter ASSUMPTION: When performing a 'merge' with two URLs at different 2047251881Speter revisions, the client will call this command more than once. */ 2048251881Speterstatic svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2049251881Speter apr_array_header_t *params, void *baton) 2050251881Speter{ 2051251881Speter server_baton_t *b = baton; 2052251881Speter svn_revnum_t rev; 2053251881Speter apr_array_header_t *paths, *canonical_paths; 2054251881Speter svn_mergeinfo_catalog_t mergeinfo; 2055251881Speter int i; 2056251881Speter apr_hash_index_t *hi; 2057251881Speter const char *inherit_word; 2058251881Speter svn_mergeinfo_inheritance_t inherit; 2059251881Speter svn_boolean_t include_descendants; 2060251881Speter apr_pool_t *iterpool; 2061251881Speter authz_baton_t ab; 2062251881Speter 2063251881Speter ab.server = b; 2064251881Speter ab.conn = conn; 2065251881Speter 2066251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev, 2067251881Speter &inherit_word, &include_descendants)); 2068251881Speter inherit = svn_inheritance_from_word(inherit_word); 2069251881Speter 2070251881Speter /* Canonicalize the paths which mergeinfo has been requested for. */ 2071251881Speter canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2072251881Speter for (i = 0; i < paths->nelts; i++) 2073251881Speter { 2074251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2075251881Speter const char *full_path; 2076251881Speter 2077251881Speter if (item->kind != SVN_RA_SVN_STRING) 2078251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2079251881Speter _("Path is not a string")); 2080251881Speter full_path = svn_relpath_canonicalize(item->u.string->data, pool); 2081299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, full_path, pool); 2082251881Speter APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; 2083251881Speter } 2084251881Speter 2085251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2086251881Speter svn_log__get_mergeinfo(canonical_paths, inherit, 2087251881Speter include_descendants, 2088251881Speter pool))); 2089251881Speter 2090251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2091299742Sdim SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repository->repos, 2092251881Speter canonical_paths, rev, 2093251881Speter inherit, 2094251881Speter include_descendants, 2095251881Speter authz_check_access_cb_func(b), &ab, 2096251881Speter pool)); 2097251881Speter SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo, 2098299742Sdim b->repository->fs_path->data, pool)); 2099251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2100251881Speter iterpool = svn_pool_create(pool); 2101251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2102251881Speter { 2103299742Sdim const char *key = apr_hash_this_key(hi); 2104299742Sdim svn_mergeinfo_t value = apr_hash_this_val(hi); 2105251881Speter svn_string_t *mergeinfo_string; 2106251881Speter 2107251881Speter svn_pool_clear(iterpool); 2108251881Speter 2109251881Speter SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool)); 2110251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key, 2111251881Speter mergeinfo_string)); 2112251881Speter } 2113251881Speter svn_pool_destroy(iterpool); 2114251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2115251881Speter 2116251881Speter return SVN_NO_ERROR; 2117251881Speter} 2118251881Speter 2119251881Speter/* Send a log entry to the client. */ 2120251881Speterstatic svn_error_t *log_receiver(void *baton, 2121251881Speter svn_log_entry_t *log_entry, 2122251881Speter apr_pool_t *pool) 2123251881Speter{ 2124251881Speter log_baton_t *b = baton; 2125251881Speter svn_ra_svn_conn_t *conn = b->conn; 2126251881Speter apr_hash_index_t *h; 2127251881Speter svn_boolean_t invalid_revnum = FALSE; 2128299742Sdim const svn_string_t *author, *date, *message; 2129299742Sdim unsigned revprop_count; 2130251881Speter 2131251881Speter if (log_entry->revision == SVN_INVALID_REVNUM) 2132251881Speter { 2133251881Speter /* If the stack depth is zero, we've seen the last revision, so don't 2134251881Speter send it, just return. */ 2135251881Speter if (b->stack_depth == 0) 2136251881Speter return SVN_NO_ERROR; 2137251881Speter 2138251881Speter /* Because the svn protocol won't let us send an invalid revnum, we have 2139251881Speter to fudge here and send an additional flag. */ 2140251881Speter log_entry->revision = 0; 2141251881Speter invalid_revnum = TRUE; 2142251881Speter b->stack_depth--; 2143251881Speter } 2144251881Speter 2145299742Sdim svn_compat_log_revprops_out_string(&author, &date, &message, 2146299742Sdim log_entry->revprops); 2147299742Sdim svn_compat_log_revprops_clear(log_entry->revprops); 2148299742Sdim if (log_entry->revprops) 2149299742Sdim revprop_count = apr_hash_count(log_entry->revprops); 2150299742Sdim else 2151299742Sdim revprop_count = 0; 2152299742Sdim 2153299742Sdim /* send LOG_ENTRY */ 2154299742Sdim SVN_ERR(svn_ra_svn__start_list(conn, pool)); 2155299742Sdim 2156299742Sdim /* send LOG_ENTRY->CHANGED_PATHS2 */ 2157299742Sdim SVN_ERR(svn_ra_svn__start_list(conn, pool)); 2158251881Speter if (log_entry->changed_paths2) 2159251881Speter { 2160251881Speter for (h = apr_hash_first(pool, log_entry->changed_paths2); h; 2161251881Speter h = apr_hash_next(h)) 2162251881Speter { 2163299742Sdim const char *path = apr_hash_this_key(h); 2164299742Sdim svn_log_changed_path2_t *change = apr_hash_this_val(h); 2165251881Speter 2166299742Sdim SVN_ERR(svn_ra_svn__write_data_log_changed_path( 2167299742Sdim conn, pool, 2168251881Speter path, 2169299742Sdim change->action, 2170251881Speter change->copyfrom_path, 2171251881Speter change->copyfrom_rev, 2172299742Sdim change->node_kind, 2173251881Speter /* text_modified and props_modified are never unknown */ 2174251881Speter change->text_modified == svn_tristate_true, 2175251881Speter change->props_modified == svn_tristate_true)); 2176251881Speter } 2177251881Speter } 2178299742Sdim SVN_ERR(svn_ra_svn__end_list(conn, pool)); 2179251881Speter 2180299742Sdim /* send LOG_ENTRY main members */ 2181299742Sdim SVN_ERR(svn_ra_svn__write_data_log_entry(conn, pool, 2182299742Sdim log_entry->revision, 2183299742Sdim author, date, message, 2184299742Sdim log_entry->has_children, 2185299742Sdim invalid_revnum, revprop_count)); 2186299742Sdim 2187299742Sdim /* send LOG_ENTRY->REVPROPS */ 2188299742Sdim SVN_ERR(svn_ra_svn__start_list(conn, pool)); 2189299742Sdim if (revprop_count) 2190299742Sdim SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); 2191299742Sdim SVN_ERR(svn_ra_svn__end_list(conn, pool)); 2192299742Sdim 2193299742Sdim /* send LOG_ENTRY members that were added in later SVN releases */ 2194299742Sdim SVN_ERR(svn_ra_svn__write_boolean(conn, pool, log_entry->subtractive_merge)); 2195299742Sdim SVN_ERR(svn_ra_svn__end_list(conn, pool)); 2196299742Sdim 2197251881Speter if (log_entry->has_children) 2198251881Speter b->stack_depth++; 2199251881Speter 2200251881Speter return SVN_NO_ERROR; 2201251881Speter} 2202251881Speter 2203251881Speterstatic svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2204251881Speter apr_array_header_t *params, void *baton) 2205251881Speter{ 2206251881Speter svn_error_t *err, *write_err; 2207251881Speter server_baton_t *b = baton; 2208251881Speter svn_revnum_t start_rev, end_rev; 2209251881Speter const char *full_path; 2210251881Speter svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; 2211251881Speter apr_array_header_t *paths, *full_paths, *revprop_items, *revprops; 2212251881Speter char *revprop_word; 2213251881Speter svn_ra_svn_item_t *elt; 2214251881Speter int i; 2215251881Speter apr_uint64_t limit, include_merged_revs_param; 2216251881Speter log_baton_t lb; 2217251881Speter authz_baton_t ab; 2218251881Speter 2219251881Speter ab.server = b; 2220251881Speter ab.conn = conn; 2221251881Speter 2222251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, 2223251881Speter &start_rev, &end_rev, &send_changed_paths, 2224251881Speter &strict_node, &limit, 2225251881Speter &include_merged_revs_param, 2226251881Speter &revprop_word, &revprop_items)); 2227251881Speter 2228251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2229251881Speter include_merged_revisions = FALSE; 2230251881Speter else 2231251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2232251881Speter 2233251881Speter if (revprop_word == NULL) 2234251881Speter /* pre-1.5 client */ 2235251881Speter revprops = svn_compat_log_revprops_in(pool); 2236251881Speter else if (strcmp(revprop_word, "all-revprops") == 0) 2237251881Speter revprops = NULL; 2238251881Speter else if (strcmp(revprop_word, "revprops") == 0) 2239251881Speter { 2240251881Speter SVN_ERR_ASSERT(revprop_items); 2241251881Speter 2242251881Speter revprops = apr_array_make(pool, revprop_items->nelts, 2243251881Speter sizeof(char *)); 2244251881Speter for (i = 0; i < revprop_items->nelts; i++) 2245251881Speter { 2246251881Speter elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t); 2247251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2248251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2249251881Speter _("Log revprop entry not a string")); 2250251881Speter APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data; 2251251881Speter } 2252251881Speter } 2253251881Speter else 2254251881Speter return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2255251881Speter _("Unknown revprop word '%s' in log command"), 2256251881Speter revprop_word); 2257251881Speter 2258251881Speter /* If we got an unspecified number then the user didn't send us anything, 2259251881Speter so we assume no limit. If it's larger than INT_MAX then someone is 2260251881Speter messing with us, since we know the svn client libraries will never send 2261251881Speter us anything that big, so play it safe and default to no limit. */ 2262251881Speter if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX) 2263251881Speter limit = 0; 2264251881Speter 2265251881Speter full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2266251881Speter for (i = 0; i < paths->nelts; i++) 2267251881Speter { 2268251881Speter elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2269251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2270251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2271251881Speter _("Log path entry not a string")); 2272251881Speter full_path = svn_relpath_canonicalize(elt->u.string->data, pool), 2273299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, full_path, 2274299742Sdim pool); 2275251881Speter APR_ARRAY_PUSH(full_paths, const char *) = full_path; 2276251881Speter } 2277251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2278251881Speter 2279251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2280251881Speter svn_log__log(full_paths, start_rev, end_rev, 2281251881Speter (int) limit, send_changed_paths, 2282251881Speter strict_node, include_merged_revisions, 2283251881Speter revprops, pool))); 2284251881Speter 2285251881Speter /* Get logs. (Can't report errors back to the client at this point.) */ 2286299742Sdim lb.fs_path = b->repository->fs_path->data; 2287251881Speter lb.conn = conn; 2288251881Speter lb.stack_depth = 0; 2289299742Sdim err = svn_repos_get_logs4(b->repository->repos, full_paths, start_rev, 2290299742Sdim end_rev, (int) limit, send_changed_paths, 2291299742Sdim strict_node, include_merged_revisions, 2292299742Sdim revprops, authz_check_access_cb_func(b), &ab, 2293299742Sdim log_receiver, &lb, pool); 2294251881Speter 2295251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2296251881Speter if (write_err) 2297251881Speter { 2298251881Speter svn_error_clear(err); 2299251881Speter return write_err; 2300251881Speter } 2301251881Speter SVN_CMD_ERR(err); 2302251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2303251881Speter return SVN_NO_ERROR; 2304251881Speter} 2305251881Speter 2306251881Speterstatic svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2307251881Speter apr_array_header_t *params, void *baton) 2308251881Speter{ 2309251881Speter server_baton_t *b = baton; 2310251881Speter svn_revnum_t rev; 2311251881Speter const char *path, *full_path; 2312251881Speter svn_fs_root_t *root; 2313251881Speter svn_node_kind_t kind; 2314251881Speter 2315251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2316299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2317251881Speter svn_relpath_canonicalize(path, pool), pool); 2318251881Speter 2319251881Speter /* Check authorizations */ 2320251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2321251881Speter full_path, FALSE)); 2322251881Speter 2323251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2324299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2325251881Speter 2326251881Speter SVN_ERR(log_command(b, conn, pool, "check-path %s@%d", 2327251881Speter svn_path_uri_encode(full_path, pool), rev)); 2328251881Speter 2329299742Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 2330251881Speter SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool)); 2331251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w", 2332251881Speter svn_node_kind_to_word(kind))); 2333251881Speter return SVN_NO_ERROR; 2334251881Speter} 2335251881Speter 2336251881Speterstatic svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2337251881Speter apr_array_header_t *params, void *baton) 2338251881Speter{ 2339251881Speter server_baton_t *b = baton; 2340251881Speter svn_revnum_t rev; 2341251881Speter const char *path, *full_path, *cdate; 2342251881Speter svn_fs_root_t *root; 2343251881Speter svn_dirent_t *dirent; 2344251881Speter 2345251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2346299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2347251881Speter svn_relpath_canonicalize(path, pool), pool); 2348251881Speter 2349251881Speter /* Check authorizations */ 2350251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2351251881Speter full_path, FALSE)); 2352251881Speter 2353251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2354299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 2355251881Speter 2356251881Speter SVN_ERR(log_command(b, conn, pool, "stat %s@%d", 2357251881Speter svn_path_uri_encode(full_path, pool), rev)); 2358251881Speter 2359299742Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, pool)); 2360251881Speter SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool)); 2361251881Speter 2362251881Speter /* Need to return the equivalent of "(?l)", since that's what the 2363251881Speter client is reading. */ 2364251881Speter 2365251881Speter if (dirent == NULL) 2366251881Speter { 2367251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()")); 2368251881Speter return SVN_NO_ERROR; 2369251881Speter } 2370251881Speter 2371251881Speter cdate = (dirent->time == (time_t) -1) ? NULL 2372251881Speter : svn_time_to_cstring(dirent->time, pool); 2373251881Speter 2374251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", 2375251881Speter svn_node_kind_to_word(dirent->kind), 2376251881Speter (apr_uint64_t) dirent->size, 2377251881Speter dirent->has_props, dirent->created_rev, 2378251881Speter cdate, dirent->last_author)); 2379251881Speter 2380251881Speter return SVN_NO_ERROR; 2381251881Speter} 2382251881Speter 2383251881Speterstatic svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2384251881Speter apr_array_header_t *params, void *baton) 2385251881Speter{ 2386251881Speter svn_error_t *err, *write_err; 2387251881Speter server_baton_t *b = baton; 2388251881Speter svn_revnum_t revision; 2389251881Speter apr_array_header_t *location_revisions, *loc_revs_proto; 2390251881Speter svn_ra_svn_item_t *elt; 2391251881Speter int i; 2392251881Speter const char *relative_path; 2393251881Speter svn_revnum_t peg_revision; 2394251881Speter apr_hash_t *fs_locations; 2395251881Speter const char *abs_path; 2396251881Speter authz_baton_t ab; 2397251881Speter 2398251881Speter ab.server = b; 2399251881Speter ab.conn = conn; 2400251881Speter 2401251881Speter /* Parse the arguments. */ 2402251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path, 2403251881Speter &peg_revision, 2404251881Speter &loc_revs_proto)); 2405251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2406251881Speter 2407299742Sdim abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, 2408299742Sdim pool); 2409251881Speter 2410251881Speter location_revisions = apr_array_make(pool, loc_revs_proto->nelts, 2411251881Speter sizeof(svn_revnum_t)); 2412251881Speter for (i = 0; i < loc_revs_proto->nelts; i++) 2413251881Speter { 2414251881Speter elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t); 2415251881Speter if (elt->kind != SVN_RA_SVN_NUMBER) 2416251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2417251881Speter "Get-locations location revisions entry " 2418251881Speter "not a revision number"); 2419251881Speter revision = (svn_revnum_t)(elt->u.number); 2420251881Speter APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision; 2421251881Speter } 2422251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2423251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2424251881Speter svn_log__get_locations(abs_path, peg_revision, 2425251881Speter location_revisions, pool))); 2426251881Speter 2427251881Speter /* All the parameters are fine - let's perform the query against the 2428251881Speter * repository. */ 2429251881Speter 2430251881Speter /* We store both err and write_err here, so the client will get 2431251881Speter * the "done" even if there was an error in fetching the results. */ 2432251881Speter 2433299742Sdim err = svn_repos_trace_node_locations(b->repository->fs, &fs_locations, 2434299742Sdim abs_path, peg_revision, 2435299742Sdim location_revisions, 2436251881Speter authz_check_access_cb_func(b), &ab, 2437251881Speter pool); 2438251881Speter 2439251881Speter /* Now, write the results to the connection. */ 2440251881Speter if (!err) 2441251881Speter { 2442251881Speter if (fs_locations) 2443251881Speter { 2444251881Speter apr_hash_index_t *iter; 2445251881Speter 2446251881Speter for (iter = apr_hash_first(pool, fs_locations); iter; 2447251881Speter iter = apr_hash_next(iter)) 2448251881Speter { 2449299742Sdim const svn_revnum_t *iter_key = apr_hash_this_key(iter); 2450299742Sdim const char *iter_value = apr_hash_this_val(iter); 2451251881Speter 2452251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", 2453251881Speter *iter_key, iter_value)); 2454251881Speter } 2455251881Speter } 2456251881Speter } 2457251881Speter 2458251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2459251881Speter if (write_err) 2460251881Speter { 2461251881Speter svn_error_clear(err); 2462251881Speter return write_err; 2463251881Speter } 2464251881Speter SVN_CMD_ERR(err); 2465251881Speter 2466251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2467251881Speter 2468251881Speter return SVN_NO_ERROR; 2469251881Speter} 2470251881Speter 2471251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment, 2472251881Speter void *baton, 2473251881Speter apr_pool_t *pool) 2474251881Speter{ 2475251881Speter svn_ra_svn_conn_t *conn = baton; 2476251881Speter return svn_ra_svn__write_tuple(conn, pool, "rr(?c)", 2477251881Speter segment->range_start, 2478251881Speter segment->range_end, 2479251881Speter segment->path); 2480251881Speter} 2481251881Speter 2482251881Speterstatic svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, 2483251881Speter apr_pool_t *pool, 2484251881Speter apr_array_header_t *params, 2485251881Speter void *baton) 2486251881Speter{ 2487251881Speter svn_error_t *err, *write_err; 2488251881Speter server_baton_t *b = baton; 2489251881Speter svn_revnum_t peg_revision, start_rev, end_rev; 2490251881Speter const char *relative_path; 2491251881Speter const char *abs_path; 2492251881Speter authz_baton_t ab; 2493251881Speter 2494251881Speter ab.server = b; 2495251881Speter ab.conn = conn; 2496251881Speter 2497251881Speter /* Parse the arguments. */ 2498251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)", 2499251881Speter &relative_path, &peg_revision, 2500251881Speter &start_rev, &end_rev)); 2501251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2502251881Speter 2503299742Sdim abs_path = svn_fspath__join(b->repository->fs_path->data, relative_path, 2504299742Sdim pool); 2505251881Speter 2506289166Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2507289166Speter SVN_ERR(log_command(baton, conn, pool, "%s", 2508289166Speter svn_log__get_location_segments(abs_path, peg_revision, 2509289166Speter start_rev, end_rev, 2510289166Speter pool))); 2511289166Speter 2512289166Speter /* No START_REV or PEG_REVISION? We'll use HEAD. */ 2513289166Speter if (!SVN_IS_VALID_REVNUM(start_rev) || !SVN_IS_VALID_REVNUM(peg_revision)) 2514251881Speter { 2515289166Speter svn_revnum_t youngest; 2516289166Speter 2517299742Sdim err = svn_fs_youngest_rev(&youngest, b->repository->fs, pool); 2518289166Speter 2519299742Sdim if (err) 2520299742Sdim { 2521299742Sdim err = svn_error_compose_create( 2522299742Sdim svn_ra_svn__write_word(conn, pool, "done"), 2523299742Sdim err); 2524299742Sdim 2525299742Sdim return log_fail_and_flush(err, b, conn, pool); 2526299742Sdim } 2527299742Sdim 2528289166Speter if (!SVN_IS_VALID_REVNUM(start_rev)) 2529289166Speter start_rev = youngest; 2530289166Speter if (!SVN_IS_VALID_REVNUM(peg_revision)) 2531289166Speter peg_revision = youngest; 2532289166Speter } 2533289166Speter 2534289166Speter /* No END_REV? We'll use 0. */ 2535289166Speter if (!SVN_IS_VALID_REVNUM(end_rev)) 2536289166Speter end_rev = 0; 2537289166Speter 2538289166Speter if (end_rev > start_rev) 2539289166Speter { 2540299742Sdim err = svn_ra_svn__write_word(conn, pool, "done"); 2541299742Sdim err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, 2542251881Speter "Get-location-segments end revision must not be " 2543251881Speter "younger than start revision"); 2544251881Speter return log_fail_and_flush(err, b, conn, pool); 2545251881Speter } 2546251881Speter 2547289166Speter if (start_rev > peg_revision) 2548251881Speter { 2549299742Sdim err = svn_ra_svn__write_word(conn, pool, "done"); 2550299742Sdim err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, err, 2551251881Speter "Get-location-segments start revision must not " 2552251881Speter "be younger than peg revision"); 2553251881Speter return log_fail_and_flush(err, b, conn, pool); 2554251881Speter } 2555251881Speter 2556251881Speter /* All the parameters are fine - let's perform the query against the 2557251881Speter * repository. */ 2558251881Speter 2559251881Speter /* We store both err and write_err here, so the client will get 2560251881Speter * the "done" even if there was an error in fetching the results. */ 2561251881Speter 2562299742Sdim err = svn_repos_node_location_segments(b->repository->repos, abs_path, 2563251881Speter peg_revision, start_rev, end_rev, 2564251881Speter gls_receiver, (void *)conn, 2565251881Speter authz_check_access_cb_func(b), &ab, 2566251881Speter pool); 2567251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2568251881Speter if (write_err) 2569251881Speter { 2570299742Sdim return svn_error_compose_create(write_err, err); 2571251881Speter } 2572251881Speter SVN_CMD_ERR(err); 2573251881Speter 2574251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2575251881Speter 2576251881Speter return SVN_NO_ERROR; 2577251881Speter} 2578251881Speter 2579251881Speter/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the 2580251881Speter client as a string. */ 2581251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data, 2582251881Speter apr_size_t *len) 2583251881Speter{ 2584251881Speter file_revs_baton_t *b = baton; 2585251881Speter svn_string_t str; 2586251881Speter 2587251881Speter str.data = data; 2588251881Speter str.len = *len; 2589251881Speter return svn_ra_svn__write_string(b->conn, b->pool, &str); 2590251881Speter} 2591251881Speter 2592251881Speter/* This implements svn_close_fn_t. Mark the end of the data by writing an 2593251881Speter empty string to the client. */ 2594251881Speterstatic svn_error_t *svndiff_close_handler(void *baton) 2595251881Speter{ 2596251881Speter file_revs_baton_t *b = baton; 2597251881Speter 2598251881Speter SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, "")); 2599251881Speter return SVN_NO_ERROR; 2600251881Speter} 2601251881Speter 2602251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */ 2603251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path, 2604251881Speter svn_revnum_t rev, apr_hash_t *rev_props, 2605251881Speter svn_boolean_t merged_revision, 2606251881Speter svn_txdelta_window_handler_t *d_handler, 2607251881Speter void **d_baton, 2608251881Speter apr_array_header_t *prop_diffs, 2609251881Speter apr_pool_t *pool) 2610251881Speter{ 2611251881Speter file_revs_baton_t *frb = baton; 2612251881Speter svn_stream_t *stream; 2613251881Speter 2614251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!", 2615251881Speter path, rev)); 2616251881Speter SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props)); 2617251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!")); 2618251881Speter SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs)); 2619251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision)); 2620251881Speter 2621251881Speter /* Store the pool for the delta stream. */ 2622251881Speter frb->pool = pool; 2623251881Speter 2624251881Speter /* Prepare for the delta or just write an empty string. */ 2625251881Speter if (d_handler) 2626251881Speter { 2627251881Speter stream = svn_stream_create(baton, pool); 2628251881Speter svn_stream_set_write(stream, svndiff_handler); 2629251881Speter svn_stream_set_close(stream, svndiff_close_handler); 2630251881Speter 2631251881Speter /* If the connection does not support SVNDIFF1 or if we don't want to use 2632251881Speter * compression, use the non-compressing "version 0" implementation */ 2633251881Speter if ( svn_ra_svn_compression_level(frb->conn) > 0 2634251881Speter && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1)) 2635251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1, 2636251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2637251881Speter else 2638251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0, 2639251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2640251881Speter } 2641251881Speter else 2642251881Speter SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); 2643251881Speter 2644251881Speter return SVN_NO_ERROR; 2645251881Speter} 2646251881Speter 2647251881Speterstatic svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2648251881Speter apr_array_header_t *params, void *baton) 2649251881Speter{ 2650251881Speter server_baton_t *b = baton; 2651251881Speter svn_error_t *err, *write_err; 2652251881Speter file_revs_baton_t frb; 2653251881Speter svn_revnum_t start_rev, end_rev; 2654251881Speter const char *path; 2655251881Speter const char *full_path; 2656251881Speter apr_uint64_t include_merged_revs_param; 2657251881Speter svn_boolean_t include_merged_revisions; 2658251881Speter authz_baton_t ab; 2659251881Speter 2660251881Speter ab.server = b; 2661251881Speter ab.conn = conn; 2662251881Speter 2663251881Speter /* Parse arguments. */ 2664251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B", 2665251881Speter &path, &start_rev, &end_rev, 2666251881Speter &include_merged_revs_param)); 2667251881Speter path = svn_relpath_canonicalize(path, pool); 2668251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2669299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, path, pool); 2670251881Speter 2671251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2672251881Speter include_merged_revisions = FALSE; 2673251881Speter else 2674251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2675251881Speter 2676251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2677251881Speter svn_log__get_file_revs(full_path, start_rev, end_rev, 2678251881Speter include_merged_revisions, 2679251881Speter pool))); 2680251881Speter 2681251881Speter frb.conn = conn; 2682251881Speter frb.pool = NULL; 2683251881Speter 2684299742Sdim err = svn_repos_get_file_revs2(b->repository->repos, full_path, start_rev, 2685299742Sdim end_rev, include_merged_revisions, 2686251881Speter authz_check_access_cb_func(b), &ab, 2687251881Speter file_rev_handler, &frb, pool); 2688251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2689251881Speter if (write_err) 2690251881Speter { 2691251881Speter svn_error_clear(err); 2692251881Speter return write_err; 2693251881Speter } 2694251881Speter SVN_CMD_ERR(err); 2695251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2696251881Speter 2697251881Speter return SVN_NO_ERROR; 2698251881Speter} 2699251881Speter 2700251881Speterstatic svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2701251881Speter apr_array_header_t *params, void *baton) 2702251881Speter{ 2703251881Speter server_baton_t *b = baton; 2704251881Speter const char *path; 2705251881Speter const char *comment; 2706251881Speter const char *full_path; 2707251881Speter svn_boolean_t steal_lock; 2708251881Speter svn_revnum_t current_rev; 2709251881Speter svn_lock_t *l; 2710251881Speter 2711251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, 2712251881Speter &steal_lock, ¤t_rev)); 2713299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2714251881Speter svn_relpath_canonicalize(path, pool), pool); 2715251881Speter 2716251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2717251881Speter full_path, TRUE)); 2718251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2719251881Speter svn_log__lock_one_path(full_path, steal_lock, pool))); 2720251881Speter 2721299742Sdim SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repository->repos, full_path, NULL, 2722299742Sdim comment, 0, 0, /* No expiration time. */ 2723251881Speter current_rev, steal_lock, pool)); 2724251881Speter 2725251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success")); 2726251881Speter SVN_ERR(write_lock(conn, pool, l)); 2727251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)")); 2728251881Speter 2729251881Speter return SVN_NO_ERROR; 2730251881Speter} 2731251881Speter 2732299742Sdimstruct lock_result_t { 2733299742Sdim const svn_lock_t *lock; 2734299742Sdim svn_error_t *err; 2735299742Sdim}; 2736299742Sdim 2737299742Sdimstruct lock_many_baton_t { 2738299742Sdim apr_hash_t *results; 2739299742Sdim apr_pool_t *pool; 2740299742Sdim}; 2741299742Sdim 2742299742Sdim/* Implements svn_fs_lock_callback_t. */ 2743299742Sdimstatic svn_error_t * 2744299742Sdimlock_many_cb(void *baton, 2745299742Sdim const char *path, 2746299742Sdim const svn_lock_t *fs_lock, 2747299742Sdim svn_error_t *fs_err, 2748299742Sdim apr_pool_t *pool) 2749299742Sdim{ 2750299742Sdim struct lock_many_baton_t *b = baton; 2751299742Sdim struct lock_result_t *result = apr_palloc(b->pool, 2752299742Sdim sizeof(struct lock_result_t)); 2753299742Sdim 2754299742Sdim result->lock = fs_lock; 2755299742Sdim result->err = svn_error_dup(fs_err); 2756299742Sdim svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result); 2757299742Sdim 2758299742Sdim return SVN_NO_ERROR; 2759299742Sdim} 2760299742Sdim 2761299742Sdimstatic void 2762299742Sdimclear_lock_result_hash(apr_hash_t *results, 2763299742Sdim apr_pool_t *scratch_pool) 2764299742Sdim{ 2765299742Sdim apr_hash_index_t *hi; 2766299742Sdim 2767299742Sdim for (hi = apr_hash_first(scratch_pool, results); hi; hi = apr_hash_next(hi)) 2768299742Sdim { 2769299742Sdim struct lock_result_t *result = apr_hash_this_val(hi); 2770299742Sdim svn_error_clear(result->err); 2771299742Sdim } 2772299742Sdim} 2773299742Sdim 2774251881Speterstatic svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2775251881Speter apr_array_header_t *params, void *baton) 2776251881Speter{ 2777251881Speter server_baton_t *b = baton; 2778251881Speter apr_array_header_t *path_revs; 2779251881Speter const char *comment; 2780251881Speter svn_boolean_t steal_lock; 2781251881Speter int i; 2782251881Speter apr_pool_t *subpool; 2783299742Sdim svn_error_t *err, *write_err = SVN_NO_ERROR; 2784299742Sdim apr_hash_t *targets = apr_hash_make(pool); 2785299742Sdim apr_hash_t *authz_results = apr_hash_make(pool); 2786299742Sdim apr_hash_index_t *hi; 2787299742Sdim struct lock_many_baton_t lmb; 2788251881Speter 2789251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, 2790251881Speter &path_revs)); 2791251881Speter 2792251881Speter subpool = svn_pool_create(pool); 2793251881Speter 2794251881Speter /* Because we can only send a single auth reply per request, we send 2795251881Speter a reply before parsing the lock commands. This means an authz 2796251881Speter access denial will abort the processing of the locks and return 2797251881Speter an error. */ 2798251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE)); 2799251881Speter 2800299742Sdim /* Parse the lock requests from PATH_REVS into TARGETS. */ 2801251881Speter for (i = 0; i < path_revs->nelts; ++i) 2802251881Speter { 2803299742Sdim const char *path, *full_path; 2804299742Sdim svn_revnum_t current_rev; 2805251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, 2806251881Speter svn_ra_svn_item_t); 2807299742Sdim svn_fs_lock_target_t *target; 2808251881Speter 2809251881Speter svn_pool_clear(subpool); 2810251881Speter 2811251881Speter if (item->kind != SVN_RA_SVN_LIST) 2812251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2813251881Speter "Lock requests should be list of lists"); 2814251881Speter 2815299742Sdim SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, 2816251881Speter ¤t_rev)); 2817251881Speter 2818299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2819251881Speter svn_relpath_canonicalize(path, subpool), 2820251881Speter pool); 2821299742Sdim target = svn_fs_lock_target_create(NULL, current_rev, pool); 2822251881Speter 2823299742Sdim /* Any duplicate paths, once canonicalized, get collapsed into a 2824299742Sdim single path that is processed once. The result is then 2825299742Sdim returned multiple times. */ 2826299742Sdim svn_hash_sets(targets, full_path, target); 2827299742Sdim } 2828299742Sdim 2829299742Sdim SVN_ERR(log_command(b, conn, subpool, "%s", 2830299742Sdim svn_log__lock(targets, steal_lock, subpool))); 2831299742Sdim 2832299742Sdim /* Check authz. 2833299742Sdim 2834299742Sdim Note: From here on we need to make sure any errors in authz_results, or 2835299742Sdim results, are cleared before returning from this function. */ 2836299742Sdim for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) 2837299742Sdim { 2838299742Sdim const char *full_path = apr_hash_this_key(hi); 2839299742Sdim 2840299742Sdim svn_pool_clear(subpool); 2841299742Sdim 2842299742Sdim if (! lookup_access(subpool, b, svn_authz_write, full_path, TRUE)) 2843251881Speter { 2844299742Sdim struct lock_result_t *result 2845299742Sdim = apr_palloc(pool, sizeof(struct lock_result_t)); 2846299742Sdim 2847299742Sdim result->lock = NULL; 2848299742Sdim result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 2849299742Sdim NULL, NULL, b); 2850299742Sdim svn_hash_sets(authz_results, full_path, result); 2851299742Sdim svn_hash_sets(targets, full_path, NULL); 2852251881Speter } 2853299742Sdim } 2854251881Speter 2855299742Sdim lmb.results = apr_hash_make(pool); 2856299742Sdim lmb.pool = pool; 2857251881Speter 2858299742Sdim err = svn_repos_fs_lock_many(b->repository->repos, targets, 2859299742Sdim comment, FALSE, 2860299742Sdim 0, /* No expiration time. */ 2861299742Sdim steal_lock, lock_many_cb, &lmb, 2862299742Sdim pool, subpool); 2863299742Sdim 2864299742Sdim /* Return results in the same order as the paths were supplied. */ 2865299742Sdim for (i = 0; i < path_revs->nelts; ++i) 2866299742Sdim { 2867299742Sdim const char *path, *full_path; 2868299742Sdim svn_revnum_t current_rev; 2869299742Sdim svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, 2870299742Sdim svn_ra_svn_item_t); 2871299742Sdim struct lock_result_t *result; 2872299742Sdim 2873299742Sdim svn_pool_clear(subpool); 2874299742Sdim 2875299742Sdim write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?r)", &path, 2876299742Sdim ¤t_rev); 2877299742Sdim if (write_err) 2878299742Sdim break; 2879299742Sdim 2880299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2881299742Sdim svn_relpath_canonicalize(path, subpool), 2882299742Sdim subpool); 2883299742Sdim 2884299742Sdim result = svn_hash_gets(lmb.results, full_path); 2885299742Sdim if (!result) 2886299742Sdim result = svn_hash_gets(authz_results, full_path); 2887299742Sdim if (!result) 2888251881Speter { 2889299742Sdim /* No result? Something really odd happened, create a 2890299742Sdim placeholder error so that any other results can be 2891299742Sdim reported in the correct order. */ 2892299742Sdim result = apr_palloc(pool, sizeof(struct lock_result_t)); 2893299742Sdim result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, 2894299742Sdim _("No result for '%s'."), path); 2895299742Sdim svn_hash_sets(lmb.results, full_path, result); 2896251881Speter } 2897299742Sdim 2898299742Sdim if (result->err) 2899299742Sdim write_err = svn_ra_svn__write_cmd_failure(conn, subpool, 2900299742Sdim result->err); 2901251881Speter else 2902251881Speter { 2903299742Sdim write_err = svn_ra_svn__write_tuple(conn, subpool, 2904299742Sdim "w!", "success"); 2905299742Sdim if (!write_err) 2906299742Sdim write_err = write_lock(conn, subpool, result->lock); 2907299742Sdim if (!write_err) 2908299742Sdim write_err = svn_ra_svn__write_tuple(conn, subpool, "!"); 2909251881Speter } 2910299742Sdim if (write_err) 2911299742Sdim break; 2912251881Speter } 2913251881Speter 2914299742Sdim clear_lock_result_hash(authz_results, subpool); 2915299742Sdim clear_lock_result_hash(lmb.results, subpool); 2916299742Sdim 2917251881Speter svn_pool_destroy(subpool); 2918251881Speter 2919251881Speter if (!write_err) 2920299742Sdim write_err = svn_ra_svn__write_word(conn, pool, "done"); 2921299742Sdim if (!write_err) 2922251881Speter SVN_CMD_ERR(err); 2923251881Speter svn_error_clear(err); 2924251881Speter SVN_ERR(write_err); 2925251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2926251881Speter 2927251881Speter return SVN_NO_ERROR; 2928251881Speter} 2929251881Speter 2930251881Speterstatic svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2931251881Speter apr_array_header_t *params, void *baton) 2932251881Speter{ 2933251881Speter server_baton_t *b = baton; 2934251881Speter const char *path, *token, *full_path; 2935251881Speter svn_boolean_t break_lock; 2936251881Speter 2937251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token, 2938251881Speter &break_lock)); 2939251881Speter 2940299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2941251881Speter svn_relpath_canonicalize(path, pool), pool); 2942251881Speter 2943251881Speter /* Username required unless break_lock was specified. */ 2944251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2945251881Speter full_path, ! break_lock)); 2946251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2947251881Speter svn_log__unlock_one_path(full_path, break_lock, pool))); 2948251881Speter 2949299742Sdim SVN_CMD_ERR(svn_repos_fs_unlock(b->repository->repos, full_path, token, 2950299742Sdim break_lock, pool)); 2951251881Speter 2952251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2953251881Speter 2954251881Speter return SVN_NO_ERROR; 2955251881Speter} 2956251881Speter 2957251881Speterstatic svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2958251881Speter apr_array_header_t *params, void *baton) 2959251881Speter{ 2960251881Speter server_baton_t *b = baton; 2961251881Speter svn_boolean_t break_lock; 2962251881Speter apr_array_header_t *unlock_tokens; 2963251881Speter int i; 2964251881Speter apr_pool_t *subpool; 2965299742Sdim svn_error_t *err = SVN_NO_ERROR, *write_err = SVN_NO_ERROR; 2966299742Sdim apr_hash_t *targets = apr_hash_make(pool); 2967299742Sdim apr_hash_t *authz_results = apr_hash_make(pool); 2968299742Sdim apr_hash_index_t *hi; 2969299742Sdim struct lock_many_baton_t lmb; 2970251881Speter 2971251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, 2972251881Speter &unlock_tokens)); 2973251881Speter 2974251881Speter /* Username required unless break_lock was specified. */ 2975251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); 2976251881Speter 2977251881Speter subpool = svn_pool_create(pool); 2978251881Speter 2979299742Sdim /* Parse the unlock requests from PATH_REVS into TARGETS. */ 2980251881Speter for (i = 0; i < unlock_tokens->nelts; i++) 2981251881Speter { 2982251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, 2983251881Speter svn_ra_svn_item_t); 2984299742Sdim const char *path, *full_path, *token; 2985251881Speter 2986251881Speter svn_pool_clear(subpool); 2987251881Speter 2988251881Speter if (item->kind != SVN_RA_SVN_LIST) 2989251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2990251881Speter "Unlock request should be a list of lists"); 2991251881Speter 2992251881Speter SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, 2993251881Speter &token)); 2994299742Sdim if (!token) 2995299742Sdim token = ""; 2996251881Speter 2997299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 2998251881Speter svn_relpath_canonicalize(path, subpool), 2999251881Speter pool); 3000251881Speter 3001299742Sdim /* Any duplicate paths, once canonicalized, get collapsed into a 3002299742Sdim single path that is processed once. The result is then 3003299742Sdim returned multiple times. */ 3004299742Sdim svn_hash_sets(targets, full_path, token); 3005299742Sdim } 3006299742Sdim 3007299742Sdim SVN_ERR(log_command(b, conn, subpool, "%s", 3008299742Sdim svn_log__unlock(targets, break_lock, subpool))); 3009299742Sdim 3010299742Sdim /* Check authz. 3011299742Sdim 3012299742Sdim Note: From here on we need to make sure any errors in authz_results, or 3013299742Sdim results, are cleared before returning from this function. */ 3014299742Sdim for (hi = apr_hash_first(pool, targets); hi; hi = apr_hash_next(hi)) 3015299742Sdim { 3016299742Sdim const char *full_path = apr_hash_this_key(hi); 3017299742Sdim 3018299742Sdim svn_pool_clear(subpool); 3019299742Sdim 3020299742Sdim if (! lookup_access(subpool, b, svn_authz_write, full_path, 3021251881Speter ! break_lock)) 3022299742Sdim { 3023299742Sdim struct lock_result_t *result 3024299742Sdim = apr_palloc(pool, sizeof(struct lock_result_t)); 3025251881Speter 3026299742Sdim result->lock = NULL; 3027299742Sdim result->err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 3028299742Sdim NULL, NULL, b); 3029299742Sdim svn_hash_sets(authz_results, full_path, result); 3030299742Sdim svn_hash_sets(targets, full_path, NULL); 3031299742Sdim } 3032299742Sdim } 3033299742Sdim 3034299742Sdim lmb.results = apr_hash_make(pool); 3035299742Sdim lmb.pool = pool; 3036299742Sdim 3037299742Sdim err = svn_repos_fs_unlock_many(b->repository->repos, targets, 3038299742Sdim break_lock, lock_many_cb, &lmb, 3039299742Sdim pool, subpool); 3040299742Sdim 3041299742Sdim /* Return results in the same order as the paths were supplied. */ 3042299742Sdim for (i = 0; i < unlock_tokens->nelts; ++i) 3043299742Sdim { 3044299742Sdim const char *path, *token, *full_path; 3045299742Sdim svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, 3046299742Sdim svn_ra_svn_item_t); 3047299742Sdim struct lock_result_t *result; 3048299742Sdim 3049299742Sdim svn_pool_clear(subpool); 3050299742Sdim 3051299742Sdim write_err = svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, 3052299742Sdim &token); 3053299742Sdim if (write_err) 3054299742Sdim break; 3055299742Sdim 3056299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3057299742Sdim svn_relpath_canonicalize(path, subpool), 3058299742Sdim pool); 3059299742Sdim 3060299742Sdim result = svn_hash_gets(lmb.results, full_path); 3061299742Sdim if (!result) 3062299742Sdim result = svn_hash_gets(authz_results, full_path); 3063299742Sdim if (!result) 3064251881Speter { 3065299742Sdim /* No result? Something really odd happened, create a 3066299742Sdim placeholder error so that any other results can be 3067299742Sdim reported in the correct order. */ 3068299742Sdim result = apr_palloc(pool, sizeof(struct lock_result_t)); 3069299742Sdim result->err = svn_error_createf(SVN_ERR_FS_LOCK_OPERATION_FAILED, 0, 3070299742Sdim _("No result for '%s'."), path); 3071299742Sdim svn_hash_sets(lmb.results, full_path, result); 3072251881Speter } 3073299742Sdim 3074299742Sdim if (result->err) 3075299742Sdim write_err = svn_ra_svn__write_cmd_failure(conn, pool, result->err); 3076251881Speter else 3077299742Sdim write_err = svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", 3078299742Sdim path); 3079299742Sdim if (write_err) 3080299742Sdim break; 3081251881Speter } 3082251881Speter 3083299742Sdim clear_lock_result_hash(authz_results, subpool); 3084299742Sdim clear_lock_result_hash(lmb.results, subpool); 3085299742Sdim 3086251881Speter svn_pool_destroy(subpool); 3087251881Speter 3088299742Sdim if (!write_err) 3089299742Sdim write_err = svn_ra_svn__write_word(conn, pool, "done"); 3090251881Speter if (! write_err) 3091251881Speter SVN_CMD_ERR(err); 3092251881Speter svn_error_clear(err); 3093299742Sdim SVN_ERR(write_err); 3094251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3095251881Speter 3096251881Speter return SVN_NO_ERROR; 3097251881Speter} 3098251881Speter 3099251881Speterstatic svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3100251881Speter apr_array_header_t *params, void *baton) 3101251881Speter{ 3102251881Speter server_baton_t *b = baton; 3103251881Speter const char *path; 3104251881Speter const char *full_path; 3105251881Speter svn_lock_t *l; 3106251881Speter 3107251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); 3108251881Speter 3109299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3110251881Speter svn_relpath_canonicalize(path, pool), pool); 3111251881Speter 3112251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 3113251881Speter full_path, FALSE)); 3114251881Speter SVN_ERR(log_command(b, conn, pool, "get-lock %s", 3115251881Speter svn_path_uri_encode(full_path, pool))); 3116251881Speter 3117299742Sdim SVN_CMD_ERR(svn_fs_get_lock(&l, b->repository->fs, full_path, pool)); 3118251881Speter 3119251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 3120251881Speter if (l) 3121251881Speter SVN_ERR(write_lock(conn, pool, l)); 3122251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3123251881Speter 3124251881Speter return SVN_NO_ERROR; 3125251881Speter} 3126251881Speter 3127251881Speterstatic svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3128251881Speter apr_array_header_t *params, void *baton) 3129251881Speter{ 3130251881Speter server_baton_t *b = baton; 3131251881Speter const char *path; 3132251881Speter const char *full_path; 3133251881Speter const char *depth_word; 3134251881Speter svn_depth_t depth; 3135251881Speter apr_hash_t *locks; 3136251881Speter apr_hash_index_t *hi; 3137251881Speter svn_error_t *err; 3138251881Speter authz_baton_t ab; 3139251881Speter 3140251881Speter ab.server = b; 3141251881Speter ab.conn = conn; 3142251881Speter 3143251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); 3144251881Speter 3145251881Speter depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; 3146251881Speter if ((depth != svn_depth_empty) && 3147251881Speter (depth != svn_depth_files) && 3148251881Speter (depth != svn_depth_immediates) && 3149251881Speter (depth != svn_depth_infinity)) 3150251881Speter { 3151251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 3152251881Speter "Invalid 'depth' specified in get-locks request"); 3153251881Speter return log_fail_and_flush(err, b, conn, pool); 3154251881Speter } 3155251881Speter 3156299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3157251881Speter svn_relpath_canonicalize(path, pool), pool); 3158251881Speter 3159251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3160251881Speter 3161251881Speter SVN_ERR(log_command(b, conn, pool, "get-locks %s", 3162251881Speter svn_path_uri_encode(full_path, pool))); 3163299742Sdim SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repository->repos, 3164299742Sdim full_path, depth, 3165251881Speter authz_check_access_cb_func(b), &ab, 3166251881Speter pool)); 3167251881Speter 3168251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 3169251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 3170251881Speter { 3171299742Sdim svn_lock_t *l = apr_hash_this_val(hi); 3172251881Speter 3173251881Speter SVN_ERR(write_lock(conn, pool, l)); 3174251881Speter } 3175251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3176251881Speter 3177251881Speter return SVN_NO_ERROR; 3178251881Speter} 3179251881Speter 3180251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, 3181251881Speter server_baton_t *b, 3182251881Speter svn_revnum_t rev, 3183251881Speter svn_revnum_t low_water_mark, 3184251881Speter svn_boolean_t send_deltas, 3185251881Speter apr_pool_t *pool) 3186251881Speter{ 3187251881Speter const svn_delta_editor_t *editor; 3188251881Speter void *edit_baton; 3189251881Speter svn_fs_root_t *root; 3190251881Speter svn_error_t *err; 3191251881Speter authz_baton_t ab; 3192251881Speter 3193251881Speter ab.server = b; 3194251881Speter ab.conn = conn; 3195251881Speter 3196251881Speter SVN_ERR(log_command(b, conn, pool, 3197299742Sdim svn_log__replay(b->repository->fs_path->data, rev, 3198299742Sdim pool))); 3199251881Speter 3200251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 3201251881Speter 3202299742Sdim err = svn_fs_revision_root(&root, b->repository->fs, rev, pool); 3203251881Speter 3204251881Speter if (! err) 3205299742Sdim err = svn_repos_replay2(root, b->repository->fs_path->data, 3206299742Sdim low_water_mark, send_deltas, editor, edit_baton, 3207251881Speter authz_check_access_cb_func(b), &ab, pool); 3208251881Speter 3209251881Speter if (err) 3210251881Speter svn_error_clear(editor->abort_edit(edit_baton, pool)); 3211251881Speter SVN_CMD_ERR(err); 3212251881Speter 3213251881Speter return svn_ra_svn__write_cmd_finish_replay(conn, pool); 3214251881Speter} 3215251881Speter 3216251881Speterstatic svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3217251881Speter apr_array_header_t *params, void *baton) 3218251881Speter{ 3219251881Speter svn_revnum_t rev, low_water_mark; 3220251881Speter svn_boolean_t send_deltas; 3221251881Speter server_baton_t *b = baton; 3222251881Speter 3223251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark, 3224251881Speter &send_deltas)); 3225251881Speter 3226251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3227251881Speter 3228251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3229251881Speter send_deltas, pool)); 3230251881Speter 3231251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3232251881Speter 3233251881Speter return SVN_NO_ERROR; 3234251881Speter} 3235251881Speter 3236251881Speterstatic svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3237251881Speter apr_array_header_t *params, void *baton) 3238251881Speter{ 3239251881Speter svn_revnum_t start_rev, end_rev, rev, low_water_mark; 3240251881Speter svn_boolean_t send_deltas; 3241251881Speter server_baton_t *b = baton; 3242251881Speter apr_pool_t *iterpool; 3243251881Speter authz_baton_t ab; 3244251881Speter 3245251881Speter ab.server = b; 3246251881Speter ab.conn = conn; 3247251881Speter 3248251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev, 3249251881Speter &end_rev, &low_water_mark, 3250251881Speter &send_deltas)); 3251251881Speter 3252251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3253251881Speter 3254251881Speter iterpool = svn_pool_create(pool); 3255251881Speter for (rev = start_rev; rev <= end_rev; rev++) 3256251881Speter { 3257251881Speter apr_hash_t *props; 3258251881Speter 3259251881Speter svn_pool_clear(iterpool); 3260251881Speter 3261299742Sdim SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, 3262299742Sdim b->repository->repos, rev, 3263251881Speter authz_check_access_cb_func(b), 3264251881Speter &ab, 3265251881Speter iterpool)); 3266251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops")); 3267251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props)); 3268251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)")); 3269251881Speter 3270251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3271251881Speter send_deltas, iterpool)); 3272251881Speter 3273251881Speter } 3274251881Speter svn_pool_destroy(iterpool); 3275251881Speter 3276251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3277251881Speter 3278251881Speter return SVN_NO_ERROR; 3279251881Speter} 3280251881Speter 3281251881Speterstatic svn_error_t * 3282251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn, 3283251881Speter apr_pool_t *pool, 3284251881Speter apr_array_header_t *params, 3285251881Speter void *baton) 3286251881Speter{ 3287251881Speter server_baton_t *b = baton; 3288251881Speter const char *path, *full_path; 3289251881Speter svn_revnum_t peg_revision; 3290251881Speter svn_revnum_t end_revision; 3291251881Speter svn_revnum_t revision_deleted; 3292251881Speter 3293251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", 3294251881Speter &path, &peg_revision, &end_revision)); 3295299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3296251881Speter svn_relpath_canonicalize(path, pool), pool); 3297251881Speter SVN_ERR(log_command(b, conn, pool, "get-deleted-rev")); 3298251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3299299742Sdim SVN_ERR(svn_repos_deleted_rev(b->repository->fs, full_path, peg_revision, 3300299742Sdim end_revision, &revision_deleted, pool)); 3301251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted)); 3302251881Speter return SVN_NO_ERROR; 3303251881Speter} 3304251881Speter 3305251881Speterstatic svn_error_t * 3306251881Speterget_inherited_props(svn_ra_svn_conn_t *conn, 3307251881Speter apr_pool_t *pool, 3308251881Speter apr_array_header_t *params, 3309251881Speter void *baton) 3310251881Speter{ 3311251881Speter server_baton_t *b = baton; 3312251881Speter const char *path, *full_path; 3313251881Speter svn_revnum_t rev; 3314251881Speter svn_fs_root_t *root; 3315251881Speter apr_array_header_t *inherited_props; 3316251881Speter int i; 3317251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 3318251881Speter authz_baton_t ab; 3319251881Speter 3320251881Speter ab.server = b; 3321251881Speter ab.conn = conn; 3322251881Speter 3323251881Speter /* Parse arguments. */ 3324251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev)); 3325251881Speter 3326299742Sdim full_path = svn_fspath__join(b->repository->fs_path->data, 3327251881Speter svn_relpath_canonicalize(path, iterpool), 3328251881Speter pool); 3329251881Speter 3330251881Speter /* Check authorizations */ 3331251881Speter SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, 3332251881Speter full_path, FALSE)); 3333251881Speter 3334251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 3335299742Sdim SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->repository->fs, pool)); 3336251881Speter 3337251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 3338251881Speter svn_log__get_inherited_props(full_path, rev, 3339251881Speter iterpool))); 3340251881Speter 3341251881Speter /* Fetch the properties and a stream for the contents. */ 3342299742Sdim SVN_CMD_ERR(svn_fs_revision_root(&root, b->repository->fs, rev, iterpool)); 3343251881Speter SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); 3344251881Speter 3345251881Speter /* Send successful command response with revision and props. */ 3346251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success")); 3347251881Speter 3348251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!")); 3349251881Speter 3350251881Speter for (i = 0; i < inherited_props->nelts; i++) 3351251881Speter { 3352251881Speter svn_prop_inherited_item_t *iprop = 3353251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 3354251881Speter 3355251881Speter svn_pool_clear(iterpool); 3356251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 3357251881Speter iprop->path_or_url)); 3358251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 3359251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 3360251881Speter iprop->path_or_url)); 3361251881Speter } 3362251881Speter 3363251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))")); 3364251881Speter svn_pool_destroy(iterpool); 3365251881Speter return SVN_NO_ERROR; 3366251881Speter} 3367251881Speter 3368251881Speterstatic const svn_ra_svn_cmd_entry_t main_commands[] = { 3369251881Speter { "reparent", reparent }, 3370251881Speter { "get-latest-rev", get_latest_rev }, 3371251881Speter { "get-dated-rev", get_dated_rev }, 3372251881Speter { "change-rev-prop", change_rev_prop }, 3373251881Speter { "change-rev-prop2",change_rev_prop2 }, 3374251881Speter { "rev-proplist", rev_proplist }, 3375251881Speter { "rev-prop", rev_prop }, 3376251881Speter { "commit", commit }, 3377251881Speter { "get-file", get_file }, 3378251881Speter { "get-dir", get_dir }, 3379251881Speter { "update", update }, 3380251881Speter { "switch", switch_cmd }, 3381251881Speter { "status", status }, 3382251881Speter { "diff", diff }, 3383251881Speter { "get-mergeinfo", get_mergeinfo }, 3384251881Speter { "log", log_cmd }, 3385251881Speter { "check-path", check_path }, 3386251881Speter { "stat", stat_cmd }, 3387251881Speter { "get-locations", get_locations }, 3388251881Speter { "get-location-segments", get_location_segments }, 3389251881Speter { "get-file-revs", get_file_revs }, 3390251881Speter { "lock", lock }, 3391251881Speter { "lock-many", lock_many }, 3392251881Speter { "unlock", unlock }, 3393251881Speter { "unlock-many", unlock_many }, 3394251881Speter { "get-lock", get_lock }, 3395251881Speter { "get-locks", get_locks }, 3396251881Speter { "replay", replay }, 3397251881Speter { "replay-range", replay_range }, 3398251881Speter { "get-deleted-rev", get_deleted_rev }, 3399251881Speter { "get-iprops", get_inherited_props }, 3400251881Speter { NULL } 3401251881Speter}; 3402251881Speter 3403251881Speter/* Skip past the scheme part of a URL, including the tunnel specification 3404251881Speter * if present. Return NULL if the scheme part is invalid for ra_svn. */ 3405251881Speterstatic const char *skip_scheme_part(const char *url) 3406251881Speter{ 3407251881Speter if (strncmp(url, "svn", 3) != 0) 3408251881Speter return NULL; 3409251881Speter url += 3; 3410251881Speter if (*url == '+') 3411251881Speter url += strcspn(url, ":"); 3412251881Speter if (strncmp(url, "://", 3) != 0) 3413251881Speter return NULL; 3414251881Speter return url + 3; 3415251881Speter} 3416251881Speter 3417251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any 3418251881Speter '..' path segments. 3419251881Speter NOTE: This is similar to svn_path_is_backpath_present, but that function 3420251881Speter assumes the path separator is '/'. This function also checks for 3421251881Speter segments delimited by the local path separator. */ 3422251881Speterstatic svn_boolean_t 3423251881Speterrepos_path_valid(const char *path) 3424251881Speter{ 3425251881Speter const char *s = path; 3426251881Speter 3427251881Speter while (*s) 3428251881Speter { 3429251881Speter /* Scan for the end of the segment. */ 3430251881Speter while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR) 3431251881Speter ++path; 3432251881Speter 3433251881Speter /* Check for '..'. */ 3434251881Speter#ifdef WIN32 3435251881Speter /* On Windows, don't allow sequences of more than one character 3436251881Speter consisting of just dots and spaces. Win32 functions treat 3437251881Speter paths such as ".. " and "......." inconsistently. Make sure 3438251881Speter no one can escape out of the root. */ 3439251881Speter if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s)) 3440251881Speter return FALSE; 3441251881Speter#else /* ! WIN32 */ 3442251881Speter if (path - s == 2 && s[0] == '.' && s[1] == '.') 3443251881Speter return FALSE; 3444251881Speter#endif 3445251881Speter 3446251881Speter /* Skip all separators. */ 3447251881Speter while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR)) 3448251881Speter ++path; 3449251881Speter s = path; 3450251881Speter } 3451251881Speter 3452251881Speter return TRUE; 3453251881Speter} 3454251881Speter 3455251881Speter/* Look for the repository given by URL, using ROOT as the virtual 3456299742Sdim * repository root. If we find one, fill in the repos, fs, repos_url, 3457299742Sdim * and fs_path fields of REPOSITORY. VHOST and READ_ONLY flags are the 3458299742Sdim * same as in the server baton. 3459299742Sdim * 3460299742Sdim * CONFIG_POOL and AUTHZ_POOL shall be used to load any object of the 3461299742Sdim * respective type. 3462299742Sdim * 3463299742Sdim * Use SCRATCH_POOL for temporary allocations. 3464299742Sdim * 3465251881Speter */ 3466299742Sdimstatic svn_error_t * 3467299742Sdimfind_repos(const char *url, 3468299742Sdim const char *root, 3469299742Sdim svn_boolean_t vhost, 3470299742Sdim svn_boolean_t read_only, 3471299742Sdim svn_config_t *cfg, 3472299742Sdim repository_t *repository, 3473299742Sdim svn_repos__config_pool_t *config_pool, 3474299742Sdim svn_repos__authz_pool_t *authz_pool, 3475299742Sdim apr_hash_t *fs_config, 3476299742Sdim apr_pool_t *result_pool, 3477299742Sdim apr_pool_t *scratch_pool) 3478251881Speter{ 3479299742Sdim const char *path, *full_path, *fs_path, *hooks_env; 3480251881Speter svn_stringbuf_t *url_buf; 3481251881Speter 3482251881Speter /* Skip past the scheme and authority part. */ 3483251881Speter path = skip_scheme_part(url); 3484251881Speter if (path == NULL) 3485251881Speter return svn_error_createf(SVN_ERR_BAD_URL, NULL, 3486251881Speter "Non-svn URL passed to svn server: '%s'", url); 3487251881Speter 3488299742Sdim if (! vhost) 3489251881Speter { 3490251881Speter path = strchr(path, '/'); 3491251881Speter if (path == NULL) 3492251881Speter path = ""; 3493251881Speter } 3494299742Sdim path = svn_relpath_canonicalize(path, scratch_pool); 3495299742Sdim path = svn_path_uri_decode(path, scratch_pool); 3496251881Speter 3497251881Speter /* Ensure that it isn't possible to escape the root by disallowing 3498251881Speter '..' segments. */ 3499251881Speter if (!repos_path_valid(path)) 3500251881Speter return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 3501251881Speter "Couldn't determine repository path"); 3502251881Speter 3503251881Speter /* Join the server-configured root with the client path. */ 3504299742Sdim full_path = svn_dirent_join(svn_dirent_canonicalize(root, scratch_pool), 3505299742Sdim path, scratch_pool); 3506251881Speter 3507251881Speter /* Search for a repository in the full path. */ 3508299742Sdim repository->repos_root = svn_repos_find_root_path(full_path, result_pool); 3509299742Sdim if (!repository->repos_root) 3510251881Speter return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL, 3511251881Speter "No repository found in '%s'", url); 3512251881Speter 3513251881Speter /* Open the repository and fill in b with the resulting information. */ 3514299742Sdim SVN_ERR(svn_repos_open3(&repository->repos, repository->repos_root, 3515299742Sdim fs_config, result_pool, scratch_pool)); 3516299742Sdim SVN_ERR(svn_repos_remember_client_capabilities(repository->repos, 3517299742Sdim repository->capabilities)); 3518299742Sdim repository->fs = svn_repos_fs(repository->repos); 3519299742Sdim fs_path = full_path + strlen(repository->repos_root); 3520299742Sdim repository->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", 3521299742Sdim result_pool); 3522299742Sdim url_buf = svn_stringbuf_create(url, result_pool); 3523251881Speter svn_path_remove_components(url_buf, 3524299742Sdim svn_path_component_count(repository->fs_path->data)); 3525299742Sdim repository->repos_url = url_buf->data; 3526299742Sdim repository->authz_repos_name = svn_dirent_is_child(root, 3527299742Sdim repository->repos_root, 3528299742Sdim result_pool); 3529299742Sdim if (repository->authz_repos_name == NULL) 3530299742Sdim repository->repos_name = svn_dirent_basename(repository->repos_root, 3531299742Sdim result_pool); 3532251881Speter else 3533299742Sdim repository->repos_name = repository->authz_repos_name; 3534299742Sdim repository->repos_name = svn_path_uri_encode(repository->repos_name, 3535299742Sdim result_pool); 3536251881Speter 3537251881Speter /* If the svnserve configuration has not been loaded then load it from the 3538251881Speter * repository. */ 3539299742Sdim if (NULL == cfg) 3540251881Speter { 3541299742Sdim repository->base = svn_repos_conf_dir(repository->repos, result_pool); 3542251881Speter 3543299742Sdim SVN_ERR(svn_repos__config_pool_get(&cfg, NULL, config_pool, 3544299742Sdim svn_repos_svnserve_conf 3545299742Sdim (repository->repos, result_pool), 3546299742Sdim FALSE, FALSE, repository->repos, 3547299742Sdim result_pool)); 3548251881Speter } 3549299742Sdim 3550299742Sdim SVN_ERR(load_pwdb_config(repository, cfg, config_pool, result_pool)); 3551299742Sdim SVN_ERR(load_authz_config(repository, repository->repos_root, cfg, 3552299742Sdim authz_pool, result_pool)); 3553299742Sdim 3554299742Sdim#ifdef SVN_HAVE_SASL 3555251881Speter { 3556299742Sdim const char *val; 3557299742Sdim 3558299742Sdim /* Should we use Cyrus SASL? */ 3559299742Sdim SVN_ERR(svn_config_get_bool(cfg, &repository->use_sasl, 3560299742Sdim SVN_CONFIG_SECTION_SASL, 3561299742Sdim SVN_CONFIG_OPTION_USE_SASL, FALSE)); 3562299742Sdim 3563299742Sdim svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, 3564299742Sdim SVN_CONFIG_OPTION_MIN_SSF, "0"); 3565299742Sdim SVN_ERR(svn_cstring_atoui(&repository->min_ssf, val)); 3566299742Sdim 3567299742Sdim svn_config_get(cfg, &val, SVN_CONFIG_SECTION_SASL, 3568299742Sdim SVN_CONFIG_OPTION_MAX_SSF, "256"); 3569299742Sdim SVN_ERR(svn_cstring_atoui(&repository->max_ssf, val)); 3570251881Speter } 3571251881Speter#endif 3572251881Speter 3573251881Speter /* Use the repository UUID as the default realm. */ 3574299742Sdim SVN_ERR(svn_fs_get_uuid(repository->fs, &repository->realm, scratch_pool)); 3575299742Sdim svn_config_get(cfg, &repository->realm, SVN_CONFIG_SECTION_GENERAL, 3576299742Sdim SVN_CONFIG_OPTION_REALM, repository->realm); 3577299742Sdim repository->realm = apr_pstrdup(result_pool, repository->realm); 3578251881Speter 3579251881Speter /* Make sure it's possible for the client to authenticate. Note 3580251881Speter that this doesn't take into account any authz configuration read 3581251881Speter above, because we can't know about access it grants until paths 3582251881Speter are given by the client. */ 3583299742Sdim set_access(repository, cfg, read_only); 3584251881Speter 3585251881Speter /* Configure hook script environment variables. */ 3586299742Sdim svn_config_get(cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, 3587251881Speter SVN_CONFIG_OPTION_HOOKS_ENV, NULL); 3588251881Speter if (hooks_env) 3589299742Sdim hooks_env = svn_dirent_internal_style(hooks_env, scratch_pool); 3590251881Speter 3591309512Speter SVN_ERR(svn_repos_hooks_setenv(repository->repos, hooks_env, scratch_pool)); 3592299742Sdim repository->hooks_env = apr_pstrdup(result_pool, hooks_env); 3593299742Sdim 3594251881Speter return SVN_NO_ERROR; 3595251881Speter} 3596251881Speter 3597251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */ 3598251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool) 3599251881Speter{ 3600251881Speter /* Only offer EXTERNAL for connections tunneled over a login agent. */ 3601251881Speter if (!params->tunnel) 3602251881Speter return NULL; 3603251881Speter 3604251881Speter /* If a tunnel user was provided on the command line, use that. */ 3605251881Speter if (params->tunnel_user) 3606251881Speter return params->tunnel_user; 3607251881Speter 3608251881Speter return svn_user_get_name(pool); 3609251881Speter} 3610251881Speter 3611251881Speterstatic void 3612251881Speterfs_warning_func(void *baton, svn_error_t *err) 3613251881Speter{ 3614251881Speter fs_warning_baton_t *b = baton; 3615299742Sdim log_error(err, b->server); 3616251881Speter} 3617251881Speter 3618251881Speter/* Return the normalized repository-relative path for the given PATH 3619251881Speter * (may be a URL, full path or relative path) and fs contained in the 3620251881Speter * server baton BATON. Allocate the result in POOL. 3621251881Speter */ 3622251881Speterstatic const char * 3623251881Speterget_normalized_repo_rel_path(void *baton, 3624251881Speter const char *path, 3625251881Speter apr_pool_t *pool) 3626251881Speter{ 3627251881Speter server_baton_t *sb = baton; 3628251881Speter 3629251881Speter if (svn_path_is_url(path)) 3630251881Speter { 3631251881Speter /* This is a copyfrom URL. */ 3632299742Sdim path = svn_uri_skip_ancestor(sb->repository->repos_url, path, pool); 3633251881Speter path = svn_fspath__canonicalize(path, pool); 3634251881Speter } 3635251881Speter else 3636251881Speter { 3637251881Speter /* This is a base-relative path. */ 3638251881Speter if ((path)[0] != '/') 3639251881Speter /* Get an absolute path for use in the FS. */ 3640299742Sdim path = svn_fspath__join(sb->repository->fs_path->data, path, pool); 3641251881Speter } 3642251881Speter 3643251881Speter return path; 3644251881Speter} 3645251881Speter 3646251881Speter/* Get the revision root for REVISION in fs given by server baton BATON 3647251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM. 3648251881Speter * Use POOL for allocations. 3649251881Speter */ 3650251881Speterstatic svn_error_t * 3651251881Speterget_revision_root(svn_fs_root_t **fs_root, 3652251881Speter void *baton, 3653251881Speter svn_revnum_t revision, 3654251881Speter apr_pool_t *pool) 3655251881Speter{ 3656251881Speter server_baton_t *sb = baton; 3657251881Speter 3658251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 3659299742Sdim SVN_ERR(svn_fs_youngest_rev(&revision, sb->repository->fs, pool)); 3660251881Speter 3661299742Sdim SVN_ERR(svn_fs_revision_root(fs_root, sb->repository->fs, revision, pool)); 3662251881Speter 3663251881Speter return SVN_NO_ERROR; 3664251881Speter} 3665251881Speter 3666251881Speterstatic svn_error_t * 3667251881Speterfetch_props_func(apr_hash_t **props, 3668251881Speter void *baton, 3669251881Speter const char *path, 3670251881Speter svn_revnum_t base_revision, 3671251881Speter apr_pool_t *result_pool, 3672251881Speter apr_pool_t *scratch_pool) 3673251881Speter{ 3674251881Speter svn_fs_root_t *fs_root; 3675251881Speter svn_error_t *err; 3676251881Speter 3677251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3678251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3679251881Speter 3680251881Speter err = svn_fs_node_proplist(props, fs_root, path, result_pool); 3681251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3682251881Speter { 3683251881Speter svn_error_clear(err); 3684251881Speter *props = apr_hash_make(result_pool); 3685251881Speter return SVN_NO_ERROR; 3686251881Speter } 3687251881Speter else if (err) 3688251881Speter return svn_error_trace(err); 3689251881Speter 3690251881Speter return SVN_NO_ERROR; 3691251881Speter} 3692251881Speter 3693251881Speterstatic svn_error_t * 3694251881Speterfetch_kind_func(svn_node_kind_t *kind, 3695251881Speter void *baton, 3696251881Speter const char *path, 3697251881Speter svn_revnum_t base_revision, 3698251881Speter apr_pool_t *scratch_pool) 3699251881Speter{ 3700251881Speter svn_fs_root_t *fs_root; 3701251881Speter 3702251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3703251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3704251881Speter 3705251881Speter SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); 3706251881Speter 3707251881Speter return SVN_NO_ERROR; 3708251881Speter} 3709251881Speter 3710251881Speterstatic svn_error_t * 3711251881Speterfetch_base_func(const char **filename, 3712251881Speter void *baton, 3713251881Speter const char *path, 3714251881Speter svn_revnum_t base_revision, 3715251881Speter apr_pool_t *result_pool, 3716251881Speter apr_pool_t *scratch_pool) 3717251881Speter{ 3718251881Speter svn_stream_t *contents; 3719251881Speter svn_stream_t *file_stream; 3720251881Speter const char *tmp_filename; 3721251881Speter svn_fs_root_t *fs_root; 3722251881Speter svn_error_t *err; 3723251881Speter 3724251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3725251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3726251881Speter 3727251881Speter err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); 3728251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3729251881Speter { 3730251881Speter svn_error_clear(err); 3731251881Speter *filename = NULL; 3732251881Speter return SVN_NO_ERROR; 3733251881Speter } 3734251881Speter else if (err) 3735251881Speter return svn_error_trace(err); 3736251881Speter SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, 3737251881Speter svn_io_file_del_on_pool_cleanup, 3738251881Speter scratch_pool, scratch_pool)); 3739251881Speter SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); 3740251881Speter 3741251881Speter *filename = apr_pstrdup(result_pool, tmp_filename); 3742251881Speter 3743251881Speter return SVN_NO_ERROR; 3744251881Speter} 3745251881Speter 3746299742Sdimclient_info_t * 3747299742Sdimget_client_info(svn_ra_svn_conn_t *conn, 3748299742Sdim serve_params_t *params, 3749299742Sdim apr_pool_t *pool) 3750251881Speter{ 3751299742Sdim client_info_t *client_info = apr_pcalloc(pool, sizeof(*client_info)); 3752299742Sdim 3753299742Sdim client_info->tunnel = params->tunnel; 3754299742Sdim client_info->tunnel_user = get_tunnel_user(params, pool); 3755299742Sdim client_info->user = NULL; 3756299742Sdim client_info->authz_user = NULL; 3757299742Sdim client_info->remote_host = svn_ra_svn_conn_remote_host(conn); 3758299742Sdim 3759299742Sdim return client_info; 3760299742Sdim} 3761299742Sdim 3762299742Sdim/* Construct the server baton for CONN using PARAMS and return it in *BATON. 3763299742Sdim * It's lifetime is the same as that of CONN. SCRATCH_POOL 3764299742Sdim */ 3765299742Sdimstatic svn_error_t * 3766299742Sdimconstruct_server_baton(server_baton_t **baton, 3767299742Sdim svn_ra_svn_conn_t *conn, 3768299742Sdim serve_params_t *params, 3769299742Sdim apr_pool_t *scratch_pool) 3770299742Sdim{ 3771251881Speter svn_error_t *err, *io_err; 3772251881Speter apr_uint64_t ver; 3773299742Sdim const char *client_url, *ra_client_string, *client_string; 3774299742Sdim apr_array_header_t *caplist; 3775299742Sdim apr_pool_t *conn_pool = svn_ra_svn__get_pool(conn); 3776299742Sdim server_baton_t *b = apr_pcalloc(conn_pool, sizeof(*b)); 3777299742Sdim fs_warning_baton_t *warn_baton; 3778299742Sdim svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(scratch_pool); 3779251881Speter 3780299742Sdim b->repository = apr_pcalloc(conn_pool, sizeof(*b->repository)); 3781299742Sdim b->repository->username_case = params->username_case; 3782299742Sdim b->repository->base = params->base; 3783299742Sdim b->repository->pwdb = NULL; 3784299742Sdim b->repository->authzdb = NULL; 3785299742Sdim b->repository->realm = NULL; 3786299742Sdim b->repository->use_sasl = FALSE; 3787251881Speter 3788299742Sdim b->read_only = params->read_only; 3789299742Sdim b->pool = conn_pool; 3790299742Sdim b->vhost = params->vhost; 3791251881Speter 3792299742Sdim b->logger = params->logger; 3793299742Sdim b->client_info = get_client_info(conn, params, conn_pool); 3794299742Sdim 3795251881Speter /* Send greeting. We don't support version 1 any more, so we can 3796251881Speter * send an empty mechlist. */ 3797251881Speter if (params->compression_level > 0) 3798299742Sdim SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, 3799299742Sdim "nn()(wwwwwwwwwww)", 3800251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3801251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3802251881Speter SVN_RA_SVN_CAP_SVNDIFF1, 3803251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3804251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3805251881Speter SVN_RA_SVN_CAP_DEPTH, 3806251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3807251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3808251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3809251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3810251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3811251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3812251881Speter )); 3813251881Speter else 3814299742Sdim SVN_ERR(svn_ra_svn__write_cmd_response(conn, scratch_pool, 3815299742Sdim "nn()(wwwwwwwwww)", 3816251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3817251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3818251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3819251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3820251881Speter SVN_RA_SVN_CAP_DEPTH, 3821251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3822251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3823251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3824251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3825251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3826251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3827251881Speter )); 3828251881Speter 3829251881Speter /* Read client response, which we assume to be in version 2 format: 3830251881Speter * version, capability list, and client URL; then we do an auth 3831251881Speter * request. */ 3832299742Sdim SVN_ERR(svn_ra_svn__read_tuple(conn, scratch_pool, "nlc?c(?c)", 3833251881Speter &ver, &caplist, &client_url, 3834251881Speter &ra_client_string, 3835251881Speter &client_string)); 3836251881Speter if (ver != 2) 3837251881Speter return SVN_NO_ERROR; 3838251881Speter 3839299742Sdim client_url = svn_uri_canonicalize(client_url, conn_pool); 3840251881Speter SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); 3841251881Speter 3842251881Speter /* All released versions of Subversion support edit-pipeline, 3843251881Speter * so we do not accept connections from clients that do not. */ 3844251881Speter if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) 3845251881Speter return SVN_NO_ERROR; 3846251881Speter 3847251881Speter /* find_repos needs the capabilities as a list of words (eventually 3848251881Speter they get handed to the start-commit hook). While we could add a 3849251881Speter new interface to re-retrieve them from conn and convert the 3850251881Speter result to a list, it's simpler to just convert caplist by hand 3851251881Speter here, since we already have it and turning 'svn_ra_svn_item_t's 3852251881Speter into 'const char *'s is pretty easy. 3853251881Speter 3854251881Speter We only record capabilities we care about. The client may report 3855251881Speter more (because it doesn't know what the server cares about). */ 3856251881Speter { 3857251881Speter int i; 3858251881Speter svn_ra_svn_item_t *item; 3859251881Speter 3860299742Sdim b->repository->capabilities = apr_array_make(conn_pool, 1, 3861299742Sdim sizeof(const char *)); 3862251881Speter for (i = 0; i < caplist->nelts; i++) 3863251881Speter { 3864251881Speter item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t); 3865251881Speter /* ra_svn_set_capabilities() already type-checked for us */ 3866251881Speter if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0) 3867251881Speter { 3868299742Sdim APR_ARRAY_PUSH(b->repository->capabilities, const char *) 3869251881Speter = SVN_RA_CAPABILITY_MERGEINFO; 3870251881Speter } 3871251881Speter /* Save for operational log. */ 3872251881Speter if (cap_log->len > 0) 3873251881Speter svn_stringbuf_appendcstr(cap_log, " "); 3874251881Speter svn_stringbuf_appendcstr(cap_log, item->u.word); 3875251881Speter } 3876251881Speter } 3877251881Speter 3878299742Sdim err = handle_config_error(find_repos(client_url, params->root, b->vhost, 3879299742Sdim b->read_only, params->cfg, 3880299742Sdim b->repository, params->config_pool, 3881299742Sdim params->authz_pool, params->fs_config, 3882299742Sdim conn_pool, scratch_pool), 3883299742Sdim b); 3884251881Speter if (!err) 3885251881Speter { 3886299742Sdim if (b->repository->anon_access == NO_ACCESS 3887299742Sdim && (b->repository->auth_access == NO_ACCESS 3888299742Sdim || (!b->client_info->tunnel_user && !b->repository->pwdb 3889299742Sdim && !b->repository->use_sasl))) 3890251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3891299742Sdim "No access allowed to this repository", 3892299742Sdim b); 3893251881Speter } 3894299742Sdim if (!err) 3895299742Sdim { 3896299742Sdim SVN_ERR(auth_request(conn, scratch_pool, b, READ_ACCESS, FALSE)); 3897299742Sdim if (current_access(b) == NO_ACCESS) 3898299742Sdim err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3899299742Sdim "Not authorized for access", b); 3900299742Sdim } 3901251881Speter if (err) 3902251881Speter { 3903299742Sdim log_error(err, b); 3904299742Sdim io_err = svn_ra_svn__write_cmd_failure(conn, scratch_pool, err); 3905251881Speter svn_error_clear(err); 3906251881Speter SVN_ERR(io_err); 3907299742Sdim return svn_ra_svn__flush(conn, scratch_pool); 3908251881Speter } 3909251881Speter 3910299742Sdim SVN_ERR(svn_fs_get_uuid(b->repository->fs, &b->repository->uuid, 3911299742Sdim conn_pool)); 3912251881Speter 3913251881Speter /* We can't claim mergeinfo capability until we know whether the 3914251881Speter repository supports mergeinfo (i.e., is not a 1.4 repository), 3915251881Speter but we don't get the repository url from the client until after 3916251881Speter we've already sent the initial list of server capabilities. So 3917251881Speter we list repository capabilities here, in our first response after 3918251881Speter the client has sent the url. */ 3919251881Speter { 3920251881Speter svn_boolean_t supports_mergeinfo; 3921299742Sdim SVN_ERR(svn_repos_has_capability(b->repository->repos, 3922299742Sdim &supports_mergeinfo, 3923299742Sdim SVN_REPOS_CAPABILITY_MERGEINFO, 3924299742Sdim scratch_pool)); 3925251881Speter 3926299742Sdim SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "w(cc(!", 3927299742Sdim "success", b->repository->uuid, 3928299742Sdim b->repository->repos_url)); 3929251881Speter if (supports_mergeinfo) 3930299742Sdim SVN_ERR(svn_ra_svn__write_word(conn, scratch_pool, 3931299742Sdim SVN_RA_SVN_CAP_MERGEINFO)); 3932299742Sdim SVN_ERR(svn_ra_svn__write_tuple(conn, scratch_pool, "!))")); 3933299742Sdim SVN_ERR(svn_ra_svn__flush(conn, scratch_pool)); 3934251881Speter } 3935251881Speter 3936299742Sdim /* Log the open. */ 3937299742Sdim if (ra_client_string == NULL || ra_client_string[0] == '\0') 3938299742Sdim ra_client_string = "-"; 3939299742Sdim else 3940299742Sdim ra_client_string = svn_path_uri_encode(ra_client_string, scratch_pool); 3941299742Sdim if (client_string == NULL || client_string[0] == '\0') 3942299742Sdim client_string = "-"; 3943299742Sdim else 3944299742Sdim client_string = svn_path_uri_encode(client_string, scratch_pool); 3945299742Sdim SVN_ERR(log_command(b, conn, scratch_pool, 3946299742Sdim "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s", 3947299742Sdim ver, cap_log->data, 3948299742Sdim svn_path_uri_encode(b->repository->fs_path->data, 3949299742Sdim scratch_pool), 3950299742Sdim ra_client_string, client_string)); 3951299742Sdim 3952299742Sdim warn_baton = apr_pcalloc(conn_pool, sizeof(*warn_baton)); 3953299742Sdim warn_baton->server = b; 3954299742Sdim warn_baton->conn = conn; 3955299742Sdim svn_fs_set_warning_func(b->repository->fs, fs_warning_func, warn_baton); 3956299742Sdim 3957251881Speter /* Set up editor shims. */ 3958251881Speter { 3959251881Speter svn_delta_shim_callbacks_t *callbacks = 3960299742Sdim svn_delta_shim_callbacks_default(conn_pool); 3961251881Speter 3962251881Speter callbacks->fetch_base_func = fetch_base_func; 3963251881Speter callbacks->fetch_props_func = fetch_props_func; 3964251881Speter callbacks->fetch_kind_func = fetch_kind_func; 3965299742Sdim callbacks->fetch_baton = b; 3966251881Speter 3967251881Speter SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks)); 3968251881Speter } 3969251881Speter 3970299742Sdim *baton = b; 3971299742Sdim 3972299742Sdim return SVN_NO_ERROR; 3973251881Speter} 3974299742Sdim 3975299742Sdimsvn_error_t * 3976299742Sdimserve_interruptable(svn_boolean_t *terminate_p, 3977299742Sdim connection_t *connection, 3978299742Sdim svn_boolean_t (* is_busy)(connection_t *), 3979299742Sdim apr_pool_t *pool) 3980299742Sdim{ 3981299742Sdim svn_boolean_t terminate = FALSE; 3982299742Sdim svn_error_t *err = NULL; 3983299742Sdim const svn_ra_svn_cmd_entry_t *command; 3984299742Sdim apr_pool_t *iterpool = svn_pool_create(pool); 3985299742Sdim 3986299742Sdim /* Prepare command parser. */ 3987299742Sdim apr_hash_t *cmd_hash = apr_hash_make(pool); 3988299742Sdim for (command = main_commands; command->cmdname; command++) 3989299742Sdim svn_hash_sets(cmd_hash, command->cmdname, command); 3990299742Sdim 3991299742Sdim /* Auto-initialize connection */ 3992299742Sdim if (! connection->conn) 3993299742Sdim { 3994299742Sdim apr_status_t ar; 3995299742Sdim 3996299742Sdim /* Enable TCP keep-alives on the socket so we time out when 3997299742Sdim * the connection breaks due to network-layer problems. 3998299742Sdim * If the peer has dropped the connection due to a network partition 3999299742Sdim * or a crash, or if the peer no longer considers the connection 4000299742Sdim * valid because we are behind a NAT and our public IP has changed, 4001299742Sdim * it will respond to the keep-alive probe with a RST instead of an 4002299742Sdim * acknowledgment segment, which will cause svn to abort the session 4003299742Sdim * even while it is currently blocked waiting for data from the peer. */ 4004299742Sdim ar = apr_socket_opt_set(connection->usock, APR_SO_KEEPALIVE, 1); 4005299742Sdim if (ar) 4006299742Sdim { 4007299742Sdim /* It's not a fatal error if we cannot enable keep-alives. */ 4008299742Sdim } 4009299742Sdim 4010299742Sdim /* create the connection, configure ports etc. */ 4011299742Sdim connection->conn 4012299742Sdim = svn_ra_svn_create_conn4(connection->usock, NULL, NULL, 4013299742Sdim connection->params->compression_level, 4014299742Sdim connection->params->zero_copy_limit, 4015299742Sdim connection->params->error_check_interval, 4016299742Sdim connection->pool); 4017299742Sdim 4018299742Sdim /* Construct server baton and open the repository for the first time. */ 4019299742Sdim err = construct_server_baton(&connection->baton, connection->conn, 4020299742Sdim connection->params, pool); 4021299742Sdim } 4022299742Sdim 4023299742Sdim /* If we can't access the repo for some reason, end this connection. */ 4024299742Sdim if (err) 4025299742Sdim terminate = TRUE; 4026299742Sdim 4027299742Sdim /* Process incoming commands. */ 4028299742Sdim while (!terminate && !err) 4029299742Sdim { 4030299742Sdim svn_pool_clear(iterpool); 4031299742Sdim if (is_busy && is_busy(connection)) 4032299742Sdim { 4033299742Sdim svn_boolean_t has_command; 4034299742Sdim 4035299742Sdim /* If the server is busy, execute just one command and only if 4036299742Sdim * there is one currently waiting in our receive buffers. 4037299742Sdim */ 4038299742Sdim err = svn_ra_svn__has_command(&has_command, &terminate, 4039299742Sdim connection->conn, iterpool); 4040299742Sdim if (!err && has_command) 4041299742Sdim err = svn_ra_svn__handle_command(&terminate, cmd_hash, 4042299742Sdim connection->baton, 4043299742Sdim connection->conn, 4044299742Sdim FALSE, iterpool); 4045299742Sdim 4046299742Sdim break; 4047299742Sdim } 4048299742Sdim else 4049299742Sdim { 4050299742Sdim /* The server is not busy, thus let's serve whichever command 4051299742Sdim * comes in next and whenever it comes in. This requires the 4052299742Sdim * busy() callback test to return TRUE while there are still some 4053299742Sdim * resources left. 4054299742Sdim */ 4055299742Sdim err = svn_ra_svn__handle_command(&terminate, cmd_hash, 4056299742Sdim connection->baton, 4057299742Sdim connection->conn, 4058299742Sdim FALSE, iterpool); 4059299742Sdim } 4060299742Sdim } 4061299742Sdim 4062299742Sdim /* error or normal end of session. Close the connection */ 4063299742Sdim svn_pool_destroy(iterpool); 4064299742Sdim if (terminate_p) 4065299742Sdim *terminate_p = terminate; 4066299742Sdim 4067299742Sdim return svn_error_trace(err); 4068299742Sdim} 4069299742Sdim 4070299742Sdimsvn_error_t *serve(svn_ra_svn_conn_t *conn, 4071299742Sdim serve_params_t *params, 4072299742Sdim apr_pool_t *pool) 4073299742Sdim{ 4074299742Sdim server_baton_t *baton = NULL; 4075299742Sdim 4076299742Sdim SVN_ERR(construct_server_baton(&baton, conn, params, pool)); 4077299742Sdim return svn_ra_svn__handle_commands2(conn, pool, main_commands, baton, FALSE); 4078299742Sdim} 4079