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