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