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_CLIENT_LINKS_RA_SVN
84 svn_ra_svn__init,
85 svn_ra_svn__deprecated_init
86 #endif
87 },
88
89 {
90 "local",
91 local_schemes,
92 #ifdef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
93 svn_ra_local__init,
94 svn_ra_local__deprecated_init
95 #endif
96 },
97
98 {
99 "serf",
100 dav_schemes,
101 #ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
102 svn_ra_serf__init,
103 svn_ra_serf__deprecated_init
104 #endif
105 },
106
107 /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */
108
109 /* sentinel */
110 { NULL }
111 };
112
113 /* Ensure that the RA library NAME is loaded.
114 *
115 * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init
116 * function of the library.
117 *
118 * If COMPAT_FUNC is non-NULL, set *COMPAT_FUNC to the address of the
119 * svn_ra_NAME_init compatibility init function of the library.
120 *
121 * ### todo: Any RA libraries implemented from this point forward
122 * ### don't really need an svn_ra_NAME_init compatibility function.
123 * ### Currently, load_ra_module() will error if no such function is
124 * ### found, but it might be more friendly to simply set *COMPAT_FUNC
125 * ### to null (assuming COMPAT_FUNC itself is non-null).
126 */
127 static svn_error_t *
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-%d.so.%d",
147 ra_name, SVN_VER_MAJOR, SVN_SOVERSION);
148 funcname = apr_psprintf(pool, "svn_ra_%s__init", ra_name);
149 compat_funcname = apr_psprintf(pool, "svn_ra_%s_init", ra_name);
150
151 /* find/load the specified library */
152 SVN_ERR(svn_dso_load(&dso, libname));
153 if (! dso)
154 return SVN_NO_ERROR;
155
156 /* find the initialization routines */
157 if (func)
158 {
159 status = apr_dso_sym(&symbol, dso, funcname);
160 if (status)
161 {
162 return svn_error_wrap_apr(status,
163 _("'%s' does not define '%s()'"),
164 libname, funcname);
165 }
166
167 *func = (svn_ra__init_func_t) symbol;
168 }
169
170 if (compat_func)
171 {
172 status = apr_dso_sym(&symbol, dso, compat_funcname);
173 if (status)
174 {
175 return svn_error_wrap_apr(status,
176 _("'%s' does not define '%s()'"),
177 libname, compat_funcname);
178 }
179
180 *compat_func = (svn_ra_init_func_t) symbol;
181 }
182 }
183 #endif /* APR_HAS_DSO */
184
185 return SVN_NO_ERROR;
186 }
187
188 /* If SCHEMES contains URL, return the scheme. Else, return NULL. */
189 static const char *
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 /* Check the UUID. */
414 if (uuid)
415 {
416 const char *repository_uuid;
417
418 SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
419 if (strcmp(uuid, repository_uuid) != 0)
420 {
421 /* Duplicate the uuid as it is allocated in sesspool */
422 repository_uuid = apr_pstrdup(pool, repository_uuid);
423 svn_pool_destroy(sesspool); /* includes scratch_pool */
424 return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
425 _("Repository UUID '%s' doesn't match "
426 "expected UUID '%s'"),
427 repository_uuid, uuid);
428 }
429 }
430
431 svn_pool_destroy(scratch_pool);
432 *session_p = session;
433 return SVN_NO_ERROR;
434 }
435
436 svn_error_t *
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)437 svn_ra__dup_session(svn_ra_session_t **new_session,
438 svn_ra_session_t *old_session,
439 const char *session_url,
440 apr_pool_t *result_pool,
441 apr_pool_t *scratch_pool)
442 {
443 svn_ra_session_t *session;
444
445 if (session_url)
446 {
447 const char *dummy;
448
449 /* This verifies in new_session_url is in the repository */
450 SVN_ERR(svn_ra_get_path_relative_to_root(old_session,
451 &dummy,
452 session_url,
453 scratch_pool));
454 }
455 else
456 SVN_ERR(svn_ra_get_session_url(old_session, &session_url, scratch_pool));
457
458 /* Create the session object. */
459 session = apr_pcalloc(result_pool, sizeof(*session));
460 session->cancel_func = old_session->cancel_func;
461 session->cancel_baton = old_session->cancel_baton;
462 session->vtable = old_session->vtable;
463 session->pool = result_pool;
464
465 SVN_ERR(old_session->vtable->dup_session(session,
466 old_session,
467 session_url,
468 result_pool,
469 scratch_pool));
470
471 *new_session = session;
472 return SVN_NO_ERROR;
473 }
474
svn_ra_reparent(svn_ra_session_t * session,const char * url,apr_pool_t * pool)475 svn_error_t *svn_ra_reparent(svn_ra_session_t *session,
476 const char *url,
477 apr_pool_t *pool)
478 {
479 const char *repos_root;
480
481 /* Make sure the new URL is in the same repository, so that the
482 implementations don't have to do it. */
483 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, pool));
484 if (! svn_uri__is_ancestor(repos_root, url))
485 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
486 _("'%s' isn't in the same repository as '%s'"),
487 url, repos_root);
488
489 return session->vtable->reparent(session, url, pool);
490 }
491
svn_ra_get_session_url(svn_ra_session_t * session,const char ** url,apr_pool_t * pool)492 svn_error_t *svn_ra_get_session_url(svn_ra_session_t *session,
493 const char **url,
494 apr_pool_t *pool)
495 {
496 return session->vtable->get_session_url(session, url, pool);
497 }
498
svn_ra_get_path_relative_to_session(svn_ra_session_t * session,const char ** rel_path,const char * url,apr_pool_t * pool)499 svn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session,
500 const char **rel_path,
501 const char *url,
502 apr_pool_t *pool)
503 {
504 const char *sess_url;
505
506 SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool));
507 *rel_path = svn_uri_skip_ancestor(sess_url, url, pool);
508 if (! *rel_path)
509 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
510 _("'%s' isn't a child of session URL '%s'"),
511 url, sess_url);
512 return SVN_NO_ERROR;
513 }
514
svn_ra_get_path_relative_to_root(svn_ra_session_t * session,const char ** rel_path,const char * url,apr_pool_t * pool)515 svn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session,
516 const char **rel_path,
517 const char *url,
518 apr_pool_t *pool)
519 {
520 const char *root_url;
521
522 SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool));
523 *rel_path = svn_uri_skip_ancestor(root_url, url, pool);
524 if (! *rel_path)
525 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
526 _("'%s' isn't a child of repository root "
527 "URL '%s'"),
528 url, root_url);
529 return SVN_NO_ERROR;
530 }
531
svn_ra_get_latest_revnum(svn_ra_session_t * session,svn_revnum_t * latest_revnum,apr_pool_t * pool)532 svn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session,
533 svn_revnum_t *latest_revnum,
534 apr_pool_t *pool)
535 {
536 return session->vtable->get_latest_revnum(session, latest_revnum, pool);
537 }
538
svn_ra_get_dated_revision(svn_ra_session_t * session,svn_revnum_t * revision,apr_time_t tm,apr_pool_t * pool)539 svn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session,
540 svn_revnum_t *revision,
541 apr_time_t tm,
542 apr_pool_t *pool)
543 {
544 return session->vtable->get_dated_revision(session, revision, tm, pool);
545 }
546
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)547 svn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session,
548 svn_revnum_t rev,
549 const char *name,
550 const svn_string_t *const *old_value_p,
551 const svn_string_t *value,
552 apr_pool_t *pool)
553 {
554 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
555
556 /* If an old value was specified, make sure the server supports
557 * specifying it. */
558 if (old_value_p)
559 {
560 svn_boolean_t has_atomic_revprops;
561
562 SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops,
563 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
564 pool));
565
566 if (!has_atomic_revprops)
567 /* API violation. (Should be an ASSERT, but gstein talked me
568 * out of it.) */
569 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
570 _("Specifying 'old_value_p' is not allowed when "
571 "the '%s' capability is not advertised, and "
572 "could indicate a bug in your client"),
573 SVN_RA_CAPABILITY_ATOMIC_REVPROPS);
574 }
575
576 return session->vtable->change_rev_prop(session, rev, name,
577 old_value_p, value, pool);
578 }
579
svn_ra_rev_proplist(svn_ra_session_t * session,svn_revnum_t rev,apr_hash_t ** props,apr_pool_t * pool)580 svn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session,
581 svn_revnum_t rev,
582 apr_hash_t **props,
583 apr_pool_t *pool)
584 {
585 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
586 return session->vtable->rev_proplist(session, rev, props, pool);
587 }
588
svn_ra_rev_prop(svn_ra_session_t * session,svn_revnum_t rev,const char * name,svn_string_t ** value,apr_pool_t * pool)589 svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session,
590 svn_revnum_t rev,
591 const char *name,
592 svn_string_t **value,
593 apr_pool_t *pool)
594 {
595 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
596 return session->vtable->rev_prop(session, rev, name, value, pool);
597 }
598
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)599 svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
600 const svn_delta_editor_t **editor,
601 void **edit_baton,
602 apr_hash_t *revprop_table,
603 svn_commit_callback2_t commit_callback,
604 void *commit_baton,
605 apr_hash_t *lock_tokens,
606 svn_boolean_t keep_locks,
607 apr_pool_t *pool)
608 {
609 return session->vtable->get_commit_editor(session, editor, edit_baton,
610 revprop_table,
611 commit_callback, commit_baton,
612 lock_tokens, keep_locks, pool);
613 }
614
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)615 svn_error_t *svn_ra_get_file(svn_ra_session_t *session,
616 const char *path,
617 svn_revnum_t revision,
618 svn_stream_t *stream,
619 svn_revnum_t *fetched_rev,
620 apr_hash_t **props,
621 apr_pool_t *pool)
622 {
623 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
624 return session->vtable->get_file(session, path, revision, stream,
625 fetched_rev, props, pool);
626 }
627
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)628 svn_error_t *svn_ra_get_dir2(svn_ra_session_t *session,
629 apr_hash_t **dirents,
630 svn_revnum_t *fetched_rev,
631 apr_hash_t **props,
632 const char *path,
633 svn_revnum_t revision,
634 apr_uint32_t dirent_fields,
635 apr_pool_t *pool)
636 {
637 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
638 return session->vtable->get_dir(session, dirents, fetched_rev, props,
639 path, revision, dirent_fields, pool);
640 }
641
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)642 svn_error_t *svn_ra_get_mergeinfo(svn_ra_session_t *session,
643 svn_mergeinfo_catalog_t *catalog,
644 const apr_array_header_t *paths,
645 svn_revnum_t revision,
646 svn_mergeinfo_inheritance_t inherit,
647 svn_boolean_t include_descendants,
648 apr_pool_t *pool)
649 {
650 svn_error_t *err;
651 int i;
652
653 /* Validate path format. */
654 for (i = 0; i < paths->nelts; i++)
655 {
656 const char *path = APR_ARRAY_IDX(paths, i, const char *);
657 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
658 }
659
660 /* Check server Merge Tracking capability. */
661 err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool);
662 if (err)
663 {
664 *catalog = NULL;
665 return err;
666 }
667
668 return session->vtable->get_mergeinfo(session, catalog, paths,
669 revision, inherit,
670 include_descendants, pool);
671 }
672
673 svn_error_t *
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)674 svn_ra_do_update3(svn_ra_session_t *session,
675 const svn_ra_reporter3_t **reporter,
676 void **report_baton,
677 svn_revnum_t revision_to_update_to,
678 const char *update_target,
679 svn_depth_t depth,
680 svn_boolean_t send_copyfrom_args,
681 svn_boolean_t ignore_ancestry,
682 const svn_delta_editor_t *update_editor,
683 void *update_baton,
684 apr_pool_t *result_pool,
685 apr_pool_t *scratch_pool)
686 {
687 SVN_ERR_ASSERT(svn_path_is_empty(update_target)
688 || svn_path_is_single_path_component(update_target));
689 return session->vtable->do_update(session,
690 reporter, report_baton,
691 revision_to_update_to, update_target,
692 depth, send_copyfrom_args,
693 ignore_ancestry,
694 update_editor, update_baton,
695 result_pool, scratch_pool);
696 }
697
698 svn_error_t *
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)699 svn_ra_do_switch3(svn_ra_session_t *session,
700 const svn_ra_reporter3_t **reporter,
701 void **report_baton,
702 svn_revnum_t revision_to_switch_to,
703 const char *switch_target,
704 svn_depth_t depth,
705 const char *switch_url,
706 svn_boolean_t send_copyfrom_args,
707 svn_boolean_t ignore_ancestry,
708 const svn_delta_editor_t *switch_editor,
709 void *switch_baton,
710 apr_pool_t *result_pool,
711 apr_pool_t *scratch_pool)
712 {
713 SVN_ERR_ASSERT(svn_path_is_empty(switch_target)
714 || svn_path_is_single_path_component(switch_target));
715 return session->vtable->do_switch(session,
716 reporter, report_baton,
717 revision_to_switch_to, switch_target,
718 depth, switch_url,
719 send_copyfrom_args,
720 ignore_ancestry,
721 switch_editor,
722 switch_baton,
723 result_pool, scratch_pool);
724 }
725
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)726 svn_error_t *svn_ra_do_status2(svn_ra_session_t *session,
727 const svn_ra_reporter3_t **reporter,
728 void **report_baton,
729 const char *status_target,
730 svn_revnum_t revision,
731 svn_depth_t depth,
732 const svn_delta_editor_t *status_editor,
733 void *status_baton,
734 apr_pool_t *pool)
735 {
736 SVN_ERR_ASSERT(svn_path_is_empty(status_target)
737 || svn_path_is_single_path_component(status_target));
738 return session->vtable->do_status(session,
739 reporter, report_baton,
740 status_target, revision, depth,
741 status_editor, status_baton, pool);
742 }
743
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)744 svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
745 const svn_ra_reporter3_t **reporter,
746 void **report_baton,
747 svn_revnum_t revision,
748 const char *diff_target,
749 svn_depth_t depth,
750 svn_boolean_t ignore_ancestry,
751 svn_boolean_t text_deltas,
752 const char *versus_url,
753 const svn_delta_editor_t *diff_editor,
754 void *diff_baton,
755 apr_pool_t *pool)
756 {
757 SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
758 || svn_path_is_single_path_component(diff_target));
759 return session->vtable->do_diff(session,
760 reporter, report_baton,
761 revision, diff_target,
762 depth, ignore_ancestry,
763 text_deltas, versus_url, diff_editor,
764 diff_baton, pool);
765 }
766
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)767 svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
768 const apr_array_header_t *paths,
769 svn_revnum_t start,
770 svn_revnum_t end,
771 int limit,
772 svn_boolean_t discover_changed_paths,
773 svn_boolean_t strict_node_history,
774 svn_boolean_t include_merged_revisions,
775 const apr_array_header_t *revprops,
776 svn_log_entry_receiver_t receiver,
777 void *receiver_baton,
778 apr_pool_t *pool)
779 {
780 if (paths)
781 {
782 int i;
783 for (i = 0; i < paths->nelts; i++)
784 {
785 const char *path = APR_ARRAY_IDX(paths, i, const char *);
786 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
787 }
788 }
789
790 if (include_merged_revisions)
791 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
792
793 return session->vtable->get_log(session, paths, start, end, limit,
794 discover_changed_paths, strict_node_history,
795 include_merged_revisions, revprops,
796 receiver, receiver_baton, pool);
797 }
798
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)799 svn_error_t *svn_ra_check_path(svn_ra_session_t *session,
800 const char *path,
801 svn_revnum_t revision,
802 svn_node_kind_t *kind,
803 apr_pool_t *pool)
804 {
805 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
806 return session->vtable->check_path(session, path, revision, kind, pool);
807 }
808
svn_ra_stat(svn_ra_session_t * session,const char * path,svn_revnum_t revision,svn_dirent_t ** dirent,apr_pool_t * pool)809 svn_error_t *svn_ra_stat(svn_ra_session_t *session,
810 const char *path,
811 svn_revnum_t revision,
812 svn_dirent_t **dirent,
813 apr_pool_t *pool)
814 {
815 svn_error_t *err;
816 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
817 err = session->vtable->stat(session, path, revision, dirent, pool);
818
819 /* svnserve before 1.2 doesn't support the above, so fall back on
820 a far less efficient, but still correct method. */
821 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
822 {
823 /* ### TODO: Find out if we can somehow move this code in libsvn_ra_svn.
824 */
825 apr_pool_t *scratch_pool = svn_pool_create(pool);
826 svn_node_kind_t kind;
827
828 svn_error_clear(err);
829
830 SVN_ERR(svn_ra_check_path(session, path, revision, &kind, scratch_pool));
831
832 if (kind != svn_node_none)
833 {
834 const char *repos_root_url;
835 const char *session_url;
836
837 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url,
838 scratch_pool));
839 SVN_ERR(svn_ra_get_session_url(session, &session_url,
840 scratch_pool));
841
842 if (!svn_path_is_empty(path))
843 session_url = svn_path_url_add_component2(session_url, path,
844 scratch_pool);
845
846 if (strcmp(session_url, repos_root_url) != 0)
847 {
848 svn_ra_session_t *parent_session;
849 apr_hash_t *parent_ents;
850 const char *parent_url, *base_name;
851
852 /* Open another session to the path's parent. This server
853 doesn't support svn_ra_reparent anyway, so don't try it. */
854 svn_uri_split(&parent_url, &base_name, session_url,
855 scratch_pool);
856
857 SVN_ERR(svn_ra__dup_session(&parent_session, session, parent_url,
858 scratch_pool, scratch_pool));
859
860 /* Get all parent's entries, no props. */
861 SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
862 NULL, "", revision, SVN_DIRENT_ALL,
863 scratch_pool));
864
865 /* Get the relevant entry. */
866 *dirent = svn_hash_gets(parent_ents, base_name);
867
868 if (*dirent)
869 *dirent = svn_dirent_dup(*dirent, pool);
870 }
871 else
872 {
873 apr_hash_t *props;
874 const svn_string_t *val;
875
876 /* We can't get the directory entry for the repository root,
877 but we can still get the information we want.
878 The created-rev of the repository root must, by definition,
879 be rev. */
880 *dirent = apr_pcalloc(pool, sizeof(**dirent));
881 (*dirent)->kind = kind;
882 (*dirent)->size = SVN_INVALID_FILESIZE;
883
884 SVN_ERR(svn_ra_get_dir2(session, NULL, NULL, &props,
885 "", revision, 0 /* no dirent fields */,
886 scratch_pool));
887 (*dirent)->has_props = (apr_hash_count(props) != 0);
888
889 (*dirent)->created_rev = revision;
890
891 SVN_ERR(svn_ra_rev_proplist(session, revision, &props,
892 scratch_pool));
893
894 val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
895 if (val)
896 SVN_ERR(svn_time_from_cstring(&(*dirent)->time, val->data,
897 scratch_pool));
898
899 val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
900 (*dirent)->last_author = val ? apr_pstrdup(pool, val->data)
901 : NULL;
902 }
903 }
904 else
905 *dirent = NULL;
906
907 svn_pool_clear(scratch_pool);
908 }
909 else
910 SVN_ERR(err);
911
912 return SVN_NO_ERROR;
913 }
914
svn_ra_get_uuid2(svn_ra_session_t * session,const char ** uuid,apr_pool_t * pool)915 svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session,
916 const char **uuid,
917 apr_pool_t *pool)
918 {
919 SVN_ERR(session->vtable->get_uuid(session, uuid, pool));
920 *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL;
921 return SVN_NO_ERROR;
922 }
923
svn_ra_get_uuid(svn_ra_session_t * session,const char ** uuid,apr_pool_t * pool)924 svn_error_t *svn_ra_get_uuid(svn_ra_session_t *session,
925 const char **uuid,
926 apr_pool_t *pool)
927 {
928 return session->vtable->get_uuid(session, uuid, pool);
929 }
930
svn_ra_get_repos_root2(svn_ra_session_t * session,const char ** url,apr_pool_t * pool)931 svn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session,
932 const char **url,
933 apr_pool_t *pool)
934 {
935 SVN_ERR(session->vtable->get_repos_root(session, url, pool));
936 *url = *url ? apr_pstrdup(pool, *url) : NULL;
937 return SVN_NO_ERROR;
938 }
939
svn_ra_get_repos_root(svn_ra_session_t * session,const char ** url,apr_pool_t * pool)940 svn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session,
941 const char **url,
942 apr_pool_t *pool)
943 {
944 return session->vtable->get_repos_root(session, url, pool);
945 }
946
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)947 svn_error_t *svn_ra_get_locations(svn_ra_session_t *session,
948 apr_hash_t **locations,
949 const char *path,
950 svn_revnum_t peg_revision,
951 const apr_array_header_t *location_revisions,
952 apr_pool_t *pool)
953 {
954 svn_error_t *err;
955
956 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
957 err = session->vtable->get_locations(session, locations, path,
958 peg_revision, location_revisions, pool);
959 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
960 {
961 svn_error_clear(err);
962
963 /* Do it the slow way, using get-logs, for older servers. */
964 err = svn_ra__locations_from_log(session, locations, path,
965 peg_revision, location_revisions,
966 pool);
967 }
968 return err;
969 }
970
971 svn_error_t *
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)972 svn_ra_get_location_segments(svn_ra_session_t *session,
973 const char *path,
974 svn_revnum_t peg_revision,
975 svn_revnum_t start_rev,
976 svn_revnum_t end_rev,
977 svn_location_segment_receiver_t receiver,
978 void *receiver_baton,
979 apr_pool_t *pool)
980 {
981 svn_error_t *err;
982
983 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
984 err = session->vtable->get_location_segments(session, path, peg_revision,
985 start_rev, end_rev,
986 receiver, receiver_baton, pool);
987 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
988 {
989 svn_error_clear(err);
990
991 /* Do it the slow way, using get-logs, for older servers. */
992 err = svn_ra__location_segments_from_log(session, path,
993 peg_revision, start_rev,
994 end_rev, receiver,
995 receiver_baton, pool);
996 }
997 return err;
998 }
999
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)1000 svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session,
1001 const char *path,
1002 svn_revnum_t start,
1003 svn_revnum_t end,
1004 svn_boolean_t include_merged_revisions,
1005 svn_file_rev_handler_t handler,
1006 void *handler_baton,
1007 apr_pool_t *pool)
1008 {
1009 svn_error_t *err;
1010
1011 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1012
1013 if (include_merged_revisions)
1014 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
1015
1016 if (start > end || !SVN_IS_VALID_REVNUM(start))
1017 SVN_ERR(
1018 svn_ra__assert_capable_server(session,
1019 SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
1020 NULL,
1021 pool));
1022
1023 err = session->vtable->get_file_revs(session, path, start, end,
1024 include_merged_revisions,
1025 handler, handler_baton, pool);
1026 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
1027 && !include_merged_revisions)
1028 {
1029 svn_error_clear(err);
1030
1031 /* Do it the slow way, using get-logs, for older servers. */
1032 err = svn_ra__file_revs_from_log(session, path, start, end,
1033 handler, handler_baton, pool);
1034 }
1035 return svn_error_trace(err);
1036 }
1037
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)1038 svn_error_t *svn_ra_lock(svn_ra_session_t *session,
1039 apr_hash_t *path_revs,
1040 const char *comment,
1041 svn_boolean_t steal_lock,
1042 svn_ra_lock_callback_t lock_func,
1043 void *lock_baton,
1044 apr_pool_t *pool)
1045 {
1046 apr_hash_index_t *hi;
1047
1048 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
1049 {
1050 const char *path = apr_hash_this_key(hi);
1051
1052 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1053 }
1054
1055 if (comment && ! svn_xml_is_xml_safe(comment, strlen(comment)))
1056 return svn_error_create
1057 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1058 _("Lock comment contains illegal characters"));
1059
1060 return session->vtable->lock(session, path_revs, comment, steal_lock,
1061 lock_func, lock_baton, pool);
1062 }
1063
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)1064 svn_error_t *svn_ra_unlock(svn_ra_session_t *session,
1065 apr_hash_t *path_tokens,
1066 svn_boolean_t break_lock,
1067 svn_ra_lock_callback_t lock_func,
1068 void *lock_baton,
1069 apr_pool_t *pool)
1070 {
1071 apr_hash_index_t *hi;
1072
1073 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
1074 {
1075 const char *path = apr_hash_this_key(hi);
1076
1077 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1078 }
1079
1080 return session->vtable->unlock(session, path_tokens, break_lock,
1081 lock_func, lock_baton, pool);
1082 }
1083
svn_ra_get_lock(svn_ra_session_t * session,svn_lock_t ** lock,const char * path,apr_pool_t * pool)1084 svn_error_t *svn_ra_get_lock(svn_ra_session_t *session,
1085 svn_lock_t **lock,
1086 const char *path,
1087 apr_pool_t *pool)
1088 {
1089 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1090 return session->vtable->get_lock(session, lock, path, pool);
1091 }
1092
svn_ra_get_locks2(svn_ra_session_t * session,apr_hash_t ** locks,const char * path,svn_depth_t depth,apr_pool_t * pool)1093 svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session,
1094 apr_hash_t **locks,
1095 const char *path,
1096 svn_depth_t depth,
1097 apr_pool_t *pool)
1098 {
1099 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1100 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1101 (depth == svn_depth_files) ||
1102 (depth == svn_depth_immediates) ||
1103 (depth == svn_depth_infinity));
1104 return session->vtable->get_locks(session, locks, path, depth, pool);
1105 }
1106
svn_ra_get_locks(svn_ra_session_t * session,apr_hash_t ** locks,const char * path,apr_pool_t * pool)1107 svn_error_t *svn_ra_get_locks(svn_ra_session_t *session,
1108 apr_hash_t **locks,
1109 const char *path,
1110 apr_pool_t *pool)
1111 {
1112 return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool);
1113 }
1114
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)1115 svn_error_t *svn_ra_replay(svn_ra_session_t *session,
1116 svn_revnum_t revision,
1117 svn_revnum_t low_water_mark,
1118 svn_boolean_t text_deltas,
1119 const svn_delta_editor_t *editor,
1120 void *edit_baton,
1121 apr_pool_t *pool)
1122 {
1123 return session->vtable->replay(session, revision, low_water_mark,
1124 text_deltas, editor, edit_baton, pool);
1125 }
1126
1127 svn_error_t *
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)1128 svn_ra__replay_ev2(svn_ra_session_t *session,
1129 svn_revnum_t revision,
1130 svn_revnum_t low_water_mark,
1131 svn_boolean_t send_deltas,
1132 svn_editor_t *editor,
1133 apr_pool_t *scratch_pool)
1134 {
1135 SVN__NOT_IMPLEMENTED();
1136 }
1137
1138 static svn_error_t *
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)1139 replay_range_from_replays(svn_ra_session_t *session,
1140 svn_revnum_t start_revision,
1141 svn_revnum_t end_revision,
1142 svn_revnum_t low_water_mark,
1143 svn_boolean_t text_deltas,
1144 svn_ra_replay_revstart_callback_t revstart_func,
1145 svn_ra_replay_revfinish_callback_t revfinish_func,
1146 void *replay_baton,
1147 apr_pool_t *scratch_pool)
1148 {
1149 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1150 svn_revnum_t rev;
1151
1152 for (rev = start_revision ; rev <= end_revision ; rev++)
1153 {
1154 const svn_delta_editor_t *editor;
1155 void *edit_baton;
1156 apr_hash_t *rev_props;
1157
1158 svn_pool_clear(iterpool);
1159
1160 SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool));
1161
1162 SVN_ERR(revstart_func(rev, replay_baton,
1163 &editor, &edit_baton,
1164 rev_props,
1165 iterpool));
1166 SVN_ERR(svn_ra_replay(session, rev, low_water_mark,
1167 text_deltas, editor, edit_baton,
1168 iterpool));
1169 SVN_ERR(revfinish_func(rev, replay_baton,
1170 editor, edit_baton,
1171 rev_props,
1172 iterpool));
1173 }
1174 svn_pool_destroy(iterpool);
1175
1176 return SVN_NO_ERROR;
1177 }
1178
1179 svn_error_t *
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)1180 svn_ra_replay_range(svn_ra_session_t *session,
1181 svn_revnum_t start_revision,
1182 svn_revnum_t end_revision,
1183 svn_revnum_t low_water_mark,
1184 svn_boolean_t text_deltas,
1185 svn_ra_replay_revstart_callback_t revstart_func,
1186 svn_ra_replay_revfinish_callback_t revfinish_func,
1187 void *replay_baton,
1188 apr_pool_t *pool)
1189 {
1190 svn_error_t *err =
1191 session->vtable->replay_range(session, start_revision, end_revision,
1192 low_water_mark, text_deltas,
1193 revstart_func, revfinish_func,
1194 replay_baton, pool);
1195
1196 if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED)))
1197 return svn_error_trace(err);
1198
1199 svn_error_clear(err);
1200 return svn_error_trace(replay_range_from_replays(session, start_revision,
1201 end_revision,
1202 low_water_mark,
1203 text_deltas,
1204 revstart_func,
1205 revfinish_func,
1206 replay_baton, pool));
1207 }
1208
1209 svn_error_t *
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)1210 svn_ra__replay_range_ev2(svn_ra_session_t *session,
1211 svn_revnum_t start_revision,
1212 svn_revnum_t end_revision,
1213 svn_revnum_t low_water_mark,
1214 svn_boolean_t send_deltas,
1215 svn_ra__replay_revstart_ev2_callback_t revstart_func,
1216 svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
1217 void *replay_baton,
1218 svn_ra__provide_base_cb_t provide_base_cb,
1219 svn_ra__provide_props_cb_t provide_props_cb,
1220 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1221 void *cb_baton,
1222 apr_pool_t *scratch_pool)
1223 {
1224 if (session->vtable->replay_range_ev2 == NULL)
1225 {
1226 /* The specific RA layer does not have an implementation. Use our
1227 default shim over the normal replay editor. */
1228
1229 /* This will call the Ev1 replay range handler with modified
1230 callbacks. */
1231 return svn_error_trace(svn_ra__use_replay_range_shim(
1232 session,
1233 start_revision,
1234 end_revision,
1235 low_water_mark,
1236 send_deltas,
1237 revstart_func,
1238 revfinish_func,
1239 replay_baton,
1240 provide_base_cb,
1241 provide_props_cb,
1242 cb_baton,
1243 scratch_pool));
1244 }
1245
1246 return svn_error_trace(session->vtable->replay_range_ev2(
1247 session, start_revision, end_revision,
1248 low_water_mark, send_deltas, revstart_func,
1249 revfinish_func, replay_baton, scratch_pool));
1250 }
1251
svn_ra_has_capability(svn_ra_session_t * session,svn_boolean_t * has,const char * capability,apr_pool_t * pool)1252 svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
1253 svn_boolean_t *has,
1254 const char *capability,
1255 apr_pool_t *pool)
1256 {
1257 return session->vtable->has_capability(session, has, capability, pool);
1258 }
1259
1260 svn_error_t *
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)1261 svn_ra_get_deleted_rev(svn_ra_session_t *session,
1262 const char *path,
1263 svn_revnum_t peg_revision,
1264 svn_revnum_t end_revision,
1265 svn_revnum_t *revision_deleted,
1266 apr_pool_t *pool)
1267 {
1268 svn_error_t *err;
1269
1270 /* Path must be relative. */
1271 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1272
1273 if (!SVN_IS_VALID_REVNUM(peg_revision))
1274 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1275 _("Invalid peg revision %ld"), peg_revision);
1276 if (!SVN_IS_VALID_REVNUM(end_revision))
1277 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1278 _("Invalid end revision %ld"), end_revision);
1279 if (end_revision <= peg_revision)
1280 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1281 _("Peg revision must precede end revision"));
1282 err = session->vtable->get_deleted_rev(session, path,
1283 peg_revision,
1284 end_revision,
1285 revision_deleted,
1286 pool);
1287 if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
1288 {
1289 svn_error_clear(err);
1290
1291 /* Do it the slow way, using get-logs, for older servers. */
1292 err = svn_ra__get_deleted_rev_from_log(session, path, peg_revision,
1293 end_revision, revision_deleted,
1294 pool);
1295 }
1296 return err;
1297 }
1298
1299 svn_error_t *
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)1300 svn_ra_get_inherited_props(svn_ra_session_t *session,
1301 apr_array_header_t **iprops,
1302 const char *path,
1303 svn_revnum_t revision,
1304 apr_pool_t *result_pool,
1305 apr_pool_t *scratch_pool)
1306 {
1307 svn_error_t *err;
1308 /* Path must be relative. */
1309 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1310
1311 err = session->vtable->get_inherited_props(session, iprops, path,
1312 revision, result_pool,
1313 scratch_pool);
1314
1315 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
1316 {
1317 svn_error_clear(err);
1318
1319 /* Fallback for legacy servers. */
1320 SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops,
1321 result_pool, scratch_pool));
1322 }
1323 else
1324 SVN_ERR(err);
1325
1326 return SVN_NO_ERROR;
1327 }
1328
1329 svn_error_t *
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)1330 svn_ra__get_commit_ev2(svn_editor_t **editor,
1331 svn_ra_session_t *session,
1332 apr_hash_t *revprop_table,
1333 svn_commit_callback2_t commit_callback,
1334 void *commit_baton,
1335 apr_hash_t *lock_tokens,
1336 svn_boolean_t keep_locks,
1337 svn_ra__provide_base_cb_t provide_base_cb,
1338 svn_ra__provide_props_cb_t provide_props_cb,
1339 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1340 void *cb_baton,
1341 apr_pool_t *result_pool,
1342 apr_pool_t *scratch_pool)
1343 {
1344 if (session->vtable->get_commit_ev2 == NULL)
1345 {
1346 /* The specific RA layer does not have an implementation. Use our
1347 default shim over the normal commit editor. */
1348
1349 return svn_error_trace(svn_ra__use_commit_shim(
1350 editor,
1351 session,
1352 revprop_table,
1353 commit_callback, commit_baton,
1354 lock_tokens,
1355 keep_locks,
1356 provide_base_cb,
1357 provide_props_cb,
1358 get_copysrc_kind_cb,
1359 cb_baton,
1360 session->cancel_func, session->cancel_baton,
1361 result_pool, scratch_pool));
1362 }
1363
1364 /* Note: no need to remap the callback for Ev2. RA layers providing this
1365 vtable entry should completely fill in commit_info. */
1366
1367 return svn_error_trace(session->vtable->get_commit_ev2(
1368 editor,
1369 session,
1370 revprop_table,
1371 commit_callback, commit_baton,
1372 lock_tokens,
1373 keep_locks,
1374 provide_base_cb,
1375 provide_props_cb,
1376 get_copysrc_kind_cb,
1377 cb_baton,
1378 session->cancel_func, session->cancel_baton,
1379 result_pool, scratch_pool));
1380 }
1381
1382
1383 svn_error_t *
svn_ra_print_modules(svn_stringbuf_t * output,apr_pool_t * pool)1384 svn_ra_print_modules(svn_stringbuf_t *output,
1385 apr_pool_t *pool)
1386 {
1387 const struct ra_lib_defn *defn;
1388 const char * const *schemes;
1389 svn_ra__init_func_t initfunc;
1390 const svn_ra__vtable_t *vtable;
1391 apr_pool_t *iterpool = svn_pool_create(pool);
1392
1393 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1394 {
1395 char *line;
1396
1397 svn_pool_clear(iterpool);
1398
1399 initfunc = defn->initfunc;
1400 if (! initfunc)
1401 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
1402 iterpool));
1403
1404 if (initfunc)
1405 {
1406 SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool));
1407
1408 SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name));
1409
1410 /* Note: if you change the formatting of the description,
1411 bear in mind that ra_svn's description has multiple lines when
1412 built with SASL. */
1413 line = apr_psprintf(iterpool, "* ra_%s : %s\n",
1414 defn->ra_name,
1415 vtable->get_description(iterpool));
1416 svn_stringbuf_appendcstr(output, line);
1417
1418 for (schemes = vtable->get_schemes(iterpool); *schemes != NULL;
1419 ++schemes)
1420 {
1421 line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"),
1422 *schemes);
1423 svn_stringbuf_appendcstr(output, line);
1424 }
1425 }
1426 }
1427
1428 svn_pool_destroy(iterpool);
1429
1430 return SVN_NO_ERROR;
1431 }
1432
1433
1434 svn_error_t *
svn_ra_print_ra_libraries(svn_stringbuf_t ** descriptions,void * ra_baton,apr_pool_t * pool)1435 svn_ra_print_ra_libraries(svn_stringbuf_t **descriptions,
1436 void *ra_baton,
1437 apr_pool_t *pool)
1438 {
1439 *descriptions = svn_stringbuf_create_empty(pool);
1440 return svn_ra_print_modules(*descriptions, pool);
1441 }
1442
1443
1444 svn_error_t *
svn_ra__register_editor_shim_callbacks(svn_ra_session_t * session,svn_delta_shim_callbacks_t * callbacks)1445 svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session,
1446 svn_delta_shim_callbacks_t *callbacks)
1447 {
1448 SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks));
1449 return SVN_NO_ERROR;
1450 }
1451
1452
1453 /* Return the library version number. */
1454 const svn_version_t *
svn_ra_version(void)1455 svn_ra_version(void)
1456 {
1457 SVN_VERSION_BODY;
1458 }
1459
1460
1461 /*** Compatibility Interfaces **/
1462 svn_error_t *
svn_ra_init_ra_libs(void ** ra_baton,apr_pool_t * pool)1463 svn_ra_init_ra_libs(void **ra_baton,
1464 apr_pool_t *pool)
1465 {
1466 *ra_baton = pool;
1467 return SVN_NO_ERROR;
1468 }
1469
1470 svn_error_t *
svn_ra_get_ra_library(svn_ra_plugin_t ** library,void * ra_baton,const char * url,apr_pool_t * pool)1471 svn_ra_get_ra_library(svn_ra_plugin_t **library,
1472 void *ra_baton,
1473 const char *url,
1474 apr_pool_t *pool)
1475 {
1476 const struct ra_lib_defn *defn;
1477 apr_pool_t *load_pool = ra_baton;
1478 apr_hash_t *ht = apr_hash_make(pool);
1479
1480 /* Figure out which RA library key matches URL. */
1481 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1482 {
1483 const char *scheme;
1484 if ((scheme = has_scheme_of(defn->schemes, url)))
1485 {
1486 svn_ra_init_func_t compat_initfunc = defn->compat_initfunc;
1487
1488 if (! compat_initfunc)
1489 {
1490 SVN_ERR(load_ra_module
1491 (NULL, &compat_initfunc, defn->ra_name, load_pool));
1492 }
1493 if (! compat_initfunc)
1494 {
1495 continue;
1496 }
1497
1498 SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht));
1499
1500 *library = svn_hash_gets(ht, scheme);
1501
1502 /* The library may support just a subset of the schemes listed,
1503 so we have to check here too. */
1504 if (! *library)
1505 break;
1506
1507 return check_ra_version((*library)->get_version(), scheme);
1508 }
1509 }
1510
1511 /* Couldn't find a match... */
1512 *library = NULL;
1513 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
1514 _("Unrecognized URL scheme '%s'"), url);
1515 }
1516
1517 /* For each libsvn_ra_foo library that is not linked in, provide a default
1518 implementation for svn_ra_foo_init which returns a "not implemented"
1519 error. */
1520
1521 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_NEON
1522 svn_error_t *
svn_ra_dav_init(int abi_version,apr_pool_t * pool,apr_hash_t * hash)1523 svn_ra_dav_init(int abi_version,
1524 apr_pool_t *pool,
1525 apr_hash_t *hash)
1526 {
1527 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1528 }
1529 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_NEON */
1530
1531 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SVN
1532 svn_error_t *
svn_ra_svn_init(int abi_version,apr_pool_t * pool,apr_hash_t * hash)1533 svn_ra_svn_init(int abi_version,
1534 apr_pool_t *pool,
1535 apr_hash_t *hash)
1536 {
1537 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1538 }
1539 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SVN */
1540
1541 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
1542 svn_error_t *
svn_ra_local_init(int abi_version,apr_pool_t * pool,apr_hash_t * hash)1543 svn_ra_local_init(int abi_version,
1544 apr_pool_t *pool,
1545 apr_hash_t *hash)
1546 {
1547 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1548 }
1549 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL */
1550
1551 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
1552 svn_error_t *
svn_ra_serf_init(int abi_version,apr_pool_t * pool,apr_hash_t * hash)1553 svn_ra_serf_init(int abi_version,
1554 apr_pool_t *pool,
1555 apr_hash_t *hash)
1556 {
1557 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1558 }
1559 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SERF */
1560