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