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