1251881Speter/* 2251881Speter * ra_loader.c: logic for loading different RA library implementations 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter/* ==================================================================== */ 25251881Speter 26251881Speter/*** Includes. ***/ 27251881Speter#define APR_WANT_STRFUNC 28251881Speter#include <apr_want.h> 29251881Speter 30251881Speter#include <apr.h> 31251881Speter#include <apr_strings.h> 32251881Speter#include <apr_pools.h> 33251881Speter#include <apr_hash.h> 34251881Speter#include <apr_uri.h> 35251881Speter 36251881Speter#include "svn_hash.h" 37251881Speter#include "svn_version.h" 38251881Speter#include "svn_types.h" 39251881Speter#include "svn_error.h" 40251881Speter#include "svn_error_codes.h" 41251881Speter#include "svn_pools.h" 42251881Speter#include "svn_delta.h" 43251881Speter#include "svn_ra.h" 44251881Speter#include "svn_xml.h" 45251881Speter#include "svn_path.h" 46251881Speter#include "svn_dso.h" 47251881Speter#include "svn_props.h" 48251881Speter#include "svn_sorts.h" 49251881Speter 50251881Speter#include "svn_config.h" 51251881Speter#include "ra_loader.h" 52251881Speter#include "deprecated.h" 53251881Speter 54251881Speter#include "private/svn_ra_private.h" 55251881Speter#include "svn_private_config.h" 56251881Speter 57251881Speter 58251881Speter 59251881Speter 60251881Speter/* These are the URI schemes that the respective libraries *may* support. 61251881Speter * The schemes actually supported may be a subset of the schemes listed below. 62251881Speter * This can't be determine until the library is loaded. 63251881Speter * (Currently, this applies to the https scheme, which is only 64251881Speter * available if SSL is supported.) */ 65251881Speterstatic const char * const dav_schemes[] = { "http", "https", NULL }; 66251881Speterstatic const char * const svn_schemes[] = { "svn", NULL }; 67251881Speterstatic const char * const local_schemes[] = { "file", NULL }; 68251881Speter 69251881Speterstatic const struct ra_lib_defn { 70251881Speter /* the name of this RA library (e.g. "neon" or "local") */ 71251881Speter const char *ra_name; 72251881Speter 73251881Speter const char * const *schemes; 74251881Speter /* the initialization function if linked in; otherwise, NULL */ 75251881Speter svn_ra__init_func_t initfunc; 76251881Speter svn_ra_init_func_t compat_initfunc; 77251881Speter} ra_libraries[] = { 78251881Speter { 79251881Speter "svn", 80251881Speter svn_schemes, 81251881Speter#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SVN 82251881Speter svn_ra_svn__init, 83251881Speter svn_ra_svn__deprecated_init 84251881Speter#endif 85251881Speter }, 86251881Speter 87251881Speter { 88251881Speter "local", 89251881Speter local_schemes, 90251881Speter#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 91251881Speter svn_ra_local__init, 92251881Speter svn_ra_local__deprecated_init 93251881Speter#endif 94251881Speter }, 95251881Speter 96251881Speter { 97251881Speter "serf", 98251881Speter dav_schemes, 99251881Speter#ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SERF 100251881Speter svn_ra_serf__init, 101251881Speter svn_ra_serf__deprecated_init 102251881Speter#endif 103251881Speter }, 104251881Speter 105251881Speter /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */ 106251881Speter 107251881Speter /* sentinel */ 108251881Speter { NULL } 109251881Speter}; 110251881Speter 111251881Speter/* Ensure that the RA library NAME is loaded. 112251881Speter * 113251881Speter * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init 114251881Speter * function of the library. 115251881Speter * 116251881Speter * If COMPAT_FUNC is non-NULL, set *COMPAT_FUNC to the address of the 117251881Speter * svn_ra_NAME_init compatibility init function of the library. 118251881Speter * 119251881Speter * ### todo: Any RA libraries implemented from this point forward 120251881Speter * ### don't really need an svn_ra_NAME_init compatibility function. 121251881Speter * ### Currently, load_ra_module() will error if no such function is 122251881Speter * ### found, but it might be more friendly to simply set *COMPAT_FUNC 123251881Speter * ### to null (assuming COMPAT_FUNC itself is non-null). 124251881Speter */ 125251881Speterstatic svn_error_t * 126251881Speterload_ra_module(svn_ra__init_func_t *func, 127251881Speter svn_ra_init_func_t *compat_func, 128251881Speter const char *ra_name, apr_pool_t *pool) 129251881Speter{ 130251881Speter if (func) 131251881Speter *func = NULL; 132251881Speter if (compat_func) 133251881Speter *compat_func = NULL; 134251881Speter 135251881Speter#if defined(SVN_USE_DSO) && APR_HAS_DSO 136251881Speter { 137251881Speter apr_dso_handle_t *dso; 138251881Speter apr_dso_handle_sym_t symbol; 139251881Speter const char *libname; 140251881Speter const char *funcname; 141251881Speter const char *compat_funcname; 142251881Speter apr_status_t status; 143251881Speter 144251881Speter libname = apr_psprintf(pool, "libsvn_ra_%s-%d.so.%d", 145251881Speter ra_name, SVN_VER_MAJOR, SVN_SOVERSION); 146251881Speter funcname = apr_psprintf(pool, "svn_ra_%s__init", ra_name); 147251881Speter compat_funcname = apr_psprintf(pool, "svn_ra_%s_init", ra_name); 148251881Speter 149251881Speter /* find/load the specified library */ 150251881Speter SVN_ERR(svn_dso_load(&dso, libname)); 151251881Speter if (! dso) 152251881Speter return SVN_NO_ERROR; 153251881Speter 154251881Speter /* find the initialization routines */ 155251881Speter if (func) 156251881Speter { 157251881Speter status = apr_dso_sym(&symbol, dso, funcname); 158251881Speter if (status) 159251881Speter { 160251881Speter return svn_error_wrap_apr(status, 161251881Speter _("'%s' does not define '%s()'"), 162251881Speter libname, funcname); 163251881Speter } 164251881Speter 165251881Speter *func = (svn_ra__init_func_t) symbol; 166251881Speter } 167251881Speter 168251881Speter if (compat_func) 169251881Speter { 170251881Speter status = apr_dso_sym(&symbol, dso, compat_funcname); 171251881Speter if (status) 172251881Speter { 173251881Speter return svn_error_wrap_apr(status, 174251881Speter _("'%s' does not define '%s()'"), 175251881Speter libname, compat_funcname); 176251881Speter } 177251881Speter 178251881Speter *compat_func = (svn_ra_init_func_t) symbol; 179251881Speter } 180251881Speter } 181251881Speter#endif /* APR_HAS_DSO */ 182251881Speter 183251881Speter return SVN_NO_ERROR; 184251881Speter} 185251881Speter 186251881Speter/* If SCHEMES contains URL, return the scheme. Else, return NULL. */ 187251881Speterstatic const char * 188251881Speterhas_scheme_of(const char * const *schemes, const char *url) 189251881Speter{ 190251881Speter apr_size_t len; 191251881Speter 192251881Speter for ( ; *schemes != NULL; ++schemes) 193251881Speter { 194251881Speter const char *scheme = *schemes; 195251881Speter len = strlen(scheme); 196251881Speter /* Case-insensitive comparison, per RFC 2396 section 3.1. Allow 197251881Speter URL to contain a trailing "+foo" section in the scheme, since 198251881Speter that's how we specify tunnel schemes in ra_svn. */ 199251881Speter if (strncasecmp(scheme, url, len) == 0 && 200251881Speter (url[len] == ':' || url[len] == '+')) 201251881Speter return scheme; 202251881Speter } 203251881Speter 204251881Speter return NULL; 205251881Speter} 206251881Speter 207251881Speter/* Return an error if RA_VERSION doesn't match the version of this library. 208251881Speter Use SCHEME in the error message to describe the library that was loaded. */ 209251881Speterstatic svn_error_t * 210251881Spetercheck_ra_version(const svn_version_t *ra_version, const char *scheme) 211251881Speter{ 212251881Speter const svn_version_t *my_version = svn_ra_version(); 213251881Speter if (!svn_ver_equal(my_version, ra_version)) 214251881Speter return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL, 215251881Speter _("Mismatched RA version for '%s':" 216251881Speter " found %d.%d.%d%s," 217251881Speter " expected %d.%d.%d%s"), 218251881Speter scheme, 219251881Speter my_version->major, my_version->minor, 220251881Speter my_version->patch, my_version->tag, 221251881Speter ra_version->major, ra_version->minor, 222251881Speter ra_version->patch, ra_version->tag); 223251881Speter 224251881Speter return SVN_NO_ERROR; 225251881Speter} 226251881Speter 227251881Speter/* -------------------------------------------------------------- */ 228251881Speter 229251881Speter/*** Public Interfaces ***/ 230251881Speter 231251881Spetersvn_error_t *svn_ra_initialize(apr_pool_t *pool) 232251881Speter{ 233251881Speter return SVN_NO_ERROR; 234251881Speter} 235251881Speter 236251881Speter/* Please note: the implementation of svn_ra_create_callbacks is 237251881Speter * duplicated in libsvn_ra/wrapper_template.h:compat_open() . This 238251881Speter * duplication is intentional, is there to avoid a circular 239251881Speter * dependancy, and is justified in great length in the code of 240251881Speter * compat_open() in libsvn_ra/wrapper_template.h. If you modify the 241251881Speter * implementation of svn_ra_create_callbacks(), be sure to keep the 242251881Speter * code in wrapper_template.h:compat_open() in sync with your 243251881Speter * changes. */ 244251881Spetersvn_error_t * 245251881Spetersvn_ra_create_callbacks(svn_ra_callbacks2_t **callbacks, 246251881Speter apr_pool_t *pool) 247251881Speter{ 248251881Speter *callbacks = apr_pcalloc(pool, sizeof(**callbacks)); 249251881Speter return SVN_NO_ERROR; 250251881Speter} 251251881Speter 252251881Spetersvn_error_t *svn_ra_open4(svn_ra_session_t **session_p, 253251881Speter const char **corrected_url_p, 254251881Speter const char *repos_URL, 255251881Speter const char *uuid, 256251881Speter const svn_ra_callbacks2_t *callbacks, 257251881Speter void *callback_baton, 258251881Speter apr_hash_t *config, 259251881Speter apr_pool_t *pool) 260251881Speter{ 261251881Speter apr_pool_t *sesspool = svn_pool_create(pool); 262251881Speter svn_ra_session_t *session; 263251881Speter const struct ra_lib_defn *defn; 264251881Speter const svn_ra__vtable_t *vtable = NULL; 265251881Speter svn_config_t *servers = NULL; 266251881Speter const char *server_group; 267251881Speter apr_uri_t repos_URI; 268251881Speter apr_status_t apr_err; 269251881Speter#ifdef CHOOSABLE_DAV_MODULE 270251881Speter const char *http_library = DEFAULT_HTTP_LIBRARY; 271251881Speter#endif 272251881Speter /* Auth caching parameters. */ 273251881Speter svn_boolean_t store_passwords = SVN_CONFIG_DEFAULT_OPTION_STORE_PASSWORDS; 274251881Speter svn_boolean_t store_auth_creds = SVN_CONFIG_DEFAULT_OPTION_STORE_AUTH_CREDS; 275251881Speter const char *store_plaintext_passwords 276251881Speter = SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS; 277251881Speter svn_boolean_t store_pp = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP; 278251881Speter const char *store_pp_plaintext 279251881Speter = SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT; 280251881Speter const char *corrected_url; 281251881Speter 282251881Speter /* Initialize the return variable. */ 283251881Speter *session_p = NULL; 284251881Speter 285251881Speter apr_err = apr_uri_parse(sesspool, repos_URL, &repos_URI); 286251881Speter /* ### Should apr_uri_parse leave hostname NULL? It doesn't 287251881Speter * for "file:///" URLs, only for bogus URLs like "bogus". 288251881Speter * If this is the right behavior for apr_uri_parse, maybe we 289251881Speter * should have a svn_uri_parse wrapper. */ 290251881Speter if (apr_err != APR_SUCCESS || repos_URI.hostname == NULL) 291251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 292251881Speter _("Illegal repository URL '%s'"), 293251881Speter repos_URL); 294251881Speter 295251881Speter if (callbacks->auth_baton) 296251881Speter { 297251881Speter /* The 'store-passwords' and 'store-auth-creds' parameters used to 298251881Speter * live in SVN_CONFIG_CATEGORY_CONFIG. For backward compatibility, 299251881Speter * if values for these parameters have already been set by our 300251881Speter * callers, we use those values as defaults. 301251881Speter * 302251881Speter * Note that we can only catch the case where users explicitly set 303251881Speter * "store-passwords = no" or 'store-auth-creds = no". 304251881Speter * 305251881Speter * However, since the default value for both these options is 306251881Speter * currently (and has always been) "yes", users won't know 307251881Speter * the difference if they set "store-passwords = yes" or 308251881Speter * "store-auth-creds = yes" -- they'll get the expected behaviour. 309251881Speter */ 310251881Speter 311251881Speter if (svn_auth_get_parameter(callbacks->auth_baton, 312251881Speter SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL) 313251881Speter store_passwords = FALSE; 314251881Speter 315251881Speter if (svn_auth_get_parameter(callbacks->auth_baton, 316251881Speter SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL) 317251881Speter store_auth_creds = FALSE; 318251881Speter } 319251881Speter 320251881Speter if (config) 321251881Speter { 322251881Speter /* Grab the 'servers' config. */ 323251881Speter servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS); 324251881Speter if (servers) 325251881Speter { 326251881Speter /* First, look in the global section. */ 327251881Speter 328251881Speter SVN_ERR(svn_config_get_bool 329251881Speter (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL, 330251881Speter SVN_CONFIG_OPTION_STORE_PASSWORDS, 331251881Speter store_passwords)); 332251881Speter 333251881Speter SVN_ERR(svn_config_get_yes_no_ask 334251881Speter (servers, &store_plaintext_passwords, SVN_CONFIG_SECTION_GLOBAL, 335251881Speter SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, 336251881Speter SVN_CONFIG_DEFAULT_OPTION_STORE_PLAINTEXT_PASSWORDS)); 337251881Speter 338251881Speter SVN_ERR(svn_config_get_bool 339251881Speter (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL, 340251881Speter SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, 341251881Speter store_pp)); 342251881Speter 343251881Speter SVN_ERR(svn_config_get_yes_no_ask 344251881Speter (servers, &store_pp_plaintext, 345251881Speter SVN_CONFIG_SECTION_GLOBAL, 346251881Speter SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 347251881Speter SVN_CONFIG_DEFAULT_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT)); 348251881Speter 349251881Speter SVN_ERR(svn_config_get_bool 350251881Speter (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL, 351251881Speter SVN_CONFIG_OPTION_STORE_AUTH_CREDS, 352251881Speter store_auth_creds)); 353251881Speter 354251881Speter /* Find out where we're about to connect to, and 355251881Speter * try to pick a server group based on the destination. */ 356251881Speter server_group = svn_config_find_group(servers, repos_URI.hostname, 357251881Speter SVN_CONFIG_SECTION_GROUPS, 358251881Speter sesspool); 359251881Speter 360251881Speter if (server_group) 361251881Speter { 362251881Speter /* Override global auth caching parameters with the ones 363251881Speter * for the server group, if any. */ 364251881Speter SVN_ERR(svn_config_get_bool(servers, &store_auth_creds, 365251881Speter server_group, 366251881Speter SVN_CONFIG_OPTION_STORE_AUTH_CREDS, 367251881Speter store_auth_creds)); 368251881Speter 369251881Speter SVN_ERR(svn_config_get_bool(servers, &store_passwords, 370251881Speter server_group, 371251881Speter SVN_CONFIG_OPTION_STORE_PASSWORDS, 372251881Speter store_passwords)); 373251881Speter 374251881Speter SVN_ERR(svn_config_get_yes_no_ask 375251881Speter (servers, &store_plaintext_passwords, server_group, 376251881Speter SVN_CONFIG_OPTION_STORE_PLAINTEXT_PASSWORDS, 377251881Speter store_plaintext_passwords)); 378251881Speter 379251881Speter SVN_ERR(svn_config_get_bool 380251881Speter (servers, &store_pp, 381251881Speter server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP, 382251881Speter store_pp)); 383251881Speter 384251881Speter SVN_ERR(svn_config_get_yes_no_ask 385251881Speter (servers, &store_pp_plaintext, server_group, 386251881Speter SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 387251881Speter store_pp_plaintext)); 388251881Speter } 389251881Speter#ifdef CHOOSABLE_DAV_MODULE 390251881Speter /* Now, which DAV-based RA method do we want to use today? */ 391251881Speter http_library 392251881Speter = svn_config_get_server_setting(servers, 393251881Speter server_group, /* NULL is OK */ 394251881Speter SVN_CONFIG_OPTION_HTTP_LIBRARY, 395251881Speter DEFAULT_HTTP_LIBRARY); 396251881Speter 397251881Speter if (strcmp(http_library, "serf") != 0) 398251881Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 399251881Speter _("Invalid config: unknown HTTP library " 400251881Speter "'%s'"), 401251881Speter http_library); 402251881Speter#endif 403251881Speter } 404251881Speter } 405251881Speter 406251881Speter if (callbacks->auth_baton) 407251881Speter { 408251881Speter /* Save auth caching parameters in the auth parameter hash. */ 409251881Speter if (! store_passwords) 410251881Speter svn_auth_set_parameter(callbacks->auth_baton, 411251881Speter SVN_AUTH_PARAM_DONT_STORE_PASSWORDS, ""); 412251881Speter 413251881Speter svn_auth_set_parameter(callbacks->auth_baton, 414251881Speter SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS, 415251881Speter store_plaintext_passwords); 416251881Speter 417251881Speter if (! store_pp) 418251881Speter svn_auth_set_parameter(callbacks->auth_baton, 419251881Speter SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP, 420251881Speter ""); 421251881Speter 422251881Speter svn_auth_set_parameter(callbacks->auth_baton, 423251881Speter SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT, 424251881Speter store_pp_plaintext); 425251881Speter 426251881Speter if (! store_auth_creds) 427251881Speter svn_auth_set_parameter(callbacks->auth_baton, 428251881Speter SVN_AUTH_PARAM_NO_AUTH_CACHE, ""); 429251881Speter } 430251881Speter 431251881Speter /* Find the library. */ 432251881Speter for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 433251881Speter { 434251881Speter const char *scheme; 435251881Speter 436251881Speter if ((scheme = has_scheme_of(defn->schemes, repos_URL))) 437251881Speter { 438251881Speter svn_ra__init_func_t initfunc = defn->initfunc; 439251881Speter 440251881Speter#ifdef CHOOSABLE_DAV_MODULE 441251881Speter if (defn->schemes == dav_schemes 442251881Speter && strcmp(defn->ra_name, http_library) != 0) 443251881Speter continue; 444251881Speter#endif 445251881Speter 446251881Speter if (! initfunc) 447251881Speter SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, 448251881Speter sesspool)); 449251881Speter if (! initfunc) 450251881Speter /* Library not found. */ 451251881Speter continue; 452251881Speter 453251881Speter SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool)); 454251881Speter 455251881Speter SVN_ERR(check_ra_version(vtable->get_version(), scheme)); 456251881Speter 457251881Speter if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL)) 458251881Speter /* Library doesn't support the scheme at runtime. */ 459251881Speter continue; 460251881Speter 461251881Speter 462251881Speter break; 463251881Speter } 464251881Speter } 465251881Speter 466251881Speter if (vtable == NULL) 467251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 468251881Speter _("Unrecognized URL scheme for '%s'"), 469251881Speter repos_URL); 470251881Speter 471251881Speter /* Create the session object. */ 472251881Speter session = apr_pcalloc(sesspool, sizeof(*session)); 473251881Speter session->cancel_func = callbacks->cancel_func; 474251881Speter session->cancel_baton = callback_baton; 475251881Speter session->vtable = vtable; 476251881Speter session->pool = sesspool; 477251881Speter 478251881Speter /* Ask the library to open the session. */ 479251881Speter SVN_ERR_W(vtable->open_session(session, &corrected_url, repos_URL, 480251881Speter callbacks, callback_baton, config, sesspool), 481251881Speter apr_psprintf(pool, "Unable to connect to a repository at URL '%s'", 482251881Speter repos_URL)); 483251881Speter 484251881Speter /* If the session open stuff detected a server-provided URL 485251881Speter correction (a 301 or 302 redirect response during the initial 486251881Speter OPTIONS request), then kill the session so the caller can decide 487251881Speter what to do. */ 488251881Speter if (corrected_url_p && corrected_url) 489251881Speter { 490251881Speter if (! svn_path_is_url(corrected_url)) 491251881Speter { 492251881Speter /* RFC1945 and RFC2616 state that the Location header's 493251881Speter value (from whence this CORRECTED_URL ultimately comes), 494251881Speter if present, must be an absolute URI. But some Apache 495251881Speter versions (those older than 2.2.11, it seems) transmit 496251881Speter only the path portion of the URI. See issue #3775 for 497251881Speter details. */ 498251881Speter apr_uri_t corrected_URI = repos_URI; 499251881Speter corrected_URI.path = (char *)corrected_url; 500251881Speter corrected_url = apr_uri_unparse(pool, &corrected_URI, 0); 501251881Speter } 502251881Speter *corrected_url_p = svn_uri_canonicalize(corrected_url, pool); 503251881Speter svn_pool_destroy(sesspool); 504251881Speter return SVN_NO_ERROR; 505251881Speter } 506251881Speter 507251881Speter /* Check the UUID. */ 508251881Speter if (uuid) 509251881Speter { 510251881Speter const char *repository_uuid; 511251881Speter 512251881Speter SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool)); 513251881Speter if (strcmp(uuid, repository_uuid) != 0) 514251881Speter { 515251881Speter /* Duplicate the uuid as it is allocated in sesspool */ 516251881Speter repository_uuid = apr_pstrdup(pool, repository_uuid); 517251881Speter svn_pool_destroy(sesspool); 518251881Speter return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL, 519251881Speter _("Repository UUID '%s' doesn't match " 520251881Speter "expected UUID '%s'"), 521251881Speter repository_uuid, uuid); 522251881Speter } 523251881Speter } 524251881Speter 525251881Speter *session_p = session; 526251881Speter return SVN_NO_ERROR; 527251881Speter} 528251881Speter 529251881Spetersvn_error_t *svn_ra_reparent(svn_ra_session_t *session, 530251881Speter const char *url, 531251881Speter apr_pool_t *pool) 532251881Speter{ 533251881Speter const char *repos_root; 534251881Speter 535251881Speter /* Make sure the new URL is in the same repository, so that the 536251881Speter implementations don't have to do it. */ 537251881Speter SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, pool)); 538251881Speter if (! svn_uri__is_ancestor(repos_root, url)) 539251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 540251881Speter _("'%s' isn't in the same repository as '%s'"), 541251881Speter url, repos_root); 542251881Speter 543251881Speter return session->vtable->reparent(session, url, pool); 544251881Speter} 545251881Speter 546251881Spetersvn_error_t *svn_ra_get_session_url(svn_ra_session_t *session, 547251881Speter const char **url, 548251881Speter apr_pool_t *pool) 549251881Speter{ 550251881Speter return session->vtable->get_session_url(session, url, pool); 551251881Speter} 552251881Speter 553251881Spetersvn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session, 554251881Speter const char **rel_path, 555251881Speter const char *url, 556251881Speter apr_pool_t *pool) 557251881Speter{ 558251881Speter const char *sess_url; 559251881Speter 560251881Speter SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool)); 561251881Speter *rel_path = svn_uri_skip_ancestor(sess_url, url, pool); 562251881Speter if (! *rel_path) 563251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 564251881Speter _("'%s' isn't a child of session URL '%s'"), 565251881Speter url, sess_url); 566251881Speter return SVN_NO_ERROR; 567251881Speter} 568251881Speter 569251881Spetersvn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session, 570251881Speter const char **rel_path, 571251881Speter const char *url, 572251881Speter apr_pool_t *pool) 573251881Speter{ 574251881Speter const char *root_url; 575251881Speter 576251881Speter SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool)); 577251881Speter *rel_path = svn_uri_skip_ancestor(root_url, url, pool); 578251881Speter if (! *rel_path) 579251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 580251881Speter _("'%s' isn't a child of repository root " 581251881Speter "URL '%s'"), 582251881Speter url, root_url); 583251881Speter return SVN_NO_ERROR; 584251881Speter} 585251881Speter 586251881Spetersvn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session, 587251881Speter svn_revnum_t *latest_revnum, 588251881Speter apr_pool_t *pool) 589251881Speter{ 590251881Speter return session->vtable->get_latest_revnum(session, latest_revnum, pool); 591251881Speter} 592251881Speter 593251881Spetersvn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session, 594251881Speter svn_revnum_t *revision, 595251881Speter apr_time_t tm, 596251881Speter apr_pool_t *pool) 597251881Speter{ 598251881Speter return session->vtable->get_dated_revision(session, revision, tm, pool); 599251881Speter} 600251881Speter 601251881Spetersvn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session, 602251881Speter svn_revnum_t rev, 603251881Speter const char *name, 604251881Speter const svn_string_t *const *old_value_p, 605251881Speter const svn_string_t *value, 606251881Speter apr_pool_t *pool) 607251881Speter{ 608251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 609251881Speter 610251881Speter /* If an old value was specified, make sure the server supports 611251881Speter * specifying it. */ 612251881Speter if (old_value_p) 613251881Speter { 614251881Speter svn_boolean_t has_atomic_revprops; 615251881Speter 616251881Speter SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops, 617251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS, 618251881Speter pool)); 619251881Speter 620251881Speter if (!has_atomic_revprops) 621251881Speter /* API violation. (Should be an ASSERT, but gstein talked me 622251881Speter * out of it.) */ 623251881Speter return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 624251881Speter _("Specifying 'old_value_p' is not allowed when " 625251881Speter "the '%s' capability is not advertised, and " 626251881Speter "could indicate a bug in your client"), 627251881Speter SVN_RA_CAPABILITY_ATOMIC_REVPROPS); 628251881Speter } 629251881Speter 630251881Speter return session->vtable->change_rev_prop(session, rev, name, 631251881Speter old_value_p, value, pool); 632251881Speter} 633251881Speter 634251881Spetersvn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session, 635251881Speter svn_revnum_t rev, 636251881Speter apr_hash_t **props, 637251881Speter apr_pool_t *pool) 638251881Speter{ 639251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 640251881Speter return session->vtable->rev_proplist(session, rev, props, pool); 641251881Speter} 642251881Speter 643251881Spetersvn_error_t *svn_ra_rev_prop(svn_ra_session_t *session, 644251881Speter svn_revnum_t rev, 645251881Speter const char *name, 646251881Speter svn_string_t **value, 647251881Speter apr_pool_t *pool) 648251881Speter{ 649251881Speter SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev)); 650251881Speter return session->vtable->rev_prop(session, rev, name, value, pool); 651251881Speter} 652251881Speter 653251881Speterstruct ccw_baton 654251881Speter{ 655251881Speter svn_commit_callback2_t original_callback; 656251881Speter void *original_baton; 657251881Speter 658251881Speter svn_ra_session_t *session; 659251881Speter}; 660251881Speter 661251881Speter/* Wrapper which populates the repos_root field of the commit_info struct */ 662251881Speterstatic svn_error_t * 663251881Spetercommit_callback_wrapper(const svn_commit_info_t *commit_info, 664251881Speter void *baton, 665251881Speter apr_pool_t *pool) 666251881Speter{ 667251881Speter struct ccw_baton *ccwb = baton; 668251881Speter svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool); 669251881Speter 670251881Speter SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool)); 671251881Speter 672251881Speter return ccwb->original_callback(ci, ccwb->original_baton, pool); 673251881Speter} 674251881Speter 675251881Speter 676251881Speter/* Some RA layers do not correctly fill in REPOS_ROOT in commit_info, or 677251881Speter they are third-party layers conforming to an older commit_info structure. 678251881Speter Interpose a utility function to ensure the field is valid. */ 679251881Speterstatic void 680251881Speterremap_commit_callback(svn_commit_callback2_t *callback, 681251881Speter void **callback_baton, 682251881Speter svn_ra_session_t *session, 683251881Speter svn_commit_callback2_t original_callback, 684251881Speter void *original_baton, 685251881Speter apr_pool_t *result_pool) 686251881Speter{ 687251881Speter if (original_callback == NULL) 688251881Speter { 689251881Speter *callback = NULL; 690251881Speter *callback_baton = NULL; 691251881Speter } 692251881Speter else 693251881Speter { 694251881Speter /* Allocate this in RESULT_POOL, since the callback will be called 695251881Speter long after this function has returned. */ 696251881Speter struct ccw_baton *ccwb = apr_palloc(result_pool, sizeof(*ccwb)); 697251881Speter 698251881Speter ccwb->session = session; 699251881Speter ccwb->original_callback = original_callback; 700251881Speter ccwb->original_baton = original_baton; 701251881Speter 702251881Speter *callback = commit_callback_wrapper; 703251881Speter *callback_baton = ccwb; 704251881Speter } 705251881Speter} 706251881Speter 707251881Speter 708251881Spetersvn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session, 709251881Speter const svn_delta_editor_t **editor, 710251881Speter void **edit_baton, 711251881Speter apr_hash_t *revprop_table, 712251881Speter svn_commit_callback2_t commit_callback, 713251881Speter void *commit_baton, 714251881Speter apr_hash_t *lock_tokens, 715251881Speter svn_boolean_t keep_locks, 716251881Speter apr_pool_t *pool) 717251881Speter{ 718251881Speter remap_commit_callback(&commit_callback, &commit_baton, 719251881Speter session, commit_callback, commit_baton, 720251881Speter pool); 721251881Speter 722251881Speter return session->vtable->get_commit_editor(session, editor, edit_baton, 723251881Speter revprop_table, 724251881Speter commit_callback, commit_baton, 725251881Speter lock_tokens, keep_locks, pool); 726251881Speter} 727251881Speter 728251881Spetersvn_error_t *svn_ra_get_file(svn_ra_session_t *session, 729251881Speter const char *path, 730251881Speter svn_revnum_t revision, 731251881Speter svn_stream_t *stream, 732251881Speter svn_revnum_t *fetched_rev, 733251881Speter apr_hash_t **props, 734251881Speter apr_pool_t *pool) 735251881Speter{ 736251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 737251881Speter return session->vtable->get_file(session, path, revision, stream, 738251881Speter fetched_rev, props, pool); 739251881Speter} 740251881Speter 741251881Spetersvn_error_t *svn_ra_get_dir2(svn_ra_session_t *session, 742251881Speter apr_hash_t **dirents, 743251881Speter svn_revnum_t *fetched_rev, 744251881Speter apr_hash_t **props, 745251881Speter const char *path, 746251881Speter svn_revnum_t revision, 747251881Speter apr_uint32_t dirent_fields, 748251881Speter apr_pool_t *pool) 749251881Speter{ 750251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 751251881Speter return session->vtable->get_dir(session, dirents, fetched_rev, props, 752251881Speter path, revision, dirent_fields, pool); 753251881Speter} 754251881Speter 755251881Spetersvn_error_t *svn_ra_get_mergeinfo(svn_ra_session_t *session, 756251881Speter svn_mergeinfo_catalog_t *catalog, 757251881Speter const apr_array_header_t *paths, 758251881Speter svn_revnum_t revision, 759251881Speter svn_mergeinfo_inheritance_t inherit, 760251881Speter svn_boolean_t include_descendants, 761251881Speter apr_pool_t *pool) 762251881Speter{ 763251881Speter svn_error_t *err; 764251881Speter int i; 765251881Speter 766251881Speter /* Validate path format. */ 767251881Speter for (i = 0; i < paths->nelts; i++) 768251881Speter { 769251881Speter const char *path = APR_ARRAY_IDX(paths, i, const char *); 770251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 771251881Speter } 772251881Speter 773251881Speter /* Check server Merge Tracking capability. */ 774251881Speter err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool); 775251881Speter if (err) 776251881Speter { 777251881Speter *catalog = NULL; 778251881Speter return err; 779251881Speter } 780251881Speter 781251881Speter return session->vtable->get_mergeinfo(session, catalog, paths, 782251881Speter revision, inherit, 783251881Speter include_descendants, pool); 784251881Speter} 785251881Speter 786251881Spetersvn_error_t * 787251881Spetersvn_ra_do_update3(svn_ra_session_t *session, 788251881Speter const svn_ra_reporter3_t **reporter, 789251881Speter void **report_baton, 790251881Speter svn_revnum_t revision_to_update_to, 791251881Speter const char *update_target, 792251881Speter svn_depth_t depth, 793251881Speter svn_boolean_t send_copyfrom_args, 794251881Speter svn_boolean_t ignore_ancestry, 795251881Speter const svn_delta_editor_t *update_editor, 796251881Speter void *update_baton, 797251881Speter apr_pool_t *result_pool, 798251881Speter apr_pool_t *scratch_pool) 799251881Speter{ 800251881Speter SVN_ERR_ASSERT(svn_path_is_empty(update_target) 801251881Speter || svn_path_is_single_path_component(update_target)); 802251881Speter return session->vtable->do_update(session, 803251881Speter reporter, report_baton, 804251881Speter revision_to_update_to, update_target, 805251881Speter depth, send_copyfrom_args, 806251881Speter ignore_ancestry, 807251881Speter update_editor, update_baton, 808251881Speter result_pool, scratch_pool); 809251881Speter} 810251881Speter 811251881Spetersvn_error_t * 812251881Spetersvn_ra_do_switch3(svn_ra_session_t *session, 813251881Speter const svn_ra_reporter3_t **reporter, 814251881Speter void **report_baton, 815251881Speter svn_revnum_t revision_to_switch_to, 816251881Speter const char *switch_target, 817251881Speter svn_depth_t depth, 818251881Speter const char *switch_url, 819251881Speter svn_boolean_t send_copyfrom_args, 820251881Speter svn_boolean_t ignore_ancestry, 821251881Speter const svn_delta_editor_t *switch_editor, 822251881Speter void *switch_baton, 823251881Speter apr_pool_t *result_pool, 824251881Speter apr_pool_t *scratch_pool) 825251881Speter{ 826251881Speter SVN_ERR_ASSERT(svn_path_is_empty(switch_target) 827251881Speter || svn_path_is_single_path_component(switch_target)); 828251881Speter return session->vtable->do_switch(session, 829251881Speter reporter, report_baton, 830251881Speter revision_to_switch_to, switch_target, 831251881Speter depth, switch_url, 832251881Speter send_copyfrom_args, 833251881Speter ignore_ancestry, 834251881Speter switch_editor, 835251881Speter switch_baton, 836251881Speter result_pool, scratch_pool); 837251881Speter} 838251881Speter 839251881Spetersvn_error_t *svn_ra_do_status2(svn_ra_session_t *session, 840251881Speter const svn_ra_reporter3_t **reporter, 841251881Speter void **report_baton, 842251881Speter const char *status_target, 843251881Speter svn_revnum_t revision, 844251881Speter svn_depth_t depth, 845251881Speter const svn_delta_editor_t *status_editor, 846251881Speter void *status_baton, 847251881Speter apr_pool_t *pool) 848251881Speter{ 849251881Speter SVN_ERR_ASSERT(svn_path_is_empty(status_target) 850251881Speter || svn_path_is_single_path_component(status_target)); 851251881Speter return session->vtable->do_status(session, 852251881Speter reporter, report_baton, 853251881Speter status_target, revision, depth, 854251881Speter status_editor, status_baton, pool); 855251881Speter} 856251881Speter 857251881Spetersvn_error_t *svn_ra_do_diff3(svn_ra_session_t *session, 858251881Speter const svn_ra_reporter3_t **reporter, 859251881Speter void **report_baton, 860251881Speter svn_revnum_t revision, 861251881Speter const char *diff_target, 862251881Speter svn_depth_t depth, 863251881Speter svn_boolean_t ignore_ancestry, 864251881Speter svn_boolean_t text_deltas, 865251881Speter const char *versus_url, 866251881Speter const svn_delta_editor_t *diff_editor, 867251881Speter void *diff_baton, 868251881Speter apr_pool_t *pool) 869251881Speter{ 870251881Speter SVN_ERR_ASSERT(svn_path_is_empty(diff_target) 871251881Speter || svn_path_is_single_path_component(diff_target)); 872251881Speter return session->vtable->do_diff(session, 873251881Speter reporter, report_baton, 874251881Speter revision, diff_target, 875251881Speter depth, ignore_ancestry, 876251881Speter text_deltas, versus_url, diff_editor, 877251881Speter diff_baton, pool); 878251881Speter} 879251881Speter 880251881Spetersvn_error_t *svn_ra_get_log2(svn_ra_session_t *session, 881251881Speter const apr_array_header_t *paths, 882251881Speter svn_revnum_t start, 883251881Speter svn_revnum_t end, 884251881Speter int limit, 885251881Speter svn_boolean_t discover_changed_paths, 886251881Speter svn_boolean_t strict_node_history, 887251881Speter svn_boolean_t include_merged_revisions, 888251881Speter const apr_array_header_t *revprops, 889251881Speter svn_log_entry_receiver_t receiver, 890251881Speter void *receiver_baton, 891251881Speter apr_pool_t *pool) 892251881Speter{ 893251881Speter if (paths) 894251881Speter { 895251881Speter int i; 896251881Speter for (i = 0; i < paths->nelts; i++) 897251881Speter { 898251881Speter const char *path = APR_ARRAY_IDX(paths, i, const char *); 899251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 900251881Speter } 901251881Speter } 902251881Speter 903251881Speter if (include_merged_revisions) 904251881Speter SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool)); 905251881Speter 906251881Speter return session->vtable->get_log(session, paths, start, end, limit, 907251881Speter discover_changed_paths, strict_node_history, 908251881Speter include_merged_revisions, revprops, 909251881Speter receiver, receiver_baton, pool); 910251881Speter} 911251881Speter 912251881Spetersvn_error_t *svn_ra_check_path(svn_ra_session_t *session, 913251881Speter const char *path, 914251881Speter svn_revnum_t revision, 915251881Speter svn_node_kind_t *kind, 916251881Speter apr_pool_t *pool) 917251881Speter{ 918251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 919251881Speter return session->vtable->check_path(session, path, revision, kind, pool); 920251881Speter} 921251881Speter 922251881Spetersvn_error_t *svn_ra_stat(svn_ra_session_t *session, 923251881Speter const char *path, 924251881Speter svn_revnum_t revision, 925251881Speter svn_dirent_t **dirent, 926251881Speter apr_pool_t *pool) 927251881Speter{ 928251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 929251881Speter return session->vtable->stat(session, path, revision, dirent, pool); 930251881Speter} 931251881Speter 932251881Spetersvn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session, 933251881Speter const char **uuid, 934251881Speter apr_pool_t *pool) 935251881Speter{ 936251881Speter SVN_ERR(session->vtable->get_uuid(session, uuid, pool)); 937251881Speter *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL; 938251881Speter return SVN_NO_ERROR; 939251881Speter} 940251881Speter 941251881Spetersvn_error_t *svn_ra_get_uuid(svn_ra_session_t *session, 942251881Speter const char **uuid, 943251881Speter apr_pool_t *pool) 944251881Speter{ 945251881Speter return session->vtable->get_uuid(session, uuid, pool); 946251881Speter} 947251881Speter 948251881Spetersvn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session, 949251881Speter const char **url, 950251881Speter apr_pool_t *pool) 951251881Speter{ 952251881Speter SVN_ERR(session->vtable->get_repos_root(session, url, pool)); 953251881Speter *url = *url ? apr_pstrdup(pool, *url) : NULL; 954251881Speter return SVN_NO_ERROR; 955251881Speter} 956251881Speter 957251881Spetersvn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session, 958251881Speter const char **url, 959251881Speter apr_pool_t *pool) 960251881Speter{ 961251881Speter return session->vtable->get_repos_root(session, url, pool); 962251881Speter} 963251881Speter 964251881Spetersvn_error_t *svn_ra_get_locations(svn_ra_session_t *session, 965251881Speter apr_hash_t **locations, 966251881Speter const char *path, 967251881Speter svn_revnum_t peg_revision, 968251881Speter const apr_array_header_t *location_revisions, 969251881Speter apr_pool_t *pool) 970251881Speter{ 971251881Speter svn_error_t *err; 972251881Speter 973251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 974251881Speter err = session->vtable->get_locations(session, locations, path, 975251881Speter peg_revision, location_revisions, pool); 976251881Speter if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 977251881Speter { 978251881Speter svn_error_clear(err); 979251881Speter 980251881Speter /* Do it the slow way, using get-logs, for older servers. */ 981251881Speter err = svn_ra__locations_from_log(session, locations, path, 982251881Speter peg_revision, location_revisions, 983251881Speter pool); 984251881Speter } 985251881Speter return err; 986251881Speter} 987251881Speter 988251881Spetersvn_error_t * 989251881Spetersvn_ra_get_location_segments(svn_ra_session_t *session, 990251881Speter const char *path, 991251881Speter svn_revnum_t peg_revision, 992251881Speter svn_revnum_t start_rev, 993251881Speter svn_revnum_t end_rev, 994251881Speter svn_location_segment_receiver_t receiver, 995251881Speter void *receiver_baton, 996251881Speter apr_pool_t *pool) 997251881Speter{ 998251881Speter svn_error_t *err; 999251881Speter 1000251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1001251881Speter err = session->vtable->get_location_segments(session, path, peg_revision, 1002251881Speter start_rev, end_rev, 1003251881Speter receiver, receiver_baton, pool); 1004251881Speter if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 1005251881Speter { 1006251881Speter svn_error_clear(err); 1007251881Speter 1008251881Speter /* Do it the slow way, using get-logs, for older servers. */ 1009251881Speter err = svn_ra__location_segments_from_log(session, path, 1010251881Speter peg_revision, start_rev, 1011251881Speter end_rev, receiver, 1012251881Speter receiver_baton, pool); 1013251881Speter } 1014251881Speter return err; 1015251881Speter} 1016251881Speter 1017251881Spetersvn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session, 1018251881Speter const char *path, 1019251881Speter svn_revnum_t start, 1020251881Speter svn_revnum_t end, 1021251881Speter svn_boolean_t include_merged_revisions, 1022251881Speter svn_file_rev_handler_t handler, 1023251881Speter void *handler_baton, 1024251881Speter apr_pool_t *pool) 1025251881Speter{ 1026251881Speter svn_error_t *err; 1027251881Speter 1028251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1029251881Speter 1030251881Speter if (include_merged_revisions) 1031251881Speter SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool)); 1032251881Speter 1033253734Speter if (start > end) 1034253734Speter SVN_ERR( 1035253734Speter svn_ra__assert_capable_server(session, 1036253734Speter SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE, 1037253734Speter NULL, 1038253734Speter pool)); 1039253734Speter 1040251881Speter err = session->vtable->get_file_revs(session, path, start, end, 1041251881Speter include_merged_revisions, 1042251881Speter handler, handler_baton, pool); 1043251881Speter if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)) 1044251881Speter { 1045251881Speter svn_error_clear(err); 1046251881Speter 1047251881Speter /* Do it the slow way, using get-logs, for older servers. */ 1048251881Speter err = svn_ra__file_revs_from_log(session, path, start, end, 1049251881Speter handler, handler_baton, pool); 1050251881Speter } 1051251881Speter return err; 1052251881Speter} 1053251881Speter 1054251881Spetersvn_error_t *svn_ra_lock(svn_ra_session_t *session, 1055251881Speter apr_hash_t *path_revs, 1056251881Speter const char *comment, 1057251881Speter svn_boolean_t steal_lock, 1058251881Speter svn_ra_lock_callback_t lock_func, 1059251881Speter void *lock_baton, 1060251881Speter apr_pool_t *pool) 1061251881Speter{ 1062251881Speter apr_hash_index_t *hi; 1063251881Speter 1064251881Speter for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi)) 1065251881Speter { 1066251881Speter const char *path = svn__apr_hash_index_key(hi); 1067251881Speter 1068251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1069251881Speter } 1070251881Speter 1071251881Speter if (comment && ! svn_xml_is_xml_safe(comment, strlen(comment))) 1072251881Speter return svn_error_create 1073251881Speter (SVN_ERR_XML_UNESCAPABLE_DATA, NULL, 1074251881Speter _("Lock comment contains illegal characters")); 1075251881Speter 1076251881Speter return session->vtable->lock(session, path_revs, comment, steal_lock, 1077251881Speter lock_func, lock_baton, pool); 1078251881Speter} 1079251881Speter 1080251881Spetersvn_error_t *svn_ra_unlock(svn_ra_session_t *session, 1081251881Speter apr_hash_t *path_tokens, 1082251881Speter svn_boolean_t break_lock, 1083251881Speter svn_ra_lock_callback_t lock_func, 1084251881Speter void *lock_baton, 1085251881Speter apr_pool_t *pool) 1086251881Speter{ 1087251881Speter apr_hash_index_t *hi; 1088251881Speter 1089251881Speter for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi)) 1090251881Speter { 1091251881Speter const char *path = svn__apr_hash_index_key(hi); 1092251881Speter 1093251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1094251881Speter } 1095251881Speter 1096251881Speter return session->vtable->unlock(session, path_tokens, break_lock, 1097251881Speter lock_func, lock_baton, pool); 1098251881Speter} 1099251881Speter 1100251881Spetersvn_error_t *svn_ra_get_lock(svn_ra_session_t *session, 1101251881Speter svn_lock_t **lock, 1102251881Speter const char *path, 1103251881Speter apr_pool_t *pool) 1104251881Speter{ 1105251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1106251881Speter return session->vtable->get_lock(session, lock, path, pool); 1107251881Speter} 1108251881Speter 1109251881Spetersvn_error_t *svn_ra_get_locks2(svn_ra_session_t *session, 1110251881Speter apr_hash_t **locks, 1111251881Speter const char *path, 1112251881Speter svn_depth_t depth, 1113251881Speter apr_pool_t *pool) 1114251881Speter{ 1115251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1116251881Speter SVN_ERR_ASSERT((depth == svn_depth_empty) || 1117251881Speter (depth == svn_depth_files) || 1118251881Speter (depth == svn_depth_immediates) || 1119251881Speter (depth == svn_depth_infinity)); 1120251881Speter return session->vtable->get_locks(session, locks, path, depth, pool); 1121251881Speter} 1122251881Speter 1123251881Spetersvn_error_t *svn_ra_get_locks(svn_ra_session_t *session, 1124251881Speter apr_hash_t **locks, 1125251881Speter const char *path, 1126251881Speter apr_pool_t *pool) 1127251881Speter{ 1128251881Speter return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool); 1129251881Speter} 1130251881Speter 1131251881Spetersvn_error_t *svn_ra_replay(svn_ra_session_t *session, 1132251881Speter svn_revnum_t revision, 1133251881Speter svn_revnum_t low_water_mark, 1134251881Speter svn_boolean_t text_deltas, 1135251881Speter const svn_delta_editor_t *editor, 1136251881Speter void *edit_baton, 1137251881Speter apr_pool_t *pool) 1138251881Speter{ 1139251881Speter return session->vtable->replay(session, revision, low_water_mark, 1140251881Speter text_deltas, editor, edit_baton, pool); 1141251881Speter} 1142251881Speter 1143251881Spetersvn_error_t * 1144251881Spetersvn_ra__replay_ev2(svn_ra_session_t *session, 1145251881Speter svn_revnum_t revision, 1146251881Speter svn_revnum_t low_water_mark, 1147251881Speter svn_boolean_t send_deltas, 1148251881Speter svn_editor_t *editor, 1149251881Speter apr_pool_t *scratch_pool) 1150251881Speter{ 1151251881Speter SVN__NOT_IMPLEMENTED(); 1152251881Speter} 1153251881Speter 1154251881Speterstatic svn_error_t * 1155251881Speterreplay_range_from_replays(svn_ra_session_t *session, 1156251881Speter svn_revnum_t start_revision, 1157251881Speter svn_revnum_t end_revision, 1158251881Speter svn_revnum_t low_water_mark, 1159251881Speter svn_boolean_t text_deltas, 1160251881Speter svn_ra_replay_revstart_callback_t revstart_func, 1161251881Speter svn_ra_replay_revfinish_callback_t revfinish_func, 1162251881Speter void *replay_baton, 1163251881Speter apr_pool_t *scratch_pool) 1164251881Speter{ 1165251881Speter apr_pool_t *iterpool = svn_pool_create(scratch_pool); 1166251881Speter svn_revnum_t rev; 1167251881Speter 1168251881Speter for (rev = start_revision ; rev <= end_revision ; rev++) 1169251881Speter { 1170251881Speter const svn_delta_editor_t *editor; 1171251881Speter void *edit_baton; 1172251881Speter apr_hash_t *rev_props; 1173251881Speter 1174251881Speter svn_pool_clear(iterpool); 1175251881Speter 1176251881Speter SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool)); 1177251881Speter 1178251881Speter SVN_ERR(revstart_func(rev, replay_baton, 1179251881Speter &editor, &edit_baton, 1180251881Speter rev_props, 1181251881Speter iterpool)); 1182251881Speter SVN_ERR(svn_ra_replay(session, rev, low_water_mark, 1183251881Speter text_deltas, editor, edit_baton, 1184251881Speter iterpool)); 1185251881Speter SVN_ERR(revfinish_func(rev, replay_baton, 1186251881Speter editor, edit_baton, 1187251881Speter rev_props, 1188251881Speter iterpool)); 1189251881Speter } 1190251881Speter svn_pool_destroy(iterpool); 1191251881Speter 1192251881Speter return SVN_NO_ERROR; 1193251881Speter} 1194251881Speter 1195251881Spetersvn_error_t * 1196251881Spetersvn_ra_replay_range(svn_ra_session_t *session, 1197251881Speter svn_revnum_t start_revision, 1198251881Speter svn_revnum_t end_revision, 1199251881Speter svn_revnum_t low_water_mark, 1200251881Speter svn_boolean_t text_deltas, 1201251881Speter svn_ra_replay_revstart_callback_t revstart_func, 1202251881Speter svn_ra_replay_revfinish_callback_t revfinish_func, 1203251881Speter void *replay_baton, 1204251881Speter apr_pool_t *pool) 1205251881Speter{ 1206251881Speter svn_error_t *err = 1207251881Speter session->vtable->replay_range(session, start_revision, end_revision, 1208251881Speter low_water_mark, text_deltas, 1209251881Speter revstart_func, revfinish_func, 1210251881Speter replay_baton, pool); 1211251881Speter 1212251881Speter if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED))) 1213251881Speter return svn_error_trace(err); 1214251881Speter 1215251881Speter svn_error_clear(err); 1216251881Speter return svn_error_trace(replay_range_from_replays(session, start_revision, 1217251881Speter end_revision, 1218251881Speter low_water_mark, 1219251881Speter text_deltas, 1220251881Speter revstart_func, 1221251881Speter revfinish_func, 1222251881Speter replay_baton, pool)); 1223251881Speter} 1224251881Speter 1225251881Spetersvn_error_t * 1226251881Spetersvn_ra__replay_range_ev2(svn_ra_session_t *session, 1227251881Speter svn_revnum_t start_revision, 1228251881Speter svn_revnum_t end_revision, 1229251881Speter svn_revnum_t low_water_mark, 1230251881Speter svn_boolean_t send_deltas, 1231251881Speter svn_ra__replay_revstart_ev2_callback_t revstart_func, 1232251881Speter svn_ra__replay_revfinish_ev2_callback_t revfinish_func, 1233251881Speter void *replay_baton, 1234251881Speter svn_ra__provide_base_cb_t provide_base_cb, 1235251881Speter svn_ra__provide_props_cb_t provide_props_cb, 1236251881Speter svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1237251881Speter void *cb_baton, 1238251881Speter apr_pool_t *scratch_pool) 1239251881Speter{ 1240251881Speter if (session->vtable->replay_range_ev2 == NULL) 1241251881Speter { 1242251881Speter /* The specific RA layer does not have an implementation. Use our 1243251881Speter default shim over the normal replay editor. */ 1244251881Speter 1245251881Speter /* This will call the Ev1 replay range handler with modified 1246251881Speter callbacks. */ 1247251881Speter return svn_error_trace(svn_ra__use_replay_range_shim( 1248251881Speter session, 1249251881Speter start_revision, 1250251881Speter end_revision, 1251251881Speter low_water_mark, 1252251881Speter send_deltas, 1253251881Speter revstart_func, 1254251881Speter revfinish_func, 1255251881Speter replay_baton, 1256251881Speter provide_base_cb, 1257251881Speter provide_props_cb, 1258251881Speter cb_baton, 1259251881Speter scratch_pool)); 1260251881Speter } 1261251881Speter 1262251881Speter return svn_error_trace(session->vtable->replay_range_ev2( 1263251881Speter session, start_revision, end_revision, 1264251881Speter low_water_mark, send_deltas, revstart_func, 1265251881Speter revfinish_func, replay_baton, scratch_pool)); 1266251881Speter} 1267251881Speter 1268251881Spetersvn_error_t *svn_ra_has_capability(svn_ra_session_t *session, 1269251881Speter svn_boolean_t *has, 1270251881Speter const char *capability, 1271251881Speter apr_pool_t *pool) 1272251881Speter{ 1273251881Speter return session->vtable->has_capability(session, has, capability, pool); 1274251881Speter} 1275251881Speter 1276251881Spetersvn_error_t * 1277251881Spetersvn_ra_get_deleted_rev(svn_ra_session_t *session, 1278251881Speter const char *path, 1279251881Speter svn_revnum_t peg_revision, 1280251881Speter svn_revnum_t end_revision, 1281251881Speter svn_revnum_t *revision_deleted, 1282251881Speter apr_pool_t *pool) 1283251881Speter{ 1284251881Speter svn_error_t *err; 1285251881Speter 1286251881Speter /* Path must be relative. */ 1287251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1288251881Speter 1289251881Speter if (!SVN_IS_VALID_REVNUM(peg_revision)) 1290251881Speter return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1291251881Speter _("Invalid peg revision %ld"), peg_revision); 1292251881Speter if (!SVN_IS_VALID_REVNUM(end_revision)) 1293251881Speter return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1294251881Speter _("Invalid end revision %ld"), end_revision); 1295251881Speter if (end_revision <= peg_revision) 1296251881Speter return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL, 1297251881Speter _("Peg revision must precede end revision")); 1298251881Speter err = session->vtable->get_deleted_rev(session, path, 1299251881Speter peg_revision, 1300251881Speter end_revision, 1301251881Speter revision_deleted, 1302251881Speter pool); 1303251881Speter if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE)) 1304251881Speter { 1305251881Speter svn_error_clear(err); 1306251881Speter 1307251881Speter /* Do it the slow way, using get-logs, for older servers. */ 1308251881Speter err = svn_ra__get_deleted_rev_from_log(session, path, peg_revision, 1309251881Speter end_revision, revision_deleted, 1310251881Speter pool); 1311251881Speter } 1312251881Speter return err; 1313251881Speter} 1314251881Speter 1315251881Spetersvn_error_t * 1316251881Spetersvn_ra_get_inherited_props(svn_ra_session_t *session, 1317251881Speter apr_array_header_t **iprops, 1318251881Speter const char *path, 1319251881Speter svn_revnum_t revision, 1320251881Speter apr_pool_t *result_pool, 1321251881Speter apr_pool_t *scratch_pool) 1322251881Speter{ 1323251881Speter svn_boolean_t iprop_capable; 1324251881Speter 1325251881Speter /* Path must be relative. */ 1326251881Speter SVN_ERR_ASSERT(svn_relpath_is_canonical(path)); 1327251881Speter 1328251881Speter SVN_ERR(svn_ra_has_capability(session, &iprop_capable, 1329251881Speter SVN_RA_CAPABILITY_INHERITED_PROPS, 1330251881Speter scratch_pool)); 1331251881Speter 1332251881Speter if (iprop_capable) 1333251881Speter { 1334251881Speter SVN_ERR(session->vtable->get_inherited_props(session, iprops, path, 1335251881Speter revision, result_pool, 1336251881Speter scratch_pool)); 1337251881Speter } 1338251881Speter else 1339251881Speter { 1340251881Speter /* Fallback for legacy servers. */ 1341251881Speter SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops, 1342251881Speter result_pool, scratch_pool)); 1343251881Speter } 1344251881Speter 1345251881Speter return SVN_NO_ERROR; 1346251881Speter} 1347251881Speter 1348251881Spetersvn_error_t * 1349251881Spetersvn_ra__get_commit_ev2(svn_editor_t **editor, 1350251881Speter svn_ra_session_t *session, 1351251881Speter apr_hash_t *revprop_table, 1352251881Speter svn_commit_callback2_t commit_callback, 1353251881Speter void *commit_baton, 1354251881Speter apr_hash_t *lock_tokens, 1355251881Speter svn_boolean_t keep_locks, 1356251881Speter svn_ra__provide_base_cb_t provide_base_cb, 1357251881Speter svn_ra__provide_props_cb_t provide_props_cb, 1358251881Speter svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb, 1359251881Speter void *cb_baton, 1360251881Speter apr_pool_t *result_pool, 1361251881Speter apr_pool_t *scratch_pool) 1362251881Speter{ 1363251881Speter if (session->vtable->get_commit_ev2 == NULL) 1364251881Speter { 1365251881Speter /* The specific RA layer does not have an implementation. Use our 1366251881Speter default shim over the normal commit editor. */ 1367251881Speter 1368251881Speter /* Remap for RA layers exposing Ev1. */ 1369251881Speter remap_commit_callback(&commit_callback, &commit_baton, 1370251881Speter session, commit_callback, commit_baton, 1371251881Speter result_pool); 1372251881Speter 1373251881Speter return svn_error_trace(svn_ra__use_commit_shim( 1374251881Speter editor, 1375251881Speter session, 1376251881Speter revprop_table, 1377251881Speter commit_callback, commit_baton, 1378251881Speter lock_tokens, 1379251881Speter keep_locks, 1380251881Speter provide_base_cb, 1381251881Speter provide_props_cb, 1382251881Speter get_copysrc_kind_cb, 1383251881Speter cb_baton, 1384251881Speter session->cancel_func, session->cancel_baton, 1385251881Speter result_pool, scratch_pool)); 1386251881Speter } 1387251881Speter 1388251881Speter /* Note: no need to remap the callback for Ev2. RA layers providing this 1389251881Speter vtable entry should completely fill in commit_info. */ 1390251881Speter 1391251881Speter return svn_error_trace(session->vtable->get_commit_ev2( 1392251881Speter editor, 1393251881Speter session, 1394251881Speter revprop_table, 1395251881Speter commit_callback, commit_baton, 1396251881Speter lock_tokens, 1397251881Speter keep_locks, 1398251881Speter provide_base_cb, 1399251881Speter provide_props_cb, 1400251881Speter get_copysrc_kind_cb, 1401251881Speter cb_baton, 1402251881Speter session->cancel_func, session->cancel_baton, 1403251881Speter result_pool, scratch_pool)); 1404251881Speter} 1405251881Speter 1406251881Speter 1407251881Spetersvn_error_t * 1408251881Spetersvn_ra_print_modules(svn_stringbuf_t *output, 1409251881Speter apr_pool_t *pool) 1410251881Speter{ 1411251881Speter const struct ra_lib_defn *defn; 1412251881Speter const char * const *schemes; 1413251881Speter svn_ra__init_func_t initfunc; 1414251881Speter const svn_ra__vtable_t *vtable; 1415251881Speter apr_pool_t *iterpool = svn_pool_create(pool); 1416251881Speter 1417251881Speter for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 1418251881Speter { 1419251881Speter char *line; 1420251881Speter 1421251881Speter svn_pool_clear(iterpool); 1422251881Speter 1423251881Speter initfunc = defn->initfunc; 1424251881Speter if (! initfunc) 1425251881Speter SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name, 1426251881Speter iterpool)); 1427251881Speter 1428251881Speter if (initfunc) 1429251881Speter { 1430251881Speter SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool)); 1431251881Speter 1432251881Speter SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name)); 1433251881Speter 1434251881Speter /* Note: if you change the formatting of the description, 1435251881Speter bear in mind that ra_svn's description has multiple lines when 1436251881Speter built with SASL. */ 1437251881Speter line = apr_psprintf(iterpool, "* ra_%s : %s\n", 1438251881Speter defn->ra_name, 1439251881Speter vtable->get_description()); 1440251881Speter svn_stringbuf_appendcstr(output, line); 1441251881Speter 1442251881Speter for (schemes = vtable->get_schemes(iterpool); *schemes != NULL; 1443251881Speter ++schemes) 1444251881Speter { 1445251881Speter line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"), 1446251881Speter *schemes); 1447251881Speter svn_stringbuf_appendcstr(output, line); 1448251881Speter } 1449251881Speter } 1450251881Speter } 1451251881Speter 1452251881Speter svn_pool_destroy(iterpool); 1453251881Speter 1454251881Speter return SVN_NO_ERROR; 1455251881Speter} 1456251881Speter 1457251881Speter 1458251881Spetersvn_error_t * 1459251881Spetersvn_ra_print_ra_libraries(svn_stringbuf_t **descriptions, 1460251881Speter void *ra_baton, 1461251881Speter apr_pool_t *pool) 1462251881Speter{ 1463251881Speter *descriptions = svn_stringbuf_create_empty(pool); 1464251881Speter return svn_ra_print_modules(*descriptions, pool); 1465251881Speter} 1466251881Speter 1467251881Speter 1468251881Spetersvn_error_t * 1469251881Spetersvn_ra__register_editor_shim_callbacks(svn_ra_session_t *session, 1470251881Speter svn_delta_shim_callbacks_t *callbacks) 1471251881Speter{ 1472251881Speter SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks)); 1473251881Speter return SVN_NO_ERROR; 1474251881Speter} 1475251881Speter 1476251881Speter 1477251881Speter/* Return the library version number. */ 1478251881Speterconst svn_version_t * 1479251881Spetersvn_ra_version(void) 1480251881Speter{ 1481251881Speter SVN_VERSION_BODY; 1482251881Speter} 1483251881Speter 1484251881Speter 1485251881Speter/*** Compatibility Interfaces **/ 1486251881Spetersvn_error_t * 1487251881Spetersvn_ra_init_ra_libs(void **ra_baton, 1488251881Speter apr_pool_t *pool) 1489251881Speter{ 1490251881Speter *ra_baton = pool; 1491251881Speter return SVN_NO_ERROR; 1492251881Speter} 1493251881Speter 1494251881Spetersvn_error_t * 1495251881Spetersvn_ra_get_ra_library(svn_ra_plugin_t **library, 1496251881Speter void *ra_baton, 1497251881Speter const char *url, 1498251881Speter apr_pool_t *pool) 1499251881Speter{ 1500251881Speter const struct ra_lib_defn *defn; 1501251881Speter apr_pool_t *load_pool = ra_baton; 1502251881Speter apr_hash_t *ht = apr_hash_make(pool); 1503251881Speter 1504251881Speter /* Figure out which RA library key matches URL. */ 1505251881Speter for (defn = ra_libraries; defn->ra_name != NULL; ++defn) 1506251881Speter { 1507251881Speter const char *scheme; 1508251881Speter if ((scheme = has_scheme_of(defn->schemes, url))) 1509251881Speter { 1510251881Speter svn_ra_init_func_t compat_initfunc = defn->compat_initfunc; 1511251881Speter 1512251881Speter if (! compat_initfunc) 1513251881Speter { 1514251881Speter SVN_ERR(load_ra_module 1515251881Speter (NULL, &compat_initfunc, defn->ra_name, load_pool)); 1516251881Speter } 1517251881Speter if (! compat_initfunc) 1518251881Speter { 1519251881Speter continue; 1520251881Speter } 1521251881Speter 1522251881Speter SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht)); 1523251881Speter 1524251881Speter *library = svn_hash_gets(ht, scheme); 1525251881Speter 1526251881Speter /* The library may support just a subset of the schemes listed, 1527251881Speter so we have to check here too. */ 1528251881Speter if (! *library) 1529251881Speter break; 1530251881Speter 1531251881Speter return check_ra_version((*library)->get_version(), scheme); 1532251881Speter } 1533251881Speter } 1534251881Speter 1535251881Speter /* Couldn't find a match... */ 1536251881Speter *library = NULL; 1537251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 1538251881Speter _("Unrecognized URL scheme '%s'"), url); 1539251881Speter} 1540251881Speter 1541251881Speter/* For each libsvn_ra_foo library that is not linked in, provide a default 1542251881Speter implementation for svn_ra_foo_init which returns a "not implemented" 1543251881Speter error. */ 1544251881Speter 1545251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_NEON 1546251881Spetersvn_error_t * 1547251881Spetersvn_ra_dav_init(int abi_version, 1548251881Speter apr_pool_t *pool, 1549251881Speter apr_hash_t *hash) 1550251881Speter{ 1551251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1552251881Speter} 1553251881Speter#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_NEON */ 1554251881Speter 1555251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SVN 1556251881Spetersvn_error_t * 1557251881Spetersvn_ra_svn_init(int abi_version, 1558251881Speter apr_pool_t *pool, 1559251881Speter apr_hash_t *hash) 1560251881Speter{ 1561251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1562251881Speter} 1563251881Speter#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SVN */ 1564251881Speter 1565251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL 1566251881Spetersvn_error_t * 1567251881Spetersvn_ra_local_init(int abi_version, 1568251881Speter apr_pool_t *pool, 1569251881Speter apr_hash_t *hash) 1570251881Speter{ 1571251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1572251881Speter} 1573251881Speter#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL */ 1574251881Speter 1575251881Speter#ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SERF 1576251881Spetersvn_error_t * 1577251881Spetersvn_ra_serf_init(int abi_version, 1578251881Speter apr_pool_t *pool, 1579251881Speter apr_hash_t *hash) 1580251881Speter{ 1581251881Speter return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL); 1582251881Speter} 1583251881Speter#endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SERF */ 1584