1/* 2 * ra_loader.c: logic for loading different RA library implementations 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24/* ==================================================================== */ 25 26/*** Includes. ***/ 27#define APR_WANT_STRFUNC 28#include <apr_want.h> 29 30#include <apr.h> 31#include <apr_strings.h> 32#include <apr_pools.h> 33#include <apr_hash.h> 34#include <apr_uri.h> 35 36#include "svn_hash.h" 37#include "svn_version.h" 38#include "svn_types.h" 39#include "svn_error.h" 40#include "svn_error_codes.h" 41#include "svn_pools.h" 42#include "svn_delta.h" 43#include "svn_ra.h" 44#include "svn_xml.h" 45#include "svn_path.h" 46#include "svn_dso.h" 47#include "svn_props.h" 48#include "svn_sorts.h" 49 50#include "svn_config.h" 51#include "ra_loader.h" 52#include "deprecated.h" 53 54#include "private/svn_ra_private.h" 55#include "svn_private_config.h" 56 57 58 59 60/* These are the URI schemes that the respective libraries *may* support. 61 * The schemes actually supported may be a subset of the schemes listed below. 62 * This can't be determine until the library is loaded. 63 * (Currently, this applies to the https scheme, which is only 64 * available if SSL is supported.) */ 65static const char * const dav_schemes[] = { "http", "https", NULL }; 66static const char * const svn_schemes[] = { "svn", NULL }; 67static const char * const local_schemes[] = { "file", NULL }; 68 69static const struct ra_lib_defn { 70 /* the name of this RA library (e.g. "neon" or "local") */ 71 const char *ra_name; 72 73 const char * const *schemes; 74 /* the initialization function if linked in; otherwise, NULL */ 75 svn_ra__init_func_t initfunc; 76 svn_ra_init_func_t compat_initfunc; 77} ra_libraries[] = { 78 { 79 "svn", 80 svn_schemes, 81#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SVN 82 svn_ra_svn__init, 83 svn_ra_svn__deprecated_init 84#endif 85 }, 86 87 { 88 "local", 89 local_schemes, 90#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 91 svn_ra_local__init, 92 svn_ra_local__deprecated_init 93#endif 94 }, 95 96 { 97 "serf", 98 dav_schemes, 99#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SERF 100 svn_ra_serf__init, 101 svn_ra_serf__deprecated_init 102#endif 103 }, 104 105 /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */ 106 107 /* sentinel */ 108 { NULL } 109}; 110 111/* Ensure that the RA library NAME is loaded. 112 * 113 * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init 114 * function of the library. 115 * 116 * If COMPAT_FUNC is non-NULL, set *COMPAT_FUNC to the address of the 117 * svn_ra_NAME_init compatibility init function of the library. 118 * 119 * ### todo: Any RA libraries implemented from this point forward 120 * ### don't really need an svn_ra_NAME_init compatibility function. 121 * ### Currently, load_ra_module() will error if no such function is 122 * ### found, but it might be more friendly to simply set *COMPAT_FUNC 123 * ### to null (assuming COMPAT_FUNC itself is non-null). 124 */ 125static svn_error_t * 126load_ra_module(svn_ra__init_func_t *func, 127 svn_ra_init_func_t *compat_func, 128 const char *ra_name, apr_pool_t *pool) 129{ 130 if (func) 131 *func = NULL; 132 if (compat_func) 133 *compat_func = NULL; 134 135#if defined(SVN_USE_DSO) && APR_HAS_DSO 136 { 137 apr_dso_handle_t *dso; 138 apr_dso_handle_sym_t symbol; 139 const char *libname; 140 const char *funcname; 141 const char *compat_funcname; 142 apr_status_t status; 143 144 libname = apr_psprintf(pool, "libsvn_ra_%s-%d.so.%d", 145 ra_name, SVN_VER_MAJOR, SVN_SOVERSION); 146 funcname = apr_psprintf(pool, "svn_ra_%s__init", ra_name); 147 compat_funcname = apr_psprintf(pool, "svn_ra_%s_init", ra_name); 148 149 /* find/load the specified library */ 150 SVN_ERR(svn_dso_load(&dso, libname)); 151 if (! dso) 152 return SVN_NO_ERROR; 153 154 /* find the initialization routines */ 155 if (func) 156 { 157 status = apr_dso_sym(&symbol, dso, funcname); 158 if (status) 159 { 160 return svn_error_wrap_apr(status, 161 _("'%s' does not define '%s()'"), 162 libname, funcname); 163 } 164 165 *func = (svn_ra__init_func_t) symbol; 166 } 167 168 if (compat_func) 169 { 170 status = apr_dso_sym(&symbol, dso, compat_funcname); 171 if (status) 172 { 173 return svn_error_wrap_apr(status, 174 _("'%s' does not define '%s()'"), 175 libname, compat_funcname); 176 } 177 178 *compat_func = (svn_ra_init_func_t) symbol; 179 } 180 } 181#endif /* APR_HAS_DSO */ 182 183 return SVN_NO_ERROR; 184} 185 186/* If SCHEMES contains URL, return the scheme. Else, return NULL. */ 187static const char * 188has_scheme_of(const char * const *schemes, const char *url) 189{ 190 apr_size_t len; 191 192 for ( ; *schemes != NULL; ++schemes) 193 { 194 const char *scheme = *schemes; 195 len = strlen(scheme); 196 /* Case-insensitive comparison, per RFC 2396 section 3.1. Allow 197 URL to contain a trailing "+foo" section in the scheme, since 198 that's how we specify tunnel schemes in ra_svn. */ 199 if (strncasecmp(scheme, url, len) == 0 && 200 (url[len] == ':' || url[len] == '+')) 201 return scheme; 202 } 203 204 return NULL; 205} 206 207/* Return an error if RA_VERSION doesn't match the version of this library. 208 Use SCHEME in the error message to describe the library that was loaded. */ 209static svn_error_t * 210check_ra_version(const svn_version_t *ra_version, const char *scheme) 211{ 212 const svn_version_t *my_version = svn_ra_version(); 213 if (!svn_ver_equal(my_version, ra_version)) 214 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 215 _("Mismatched RA version for '%s':" 216 " found %d.%d.%d%s," 217 " expected %d.%d.%d%s"), 218 scheme, 219 my_version->major, my_version->minor, 220 my_version->patch, my_version->tag, 221 ra_version->major, ra_version->minor, 222 ra_version->patch, ra_version->tag); 223 224 return SVN_NO_ERROR; 225} 226 227/* -------------------------------------------------------------- */ 228 229/*** Public Interfaces ***/ 230 231svn_error_t *svn_ra_initialize(apr_pool_t *pool) 232{ 233 return SVN_NO_ERROR; 234} 235 236/* Please note: the implementation of svn_ra_create_callbacks is 237 * duplicated in libsvn_ra/wrapper_template.h:compat_open() . This 238 * duplication is intentional, is there to avoid a circular 239 * dependancy, and is justified in great length in the code of 240 * compat_open() in libsvn_ra/wrapper_template.h. If you modify the 241 * implementation of svn_ra_create_callbacks(), be sure to keep the 242 * code in wrapper_template.h:compat_open() in sync with your 243 * changes. */ 244svn_error_t * 245svn_ra_create_callbacks(svn_ra_callbacks2_t **callbacks, 246 apr_pool_t *pool) 247{ 248 *callbacks = apr_pcalloc(pool, sizeof(**callbacks)); 249 return SVN_NO_ERROR; 250} 251 252svn_error_t *svn_ra_open4(svn_ra_session_t **session_p, 253 const char **corrected_url_p, 254 const char *repos_URL, 255 const char *uuid, 256 const svn_ra_callbacks2_t *callbacks, 257 void *callback_baton, 258 apr_hash_t *config, 259 apr_pool_t *pool) 260{ 261 apr_pool_t *sesspool = svn_pool_create(pool); 262 svn_ra_session_t *session; 263 const struct ra_lib_defn *defn; 264 const svn_ra__vtable_t *vtable = NULL; 265 svn_config_t *servers = NULL; 266 const char *server_group; 267 apr_uri_t repos_URI; 268 apr_status_t apr_err; 269#ifdef CHOOSABLE_DAV_MODULE 270 const char *http_library = DEFAULT_HTTP_LIBRARY; 271#endif 272 /* Auth caching parameters. */ 273 svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; 274 svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; 275 const char *store_plaintext_passwords 276 = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS; 277 svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP; 278 const char *store_pp_plaintext 279 = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT; 280 const char *corrected_url; 281 282 /* Initialize the return variable. */ 283 *session_p = NULL; 284 285 apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI); 286 /* ### Should apr_uri_parse leave hostname NULL? It doesn't 287 * for "file:///" URLs, only for bogus URLs like "bogus". 288 * If this is the right behavior for apr_uri_parse, maybe we 289 * should have a svn_uri_parse wrapper. */ 290 if (apr_err != APR_SUCCESS || repos_URI.hostname == NULL) 291 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 292 _("Illegal repository URL '%s'"), 293 repos_URL); 294 295 if (callbacks->auth_baton) 296 { 297 /* The 'store-passwords' and 'store-auth-creds' parameters used to 298 * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility, 299 * if values for these parameters have already been set by our 300 * callers, we use those values as defaults. 301 * 302 * Note that we can only catch the case where users explicitly set 303 * "store-passwords = no" or 'store-auth-creds = no". 304 * 305 * However, since the default value for both these options is 306 * currently (and has always been) "yes", users won't know 307 * the difference if they set "store-passwords = yes" or 308 * "store-auth-creds = yes" -- they'll get the expected behaviour. 309 */ 310 311 if (svn_auth_get_parameter(callbacks->auth_baton, 312 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL) 313 store_passwords = FALSE; 314 315 if (svn_auth_get_parameter(callbacks->auth_baton, 316 SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL) 317 store_auth_creds = FALSE; 318 } 319 320 if (config) 321 { 322 /* Grab the 'servers' config. */ 323 servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS); 324 if (servers) 325 { 326 /* First, look in the global section. */ 327 328 SVN_ERR(svn_config_get_bool 329 (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL, 330 SVN_CONFIG_OPTION_STORE_PASSWORDS, 331 store_passwords)); 332 333 SVN_ERR(svn_config_get_yes_no_ask 334 (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL, 335 SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, 336 SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); 337 338 SVN_ERR(svn_config_get_bool 339 (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL, 340 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, 341 store_pp)); 342 343 SVN_ERR(svn_config_get_yes_no_ask 344 (servers, &store_pp_plaintext, 345 SVN_CONFIG_SECTION_GLOBAL, 346 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 347 SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT)); 348 349 SVN_ERR(svn_config_get_bool 350 (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL, 351 SVN_CONFIG_OPTION_STORE_AUTH_CREDS, 352 store_auth_creds)); 353 354 /* Find out where we're about to connect to, and 355 * try to pick a server group based on the destination. */ 356 server_group = svn_config_find_group(servers, repos_URI.hostname, 357 SVN_CONFIG_SECTION_GROUPS, 358 sesspool); 359 360 if (server_group) 361 { 362 /* Override global auth caching parameters with the ones 363 * for the server group, if any. */ 364 SVN_ERR(svn_config_get_bool(servers, &store_auth_creds, 365 server_group, 366 SVN_CONFIG_OPTION_STORE_AUTH_CREDS, 367 store_auth_creds)); 368 369 SVN_ERR(svn_config_get_bool(servers, &store_passwords, 370 server_group, 371 SVN_CONFIG_OPTION_STORE_PASSWORDS, 372 store_passwords)); 373 374 SVN_ERR(svn_config_get_yes_no_ask 375 (servers, &store_plaintext_passwords, server_group, 376 SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, 377 store_plaintext_passwords)); 378 379 SVN_ERR(svn_config_get_bool 380 (servers, &store_pp, 381 server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, 382 store_pp)); 383 384 SVN_ERR(svn_config_get_yes_no_ask 385 (servers, &store_pp_plaintext, server_group, 386 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 387 store_pp_plaintext)); 388 } 389#ifdef CHOOSABLE_DAV_MODULE 390 /* Now, which DAV-based RA method do we want to use today? */ 391 http_library 392 = svn_config_get_server_setting(servers, 393 server_group, /* NULL is OK */ 394 SVN_CONFIG_OPTION_HTTP_LIBRARY, 395 DEFAULT_HTTP_LIBRARY); 396 397 if (strcmp(http_library, "serf") != 0) 398 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 399 _("Invalid config: unknown HTTP library " 400 "'%s'"), 401 http_library); 402#endif 403 } 404 } 405 406 if (callbacks->auth_baton) 407 { 408 /* Save auth caching parameters in the auth parameter hash. */ 409 if (! store_passwords) 410 svn_auth_set_parameter(callbacks->auth_baton, 411 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); 412 413 svn_auth_set_parameter(callbacks->auth_baton, 414 SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS, 415 store_plaintext_passwords); 416 417 if (! store_pp) 418 svn_auth_set_parameter(callbacks->auth_baton, 419 SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP, 420 ""); 421 422 svn_auth_set_parameter(callbacks->auth_baton, 423 SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 424 store_pp_plaintext); 425 426 if (! store_auth_creds) 427 svn_auth_set_parameter(callbacks->auth_baton, 428 SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); 429 } 430 431 /* Find the library. */ 432 for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 433 { 434 const char *scheme; 435 436 if ((scheme = has_scheme_of(defn->schemes, repos_URL))) 437 { 438 svn_ra__init_func_t initfunc = defn->initfunc; 439 440#ifdef CHOOSABLE_DAV_MODULE 441 if (defn->schemes == dav_schemes 442 && strcmp(defn->ra_name, http_library) != 0) 443 continue; 444#endif 445 446 if (! initfunc) 447 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, 448 sesspool)); 449 if (! initfunc) 450 /* Library not found. */ 451 continue; 452 453 SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool)); 454 455 SVN_ERR(check_ra_version(vtable->get_version(), scheme)); 456 457 if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL)) 458 /* Library doesn't support the scheme at runtime. */ 459 continue; 460 461 462 break; 463 } 464 } 465 466 if (vtable == NULL) 467 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 468 _("Unrecognized URL scheme for '%s'"), 469 repos_URL); 470 471 /* Create the session object. */ 472 session = apr_pcalloc(sesspool, sizeof(*session)); 473 session->cancel_func = callbacks->cancel_func; 474 session->cancel_baton = callback_baton; 475 session->vtable = vtable; 476 session->pool = sesspool; 477 478 /* Ask the library to open the session. */ 479 SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL, 480 callbacks, callback_baton, config, sesspool), 481 apr_psprintf(pool, "Unable to connect to a repository at URL '%s'", 482 repos_URL)); 483 484 /* If the session open stuff detected a server-provided URL 485 correction (a 301 or 302 redirect response during the initial 486 OPTIONS request), then kill the session so the caller can decide 487 what to do. */ 488 if (corrected_url_p && corrected_url) 489 { 490 if (! svn_path_is_url(corrected_url)) 491 { 492 /* RFC1945 and RFC2616 state that the Location header's 493 value (from whence this CORRECTED_URL ultimately comes), 494 if present, must be an absolute URI. But some Apache 495 versions (those older than 2.2.11, it seems) transmit 496 only the path portion of the URI. See issue #3775 for 497 details. */ 498 apr_uri_t corrected_URI = repos_URI; 499 corrected_URI.path = (char *)corrected_url; 500 corrected_url = apr_uri_unparse(pool, &corrected_URI, 0); 501 } 502 *corrected_url_p = svn_uri_canonicalize(corrected_url, pool); 503 svn_pool_destroy(sesspool); 504 return SVN_NO_ERROR; 505 } 506 507 /* Check the UUID. */ 508 if (uuid) 509 { 510 const char *repository_uuid; 511 512 SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool)); 513 if (strcmp(uuid, repository_uuid) != 0) 514 { 515 /* Duplicate the uuid as it is allocated in sesspool */ 516 repository_uuid = apr_pstrdup(pool, repository_uuid); 517 svn_pool_destroy(sesspool); 518 return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL, 519 _("Repository UUID '%s' doesn't match " 520 "expected UUID '%s'"), 521 repository_uuid, uuid); 522 } 523 } 524 525 *session_p = session; 526 return SVN_NO_ERROR; 527} 528 529svn_error_t *svn_ra_reparent(svn_ra_session_t *session, 530 const char *url, 531 apr_pool_t *pool) 532{ 533 const char *repos_root; 534 535 /* Make sure the new URL is in the same repository, so that the 536 implementations don't have to do it. */ 537 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, pool)); 538 if (! svn_uri__is_ancestor(repos_root, url)) 539 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 540 _("'%s' isn't in the same repository as '%s'"), 541 url, repos_root); 542 543 return session->vtable->reparent(session, url, pool); 544} 545 546svn_error_t *svn_ra_get_session_url(svn_ra_session_t *session, 547 const char **url, 548 apr_pool_t *pool) 549{ 550 return session->vtable->get_session_url(session, url, pool); 551} 552 553svn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session, 554 const char **rel_path, 555 const char *url, 556 apr_pool_t *pool) 557{ 558 const char *sess_url; 559 560 SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool)); 561 *rel_path = svn_uri_skip_ancestor(sess_url, url, pool); 562 if (! *rel_path) 563 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 564 _("'%s' isn't a child of session URL '%s'"), 565 url, sess_url); 566 return SVN_NO_ERROR; 567} 568 569svn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session, 570 const char **rel_path, 571 const char *url, 572 apr_pool_t *pool) 573{ 574 const char *root_url; 575 576 SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool)); 577 *rel_path = svn_uri_skip_ancestor(root_url, url, pool); 578 if (! *rel_path) 579 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 580 _("'%s' isn't a child of repository root " 581 "URL '%s'"), 582 url, root_url); 583 return SVN_NO_ERROR; 584} 585 586svn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session, 587 svn_revnum_t *latest_revnum, 588 apr_pool_t *pool) 589{ 590 return session->vtable->get_latest_revnum(session, latest_revnum, pool); 591} 592 593svn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session, 594 svn_revnum_t *revision, 595 apr_time_t tm, 596 apr_pool_t *pool) 597{ 598 return session->vtable->get_dated_revision(session, revision, tm, pool); 599} 600 601svn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session, 602 svn_revnum_t rev, 603 const char *name, 604 const svn_string_t *const *old_value_p, 605 const svn_string_t *value, 606 apr_pool_t *pool) 607{ 608 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 609 610 /* If an old value was specified, make sure the server supports 611 * specifying it. */ 612 if (old_value_p) 613 { 614 svn_boolean_t has_atomic_revprops; 615 616 SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops, 617 SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 618 pool)); 619 620 if (!has_atomic_revprops) 621 /* API violation. (Should be an ASSERT, but gstein talked me 622 * out of it.) */ 623 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 624 _("Specifying 'old_value_p' is not allowed when " 625 "the '%s' capability is not advertised, and " 626 "could indicate a bug in your client"), 627 SVN_RA_CAPABILITY_ATOMIC_REVPROPS); 628 } 629 630 return session->vtable->change_rev_prop(session, rev, name, 631 old_value_p, value, pool); 632} 633 634svn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session, 635 svn_revnum_t rev, 636 apr_hash_t **props, 637 apr_pool_t *pool) 638{ 639 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 640 return session->vtable->rev_proplist(session, rev, props, pool); 641} 642 643svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session, 644 svn_revnum_t rev, 645 const char *name, 646 svn_string_t **value, 647 apr_pool_t *pool) 648{ 649 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 650 return session->vtable->rev_prop(session, rev, name, value, pool); 651} 652 653struct ccw_baton 654{ 655 svn_commit_callback2_t original_callback; 656 void *original_baton; 657 658 svn_ra_session_t *session; 659}; 660 661/* Wrapper which populates the repos_root field of the commit_info struct */ 662static svn_error_t * 663commit_callback_wrapper(const svn_commit_info_t *commit_info, 664 void *baton, 665 apr_pool_t *pool) 666{ 667 struct ccw_baton *ccwb = baton; 668 svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool); 669 670 SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool)); 671 672 return ccwb->original_callback(ci, ccwb->original_baton, pool); 673} 674 675 676/* Some RA layers do not correctly fill in REPOS_ROOT in commit_info, or 677 they are third-party layers conforming to an older commit_info structure. 678 Interpose a utility function to ensure the field is valid. */ 679static void 680remap_commit_callback(svn_commit_callback2_t *callback, 681 void **callback_baton, 682 svn_ra_session_t *session, 683 svn_commit_callback2_t original_callback, 684 void *original_baton, 685 apr_pool_t *result_pool) 686{ 687 if (original_callback == NULL) 688 { 689 *callback = NULL; 690 *callback_baton = NULL; 691 } 692 else 693 { 694 /* Allocate this in RESULT_POOL, since the callback will be called 695 long after this function has returned. */ 696 struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb)); 697 698 ccwb->session = session; 699 ccwb->original_callback = original_callback; 700 ccwb->original_baton = original_baton; 701 702 *callback = commit_callback_wrapper; 703 *callback_baton = ccwb; 704 } 705} 706 707 708svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session, 709 const svn_delta_editor_t **editor, 710 void **edit_baton, 711 apr_hash_t *revprop_table, 712 svn_commit_callback2_t commit_callback, 713 void *commit_baton, 714 apr_hash_t *lock_tokens, 715 svn_boolean_t keep_locks, 716 apr_pool_t *pool) 717{ 718 remap_commit_callback(&commit_callback, &commit_baton, 719 session, commit_callback, commit_baton, 720 pool); 721 722 return session->vtable->get_commit_editor(session, editor, edit_baton, 723 revprop_table, 724 commit_callback, commit_baton, 725 lock_tokens, keep_locks, pool); 726} 727 728svn_error_t *svn_ra_get_file(svn_ra_session_t *session, 729 const char *path, 730 svn_revnum_t revision, 731 svn_stream_t *stream, 732 svn_revnum_t *fetched_rev, 733 apr_hash_t **props, 734 apr_pool_t *pool) 735{ 736 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 737 return session->vtable->get_file(session, path, revision, stream, 738 fetched_rev, props, pool); 739} 740 741svn_error_t *svn_ra_get_dir2(svn_ra_session_t *session, 742 apr_hash_t **dirents, 743 svn_revnum_t *fetched_rev, 744 apr_hash_t **props, 745 const char *path, 746 svn_revnum_t revision, 747 apr_uint32_t dirent_fields, 748 apr_pool_t *pool) 749{ 750 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 751 return session->vtable->get_dir(session, dirents, fetched_rev, props, 752 path, revision, dirent_fields, pool); 753} 754 755svn_error_t *svn_ra_get_mergeinfo(svn_ra_session_t *session, 756 svn_mergeinfo_catalog_t *catalog, 757 const apr_array_header_t *paths, 758 svn_revnum_t revision, 759 svn_mergeinfo_inheritance_t inherit, 760 svn_boolean_t include_descendants, 761 apr_pool_t *pool) 762{ 763 svn_error_t *err; 764 int i; 765 766 /* Validate path format. */ 767 for (i = 0; i < paths->nelts; i++) 768 { 769 const char *path = APR_ARRAY_IDX(paths, i, const char *); 770 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 771 } 772 773 /* Check server Merge Tracking capability. */ 774 err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool); 775 if (err) 776 { 777 *catalog = NULL; 778 return err; 779 } 780 781 return session->vtable->get_mergeinfo(session, catalog, paths, 782 revision, inherit, 783 include_descendants, pool); 784} 785 786svn_error_t * 787svn_ra_do_update3(svn_ra_session_t *session, 788 const svn_ra_reporter3_t **reporter, 789 void **report_baton, 790 svn_revnum_t revision_to_update_to, 791 const char *update_target, 792 svn_depth_t depth, 793 svn_boolean_t send_copyfrom_args, 794 svn_boolean_t ignore_ancestry, 795 const svn_delta_editor_t *update_editor, 796 void *update_baton, 797 apr_pool_t *result_pool, 798 apr_pool_t *scratch_pool) 799{ 800 SVN_ERR_ASSERT(svn_path_is_empty(update_target) 801 || svn_path_is_single_path_component(update_target)); 802 return session->vtable->do_update(session, 803 reporter, report_baton, 804 revision_to_update_to, update_target, 805 depth, send_copyfrom_args, 806 ignore_ancestry, 807 update_editor, update_baton, 808 result_pool, scratch_pool); 809} 810 811svn_error_t * 812svn_ra_do_switch3(svn_ra_session_t *session, 813 const svn_ra_reporter3_t **reporter, 814 void **report_baton, 815 svn_revnum_t revision_to_switch_to, 816 const char *switch_target, 817 svn_depth_t depth, 818 const char *switch_url, 819 svn_boolean_t send_copyfrom_args, 820 svn_boolean_t ignore_ancestry, 821 const svn_delta_editor_t *switch_editor, 822 void *switch_baton, 823 apr_pool_t *result_pool, 824 apr_pool_t *scratch_pool) 825{ 826 SVN_ERR_ASSERT(svn_path_is_empty(switch_target) 827 || svn_path_is_single_path_component(switch_target)); 828 return session->vtable->do_switch(session, 829 reporter, report_baton, 830 revision_to_switch_to, switch_target, 831 depth, switch_url, 832 send_copyfrom_args, 833 ignore_ancestry, 834 switch_editor, 835 switch_baton, 836 result_pool, scratch_pool); 837} 838 839svn_error_t *svn_ra_do_status2(svn_ra_session_t *session, 840 const svn_ra_reporter3_t **reporter, 841 void **report_baton, 842 const char *status_target, 843 svn_revnum_t revision, 844 svn_depth_t depth, 845 const svn_delta_editor_t *status_editor, 846 void *status_baton, 847 apr_pool_t *pool) 848{ 849 SVN_ERR_ASSERT(svn_path_is_empty(status_target) 850 || svn_path_is_single_path_component(status_target)); 851 return session->vtable->do_status(session, 852 reporter, report_baton, 853 status_target, revision, depth, 854 status_editor, status_baton, pool); 855} 856 857svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session, 858 const svn_ra_reporter3_t **reporter, 859 void **report_baton, 860 svn_revnum_t revision, 861 const char *diff_target, 862 svn_depth_t depth, 863 svn_boolean_t ignore_ancestry, 864 svn_boolean_t text_deltas, 865 const char *versus_url, 866 const svn_delta_editor_t *diff_editor, 867 void *diff_baton, 868 apr_pool_t *pool) 869{ 870 SVN_ERR_ASSERT(svn_path_is_empty(diff_target) 871 || svn_path_is_single_path_component(diff_target)); 872 return session->vtable->do_diff(session, 873 reporter, report_baton, 874 revision, diff_target, 875 depth, ignore_ancestry, 876 text_deltas, versus_url, diff_editor, 877 diff_baton, pool); 878} 879 880svn_error_t *svn_ra_get_log2(svn_ra_session_t *session, 881 const apr_array_header_t *paths, 882 svn_revnum_t start, 883 svn_revnum_t end, 884 int limit, 885 svn_boolean_t discover_changed_paths, 886 svn_boolean_t strict_node_history, 887 svn_boolean_t include_merged_revisions, 888 const apr_array_header_t *revprops, 889 svn_log_entry_receiver_t receiver, 890 void *receiver_baton, 891 apr_pool_t *pool) 892{ 893 if (paths) 894 { 895 int i; 896 for (i = 0; i < paths->nelts; i++) 897 { 898 const char *path = APR_ARRAY_IDX(paths, i, const char *); 899 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 900 } 901 } 902 903 if (include_merged_revisions) 904 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool)); 905 906 return session->vtable->get_log(session, paths, start, end, limit, 907 discover_changed_paths, strict_node_history, 908 include_merged_revisions, revprops, 909 receiver, receiver_baton, pool); 910} 911 912svn_error_t *svn_ra_check_path(svn_ra_session_t *session, 913 const char *path, 914 svn_revnum_t revision, 915 svn_node_kind_t *kind, 916 apr_pool_t *pool) 917{ 918 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 919 return session->vtable->check_path(session, path, revision, kind, pool); 920} 921 922svn_error_t *svn_ra_stat(svn_ra_session_t *session, 923 const char *path, 924 svn_revnum_t revision, 925 svn_dirent_t **dirent, 926 apr_pool_t *pool) 927{ 928 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 929 return session->vtable->stat(session, path, revision, dirent, pool); 930} 931 932svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session, 933 const char **uuid, 934 apr_pool_t *pool) 935{ 936 SVN_ERR(session->vtable->get_uuid(session, uuid, pool)); 937 *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL; 938 return SVN_NO_ERROR; 939} 940 941svn_error_t *svn_ra_get_uuid(svn_ra_session_t *session, 942 const char **uuid, 943 apr_pool_t *pool) 944{ 945 return session->vtable->get_uuid(session, uuid, pool); 946} 947 948svn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session, 949 const char **url, 950 apr_pool_t *pool) 951{ 952 SVN_ERR(session->vtable->get_repos_root(session, url, pool)); 953 *url = *url ? apr_pstrdup(pool, *url) : NULL; 954 return SVN_NO_ERROR; 955} 956 957svn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session, 958 const char **url, 959 apr_pool_t *pool) 960{ 961 return session->vtable->get_repos_root(session, url, pool); 962} 963 964svn_error_t *svn_ra_get_locations(svn_ra_session_t *session, 965 apr_hash_t **locations, 966 const char *path, 967 svn_revnum_t peg_revision, 968 const apr_array_header_t *location_revisions, 969 apr_pool_t *pool) 970{ 971 svn_error_t *err; 972 973 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 974 err = session->vtable->get_locations(session, locations, path, 975 peg_revision, location_revisions, pool); 976 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 977 { 978 svn_error_clear(err); 979 980 /* Do it the slow way, using get-logs, for older servers. */ 981 err = svn_ra__locations_from_log(session, locations, path, 982 peg_revision, location_revisions, 983 pool); 984 } 985 return err; 986} 987 988svn_error_t * 989svn_ra_get_location_segments(svn_ra_session_t *session, 990 const char *path, 991 svn_revnum_t peg_revision, 992 svn_revnum_t start_rev, 993 svn_revnum_t end_rev, 994 svn_location_segment_receiver_t receiver, 995 void *receiver_baton, 996 apr_pool_t *pool) 997{ 998 svn_error_t *err; 999 1000 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1001 err = session->vtable->get_location_segments(session, path, peg_revision, 1002 start_rev, end_rev, 1003 receiver, receiver_baton, pool); 1004 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 1005 { 1006 svn_error_clear(err); 1007 1008 /* Do it the slow way, using get-logs, for older servers. */ 1009 err = svn_ra__location_segments_from_log(session, path, 1010 peg_revision, start_rev, 1011 end_rev, receiver, 1012 receiver_baton, pool); 1013 } 1014 return err; 1015} 1016 1017svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session, 1018 const char *path, 1019 svn_revnum_t start, 1020 svn_revnum_t end, 1021 svn_boolean_t include_merged_revisions, 1022 svn_file_rev_handler_t handler, 1023 void *handler_baton, 1024 apr_pool_t *pool) 1025{ 1026 svn_error_t *err; 1027 1028 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1029 1030 if (include_merged_revisions) 1031 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool)); 1032 1033 if (start > end) 1034 SVN_ERR( 1035 svn_ra__assert_capable_server(session, 1036 SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 1037 NULL, 1038 pool)); 1039 1040 err = session->vtable->get_file_revs(session, path, start, end, 1041 include_merged_revisions, 1042 handler, handler_baton, pool); 1043 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 1044 { 1045 svn_error_clear(err); 1046 1047 /* Do it the slow way, using get-logs, for older servers. */ 1048 err = svn_ra__file_revs_from_log(session, path, start, end, 1049 handler, handler_baton, pool); 1050 } 1051 return err; 1052} 1053 1054svn_error_t *svn_ra_lock(svn_ra_session_t *session, 1055 apr_hash_t *path_revs, 1056 const char *comment, 1057 svn_boolean_t steal_lock, 1058 svn_ra_lock_callback_t lock_func, 1059 void *lock_baton, 1060 apr_pool_t *pool) 1061{ 1062 apr_hash_index_t *hi; 1063 1064 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) 1065 { 1066 const char *path = svn__apr_hash_index_key(hi); 1067 1068 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1069 } 1070 1071 if (comment && ! svn_xml_is_xml_safe(comment, strlen(comment))) 1072 return svn_error_create 1073 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL, 1074 _("Lock comment contains illegal characters")); 1075 1076 return session->vtable->lock(session, path_revs, comment, steal_lock, 1077 lock_func, lock_baton, pool); 1078} 1079 1080svn_error_t *svn_ra_unlock(svn_ra_session_t *session, 1081 apr_hash_t *path_tokens, 1082 svn_boolean_t break_lock, 1083 svn_ra_lock_callback_t lock_func, 1084 void *lock_baton, 1085 apr_pool_t *pool) 1086{ 1087 apr_hash_index_t *hi; 1088 1089 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) 1090 { 1091 const char *path = svn__apr_hash_index_key(hi); 1092 1093 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1094 } 1095 1096 return session->vtable->unlock(session, path_tokens, break_lock, 1097 lock_func, lock_baton, pool); 1098} 1099 1100svn_error_t *svn_ra_get_lock(svn_ra_session_t *session, 1101 svn_lock_t **lock, 1102 const char *path, 1103 apr_pool_t *pool) 1104{ 1105 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1106 return session->vtable->get_lock(session, lock, path, pool); 1107} 1108 1109svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session, 1110 apr_hash_t **locks, 1111 const char *path, 1112 svn_depth_t depth, 1113 apr_pool_t *pool) 1114{ 1115 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1116 SVN_ERR_ASSERT((depth == svn_depth_empty) || 1117 (depth == svn_depth_files) || 1118 (depth == svn_depth_immediates) || 1119 (depth == svn_depth_infinity)); 1120 return session->vtable->get_locks(session, locks, path, depth, pool); 1121} 1122 1123svn_error_t *svn_ra_get_locks(svn_ra_session_t *session, 1124 apr_hash_t **locks, 1125 const char *path, 1126 apr_pool_t *pool) 1127{ 1128 return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool); 1129} 1130 1131svn_error_t *svn_ra_replay(svn_ra_session_t *session, 1132 svn_revnum_t revision, 1133 svn_revnum_t low_water_mark, 1134 svn_boolean_t text_deltas, 1135 const svn_delta_editor_t *editor, 1136 void *edit_baton, 1137 apr_pool_t *pool) 1138{ 1139 return session->vtable->replay(session, revision, low_water_mark, 1140 text_deltas, editor, edit_baton, pool); 1141} 1142 1143svn_error_t * 1144svn_ra__replay_ev2(svn_ra_session_t *session, 1145 svn_revnum_t revision, 1146 svn_revnum_t low_water_mark, 1147 svn_boolean_t send_deltas, 1148 svn_editor_t *editor, 1149 apr_pool_t *scratch_pool) 1150{ 1151 SVN__NOT_IMPLEMENTED(); 1152} 1153 1154static svn_error_t * 1155replay_range_from_replays(svn_ra_session_t *session, 1156 svn_revnum_t start_revision, 1157 svn_revnum_t end_revision, 1158 svn_revnum_t low_water_mark, 1159 svn_boolean_t text_deltas, 1160 svn_ra_replay_revstart_callback_t revstart_func, 1161 svn_ra_replay_revfinish_callback_t revfinish_func, 1162 void *replay_baton, 1163 apr_pool_t *scratch_pool) 1164{ 1165 apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1166 svn_revnum_t rev; 1167 1168 for (rev = start_revision ; rev <= end_revision ; rev++) 1169 { 1170 const svn_delta_editor_t *editor; 1171 void *edit_baton; 1172 apr_hash_t *rev_props; 1173 1174 svn_pool_clear(iterpool); 1175 1176 SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool)); 1177 1178 SVN_ERR(revstart_func(rev, replay_baton, 1179 &editor, &edit_baton, 1180 rev_props, 1181 iterpool)); 1182 SVN_ERR(svn_ra_replay(session, rev, low_water_mark, 1183 text_deltas, editor, edit_baton, 1184 iterpool)); 1185 SVN_ERR(revfinish_func(rev, replay_baton, 1186 editor, edit_baton, 1187 rev_props, 1188 iterpool)); 1189 } 1190 svn_pool_destroy(iterpool); 1191 1192 return SVN_NO_ERROR; 1193} 1194 1195svn_error_t * 1196svn_ra_replay_range(svn_ra_session_t *session, 1197 svn_revnum_t start_revision, 1198 svn_revnum_t end_revision, 1199 svn_revnum_t low_water_mark, 1200 svn_boolean_t text_deltas, 1201 svn_ra_replay_revstart_callback_t revstart_func, 1202 svn_ra_replay_revfinish_callback_t revfinish_func, 1203 void *replay_baton, 1204 apr_pool_t *pool) 1205{ 1206 svn_error_t *err = 1207 session->vtable->replay_range(session, start_revision, end_revision, 1208 low_water_mark, text_deltas, 1209 revstart_func, revfinish_func, 1210 replay_baton, pool); 1211 1212 if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED))) 1213 return svn_error_trace(err); 1214 1215 svn_error_clear(err); 1216 return svn_error_trace(replay_range_from_replays(session, start_revision, 1217 end_revision, 1218 low_water_mark, 1219 text_deltas, 1220 revstart_func, 1221 revfinish_func, 1222 replay_baton, pool)); 1223} 1224 1225svn_error_t * 1226svn_ra__replay_range_ev2(svn_ra_session_t *session, 1227 svn_revnum_t start_revision, 1228 svn_revnum_t end_revision, 1229 svn_revnum_t low_water_mark, 1230 svn_boolean_t send_deltas, 1231 svn_ra__replay_revstart_ev2_callback_t revstart_func, 1232 svn_ra__replay_revfinish_ev2_callback_t revfinish_func, 1233 void *replay_baton, 1234 svn_ra__provide_base_cb_t provide_base_cb, 1235 svn_ra__provide_props_cb_t provide_props_cb, 1236 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1237 void *cb_baton, 1238 apr_pool_t *scratch_pool) 1239{ 1240 if (session->vtable->replay_range_ev2 == NULL) 1241 { 1242 /* The specific RA layer does not have an implementation. Use our 1243 default shim over the normal replay editor. */ 1244 1245 /* This will call the Ev1 replay range handler with modified 1246 callbacks. */ 1247 return svn_error_trace(svn_ra__use_replay_range_shim( 1248 session, 1249 start_revision, 1250 end_revision, 1251 low_water_mark, 1252 send_deltas, 1253 revstart_func, 1254 revfinish_func, 1255 replay_baton, 1256 provide_base_cb, 1257 provide_props_cb, 1258 cb_baton, 1259 scratch_pool)); 1260 } 1261 1262 return svn_error_trace(session->vtable->replay_range_ev2( 1263 session, start_revision, end_revision, 1264 low_water_mark, send_deltas, revstart_func, 1265 revfinish_func, replay_baton, scratch_pool)); 1266} 1267 1268svn_error_t *svn_ra_has_capability(svn_ra_session_t *session, 1269 svn_boolean_t *has, 1270 const char *capability, 1271 apr_pool_t *pool) 1272{ 1273 return session->vtable->has_capability(session, has, capability, pool); 1274} 1275 1276svn_error_t * 1277svn_ra_get_deleted_rev(svn_ra_session_t *session, 1278 const char *path, 1279 svn_revnum_t peg_revision, 1280 svn_revnum_t end_revision, 1281 svn_revnum_t *revision_deleted, 1282 apr_pool_t *pool) 1283{ 1284 svn_error_t *err; 1285 1286 /* Path must be relative. */ 1287 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1288 1289 if (!SVN_IS_VALID_REVNUM(peg_revision)) 1290 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1291 _("Invalid peg revision %ld"), peg_revision); 1292 if (!SVN_IS_VALID_REVNUM(end_revision)) 1293 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1294 _("Invalid end revision %ld"), end_revision); 1295 if (end_revision <= peg_revision) 1296 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1297 _("Peg revision must precede end revision")); 1298 err = session->vtable->get_deleted_rev(session, path, 1299 peg_revision, 1300 end_revision, 1301 revision_deleted, 1302 pool); 1303 if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) 1304 { 1305 svn_error_clear(err); 1306 1307 /* Do it the slow way, using get-logs, for older servers. */ 1308 err = svn_ra__get_deleted_rev_from_log(session, path, peg_revision, 1309 end_revision, revision_deleted, 1310 pool); 1311 } 1312 return err; 1313} 1314 1315svn_error_t * 1316svn_ra_get_inherited_props(svn_ra_session_t *session, 1317 apr_array_header_t **iprops, 1318 const char *path, 1319 svn_revnum_t revision, 1320 apr_pool_t *result_pool, 1321 apr_pool_t *scratch_pool) 1322{ 1323 svn_boolean_t iprop_capable; 1324 1325 /* Path must be relative. */ 1326 SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1327 1328 SVN_ERR(svn_ra_has_capability(session, &iprop_capable, 1329 SVN_RA_CAPABILITY_INHERITED_PROPS, 1330 scratch_pool)); 1331 1332 if (iprop_capable) 1333 { 1334 SVN_ERR(session->vtable->get_inherited_props(session, iprops, path, 1335 revision, result_pool, 1336 scratch_pool)); 1337 } 1338 else 1339 { 1340 /* Fallback for legacy servers. */ 1341 SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops, 1342 result_pool, scratch_pool)); 1343 } 1344 1345 return SVN_NO_ERROR; 1346} 1347 1348svn_error_t * 1349svn_ra__get_commit_ev2(svn_editor_t **editor, 1350 svn_ra_session_t *session, 1351 apr_hash_t *revprop_table, 1352 svn_commit_callback2_t commit_callback, 1353 void *commit_baton, 1354 apr_hash_t *lock_tokens, 1355 svn_boolean_t keep_locks, 1356 svn_ra__provide_base_cb_t provide_base_cb, 1357 svn_ra__provide_props_cb_t provide_props_cb, 1358 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1359 void *cb_baton, 1360 apr_pool_t *result_pool, 1361 apr_pool_t *scratch_pool) 1362{ 1363 if (session->vtable->get_commit_ev2 == NULL) 1364 { 1365 /* The specific RA layer does not have an implementation. Use our 1366 default shim over the normal commit editor. */ 1367 1368 /* Remap for RA layers exposing Ev1. */ 1369 remap_commit_callback(&commit_callback, &commit_baton, 1370 session, commit_callback, commit_baton, 1371 result_pool); 1372 1373 return svn_error_trace(svn_ra__use_commit_shim( 1374 editor, 1375 session, 1376 revprop_table, 1377 commit_callback, commit_baton, 1378 lock_tokens, 1379 keep_locks, 1380 provide_base_cb, 1381 provide_props_cb, 1382 get_copysrc_kind_cb, 1383 cb_baton, 1384 session->cancel_func, session->cancel_baton, 1385 result_pool, scratch_pool)); 1386 } 1387 1388 /* Note: no need to remap the callback for Ev2. RA layers providing this 1389 vtable entry should completely fill in commit_info. */ 1390 1391 return svn_error_trace(session->vtable->get_commit_ev2( 1392 editor, 1393 session, 1394 revprop_table, 1395 commit_callback, commit_baton, 1396 lock_tokens, 1397 keep_locks, 1398 provide_base_cb, 1399 provide_props_cb, 1400 get_copysrc_kind_cb, 1401 cb_baton, 1402 session->cancel_func, session->cancel_baton, 1403 result_pool, scratch_pool)); 1404} 1405 1406 1407svn_error_t * 1408svn_ra_print_modules(svn_stringbuf_t *output, 1409 apr_pool_t *pool) 1410{ 1411 const struct ra_lib_defn *defn; 1412 const char * const *schemes; 1413 svn_ra__init_func_t initfunc; 1414 const svn_ra__vtable_t *vtable; 1415 apr_pool_t *iterpool = svn_pool_create(pool); 1416 1417 for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 1418 { 1419 char *line; 1420 1421 svn_pool_clear(iterpool); 1422 1423 initfunc = defn->initfunc; 1424 if (! initfunc) 1425 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, 1426 iterpool)); 1427 1428 if (initfunc) 1429 { 1430 SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool)); 1431 1432 SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name)); 1433 1434 /* Note: if you change the formatting of the description, 1435 bear in mind that ra_svn's description has multiple lines when 1436 built with SASL. */ 1437 line = apr_psprintf(iterpool, "* ra_%s : %s\n", 1438 defn->ra_name, 1439 vtable->get_description()); 1440 svn_stringbuf_appendcstr(output, line); 1441 1442 for (schemes = vtable->get_schemes(iterpool); *schemes != NULL; 1443 ++schemes) 1444 { 1445 line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"), 1446 *schemes); 1447 svn_stringbuf_appendcstr(output, line); 1448 } 1449 } 1450 } 1451 1452 svn_pool_destroy(iterpool); 1453 1454 return SVN_NO_ERROR; 1455} 1456 1457 1458svn_error_t * 1459svn_ra_print_ra_libraries(svn_stringbuf_t **descriptions, 1460 void *ra_baton, 1461 apr_pool_t *pool) 1462{ 1463 *descriptions = svn_stringbuf_create_empty(pool); 1464 return svn_ra_print_modules(*descriptions, pool); 1465} 1466 1467 1468svn_error_t * 1469svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session, 1470 svn_delta_shim_callbacks_t *callbacks) 1471{ 1472 SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks)); 1473 return SVN_NO_ERROR; 1474} 1475 1476 1477/* Return the library version number. */ 1478const svn_version_t * 1479svn_ra_version(void) 1480{ 1481 SVN_VERSION_BODY; 1482} 1483 1484 1485/*** Compatibility Interfaces **/ 1486svn_error_t * 1487svn_ra_init_ra_libs(void **ra_baton, 1488 apr_pool_t *pool) 1489{ 1490 *ra_baton = pool; 1491 return SVN_NO_ERROR; 1492} 1493 1494svn_error_t * 1495svn_ra_get_ra_library(svn_ra_plugin_t **library, 1496 void *ra_baton, 1497 const char *url, 1498 apr_pool_t *pool) 1499{ 1500 const struct ra_lib_defn *defn; 1501 apr_pool_t *load_pool = ra_baton; 1502 apr_hash_t *ht = apr_hash_make(pool); 1503 1504 /* Figure out which RA library key matches URL. */ 1505 for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 1506 { 1507 const char *scheme; 1508 if ((scheme = has_scheme_of(defn->schemes, url))) 1509 { 1510 svn_ra_init_func_t compat_initfunc = defn->compat_initfunc; 1511 1512 if (! compat_initfunc) 1513 { 1514 SVN_ERR(load_ra_module 1515 (NULL, &compat_initfunc, defn->ra_name, load_pool)); 1516 } 1517 if (! compat_initfunc) 1518 { 1519 continue; 1520 } 1521 1522 SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht)); 1523 1524 *library = svn_hash_gets(ht, scheme); 1525 1526 /* The library may support just a subset of the schemes listed, 1527 so we have to check here too. */ 1528 if (! *library) 1529 break; 1530 1531 return check_ra_version((*library)->get_version(), scheme); 1532 } 1533 } 1534 1535 /* Couldn't find a match... */ 1536 *library = NULL; 1537 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 1538 _("Unrecognized URL scheme '%s'"), url); 1539} 1540 1541/* For each libsvn_ra_foo library that is not linked in, provide a default 1542 implementation for svn_ra_foo_init which returns a "not implemented" 1543 error. */ 1544 1545#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_NEON 1546svn_error_t * 1547svn_ra_dav_init(int abi_version, 1548 apr_pool_t *pool, 1549 apr_hash_t *hash) 1550{ 1551 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1552} 1553#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_NEON */ 1554 1555#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SVN 1556svn_error_t * 1557svn_ra_svn_init(int abi_version, 1558 apr_pool_t *pool, 1559 apr_hash_t *hash) 1560{ 1561 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1562} 1563#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SVN */ 1564 1565#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 1566svn_error_t * 1567svn_ra_local_init(int abi_version, 1568 apr_pool_t *pool, 1569 apr_hash_t *hash) 1570{ 1571 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1572} 1573#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL */ 1574 1575#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SERF 1576svn_error_t * 1577svn_ra_serf_init(int abi_version, 1578 apr_pool_t *pool, 1579 apr_hash_t *hash) 1580{ 1581 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1582} 1583#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SERF */ 1584