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