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