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