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