]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_ra/ra_loader.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_ra / ra_loader.c
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 \f
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 \f
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.) */
65 static const char * const dav_schemes[] = { "http", "https", NULL };
66 static const char * const svn_schemes[] = { "svn", NULL };
67 static const char * const local_schemes[] = { "file", NULL };
68
69 static 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  */
125 static svn_error_t *
126 load_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. */
187 static const char *
188 has_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. */
209 static svn_error_t *
210 check_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 \f
229 /*** Public Interfaces ***/
230
231 svn_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. */
244 svn_error_t *
245 svn_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
252 svn_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
529 svn_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
546 svn_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
553 svn_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
569 svn_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
586 svn_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
593 svn_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
601 svn_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
634 svn_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
643 svn_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
653 struct 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 */
662 static svn_error_t *
663 commit_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.  */
679 static void
680 remap_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
708 svn_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
728 svn_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
741 svn_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
755 svn_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
786 svn_error_t *
787 svn_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
811 svn_error_t *
812 svn_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
839 svn_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
857 svn_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
880 svn_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
912 svn_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
922 svn_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
932 svn_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
941 svn_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
948 svn_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
957 svn_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
964 svn_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
988 svn_error_t *
989 svn_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
1017 svn_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
1054 svn_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
1080 svn_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
1100 svn_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
1109 svn_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
1123 svn_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
1131 svn_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
1143 svn_error_t *
1144 svn_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
1154 static svn_error_t *
1155 replay_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
1195 svn_error_t *
1196 svn_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
1225 svn_error_t *
1226 svn_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
1268 svn_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
1276 svn_error_t *
1277 svn_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
1315 svn_error_t *
1316 svn_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
1348 svn_error_t *
1349 svn_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 \f
1406
1407 svn_error_t *
1408 svn_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
1458 svn_error_t *
1459 svn_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
1468 svn_error_t *
1469 svn_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. */
1478 const svn_version_t *
1479 svn_ra_version(void)
1480 {
1481   SVN_VERSION_BODY;
1482 }
1483
1484 \f
1485 /*** Compatibility Interfaces **/
1486 svn_error_t *
1487 svn_ra_init_ra_libs(void **ra_baton,
1488                     apr_pool_t *pool)
1489 {
1490   *ra_baton = pool;
1491   return SVN_NO_ERROR;
1492 }
1493
1494 svn_error_t *
1495 svn_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
1546 svn_error_t *
1547 svn_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
1556 svn_error_t *
1557 svn_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
1566 svn_error_t *
1567 svn_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
1576 svn_error_t *
1577 svn_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 */