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