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" 64251881Speter 65251881Spetertypedef struct commit_callback_baton_t { 66251881Speter apr_pool_t *pool; 67251881Speter svn_revnum_t *new_rev; 68251881Speter const char **date; 69251881Speter const char **author; 70251881Speter const char **post_commit_err; 71251881Speter} commit_callback_baton_t; 72251881Speter 73251881Spetertypedef struct report_driver_baton_t { 74251881Speter server_baton_t *sb; 75251881Speter const char *repos_url; /* Decoded repository URL. */ 76251881Speter void *report_baton; 77251881Speter svn_error_t *err; 78251881Speter /* so update() can distinguish checkout from update in logging */ 79251881Speter int entry_counter; 80251881Speter svn_boolean_t only_empty_entries; 81251881Speter /* for diff() logging */ 82251881Speter svn_revnum_t *from_rev; 83251881Speter} report_driver_baton_t; 84251881Speter 85251881Spetertypedef struct log_baton_t { 86251881Speter const char *fs_path; 87251881Speter svn_ra_svn_conn_t *conn; 88251881Speter int stack_depth; 89251881Speter} log_baton_t; 90251881Speter 91251881Spetertypedef struct file_revs_baton_t { 92251881Speter svn_ra_svn_conn_t *conn; 93251881Speter apr_pool_t *pool; /* Pool provided in the handler call. */ 94251881Speter} file_revs_baton_t; 95251881Speter 96251881Spetertypedef struct fs_warning_baton_t { 97251881Speter server_baton_t *server; 98251881Speter svn_ra_svn_conn_t *conn; 99251881Speter apr_pool_t *pool; 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 107251881Speter/* Write LEN bytes of ERRSTR to LOG_FILE with svn_io_file_write(). */ 108251881Speterstatic svn_error_t * 109251881Speterlog_write(apr_file_t *log_file, const char *errstr, apr_size_t len, 110251881Speter apr_pool_t *pool) 111251881Speter{ 112251881Speter return svn_io_file_write(log_file, errstr, &len, pool); 113251881Speter} 114251881Speter 115251881Spetervoid 116251881Speterlog_error(svn_error_t *err, apr_file_t *log_file, const char *remote_host, 117251881Speter const char *user, const char *repos, apr_pool_t *pool) 118251881Speter{ 119251881Speter const char *timestr, *continuation; 120251881Speter char errbuf[256]; 121251881Speter /* 8192 from MAX_STRING_LEN in from httpd-2.2.4/include/httpd.h */ 122251881Speter char errstr[8192]; 123251881Speter 124251881Speter if (err == SVN_NO_ERROR) 125251881Speter return; 126251881Speter 127251881Speter if (log_file == NULL) 128251881Speter return; 129251881Speter 130251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 131251881Speter remote_host = (remote_host ? remote_host : "-"); 132251881Speter user = (user ? user : "-"); 133251881Speter repos = (repos ? repos : "-"); 134251881Speter 135251881Speter continuation = ""; 136251881Speter while (err != NULL) 137251881Speter { 138251881Speter const char *message = svn_err_best_message(err, errbuf, sizeof(errbuf)); 139251881Speter /* based on httpd-2.2.4/server/log.c:log_error_core */ 140251881Speter apr_size_t len = apr_snprintf(errstr, sizeof(errstr), 141251881Speter "%" APR_PID_T_FMT 142251881Speter " %s %s %s %s ERR%s %s %ld %d ", 143251881Speter getpid(), timestr, remote_host, user, 144251881Speter repos, continuation, 145251881Speter err->file ? err->file : "-", err->line, 146251881Speter err->apr_err); 147251881Speter 148251881Speter len += escape_errorlog_item(errstr + len, message, 149251881Speter sizeof(errstr) - len); 150251881Speter /* Truncate for the terminator (as apr_snprintf does) */ 151251881Speter if (len > sizeof(errstr) - sizeof(APR_EOL_STR)) { 152251881Speter len = sizeof(errstr) - sizeof(APR_EOL_STR); 153251881Speter } 154251881Speter strcpy(errstr + len, APR_EOL_STR); 155251881Speter len += strlen(APR_EOL_STR); 156251881Speter svn_error_clear(log_write(log_file, errstr, len, pool)); 157251881Speter 158251881Speter continuation = "-"; 159251881Speter err = err->child; 160251881Speter } 161251881Speter} 162251881Speter 163251881Speter/* Call log_error with log_file, remote_host, user, and repos 164251881Speter arguments from SERVER and CONN. */ 165251881Speterstatic void 166251881Speterlog_server_error(svn_error_t *err, server_baton_t *server, 167251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool) 168251881Speter{ 169251881Speter log_error(err, server->log_file, svn_ra_svn_conn_remote_host(conn), 170251881Speter server->user, server->repos_name, pool); 171251881Speter} 172251881Speter 173251881Speter/* svn_error_create() a new error, log_server_error() it, and 174251881Speter return it. */ 175251881Speterstatic svn_error_t * 176251881Spetererror_create_and_log(apr_status_t apr_err, svn_error_t *child, 177251881Speter const char *message, server_baton_t *server, 178251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool) 179251881Speter{ 180251881Speter svn_error_t *err = svn_error_create(apr_err, child, message); 181251881Speter log_server_error(err, server, conn, pool); 182251881Speter return err; 183251881Speter} 184251881Speter 185251881Speter/* Log a failure ERR, transmit ERR back to the client (as part of a 186251881Speter "failure" notification), consume ERR, and flush the connection. */ 187251881Speterstatic svn_error_t * 188251881Speterlog_fail_and_flush(svn_error_t *err, server_baton_t *server, 189251881Speter svn_ra_svn_conn_t *conn, apr_pool_t *pool) 190251881Speter{ 191251881Speter svn_error_t *io_err; 192251881Speter 193251881Speter log_server_error(err, server, conn, pool); 194251881Speter io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 195251881Speter svn_error_clear(err); 196251881Speter SVN_ERR(io_err); 197251881Speter return svn_ra_svn__flush(conn, pool); 198251881Speter} 199251881Speter 200251881Speter/* Log a client command. */ 201251881Speterstatic svn_error_t *log_command(server_baton_t *b, 202251881Speter svn_ra_svn_conn_t *conn, 203251881Speter apr_pool_t *pool, 204251881Speter const char *fmt, ...) 205251881Speter{ 206251881Speter const char *remote_host, *timestr, *log, *line; 207251881Speter va_list ap; 208251881Speter apr_size_t nbytes; 209251881Speter 210251881Speter if (b->log_file == NULL) 211251881Speter return SVN_NO_ERROR; 212251881Speter 213251881Speter remote_host = svn_ra_svn_conn_remote_host(conn); 214251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 215251881Speter 216251881Speter va_start(ap, fmt); 217251881Speter log = apr_pvsprintf(pool, fmt, ap); 218251881Speter va_end(ap); 219251881Speter 220251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 221251881Speter " %s %s %s %s %s" APR_EOL_STR, 222251881Speter getpid(), timestr, 223251881Speter (remote_host ? remote_host : "-"), 224251881Speter (b->user ? b->user : "-"), b->repos_name, log); 225251881Speter nbytes = strlen(line); 226251881Speter 227251881Speter return log_write(b->log_file, line, nbytes, pool); 228251881Speter} 229251881Speter 230251881Speter/* Log an authz failure */ 231251881Speterstatic svn_error_t * 232251881Speterlog_authz_denied(const char *path, 233251881Speter svn_repos_authz_access_t required, 234251881Speter server_baton_t *b, 235251881Speter svn_ra_svn_conn_t *conn, 236251881Speter apr_pool_t *pool) 237251881Speter{ 238251881Speter const char *timestr, *remote_host, *line; 239251881Speter 240251881Speter if (b->log_file == NULL) 241251881Speter return SVN_NO_ERROR; 242251881Speter 243251881Speter if (!b->user) 244251881Speter return SVN_NO_ERROR; 245251881Speter 246251881Speter timestr = svn_time_to_cstring(apr_time_now(), pool); 247251881Speter remote_host = svn_ra_svn_conn_remote_host(conn); 248251881Speter 249251881Speter line = apr_psprintf(pool, "%" APR_PID_T_FMT 250251881Speter " %s %s %s %s Authorization Failed %s%s %s" APR_EOL_STR, 251251881Speter getpid(), timestr, 252251881Speter (remote_host ? remote_host : "-"), 253251881Speter (b->user ? b->user : "-"), 254251881Speter b->repos_name, 255251881Speter (required & svn_authz_recursive ? "recursive " : ""), 256251881Speter (required & svn_authz_write ? "write" : "read"), 257251881Speter (path && path[0] ? path : "/")); 258251881Speter 259251881Speter return log_write(b->log_file, line, strlen(line), pool); 260251881Speter} 261251881Speter 262251881Speter 263251881Spetersvn_error_t *load_pwdb_config(server_baton_t *server, 264251881Speter svn_ra_svn_conn_t *conn, 265251881Speter apr_pool_t *pool) 266251881Speter{ 267251881Speter const char *pwdb_path; 268251881Speter svn_error_t *err; 269251881Speter 270251881Speter svn_config_get(server->cfg, &pwdb_path, SVN_CONFIG_SECTION_GENERAL, 271251881Speter SVN_CONFIG_OPTION_PASSWORD_DB, NULL); 272251881Speter 273251881Speter server->pwdb = NULL; 274251881Speter if (pwdb_path) 275251881Speter { 276251881Speter pwdb_path = svn_dirent_internal_style(pwdb_path, pool); 277251881Speter pwdb_path = svn_dirent_join(server->base, pwdb_path, pool); 278251881Speter 279251881Speter err = svn_config_read3(&server->pwdb, pwdb_path, TRUE, 280251881Speter FALSE, FALSE, pool); 281251881Speter if (err) 282251881Speter { 283251881Speter log_server_error(err, server, conn, pool); 284251881Speter 285251881Speter /* Because it may be possible to read the pwdb file with some 286251881Speter access methods and not others, ignore errors reading the pwdb 287251881Speter file and just don't present password authentication as an 288251881Speter option. Also, some authentications (e.g. --tunnel) can 289251881Speter proceed without it anyway. 290251881Speter 291251881Speter ### Not entirely sure why SVN_ERR_BAD_FILENAME is checked 292251881Speter ### for here. That seems to have been introduced in r856914, 293251881Speter ### and only in r870942 was the APR_EACCES check introduced. */ 294251881Speter if (err->apr_err != SVN_ERR_BAD_FILENAME 295251881Speter && ! APR_STATUS_IS_EACCES(err->apr_err)) 296251881Speter { 297251881Speter /* Now that we've logged the error, clear it and return a 298251881Speter * nice, generic error to the user: 299251881Speter * http://subversion.tigris.org/issues/show_bug.cgi?id=2271 */ 300251881Speter svn_error_clear(err); 301251881Speter return svn_error_create(SVN_ERR_AUTHN_FAILED, NULL, NULL); 302251881Speter } 303251881Speter else 304251881Speter /* Ignore SVN_ERR_BAD_FILENAME and APR_EACCES and proceed. */ 305251881Speter svn_error_clear(err); 306251881Speter } 307251881Speter } 308251881Speter 309251881Speter return SVN_NO_ERROR; 310251881Speter} 311251881Speter 312251881Speter/* Canonicalize *ACCESS_FILE based on the type of argument. Results are 313251881Speter * placed in *ACCESS_FILE. SERVER baton is used to convert relative paths to 314251881Speter * absolute paths rooted at the server root. REPOS_ROOT is used to calculate 315251881Speter * an absolute URL for repos-relative URLs. */ 316251881Speterstatic svn_error_t * 317251881Spetercanonicalize_access_file(const char **access_file, server_baton_t *server, 318251881Speter const char *repos_root, apr_pool_t *pool) 319251881Speter{ 320251881Speter if (svn_path_is_url(*access_file)) 321251881Speter { 322251881Speter *access_file = svn_uri_canonicalize(*access_file, pool); 323251881Speter } 324251881Speter else if (svn_path_is_repos_relative_url(*access_file)) 325251881Speter { 326251881Speter const char *repos_root_url; 327251881Speter 328251881Speter SVN_ERR(svn_uri_get_file_url_from_dirent(&repos_root_url, repos_root, 329251881Speter pool)); 330251881Speter SVN_ERR(svn_path_resolve_repos_relative_url(access_file, *access_file, 331251881Speter repos_root_url, pool)); 332251881Speter *access_file = svn_uri_canonicalize(*access_file, pool); 333251881Speter } 334251881Speter else 335251881Speter { 336251881Speter *access_file = svn_dirent_internal_style(*access_file, pool); 337251881Speter *access_file = svn_dirent_join(server->base, *access_file, pool); 338251881Speter } 339251881Speter 340251881Speter return SVN_NO_ERROR; 341251881Speter} 342251881Speter 343251881Spetersvn_error_t *load_authz_config(server_baton_t *server, 344251881Speter svn_ra_svn_conn_t *conn, 345251881Speter const char *repos_root, 346251881Speter apr_pool_t *pool) 347251881Speter{ 348251881Speter const char *authzdb_path; 349251881Speter const char *groupsdb_path; 350251881Speter svn_error_t *err; 351251881Speter 352251881Speter /* Read authz configuration. */ 353251881Speter svn_config_get(server->cfg, &authzdb_path, SVN_CONFIG_SECTION_GENERAL, 354251881Speter SVN_CONFIG_OPTION_AUTHZ_DB, NULL); 355251881Speter 356251881Speter svn_config_get(server->cfg, &groupsdb_path, SVN_CONFIG_SECTION_GENERAL, 357251881Speter SVN_CONFIG_OPTION_GROUPS_DB, NULL); 358251881Speter 359251881Speter if (authzdb_path) 360251881Speter { 361251881Speter const char *case_force_val; 362251881Speter 363251881Speter /* Canonicalize and add the base onto the authzdb_path (if needed). */ 364251881Speter err = canonicalize_access_file(&authzdb_path, server, 365251881Speter repos_root, pool); 366251881Speter 367251881Speter /* Same for the groupsdb_path if it is present. */ 368251881Speter if (groupsdb_path && !err) 369251881Speter err = canonicalize_access_file(&groupsdb_path, server, 370251881Speter repos_root, pool); 371251881Speter 372251881Speter if (!err) 373251881Speter err = svn_repos_authz_read2(&server->authzdb, authzdb_path, 374251881Speter groupsdb_path, TRUE, pool); 375251881Speter 376251881Speter if (err) 377251881Speter { 378251881Speter log_server_error(err, server, conn, pool); 379251881Speter svn_error_clear(err); 380251881Speter return svn_error_create(SVN_ERR_AUTHZ_INVALID_CONFIG, NULL, NULL); 381251881Speter } 382251881Speter 383251881Speter /* Are we going to be case-normalizing usernames when we consult 384251881Speter * this authz file? */ 385251881Speter svn_config_get(server->cfg, &case_force_val, SVN_CONFIG_SECTION_GENERAL, 386251881Speter SVN_CONFIG_OPTION_FORCE_USERNAME_CASE, NULL); 387251881Speter if (case_force_val) 388251881Speter { 389251881Speter if (strcmp(case_force_val, "upper") == 0) 390251881Speter server->username_case = CASE_FORCE_UPPER; 391251881Speter else if (strcmp(case_force_val, "lower") == 0) 392251881Speter server->username_case = CASE_FORCE_LOWER; 393251881Speter else 394251881Speter server->username_case = CASE_ASIS; 395251881Speter } 396251881Speter } 397251881Speter else 398251881Speter { 399251881Speter server->authzdb = NULL; 400251881Speter server->username_case = CASE_ASIS; 401251881Speter } 402251881Speter 403251881Speter return SVN_NO_ERROR; 404251881Speter} 405251881Speter 406251881Speter/* Set *FS_PATH to the portion of URL that is the path within the 407251881Speter repository, if URL is inside REPOS_URL (if URL is not inside 408251881Speter REPOS_URL, then error, with the effect on *FS_PATH undefined). 409251881Speter 410251881Speter If the resultant fs path would be the empty string (i.e., URL and 411251881Speter REPOS_URL are the same), then set *FS_PATH to "/". 412251881Speter 413251881Speter Assume that REPOS_URL and URL are already URI-decoded. */ 414251881Speterstatic svn_error_t *get_fs_path(const char *repos_url, const char *url, 415251881Speter const char **fs_path) 416251881Speter{ 417251881Speter apr_size_t len; 418251881Speter 419251881Speter len = strlen(repos_url); 420251881Speter if (strncmp(url, repos_url, len) != 0) 421251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 422251881Speter "'%s' is not the same repository as '%s'", 423251881Speter url, repos_url); 424251881Speter *fs_path = url + len; 425251881Speter if (! **fs_path) 426251881Speter *fs_path = "/"; 427251881Speter 428251881Speter return SVN_NO_ERROR; 429251881Speter} 430251881Speter 431251881Speter/* --- AUTHENTICATION AND AUTHORIZATION FUNCTIONS --- */ 432251881Speter 433251881Speter/* Convert TEXT to upper case if TO_UPPERCASE is TRUE, else 434251881Speter converts it to lower case. */ 435251881Speterstatic void convert_case(char *text, svn_boolean_t to_uppercase) 436251881Speter{ 437251881Speter char *c = text; 438251881Speter while (*c) 439251881Speter { 440251881Speter *c = (char)(to_uppercase ? apr_toupper(*c) : apr_tolower(*c)); 441251881Speter ++c; 442251881Speter } 443251881Speter} 444251881Speter 445251881Speter/* Set *ALLOWED to TRUE if PATH is accessible in the REQUIRED mode to 446251881Speter the user described in BATON according to the authz rules in BATON. 447251881Speter Use POOL for temporary allocations only. If no authz rules are 448251881Speter present in BATON, grant access by default. */ 449251881Speterstatic svn_error_t *authz_check_access(svn_boolean_t *allowed, 450251881Speter const char *path, 451251881Speter svn_repos_authz_access_t required, 452251881Speter server_baton_t *b, 453251881Speter svn_ra_svn_conn_t *conn, 454251881Speter apr_pool_t *pool) 455251881Speter{ 456251881Speter /* If authz cannot be performed, grant access. This is NOT the same 457251881Speter as the default policy when authz is performed on a path with no 458251881Speter rules. In the latter case, the default is to deny access, and is 459251881Speter set by svn_repos_authz_check_access. */ 460251881Speter if (!b->authzdb) 461251881Speter { 462251881Speter *allowed = TRUE; 463251881Speter return SVN_NO_ERROR; 464251881Speter } 465251881Speter 466251881Speter /* If the authz request is for the empty path (ie. ""), replace it 467251881Speter with the root path. This happens because of stripping done at 468251881Speter various levels in svnserve that remove the leading / on an 469251881Speter absolute path. Passing such a malformed path to the authz 470251881Speter routines throws them into an infinite loop and makes them miss 471251881Speter ACLs. */ 472251881Speter if (path) 473251881Speter path = svn_fspath__canonicalize(path, pool); 474251881Speter 475251881Speter /* If we have a username, and we've not yet used it + any username 476251881Speter case normalization that might be requested to determine "the 477251881Speter username we used for authz purposes", do so now. */ 478251881Speter if (b->user && (! b->authz_user)) 479251881Speter { 480251881Speter char *authz_user = apr_pstrdup(b->pool, b->user); 481251881Speter if (b->username_case == CASE_FORCE_UPPER) 482251881Speter convert_case(authz_user, TRUE); 483251881Speter else if (b->username_case == CASE_FORCE_LOWER) 484251881Speter convert_case(authz_user, FALSE); 485251881Speter b->authz_user = authz_user; 486251881Speter } 487251881Speter 488251881Speter SVN_ERR(svn_repos_authz_check_access(b->authzdb, b->authz_repos_name, 489251881Speter path, b->authz_user, required, 490251881Speter allowed, pool)); 491251881Speter if (!*allowed) 492251881Speter SVN_ERR(log_authz_denied(path, required, b, conn, pool)); 493251881Speter 494251881Speter return SVN_NO_ERROR; 495251881Speter} 496251881Speter 497251881Speter/* Set *ALLOWED to TRUE if PATH is readable by the user described in 498251881Speter * BATON. Use POOL for temporary allocations only. ROOT is not used. 499251881Speter * Implements the svn_repos_authz_func_t interface. 500251881Speter */ 501251881Speterstatic svn_error_t *authz_check_access_cb(svn_boolean_t *allowed, 502251881Speter svn_fs_root_t *root, 503251881Speter const char *path, 504251881Speter void *baton, 505251881Speter apr_pool_t *pool) 506251881Speter{ 507251881Speter authz_baton_t *sb = baton; 508251881Speter 509251881Speter return authz_check_access(allowed, path, svn_authz_read, 510251881Speter sb->server, sb->conn, pool); 511251881Speter} 512251881Speter 513251881Speter/* If authz is enabled in the specified BATON, return a read authorization 514251881Speter function. Otherwise, return NULL. */ 515251881Speterstatic svn_repos_authz_func_t authz_check_access_cb_func(server_baton_t *baton) 516251881Speter{ 517251881Speter if (baton->authzdb) 518251881Speter return authz_check_access_cb; 519251881Speter return NULL; 520251881Speter} 521251881Speter 522251881Speter/* Set *ALLOWED to TRUE if the REQUIRED access to PATH is granted, 523251881Speter * according to the state in BATON. Use POOL for temporary 524251881Speter * allocations only. ROOT is not used. Implements the 525251881Speter * svn_repos_authz_callback_t interface. 526251881Speter */ 527251881Speterstatic svn_error_t *authz_commit_cb(svn_repos_authz_access_t required, 528251881Speter svn_boolean_t *allowed, 529251881Speter svn_fs_root_t *root, 530251881Speter const char *path, 531251881Speter void *baton, 532251881Speter apr_pool_t *pool) 533251881Speter{ 534251881Speter authz_baton_t *sb = baton; 535251881Speter 536251881Speter return authz_check_access(allowed, path, required, 537251881Speter sb->server, sb->conn, pool); 538251881Speter} 539251881Speter 540251881Speter 541251881Speterenum access_type get_access(server_baton_t *b, enum authn_type auth) 542251881Speter{ 543251881Speter const char *var = (auth == AUTHENTICATED) ? SVN_CONFIG_OPTION_AUTH_ACCESS : 544251881Speter SVN_CONFIG_OPTION_ANON_ACCESS; 545251881Speter const char *val, *def = (auth == AUTHENTICATED) ? "write" : "read"; 546251881Speter enum access_type result; 547251881Speter 548251881Speter svn_config_get(b->cfg, &val, SVN_CONFIG_SECTION_GENERAL, var, def); 549251881Speter result = (strcmp(val, "write") == 0 ? WRITE_ACCESS : 550251881Speter strcmp(val, "read") == 0 ? READ_ACCESS : NO_ACCESS); 551251881Speter return (result == WRITE_ACCESS && b->read_only) ? READ_ACCESS : result; 552251881Speter} 553251881Speter 554251881Speterstatic enum access_type current_access(server_baton_t *b) 555251881Speter{ 556251881Speter return get_access(b, (b->user) ? AUTHENTICATED : UNAUTHENTICATED); 557251881Speter} 558251881Speter 559251881Speter/* Send authentication mechs for ACCESS_TYPE to the client. If NEEDS_USERNAME 560251881Speter is true, don't send anonymous mech even if that would give the desired 561251881Speter access. */ 562251881Speterstatic svn_error_t *send_mechs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 563251881Speter server_baton_t *b, enum access_type required, 564251881Speter svn_boolean_t needs_username) 565251881Speter{ 566251881Speter if (!needs_username && get_access(b, UNAUTHENTICATED) >= required) 567251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "ANONYMOUS")); 568251881Speter if (b->tunnel_user && get_access(b, AUTHENTICATED) >= required) 569251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "EXTERNAL")); 570251881Speter if (b->pwdb && get_access(b, AUTHENTICATED) >= required) 571251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, "CRAM-MD5")); 572251881Speter return SVN_NO_ERROR; 573251881Speter} 574251881Speter 575251881Speter/* Context for cleanup handler. */ 576251881Speterstruct cleanup_fs_access_baton 577251881Speter{ 578251881Speter svn_fs_t *fs; 579251881Speter apr_pool_t *pool; 580251881Speter}; 581251881Speter 582251881Speter/* Pool cleanup handler. Make sure fs's access_t points to NULL when 583251881Speter the command pool is destroyed. */ 584251881Speterstatic apr_status_t cleanup_fs_access(void *data) 585251881Speter{ 586251881Speter svn_error_t *serr; 587251881Speter struct cleanup_fs_access_baton *baton = data; 588251881Speter 589251881Speter serr = svn_fs_set_access(baton->fs, NULL); 590251881Speter if (serr) 591251881Speter { 592251881Speter apr_status_t apr_err = serr->apr_err; 593251881Speter svn_error_clear(serr); 594251881Speter return apr_err; 595251881Speter } 596251881Speter 597251881Speter return APR_SUCCESS; 598251881Speter} 599251881Speter 600251881Speter 601251881Speter/* Create an svn_fs_access_t in POOL for USER and associate it with 602251881Speter B's filesystem. Also, register a cleanup handler with POOL which 603251881Speter de-associates the svn_fs_access_t from B's filesystem. */ 604251881Speterstatic svn_error_t * 605251881Spetercreate_fs_access(server_baton_t *b, apr_pool_t *pool) 606251881Speter{ 607251881Speter svn_fs_access_t *fs_access; 608251881Speter struct cleanup_fs_access_baton *cleanup_baton; 609251881Speter 610251881Speter if (!b->user) 611251881Speter return SVN_NO_ERROR; 612251881Speter 613251881Speter SVN_ERR(svn_fs_create_access(&fs_access, b->user, pool)); 614251881Speter SVN_ERR(svn_fs_set_access(b->fs, fs_access)); 615251881Speter 616251881Speter cleanup_baton = apr_pcalloc(pool, sizeof(*cleanup_baton)); 617251881Speter cleanup_baton->pool = pool; 618251881Speter cleanup_baton->fs = b->fs; 619251881Speter apr_pool_cleanup_register(pool, cleanup_baton, cleanup_fs_access, 620251881Speter apr_pool_cleanup_null); 621251881Speter 622251881Speter return SVN_NO_ERROR; 623251881Speter} 624251881Speter 625251881Speter/* Authenticate, once the client has chosen a mechanism and possibly 626251881Speter * sent an initial mechanism token. On success, set *success to true 627251881Speter * and b->user to the authenticated username (or NULL for anonymous). 628251881Speter * On authentication failure, report failure to the client and set 629251881Speter * *success to FALSE. On communications failure, return an error. 630251881Speter * If NEEDS_USERNAME is TRUE, don't allow anonymous authentication. */ 631251881Speterstatic svn_error_t *auth(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 632251881Speter const char *mech, const char *mecharg, 633251881Speter server_baton_t *b, enum access_type required, 634251881Speter svn_boolean_t needs_username, 635251881Speter svn_boolean_t *success) 636251881Speter{ 637251881Speter const char *user; 638251881Speter *success = FALSE; 639251881Speter 640251881Speter if (get_access(b, AUTHENTICATED) >= required 641251881Speter && b->tunnel_user && strcmp(mech, "EXTERNAL") == 0) 642251881Speter { 643251881Speter if (*mecharg && strcmp(mecharg, b->tunnel_user) != 0) 644251881Speter return svn_ra_svn__write_tuple(conn, pool, "w(c)", "failure", 645251881Speter "Requested username does not match"); 646251881Speter b->user = b->tunnel_user; 647251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); 648251881Speter *success = TRUE; 649251881Speter return SVN_NO_ERROR; 650251881Speter } 651251881Speter 652251881Speter if (get_access(b, UNAUTHENTICATED) >= required 653251881Speter && strcmp(mech, "ANONYMOUS") == 0 && ! needs_username) 654251881Speter { 655251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w()", "success")); 656251881Speter *success = TRUE; 657251881Speter return SVN_NO_ERROR; 658251881Speter } 659251881Speter 660251881Speter if (get_access(b, AUTHENTICATED) >= required 661251881Speter && b->pwdb && strcmp(mech, "CRAM-MD5") == 0) 662251881Speter { 663251881Speter SVN_ERR(svn_ra_svn_cram_server(conn, pool, b->pwdb, &user, success)); 664251881Speter b->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)); 683251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)c)", b->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 705251881Speter if (b->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_ra_svn_conn_t *conn, 736251881Speter svn_repos_authz_access_t required, 737251881Speter const char *path, 738251881Speter svn_boolean_t needs_username) 739251881Speter{ 740251881Speter enum access_type req = (required & svn_authz_write) ? 741251881Speter WRITE_ACCESS : READ_ACCESS; 742251881Speter svn_boolean_t authorized; 743251881Speter svn_error_t *err; 744251881Speter 745251881Speter /* Get authz's opinion on the access. */ 746251881Speter err = authz_check_access(&authorized, path, required, baton, conn, pool); 747251881Speter 748251881Speter /* If an error made lookup fail, deny access. */ 749251881Speter if (err) 750251881Speter { 751251881Speter log_server_error(err, baton, conn, pool); 752251881Speter svn_error_clear(err); 753251881Speter return FALSE; 754251881Speter } 755251881Speter 756251881Speter /* If the required access is blanket-granted AND granted by authz 757251881Speter AND we already have a username if one is required, then the 758251881Speter lookup has succeeded. */ 759251881Speter if (current_access(baton) >= req 760251881Speter && authorized 761251881Speter && (! needs_username || baton->user)) 762251881Speter return TRUE; 763251881Speter 764251881Speter return FALSE; 765251881Speter} 766251881Speter 767251881Speter/* Check that the client has the REQUIRED access by consulting the 768251881Speter * authentication and authorization states stored in BATON. If the 769251881Speter * client does not have the required access credentials, attempt to 770251881Speter * authenticate the client to get that access, using CONN for 771251881Speter * communication. 772251881Speter * 773251881Speter * This function is supposed to be called to handle the authentication 774251881Speter * half of a standard svn protocol reply. If an error is returned, it 775251881Speter * probably means that the server can terminate the client connection 776251881Speter * with an apologetic error, as it implies an authentication failure. 777251881Speter * 778251881Speter * PATH and NEEDS_USERNAME are passed along to lookup_access, their 779251881Speter * behaviour is documented there. 780251881Speter */ 781251881Speterstatic svn_error_t *must_have_access(svn_ra_svn_conn_t *conn, 782251881Speter apr_pool_t *pool, 783251881Speter server_baton_t *b, 784251881Speter svn_repos_authz_access_t required, 785251881Speter const char *path, 786251881Speter svn_boolean_t needs_username) 787251881Speter{ 788251881Speter enum access_type req = (required & svn_authz_write) ? 789251881Speter WRITE_ACCESS : READ_ACCESS; 790251881Speter 791251881Speter /* See whether the user already has the required access. If so, 792251881Speter nothing needs to be done. Create the FS access and send a 793251881Speter trivial auth request. */ 794251881Speter if (lookup_access(pool, b, conn, required, path, needs_username)) 795251881Speter { 796251881Speter SVN_ERR(create_fs_access(b, pool)); 797251881Speter return trivial_auth_request(conn, pool, b); 798251881Speter } 799251881Speter 800251881Speter /* If the required blanket access can be obtained by authenticating, 801251881Speter try that. Unfortunately, we can't tell until after 802251881Speter authentication whether authz will work or not. We force 803251881Speter requiring a username because we need one to be able to check 804251881Speter authz configuration again with a different user credentials than 805251881Speter the first time round. */ 806251881Speter if (b->user == NULL 807251881Speter && get_access(b, AUTHENTICATED) >= req 808251881Speter && (b->tunnel_user || b->pwdb || b->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. */ 813251881Speter if (! lookup_access(pool, b, conn, 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, 816251881Speter NULL, NULL, b, conn, pool), 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); 969251881Speter SVN_CMD_ERR(svn_repos_begin_report3(&report_baton, rev, b->repos, 970251881Speter b->fs_path->data, target, tgt_path, 971251881Speter text_deltas, depth, ignore_ancestry, 972251881Speter send_copyfrom_args, 973251881Speter editor, edit_baton, 974251881Speter authz_check_access_cb_func(b), 975251881Speter &ab, svn_ra_svn_zero_copy_limit(conn), 976251881Speter pool)); 977251881Speter 978251881Speter rb.sb = b; 979251881Speter rb.repos_url = svn_path_uri_decode(b->repos_url, pool); 980251881Speter rb.report_baton = report_baton; 981251881Speter rb.err = NULL; 982251881Speter rb.entry_counter = 0; 983251881Speter rb.only_empty_entries = TRUE; 984251881Speter rb.from_rev = from_rev; 985251881Speter if (from_rev) 986251881Speter *from_rev = SVN_INVALID_REVNUM; 987251881Speter err = svn_ra_svn__handle_commands2(conn, pool, report_commands, &rb, TRUE); 988251881Speter if (err) 989251881Speter { 990251881Speter /* Network or protocol error while handling commands. */ 991251881Speter svn_error_clear(rb.err); 992251881Speter return err; 993251881Speter } 994251881Speter else if (rb.err) 995251881Speter { 996251881Speter /* Some failure during the reporting or editing operations. */ 997251881Speter SVN_CMD_ERR(rb.err); 998251881Speter } 999251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1000251881Speter 1001251881Speter if (only_empty_entry) 1002251881Speter *only_empty_entry = rb.entry_counter == 1 && rb.only_empty_entries; 1003251881Speter 1004251881Speter return SVN_NO_ERROR; 1005251881Speter} 1006251881Speter 1007251881Speter/* --- MAIN COMMAND SET --- */ 1008251881Speter 1009251881Speter/* Write out a list of property diffs. PROPDIFFS is an array of svn_prop_t 1010251881Speter * values. */ 1011251881Speterstatic svn_error_t *write_prop_diffs(svn_ra_svn_conn_t *conn, 1012251881Speter apr_pool_t *pool, 1013251881Speter const apr_array_header_t *propdiffs) 1014251881Speter{ 1015251881Speter int i; 1016251881Speter 1017251881Speter for (i = 0; i < propdiffs->nelts; ++i) 1018251881Speter { 1019251881Speter const svn_prop_t *prop = &APR_ARRAY_IDX(propdiffs, i, svn_prop_t); 1020251881Speter 1021251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "c(?s)", 1022251881Speter prop->name, prop->value)); 1023251881Speter } 1024251881Speter 1025251881Speter return SVN_NO_ERROR; 1026251881Speter} 1027251881Speter 1028251881Speter/* Write out a lock to the client. */ 1029251881Speterstatic svn_error_t *write_lock(svn_ra_svn_conn_t *conn, 1030251881Speter apr_pool_t *pool, 1031251881Speter svn_lock_t *lock) 1032251881Speter{ 1033251881Speter const char *cdate, *edate; 1034251881Speter 1035251881Speter cdate = svn_time_to_cstring(lock->creation_date, pool); 1036251881Speter edate = lock->expiration_date 1037251881Speter ? svn_time_to_cstring(lock->expiration_date, pool) : NULL; 1038251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "ccc(?c)c(?c)", lock->path, 1039251881Speter lock->token, lock->owner, lock->comment, 1040251881Speter cdate, edate)); 1041251881Speter 1042251881Speter return SVN_NO_ERROR; 1043251881Speter} 1044251881Speter 1045251881Speter/* ### This really belongs in libsvn_repos. */ 1046251881Speter/* Get the explicit properties and/or inherited properties for a PATH in 1047251881Speter ROOT, with hardcoded committed-info values. */ 1048251881Speterstatic svn_error_t * 1049251881Speterget_props(apr_hash_t **props, 1050251881Speter apr_array_header_t **iprops, 1051251881Speter authz_baton_t *b, 1052251881Speter svn_fs_root_t *root, 1053251881Speter const char *path, 1054251881Speter apr_pool_t *pool) 1055251881Speter{ 1056251881Speter /* Get the explicit properties. */ 1057251881Speter if (props) 1058251881Speter { 1059251881Speter svn_string_t *str; 1060251881Speter svn_revnum_t crev; 1061251881Speter const char *cdate, *cauthor, *uuid; 1062251881Speter 1063251881Speter SVN_ERR(svn_fs_node_proplist(props, root, path, pool)); 1064251881Speter 1065251881Speter /* Hardcode the values for the committed revision, date, and author. */ 1066251881Speter SVN_ERR(svn_repos_get_committed_info(&crev, &cdate, &cauthor, root, 1067251881Speter path, pool)); 1068251881Speter str = svn_string_create(apr_psprintf(pool, "%ld", crev), 1069251881Speter pool); 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)); 1105251881Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->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))); 1109251881Speter svn_stringbuf_set(b->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)); 1123251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->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)); 1141251881Speter SVN_CMD_ERR(svn_repos_dated_revision(&rev, b->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))); 1163251881Speter SVN_CMD_ERR(svn_repos_fs_change_rev_prop4(b->repos, rev, b->user, 1164251881Speter name, old_value_p, value, 1165251881Speter TRUE, TRUE, 1166251881Speter authz_check_access_cb_func(b), &ab, 1167251881Speter pool)); 1168251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1169251881Speter 1170251881Speter return SVN_NO_ERROR; 1171251881Speter} 1172251881Speter 1173251881Speterstatic svn_error_t *change_rev_prop2(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1174251881Speter apr_array_header_t *params, void *baton) 1175251881Speter{ 1176251881Speter server_baton_t *b = baton; 1177251881Speter svn_revnum_t rev; 1178251881Speter const char *name; 1179251881Speter svn_string_t *value; 1180251881Speter const svn_string_t *const *old_value_p; 1181251881Speter svn_string_t *old_value; 1182251881Speter svn_boolean_t dont_care; 1183251881Speter 1184251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc(?s)(b?s)", 1185251881Speter &rev, &name, &value, 1186251881Speter &dont_care, &old_value)); 1187251881Speter 1188251881Speter /* Argument parsing. */ 1189251881Speter if (dont_care) 1190251881Speter old_value_p = NULL; 1191251881Speter else 1192251881Speter old_value_p = (const svn_string_t *const *)&old_value; 1193251881Speter 1194251881Speter /* Input validation. */ 1195251881Speter if (dont_care && old_value) 1196251881Speter { 1197251881Speter svn_error_t *err; 1198251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 1199251881Speter "'previous-value' and 'dont-care' cannot both be " 1200251881Speter "set in 'change-rev-prop2' request"); 1201251881Speter return log_fail_and_flush(err, b, conn, pool); 1202251881Speter } 1203251881Speter 1204251881Speter /* Do it. */ 1205251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, old_value_p, value, pool)); 1206251881Speter 1207251881Speter return SVN_NO_ERROR; 1208251881Speter} 1209251881Speter 1210251881Speterstatic svn_error_t *change_rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1211251881Speter apr_array_header_t *params, void *baton) 1212251881Speter{ 1213251881Speter server_baton_t *b = baton; 1214251881Speter svn_revnum_t rev; 1215251881Speter const char *name; 1216251881Speter svn_string_t *value; 1217251881Speter 1218251881Speter /* Because the revprop value was at one time mandatory, the usual 1219251881Speter optional element pattern "(?s)" isn't used. */ 1220251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc?s", &rev, &name, &value)); 1221251881Speter 1222251881Speter SVN_ERR(do_change_rev_prop(conn, b, rev, name, NULL, value, pool)); 1223251881Speter 1224251881Speter return SVN_NO_ERROR; 1225251881Speter} 1226251881Speter 1227251881Speterstatic svn_error_t *rev_proplist(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1228251881Speter apr_array_header_t *params, void *baton) 1229251881Speter{ 1230251881Speter server_baton_t *b = baton; 1231251881Speter svn_revnum_t rev; 1232251881Speter apr_hash_t *props; 1233251881Speter authz_baton_t ab; 1234251881Speter 1235251881Speter ab.server = b; 1236251881Speter ab.conn = conn; 1237251881Speter 1238251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "r", &rev)); 1239251881Speter SVN_ERR(log_command(b, conn, pool, "%s", svn_log__rev_proplist(rev, pool))); 1240251881Speter 1241251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1242251881Speter SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, 1243251881Speter authz_check_access_cb_func(b), &ab, 1244251881Speter pool)); 1245251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 1246251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1247251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1248251881Speter return SVN_NO_ERROR; 1249251881Speter} 1250251881Speter 1251251881Speterstatic svn_error_t *rev_prop(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1252251881Speter apr_array_header_t *params, void *baton) 1253251881Speter{ 1254251881Speter server_baton_t *b = baton; 1255251881Speter svn_revnum_t rev; 1256251881Speter const char *name; 1257251881Speter svn_string_t *value; 1258251881Speter authz_baton_t ab; 1259251881Speter 1260251881Speter ab.server = b; 1261251881Speter ab.conn = conn; 1262251881Speter 1263251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rc", &rev, &name)); 1264251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1265251881Speter svn_log__rev_prop(rev, name, pool))); 1266251881Speter 1267251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1268251881Speter SVN_CMD_ERR(svn_repos_fs_revision_prop(&value, b->repos, rev, name, 1269251881Speter authz_check_access_cb_func(b), &ab, 1270251881Speter pool)); 1271251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "(?s)", value)); 1272251881Speter return SVN_NO_ERROR; 1273251881Speter} 1274251881Speter 1275251881Speterstatic svn_error_t *commit_done(const svn_commit_info_t *commit_info, 1276251881Speter void *baton, apr_pool_t *pool) 1277251881Speter{ 1278251881Speter commit_callback_baton_t *ccb = baton; 1279251881Speter 1280251881Speter *ccb->new_rev = commit_info->revision; 1281251881Speter *ccb->date = commit_info->date 1282251881Speter ? apr_pstrdup(ccb->pool, commit_info->date): NULL; 1283251881Speter *ccb->author = commit_info->author 1284251881Speter ? apr_pstrdup(ccb->pool, commit_info->author) : NULL; 1285251881Speter *ccb->post_commit_err = commit_info->post_commit_err 1286251881Speter ? apr_pstrdup(ccb->pool, commit_info->post_commit_err) : NULL; 1287251881Speter return SVN_NO_ERROR; 1288251881Speter} 1289251881Speter 1290251881Speter/* Add the LOCK_TOKENS (if any) to the filesystem access context, 1291251881Speter * checking path authorizations using the state in SB as we go. 1292251881Speter * LOCK_TOKENS is an array of svn_ra_svn_item_t structs. Return a 1293251881Speter * client error if LOCK_TOKENS is not a list of lists. If a lock 1294251881Speter * violates the authz configuration, return SVN_ERR_RA_NOT_AUTHORIZED 1295251881Speter * to the client. Use POOL for temporary allocations only. 1296251881Speter */ 1297251881Speterstatic svn_error_t *add_lock_tokens(svn_ra_svn_conn_t *conn, 1298251881Speter const apr_array_header_t *lock_tokens, 1299251881Speter server_baton_t *sb, 1300251881Speter apr_pool_t *pool) 1301251881Speter{ 1302251881Speter int i; 1303251881Speter svn_fs_access_t *fs_access; 1304251881Speter 1305251881Speter SVN_ERR(svn_fs_get_access(&fs_access, sb->fs)); 1306251881Speter 1307251881Speter /* If there is no access context, nowhere to add the tokens. */ 1308251881Speter if (! fs_access) 1309251881Speter return SVN_NO_ERROR; 1310251881Speter 1311251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1312251881Speter { 1313251881Speter const char *path, *token, *full_path; 1314251881Speter svn_ra_svn_item_t *path_item, *token_item; 1315251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(lock_tokens, i, 1316251881Speter svn_ra_svn_item_t); 1317251881Speter if (item->kind != SVN_RA_SVN_LIST) 1318251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1319251881Speter "Lock tokens aren't a list of lists"); 1320251881Speter 1321251881Speter path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); 1322251881Speter if (path_item->kind != SVN_RA_SVN_STRING) 1323251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1324251881Speter "Lock path isn't a string"); 1325251881Speter 1326251881Speter token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); 1327251881Speter if (token_item->kind != SVN_RA_SVN_STRING) 1328251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1329251881Speter "Lock token isn't a string"); 1330251881Speter 1331251881Speter path = path_item->u.string->data; 1332251881Speter full_path = svn_fspath__join(sb->fs_path->data, 1333251881Speter svn_relpath_canonicalize(path, pool), 1334251881Speter pool); 1335251881Speter 1336251881Speter if (! lookup_access(pool, sb, conn, svn_authz_write, 1337251881Speter full_path, TRUE)) 1338251881Speter return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, 1339251881Speter sb, conn, pool); 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 1348251881Speter/* Unlock the paths with lock tokens in LOCK_TOKENS, ignoring any errors. 1349251881Speter LOCK_TOKENS contains svn_ra_svn_item_t elements, assumed to be lists. */ 1350251881Speterstatic svn_error_t *unlock_paths(const apr_array_header_t *lock_tokens, 1351251881Speter server_baton_t *sb, 1352251881Speter svn_ra_svn_conn_t *conn, 1353251881Speter apr_pool_t *pool) 1354251881Speter{ 1355251881Speter int i; 1356251881Speter apr_pool_t *iterpool; 1357251881Speter 1358251881Speter iterpool = svn_pool_create(pool); 1359251881Speter 1360251881Speter for (i = 0; i < lock_tokens->nelts; ++i) 1361251881Speter { 1362251881Speter svn_ra_svn_item_t *item, *path_item, *token_item; 1363251881Speter const char *path, *token, *full_path; 1364251881Speter svn_error_t *err; 1365251881Speter svn_pool_clear(iterpool); 1366251881Speter 1367251881Speter item = &APR_ARRAY_IDX(lock_tokens, i, svn_ra_svn_item_t); 1368251881Speter path_item = &APR_ARRAY_IDX(item->u.list, 0, svn_ra_svn_item_t); 1369251881Speter token_item = &APR_ARRAY_IDX(item->u.list, 1, svn_ra_svn_item_t); 1370251881Speter 1371251881Speter path = path_item->u.string->data; 1372251881Speter token = token_item->u.string->data; 1373251881Speter 1374251881Speter full_path = svn_fspath__join(sb->fs_path->data, 1375251881Speter svn_relpath_canonicalize(path, iterpool), 1376251881Speter iterpool); 1377251881Speter 1378251881Speter /* The lock may have become defunct after the commit, so ignore such 1379251881Speter errors. */ 1380251881Speter err = svn_repos_fs_unlock(sb->repos, full_path, token, 1381251881Speter FALSE, iterpool); 1382251881Speter log_server_error(err, sb, conn, iterpool); 1383251881Speter svn_error_clear(err); 1384251881Speter } 1385251881Speter 1386251881Speter svn_pool_destroy(iterpool); 1387251881Speter 1388251881Speter return SVN_NO_ERROR; 1389251881Speter} 1390251881Speter 1391251881Speterstatic svn_error_t *commit(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1392251881Speter apr_array_header_t *params, void *baton) 1393251881Speter{ 1394251881Speter server_baton_t *b = baton; 1395251881Speter const char *log_msg = NULL, 1396251881Speter *date = NULL, 1397251881Speter *author = NULL, 1398251881Speter *post_commit_err = NULL; 1399251881Speter apr_array_header_t *lock_tokens; 1400251881Speter svn_boolean_t keep_locks; 1401251881Speter apr_array_header_t *revprop_list = NULL; 1402251881Speter apr_hash_t *revprop_table; 1403251881Speter const svn_delta_editor_t *editor; 1404251881Speter void *edit_baton; 1405251881Speter svn_boolean_t aborted; 1406251881Speter commit_callback_baton_t ccb; 1407251881Speter svn_revnum_t new_rev; 1408251881Speter authz_baton_t ab; 1409251881Speter 1410251881Speter ab.server = b; 1411251881Speter ab.conn = conn; 1412251881Speter 1413251881Speter if (params->nelts == 1) 1414251881Speter { 1415251881Speter /* Clients before 1.2 don't send lock-tokens, keep-locks, 1416251881Speter and rev-props fields. */ 1417251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &log_msg)); 1418251881Speter lock_tokens = NULL; 1419251881Speter keep_locks = TRUE; 1420251881Speter revprop_list = NULL; 1421251881Speter } 1422251881Speter else 1423251881Speter { 1424251881Speter /* Clients before 1.5 don't send the rev-props field. */ 1425251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "clb?l", &log_msg, 1426251881Speter &lock_tokens, &keep_locks, 1427251881Speter &revprop_list)); 1428251881Speter } 1429251881Speter 1430251881Speter /* The handling for locks is a little problematic, because the 1431251881Speter protocol won't let us send several auth requests once one has 1432251881Speter succeeded. So we request write access and a username before 1433251881Speter adding tokens (if we have any), and subsequently fail if a lock 1434251881Speter violates authz. */ 1435251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 1436251881Speter NULL, 1437251881Speter (lock_tokens && lock_tokens->nelts))); 1438251881Speter 1439251881Speter /* Authorize the lock tokens and give them to the FS if we got 1440251881Speter any. */ 1441251881Speter if (lock_tokens && lock_tokens->nelts) 1442251881Speter SVN_CMD_ERR(add_lock_tokens(conn, lock_tokens, b, pool)); 1443251881Speter 1444253734Speter /* Ignore LOG_MSG, per the protocol. See ra_svn_commit(). */ 1445251881Speter if (revprop_list) 1446251881Speter SVN_ERR(svn_ra_svn__parse_proplist(revprop_list, pool, &revprop_table)); 1447251881Speter else 1448251881Speter { 1449251881Speter revprop_table = apr_hash_make(pool); 1450251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_LOG, 1451251881Speter svn_string_create(log_msg, pool)); 1452251881Speter } 1453251881Speter 1454251881Speter /* Get author from the baton, making sure clients can't circumvent 1455251881Speter the authentication via the revision props. */ 1456251881Speter svn_hash_sets(revprop_table, SVN_PROP_REVISION_AUTHOR, 1457251881Speter b->user ? svn_string_create(b->user, pool) : NULL); 1458251881Speter 1459251881Speter ccb.pool = pool; 1460251881Speter ccb.new_rev = &new_rev; 1461251881Speter ccb.date = &date; 1462251881Speter ccb.author = &author; 1463251881Speter ccb.post_commit_err = &post_commit_err; 1464251881Speter /* ### Note that svn_repos_get_commit_editor5 actually wants a decoded URL. */ 1465251881Speter SVN_CMD_ERR(svn_repos_get_commit_editor5 1466251881Speter (&editor, &edit_baton, b->repos, NULL, 1467251881Speter svn_path_uri_decode(b->repos_url, pool), 1468251881Speter b->fs_path->data, revprop_table, 1469251881Speter commit_done, &ccb, 1470251881Speter authz_commit_cb, &ab, pool)); 1471251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1472251881Speter SVN_ERR(svn_ra_svn_drive_editor2(conn, pool, editor, edit_baton, 1473251881Speter &aborted, FALSE)); 1474251881Speter if (!aborted) 1475251881Speter { 1476251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1477251881Speter svn_log__commit(new_rev, pool))); 1478251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1479251881Speter 1480251881Speter /* In tunnel mode, deltify before answering the client, because 1481251881Speter answering may cause the client to terminate the connection 1482251881Speter and thus kill the server. But otherwise, deltify after 1483251881Speter answering the client, to avoid user-visible delay. */ 1484251881Speter 1485251881Speter if (b->tunnel) 1486251881Speter SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool)); 1487251881Speter 1488251881Speter /* Unlock the paths. */ 1489251881Speter if (! keep_locks && lock_tokens && lock_tokens->nelts) 1490251881Speter SVN_ERR(unlock_paths(lock_tokens, b, conn, pool)); 1491251881Speter 1492251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "r(?c)(?c)(?c)", 1493251881Speter new_rev, date, author, post_commit_err)); 1494251881Speter 1495251881Speter if (! b->tunnel) 1496251881Speter SVN_ERR(svn_fs_deltify_revision(b->fs, new_rev, pool)); 1497251881Speter } 1498251881Speter return SVN_NO_ERROR; 1499251881Speter} 1500251881Speter 1501251881Speterstatic svn_error_t *get_file(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1502251881Speter apr_array_header_t *params, void *baton) 1503251881Speter{ 1504251881Speter server_baton_t *b = baton; 1505251881Speter const char *path, *full_path, *hex_digest; 1506251881Speter svn_revnum_t rev; 1507251881Speter svn_fs_root_t *root; 1508251881Speter svn_stream_t *contents; 1509251881Speter apr_hash_t *props = NULL; 1510251881Speter apr_array_header_t *inherited_props; 1511251881Speter svn_string_t write_str; 1512251881Speter char buf[4096]; 1513251881Speter apr_size_t len; 1514251881Speter svn_boolean_t want_props, want_contents; 1515251881Speter apr_uint64_t wants_inherited_props; 1516251881Speter svn_checksum_t *checksum; 1517251881Speter svn_error_t *err, *write_err; 1518251881Speter int i; 1519251881Speter authz_baton_t ab; 1520251881Speter 1521251881Speter ab.server = b; 1522251881Speter ab.conn = conn; 1523251881Speter 1524251881Speter /* Parse arguments. */ 1525251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?B", &path, &rev, 1526251881Speter &want_props, &want_contents, 1527251881Speter &wants_inherited_props)); 1528251881Speter 1529251881Speter full_path = svn_fspath__join(b->fs_path->data, 1530251881Speter svn_relpath_canonicalize(path, pool), pool); 1531251881Speter 1532251881Speter /* Check authorizations */ 1533251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1534251881Speter full_path, FALSE)); 1535251881Speter 1536251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1537251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1538251881Speter 1539251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1540251881Speter svn_log__get_file(full_path, rev, 1541251881Speter want_contents, want_props, pool))); 1542251881Speter 1543251881Speter /* Fetch the properties and a stream for the contents. */ 1544251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 1545251881Speter SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root, 1546251881Speter full_path, TRUE, pool)); 1547251881Speter hex_digest = svn_checksum_to_cstring_display(checksum, pool); 1548251881Speter if (want_props || wants_inherited_props) 1549251881Speter SVN_CMD_ERR(get_props(&props, &inherited_props, &ab, root, full_path, 1550251881Speter pool)); 1551251881Speter if (want_contents) 1552251881Speter SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool)); 1553251881Speter 1554251881Speter /* Send successful command response with revision and props. */ 1555251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success", 1556251881Speter hex_digest, rev)); 1557251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1558251881Speter 1559251881Speter if (wants_inherited_props) 1560251881Speter { 1561251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1562251881Speter 1563251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1564251881Speter for (i = 0; i < inherited_props->nelts; i++) 1565251881Speter { 1566251881Speter svn_prop_inherited_item_t *iprop = 1567251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1568251881Speter 1569251881Speter svn_pool_clear(iterpool); 1570251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1571251881Speter iprop->path_or_url)); 1572251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1573251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1574251881Speter iprop->path_or_url)); 1575251881Speter } 1576251881Speter svn_pool_destroy(iterpool); 1577251881Speter } 1578251881Speter 1579251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1580251881Speter 1581251881Speter /* Now send the file's contents. */ 1582251881Speter if (want_contents) 1583251881Speter { 1584251881Speter err = SVN_NO_ERROR; 1585251881Speter while (1) 1586251881Speter { 1587251881Speter len = sizeof(buf); 1588251881Speter err = svn_stream_read(contents, buf, &len); 1589251881Speter if (err) 1590251881Speter break; 1591251881Speter if (len > 0) 1592251881Speter { 1593251881Speter write_str.data = buf; 1594251881Speter write_str.len = len; 1595251881Speter SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str)); 1596251881Speter } 1597251881Speter if (len < sizeof(buf)) 1598251881Speter { 1599251881Speter err = svn_stream_close(contents); 1600251881Speter break; 1601251881Speter } 1602251881Speter } 1603251881Speter write_err = svn_ra_svn__write_cstring(conn, pool, ""); 1604251881Speter if (write_err) 1605251881Speter { 1606251881Speter svn_error_clear(err); 1607251881Speter return write_err; 1608251881Speter } 1609251881Speter SVN_CMD_ERR(err); 1610251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1611251881Speter } 1612251881Speter 1613251881Speter return SVN_NO_ERROR; 1614251881Speter} 1615251881Speter 1616251881Speterstatic svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1617251881Speter apr_array_header_t *params, void *baton) 1618251881Speter{ 1619251881Speter server_baton_t *b = baton; 1620251881Speter const char *path, *full_path; 1621251881Speter svn_revnum_t rev; 1622251881Speter apr_hash_t *entries, *props = NULL; 1623251881Speter apr_array_header_t *inherited_props; 1624251881Speter apr_hash_index_t *hi; 1625251881Speter svn_fs_root_t *root; 1626251881Speter apr_pool_t *subpool; 1627251881Speter svn_boolean_t want_props, want_contents; 1628251881Speter apr_uint64_t wants_inherited_props; 1629251881Speter apr_uint64_t dirent_fields; 1630251881Speter apr_array_header_t *dirent_fields_list = NULL; 1631251881Speter svn_ra_svn_item_t *elt; 1632251881Speter int i; 1633251881Speter authz_baton_t ab; 1634251881Speter 1635251881Speter ab.server = b; 1636251881Speter ab.conn = conn; 1637251881Speter 1638251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev, 1639251881Speter &want_props, &want_contents, 1640251881Speter &dirent_fields_list, 1641251881Speter &wants_inherited_props)); 1642251881Speter 1643251881Speter if (! dirent_fields_list) 1644251881Speter { 1645251881Speter dirent_fields = SVN_DIRENT_ALL; 1646251881Speter } 1647251881Speter else 1648251881Speter { 1649251881Speter dirent_fields = 0; 1650251881Speter 1651251881Speter for (i = 0; i < dirent_fields_list->nelts; ++i) 1652251881Speter { 1653251881Speter elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t); 1654251881Speter 1655251881Speter if (elt->kind != SVN_RA_SVN_WORD) 1656251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1657251881Speter "Dirent field not a string"); 1658251881Speter 1659251881Speter if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0) 1660251881Speter dirent_fields |= SVN_DIRENT_KIND; 1661251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0) 1662251881Speter dirent_fields |= SVN_DIRENT_SIZE; 1663251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0) 1664251881Speter dirent_fields |= SVN_DIRENT_HAS_PROPS; 1665251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0) 1666251881Speter dirent_fields |= SVN_DIRENT_CREATED_REV; 1667251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0) 1668251881Speter dirent_fields |= SVN_DIRENT_TIME; 1669251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0) 1670251881Speter dirent_fields |= SVN_DIRENT_LAST_AUTHOR; 1671251881Speter } 1672251881Speter } 1673251881Speter 1674251881Speter full_path = svn_fspath__join(b->fs_path->data, 1675251881Speter svn_relpath_canonicalize(path, pool), pool); 1676251881Speter 1677251881Speter /* Check authorizations */ 1678251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1679251881Speter full_path, FALSE)); 1680251881Speter 1681251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1682251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1683251881Speter 1684251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1685251881Speter svn_log__get_dir(full_path, rev, 1686251881Speter want_contents, want_props, 1687251881Speter dirent_fields, pool))); 1688251881Speter 1689251881Speter /* Fetch the root of the appropriate revision. */ 1690251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 1691251881Speter 1692251881Speter /* Fetch the directory's explicit and/or inherited properties 1693251881Speter if requested. */ 1694251881Speter if (want_props || wants_inherited_props) 1695251881Speter SVN_CMD_ERR(get_props(&props, &inherited_props, &ab, root, full_path, 1696251881Speter pool)); 1697251881Speter 1698251881Speter /* Begin response ... */ 1699251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev)); 1700251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1701251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!")); 1702251881Speter 1703251881Speter /* Fetch the directory entries if requested and send them immediately. */ 1704251881Speter if (want_contents) 1705251881Speter { 1706251881Speter /* Use epoch for a placeholder for a missing date. */ 1707251881Speter const char *missing_date = svn_time_to_cstring(0, pool); 1708251881Speter 1709251881Speter SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); 1710251881Speter 1711251881Speter /* Transform the hash table's FS entries into dirents. This probably 1712251881Speter * belongs in libsvn_repos. */ 1713251881Speter subpool = svn_pool_create(pool); 1714251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1715251881Speter { 1716251881Speter const char *name = svn__apr_hash_index_key(hi); 1717251881Speter svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi); 1718251881Speter const char *file_path; 1719251881Speter 1720251881Speter /* The fields in the entry tuple. */ 1721251881Speter svn_node_kind_t entry_kind = svn_node_none; 1722251881Speter svn_filesize_t entry_size = 0; 1723251881Speter svn_boolean_t has_props = FALSE; 1724251881Speter /* If 'created rev' was not requested, send 0. We can't use 1725251881Speter * SVN_INVALID_REVNUM as the tuple field is not optional. 1726251881Speter * See the email thread on dev@, 2012-03-28, subject 1727251881Speter * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra", 1728251881Speter * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */ 1729251881Speter svn_revnum_t created_rev = 0; 1730251881Speter const char *cdate = NULL; 1731251881Speter const char *last_author = NULL; 1732251881Speter 1733251881Speter svn_pool_clear(subpool); 1734251881Speter 1735251881Speter file_path = svn_fspath__join(full_path, name, subpool); 1736251881Speter if (! lookup_access(subpool, b, conn, svn_authz_read, 1737251881Speter file_path, FALSE)) 1738251881Speter continue; 1739251881Speter 1740251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1741251881Speter entry_kind = fsent->kind; 1742251881Speter 1743251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1744251881Speter if (entry_kind != svn_node_dir) 1745251881Speter SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, 1746251881Speter subpool)); 1747251881Speter 1748251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1749251881Speter { 1750251881Speter apr_hash_t *file_props; 1751251881Speter 1752251881Speter /* has_props */ 1753251881Speter SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path, 1754251881Speter subpool)); 1755251881Speter has_props = (apr_hash_count(file_props) > 0); 1756251881Speter } 1757251881Speter 1758251881Speter if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1759251881Speter || (dirent_fields & SVN_DIRENT_TIME) 1760251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1761251881Speter { 1762251881Speter /* created_rev, last_author, time */ 1763251881Speter SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev, 1764251881Speter &cdate, 1765251881Speter &last_author, 1766251881Speter root, 1767251881Speter file_path, 1768251881Speter subpool)); 1769251881Speter } 1770251881Speter 1771251881Speter /* The client does not properly handle a missing CDATE. For 1772251881Speter interoperability purposes, we must fill in some junk. 1773251881Speter 1774251881Speter See libsvn_ra_svn/client.c:ra_svn_get_dir() */ 1775251881Speter if (cdate == NULL) 1776251881Speter cdate = missing_date; 1777251881Speter 1778251881Speter /* Send the entry. */ 1779251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name, 1780251881Speter svn_node_kind_to_word(entry_kind), 1781251881Speter (apr_uint64_t) entry_size, 1782251881Speter has_props, created_rev, 1783251881Speter cdate, last_author)); 1784251881Speter } 1785251881Speter svn_pool_destroy(subpool); 1786251881Speter } 1787251881Speter 1788251881Speter if (wants_inherited_props) 1789251881Speter { 1790251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1791251881Speter 1792251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1793251881Speter for (i = 0; i < inherited_props->nelts; i++) 1794251881Speter { 1795251881Speter svn_prop_inherited_item_t *iprop = 1796251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1797251881Speter 1798251881Speter svn_pool_clear(iterpool); 1799251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1800251881Speter iprop->path_or_url)); 1801251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1802251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1803251881Speter iprop->path_or_url)); 1804251881Speter } 1805251881Speter svn_pool_destroy(iterpool); 1806251881Speter } 1807251881Speter 1808251881Speter /* Finish response. */ 1809251881Speter return svn_ra_svn__write_tuple(conn, pool, "!))"); 1810251881Speter} 1811251881Speter 1812251881Speterstatic svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1813251881Speter apr_array_header_t *params, void *baton) 1814251881Speter{ 1815251881Speter server_baton_t *b = baton; 1816251881Speter svn_revnum_t rev; 1817251881Speter const char *target, *full_path, *depth_word; 1818251881Speter svn_boolean_t recurse; 1819251881Speter apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ 1820251881Speter apr_uint64_t ignore_ancestry; /* Optional; default FALSE */ 1821251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1822251881Speter handle that by converting recurse if necessary. */ 1823251881Speter svn_depth_t depth = svn_depth_unknown; 1824251881Speter svn_boolean_t is_checkout; 1825251881Speter 1826251881Speter /* Parse the arguments. */ 1827251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target, 1828251881Speter &recurse, &depth_word, 1829251881Speter &send_copyfrom_args, &ignore_ancestry)); 1830251881Speter target = svn_relpath_canonicalize(target, pool); 1831251881Speter 1832251881Speter if (depth_word) 1833251881Speter depth = svn_depth_from_word(depth_word); 1834251881Speter else 1835251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1836251881Speter 1837251881Speter full_path = svn_fspath__join(b->fs_path->data, target, pool); 1838251881Speter /* Check authorization and authenticate the user if necessary. */ 1839251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE)); 1840251881Speter 1841251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1842251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1843251881Speter 1844251881Speter SVN_ERR(accept_report(&is_checkout, NULL, 1845251881Speter conn, pool, b, rev, target, NULL, TRUE, 1846251881Speter depth, 1847251881Speter (send_copyfrom_args == TRUE) /* send_copyfrom_args */, 1848251881Speter (ignore_ancestry == TRUE) /* ignore_ancestry */)); 1849251881Speter if (is_checkout) 1850251881Speter { 1851251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1852251881Speter svn_log__checkout(full_path, rev, 1853251881Speter depth, pool))); 1854251881Speter } 1855251881Speter else 1856251881Speter { 1857251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1858251881Speter svn_log__update(full_path, rev, depth, 1859251881Speter send_copyfrom_args, pool))); 1860251881Speter } 1861251881Speter 1862251881Speter return SVN_NO_ERROR; 1863251881Speter} 1864251881Speter 1865251881Speterstatic svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1866251881Speter apr_array_header_t *params, void *baton) 1867251881Speter{ 1868251881Speter server_baton_t *b = baton; 1869251881Speter svn_revnum_t rev; 1870251881Speter const char *target, *depth_word; 1871251881Speter const char *switch_url, *switch_path; 1872251881Speter svn_boolean_t recurse; 1873251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1874251881Speter handle that by converting recurse if necessary. */ 1875251881Speter svn_depth_t depth = svn_depth_unknown; 1876251881Speter apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ 1877251881Speter apr_uint64_t ignore_ancestry; /* Optional; default TRUE */ 1878251881Speter 1879251881Speter /* Parse the arguments. */ 1880251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target, 1881251881Speter &recurse, &switch_url, &depth_word, 1882251881Speter &send_copyfrom_args, &ignore_ancestry)); 1883251881Speter target = svn_relpath_canonicalize(target, pool); 1884251881Speter switch_url = svn_uri_canonicalize(switch_url, pool); 1885251881Speter 1886251881Speter if (depth_word) 1887251881Speter depth = svn_depth_from_word(depth_word); 1888251881Speter else 1889251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1890251881Speter 1891251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1892251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1893251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1894251881Speter 1895251881Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), 1896251881Speter svn_path_uri_decode(switch_url, pool), 1897251881Speter &switch_path)); 1898251881Speter 1899251881Speter { 1900251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 1901251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1902251881Speter svn_log__switch(full_path, switch_path, rev, 1903251881Speter depth, pool))); 1904251881Speter } 1905251881Speter 1906251881Speter return accept_report(NULL, NULL, 1907251881Speter conn, pool, b, rev, target, switch_path, TRUE, 1908251881Speter depth, 1909251881Speter (send_copyfrom_args == TRUE) /* send_copyfrom_args */, 1910251881Speter (ignore_ancestry != FALSE) /* ignore_ancestry */); 1911251881Speter} 1912251881Speter 1913251881Speterstatic svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1914251881Speter apr_array_header_t *params, void *baton) 1915251881Speter{ 1916251881Speter server_baton_t *b = baton; 1917251881Speter svn_revnum_t rev; 1918251881Speter const char *target, *depth_word; 1919251881Speter svn_boolean_t recurse; 1920251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1921251881Speter handle that by converting recurse if necessary. */ 1922251881Speter svn_depth_t depth = svn_depth_unknown; 1923251881Speter 1924251881Speter /* Parse the arguments. */ 1925251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w", 1926251881Speter &target, &recurse, &rev, &depth_word)); 1927251881Speter target = svn_relpath_canonicalize(target, pool); 1928251881Speter 1929251881Speter if (depth_word) 1930251881Speter depth = svn_depth_from_word(depth_word); 1931251881Speter else 1932251881Speter depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse); 1933251881Speter 1934251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1935251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1936251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1937251881Speter 1938251881Speter { 1939251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 1940251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1941251881Speter svn_log__status(full_path, rev, depth, pool))); 1942251881Speter } 1943251881Speter 1944251881Speter return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE, 1945251881Speter depth, FALSE, FALSE); 1946251881Speter} 1947251881Speter 1948251881Speterstatic svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1949251881Speter apr_array_header_t *params, void *baton) 1950251881Speter{ 1951251881Speter server_baton_t *b = baton; 1952251881Speter svn_revnum_t rev; 1953251881Speter const char *target, *versus_url, *versus_path, *depth_word; 1954251881Speter svn_boolean_t recurse, ignore_ancestry; 1955251881Speter svn_boolean_t text_deltas; 1956251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1957251881Speter handle that by converting recurse if necessary. */ 1958251881Speter svn_depth_t depth = svn_depth_unknown; 1959251881Speter 1960251881Speter /* Parse the arguments. */ 1961251881Speter if (params->nelts == 5) 1962251881Speter { 1963251881Speter /* Clients before 1.4 don't send the text_deltas boolean or depth. */ 1964251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target, 1965251881Speter &recurse, &ignore_ancestry, &versus_url)); 1966251881Speter text_deltas = TRUE; 1967251881Speter depth_word = NULL; 1968251881Speter } 1969251881Speter else 1970251881Speter { 1971251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w", 1972251881Speter &rev, &target, &recurse, 1973251881Speter &ignore_ancestry, &versus_url, 1974251881Speter &text_deltas, &depth_word)); 1975251881Speter } 1976251881Speter target = svn_relpath_canonicalize(target, pool); 1977251881Speter versus_url = svn_uri_canonicalize(versus_url, pool); 1978251881Speter 1979251881Speter if (depth_word) 1980251881Speter depth = svn_depth_from_word(depth_word); 1981251881Speter else 1982251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1983251881Speter 1984251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1985251881Speter 1986251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1987251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1988251881Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), 1989251881Speter svn_path_uri_decode(versus_url, pool), 1990251881Speter &versus_path)); 1991251881Speter 1992251881Speter { 1993251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 1994251881Speter svn_revnum_t from_rev; 1995251881Speter SVN_ERR(accept_report(NULL, &from_rev, 1996251881Speter conn, pool, b, rev, target, versus_path, 1997251881Speter text_deltas, depth, FALSE, ignore_ancestry)); 1998251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1999251881Speter svn_log__diff(full_path, from_rev, versus_path, 2000251881Speter rev, depth, ignore_ancestry, 2001251881Speter pool))); 2002251881Speter } 2003251881Speter return SVN_NO_ERROR; 2004251881Speter} 2005251881Speter 2006251881Speter/* Regardless of whether a client's capabilities indicate an 2007251881Speter understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO), 2008251881Speter we provide a response. 2009251881Speter 2010251881Speter ASSUMPTION: When performing a 'merge' with two URLs at different 2011251881Speter revisions, the client will call this command more than once. */ 2012251881Speterstatic svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2013251881Speter apr_array_header_t *params, void *baton) 2014251881Speter{ 2015251881Speter server_baton_t *b = baton; 2016251881Speter svn_revnum_t rev; 2017251881Speter apr_array_header_t *paths, *canonical_paths; 2018251881Speter svn_mergeinfo_catalog_t mergeinfo; 2019251881Speter int i; 2020251881Speter apr_hash_index_t *hi; 2021251881Speter const char *inherit_word; 2022251881Speter svn_mergeinfo_inheritance_t inherit; 2023251881Speter svn_boolean_t include_descendants; 2024251881Speter apr_pool_t *iterpool; 2025251881Speter authz_baton_t ab; 2026251881Speter 2027251881Speter ab.server = b; 2028251881Speter ab.conn = conn; 2029251881Speter 2030251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev, 2031251881Speter &inherit_word, &include_descendants)); 2032251881Speter inherit = svn_inheritance_from_word(inherit_word); 2033251881Speter 2034251881Speter /* Canonicalize the paths which mergeinfo has been requested for. */ 2035251881Speter canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2036251881Speter for (i = 0; i < paths->nelts; i++) 2037251881Speter { 2038251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2039251881Speter const char *full_path; 2040251881Speter 2041251881Speter if (item->kind != SVN_RA_SVN_STRING) 2042251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2043251881Speter _("Path is not a string")); 2044251881Speter full_path = svn_relpath_canonicalize(item->u.string->data, pool); 2045251881Speter full_path = svn_fspath__join(b->fs_path->data, full_path, pool); 2046251881Speter APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; 2047251881Speter } 2048251881Speter 2049251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2050251881Speter svn_log__get_mergeinfo(canonical_paths, inherit, 2051251881Speter include_descendants, 2052251881Speter pool))); 2053251881Speter 2054251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2055251881Speter SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos, 2056251881Speter canonical_paths, rev, 2057251881Speter inherit, 2058251881Speter include_descendants, 2059251881Speter authz_check_access_cb_func(b), &ab, 2060251881Speter pool)); 2061251881Speter SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo, 2062251881Speter b->fs_path->data, pool)); 2063251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2064251881Speter iterpool = svn_pool_create(pool); 2065251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2066251881Speter { 2067251881Speter const char *key = svn__apr_hash_index_key(hi); 2068251881Speter svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2069251881Speter svn_string_t *mergeinfo_string; 2070251881Speter 2071251881Speter svn_pool_clear(iterpool); 2072251881Speter 2073251881Speter SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool)); 2074251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key, 2075251881Speter mergeinfo_string)); 2076251881Speter } 2077251881Speter svn_pool_destroy(iterpool); 2078251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2079251881Speter 2080251881Speter return SVN_NO_ERROR; 2081251881Speter} 2082251881Speter 2083251881Speter/* Send a log entry to the client. */ 2084251881Speterstatic svn_error_t *log_receiver(void *baton, 2085251881Speter svn_log_entry_t *log_entry, 2086251881Speter apr_pool_t *pool) 2087251881Speter{ 2088251881Speter log_baton_t *b = baton; 2089251881Speter svn_ra_svn_conn_t *conn = b->conn; 2090251881Speter apr_hash_index_t *h; 2091251881Speter svn_boolean_t invalid_revnum = FALSE; 2092251881Speter char action[2]; 2093251881Speter const char *author, *date, *message; 2094251881Speter apr_uint64_t revprop_count; 2095251881Speter 2096251881Speter if (log_entry->revision == SVN_INVALID_REVNUM) 2097251881Speter { 2098251881Speter /* If the stack depth is zero, we've seen the last revision, so don't 2099251881Speter send it, just return. */ 2100251881Speter if (b->stack_depth == 0) 2101251881Speter return SVN_NO_ERROR; 2102251881Speter 2103251881Speter /* Because the svn protocol won't let us send an invalid revnum, we have 2104251881Speter to fudge here and send an additional flag. */ 2105251881Speter log_entry->revision = 0; 2106251881Speter invalid_revnum = TRUE; 2107251881Speter b->stack_depth--; 2108251881Speter } 2109251881Speter 2110251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!")); 2111251881Speter if (log_entry->changed_paths2) 2112251881Speter { 2113251881Speter for (h = apr_hash_first(pool, log_entry->changed_paths2); h; 2114251881Speter h = apr_hash_next(h)) 2115251881Speter { 2116251881Speter const char *path = svn__apr_hash_index_key(h); 2117251881Speter svn_log_changed_path2_t *change = svn__apr_hash_index_val(h); 2118251881Speter 2119251881Speter action[0] = change->action; 2120251881Speter action[1] = '\0'; 2121251881Speter SVN_ERR(svn_ra_svn__write_tuple( 2122251881Speter conn, pool, "cw(?cr)(cbb)", 2123251881Speter path, 2124251881Speter action, 2125251881Speter change->copyfrom_path, 2126251881Speter change->copyfrom_rev, 2127251881Speter svn_node_kind_to_word(change->node_kind), 2128251881Speter /* text_modified and props_modified are never unknown */ 2129251881Speter change->text_modified == svn_tristate_true, 2130251881Speter change->props_modified == svn_tristate_true)); 2131251881Speter } 2132251881Speter } 2133251881Speter svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops); 2134251881Speter svn_compat_log_revprops_clear(log_entry->revprops); 2135251881Speter if (log_entry->revprops) 2136251881Speter revprop_count = apr_hash_count(log_entry->revprops); 2137251881Speter else 2138251881Speter revprop_count = 0; 2139251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!", 2140251881Speter log_entry->revision, 2141251881Speter author, date, message, 2142251881Speter log_entry->has_children, 2143251881Speter invalid_revnum, revprop_count)); 2144251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); 2145251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b", 2146251881Speter log_entry->subtractive_merge)); 2147251881Speter 2148251881Speter if (log_entry->has_children) 2149251881Speter b->stack_depth++; 2150251881Speter 2151251881Speter return SVN_NO_ERROR; 2152251881Speter} 2153251881Speter 2154251881Speterstatic svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2155251881Speter apr_array_header_t *params, void *baton) 2156251881Speter{ 2157251881Speter svn_error_t *err, *write_err; 2158251881Speter server_baton_t *b = baton; 2159251881Speter svn_revnum_t start_rev, end_rev; 2160251881Speter const char *full_path; 2161251881Speter svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; 2162251881Speter apr_array_header_t *paths, *full_paths, *revprop_items, *revprops; 2163251881Speter char *revprop_word; 2164251881Speter svn_ra_svn_item_t *elt; 2165251881Speter int i; 2166251881Speter apr_uint64_t limit, include_merged_revs_param; 2167251881Speter log_baton_t lb; 2168251881Speter authz_baton_t ab; 2169251881Speter 2170251881Speter ab.server = b; 2171251881Speter ab.conn = conn; 2172251881Speter 2173251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, 2174251881Speter &start_rev, &end_rev, &send_changed_paths, 2175251881Speter &strict_node, &limit, 2176251881Speter &include_merged_revs_param, 2177251881Speter &revprop_word, &revprop_items)); 2178251881Speter 2179251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2180251881Speter include_merged_revisions = FALSE; 2181251881Speter else 2182251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2183251881Speter 2184251881Speter if (revprop_word == NULL) 2185251881Speter /* pre-1.5 client */ 2186251881Speter revprops = svn_compat_log_revprops_in(pool); 2187251881Speter else if (strcmp(revprop_word, "all-revprops") == 0) 2188251881Speter revprops = NULL; 2189251881Speter else if (strcmp(revprop_word, "revprops") == 0) 2190251881Speter { 2191251881Speter SVN_ERR_ASSERT(revprop_items); 2192251881Speter 2193251881Speter revprops = apr_array_make(pool, revprop_items->nelts, 2194251881Speter sizeof(char *)); 2195251881Speter for (i = 0; i < revprop_items->nelts; i++) 2196251881Speter { 2197251881Speter elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t); 2198251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2199251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2200251881Speter _("Log revprop entry not a string")); 2201251881Speter APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data; 2202251881Speter } 2203251881Speter } 2204251881Speter else 2205251881Speter return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2206251881Speter _("Unknown revprop word '%s' in log command"), 2207251881Speter revprop_word); 2208251881Speter 2209251881Speter /* If we got an unspecified number then the user didn't send us anything, 2210251881Speter so we assume no limit. If it's larger than INT_MAX then someone is 2211251881Speter messing with us, since we know the svn client libraries will never send 2212251881Speter us anything that big, so play it safe and default to no limit. */ 2213251881Speter if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX) 2214251881Speter limit = 0; 2215251881Speter 2216251881Speter full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2217251881Speter for (i = 0; i < paths->nelts; i++) 2218251881Speter { 2219251881Speter elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2220251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2221251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2222251881Speter _("Log path entry not a string")); 2223251881Speter full_path = svn_relpath_canonicalize(elt->u.string->data, pool), 2224251881Speter full_path = svn_fspath__join(b->fs_path->data, full_path, pool); 2225251881Speter APR_ARRAY_PUSH(full_paths, const char *) = full_path; 2226251881Speter } 2227251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2228251881Speter 2229251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2230251881Speter svn_log__log(full_paths, start_rev, end_rev, 2231251881Speter (int) limit, send_changed_paths, 2232251881Speter strict_node, include_merged_revisions, 2233251881Speter revprops, pool))); 2234251881Speter 2235251881Speter /* Get logs. (Can't report errors back to the client at this point.) */ 2236251881Speter lb.fs_path = b->fs_path->data; 2237251881Speter lb.conn = conn; 2238251881Speter lb.stack_depth = 0; 2239251881Speter err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev, 2240251881Speter (int) limit, send_changed_paths, strict_node, 2241251881Speter include_merged_revisions, revprops, 2242251881Speter authz_check_access_cb_func(b), &ab, log_receiver, 2243251881Speter &lb, pool); 2244251881Speter 2245251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2246251881Speter if (write_err) 2247251881Speter { 2248251881Speter svn_error_clear(err); 2249251881Speter return write_err; 2250251881Speter } 2251251881Speter SVN_CMD_ERR(err); 2252251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2253251881Speter return SVN_NO_ERROR; 2254251881Speter} 2255251881Speter 2256251881Speterstatic svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2257251881Speter apr_array_header_t *params, void *baton) 2258251881Speter{ 2259251881Speter server_baton_t *b = baton; 2260251881Speter svn_revnum_t rev; 2261251881Speter const char *path, *full_path; 2262251881Speter svn_fs_root_t *root; 2263251881Speter svn_node_kind_t kind; 2264251881Speter 2265251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2266251881Speter full_path = svn_fspath__join(b->fs_path->data, 2267251881Speter svn_relpath_canonicalize(path, pool), pool); 2268251881Speter 2269251881Speter /* Check authorizations */ 2270251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2271251881Speter full_path, FALSE)); 2272251881Speter 2273251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2274251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 2275251881Speter 2276251881Speter SVN_ERR(log_command(b, conn, pool, "check-path %s@%d", 2277251881Speter svn_path_uri_encode(full_path, pool), rev)); 2278251881Speter 2279251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 2280251881Speter SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool)); 2281251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w", 2282251881Speter svn_node_kind_to_word(kind))); 2283251881Speter return SVN_NO_ERROR; 2284251881Speter} 2285251881Speter 2286251881Speterstatic svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2287251881Speter apr_array_header_t *params, void *baton) 2288251881Speter{ 2289251881Speter server_baton_t *b = baton; 2290251881Speter svn_revnum_t rev; 2291251881Speter const char *path, *full_path, *cdate; 2292251881Speter svn_fs_root_t *root; 2293251881Speter svn_dirent_t *dirent; 2294251881Speter 2295251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2296251881Speter full_path = svn_fspath__join(b->fs_path->data, 2297251881Speter svn_relpath_canonicalize(path, pool), pool); 2298251881Speter 2299251881Speter /* Check authorizations */ 2300251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2301251881Speter full_path, FALSE)); 2302251881Speter 2303251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2304251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 2305251881Speter 2306251881Speter SVN_ERR(log_command(b, conn, pool, "stat %s@%d", 2307251881Speter svn_path_uri_encode(full_path, pool), rev)); 2308251881Speter 2309251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 2310251881Speter SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool)); 2311251881Speter 2312251881Speter /* Need to return the equivalent of "(?l)", since that's what the 2313251881Speter client is reading. */ 2314251881Speter 2315251881Speter if (dirent == NULL) 2316251881Speter { 2317251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()")); 2318251881Speter return SVN_NO_ERROR; 2319251881Speter } 2320251881Speter 2321251881Speter cdate = (dirent->time == (time_t) -1) ? NULL 2322251881Speter : svn_time_to_cstring(dirent->time, pool); 2323251881Speter 2324251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", 2325251881Speter svn_node_kind_to_word(dirent->kind), 2326251881Speter (apr_uint64_t) dirent->size, 2327251881Speter dirent->has_props, dirent->created_rev, 2328251881Speter cdate, dirent->last_author)); 2329251881Speter 2330251881Speter return SVN_NO_ERROR; 2331251881Speter} 2332251881Speter 2333251881Speterstatic svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2334251881Speter apr_array_header_t *params, void *baton) 2335251881Speter{ 2336251881Speter svn_error_t *err, *write_err; 2337251881Speter server_baton_t *b = baton; 2338251881Speter svn_revnum_t revision; 2339251881Speter apr_array_header_t *location_revisions, *loc_revs_proto; 2340251881Speter svn_ra_svn_item_t *elt; 2341251881Speter int i; 2342251881Speter const char *relative_path; 2343251881Speter svn_revnum_t peg_revision; 2344251881Speter apr_hash_t *fs_locations; 2345251881Speter const char *abs_path; 2346251881Speter authz_baton_t ab; 2347251881Speter 2348251881Speter ab.server = b; 2349251881Speter ab.conn = conn; 2350251881Speter 2351251881Speter /* Parse the arguments. */ 2352251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path, 2353251881Speter &peg_revision, 2354251881Speter &loc_revs_proto)); 2355251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2356251881Speter 2357251881Speter abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); 2358251881Speter 2359251881Speter location_revisions = apr_array_make(pool, loc_revs_proto->nelts, 2360251881Speter sizeof(svn_revnum_t)); 2361251881Speter for (i = 0; i < loc_revs_proto->nelts; i++) 2362251881Speter { 2363251881Speter elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t); 2364251881Speter if (elt->kind != SVN_RA_SVN_NUMBER) 2365251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2366251881Speter "Get-locations location revisions entry " 2367251881Speter "not a revision number"); 2368251881Speter revision = (svn_revnum_t)(elt->u.number); 2369251881Speter APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision; 2370251881Speter } 2371251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2372251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2373251881Speter svn_log__get_locations(abs_path, peg_revision, 2374251881Speter location_revisions, pool))); 2375251881Speter 2376251881Speter /* All the parameters are fine - let's perform the query against the 2377251881Speter * repository. */ 2378251881Speter 2379251881Speter /* We store both err and write_err here, so the client will get 2380251881Speter * the "done" even if there was an error in fetching the results. */ 2381251881Speter 2382251881Speter err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path, 2383251881Speter peg_revision, location_revisions, 2384251881Speter authz_check_access_cb_func(b), &ab, 2385251881Speter pool); 2386251881Speter 2387251881Speter /* Now, write the results to the connection. */ 2388251881Speter if (!err) 2389251881Speter { 2390251881Speter if (fs_locations) 2391251881Speter { 2392251881Speter apr_hash_index_t *iter; 2393251881Speter 2394251881Speter for (iter = apr_hash_first(pool, fs_locations); iter; 2395251881Speter iter = apr_hash_next(iter)) 2396251881Speter { 2397251881Speter const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter); 2398251881Speter const char *iter_value = svn__apr_hash_index_val(iter); 2399251881Speter 2400251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", 2401251881Speter *iter_key, iter_value)); 2402251881Speter } 2403251881Speter } 2404251881Speter } 2405251881Speter 2406251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2407251881Speter if (write_err) 2408251881Speter { 2409251881Speter svn_error_clear(err); 2410251881Speter return write_err; 2411251881Speter } 2412251881Speter SVN_CMD_ERR(err); 2413251881Speter 2414251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2415251881Speter 2416251881Speter return SVN_NO_ERROR; 2417251881Speter} 2418251881Speter 2419251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment, 2420251881Speter void *baton, 2421251881Speter apr_pool_t *pool) 2422251881Speter{ 2423251881Speter svn_ra_svn_conn_t *conn = baton; 2424251881Speter return svn_ra_svn__write_tuple(conn, pool, "rr(?c)", 2425251881Speter segment->range_start, 2426251881Speter segment->range_end, 2427251881Speter segment->path); 2428251881Speter} 2429251881Speter 2430251881Speterstatic svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, 2431251881Speter apr_pool_t *pool, 2432251881Speter apr_array_header_t *params, 2433251881Speter void *baton) 2434251881Speter{ 2435251881Speter svn_error_t *err, *write_err; 2436251881Speter server_baton_t *b = baton; 2437251881Speter svn_revnum_t peg_revision, start_rev, end_rev; 2438251881Speter const char *relative_path; 2439251881Speter const char *abs_path; 2440251881Speter authz_baton_t ab; 2441251881Speter 2442251881Speter ab.server = b; 2443251881Speter ab.conn = conn; 2444251881Speter 2445251881Speter /* Parse the arguments. */ 2446251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)", 2447251881Speter &relative_path, &peg_revision, 2448251881Speter &start_rev, &end_rev)); 2449251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2450251881Speter 2451251881Speter abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); 2452251881Speter 2453251881Speter if (SVN_IS_VALID_REVNUM(start_rev) 2454251881Speter && SVN_IS_VALID_REVNUM(end_rev) 2455251881Speter && (end_rev > start_rev)) 2456251881Speter { 2457251881Speter err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 2458251881Speter "Get-location-segments end revision must not be " 2459251881Speter "younger than start revision"); 2460251881Speter return log_fail_and_flush(err, b, conn, pool); 2461251881Speter } 2462251881Speter 2463251881Speter if (SVN_IS_VALID_REVNUM(peg_revision) 2464251881Speter && SVN_IS_VALID_REVNUM(start_rev) 2465251881Speter && (start_rev > peg_revision)) 2466251881Speter { 2467251881Speter err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 2468251881Speter "Get-location-segments start revision must not " 2469251881Speter "be younger than peg revision"); 2470251881Speter return log_fail_and_flush(err, b, conn, pool); 2471251881Speter } 2472251881Speter 2473251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2474251881Speter SVN_ERR(log_command(baton, conn, pool, "%s", 2475251881Speter svn_log__get_location_segments(abs_path, peg_revision, 2476251881Speter start_rev, end_rev, 2477251881Speter pool))); 2478251881Speter 2479251881Speter /* All the parameters are fine - let's perform the query against the 2480251881Speter * repository. */ 2481251881Speter 2482251881Speter /* We store both err and write_err here, so the client will get 2483251881Speter * the "done" even if there was an error in fetching the results. */ 2484251881Speter 2485251881Speter err = svn_repos_node_location_segments(b->repos, abs_path, 2486251881Speter peg_revision, start_rev, end_rev, 2487251881Speter gls_receiver, (void *)conn, 2488251881Speter authz_check_access_cb_func(b), &ab, 2489251881Speter pool); 2490251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2491251881Speter if (write_err) 2492251881Speter { 2493251881Speter svn_error_clear(err); 2494251881Speter return write_err; 2495251881Speter } 2496251881Speter SVN_CMD_ERR(err); 2497251881Speter 2498251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2499251881Speter 2500251881Speter return SVN_NO_ERROR; 2501251881Speter} 2502251881Speter 2503251881Speter/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the 2504251881Speter client as a string. */ 2505251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data, 2506251881Speter apr_size_t *len) 2507251881Speter{ 2508251881Speter file_revs_baton_t *b = baton; 2509251881Speter svn_string_t str; 2510251881Speter 2511251881Speter str.data = data; 2512251881Speter str.len = *len; 2513251881Speter return svn_ra_svn__write_string(b->conn, b->pool, &str); 2514251881Speter} 2515251881Speter 2516251881Speter/* This implements svn_close_fn_t. Mark the end of the data by writing an 2517251881Speter empty string to the client. */ 2518251881Speterstatic svn_error_t *svndiff_close_handler(void *baton) 2519251881Speter{ 2520251881Speter file_revs_baton_t *b = baton; 2521251881Speter 2522251881Speter SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, "")); 2523251881Speter return SVN_NO_ERROR; 2524251881Speter} 2525251881Speter 2526251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */ 2527251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path, 2528251881Speter svn_revnum_t rev, apr_hash_t *rev_props, 2529251881Speter svn_boolean_t merged_revision, 2530251881Speter svn_txdelta_window_handler_t *d_handler, 2531251881Speter void **d_baton, 2532251881Speter apr_array_header_t *prop_diffs, 2533251881Speter apr_pool_t *pool) 2534251881Speter{ 2535251881Speter file_revs_baton_t *frb = baton; 2536251881Speter svn_stream_t *stream; 2537251881Speter 2538251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!", 2539251881Speter path, rev)); 2540251881Speter SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props)); 2541251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!")); 2542251881Speter SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs)); 2543251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision)); 2544251881Speter 2545251881Speter /* Store the pool for the delta stream. */ 2546251881Speter frb->pool = pool; 2547251881Speter 2548251881Speter /* Prepare for the delta or just write an empty string. */ 2549251881Speter if (d_handler) 2550251881Speter { 2551251881Speter stream = svn_stream_create(baton, pool); 2552251881Speter svn_stream_set_write(stream, svndiff_handler); 2553251881Speter svn_stream_set_close(stream, svndiff_close_handler); 2554251881Speter 2555251881Speter /* If the connection does not support SVNDIFF1 or if we don't want to use 2556251881Speter * compression, use the non-compressing "version 0" implementation */ 2557251881Speter if ( svn_ra_svn_compression_level(frb->conn) > 0 2558251881Speter && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1)) 2559251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1, 2560251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2561251881Speter else 2562251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0, 2563251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2564251881Speter } 2565251881Speter else 2566251881Speter SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); 2567251881Speter 2568251881Speter return SVN_NO_ERROR; 2569251881Speter} 2570251881Speter 2571251881Speterstatic svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2572251881Speter apr_array_header_t *params, void *baton) 2573251881Speter{ 2574251881Speter server_baton_t *b = baton; 2575251881Speter svn_error_t *err, *write_err; 2576251881Speter file_revs_baton_t frb; 2577251881Speter svn_revnum_t start_rev, end_rev; 2578251881Speter const char *path; 2579251881Speter const char *full_path; 2580251881Speter apr_uint64_t include_merged_revs_param; 2581251881Speter svn_boolean_t include_merged_revisions; 2582251881Speter authz_baton_t ab; 2583251881Speter 2584251881Speter ab.server = b; 2585251881Speter ab.conn = conn; 2586251881Speter 2587251881Speter /* Parse arguments. */ 2588251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B", 2589251881Speter &path, &start_rev, &end_rev, 2590251881Speter &include_merged_revs_param)); 2591251881Speter path = svn_relpath_canonicalize(path, pool); 2592251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2593251881Speter full_path = svn_fspath__join(b->fs_path->data, path, pool); 2594251881Speter 2595251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2596251881Speter include_merged_revisions = FALSE; 2597251881Speter else 2598251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2599251881Speter 2600251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2601251881Speter svn_log__get_file_revs(full_path, start_rev, end_rev, 2602251881Speter include_merged_revisions, 2603251881Speter pool))); 2604251881Speter 2605251881Speter frb.conn = conn; 2606251881Speter frb.pool = NULL; 2607251881Speter 2608251881Speter err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev, 2609251881Speter include_merged_revisions, 2610251881Speter authz_check_access_cb_func(b), &ab, 2611251881Speter file_rev_handler, &frb, pool); 2612251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2613251881Speter if (write_err) 2614251881Speter { 2615251881Speter svn_error_clear(err); 2616251881Speter return write_err; 2617251881Speter } 2618251881Speter SVN_CMD_ERR(err); 2619251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2620251881Speter 2621251881Speter return SVN_NO_ERROR; 2622251881Speter} 2623251881Speter 2624251881Speterstatic svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2625251881Speter apr_array_header_t *params, void *baton) 2626251881Speter{ 2627251881Speter server_baton_t *b = baton; 2628251881Speter const char *path; 2629251881Speter const char *comment; 2630251881Speter const char *full_path; 2631251881Speter svn_boolean_t steal_lock; 2632251881Speter svn_revnum_t current_rev; 2633251881Speter svn_lock_t *l; 2634251881Speter 2635251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, 2636251881Speter &steal_lock, ¤t_rev)); 2637251881Speter full_path = svn_fspath__join(b->fs_path->data, 2638251881Speter svn_relpath_canonicalize(path, pool), pool); 2639251881Speter 2640251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2641251881Speter full_path, TRUE)); 2642251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2643251881Speter svn_log__lock_one_path(full_path, steal_lock, pool))); 2644251881Speter 2645251881Speter SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0, 2646251881Speter 0, /* No expiration time. */ 2647251881Speter current_rev, steal_lock, pool)); 2648251881Speter 2649251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success")); 2650251881Speter SVN_ERR(write_lock(conn, pool, l)); 2651251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)")); 2652251881Speter 2653251881Speter return SVN_NO_ERROR; 2654251881Speter} 2655251881Speter 2656251881Speterstatic svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2657251881Speter apr_array_header_t *params, void *baton) 2658251881Speter{ 2659251881Speter server_baton_t *b = baton; 2660251881Speter apr_array_header_t *path_revs; 2661251881Speter const char *comment; 2662251881Speter svn_boolean_t steal_lock; 2663251881Speter int i; 2664251881Speter apr_pool_t *subpool; 2665251881Speter const char *path; 2666251881Speter const char *full_path; 2667251881Speter svn_revnum_t current_rev; 2668251881Speter apr_array_header_t *log_paths; 2669251881Speter svn_lock_t *l; 2670251881Speter svn_error_t *err = SVN_NO_ERROR, *write_err; 2671251881Speter 2672251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, 2673251881Speter &path_revs)); 2674251881Speter 2675251881Speter subpool = svn_pool_create(pool); 2676251881Speter 2677251881Speter /* Because we can only send a single auth reply per request, we send 2678251881Speter a reply before parsing the lock commands. This means an authz 2679251881Speter access denial will abort the processing of the locks and return 2680251881Speter an error. */ 2681251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE)); 2682251881Speter 2683251881Speter /* Loop through the lock requests. */ 2684251881Speter log_paths = apr_array_make(pool, path_revs->nelts, sizeof(full_path)); 2685251881Speter for (i = 0; i < path_revs->nelts; ++i) 2686251881Speter { 2687251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, 2688251881Speter svn_ra_svn_item_t); 2689251881Speter 2690251881Speter svn_pool_clear(subpool); 2691251881Speter 2692251881Speter if (item->kind != SVN_RA_SVN_LIST) 2693251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2694251881Speter "Lock requests should be list of lists"); 2695251881Speter 2696251881Speter SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path, 2697251881Speter ¤t_rev)); 2698251881Speter 2699251881Speter /* Allocate the full_path out of pool so it will survive for use 2700251881Speter * by operational logging, after this loop. */ 2701251881Speter full_path = svn_fspath__join(b->fs_path->data, 2702251881Speter svn_relpath_canonicalize(path, subpool), 2703251881Speter pool); 2704251881Speter APR_ARRAY_PUSH(log_paths, const char *) = full_path; 2705251881Speter 2706251881Speter if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE)) 2707251881Speter { 2708251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, 2709251881Speter b, conn, pool); 2710251881Speter break; 2711251881Speter } 2712251881Speter 2713251881Speter err = svn_repos_fs_lock(&l, b->repos, full_path, 2714251881Speter NULL, comment, FALSE, 2715251881Speter 0, /* No expiration time. */ 2716251881Speter current_rev, 2717251881Speter steal_lock, subpool); 2718251881Speter 2719251881Speter if (err) 2720251881Speter { 2721251881Speter if (SVN_ERR_IS_LOCK_ERROR(err)) 2722251881Speter { 2723251881Speter write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 2724251881Speter svn_error_clear(err); 2725251881Speter err = NULL; 2726251881Speter SVN_ERR(write_err); 2727251881Speter } 2728251881Speter else 2729251881Speter break; 2730251881Speter } 2731251881Speter else 2732251881Speter { 2733251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success")); 2734251881Speter SVN_ERR(write_lock(conn, subpool, l)); 2735251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!")); 2736251881Speter } 2737251881Speter } 2738251881Speter 2739251881Speter svn_pool_destroy(subpool); 2740251881Speter 2741251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2742251881Speter svn_log__lock(log_paths, steal_lock, pool))); 2743251881Speter 2744251881Speter /* NOTE: err might contain a fatal locking error from the loop above. */ 2745251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2746251881Speter if (!write_err) 2747251881Speter SVN_CMD_ERR(err); 2748251881Speter svn_error_clear(err); 2749251881Speter SVN_ERR(write_err); 2750251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2751251881Speter 2752251881Speter return SVN_NO_ERROR; 2753251881Speter} 2754251881Speter 2755251881Speterstatic svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2756251881Speter apr_array_header_t *params, void *baton) 2757251881Speter{ 2758251881Speter server_baton_t *b = baton; 2759251881Speter const char *path, *token, *full_path; 2760251881Speter svn_boolean_t break_lock; 2761251881Speter 2762251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token, 2763251881Speter &break_lock)); 2764251881Speter 2765251881Speter full_path = svn_fspath__join(b->fs_path->data, 2766251881Speter svn_relpath_canonicalize(path, pool), pool); 2767251881Speter 2768251881Speter /* Username required unless break_lock was specified. */ 2769251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2770251881Speter full_path, ! break_lock)); 2771251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2772251881Speter svn_log__unlock_one_path(full_path, break_lock, pool))); 2773251881Speter 2774251881Speter SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock, 2775251881Speter pool)); 2776251881Speter 2777251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2778251881Speter 2779251881Speter return SVN_NO_ERROR; 2780251881Speter} 2781251881Speter 2782251881Speterstatic svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2783251881Speter apr_array_header_t *params, void *baton) 2784251881Speter{ 2785251881Speter server_baton_t *b = baton; 2786251881Speter svn_boolean_t break_lock; 2787251881Speter apr_array_header_t *unlock_tokens; 2788251881Speter int i; 2789251881Speter apr_pool_t *subpool; 2790251881Speter const char *path; 2791251881Speter const char *full_path; 2792251881Speter apr_array_header_t *log_paths; 2793251881Speter const char *token; 2794251881Speter svn_error_t *err = SVN_NO_ERROR, *write_err; 2795251881Speter 2796251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, 2797251881Speter &unlock_tokens)); 2798251881Speter 2799251881Speter /* Username required unless break_lock was specified. */ 2800251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); 2801251881Speter 2802251881Speter subpool = svn_pool_create(pool); 2803251881Speter 2804251881Speter /* Loop through the unlock requests. */ 2805251881Speter log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path)); 2806251881Speter for (i = 0; i < unlock_tokens->nelts; i++) 2807251881Speter { 2808251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, 2809251881Speter svn_ra_svn_item_t); 2810251881Speter 2811251881Speter svn_pool_clear(subpool); 2812251881Speter 2813251881Speter if (item->kind != SVN_RA_SVN_LIST) 2814251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2815251881Speter "Unlock request should be a list of lists"); 2816251881Speter 2817251881Speter SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, 2818251881Speter &token)); 2819251881Speter 2820251881Speter /* Allocate the full_path out of pool so it will survive for use 2821251881Speter * by operational logging, after this loop. */ 2822251881Speter full_path = svn_fspath__join(b->fs_path->data, 2823251881Speter svn_relpath_canonicalize(path, subpool), 2824251881Speter pool); 2825251881Speter APR_ARRAY_PUSH(log_paths, const char *) = full_path; 2826251881Speter 2827251881Speter if (! lookup_access(subpool, b, conn, svn_authz_write, full_path, 2828251881Speter ! break_lock)) 2829251881Speter return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, 2830251881Speter error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 2831251881Speter NULL, NULL, 2832251881Speter b, conn, pool), 2833251881Speter NULL); 2834251881Speter 2835251881Speter err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock, 2836251881Speter subpool); 2837251881Speter if (err) 2838251881Speter { 2839251881Speter if (SVN_ERR_IS_UNLOCK_ERROR(err)) 2840251881Speter { 2841251881Speter write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 2842251881Speter svn_error_clear(err); 2843251881Speter err = NULL; 2844251881Speter SVN_ERR(write_err); 2845251881Speter } 2846251881Speter else 2847251881Speter break; 2848251881Speter } 2849251881Speter else 2850251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", 2851251881Speter path)); 2852251881Speter } 2853251881Speter 2854251881Speter svn_pool_destroy(subpool); 2855251881Speter 2856251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2857251881Speter svn_log__unlock(log_paths, break_lock, pool))); 2858251881Speter 2859251881Speter /* NOTE: err might contain a fatal unlocking error from the loop above. */ 2860251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2861251881Speter if (! write_err) 2862251881Speter SVN_CMD_ERR(err); 2863251881Speter svn_error_clear(err); 2864251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2865251881Speter 2866251881Speter return SVN_NO_ERROR; 2867251881Speter} 2868251881Speter 2869251881Speterstatic svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2870251881Speter apr_array_header_t *params, void *baton) 2871251881Speter{ 2872251881Speter server_baton_t *b = baton; 2873251881Speter const char *path; 2874251881Speter const char *full_path; 2875251881Speter svn_lock_t *l; 2876251881Speter 2877251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); 2878251881Speter 2879251881Speter full_path = svn_fspath__join(b->fs_path->data, 2880251881Speter svn_relpath_canonicalize(path, pool), pool); 2881251881Speter 2882251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2883251881Speter full_path, FALSE)); 2884251881Speter SVN_ERR(log_command(b, conn, pool, "get-lock %s", 2885251881Speter svn_path_uri_encode(full_path, pool))); 2886251881Speter 2887251881Speter SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool)); 2888251881Speter 2889251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2890251881Speter if (l) 2891251881Speter SVN_ERR(write_lock(conn, pool, l)); 2892251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2893251881Speter 2894251881Speter return SVN_NO_ERROR; 2895251881Speter} 2896251881Speter 2897251881Speterstatic svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2898251881Speter apr_array_header_t *params, void *baton) 2899251881Speter{ 2900251881Speter server_baton_t *b = baton; 2901251881Speter const char *path; 2902251881Speter const char *full_path; 2903251881Speter const char *depth_word; 2904251881Speter svn_depth_t depth; 2905251881Speter apr_hash_t *locks; 2906251881Speter apr_hash_index_t *hi; 2907251881Speter svn_error_t *err; 2908251881Speter authz_baton_t ab; 2909251881Speter 2910251881Speter ab.server = b; 2911251881Speter ab.conn = conn; 2912251881Speter 2913251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); 2914251881Speter 2915251881Speter depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; 2916251881Speter if ((depth != svn_depth_empty) && 2917251881Speter (depth != svn_depth_files) && 2918251881Speter (depth != svn_depth_immediates) && 2919251881Speter (depth != svn_depth_infinity)) 2920251881Speter { 2921251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2922251881Speter "Invalid 'depth' specified in get-locks request"); 2923251881Speter return log_fail_and_flush(err, b, conn, pool); 2924251881Speter } 2925251881Speter 2926251881Speter full_path = svn_fspath__join(b->fs_path->data, 2927251881Speter svn_relpath_canonicalize(path, pool), pool); 2928251881Speter 2929251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2930251881Speter 2931251881Speter SVN_ERR(log_command(b, conn, pool, "get-locks %s", 2932251881Speter svn_path_uri_encode(full_path, pool))); 2933251881Speter SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth, 2934251881Speter authz_check_access_cb_func(b), &ab, 2935251881Speter pool)); 2936251881Speter 2937251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2938251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 2939251881Speter { 2940251881Speter svn_lock_t *l = svn__apr_hash_index_val(hi); 2941251881Speter 2942251881Speter SVN_ERR(write_lock(conn, pool, l)); 2943251881Speter } 2944251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2945251881Speter 2946251881Speter return SVN_NO_ERROR; 2947251881Speter} 2948251881Speter 2949251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, 2950251881Speter server_baton_t *b, 2951251881Speter svn_revnum_t rev, 2952251881Speter svn_revnum_t low_water_mark, 2953251881Speter svn_boolean_t send_deltas, 2954251881Speter apr_pool_t *pool) 2955251881Speter{ 2956251881Speter const svn_delta_editor_t *editor; 2957251881Speter void *edit_baton; 2958251881Speter svn_fs_root_t *root; 2959251881Speter svn_error_t *err; 2960251881Speter authz_baton_t ab; 2961251881Speter 2962251881Speter ab.server = b; 2963251881Speter ab.conn = conn; 2964251881Speter 2965251881Speter SVN_ERR(log_command(b, conn, pool, 2966251881Speter svn_log__replay(b->fs_path->data, rev, pool))); 2967251881Speter 2968251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 2969251881Speter 2970251881Speter err = svn_fs_revision_root(&root, b->fs, rev, pool); 2971251881Speter 2972251881Speter if (! err) 2973251881Speter err = svn_repos_replay2(root, b->fs_path->data, low_water_mark, 2974251881Speter send_deltas, editor, edit_baton, 2975251881Speter authz_check_access_cb_func(b), &ab, pool); 2976251881Speter 2977251881Speter if (err) 2978251881Speter svn_error_clear(editor->abort_edit(edit_baton, pool)); 2979251881Speter SVN_CMD_ERR(err); 2980251881Speter 2981251881Speter return svn_ra_svn__write_cmd_finish_replay(conn, pool); 2982251881Speter} 2983251881Speter 2984251881Speterstatic svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2985251881Speter apr_array_header_t *params, void *baton) 2986251881Speter{ 2987251881Speter svn_revnum_t rev, low_water_mark; 2988251881Speter svn_boolean_t send_deltas; 2989251881Speter server_baton_t *b = baton; 2990251881Speter 2991251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark, 2992251881Speter &send_deltas)); 2993251881Speter 2994251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2995251881Speter 2996251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 2997251881Speter send_deltas, pool)); 2998251881Speter 2999251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3000251881Speter 3001251881Speter return SVN_NO_ERROR; 3002251881Speter} 3003251881Speter 3004251881Speterstatic svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3005251881Speter apr_array_header_t *params, void *baton) 3006251881Speter{ 3007251881Speter svn_revnum_t start_rev, end_rev, rev, low_water_mark; 3008251881Speter svn_boolean_t send_deltas; 3009251881Speter server_baton_t *b = baton; 3010251881Speter apr_pool_t *iterpool; 3011251881Speter authz_baton_t ab; 3012251881Speter 3013251881Speter ab.server = b; 3014251881Speter ab.conn = conn; 3015251881Speter 3016251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev, 3017251881Speter &end_rev, &low_water_mark, 3018251881Speter &send_deltas)); 3019251881Speter 3020251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3021251881Speter 3022251881Speter iterpool = svn_pool_create(pool); 3023251881Speter for (rev = start_rev; rev <= end_rev; rev++) 3024251881Speter { 3025251881Speter apr_hash_t *props; 3026251881Speter 3027251881Speter svn_pool_clear(iterpool); 3028251881Speter 3029251881Speter SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, 3030251881Speter authz_check_access_cb_func(b), 3031251881Speter &ab, 3032251881Speter iterpool)); 3033251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops")); 3034251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props)); 3035251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)")); 3036251881Speter 3037251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3038251881Speter send_deltas, iterpool)); 3039251881Speter 3040251881Speter } 3041251881Speter svn_pool_destroy(iterpool); 3042251881Speter 3043251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3044251881Speter 3045251881Speter return SVN_NO_ERROR; 3046251881Speter} 3047251881Speter 3048251881Speterstatic svn_error_t * 3049251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn, 3050251881Speter apr_pool_t *pool, 3051251881Speter apr_array_header_t *params, 3052251881Speter void *baton) 3053251881Speter{ 3054251881Speter server_baton_t *b = baton; 3055251881Speter const char *path, *full_path; 3056251881Speter svn_revnum_t peg_revision; 3057251881Speter svn_revnum_t end_revision; 3058251881Speter svn_revnum_t revision_deleted; 3059251881Speter 3060251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", 3061251881Speter &path, &peg_revision, &end_revision)); 3062251881Speter full_path = svn_fspath__join(b->fs_path->data, 3063251881Speter svn_relpath_canonicalize(path, pool), pool); 3064251881Speter SVN_ERR(log_command(b, conn, pool, "get-deleted-rev")); 3065251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3066251881Speter SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision, 3067251881Speter &revision_deleted, pool)); 3068251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted)); 3069251881Speter return SVN_NO_ERROR; 3070251881Speter} 3071251881Speter 3072251881Speterstatic svn_error_t * 3073251881Speterget_inherited_props(svn_ra_svn_conn_t *conn, 3074251881Speter apr_pool_t *pool, 3075251881Speter apr_array_header_t *params, 3076251881Speter void *baton) 3077251881Speter{ 3078251881Speter server_baton_t *b = baton; 3079251881Speter const char *path, *full_path; 3080251881Speter svn_revnum_t rev; 3081251881Speter svn_fs_root_t *root; 3082251881Speter apr_array_header_t *inherited_props; 3083251881Speter int i; 3084251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 3085251881Speter authz_baton_t ab; 3086251881Speter 3087251881Speter ab.server = b; 3088251881Speter ab.conn = conn; 3089251881Speter 3090251881Speter /* Parse arguments. */ 3091251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev)); 3092251881Speter 3093251881Speter full_path = svn_fspath__join(b->fs_path->data, 3094251881Speter svn_relpath_canonicalize(path, iterpool), 3095251881Speter pool); 3096251881Speter 3097251881Speter /* Check authorizations */ 3098251881Speter SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, 3099251881Speter full_path, FALSE)); 3100251881Speter 3101251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 3102251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 3103251881Speter 3104251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 3105251881Speter svn_log__get_inherited_props(full_path, rev, 3106251881Speter iterpool))); 3107251881Speter 3108251881Speter /* Fetch the properties and a stream for the contents. */ 3109251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool)); 3110251881Speter SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); 3111251881Speter 3112251881Speter /* Send successful command response with revision and props. */ 3113251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success")); 3114251881Speter 3115251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!")); 3116251881Speter 3117251881Speter for (i = 0; i < inherited_props->nelts; i++) 3118251881Speter { 3119251881Speter svn_prop_inherited_item_t *iprop = 3120251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 3121251881Speter 3122251881Speter svn_pool_clear(iterpool); 3123251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 3124251881Speter iprop->path_or_url)); 3125251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 3126251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 3127251881Speter iprop->path_or_url)); 3128251881Speter } 3129251881Speter 3130251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))")); 3131251881Speter svn_pool_destroy(iterpool); 3132251881Speter return SVN_NO_ERROR; 3133251881Speter} 3134251881Speter 3135251881Speterstatic const svn_ra_svn_cmd_entry_t main_commands[] = { 3136251881Speter { "reparent", reparent }, 3137251881Speter { "get-latest-rev", get_latest_rev }, 3138251881Speter { "get-dated-rev", get_dated_rev }, 3139251881Speter { "change-rev-prop", change_rev_prop }, 3140251881Speter { "change-rev-prop2",change_rev_prop2 }, 3141251881Speter { "rev-proplist", rev_proplist }, 3142251881Speter { "rev-prop", rev_prop }, 3143251881Speter { "commit", commit }, 3144251881Speter { "get-file", get_file }, 3145251881Speter { "get-dir", get_dir }, 3146251881Speter { "update", update }, 3147251881Speter { "switch", switch_cmd }, 3148251881Speter { "status", status }, 3149251881Speter { "diff", diff }, 3150251881Speter { "get-mergeinfo", get_mergeinfo }, 3151251881Speter { "log", log_cmd }, 3152251881Speter { "check-path", check_path }, 3153251881Speter { "stat", stat_cmd }, 3154251881Speter { "get-locations", get_locations }, 3155251881Speter { "get-location-segments", get_location_segments }, 3156251881Speter { "get-file-revs", get_file_revs }, 3157251881Speter { "lock", lock }, 3158251881Speter { "lock-many", lock_many }, 3159251881Speter { "unlock", unlock }, 3160251881Speter { "unlock-many", unlock_many }, 3161251881Speter { "get-lock", get_lock }, 3162251881Speter { "get-locks", get_locks }, 3163251881Speter { "replay", replay }, 3164251881Speter { "replay-range", replay_range }, 3165251881Speter { "get-deleted-rev", get_deleted_rev }, 3166251881Speter { "get-iprops", get_inherited_props }, 3167251881Speter { NULL } 3168251881Speter}; 3169251881Speter 3170251881Speter/* Skip past the scheme part of a URL, including the tunnel specification 3171251881Speter * if present. Return NULL if the scheme part is invalid for ra_svn. */ 3172251881Speterstatic const char *skip_scheme_part(const char *url) 3173251881Speter{ 3174251881Speter if (strncmp(url, "svn", 3) != 0) 3175251881Speter return NULL; 3176251881Speter url += 3; 3177251881Speter if (*url == '+') 3178251881Speter url += strcspn(url, ":"); 3179251881Speter if (strncmp(url, "://", 3) != 0) 3180251881Speter return NULL; 3181251881Speter return url + 3; 3182251881Speter} 3183251881Speter 3184251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any 3185251881Speter '..' path segments. 3186251881Speter NOTE: This is similar to svn_path_is_backpath_present, but that function 3187251881Speter assumes the path separator is '/'. This function also checks for 3188251881Speter segments delimited by the local path separator. */ 3189251881Speterstatic svn_boolean_t 3190251881Speterrepos_path_valid(const char *path) 3191251881Speter{ 3192251881Speter const char *s = path; 3193251881Speter 3194251881Speter while (*s) 3195251881Speter { 3196251881Speter /* Scan for the end of the segment. */ 3197251881Speter while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR) 3198251881Speter ++path; 3199251881Speter 3200251881Speter /* Check for '..'. */ 3201251881Speter#ifdef WIN32 3202251881Speter /* On Windows, don't allow sequences of more than one character 3203251881Speter consisting of just dots and spaces. Win32 functions treat 3204251881Speter paths such as ".. " and "......." inconsistently. Make sure 3205251881Speter no one can escape out of the root. */ 3206251881Speter if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s)) 3207251881Speter return FALSE; 3208251881Speter#else /* ! WIN32 */ 3209251881Speter if (path - s == 2 && s[0] == '.' && s[1] == '.') 3210251881Speter return FALSE; 3211251881Speter#endif 3212251881Speter 3213251881Speter /* Skip all separators. */ 3214251881Speter while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR)) 3215251881Speter ++path; 3216251881Speter s = path; 3217251881Speter } 3218251881Speter 3219251881Speter return TRUE; 3220251881Speter} 3221251881Speter 3222251881Speter/* Look for the repository given by URL, using ROOT as the virtual 3223251881Speter * repository root. If we find one, fill in the repos, fs, cfg, 3224251881Speter * repos_url, and fs_path fields of B. Set B->repos's client 3225251881Speter * capabilities to CAPABILITIES, which must be at least as long-lived 3226251881Speter * as POOL, and whose elements are SVN_RA_CAPABILITY_*. 3227251881Speter */ 3228251881Speterstatic svn_error_t *find_repos(const char *url, const char *root, 3229251881Speter server_baton_t *b, 3230251881Speter svn_ra_svn_conn_t *conn, 3231251881Speter const apr_array_header_t *capabilities, 3232251881Speter apr_pool_t *pool) 3233251881Speter{ 3234251881Speter const char *path, *full_path, *repos_root, *fs_path, *hooks_env; 3235251881Speter svn_stringbuf_t *url_buf; 3236251881Speter 3237251881Speter /* Skip past the scheme and authority part. */ 3238251881Speter path = skip_scheme_part(url); 3239251881Speter if (path == NULL) 3240251881Speter return svn_error_createf(SVN_ERR_BAD_URL, NULL, 3241251881Speter "Non-svn URL passed to svn server: '%s'", url); 3242251881Speter 3243251881Speter if (! b->vhost) 3244251881Speter { 3245251881Speter path = strchr(path, '/'); 3246251881Speter if (path == NULL) 3247251881Speter path = ""; 3248251881Speter } 3249251881Speter path = svn_relpath_canonicalize(path, pool); 3250251881Speter path = svn_path_uri_decode(path, pool); 3251251881Speter 3252251881Speter /* Ensure that it isn't possible to escape the root by disallowing 3253251881Speter '..' segments. */ 3254251881Speter if (!repos_path_valid(path)) 3255251881Speter return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 3256251881Speter "Couldn't determine repository path"); 3257251881Speter 3258251881Speter /* Join the server-configured root with the client path. */ 3259251881Speter full_path = svn_dirent_join(svn_dirent_canonicalize(root, pool), 3260251881Speter path, pool); 3261251881Speter 3262251881Speter /* Search for a repository in the full path. */ 3263251881Speter repos_root = svn_repos_find_root_path(full_path, pool); 3264251881Speter if (!repos_root) 3265251881Speter return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL, 3266251881Speter "No repository found in '%s'", url); 3267251881Speter 3268251881Speter /* Open the repository and fill in b with the resulting information. */ 3269251881Speter SVN_ERR(svn_repos_open2(&b->repos, repos_root, b->fs_config, pool)); 3270251881Speter SVN_ERR(svn_repos_remember_client_capabilities(b->repos, capabilities)); 3271251881Speter b->fs = svn_repos_fs(b->repos); 3272251881Speter fs_path = full_path + strlen(repos_root); 3273251881Speter b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool); 3274251881Speter url_buf = svn_stringbuf_create(url, pool); 3275251881Speter svn_path_remove_components(url_buf, 3276251881Speter svn_path_component_count(b->fs_path->data)); 3277251881Speter b->repos_url = url_buf->data; 3278251881Speter b->authz_repos_name = svn_dirent_is_child(root, repos_root, pool); 3279251881Speter if (b->authz_repos_name == NULL) 3280251881Speter b->repos_name = svn_dirent_basename(repos_root, pool); 3281251881Speter else 3282251881Speter b->repos_name = b->authz_repos_name; 3283251881Speter b->repos_name = svn_path_uri_encode(b->repos_name, pool); 3284251881Speter 3285251881Speter /* If the svnserve configuration has not been loaded then load it from the 3286251881Speter * repository. */ 3287251881Speter if (NULL == b->cfg) 3288251881Speter { 3289251881Speter b->base = svn_repos_conf_dir(b->repos, pool); 3290251881Speter 3291251881Speter SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool), 3292251881Speter FALSE, /* must_exist */ 3293251881Speter FALSE, /* section_names_case_sensitive */ 3294251881Speter FALSE, /* option_names_case_sensitive */ 3295251881Speter pool)); 3296251881Speter SVN_ERR(load_pwdb_config(b, conn, pool)); 3297251881Speter SVN_ERR(load_authz_config(b, conn, repos_root, pool)); 3298251881Speter } 3299251881Speter /* svnserve.conf has been loaded via the --config-file option so need 3300251881Speter * to load pwdb and authz. */ 3301251881Speter else 3302251881Speter { 3303251881Speter SVN_ERR(load_pwdb_config(b, conn, pool)); 3304251881Speter SVN_ERR(load_authz_config(b, conn, repos_root, pool)); 3305251881Speter } 3306251881Speter 3307251881Speter#ifdef SVN_HAVE_SASL 3308251881Speter /* Should we use Cyrus SASL? */ 3309251881Speter SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL, 3310251881Speter SVN_CONFIG_OPTION_USE_SASL, FALSE)); 3311251881Speter#endif 3312251881Speter 3313251881Speter /* Use the repository UUID as the default realm. */ 3314251881Speter SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool)); 3315251881Speter svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL, 3316251881Speter SVN_CONFIG_OPTION_REALM, b->realm); 3317251881Speter 3318251881Speter /* Make sure it's possible for the client to authenticate. Note 3319251881Speter that this doesn't take into account any authz configuration read 3320251881Speter above, because we can't know about access it grants until paths 3321251881Speter are given by the client. */ 3322251881Speter if (get_access(b, UNAUTHENTICATED) == NO_ACCESS 3323251881Speter && (get_access(b, AUTHENTICATED) == NO_ACCESS 3324251881Speter || (!b->tunnel_user && !b->pwdb && !b->use_sasl))) 3325251881Speter return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3326251881Speter "No access allowed to this repository", 3327251881Speter b, conn, pool); 3328251881Speter 3329251881Speter /* Configure hook script environment variables. */ 3330251881Speter svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, 3331251881Speter SVN_CONFIG_OPTION_HOOKS_ENV, NULL); 3332251881Speter if (hooks_env) 3333251881Speter hooks_env = svn_dirent_internal_style(hooks_env, pool); 3334251881Speter SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool)); 3335251881Speter 3336251881Speter return SVN_NO_ERROR; 3337251881Speter} 3338251881Speter 3339251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */ 3340251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool) 3341251881Speter{ 3342251881Speter /* Only offer EXTERNAL for connections tunneled over a login agent. */ 3343251881Speter if (!params->tunnel) 3344251881Speter return NULL; 3345251881Speter 3346251881Speter /* If a tunnel user was provided on the command line, use that. */ 3347251881Speter if (params->tunnel_user) 3348251881Speter return params->tunnel_user; 3349251881Speter 3350251881Speter return svn_user_get_name(pool); 3351251881Speter} 3352251881Speter 3353251881Speterstatic void 3354251881Speterfs_warning_func(void *baton, svn_error_t *err) 3355251881Speter{ 3356251881Speter fs_warning_baton_t *b = baton; 3357251881Speter log_server_error(err, b->server, b->conn, b->pool); 3358251881Speter /* TODO: Keep log_pool in the server baton, cleared after every log? */ 3359251881Speter svn_pool_clear(b->pool); 3360251881Speter} 3361251881Speter 3362251881Speter/* Return the normalized repository-relative path for the given PATH 3363251881Speter * (may be a URL, full path or relative path) and fs contained in the 3364251881Speter * server baton BATON. Allocate the result in POOL. 3365251881Speter */ 3366251881Speterstatic const char * 3367251881Speterget_normalized_repo_rel_path(void *baton, 3368251881Speter const char *path, 3369251881Speter apr_pool_t *pool) 3370251881Speter{ 3371251881Speter server_baton_t *sb = baton; 3372251881Speter 3373251881Speter if (svn_path_is_url(path)) 3374251881Speter { 3375251881Speter /* This is a copyfrom URL. */ 3376251881Speter path = svn_uri_skip_ancestor(sb->repos_url, path, pool); 3377251881Speter path = svn_fspath__canonicalize(path, pool); 3378251881Speter } 3379251881Speter else 3380251881Speter { 3381251881Speter /* This is a base-relative path. */ 3382251881Speter if ((path)[0] != '/') 3383251881Speter /* Get an absolute path for use in the FS. */ 3384251881Speter path = svn_fspath__join(sb->fs_path->data, path, pool); 3385251881Speter } 3386251881Speter 3387251881Speter return path; 3388251881Speter} 3389251881Speter 3390251881Speter/* Get the revision root for REVISION in fs given by server baton BATON 3391251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM. 3392251881Speter * Use POOL for allocations. 3393251881Speter */ 3394251881Speterstatic svn_error_t * 3395251881Speterget_revision_root(svn_fs_root_t **fs_root, 3396251881Speter void *baton, 3397251881Speter svn_revnum_t revision, 3398251881Speter apr_pool_t *pool) 3399251881Speter{ 3400251881Speter server_baton_t *sb = baton; 3401251881Speter 3402251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 3403251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool)); 3404251881Speter 3405251881Speter SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool)); 3406251881Speter 3407251881Speter return SVN_NO_ERROR; 3408251881Speter} 3409251881Speter 3410251881Speterstatic svn_error_t * 3411251881Speterfetch_props_func(apr_hash_t **props, 3412251881Speter void *baton, 3413251881Speter const char *path, 3414251881Speter svn_revnum_t base_revision, 3415251881Speter apr_pool_t *result_pool, 3416251881Speter apr_pool_t *scratch_pool) 3417251881Speter{ 3418251881Speter svn_fs_root_t *fs_root; 3419251881Speter svn_error_t *err; 3420251881Speter 3421251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3422251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3423251881Speter 3424251881Speter err = svn_fs_node_proplist(props, fs_root, path, result_pool); 3425251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3426251881Speter { 3427251881Speter svn_error_clear(err); 3428251881Speter *props = apr_hash_make(result_pool); 3429251881Speter return SVN_NO_ERROR; 3430251881Speter } 3431251881Speter else if (err) 3432251881Speter return svn_error_trace(err); 3433251881Speter 3434251881Speter return SVN_NO_ERROR; 3435251881Speter} 3436251881Speter 3437251881Speterstatic svn_error_t * 3438251881Speterfetch_kind_func(svn_node_kind_t *kind, 3439251881Speter void *baton, 3440251881Speter const char *path, 3441251881Speter svn_revnum_t base_revision, 3442251881Speter apr_pool_t *scratch_pool) 3443251881Speter{ 3444251881Speter svn_fs_root_t *fs_root; 3445251881Speter 3446251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3447251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3448251881Speter 3449251881Speter SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); 3450251881Speter 3451251881Speter return SVN_NO_ERROR; 3452251881Speter} 3453251881Speter 3454251881Speterstatic svn_error_t * 3455251881Speterfetch_base_func(const char **filename, 3456251881Speter void *baton, 3457251881Speter const char *path, 3458251881Speter svn_revnum_t base_revision, 3459251881Speter apr_pool_t *result_pool, 3460251881Speter apr_pool_t *scratch_pool) 3461251881Speter{ 3462251881Speter svn_stream_t *contents; 3463251881Speter svn_stream_t *file_stream; 3464251881Speter const char *tmp_filename; 3465251881Speter svn_fs_root_t *fs_root; 3466251881Speter svn_error_t *err; 3467251881Speter 3468251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3469251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3470251881Speter 3471251881Speter err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); 3472251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3473251881Speter { 3474251881Speter svn_error_clear(err); 3475251881Speter *filename = NULL; 3476251881Speter return SVN_NO_ERROR; 3477251881Speter } 3478251881Speter else if (err) 3479251881Speter return svn_error_trace(err); 3480251881Speter SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, 3481251881Speter svn_io_file_del_on_pool_cleanup, 3482251881Speter scratch_pool, scratch_pool)); 3483251881Speter SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); 3484251881Speter 3485251881Speter *filename = apr_pstrdup(result_pool, tmp_filename); 3486251881Speter 3487251881Speter return SVN_NO_ERROR; 3488251881Speter} 3489251881Speter 3490251881Spetersvn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, 3491251881Speter apr_pool_t *pool) 3492251881Speter{ 3493251881Speter svn_error_t *err, *io_err; 3494251881Speter apr_uint64_t ver; 3495251881Speter const char *uuid, *client_url, *ra_client_string, *client_string; 3496251881Speter apr_array_header_t *caplist, *cap_words; 3497251881Speter server_baton_t b; 3498251881Speter fs_warning_baton_t warn_baton; 3499251881Speter svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool); 3500251881Speter 3501251881Speter b.tunnel = params->tunnel; 3502251881Speter b.tunnel_user = get_tunnel_user(params, pool); 3503251881Speter b.read_only = params->read_only; 3504251881Speter b.user = NULL; 3505251881Speter b.username_case = params->username_case; 3506251881Speter b.authz_user = NULL; 3507251881Speter b.base = params->base; 3508251881Speter b.cfg = params->cfg; 3509251881Speter b.pwdb = NULL; 3510251881Speter b.authzdb = NULL; 3511251881Speter b.realm = NULL; 3512251881Speter b.log_file = params->log_file; 3513251881Speter b.pool = pool; 3514251881Speter b.use_sasl = FALSE; 3515251881Speter b.vhost = params->vhost; 3516251881Speter 3517251881Speter /* construct FS configuration parameters */ 3518251881Speter b.fs_config = apr_hash_make(pool); 3519251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, 3520251881Speter params->cache_txdeltas ? "1" :"0"); 3521251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, 3522251881Speter params->cache_fulltexts ? "1" :"0"); 3523251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, 3524251881Speter params->cache_revprops ? "1" :"0"); 3525251881Speter 3526251881Speter /* Send greeting. We don't support version 1 any more, so we can 3527251881Speter * send an empty mechlist. */ 3528251881Speter if (params->compression_level > 0) 3529251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)", 3530251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3531251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3532251881Speter SVN_RA_SVN_CAP_SVNDIFF1, 3533251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3534251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3535251881Speter SVN_RA_SVN_CAP_DEPTH, 3536251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3537251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3538251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3539251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3540251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3541251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3542251881Speter )); 3543251881Speter else 3544251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)", 3545251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3546251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3547251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3548251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3549251881Speter SVN_RA_SVN_CAP_DEPTH, 3550251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3551251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3552251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3553251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3554251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3555251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3556251881Speter )); 3557251881Speter 3558251881Speter /* Read client response, which we assume to be in version 2 format: 3559251881Speter * version, capability list, and client URL; then we do an auth 3560251881Speter * request. */ 3561251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)", 3562251881Speter &ver, &caplist, &client_url, 3563251881Speter &ra_client_string, 3564251881Speter &client_string)); 3565251881Speter if (ver != 2) 3566251881Speter return SVN_NO_ERROR; 3567251881Speter 3568251881Speter client_url = svn_uri_canonicalize(client_url, pool); 3569251881Speter SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); 3570251881Speter 3571251881Speter /* All released versions of Subversion support edit-pipeline, 3572251881Speter * so we do not accept connections from clients that do not. */ 3573251881Speter if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) 3574251881Speter return SVN_NO_ERROR; 3575251881Speter 3576251881Speter /* find_repos needs the capabilities as a list of words (eventually 3577251881Speter they get handed to the start-commit hook). While we could add a 3578251881Speter new interface to re-retrieve them from conn and convert the 3579251881Speter result to a list, it's simpler to just convert caplist by hand 3580251881Speter here, since we already have it and turning 'svn_ra_svn_item_t's 3581251881Speter into 'const char *'s is pretty easy. 3582251881Speter 3583251881Speter We only record capabilities we care about. The client may report 3584251881Speter more (because it doesn't know what the server cares about). */ 3585251881Speter { 3586251881Speter int i; 3587251881Speter svn_ra_svn_item_t *item; 3588251881Speter 3589251881Speter cap_words = apr_array_make(pool, 1, sizeof(const char *)); 3590251881Speter for (i = 0; i < caplist->nelts; i++) 3591251881Speter { 3592251881Speter item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t); 3593251881Speter /* ra_svn_set_capabilities() already type-checked for us */ 3594251881Speter if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0) 3595251881Speter { 3596251881Speter APR_ARRAY_PUSH(cap_words, const char *) 3597251881Speter = SVN_RA_CAPABILITY_MERGEINFO; 3598251881Speter } 3599251881Speter /* Save for operational log. */ 3600251881Speter if (cap_log->len > 0) 3601251881Speter svn_stringbuf_appendcstr(cap_log, " "); 3602251881Speter svn_stringbuf_appendcstr(cap_log, item->u.word); 3603251881Speter } 3604251881Speter } 3605251881Speter 3606251881Speter err = find_repos(client_url, params->root, &b, conn, cap_words, pool); 3607251881Speter if (!err) 3608251881Speter { 3609251881Speter SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE)); 3610251881Speter if (current_access(&b) == NO_ACCESS) 3611251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3612251881Speter "Not authorized for access", 3613251881Speter &b, conn, pool); 3614251881Speter } 3615251881Speter if (err) 3616251881Speter { 3617251881Speter log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn), 3618251881Speter b.user, NULL, pool); 3619251881Speter io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 3620251881Speter svn_error_clear(err); 3621251881Speter SVN_ERR(io_err); 3622251881Speter return svn_ra_svn__flush(conn, pool); 3623251881Speter } 3624251881Speter 3625251881Speter /* Log the open. */ 3626251881Speter if (ra_client_string == NULL || ra_client_string[0] == '\0') 3627251881Speter ra_client_string = "-"; 3628251881Speter else 3629251881Speter ra_client_string = svn_path_uri_encode(ra_client_string, pool); 3630251881Speter if (client_string == NULL || client_string[0] == '\0') 3631251881Speter client_string = "-"; 3632251881Speter else 3633251881Speter client_string = svn_path_uri_encode(client_string, pool); 3634251881Speter SVN_ERR(log_command(&b, conn, pool, 3635251881Speter "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s", 3636251881Speter ver, cap_log->data, 3637251881Speter svn_path_uri_encode(b.fs_path->data, pool), 3638251881Speter ra_client_string, client_string)); 3639251881Speter 3640251881Speter warn_baton.server = &b; 3641251881Speter warn_baton.conn = conn; 3642251881Speter warn_baton.pool = svn_pool_create(pool); 3643251881Speter svn_fs_set_warning_func(b.fs, fs_warning_func, &warn_baton); 3644251881Speter 3645251881Speter SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool)); 3646251881Speter 3647251881Speter /* We can't claim mergeinfo capability until we know whether the 3648251881Speter repository supports mergeinfo (i.e., is not a 1.4 repository), 3649251881Speter but we don't get the repository url from the client until after 3650251881Speter we've already sent the initial list of server capabilities. So 3651251881Speter we list repository capabilities here, in our first response after 3652251881Speter the client has sent the url. */ 3653251881Speter { 3654251881Speter svn_boolean_t supports_mergeinfo; 3655251881Speter SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo, 3656251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, pool)); 3657251881Speter 3658251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!", 3659251881Speter "success", uuid, b.repos_url)); 3660251881Speter if (supports_mergeinfo) 3661251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO)); 3662251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3663251881Speter } 3664251881Speter 3665251881Speter /* Set up editor shims. */ 3666251881Speter { 3667251881Speter svn_delta_shim_callbacks_t *callbacks = 3668251881Speter svn_delta_shim_callbacks_default(pool); 3669251881Speter 3670251881Speter callbacks->fetch_base_func = fetch_base_func; 3671251881Speter callbacks->fetch_props_func = fetch_props_func; 3672251881Speter callbacks->fetch_kind_func = fetch_kind_func; 3673251881Speter callbacks->fetch_baton = &b; 3674251881Speter 3675251881Speter SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks)); 3676251881Speter } 3677251881Speter 3678251881Speter return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE); 3679251881Speter} 3680