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 1529269847Speter if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1530269847Speter wants_inherited_props = FALSE; 1531269847Speter 1532251881Speter full_path = svn_fspath__join(b->fs_path->data, 1533251881Speter svn_relpath_canonicalize(path, pool), pool); 1534251881Speter 1535251881Speter /* Check authorizations */ 1536251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1537251881Speter full_path, FALSE)); 1538251881Speter 1539251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1540251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1541251881Speter 1542251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1543251881Speter svn_log__get_file(full_path, rev, 1544251881Speter want_contents, want_props, pool))); 1545251881Speter 1546251881Speter /* Fetch the properties and a stream for the contents. */ 1547251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 1548251881Speter SVN_CMD_ERR(svn_fs_file_checksum(&checksum, svn_checksum_md5, root, 1549251881Speter full_path, TRUE, pool)); 1550251881Speter hex_digest = svn_checksum_to_cstring_display(checksum, pool); 1551269847Speter 1552269847Speter /* Fetch the file's explicit and/or inherited properties if 1553269847Speter requested. Although the wants-iprops boolean was added to the 1554269847Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1555251881Speter if (want_props || wants_inherited_props) 1556269847Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1557269847Speter wants_inherited_props ? &inherited_props : NULL, 1558269847Speter &ab, root, full_path, 1559251881Speter pool)); 1560251881Speter if (want_contents) 1561251881Speter SVN_CMD_ERR(svn_fs_file_contents(&contents, root, full_path, pool)); 1562251881Speter 1563251881Speter /* Send successful command response with revision and props. */ 1564251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((?c)r(!", "success", 1565251881Speter hex_digest, rev)); 1566251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1567251881Speter 1568251881Speter if (wants_inherited_props) 1569251881Speter { 1570251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1571251881Speter 1572251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1573251881Speter for (i = 0; i < inherited_props->nelts; i++) 1574251881Speter { 1575251881Speter svn_prop_inherited_item_t *iprop = 1576251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1577251881Speter 1578251881Speter svn_pool_clear(iterpool); 1579251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1580251881Speter iprop->path_or_url)); 1581251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1582251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1583251881Speter iprop->path_or_url)); 1584251881Speter } 1585251881Speter svn_pool_destroy(iterpool); 1586251881Speter } 1587251881Speter 1588251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 1589251881Speter 1590251881Speter /* Now send the file's contents. */ 1591251881Speter if (want_contents) 1592251881Speter { 1593251881Speter err = SVN_NO_ERROR; 1594251881Speter while (1) 1595251881Speter { 1596251881Speter len = sizeof(buf); 1597251881Speter err = svn_stream_read(contents, buf, &len); 1598251881Speter if (err) 1599251881Speter break; 1600251881Speter if (len > 0) 1601251881Speter { 1602251881Speter write_str.data = buf; 1603251881Speter write_str.len = len; 1604251881Speter SVN_ERR(svn_ra_svn__write_string(conn, pool, &write_str)); 1605251881Speter } 1606251881Speter if (len < sizeof(buf)) 1607251881Speter { 1608251881Speter err = svn_stream_close(contents); 1609251881Speter break; 1610251881Speter } 1611251881Speter } 1612251881Speter write_err = svn_ra_svn__write_cstring(conn, pool, ""); 1613251881Speter if (write_err) 1614251881Speter { 1615251881Speter svn_error_clear(err); 1616251881Speter return write_err; 1617251881Speter } 1618251881Speter SVN_CMD_ERR(err); 1619251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 1620251881Speter } 1621251881Speter 1622251881Speter return SVN_NO_ERROR; 1623251881Speter} 1624251881Speter 1625251881Speterstatic svn_error_t *get_dir(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1626251881Speter apr_array_header_t *params, void *baton) 1627251881Speter{ 1628251881Speter server_baton_t *b = baton; 1629251881Speter const char *path, *full_path; 1630251881Speter svn_revnum_t rev; 1631251881Speter apr_hash_t *entries, *props = NULL; 1632251881Speter apr_array_header_t *inherited_props; 1633251881Speter apr_hash_index_t *hi; 1634251881Speter svn_fs_root_t *root; 1635251881Speter apr_pool_t *subpool; 1636251881Speter svn_boolean_t want_props, want_contents; 1637251881Speter apr_uint64_t wants_inherited_props; 1638251881Speter apr_uint64_t dirent_fields; 1639251881Speter apr_array_header_t *dirent_fields_list = NULL; 1640251881Speter svn_ra_svn_item_t *elt; 1641251881Speter int i; 1642251881Speter authz_baton_t ab; 1643251881Speter 1644251881Speter ab.server = b; 1645251881Speter ab.conn = conn; 1646251881Speter 1647251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)bb?l?B", &path, &rev, 1648251881Speter &want_props, &want_contents, 1649251881Speter &dirent_fields_list, 1650251881Speter &wants_inherited_props)); 1651251881Speter 1652269847Speter if (wants_inherited_props == SVN_RA_SVN_UNSPECIFIED_NUMBER) 1653269847Speter wants_inherited_props = FALSE; 1654269847Speter 1655251881Speter if (! dirent_fields_list) 1656251881Speter { 1657251881Speter dirent_fields = SVN_DIRENT_ALL; 1658251881Speter } 1659251881Speter else 1660251881Speter { 1661251881Speter dirent_fields = 0; 1662251881Speter 1663251881Speter for (i = 0; i < dirent_fields_list->nelts; ++i) 1664251881Speter { 1665251881Speter elt = &APR_ARRAY_IDX(dirent_fields_list, i, svn_ra_svn_item_t); 1666251881Speter 1667251881Speter if (elt->kind != SVN_RA_SVN_WORD) 1668251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 1669251881Speter "Dirent field not a string"); 1670251881Speter 1671251881Speter if (strcmp(SVN_RA_SVN_DIRENT_KIND, elt->u.word) == 0) 1672251881Speter dirent_fields |= SVN_DIRENT_KIND; 1673251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_SIZE, elt->u.word) == 0) 1674251881Speter dirent_fields |= SVN_DIRENT_SIZE; 1675251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_HAS_PROPS, elt->u.word) == 0) 1676251881Speter dirent_fields |= SVN_DIRENT_HAS_PROPS; 1677251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_CREATED_REV, elt->u.word) == 0) 1678251881Speter dirent_fields |= SVN_DIRENT_CREATED_REV; 1679251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_TIME, elt->u.word) == 0) 1680251881Speter dirent_fields |= SVN_DIRENT_TIME; 1681251881Speter else if (strcmp(SVN_RA_SVN_DIRENT_LAST_AUTHOR, elt->u.word) == 0) 1682251881Speter dirent_fields |= SVN_DIRENT_LAST_AUTHOR; 1683251881Speter } 1684251881Speter } 1685251881Speter 1686251881Speter full_path = svn_fspath__join(b->fs_path->data, 1687251881Speter svn_relpath_canonicalize(path, pool), pool); 1688251881Speter 1689251881Speter /* Check authorizations */ 1690251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 1691251881Speter full_path, FALSE)); 1692251881Speter 1693251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1694251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1695251881Speter 1696251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1697251881Speter svn_log__get_dir(full_path, rev, 1698251881Speter want_contents, want_props, 1699251881Speter dirent_fields, pool))); 1700251881Speter 1701251881Speter /* Fetch the root of the appropriate revision. */ 1702251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 1703251881Speter 1704269847Speter /* Fetch the directory's explicit and/or inherited properties if 1705269847Speter requested. Although the wants-iprops boolean was added to the 1706269847Speter protocol in 1.8 a standard 1.8 client never requests iprops. */ 1707251881Speter if (want_props || wants_inherited_props) 1708269847Speter SVN_CMD_ERR(get_props(want_props ? &props : NULL, 1709269847Speter wants_inherited_props ? &inherited_props : NULL, 1710269847Speter &ab, root, full_path, 1711251881Speter pool)); 1712251881Speter 1713251881Speter /* Begin response ... */ 1714251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(r(!", "success", rev)); 1715251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, props)); 1716251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(!")); 1717251881Speter 1718251881Speter /* Fetch the directory entries if requested and send them immediately. */ 1719251881Speter if (want_contents) 1720251881Speter { 1721251881Speter /* Use epoch for a placeholder for a missing date. */ 1722251881Speter const char *missing_date = svn_time_to_cstring(0, pool); 1723251881Speter 1724251881Speter SVN_CMD_ERR(svn_fs_dir_entries(&entries, root, full_path, pool)); 1725251881Speter 1726251881Speter /* Transform the hash table's FS entries into dirents. This probably 1727251881Speter * belongs in libsvn_repos. */ 1728251881Speter subpool = svn_pool_create(pool); 1729251881Speter for (hi = apr_hash_first(pool, entries); hi; hi = apr_hash_next(hi)) 1730251881Speter { 1731251881Speter const char *name = svn__apr_hash_index_key(hi); 1732251881Speter svn_fs_dirent_t *fsent = svn__apr_hash_index_val(hi); 1733251881Speter const char *file_path; 1734251881Speter 1735251881Speter /* The fields in the entry tuple. */ 1736251881Speter svn_node_kind_t entry_kind = svn_node_none; 1737251881Speter svn_filesize_t entry_size = 0; 1738251881Speter svn_boolean_t has_props = FALSE; 1739251881Speter /* If 'created rev' was not requested, send 0. We can't use 1740251881Speter * SVN_INVALID_REVNUM as the tuple field is not optional. 1741251881Speter * See the email thread on dev@, 2012-03-28, subject 1742251881Speter * "buildbot failure in ASF Buildbot on svn-slik-w2k3-x64-ra", 1743251881Speter * <http://svn.haxx.se/dev/archive-2012-03/0655.shtml>. */ 1744251881Speter svn_revnum_t created_rev = 0; 1745251881Speter const char *cdate = NULL; 1746251881Speter const char *last_author = NULL; 1747251881Speter 1748251881Speter svn_pool_clear(subpool); 1749251881Speter 1750251881Speter file_path = svn_fspath__join(full_path, name, subpool); 1751251881Speter if (! lookup_access(subpool, b, conn, svn_authz_read, 1752251881Speter file_path, FALSE)) 1753251881Speter continue; 1754251881Speter 1755251881Speter if (dirent_fields & SVN_DIRENT_KIND) 1756251881Speter entry_kind = fsent->kind; 1757251881Speter 1758251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 1759251881Speter if (entry_kind != svn_node_dir) 1760251881Speter SVN_CMD_ERR(svn_fs_file_length(&entry_size, root, file_path, 1761251881Speter subpool)); 1762251881Speter 1763251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 1764251881Speter { 1765251881Speter apr_hash_t *file_props; 1766251881Speter 1767251881Speter /* has_props */ 1768251881Speter SVN_CMD_ERR(svn_fs_node_proplist(&file_props, root, file_path, 1769251881Speter subpool)); 1770251881Speter has_props = (apr_hash_count(file_props) > 0); 1771251881Speter } 1772251881Speter 1773251881Speter if ((dirent_fields & SVN_DIRENT_LAST_AUTHOR) 1774251881Speter || (dirent_fields & SVN_DIRENT_TIME) 1775251881Speter || (dirent_fields & SVN_DIRENT_CREATED_REV)) 1776251881Speter { 1777251881Speter /* created_rev, last_author, time */ 1778251881Speter SVN_CMD_ERR(svn_repos_get_committed_info(&created_rev, 1779251881Speter &cdate, 1780251881Speter &last_author, 1781251881Speter root, 1782251881Speter file_path, 1783251881Speter subpool)); 1784251881Speter } 1785251881Speter 1786251881Speter /* The client does not properly handle a missing CDATE. For 1787251881Speter interoperability purposes, we must fill in some junk. 1788251881Speter 1789251881Speter See libsvn_ra_svn/client.c:ra_svn_get_dir() */ 1790251881Speter if (cdate == NULL) 1791251881Speter cdate = missing_date; 1792251881Speter 1793251881Speter /* Send the entry. */ 1794251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "cwnbr(?c)(?c)", name, 1795251881Speter svn_node_kind_to_word(entry_kind), 1796251881Speter (apr_uint64_t) entry_size, 1797251881Speter has_props, created_rev, 1798251881Speter cdate, last_author)); 1799251881Speter } 1800251881Speter svn_pool_destroy(subpool); 1801251881Speter } 1802251881Speter 1803251881Speter if (wants_inherited_props) 1804251881Speter { 1805251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1806251881Speter 1807251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)(?!")); 1808251881Speter for (i = 0; i < inherited_props->nelts; i++) 1809251881Speter { 1810251881Speter svn_prop_inherited_item_t *iprop = 1811251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 1812251881Speter 1813251881Speter svn_pool_clear(iterpool); 1814251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 1815251881Speter iprop->path_or_url)); 1816251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 1817251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 1818251881Speter iprop->path_or_url)); 1819251881Speter } 1820251881Speter svn_pool_destroy(iterpool); 1821251881Speter } 1822251881Speter 1823251881Speter /* Finish response. */ 1824251881Speter return svn_ra_svn__write_tuple(conn, pool, "!))"); 1825251881Speter} 1826251881Speter 1827251881Speterstatic svn_error_t *update(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1828251881Speter apr_array_header_t *params, void *baton) 1829251881Speter{ 1830251881Speter server_baton_t *b = baton; 1831251881Speter svn_revnum_t rev; 1832251881Speter const char *target, *full_path, *depth_word; 1833251881Speter svn_boolean_t recurse; 1834251881Speter apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ 1835251881Speter apr_uint64_t ignore_ancestry; /* Optional; default FALSE */ 1836251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1837251881Speter handle that by converting recurse if necessary. */ 1838251881Speter svn_depth_t depth = svn_depth_unknown; 1839251881Speter svn_boolean_t is_checkout; 1840251881Speter 1841251881Speter /* Parse the arguments. */ 1842251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cb?wB?B", &rev, &target, 1843251881Speter &recurse, &depth_word, 1844251881Speter &send_copyfrom_args, &ignore_ancestry)); 1845251881Speter target = svn_relpath_canonicalize(target, pool); 1846251881Speter 1847251881Speter if (depth_word) 1848251881Speter depth = svn_depth_from_word(depth_word); 1849251881Speter else 1850251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1851251881Speter 1852251881Speter full_path = svn_fspath__join(b->fs_path->data, target, pool); 1853251881Speter /* Check authorization and authenticate the user if necessary. */ 1854251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, full_path, FALSE)); 1855251881Speter 1856251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1857251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1858251881Speter 1859251881Speter SVN_ERR(accept_report(&is_checkout, NULL, 1860251881Speter conn, pool, b, rev, target, NULL, TRUE, 1861251881Speter depth, 1862251881Speter (send_copyfrom_args == TRUE) /* send_copyfrom_args */, 1863251881Speter (ignore_ancestry == TRUE) /* ignore_ancestry */)); 1864251881Speter if (is_checkout) 1865251881Speter { 1866251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1867251881Speter svn_log__checkout(full_path, rev, 1868251881Speter depth, pool))); 1869251881Speter } 1870251881Speter else 1871251881Speter { 1872251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1873251881Speter svn_log__update(full_path, rev, depth, 1874251881Speter send_copyfrom_args, pool))); 1875251881Speter } 1876251881Speter 1877251881Speter return SVN_NO_ERROR; 1878251881Speter} 1879251881Speter 1880251881Speterstatic svn_error_t *switch_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1881251881Speter apr_array_header_t *params, void *baton) 1882251881Speter{ 1883251881Speter server_baton_t *b = baton; 1884251881Speter svn_revnum_t rev; 1885251881Speter const char *target, *depth_word; 1886251881Speter const char *switch_url, *switch_path; 1887251881Speter svn_boolean_t recurse; 1888251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1889251881Speter handle that by converting recurse if necessary. */ 1890251881Speter svn_depth_t depth = svn_depth_unknown; 1891251881Speter apr_uint64_t send_copyfrom_args; /* Optional; default FALSE */ 1892251881Speter apr_uint64_t ignore_ancestry; /* Optional; default TRUE */ 1893251881Speter 1894251881Speter /* Parse the arguments. */ 1895251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbc?w?BB", &rev, &target, 1896251881Speter &recurse, &switch_url, &depth_word, 1897251881Speter &send_copyfrom_args, &ignore_ancestry)); 1898251881Speter target = svn_relpath_canonicalize(target, pool); 1899251881Speter switch_url = svn_uri_canonicalize(switch_url, pool); 1900251881Speter 1901251881Speter if (depth_word) 1902251881Speter depth = svn_depth_from_word(depth_word); 1903251881Speter else 1904251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1905251881Speter 1906251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1907251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1908251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1909251881Speter 1910251881Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), 1911251881Speter svn_path_uri_decode(switch_url, pool), 1912251881Speter &switch_path)); 1913251881Speter 1914251881Speter { 1915251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 1916251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1917251881Speter svn_log__switch(full_path, switch_path, rev, 1918251881Speter depth, pool))); 1919251881Speter } 1920251881Speter 1921251881Speter return accept_report(NULL, NULL, 1922251881Speter conn, pool, b, rev, target, switch_path, TRUE, 1923251881Speter depth, 1924251881Speter (send_copyfrom_args == TRUE) /* send_copyfrom_args */, 1925251881Speter (ignore_ancestry != FALSE) /* ignore_ancestry */); 1926251881Speter} 1927251881Speter 1928251881Speterstatic svn_error_t *status(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1929251881Speter apr_array_header_t *params, void *baton) 1930251881Speter{ 1931251881Speter server_baton_t *b = baton; 1932251881Speter svn_revnum_t rev; 1933251881Speter const char *target, *depth_word; 1934251881Speter svn_boolean_t recurse; 1935251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1936251881Speter handle that by converting recurse if necessary. */ 1937251881Speter svn_depth_t depth = svn_depth_unknown; 1938251881Speter 1939251881Speter /* Parse the arguments. */ 1940251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "cb?(?r)?w", 1941251881Speter &target, &recurse, &rev, &depth_word)); 1942251881Speter target = svn_relpath_canonicalize(target, pool); 1943251881Speter 1944251881Speter if (depth_word) 1945251881Speter depth = svn_depth_from_word(depth_word); 1946251881Speter else 1947251881Speter depth = SVN_DEPTH_INFINITY_OR_EMPTY(recurse); 1948251881Speter 1949251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 1950251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 1951251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 1952251881Speter 1953251881Speter { 1954251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 1955251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 1956251881Speter svn_log__status(full_path, rev, depth, pool))); 1957251881Speter } 1958251881Speter 1959251881Speter return accept_report(NULL, NULL, conn, pool, b, rev, target, NULL, FALSE, 1960251881Speter depth, FALSE, FALSE); 1961251881Speter} 1962251881Speter 1963251881Speterstatic svn_error_t *diff(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 1964251881Speter apr_array_header_t *params, void *baton) 1965251881Speter{ 1966251881Speter server_baton_t *b = baton; 1967251881Speter svn_revnum_t rev; 1968251881Speter const char *target, *versus_url, *versus_path, *depth_word; 1969251881Speter svn_boolean_t recurse, ignore_ancestry; 1970251881Speter svn_boolean_t text_deltas; 1971251881Speter /* Default to unknown. Old clients won't send depth, but we'll 1972251881Speter handle that by converting recurse if necessary. */ 1973251881Speter svn_depth_t depth = svn_depth_unknown; 1974251881Speter 1975251881Speter /* Parse the arguments. */ 1976251881Speter if (params->nelts == 5) 1977251881Speter { 1978251881Speter /* Clients before 1.4 don't send the text_deltas boolean or depth. */ 1979251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbc", &rev, &target, 1980251881Speter &recurse, &ignore_ancestry, &versus_url)); 1981251881Speter text_deltas = TRUE; 1982251881Speter depth_word = NULL; 1983251881Speter } 1984251881Speter else 1985251881Speter { 1986251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?r)cbbcb?w", 1987251881Speter &rev, &target, &recurse, 1988251881Speter &ignore_ancestry, &versus_url, 1989251881Speter &text_deltas, &depth_word)); 1990251881Speter } 1991251881Speter target = svn_relpath_canonicalize(target, pool); 1992251881Speter versus_url = svn_uri_canonicalize(versus_url, pool); 1993251881Speter 1994251881Speter if (depth_word) 1995251881Speter depth = svn_depth_from_word(depth_word); 1996251881Speter else 1997251881Speter depth = SVN_DEPTH_INFINITY_OR_FILES(recurse); 1998251881Speter 1999251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2000251881Speter 2001251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2002251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 2003251881Speter SVN_CMD_ERR(get_fs_path(svn_path_uri_decode(b->repos_url, pool), 2004251881Speter svn_path_uri_decode(versus_url, pool), 2005251881Speter &versus_path)); 2006251881Speter 2007251881Speter { 2008251881Speter const char *full_path = svn_fspath__join(b->fs_path->data, target, pool); 2009251881Speter svn_revnum_t from_rev; 2010251881Speter SVN_ERR(accept_report(NULL, &from_rev, 2011251881Speter conn, pool, b, rev, target, versus_path, 2012251881Speter text_deltas, depth, FALSE, ignore_ancestry)); 2013251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2014251881Speter svn_log__diff(full_path, from_rev, versus_path, 2015251881Speter rev, depth, ignore_ancestry, 2016251881Speter pool))); 2017251881Speter } 2018251881Speter return SVN_NO_ERROR; 2019251881Speter} 2020251881Speter 2021251881Speter/* Regardless of whether a client's capabilities indicate an 2022251881Speter understanding of this command (by way of SVN_RA_SVN_CAP_MERGEINFO), 2023251881Speter we provide a response. 2024251881Speter 2025251881Speter ASSUMPTION: When performing a 'merge' with two URLs at different 2026251881Speter revisions, the client will call this command more than once. */ 2027251881Speterstatic svn_error_t *get_mergeinfo(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2028251881Speter apr_array_header_t *params, void *baton) 2029251881Speter{ 2030251881Speter server_baton_t *b = baton; 2031251881Speter svn_revnum_t rev; 2032251881Speter apr_array_header_t *paths, *canonical_paths; 2033251881Speter svn_mergeinfo_catalog_t mergeinfo; 2034251881Speter int i; 2035251881Speter apr_hash_index_t *hi; 2036251881Speter const char *inherit_word; 2037251881Speter svn_mergeinfo_inheritance_t inherit; 2038251881Speter svn_boolean_t include_descendants; 2039251881Speter apr_pool_t *iterpool; 2040251881Speter authz_baton_t ab; 2041251881Speter 2042251881Speter ab.server = b; 2043251881Speter ab.conn = conn; 2044251881Speter 2045251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)wb", &paths, &rev, 2046251881Speter &inherit_word, &include_descendants)); 2047251881Speter inherit = svn_inheritance_from_word(inherit_word); 2048251881Speter 2049251881Speter /* Canonicalize the paths which mergeinfo has been requested for. */ 2050251881Speter canonical_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2051251881Speter for (i = 0; i < paths->nelts; i++) 2052251881Speter { 2053251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2054251881Speter const char *full_path; 2055251881Speter 2056251881Speter if (item->kind != SVN_RA_SVN_STRING) 2057251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2058251881Speter _("Path is not a string")); 2059251881Speter full_path = svn_relpath_canonicalize(item->u.string->data, pool); 2060251881Speter full_path = svn_fspath__join(b->fs_path->data, full_path, pool); 2061251881Speter APR_ARRAY_PUSH(canonical_paths, const char *) = full_path; 2062251881Speter } 2063251881Speter 2064251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2065251881Speter svn_log__get_mergeinfo(canonical_paths, inherit, 2066251881Speter include_descendants, 2067251881Speter pool))); 2068251881Speter 2069251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2070251881Speter SVN_CMD_ERR(svn_repos_fs_get_mergeinfo(&mergeinfo, b->repos, 2071251881Speter canonical_paths, rev, 2072251881Speter inherit, 2073251881Speter include_descendants, 2074251881Speter authz_check_access_cb_func(b), &ab, 2075251881Speter pool)); 2076251881Speter SVN_ERR(svn_mergeinfo__remove_prefix_from_catalog(&mergeinfo, mergeinfo, 2077251881Speter b->fs_path->data, pool)); 2078251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2079251881Speter iterpool = svn_pool_create(pool); 2080251881Speter for (hi = apr_hash_first(pool, mergeinfo); hi; hi = apr_hash_next(hi)) 2081251881Speter { 2082251881Speter const char *key = svn__apr_hash_index_key(hi); 2083251881Speter svn_mergeinfo_t value = svn__apr_hash_index_val(hi); 2084251881Speter svn_string_t *mergeinfo_string; 2085251881Speter 2086251881Speter svn_pool_clear(iterpool); 2087251881Speter 2088251881Speter SVN_ERR(svn_mergeinfo_to_string(&mergeinfo_string, value, iterpool)); 2089251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "cs", key, 2090251881Speter mergeinfo_string)); 2091251881Speter } 2092251881Speter svn_pool_destroy(iterpool); 2093251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2094251881Speter 2095251881Speter return SVN_NO_ERROR; 2096251881Speter} 2097251881Speter 2098251881Speter/* Send a log entry to the client. */ 2099251881Speterstatic svn_error_t *log_receiver(void *baton, 2100251881Speter svn_log_entry_t *log_entry, 2101251881Speter apr_pool_t *pool) 2102251881Speter{ 2103251881Speter log_baton_t *b = baton; 2104251881Speter svn_ra_svn_conn_t *conn = b->conn; 2105251881Speter apr_hash_index_t *h; 2106251881Speter svn_boolean_t invalid_revnum = FALSE; 2107251881Speter char action[2]; 2108251881Speter const char *author, *date, *message; 2109251881Speter apr_uint64_t revprop_count; 2110251881Speter 2111251881Speter if (log_entry->revision == SVN_INVALID_REVNUM) 2112251881Speter { 2113251881Speter /* If the stack depth is zero, we've seen the last revision, so don't 2114251881Speter send it, just return. */ 2115251881Speter if (b->stack_depth == 0) 2116251881Speter return SVN_NO_ERROR; 2117251881Speter 2118251881Speter /* Because the svn protocol won't let us send an invalid revnum, we have 2119251881Speter to fudge here and send an additional flag. */ 2120251881Speter log_entry->revision = 0; 2121251881Speter invalid_revnum = TRUE; 2122251881Speter b->stack_depth--; 2123251881Speter } 2124251881Speter 2125251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "(!")); 2126251881Speter if (log_entry->changed_paths2) 2127251881Speter { 2128251881Speter for (h = apr_hash_first(pool, log_entry->changed_paths2); h; 2129251881Speter h = apr_hash_next(h)) 2130251881Speter { 2131251881Speter const char *path = svn__apr_hash_index_key(h); 2132251881Speter svn_log_changed_path2_t *change = svn__apr_hash_index_val(h); 2133251881Speter 2134251881Speter action[0] = change->action; 2135251881Speter action[1] = '\0'; 2136251881Speter SVN_ERR(svn_ra_svn__write_tuple( 2137251881Speter conn, pool, "cw(?cr)(cbb)", 2138251881Speter path, 2139251881Speter action, 2140251881Speter change->copyfrom_path, 2141251881Speter change->copyfrom_rev, 2142251881Speter svn_node_kind_to_word(change->node_kind), 2143251881Speter /* text_modified and props_modified are never unknown */ 2144251881Speter change->text_modified == svn_tristate_true, 2145251881Speter change->props_modified == svn_tristate_true)); 2146251881Speter } 2147251881Speter } 2148251881Speter svn_compat_log_revprops_out(&author, &date, &message, log_entry->revprops); 2149251881Speter svn_compat_log_revprops_clear(log_entry->revprops); 2150251881Speter if (log_entry->revprops) 2151251881Speter revprop_count = apr_hash_count(log_entry->revprops); 2152251881Speter else 2153251881Speter revprop_count = 0; 2154251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)r(?c)(?c)(?c)bbn(!", 2155251881Speter log_entry->revision, 2156251881Speter author, date, message, 2157251881Speter log_entry->has_children, 2158251881Speter invalid_revnum, revprop_count)); 2159251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, pool, log_entry->revprops)); 2160251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)b", 2161251881Speter log_entry->subtractive_merge)); 2162251881Speter 2163251881Speter if (log_entry->has_children) 2164251881Speter b->stack_depth++; 2165251881Speter 2166251881Speter return SVN_NO_ERROR; 2167251881Speter} 2168251881Speter 2169251881Speterstatic svn_error_t *log_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2170251881Speter apr_array_header_t *params, void *baton) 2171251881Speter{ 2172251881Speter svn_error_t *err, *write_err; 2173251881Speter server_baton_t *b = baton; 2174251881Speter svn_revnum_t start_rev, end_rev; 2175251881Speter const char *full_path; 2176251881Speter svn_boolean_t send_changed_paths, strict_node, include_merged_revisions; 2177251881Speter apr_array_header_t *paths, *full_paths, *revprop_items, *revprops; 2178251881Speter char *revprop_word; 2179251881Speter svn_ra_svn_item_t *elt; 2180251881Speter int i; 2181251881Speter apr_uint64_t limit, include_merged_revs_param; 2182251881Speter log_baton_t lb; 2183251881Speter authz_baton_t ab; 2184251881Speter 2185251881Speter ab.server = b; 2186251881Speter ab.conn = conn; 2187251881Speter 2188251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "l(?r)(?r)bb?n?Bwl", &paths, 2189251881Speter &start_rev, &end_rev, &send_changed_paths, 2190251881Speter &strict_node, &limit, 2191251881Speter &include_merged_revs_param, 2192251881Speter &revprop_word, &revprop_items)); 2193251881Speter 2194251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2195251881Speter include_merged_revisions = FALSE; 2196251881Speter else 2197251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2198251881Speter 2199251881Speter if (revprop_word == NULL) 2200251881Speter /* pre-1.5 client */ 2201251881Speter revprops = svn_compat_log_revprops_in(pool); 2202251881Speter else if (strcmp(revprop_word, "all-revprops") == 0) 2203251881Speter revprops = NULL; 2204251881Speter else if (strcmp(revprop_word, "revprops") == 0) 2205251881Speter { 2206251881Speter SVN_ERR_ASSERT(revprop_items); 2207251881Speter 2208251881Speter revprops = apr_array_make(pool, revprop_items->nelts, 2209251881Speter sizeof(char *)); 2210251881Speter for (i = 0; i < revprop_items->nelts; i++) 2211251881Speter { 2212251881Speter elt = &APR_ARRAY_IDX(revprop_items, i, svn_ra_svn_item_t); 2213251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2214251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2215251881Speter _("Log revprop entry not a string")); 2216251881Speter APR_ARRAY_PUSH(revprops, const char *) = elt->u.string->data; 2217251881Speter } 2218251881Speter } 2219251881Speter else 2220251881Speter return svn_error_createf(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2221251881Speter _("Unknown revprop word '%s' in log command"), 2222251881Speter revprop_word); 2223251881Speter 2224251881Speter /* If we got an unspecified number then the user didn't send us anything, 2225251881Speter so we assume no limit. If it's larger than INT_MAX then someone is 2226251881Speter messing with us, since we know the svn client libraries will never send 2227251881Speter us anything that big, so play it safe and default to no limit. */ 2228251881Speter if (limit == SVN_RA_SVN_UNSPECIFIED_NUMBER || limit > INT_MAX) 2229251881Speter limit = 0; 2230251881Speter 2231251881Speter full_paths = apr_array_make(pool, paths->nelts, sizeof(const char *)); 2232251881Speter for (i = 0; i < paths->nelts; i++) 2233251881Speter { 2234251881Speter elt = &APR_ARRAY_IDX(paths, i, svn_ra_svn_item_t); 2235251881Speter if (elt->kind != SVN_RA_SVN_STRING) 2236251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2237251881Speter _("Log path entry not a string")); 2238251881Speter full_path = svn_relpath_canonicalize(elt->u.string->data, pool), 2239251881Speter full_path = svn_fspath__join(b->fs_path->data, full_path, pool); 2240251881Speter APR_ARRAY_PUSH(full_paths, const char *) = full_path; 2241251881Speter } 2242251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2243251881Speter 2244251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2245251881Speter svn_log__log(full_paths, start_rev, end_rev, 2246251881Speter (int) limit, send_changed_paths, 2247251881Speter strict_node, include_merged_revisions, 2248251881Speter revprops, pool))); 2249251881Speter 2250251881Speter /* Get logs. (Can't report errors back to the client at this point.) */ 2251251881Speter lb.fs_path = b->fs_path->data; 2252251881Speter lb.conn = conn; 2253251881Speter lb.stack_depth = 0; 2254251881Speter err = svn_repos_get_logs4(b->repos, full_paths, start_rev, end_rev, 2255251881Speter (int) limit, send_changed_paths, strict_node, 2256251881Speter include_merged_revisions, revprops, 2257251881Speter authz_check_access_cb_func(b), &ab, log_receiver, 2258251881Speter &lb, pool); 2259251881Speter 2260251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2261251881Speter if (write_err) 2262251881Speter { 2263251881Speter svn_error_clear(err); 2264251881Speter return write_err; 2265251881Speter } 2266251881Speter SVN_CMD_ERR(err); 2267251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2268251881Speter return SVN_NO_ERROR; 2269251881Speter} 2270251881Speter 2271251881Speterstatic svn_error_t *check_path(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2272251881Speter apr_array_header_t *params, void *baton) 2273251881Speter{ 2274251881Speter server_baton_t *b = baton; 2275251881Speter svn_revnum_t rev; 2276251881Speter const char *path, *full_path; 2277251881Speter svn_fs_root_t *root; 2278251881Speter svn_node_kind_t kind; 2279251881Speter 2280251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2281251881Speter full_path = svn_fspath__join(b->fs_path->data, 2282251881Speter svn_relpath_canonicalize(path, pool), pool); 2283251881Speter 2284251881Speter /* Check authorizations */ 2285251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2286251881Speter full_path, FALSE)); 2287251881Speter 2288251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2289251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 2290251881Speter 2291251881Speter SVN_ERR(log_command(b, conn, pool, "check-path %s@%d", 2292251881Speter svn_path_uri_encode(full_path, pool), rev)); 2293251881Speter 2294251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 2295251881Speter SVN_CMD_ERR(svn_fs_check_path(&kind, root, full_path, pool)); 2296251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "w", 2297251881Speter svn_node_kind_to_word(kind))); 2298251881Speter return SVN_NO_ERROR; 2299251881Speter} 2300251881Speter 2301251881Speterstatic svn_error_t *stat_cmd(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2302251881Speter apr_array_header_t *params, void *baton) 2303251881Speter{ 2304251881Speter server_baton_t *b = baton; 2305251881Speter svn_revnum_t rev; 2306251881Speter const char *path, *full_path, *cdate; 2307251881Speter svn_fs_root_t *root; 2308251881Speter svn_dirent_t *dirent; 2309251881Speter 2310251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)", &path, &rev)); 2311251881Speter full_path = svn_fspath__join(b->fs_path->data, 2312251881Speter svn_relpath_canonicalize(path, pool), pool); 2313251881Speter 2314251881Speter /* Check authorizations */ 2315251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2316251881Speter full_path, FALSE)); 2317251881Speter 2318251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 2319251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 2320251881Speter 2321251881Speter SVN_ERR(log_command(b, conn, pool, "stat %s@%d", 2322251881Speter svn_path_uri_encode(full_path, pool), rev)); 2323251881Speter 2324251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, pool)); 2325251881Speter SVN_CMD_ERR(svn_repos_stat(&dirent, root, full_path, pool)); 2326251881Speter 2327251881Speter /* Need to return the equivalent of "(?l)", since that's what the 2328251881Speter client is reading. */ 2329251881Speter 2330251881Speter if (dirent == NULL) 2331251881Speter { 2332251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "()")); 2333251881Speter return SVN_NO_ERROR; 2334251881Speter } 2335251881Speter 2336251881Speter cdate = (dirent->time == (time_t) -1) ? NULL 2337251881Speter : svn_time_to_cstring(dirent->time, pool); 2338251881Speter 2339251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "((wnbr(?c)(?c)))", 2340251881Speter svn_node_kind_to_word(dirent->kind), 2341251881Speter (apr_uint64_t) dirent->size, 2342251881Speter dirent->has_props, dirent->created_rev, 2343251881Speter cdate, dirent->last_author)); 2344251881Speter 2345251881Speter return SVN_NO_ERROR; 2346251881Speter} 2347251881Speter 2348251881Speterstatic svn_error_t *get_locations(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2349251881Speter apr_array_header_t *params, void *baton) 2350251881Speter{ 2351251881Speter svn_error_t *err, *write_err; 2352251881Speter server_baton_t *b = baton; 2353251881Speter svn_revnum_t revision; 2354251881Speter apr_array_header_t *location_revisions, *loc_revs_proto; 2355251881Speter svn_ra_svn_item_t *elt; 2356251881Speter int i; 2357251881Speter const char *relative_path; 2358251881Speter svn_revnum_t peg_revision; 2359251881Speter apr_hash_t *fs_locations; 2360251881Speter const char *abs_path; 2361251881Speter authz_baton_t ab; 2362251881Speter 2363251881Speter ab.server = b; 2364251881Speter ab.conn = conn; 2365251881Speter 2366251881Speter /* Parse the arguments. */ 2367251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crl", &relative_path, 2368251881Speter &peg_revision, 2369251881Speter &loc_revs_proto)); 2370251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2371251881Speter 2372251881Speter abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); 2373251881Speter 2374251881Speter location_revisions = apr_array_make(pool, loc_revs_proto->nelts, 2375251881Speter sizeof(svn_revnum_t)); 2376251881Speter for (i = 0; i < loc_revs_proto->nelts; i++) 2377251881Speter { 2378251881Speter elt = &APR_ARRAY_IDX(loc_revs_proto, i, svn_ra_svn_item_t); 2379251881Speter if (elt->kind != SVN_RA_SVN_NUMBER) 2380251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2381251881Speter "Get-locations location revisions entry " 2382251881Speter "not a revision number"); 2383251881Speter revision = (svn_revnum_t)(elt->u.number); 2384251881Speter APR_ARRAY_PUSH(location_revisions, svn_revnum_t) = revision; 2385251881Speter } 2386251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2387251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2388251881Speter svn_log__get_locations(abs_path, peg_revision, 2389251881Speter location_revisions, pool))); 2390251881Speter 2391251881Speter /* All the parameters are fine - let's perform the query against the 2392251881Speter * repository. */ 2393251881Speter 2394251881Speter /* We store both err and write_err here, so the client will get 2395251881Speter * the "done" even if there was an error in fetching the results. */ 2396251881Speter 2397251881Speter err = svn_repos_trace_node_locations(b->fs, &fs_locations, abs_path, 2398251881Speter peg_revision, location_revisions, 2399251881Speter authz_check_access_cb_func(b), &ab, 2400251881Speter pool); 2401251881Speter 2402251881Speter /* Now, write the results to the connection. */ 2403251881Speter if (!err) 2404251881Speter { 2405251881Speter if (fs_locations) 2406251881Speter { 2407251881Speter apr_hash_index_t *iter; 2408251881Speter 2409251881Speter for (iter = apr_hash_first(pool, fs_locations); iter; 2410251881Speter iter = apr_hash_next(iter)) 2411251881Speter { 2412251881Speter const svn_revnum_t *iter_key = svn__apr_hash_index_key(iter); 2413251881Speter const char *iter_value = svn__apr_hash_index_val(iter); 2414251881Speter 2415251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "rc", 2416251881Speter *iter_key, iter_value)); 2417251881Speter } 2418251881Speter } 2419251881Speter } 2420251881Speter 2421251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2422251881Speter if (write_err) 2423251881Speter { 2424251881Speter svn_error_clear(err); 2425251881Speter return write_err; 2426251881Speter } 2427251881Speter SVN_CMD_ERR(err); 2428251881Speter 2429251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2430251881Speter 2431251881Speter return SVN_NO_ERROR; 2432251881Speter} 2433251881Speter 2434251881Speterstatic svn_error_t *gls_receiver(svn_location_segment_t *segment, 2435251881Speter void *baton, 2436251881Speter apr_pool_t *pool) 2437251881Speter{ 2438251881Speter svn_ra_svn_conn_t *conn = baton; 2439251881Speter return svn_ra_svn__write_tuple(conn, pool, "rr(?c)", 2440251881Speter segment->range_start, 2441251881Speter segment->range_end, 2442251881Speter segment->path); 2443251881Speter} 2444251881Speter 2445251881Speterstatic svn_error_t *get_location_segments(svn_ra_svn_conn_t *conn, 2446251881Speter apr_pool_t *pool, 2447251881Speter apr_array_header_t *params, 2448251881Speter void *baton) 2449251881Speter{ 2450251881Speter svn_error_t *err, *write_err; 2451251881Speter server_baton_t *b = baton; 2452251881Speter svn_revnum_t peg_revision, start_rev, end_rev; 2453251881Speter const char *relative_path; 2454251881Speter const char *abs_path; 2455251881Speter authz_baton_t ab; 2456251881Speter 2457251881Speter ab.server = b; 2458251881Speter ab.conn = conn; 2459251881Speter 2460251881Speter /* Parse the arguments. */ 2461251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)(?r)", 2462251881Speter &relative_path, &peg_revision, 2463251881Speter &start_rev, &end_rev)); 2464251881Speter relative_path = svn_relpath_canonicalize(relative_path, pool); 2465251881Speter 2466251881Speter abs_path = svn_fspath__join(b->fs_path->data, relative_path, pool); 2467251881Speter 2468251881Speter if (SVN_IS_VALID_REVNUM(start_rev) 2469251881Speter && SVN_IS_VALID_REVNUM(end_rev) 2470251881Speter && (end_rev > start_rev)) 2471251881Speter { 2472251881Speter err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 2473251881Speter "Get-location-segments end revision must not be " 2474251881Speter "younger than start revision"); 2475251881Speter return log_fail_and_flush(err, b, conn, pool); 2476251881Speter } 2477251881Speter 2478251881Speter if (SVN_IS_VALID_REVNUM(peg_revision) 2479251881Speter && SVN_IS_VALID_REVNUM(start_rev) 2480251881Speter && (start_rev > peg_revision)) 2481251881Speter { 2482251881Speter err = svn_error_createf(SVN_ERR_INCORRECT_PARAMS, NULL, 2483251881Speter "Get-location-segments start revision must not " 2484251881Speter "be younger than peg revision"); 2485251881Speter return log_fail_and_flush(err, b, conn, pool); 2486251881Speter } 2487251881Speter 2488251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2489251881Speter SVN_ERR(log_command(baton, conn, pool, "%s", 2490251881Speter svn_log__get_location_segments(abs_path, peg_revision, 2491251881Speter start_rev, end_rev, 2492251881Speter pool))); 2493251881Speter 2494251881Speter /* All the parameters are fine - let's perform the query against the 2495251881Speter * repository. */ 2496251881Speter 2497251881Speter /* We store both err and write_err here, so the client will get 2498251881Speter * the "done" even if there was an error in fetching the results. */ 2499251881Speter 2500251881Speter err = svn_repos_node_location_segments(b->repos, abs_path, 2501251881Speter peg_revision, start_rev, end_rev, 2502251881Speter gls_receiver, (void *)conn, 2503251881Speter authz_check_access_cb_func(b), &ab, 2504251881Speter pool); 2505251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2506251881Speter if (write_err) 2507251881Speter { 2508251881Speter svn_error_clear(err); 2509251881Speter return write_err; 2510251881Speter } 2511251881Speter SVN_CMD_ERR(err); 2512251881Speter 2513251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2514251881Speter 2515251881Speter return SVN_NO_ERROR; 2516251881Speter} 2517251881Speter 2518251881Speter/* This implements svn_write_fn_t. Write LEN bytes starting at DATA to the 2519251881Speter client as a string. */ 2520251881Speterstatic svn_error_t *svndiff_handler(void *baton, const char *data, 2521251881Speter apr_size_t *len) 2522251881Speter{ 2523251881Speter file_revs_baton_t *b = baton; 2524251881Speter svn_string_t str; 2525251881Speter 2526251881Speter str.data = data; 2527251881Speter str.len = *len; 2528251881Speter return svn_ra_svn__write_string(b->conn, b->pool, &str); 2529251881Speter} 2530251881Speter 2531251881Speter/* This implements svn_close_fn_t. Mark the end of the data by writing an 2532251881Speter empty string to the client. */ 2533251881Speterstatic svn_error_t *svndiff_close_handler(void *baton) 2534251881Speter{ 2535251881Speter file_revs_baton_t *b = baton; 2536251881Speter 2537251881Speter SVN_ERR(svn_ra_svn__write_cstring(b->conn, b->pool, "")); 2538251881Speter return SVN_NO_ERROR; 2539251881Speter} 2540251881Speter 2541251881Speter/* This implements the svn_repos_file_rev_handler_t interface. */ 2542251881Speterstatic svn_error_t *file_rev_handler(void *baton, const char *path, 2543251881Speter svn_revnum_t rev, apr_hash_t *rev_props, 2544251881Speter svn_boolean_t merged_revision, 2545251881Speter svn_txdelta_window_handler_t *d_handler, 2546251881Speter void **d_baton, 2547251881Speter apr_array_header_t *prop_diffs, 2548251881Speter apr_pool_t *pool) 2549251881Speter{ 2550251881Speter file_revs_baton_t *frb = baton; 2551251881Speter svn_stream_t *stream; 2552251881Speter 2553251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "cr(!", 2554251881Speter path, rev)); 2555251881Speter SVN_ERR(svn_ra_svn__write_proplist(frb->conn, pool, rev_props)); 2556251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)(!")); 2557251881Speter SVN_ERR(write_prop_diffs(frb->conn, pool, prop_diffs)); 2558251881Speter SVN_ERR(svn_ra_svn__write_tuple(frb->conn, pool, "!)b", merged_revision)); 2559251881Speter 2560251881Speter /* Store the pool for the delta stream. */ 2561251881Speter frb->pool = pool; 2562251881Speter 2563251881Speter /* Prepare for the delta or just write an empty string. */ 2564251881Speter if (d_handler) 2565251881Speter { 2566251881Speter stream = svn_stream_create(baton, pool); 2567251881Speter svn_stream_set_write(stream, svndiff_handler); 2568251881Speter svn_stream_set_close(stream, svndiff_close_handler); 2569251881Speter 2570251881Speter /* If the connection does not support SVNDIFF1 or if we don't want to use 2571251881Speter * compression, use the non-compressing "version 0" implementation */ 2572251881Speter if ( svn_ra_svn_compression_level(frb->conn) > 0 2573251881Speter && svn_ra_svn_has_capability(frb->conn, SVN_RA_SVN_CAP_SVNDIFF1)) 2574251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 1, 2575251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2576251881Speter else 2577251881Speter svn_txdelta_to_svndiff3(d_handler, d_baton, stream, 0, 2578251881Speter svn_ra_svn_compression_level(frb->conn), pool); 2579251881Speter } 2580251881Speter else 2581251881Speter SVN_ERR(svn_ra_svn__write_cstring(frb->conn, pool, "")); 2582251881Speter 2583251881Speter return SVN_NO_ERROR; 2584251881Speter} 2585251881Speter 2586251881Speterstatic svn_error_t *get_file_revs(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2587251881Speter apr_array_header_t *params, void *baton) 2588251881Speter{ 2589251881Speter server_baton_t *b = baton; 2590251881Speter svn_error_t *err, *write_err; 2591251881Speter file_revs_baton_t frb; 2592251881Speter svn_revnum_t start_rev, end_rev; 2593251881Speter const char *path; 2594251881Speter const char *full_path; 2595251881Speter apr_uint64_t include_merged_revs_param; 2596251881Speter svn_boolean_t include_merged_revisions; 2597251881Speter authz_baton_t ab; 2598251881Speter 2599251881Speter ab.server = b; 2600251881Speter ab.conn = conn; 2601251881Speter 2602251881Speter /* Parse arguments. */ 2603251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?r)(?r)?B", 2604251881Speter &path, &start_rev, &end_rev, 2605251881Speter &include_merged_revs_param)); 2606251881Speter path = svn_relpath_canonicalize(path, pool); 2607251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2608251881Speter full_path = svn_fspath__join(b->fs_path->data, path, pool); 2609251881Speter 2610251881Speter if (include_merged_revs_param == SVN_RA_SVN_UNSPECIFIED_NUMBER) 2611251881Speter include_merged_revisions = FALSE; 2612251881Speter else 2613251881Speter include_merged_revisions = (svn_boolean_t) include_merged_revs_param; 2614251881Speter 2615251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2616251881Speter svn_log__get_file_revs(full_path, start_rev, end_rev, 2617251881Speter include_merged_revisions, 2618251881Speter pool))); 2619251881Speter 2620251881Speter frb.conn = conn; 2621251881Speter frb.pool = NULL; 2622251881Speter 2623251881Speter err = svn_repos_get_file_revs2(b->repos, full_path, start_rev, end_rev, 2624251881Speter include_merged_revisions, 2625251881Speter authz_check_access_cb_func(b), &ab, 2626251881Speter file_rev_handler, &frb, pool); 2627251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2628251881Speter if (write_err) 2629251881Speter { 2630251881Speter svn_error_clear(err); 2631251881Speter return write_err; 2632251881Speter } 2633251881Speter SVN_CMD_ERR(err); 2634251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2635251881Speter 2636251881Speter return SVN_NO_ERROR; 2637251881Speter} 2638251881Speter 2639251881Speterstatic svn_error_t *lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2640251881Speter apr_array_header_t *params, void *baton) 2641251881Speter{ 2642251881Speter server_baton_t *b = baton; 2643251881Speter const char *path; 2644251881Speter const char *comment; 2645251881Speter const char *full_path; 2646251881Speter svn_boolean_t steal_lock; 2647251881Speter svn_revnum_t current_rev; 2648251881Speter svn_lock_t *l; 2649251881Speter 2650251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b(?r)", &path, &comment, 2651251881Speter &steal_lock, ¤t_rev)); 2652251881Speter full_path = svn_fspath__join(b->fs_path->data, 2653251881Speter svn_relpath_canonicalize(path, pool), pool); 2654251881Speter 2655251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2656251881Speter full_path, TRUE)); 2657251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2658251881Speter svn_log__lock_one_path(full_path, steal_lock, pool))); 2659251881Speter 2660251881Speter SVN_CMD_ERR(svn_repos_fs_lock(&l, b->repos, full_path, NULL, comment, 0, 2661251881Speter 0, /* No expiration time. */ 2662251881Speter current_rev, steal_lock, pool)); 2663251881Speter 2664251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(!", "success")); 2665251881Speter SVN_ERR(write_lock(conn, pool, l)); 2666251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!)")); 2667251881Speter 2668251881Speter return SVN_NO_ERROR; 2669251881Speter} 2670251881Speter 2671251881Speterstatic svn_error_t *lock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2672251881Speter apr_array_header_t *params, void *baton) 2673251881Speter{ 2674251881Speter server_baton_t *b = baton; 2675251881Speter apr_array_header_t *path_revs; 2676251881Speter const char *comment; 2677251881Speter svn_boolean_t steal_lock; 2678251881Speter int i; 2679251881Speter apr_pool_t *subpool; 2680251881Speter const char *path; 2681251881Speter const char *full_path; 2682251881Speter svn_revnum_t current_rev; 2683251881Speter apr_array_header_t *log_paths; 2684251881Speter svn_lock_t *l; 2685251881Speter svn_error_t *err = SVN_NO_ERROR, *write_err; 2686251881Speter 2687251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "(?c)bl", &comment, &steal_lock, 2688251881Speter &path_revs)); 2689251881Speter 2690251881Speter subpool = svn_pool_create(pool); 2691251881Speter 2692251881Speter /* Because we can only send a single auth reply per request, we send 2693251881Speter a reply before parsing the lock commands. This means an authz 2694251881Speter access denial will abort the processing of the locks and return 2695251881Speter an error. */ 2696251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, TRUE)); 2697251881Speter 2698251881Speter /* Loop through the lock requests. */ 2699251881Speter log_paths = apr_array_make(pool, path_revs->nelts, sizeof(full_path)); 2700251881Speter for (i = 0; i < path_revs->nelts; ++i) 2701251881Speter { 2702251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(path_revs, i, 2703251881Speter svn_ra_svn_item_t); 2704251881Speter 2705251881Speter svn_pool_clear(subpool); 2706251881Speter 2707251881Speter if (item->kind != SVN_RA_SVN_LIST) 2708251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2709251881Speter "Lock requests should be list of lists"); 2710251881Speter 2711251881Speter SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, pool, "c(?r)", &path, 2712251881Speter ¤t_rev)); 2713251881Speter 2714251881Speter /* Allocate the full_path out of pool so it will survive for use 2715251881Speter * by operational logging, after this loop. */ 2716251881Speter full_path = svn_fspath__join(b->fs_path->data, 2717251881Speter svn_relpath_canonicalize(path, subpool), 2718251881Speter pool); 2719251881Speter APR_ARRAY_PUSH(log_paths, const char *) = full_path; 2720251881Speter 2721251881Speter if (! lookup_access(pool, b, conn, svn_authz_write, full_path, TRUE)) 2722251881Speter { 2723251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, NULL, 2724251881Speter b, conn, pool); 2725251881Speter break; 2726251881Speter } 2727251881Speter 2728251881Speter err = svn_repos_fs_lock(&l, b->repos, full_path, 2729251881Speter NULL, comment, FALSE, 2730251881Speter 0, /* No expiration time. */ 2731251881Speter current_rev, 2732251881Speter steal_lock, subpool); 2733251881Speter 2734251881Speter if (err) 2735251881Speter { 2736251881Speter if (SVN_ERR_IS_LOCK_ERROR(err)) 2737251881Speter { 2738251881Speter write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 2739251881Speter svn_error_clear(err); 2740251881Speter err = NULL; 2741251881Speter SVN_ERR(write_err); 2742251881Speter } 2743251881Speter else 2744251881Speter break; 2745251881Speter } 2746251881Speter else 2747251881Speter { 2748251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w!", "success")); 2749251881Speter SVN_ERR(write_lock(conn, subpool, l)); 2750251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "!")); 2751251881Speter } 2752251881Speter } 2753251881Speter 2754251881Speter svn_pool_destroy(subpool); 2755251881Speter 2756251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2757251881Speter svn_log__lock(log_paths, steal_lock, pool))); 2758251881Speter 2759251881Speter /* NOTE: err might contain a fatal locking error from the loop above. */ 2760251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2761251881Speter if (!write_err) 2762251881Speter SVN_CMD_ERR(err); 2763251881Speter svn_error_clear(err); 2764251881Speter SVN_ERR(write_err); 2765251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2766251881Speter 2767251881Speter return SVN_NO_ERROR; 2768251881Speter} 2769251881Speter 2770251881Speterstatic svn_error_t *unlock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2771251881Speter apr_array_header_t *params, void *baton) 2772251881Speter{ 2773251881Speter server_baton_t *b = baton; 2774251881Speter const char *path, *token, *full_path; 2775251881Speter svn_boolean_t break_lock; 2776251881Speter 2777251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c(?c)b", &path, &token, 2778251881Speter &break_lock)); 2779251881Speter 2780251881Speter full_path = svn_fspath__join(b->fs_path->data, 2781251881Speter svn_relpath_canonicalize(path, pool), pool); 2782251881Speter 2783251881Speter /* Username required unless break_lock was specified. */ 2784251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, 2785251881Speter full_path, ! break_lock)); 2786251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2787251881Speter svn_log__unlock_one_path(full_path, break_lock, pool))); 2788251881Speter 2789251881Speter SVN_CMD_ERR(svn_repos_fs_unlock(b->repos, full_path, token, break_lock, 2790251881Speter pool)); 2791251881Speter 2792251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2793251881Speter 2794251881Speter return SVN_NO_ERROR; 2795251881Speter} 2796251881Speter 2797251881Speterstatic svn_error_t *unlock_many(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2798251881Speter apr_array_header_t *params, void *baton) 2799251881Speter{ 2800251881Speter server_baton_t *b = baton; 2801251881Speter svn_boolean_t break_lock; 2802251881Speter apr_array_header_t *unlock_tokens; 2803251881Speter int i; 2804251881Speter apr_pool_t *subpool; 2805251881Speter const char *path; 2806251881Speter const char *full_path; 2807251881Speter apr_array_header_t *log_paths; 2808251881Speter const char *token; 2809251881Speter svn_error_t *err = SVN_NO_ERROR, *write_err; 2810251881Speter 2811251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "bl", &break_lock, 2812251881Speter &unlock_tokens)); 2813251881Speter 2814251881Speter /* Username required unless break_lock was specified. */ 2815251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_write, NULL, ! break_lock)); 2816251881Speter 2817251881Speter subpool = svn_pool_create(pool); 2818251881Speter 2819251881Speter /* Loop through the unlock requests. */ 2820251881Speter log_paths = apr_array_make(pool, unlock_tokens->nelts, sizeof(full_path)); 2821251881Speter for (i = 0; i < unlock_tokens->nelts; i++) 2822251881Speter { 2823251881Speter svn_ra_svn_item_t *item = &APR_ARRAY_IDX(unlock_tokens, i, 2824251881Speter svn_ra_svn_item_t); 2825251881Speter 2826251881Speter svn_pool_clear(subpool); 2827251881Speter 2828251881Speter if (item->kind != SVN_RA_SVN_LIST) 2829251881Speter return svn_error_create(SVN_ERR_RA_SVN_MALFORMED_DATA, NULL, 2830251881Speter "Unlock request should be a list of lists"); 2831251881Speter 2832251881Speter SVN_ERR(svn_ra_svn__parse_tuple(item->u.list, subpool, "c(?c)", &path, 2833251881Speter &token)); 2834251881Speter 2835251881Speter /* Allocate the full_path out of pool so it will survive for use 2836251881Speter * by operational logging, after this loop. */ 2837251881Speter full_path = svn_fspath__join(b->fs_path->data, 2838251881Speter svn_relpath_canonicalize(path, subpool), 2839251881Speter pool); 2840251881Speter APR_ARRAY_PUSH(log_paths, const char *) = full_path; 2841251881Speter 2842251881Speter if (! lookup_access(subpool, b, conn, svn_authz_write, full_path, 2843251881Speter ! break_lock)) 2844251881Speter return svn_error_create(SVN_ERR_RA_SVN_CMD_ERR, 2845251881Speter error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, 2846251881Speter NULL, NULL, 2847251881Speter b, conn, pool), 2848251881Speter NULL); 2849251881Speter 2850251881Speter err = svn_repos_fs_unlock(b->repos, full_path, token, break_lock, 2851251881Speter subpool); 2852251881Speter if (err) 2853251881Speter { 2854251881Speter if (SVN_ERR_IS_UNLOCK_ERROR(err)) 2855251881Speter { 2856251881Speter write_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 2857251881Speter svn_error_clear(err); 2858251881Speter err = NULL; 2859251881Speter SVN_ERR(write_err); 2860251881Speter } 2861251881Speter else 2862251881Speter break; 2863251881Speter } 2864251881Speter else 2865251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, subpool, "w(c)", "success", 2866251881Speter path)); 2867251881Speter } 2868251881Speter 2869251881Speter svn_pool_destroy(subpool); 2870251881Speter 2871251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 2872251881Speter svn_log__unlock(log_paths, break_lock, pool))); 2873251881Speter 2874251881Speter /* NOTE: err might contain a fatal unlocking error from the loop above. */ 2875251881Speter write_err = svn_ra_svn__write_word(conn, pool, "done"); 2876251881Speter if (! write_err) 2877251881Speter SVN_CMD_ERR(err); 2878251881Speter svn_error_clear(err); 2879251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 2880251881Speter 2881251881Speter return SVN_NO_ERROR; 2882251881Speter} 2883251881Speter 2884251881Speterstatic svn_error_t *get_lock(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2885251881Speter apr_array_header_t *params, void *baton) 2886251881Speter{ 2887251881Speter server_baton_t *b = baton; 2888251881Speter const char *path; 2889251881Speter const char *full_path; 2890251881Speter svn_lock_t *l; 2891251881Speter 2892251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c", &path)); 2893251881Speter 2894251881Speter full_path = svn_fspath__join(b->fs_path->data, 2895251881Speter svn_relpath_canonicalize(path, pool), pool); 2896251881Speter 2897251881Speter SVN_ERR(must_have_access(conn, pool, b, svn_authz_read, 2898251881Speter full_path, FALSE)); 2899251881Speter SVN_ERR(log_command(b, conn, pool, "get-lock %s", 2900251881Speter svn_path_uri_encode(full_path, pool))); 2901251881Speter 2902251881Speter SVN_CMD_ERR(svn_fs_get_lock(&l, b->fs, full_path, pool)); 2903251881Speter 2904251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2905251881Speter if (l) 2906251881Speter SVN_ERR(write_lock(conn, pool, l)); 2907251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2908251881Speter 2909251881Speter return SVN_NO_ERROR; 2910251881Speter} 2911251881Speter 2912251881Speterstatic svn_error_t *get_locks(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 2913251881Speter apr_array_header_t *params, void *baton) 2914251881Speter{ 2915251881Speter server_baton_t *b = baton; 2916251881Speter const char *path; 2917251881Speter const char *full_path; 2918251881Speter const char *depth_word; 2919251881Speter svn_depth_t depth; 2920251881Speter apr_hash_t *locks; 2921251881Speter apr_hash_index_t *hi; 2922251881Speter svn_error_t *err; 2923251881Speter authz_baton_t ab; 2924251881Speter 2925251881Speter ab.server = b; 2926251881Speter ab.conn = conn; 2927251881Speter 2928251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "c?(?w)", &path, &depth_word)); 2929251881Speter 2930251881Speter depth = depth_word ? svn_depth_from_word(depth_word) : svn_depth_infinity; 2931251881Speter if ((depth != svn_depth_empty) && 2932251881Speter (depth != svn_depth_files) && 2933251881Speter (depth != svn_depth_immediates) && 2934251881Speter (depth != svn_depth_infinity)) 2935251881Speter { 2936251881Speter err = svn_error_create(SVN_ERR_INCORRECT_PARAMS, NULL, 2937251881Speter "Invalid 'depth' specified in get-locks request"); 2938251881Speter return log_fail_and_flush(err, b, conn, pool); 2939251881Speter } 2940251881Speter 2941251881Speter full_path = svn_fspath__join(b->fs_path->data, 2942251881Speter svn_relpath_canonicalize(path, pool), pool); 2943251881Speter 2944251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 2945251881Speter 2946251881Speter SVN_ERR(log_command(b, conn, pool, "get-locks %s", 2947251881Speter svn_path_uri_encode(full_path, pool))); 2948251881Speter SVN_CMD_ERR(svn_repos_fs_get_locks2(&locks, b->repos, full_path, depth, 2949251881Speter authz_check_access_cb_func(b), &ab, 2950251881Speter pool)); 2951251881Speter 2952251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w((!", "success")); 2953251881Speter for (hi = apr_hash_first(pool, locks); hi; hi = apr_hash_next(hi)) 2954251881Speter { 2955251881Speter svn_lock_t *l = svn__apr_hash_index_val(hi); 2956251881Speter 2957251881Speter SVN_ERR(write_lock(conn, pool, l)); 2958251881Speter } 2959251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 2960251881Speter 2961251881Speter return SVN_NO_ERROR; 2962251881Speter} 2963251881Speter 2964251881Speterstatic svn_error_t *replay_one_revision(svn_ra_svn_conn_t *conn, 2965251881Speter server_baton_t *b, 2966251881Speter svn_revnum_t rev, 2967251881Speter svn_revnum_t low_water_mark, 2968251881Speter svn_boolean_t send_deltas, 2969251881Speter apr_pool_t *pool) 2970251881Speter{ 2971251881Speter const svn_delta_editor_t *editor; 2972251881Speter void *edit_baton; 2973251881Speter svn_fs_root_t *root; 2974251881Speter svn_error_t *err; 2975251881Speter authz_baton_t ab; 2976251881Speter 2977251881Speter ab.server = b; 2978251881Speter ab.conn = conn; 2979251881Speter 2980251881Speter SVN_ERR(log_command(b, conn, pool, 2981251881Speter svn_log__replay(b->fs_path->data, rev, pool))); 2982251881Speter 2983251881Speter svn_ra_svn_get_editor(&editor, &edit_baton, conn, pool, NULL, NULL); 2984251881Speter 2985251881Speter err = svn_fs_revision_root(&root, b->fs, rev, pool); 2986251881Speter 2987251881Speter if (! err) 2988251881Speter err = svn_repos_replay2(root, b->fs_path->data, low_water_mark, 2989251881Speter send_deltas, editor, edit_baton, 2990251881Speter authz_check_access_cb_func(b), &ab, pool); 2991251881Speter 2992251881Speter if (err) 2993251881Speter svn_error_clear(editor->abort_edit(edit_baton, pool)); 2994251881Speter SVN_CMD_ERR(err); 2995251881Speter 2996251881Speter return svn_ra_svn__write_cmd_finish_replay(conn, pool); 2997251881Speter} 2998251881Speter 2999251881Speterstatic svn_error_t *replay(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3000251881Speter apr_array_header_t *params, void *baton) 3001251881Speter{ 3002251881Speter svn_revnum_t rev, low_water_mark; 3003251881Speter svn_boolean_t send_deltas; 3004251881Speter server_baton_t *b = baton; 3005251881Speter 3006251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrb", &rev, &low_water_mark, 3007251881Speter &send_deltas)); 3008251881Speter 3009251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3010251881Speter 3011251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3012251881Speter send_deltas, pool)); 3013251881Speter 3014251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3015251881Speter 3016251881Speter return SVN_NO_ERROR; 3017251881Speter} 3018251881Speter 3019251881Speterstatic svn_error_t *replay_range(svn_ra_svn_conn_t *conn, apr_pool_t *pool, 3020251881Speter apr_array_header_t *params, void *baton) 3021251881Speter{ 3022251881Speter svn_revnum_t start_rev, end_rev, rev, low_water_mark; 3023251881Speter svn_boolean_t send_deltas; 3024251881Speter server_baton_t *b = baton; 3025251881Speter apr_pool_t *iterpool; 3026251881Speter authz_baton_t ab; 3027251881Speter 3028251881Speter ab.server = b; 3029251881Speter ab.conn = conn; 3030251881Speter 3031251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "rrrb", &start_rev, 3032251881Speter &end_rev, &low_water_mark, 3033251881Speter &send_deltas)); 3034251881Speter 3035251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3036251881Speter 3037251881Speter iterpool = svn_pool_create(pool); 3038251881Speter for (rev = start_rev; rev <= end_rev; rev++) 3039251881Speter { 3040251881Speter apr_hash_t *props; 3041251881Speter 3042251881Speter svn_pool_clear(iterpool); 3043251881Speter 3044251881Speter SVN_CMD_ERR(svn_repos_fs_revision_proplist(&props, b->repos, rev, 3045251881Speter authz_check_access_cb_func(b), 3046251881Speter &ab, 3047251881Speter iterpool)); 3048251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "revprops")); 3049251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, props)); 3050251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!)")); 3051251881Speter 3052251881Speter SVN_ERR(replay_one_revision(conn, b, rev, low_water_mark, 3053251881Speter send_deltas, iterpool)); 3054251881Speter 3055251881Speter } 3056251881Speter svn_pool_destroy(iterpool); 3057251881Speter 3058251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "")); 3059251881Speter 3060251881Speter return SVN_NO_ERROR; 3061251881Speter} 3062251881Speter 3063251881Speterstatic svn_error_t * 3064251881Speterget_deleted_rev(svn_ra_svn_conn_t *conn, 3065251881Speter apr_pool_t *pool, 3066251881Speter apr_array_header_t *params, 3067251881Speter void *baton) 3068251881Speter{ 3069251881Speter server_baton_t *b = baton; 3070251881Speter const char *path, *full_path; 3071251881Speter svn_revnum_t peg_revision; 3072251881Speter svn_revnum_t end_revision; 3073251881Speter svn_revnum_t revision_deleted; 3074251881Speter 3075251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, pool, "crr", 3076251881Speter &path, &peg_revision, &end_revision)); 3077251881Speter full_path = svn_fspath__join(b->fs_path->data, 3078251881Speter svn_relpath_canonicalize(path, pool), pool); 3079251881Speter SVN_ERR(log_command(b, conn, pool, "get-deleted-rev")); 3080251881Speter SVN_ERR(trivial_auth_request(conn, pool, b)); 3081251881Speter SVN_ERR(svn_repos_deleted_rev(b->fs, full_path, peg_revision, end_revision, 3082251881Speter &revision_deleted, pool)); 3083251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "r", revision_deleted)); 3084251881Speter return SVN_NO_ERROR; 3085251881Speter} 3086251881Speter 3087251881Speterstatic svn_error_t * 3088251881Speterget_inherited_props(svn_ra_svn_conn_t *conn, 3089251881Speter apr_pool_t *pool, 3090251881Speter apr_array_header_t *params, 3091251881Speter void *baton) 3092251881Speter{ 3093251881Speter server_baton_t *b = baton; 3094251881Speter const char *path, *full_path; 3095251881Speter svn_revnum_t rev; 3096251881Speter svn_fs_root_t *root; 3097251881Speter apr_array_header_t *inherited_props; 3098251881Speter int i; 3099251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 3100251881Speter authz_baton_t ab; 3101251881Speter 3102251881Speter ab.server = b; 3103251881Speter ab.conn = conn; 3104251881Speter 3105251881Speter /* Parse arguments. */ 3106251881Speter SVN_ERR(svn_ra_svn__parse_tuple(params, iterpool, "c(?r)", &path, &rev)); 3107251881Speter 3108251881Speter full_path = svn_fspath__join(b->fs_path->data, 3109251881Speter svn_relpath_canonicalize(path, iterpool), 3110251881Speter pool); 3111251881Speter 3112251881Speter /* Check authorizations */ 3113251881Speter SVN_ERR(must_have_access(conn, iterpool, b, svn_authz_read, 3114251881Speter full_path, FALSE)); 3115251881Speter 3116251881Speter if (!SVN_IS_VALID_REVNUM(rev)) 3117251881Speter SVN_CMD_ERR(svn_fs_youngest_rev(&rev, b->fs, pool)); 3118251881Speter 3119251881Speter SVN_ERR(log_command(b, conn, pool, "%s", 3120251881Speter svn_log__get_inherited_props(full_path, rev, 3121251881Speter iterpool))); 3122251881Speter 3123251881Speter /* Fetch the properties and a stream for the contents. */ 3124251881Speter SVN_CMD_ERR(svn_fs_revision_root(&root, b->fs, rev, iterpool)); 3125251881Speter SVN_CMD_ERR(get_props(NULL, &inherited_props, &ab, root, full_path, pool)); 3126251881Speter 3127251881Speter /* Send successful command response with revision and props. */ 3128251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "w(!", "success")); 3129251881Speter 3130251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(?!")); 3131251881Speter 3132251881Speter for (i = 0; i < inherited_props->nelts; i++) 3133251881Speter { 3134251881Speter svn_prop_inherited_item_t *iprop = 3135251881Speter APR_ARRAY_IDX(inherited_props, i, svn_prop_inherited_item_t *); 3136251881Speter 3137251881Speter svn_pool_clear(iterpool); 3138251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!(c(!", 3139251881Speter iprop->path_or_url)); 3140251881Speter SVN_ERR(svn_ra_svn__write_proplist(conn, iterpool, iprop->prop_hash)); 3141251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))!", 3142251881Speter iprop->path_or_url)); 3143251881Speter } 3144251881Speter 3145251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, iterpool, "!))")); 3146251881Speter svn_pool_destroy(iterpool); 3147251881Speter return SVN_NO_ERROR; 3148251881Speter} 3149251881Speter 3150251881Speterstatic const svn_ra_svn_cmd_entry_t main_commands[] = { 3151251881Speter { "reparent", reparent }, 3152251881Speter { "get-latest-rev", get_latest_rev }, 3153251881Speter { "get-dated-rev", get_dated_rev }, 3154251881Speter { "change-rev-prop", change_rev_prop }, 3155251881Speter { "change-rev-prop2",change_rev_prop2 }, 3156251881Speter { "rev-proplist", rev_proplist }, 3157251881Speter { "rev-prop", rev_prop }, 3158251881Speter { "commit", commit }, 3159251881Speter { "get-file", get_file }, 3160251881Speter { "get-dir", get_dir }, 3161251881Speter { "update", update }, 3162251881Speter { "switch", switch_cmd }, 3163251881Speter { "status", status }, 3164251881Speter { "diff", diff }, 3165251881Speter { "get-mergeinfo", get_mergeinfo }, 3166251881Speter { "log", log_cmd }, 3167251881Speter { "check-path", check_path }, 3168251881Speter { "stat", stat_cmd }, 3169251881Speter { "get-locations", get_locations }, 3170251881Speter { "get-location-segments", get_location_segments }, 3171251881Speter { "get-file-revs", get_file_revs }, 3172251881Speter { "lock", lock }, 3173251881Speter { "lock-many", lock_many }, 3174251881Speter { "unlock", unlock }, 3175251881Speter { "unlock-many", unlock_many }, 3176251881Speter { "get-lock", get_lock }, 3177251881Speter { "get-locks", get_locks }, 3178251881Speter { "replay", replay }, 3179251881Speter { "replay-range", replay_range }, 3180251881Speter { "get-deleted-rev", get_deleted_rev }, 3181251881Speter { "get-iprops", get_inherited_props }, 3182251881Speter { NULL } 3183251881Speter}; 3184251881Speter 3185251881Speter/* Skip past the scheme part of a URL, including the tunnel specification 3186251881Speter * if present. Return NULL if the scheme part is invalid for ra_svn. */ 3187251881Speterstatic const char *skip_scheme_part(const char *url) 3188251881Speter{ 3189251881Speter if (strncmp(url, "svn", 3) != 0) 3190251881Speter return NULL; 3191251881Speter url += 3; 3192251881Speter if (*url == '+') 3193251881Speter url += strcspn(url, ":"); 3194251881Speter if (strncmp(url, "://", 3) != 0) 3195251881Speter return NULL; 3196251881Speter return url + 3; 3197251881Speter} 3198251881Speter 3199251881Speter/* Check that PATH is a valid repository path, meaning it doesn't contain any 3200251881Speter '..' path segments. 3201251881Speter NOTE: This is similar to svn_path_is_backpath_present, but that function 3202251881Speter assumes the path separator is '/'. This function also checks for 3203251881Speter segments delimited by the local path separator. */ 3204251881Speterstatic svn_boolean_t 3205251881Speterrepos_path_valid(const char *path) 3206251881Speter{ 3207251881Speter const char *s = path; 3208251881Speter 3209251881Speter while (*s) 3210251881Speter { 3211251881Speter /* Scan for the end of the segment. */ 3212251881Speter while (*path && *path != '/' && *path != SVN_PATH_LOCAL_SEPARATOR) 3213251881Speter ++path; 3214251881Speter 3215251881Speter /* Check for '..'. */ 3216251881Speter#ifdef WIN32 3217251881Speter /* On Windows, don't allow sequences of more than one character 3218251881Speter consisting of just dots and spaces. Win32 functions treat 3219251881Speter paths such as ".. " and "......." inconsistently. Make sure 3220251881Speter no one can escape out of the root. */ 3221251881Speter if (path - s >= 2 && strspn(s, ". ") == (size_t)(path - s)) 3222251881Speter return FALSE; 3223251881Speter#else /* ! WIN32 */ 3224251881Speter if (path - s == 2 && s[0] == '.' && s[1] == '.') 3225251881Speter return FALSE; 3226251881Speter#endif 3227251881Speter 3228251881Speter /* Skip all separators. */ 3229251881Speter while (*path && (*path == '/' || *path == SVN_PATH_LOCAL_SEPARATOR)) 3230251881Speter ++path; 3231251881Speter s = path; 3232251881Speter } 3233251881Speter 3234251881Speter return TRUE; 3235251881Speter} 3236251881Speter 3237251881Speter/* Look for the repository given by URL, using ROOT as the virtual 3238251881Speter * repository root. If we find one, fill in the repos, fs, cfg, 3239251881Speter * repos_url, and fs_path fields of B. Set B->repos's client 3240251881Speter * capabilities to CAPABILITIES, which must be at least as long-lived 3241251881Speter * as POOL, and whose elements are SVN_RA_CAPABILITY_*. 3242251881Speter */ 3243251881Speterstatic svn_error_t *find_repos(const char *url, const char *root, 3244251881Speter server_baton_t *b, 3245251881Speter svn_ra_svn_conn_t *conn, 3246251881Speter const apr_array_header_t *capabilities, 3247251881Speter apr_pool_t *pool) 3248251881Speter{ 3249251881Speter const char *path, *full_path, *repos_root, *fs_path, *hooks_env; 3250251881Speter svn_stringbuf_t *url_buf; 3251251881Speter 3252251881Speter /* Skip past the scheme and authority part. */ 3253251881Speter path = skip_scheme_part(url); 3254251881Speter if (path == NULL) 3255251881Speter return svn_error_createf(SVN_ERR_BAD_URL, NULL, 3256251881Speter "Non-svn URL passed to svn server: '%s'", url); 3257251881Speter 3258251881Speter if (! b->vhost) 3259251881Speter { 3260251881Speter path = strchr(path, '/'); 3261251881Speter if (path == NULL) 3262251881Speter path = ""; 3263251881Speter } 3264251881Speter path = svn_relpath_canonicalize(path, pool); 3265251881Speter path = svn_path_uri_decode(path, pool); 3266251881Speter 3267251881Speter /* Ensure that it isn't possible to escape the root by disallowing 3268251881Speter '..' segments. */ 3269251881Speter if (!repos_path_valid(path)) 3270251881Speter return svn_error_create(SVN_ERR_BAD_FILENAME, NULL, 3271251881Speter "Couldn't determine repository path"); 3272251881Speter 3273251881Speter /* Join the server-configured root with the client path. */ 3274251881Speter full_path = svn_dirent_join(svn_dirent_canonicalize(root, pool), 3275251881Speter path, pool); 3276251881Speter 3277251881Speter /* Search for a repository in the full path. */ 3278251881Speter repos_root = svn_repos_find_root_path(full_path, pool); 3279251881Speter if (!repos_root) 3280251881Speter return svn_error_createf(SVN_ERR_RA_SVN_REPOS_NOT_FOUND, NULL, 3281251881Speter "No repository found in '%s'", url); 3282251881Speter 3283251881Speter /* Open the repository and fill in b with the resulting information. */ 3284251881Speter SVN_ERR(svn_repos_open2(&b->repos, repos_root, b->fs_config, pool)); 3285251881Speter SVN_ERR(svn_repos_remember_client_capabilities(b->repos, capabilities)); 3286251881Speter b->fs = svn_repos_fs(b->repos); 3287251881Speter fs_path = full_path + strlen(repos_root); 3288251881Speter b->fs_path = svn_stringbuf_create(*fs_path ? fs_path : "/", pool); 3289251881Speter url_buf = svn_stringbuf_create(url, pool); 3290251881Speter svn_path_remove_components(url_buf, 3291251881Speter svn_path_component_count(b->fs_path->data)); 3292251881Speter b->repos_url = url_buf->data; 3293251881Speter b->authz_repos_name = svn_dirent_is_child(root, repos_root, pool); 3294251881Speter if (b->authz_repos_name == NULL) 3295251881Speter b->repos_name = svn_dirent_basename(repos_root, pool); 3296251881Speter else 3297251881Speter b->repos_name = b->authz_repos_name; 3298251881Speter b->repos_name = svn_path_uri_encode(b->repos_name, pool); 3299251881Speter 3300251881Speter /* If the svnserve configuration has not been loaded then load it from the 3301251881Speter * repository. */ 3302251881Speter if (NULL == b->cfg) 3303251881Speter { 3304251881Speter b->base = svn_repos_conf_dir(b->repos, pool); 3305251881Speter 3306251881Speter SVN_ERR(svn_config_read3(&b->cfg, svn_repos_svnserve_conf(b->repos, pool), 3307251881Speter FALSE, /* must_exist */ 3308251881Speter FALSE, /* section_names_case_sensitive */ 3309251881Speter FALSE, /* option_names_case_sensitive */ 3310251881Speter pool)); 3311251881Speter SVN_ERR(load_pwdb_config(b, conn, pool)); 3312251881Speter SVN_ERR(load_authz_config(b, conn, repos_root, pool)); 3313251881Speter } 3314251881Speter /* svnserve.conf has been loaded via the --config-file option so need 3315251881Speter * to load pwdb and authz. */ 3316251881Speter else 3317251881Speter { 3318251881Speter SVN_ERR(load_pwdb_config(b, conn, pool)); 3319251881Speter SVN_ERR(load_authz_config(b, conn, repos_root, pool)); 3320251881Speter } 3321251881Speter 3322251881Speter#ifdef SVN_HAVE_SASL 3323251881Speter /* Should we use Cyrus SASL? */ 3324251881Speter SVN_ERR(svn_config_get_bool(b->cfg, &b->use_sasl, SVN_CONFIG_SECTION_SASL, 3325251881Speter SVN_CONFIG_OPTION_USE_SASL, FALSE)); 3326251881Speter#endif 3327251881Speter 3328251881Speter /* Use the repository UUID as the default realm. */ 3329251881Speter SVN_ERR(svn_fs_get_uuid(b->fs, &b->realm, pool)); 3330251881Speter svn_config_get(b->cfg, &b->realm, SVN_CONFIG_SECTION_GENERAL, 3331251881Speter SVN_CONFIG_OPTION_REALM, b->realm); 3332251881Speter 3333251881Speter /* Make sure it's possible for the client to authenticate. Note 3334251881Speter that this doesn't take into account any authz configuration read 3335251881Speter above, because we can't know about access it grants until paths 3336251881Speter are given by the client. */ 3337251881Speter if (get_access(b, UNAUTHENTICATED) == NO_ACCESS 3338251881Speter && (get_access(b, AUTHENTICATED) == NO_ACCESS 3339251881Speter || (!b->tunnel_user && !b->pwdb && !b->use_sasl))) 3340251881Speter return error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3341251881Speter "No access allowed to this repository", 3342251881Speter b, conn, pool); 3343251881Speter 3344251881Speter /* Configure hook script environment variables. */ 3345251881Speter svn_config_get(b->cfg, &hooks_env, SVN_CONFIG_SECTION_GENERAL, 3346251881Speter SVN_CONFIG_OPTION_HOOKS_ENV, NULL); 3347251881Speter if (hooks_env) 3348251881Speter hooks_env = svn_dirent_internal_style(hooks_env, pool); 3349251881Speter SVN_ERR(svn_repos_hooks_setenv(b->repos, hooks_env, pool)); 3350251881Speter 3351251881Speter return SVN_NO_ERROR; 3352251881Speter} 3353251881Speter 3354251881Speter/* Compute the authentication name EXTERNAL should be able to get, if any. */ 3355251881Speterstatic const char *get_tunnel_user(serve_params_t *params, apr_pool_t *pool) 3356251881Speter{ 3357251881Speter /* Only offer EXTERNAL for connections tunneled over a login agent. */ 3358251881Speter if (!params->tunnel) 3359251881Speter return NULL; 3360251881Speter 3361251881Speter /* If a tunnel user was provided on the command line, use that. */ 3362251881Speter if (params->tunnel_user) 3363251881Speter return params->tunnel_user; 3364251881Speter 3365251881Speter return svn_user_get_name(pool); 3366251881Speter} 3367251881Speter 3368251881Speterstatic void 3369251881Speterfs_warning_func(void *baton, svn_error_t *err) 3370251881Speter{ 3371251881Speter fs_warning_baton_t *b = baton; 3372251881Speter log_server_error(err, b->server, b->conn, b->pool); 3373251881Speter /* TODO: Keep log_pool in the server baton, cleared after every log? */ 3374251881Speter svn_pool_clear(b->pool); 3375251881Speter} 3376251881Speter 3377251881Speter/* Return the normalized repository-relative path for the given PATH 3378251881Speter * (may be a URL, full path or relative path) and fs contained in the 3379251881Speter * server baton BATON. Allocate the result in POOL. 3380251881Speter */ 3381251881Speterstatic const char * 3382251881Speterget_normalized_repo_rel_path(void *baton, 3383251881Speter const char *path, 3384251881Speter apr_pool_t *pool) 3385251881Speter{ 3386251881Speter server_baton_t *sb = baton; 3387251881Speter 3388251881Speter if (svn_path_is_url(path)) 3389251881Speter { 3390251881Speter /* This is a copyfrom URL. */ 3391251881Speter path = svn_uri_skip_ancestor(sb->repos_url, path, pool); 3392251881Speter path = svn_fspath__canonicalize(path, pool); 3393251881Speter } 3394251881Speter else 3395251881Speter { 3396251881Speter /* This is a base-relative path. */ 3397251881Speter if ((path)[0] != '/') 3398251881Speter /* Get an absolute path for use in the FS. */ 3399251881Speter path = svn_fspath__join(sb->fs_path->data, path, pool); 3400251881Speter } 3401251881Speter 3402251881Speter return path; 3403251881Speter} 3404251881Speter 3405251881Speter/* Get the revision root for REVISION in fs given by server baton BATON 3406251881Speter * and return it in *FS_ROOT. Use HEAD if REVISION is SVN_INVALID_REVNUM. 3407251881Speter * Use POOL for allocations. 3408251881Speter */ 3409251881Speterstatic svn_error_t * 3410251881Speterget_revision_root(svn_fs_root_t **fs_root, 3411251881Speter void *baton, 3412251881Speter svn_revnum_t revision, 3413251881Speter apr_pool_t *pool) 3414251881Speter{ 3415251881Speter server_baton_t *sb = baton; 3416251881Speter 3417251881Speter if (!SVN_IS_VALID_REVNUM(revision)) 3418251881Speter SVN_ERR(svn_fs_youngest_rev(&revision, sb->fs, pool)); 3419251881Speter 3420251881Speter SVN_ERR(svn_fs_revision_root(fs_root, sb->fs, revision, pool)); 3421251881Speter 3422251881Speter return SVN_NO_ERROR; 3423251881Speter} 3424251881Speter 3425251881Speterstatic svn_error_t * 3426251881Speterfetch_props_func(apr_hash_t **props, 3427251881Speter void *baton, 3428251881Speter const char *path, 3429251881Speter svn_revnum_t base_revision, 3430251881Speter apr_pool_t *result_pool, 3431251881Speter apr_pool_t *scratch_pool) 3432251881Speter{ 3433251881Speter svn_fs_root_t *fs_root; 3434251881Speter svn_error_t *err; 3435251881Speter 3436251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3437251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3438251881Speter 3439251881Speter err = svn_fs_node_proplist(props, fs_root, path, result_pool); 3440251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3441251881Speter { 3442251881Speter svn_error_clear(err); 3443251881Speter *props = apr_hash_make(result_pool); 3444251881Speter return SVN_NO_ERROR; 3445251881Speter } 3446251881Speter else if (err) 3447251881Speter return svn_error_trace(err); 3448251881Speter 3449251881Speter return SVN_NO_ERROR; 3450251881Speter} 3451251881Speter 3452251881Speterstatic svn_error_t * 3453251881Speterfetch_kind_func(svn_node_kind_t *kind, 3454251881Speter void *baton, 3455251881Speter const char *path, 3456251881Speter svn_revnum_t base_revision, 3457251881Speter apr_pool_t *scratch_pool) 3458251881Speter{ 3459251881Speter svn_fs_root_t *fs_root; 3460251881Speter 3461251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3462251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3463251881Speter 3464251881Speter SVN_ERR(svn_fs_check_path(kind, fs_root, path, scratch_pool)); 3465251881Speter 3466251881Speter return SVN_NO_ERROR; 3467251881Speter} 3468251881Speter 3469251881Speterstatic svn_error_t * 3470251881Speterfetch_base_func(const char **filename, 3471251881Speter void *baton, 3472251881Speter const char *path, 3473251881Speter svn_revnum_t base_revision, 3474251881Speter apr_pool_t *result_pool, 3475251881Speter apr_pool_t *scratch_pool) 3476251881Speter{ 3477251881Speter svn_stream_t *contents; 3478251881Speter svn_stream_t *file_stream; 3479251881Speter const char *tmp_filename; 3480251881Speter svn_fs_root_t *fs_root; 3481251881Speter svn_error_t *err; 3482251881Speter 3483251881Speter path = get_normalized_repo_rel_path(baton, path, scratch_pool); 3484251881Speter SVN_ERR(get_revision_root(&fs_root, baton, base_revision, scratch_pool)); 3485251881Speter 3486251881Speter err = svn_fs_file_contents(&contents, fs_root, path, scratch_pool); 3487251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 3488251881Speter { 3489251881Speter svn_error_clear(err); 3490251881Speter *filename = NULL; 3491251881Speter return SVN_NO_ERROR; 3492251881Speter } 3493251881Speter else if (err) 3494251881Speter return svn_error_trace(err); 3495251881Speter SVN_ERR(svn_stream_open_unique(&file_stream, &tmp_filename, NULL, 3496251881Speter svn_io_file_del_on_pool_cleanup, 3497251881Speter scratch_pool, scratch_pool)); 3498251881Speter SVN_ERR(svn_stream_copy3(contents, file_stream, NULL, NULL, scratch_pool)); 3499251881Speter 3500251881Speter *filename = apr_pstrdup(result_pool, tmp_filename); 3501251881Speter 3502251881Speter return SVN_NO_ERROR; 3503251881Speter} 3504251881Speter 3505251881Spetersvn_error_t *serve(svn_ra_svn_conn_t *conn, serve_params_t *params, 3506251881Speter apr_pool_t *pool) 3507251881Speter{ 3508251881Speter svn_error_t *err, *io_err; 3509251881Speter apr_uint64_t ver; 3510251881Speter const char *uuid, *client_url, *ra_client_string, *client_string; 3511251881Speter apr_array_header_t *caplist, *cap_words; 3512251881Speter server_baton_t b; 3513251881Speter fs_warning_baton_t warn_baton; 3514251881Speter svn_stringbuf_t *cap_log = svn_stringbuf_create_empty(pool); 3515251881Speter 3516251881Speter b.tunnel = params->tunnel; 3517251881Speter b.tunnel_user = get_tunnel_user(params, pool); 3518251881Speter b.read_only = params->read_only; 3519251881Speter b.user = NULL; 3520251881Speter b.username_case = params->username_case; 3521251881Speter b.authz_user = NULL; 3522251881Speter b.base = params->base; 3523251881Speter b.cfg = params->cfg; 3524251881Speter b.pwdb = NULL; 3525251881Speter b.authzdb = NULL; 3526251881Speter b.realm = NULL; 3527251881Speter b.log_file = params->log_file; 3528251881Speter b.pool = pool; 3529251881Speter b.use_sasl = FALSE; 3530251881Speter b.vhost = params->vhost; 3531251881Speter 3532251881Speter /* construct FS configuration parameters */ 3533251881Speter b.fs_config = apr_hash_make(pool); 3534251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_DELTAS, 3535251881Speter params->cache_txdeltas ? "1" :"0"); 3536251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_FULLTEXTS, 3537251881Speter params->cache_fulltexts ? "1" :"0"); 3538251881Speter svn_hash_sets(b.fs_config, SVN_FS_CONFIG_FSFS_CACHE_REVPROPS, 3539251881Speter params->cache_revprops ? "1" :"0"); 3540251881Speter 3541251881Speter /* Send greeting. We don't support version 1 any more, so we can 3542251881Speter * send an empty mechlist. */ 3543251881Speter if (params->compression_level > 0) 3544251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwwww)", 3545251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3546251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3547251881Speter SVN_RA_SVN_CAP_SVNDIFF1, 3548251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3549251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3550251881Speter SVN_RA_SVN_CAP_DEPTH, 3551251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3552251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3553251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3554251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3555251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3556251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3557251881Speter )); 3558251881Speter else 3559251881Speter SVN_ERR(svn_ra_svn__write_cmd_response(conn, pool, "nn()(wwwwwwwwww)", 3560251881Speter (apr_uint64_t) 2, (apr_uint64_t) 2, 3561251881Speter SVN_RA_SVN_CAP_EDIT_PIPELINE, 3562251881Speter SVN_RA_SVN_CAP_ABSENT_ENTRIES, 3563251881Speter SVN_RA_SVN_CAP_COMMIT_REVPROPS, 3564251881Speter SVN_RA_SVN_CAP_DEPTH, 3565251881Speter SVN_RA_SVN_CAP_LOG_REVPROPS, 3566251881Speter SVN_RA_SVN_CAP_ATOMIC_REVPROPS, 3567251881Speter SVN_RA_SVN_CAP_PARTIAL_REPLAY, 3568251881Speter SVN_RA_SVN_CAP_INHERITED_PROPS, 3569251881Speter SVN_RA_SVN_CAP_EPHEMERAL_TXNPROPS, 3570251881Speter SVN_RA_SVN_CAP_GET_FILE_REVS_REVERSE 3571251881Speter )); 3572251881Speter 3573251881Speter /* Read client response, which we assume to be in version 2 format: 3574251881Speter * version, capability list, and client URL; then we do an auth 3575251881Speter * request. */ 3576251881Speter SVN_ERR(svn_ra_svn__read_tuple(conn, pool, "nlc?c(?c)", 3577251881Speter &ver, &caplist, &client_url, 3578251881Speter &ra_client_string, 3579251881Speter &client_string)); 3580251881Speter if (ver != 2) 3581251881Speter return SVN_NO_ERROR; 3582251881Speter 3583251881Speter client_url = svn_uri_canonicalize(client_url, pool); 3584251881Speter SVN_ERR(svn_ra_svn_set_capabilities(conn, caplist)); 3585251881Speter 3586251881Speter /* All released versions of Subversion support edit-pipeline, 3587251881Speter * so we do not accept connections from clients that do not. */ 3588251881Speter if (! svn_ra_svn_has_capability(conn, SVN_RA_SVN_CAP_EDIT_PIPELINE)) 3589251881Speter return SVN_NO_ERROR; 3590251881Speter 3591251881Speter /* find_repos needs the capabilities as a list of words (eventually 3592251881Speter they get handed to the start-commit hook). While we could add a 3593251881Speter new interface to re-retrieve them from conn and convert the 3594251881Speter result to a list, it's simpler to just convert caplist by hand 3595251881Speter here, since we already have it and turning 'svn_ra_svn_item_t's 3596251881Speter into 'const char *'s is pretty easy. 3597251881Speter 3598251881Speter We only record capabilities we care about. The client may report 3599251881Speter more (because it doesn't know what the server cares about). */ 3600251881Speter { 3601251881Speter int i; 3602251881Speter svn_ra_svn_item_t *item; 3603251881Speter 3604251881Speter cap_words = apr_array_make(pool, 1, sizeof(const char *)); 3605251881Speter for (i = 0; i < caplist->nelts; i++) 3606251881Speter { 3607251881Speter item = &APR_ARRAY_IDX(caplist, i, svn_ra_svn_item_t); 3608251881Speter /* ra_svn_set_capabilities() already type-checked for us */ 3609251881Speter if (strcmp(item->u.word, SVN_RA_SVN_CAP_MERGEINFO) == 0) 3610251881Speter { 3611251881Speter APR_ARRAY_PUSH(cap_words, const char *) 3612251881Speter = SVN_RA_CAPABILITY_MERGEINFO; 3613251881Speter } 3614251881Speter /* Save for operational log. */ 3615251881Speter if (cap_log->len > 0) 3616251881Speter svn_stringbuf_appendcstr(cap_log, " "); 3617251881Speter svn_stringbuf_appendcstr(cap_log, item->u.word); 3618251881Speter } 3619251881Speter } 3620251881Speter 3621251881Speter err = find_repos(client_url, params->root, &b, conn, cap_words, pool); 3622251881Speter if (!err) 3623251881Speter { 3624251881Speter SVN_ERR(auth_request(conn, pool, &b, READ_ACCESS, FALSE)); 3625251881Speter if (current_access(&b) == NO_ACCESS) 3626251881Speter err = error_create_and_log(SVN_ERR_RA_NOT_AUTHORIZED, NULL, 3627251881Speter "Not authorized for access", 3628251881Speter &b, conn, pool); 3629251881Speter } 3630251881Speter if (err) 3631251881Speter { 3632251881Speter log_error(err, b.log_file, svn_ra_svn_conn_remote_host(conn), 3633251881Speter b.user, NULL, pool); 3634251881Speter io_err = svn_ra_svn__write_cmd_failure(conn, pool, err); 3635251881Speter svn_error_clear(err); 3636251881Speter SVN_ERR(io_err); 3637251881Speter return svn_ra_svn__flush(conn, pool); 3638251881Speter } 3639251881Speter 3640251881Speter /* Log the open. */ 3641251881Speter if (ra_client_string == NULL || ra_client_string[0] == '\0') 3642251881Speter ra_client_string = "-"; 3643251881Speter else 3644251881Speter ra_client_string = svn_path_uri_encode(ra_client_string, pool); 3645251881Speter if (client_string == NULL || client_string[0] == '\0') 3646251881Speter client_string = "-"; 3647251881Speter else 3648251881Speter client_string = svn_path_uri_encode(client_string, pool); 3649251881Speter SVN_ERR(log_command(&b, conn, pool, 3650251881Speter "open %" APR_UINT64_T_FMT " cap=(%s) %s %s %s", 3651251881Speter ver, cap_log->data, 3652251881Speter svn_path_uri_encode(b.fs_path->data, pool), 3653251881Speter ra_client_string, client_string)); 3654251881Speter 3655251881Speter warn_baton.server = &b; 3656251881Speter warn_baton.conn = conn; 3657251881Speter warn_baton.pool = svn_pool_create(pool); 3658251881Speter svn_fs_set_warning_func(b.fs, fs_warning_func, &warn_baton); 3659251881Speter 3660251881Speter SVN_ERR(svn_fs_get_uuid(b.fs, &uuid, pool)); 3661251881Speter 3662251881Speter /* We can't claim mergeinfo capability until we know whether the 3663251881Speter repository supports mergeinfo (i.e., is not a 1.4 repository), 3664251881Speter but we don't get the repository url from the client until after 3665251881Speter we've already sent the initial list of server capabilities. So 3666251881Speter we list repository capabilities here, in our first response after 3667251881Speter the client has sent the url. */ 3668251881Speter { 3669251881Speter svn_boolean_t supports_mergeinfo; 3670251881Speter SVN_ERR(svn_repos_has_capability(b.repos, &supports_mergeinfo, 3671251881Speter SVN_REPOS_CAPABILITY_MERGEINFO, pool)); 3672251881Speter 3673251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "w(cc(!", 3674251881Speter "success", uuid, b.repos_url)); 3675251881Speter if (supports_mergeinfo) 3676251881Speter SVN_ERR(svn_ra_svn__write_word(conn, pool, SVN_RA_SVN_CAP_MERGEINFO)); 3677251881Speter SVN_ERR(svn_ra_svn__write_tuple(conn, pool, "!))")); 3678251881Speter } 3679251881Speter 3680251881Speter /* Set up editor shims. */ 3681251881Speter { 3682251881Speter svn_delta_shim_callbacks_t *callbacks = 3683251881Speter svn_delta_shim_callbacks_default(pool); 3684251881Speter 3685251881Speter callbacks->fetch_base_func = fetch_base_func; 3686251881Speter callbacks->fetch_props_func = fetch_props_func; 3687251881Speter callbacks->fetch_kind_func = fetch_kind_func; 3688251881Speter callbacks->fetch_baton = &b; 3689251881Speter 3690251881Speter SVN_ERR(svn_ra_svn__set_shim_callbacks(conn, callbacks)); 3691251881Speter } 3692251881Speter 3693251881Speter return svn_ra_svn__handle_commands2(conn, pool, main_commands, &b, FALSE); 3694251881Speter} 3695