2 * ra_loader.c: logic for loading different RA library implementations
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
13 * http://www.apache.org/licenses/LICENSE-2.0
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
21 * ====================================================================
24 /* ==================================================================== */
27 #define APR_WANT_STRFUNC
31 #include <apr_strings.h>
32 #include <apr_pools.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"
47 #include "svn_props.h"
48 #include "svn_sorts.h"
50 #include "svn_config.h"
51 #include "ra_loader.h"
52 #include "deprecated.h"
54 #include "private/svn_ra_private.h"
55 #include "svn_private_config.h"
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 };
69 static const struct ra_lib_defn {
70 /* the name of this RA library (e.g. "neon" or "local") */
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;
81 #ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SVN
83 svn_ra_svn__deprecated_init
90 #ifdef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
92 svn_ra_local__deprecated_init
99 #ifdef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
101 svn_ra_serf__deprecated_init
105 /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */
111 /* Ensure that the RA library NAME is loaded.
113 * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init
114 * function of the library.
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.
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).
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)
135 #if defined(SVN_USE_DSO) && APR_HAS_DSO
137 apr_dso_handle_t *dso;
138 apr_dso_handle_sym_t symbol;
140 const char *funcname;
141 const char *compat_funcname;
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);
149 /* find/load the specified library */
150 SVN_ERR(svn_dso_load(&dso, libname));
154 /* find the initialization routines */
157 status = apr_dso_sym(&symbol, dso, funcname);
160 return svn_error_wrap_apr(status,
161 _("'%s' does not define '%s()'"),
165 *func = (svn_ra__init_func_t) symbol;
170 status = apr_dso_sym(&symbol, dso, compat_funcname);
173 return svn_error_wrap_apr(status,
174 _("'%s' does not define '%s()'"),
175 libname, compat_funcname);
178 *compat_func = (svn_ra_init_func_t) symbol;
181 #endif /* APR_HAS_DSO */
186 /* If SCHEMES contains URL, return the scheme. Else, return NULL. */
188 has_scheme_of(const char * const *schemes, const char *url)
192 for ( ; *schemes != NULL; ++schemes)
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] == '+'))
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. */
210 check_ra_version(const svn_version_t *ra_version, const char *scheme)
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':"
217 " expected %d.%d.%d%s"),
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);
227 /* -------------------------------------------------------------- */
229 /*** Public Interfaces ***/
231 svn_error_t *svn_ra_initialize(apr_pool_t *pool)
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
245 svn_ra_create_callbacks(svn_ra_callbacks2_t **callbacks,
248 *callbacks = apr_pcalloc(pool, sizeof(**callbacks));
252 svn_error_t *svn_ra_open4(svn_ra_session_t **session_p,
253 const char **corrected_url_p,
254 const char *repos_URL,
256 const svn_ra_callbacks2_t *callbacks,
257 void *callback_baton,
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;
268 apr_status_t apr_err;
269 #ifdef CHOOSABLE_DAV_MODULE
270 const char *http_library = DEFAULT_HTTP_LIBRARY;
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;
282 /* Initialize the return variable. */
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'"),
295 if (callbacks->auth_baton)
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.
302 * Note that we can only catch the case where users explicitly set
303 * "store-passwords = no" or 'store-auth-creds = no".
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.
311 if (svn_auth_get_parameter(callbacks->auth_baton,
312 SVN_AUTH_PARAM_DONT_STORE_PASSWORDS) != NULL)
313 store_passwords = FALSE;
315 if (svn_auth_get_parameter(callbacks->auth_baton,
316 SVN_AUTH_PARAM_NO_AUTH_CACHE) != NULL)
317 store_auth_creds = FALSE;
322 /* Grab the 'servers' config. */
323 servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS);
326 /* First, look in the global section. */
328 SVN_ERR(svn_config_get_bool
329 (servers, &store_passwords, SVN_CONFIG_SECTION_GLOBAL,
330 SVN_CONFIG_OPTION_STORE_PASSWORDS,
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));
338 SVN_ERR(svn_config_get_bool
339 (servers, &store_pp, SVN_CONFIG_SECTION_GLOBAL,
340 SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
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));
349 SVN_ERR(svn_config_get_bool
350 (servers, &store_auth_creds, SVN_CONFIG_SECTION_GLOBAL,
351 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
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,
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,
366 SVN_CONFIG_OPTION_STORE_AUTH_CREDS,
369 SVN_ERR(svn_config_get_bool(servers, &store_passwords,
371 SVN_CONFIG_OPTION_STORE_PASSWORDS,
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));
379 SVN_ERR(svn_config_get_bool
381 server_group, SVN_CONFIG_OPTION_STORE_SSL_CLIENT_CERT_PP,
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));
389 #ifdef CHOOSABLE_DAV_MODULE
390 /* Now, which DAV-based RA method do we want to use today? */
392 = svn_config_get_server_setting(servers,
393 server_group, /* NULL is OK */
394 SVN_CONFIG_OPTION_HTTP_LIBRARY,
395 DEFAULT_HTTP_LIBRARY);
397 if (strcmp(http_library, "serf") != 0)
398 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
399 _("Invalid config: unknown HTTP library "
406 if (callbacks->auth_baton)
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, "");
413 svn_auth_set_parameter(callbacks->auth_baton,
414 SVN_AUTH_PARAM_STORE_PLAINTEXT_PASSWORDS,
415 store_plaintext_passwords);
418 svn_auth_set_parameter(callbacks->auth_baton,
419 SVN_AUTH_PARAM_DONT_STORE_SSL_CLIENT_CERT_PP,
422 svn_auth_set_parameter(callbacks->auth_baton,
423 SVN_AUTH_PARAM_STORE_SSL_CLIENT_CERT_PP_PLAINTEXT,
426 if (! store_auth_creds)
427 svn_auth_set_parameter(callbacks->auth_baton,
428 SVN_AUTH_PARAM_NO_AUTH_CACHE, "");
431 /* Find the library. */
432 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
436 if ((scheme = has_scheme_of(defn->schemes, repos_URL)))
438 svn_ra__init_func_t initfunc = defn->initfunc;
440 #ifdef CHOOSABLE_DAV_MODULE
441 if (defn->schemes == dav_schemes
442 && strcmp(defn->ra_name, http_library) != 0)
447 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
450 /* Library not found. */
453 SVN_ERR(initfunc(svn_ra_version(), &vtable, sesspool));
455 SVN_ERR(check_ra_version(vtable->get_version(), scheme));
457 if (! has_scheme_of(vtable->get_schemes(sesspool), repos_URL))
458 /* Library doesn't support the scheme at runtime. */
467 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
468 _("Unrecognized URL scheme for '%s'"),
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;
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'",
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
488 if (corrected_url_p && corrected_url)
490 if (! svn_path_is_url(corrected_url))
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
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);
502 *corrected_url_p = svn_uri_canonicalize(corrected_url, pool);
503 svn_pool_destroy(sesspool);
507 /* Check the UUID. */
510 const char *repository_uuid;
512 SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
513 if (strcmp(uuid, repository_uuid) != 0)
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);
525 *session_p = session;
529 svn_error_t *svn_ra_reparent(svn_ra_session_t *session,
533 const char *repos_root;
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'"),
543 return session->vtable->reparent(session, url, pool);
546 svn_error_t *svn_ra_get_session_url(svn_ra_session_t *session,
550 return session->vtable->get_session_url(session, url, pool);
553 svn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session,
554 const char **rel_path,
558 const char *sess_url;
560 SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool));
561 *rel_path = svn_uri_skip_ancestor(sess_url, url, pool);
563 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
564 _("'%s' isn't a child of session URL '%s'"),
569 svn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session,
570 const char **rel_path,
574 const char *root_url;
576 SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool));
577 *rel_path = svn_uri_skip_ancestor(root_url, url, pool);
579 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
580 _("'%s' isn't a child of repository root "
586 svn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session,
587 svn_revnum_t *latest_revnum,
590 return session->vtable->get_latest_revnum(session, latest_revnum, pool);
593 svn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session,
594 svn_revnum_t *revision,
598 return session->vtable->get_dated_revision(session, revision, tm, pool);
601 svn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session,
604 const svn_string_t *const *old_value_p,
605 const svn_string_t *value,
608 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
610 /* If an old value was specified, make sure the server supports
614 svn_boolean_t has_atomic_revprops;
616 SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops,
617 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
620 if (!has_atomic_revprops)
621 /* API violation. (Should be an ASSERT, but gstein talked me
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);
630 return session->vtable->change_rev_prop(session, rev, name,
631 old_value_p, value, pool);
634 svn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session,
639 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
640 return session->vtable->rev_proplist(session, rev, props, pool);
643 svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session,
646 svn_string_t **value,
649 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
650 return session->vtable->rev_prop(session, rev, name, value, pool);
655 svn_commit_callback2_t original_callback;
656 void *original_baton;
658 svn_ra_session_t *session;
661 /* Wrapper which populates the repos_root field of the commit_info struct */
663 commit_callback_wrapper(const svn_commit_info_t *commit_info,
667 struct ccw_baton *ccwb = baton;
668 svn_commit_info_t *ci = svn_commit_info_dup(commit_info, pool);
670 SVN_ERR(svn_ra_get_repos_root2(ccwb->session, &ci->repos_root, pool));
672 return ccwb->original_callback(ci, ccwb->original_baton, pool);
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. */
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)
687 if (original_callback == NULL)
690 *callback_baton = NULL;
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));
698 ccwb->session = session;
699 ccwb->original_callback = original_callback;
700 ccwb->original_baton = original_baton;
702 *callback = commit_callback_wrapper;
703 *callback_baton = ccwb;
708 svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
709 const svn_delta_editor_t **editor,
711 apr_hash_t *revprop_table,
712 svn_commit_callback2_t commit_callback,
714 apr_hash_t *lock_tokens,
715 svn_boolean_t keep_locks,
718 remap_commit_callback(&commit_callback, &commit_baton,
719 session, commit_callback, commit_baton,
722 return session->vtable->get_commit_editor(session, editor, edit_baton,
724 commit_callback, commit_baton,
725 lock_tokens, keep_locks, pool);
728 svn_error_t *svn_ra_get_file(svn_ra_session_t *session,
730 svn_revnum_t revision,
731 svn_stream_t *stream,
732 svn_revnum_t *fetched_rev,
736 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
737 return session->vtable->get_file(session, path, revision, stream,
738 fetched_rev, props, 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,
746 svn_revnum_t revision,
747 apr_uint32_t dirent_fields,
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);
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,
766 /* Validate path format. */
767 for (i = 0; i < paths->nelts; i++)
769 const char *path = APR_ARRAY_IDX(paths, i, const char *);
770 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
773 /* Check server Merge Tracking capability. */
774 err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool);
781 return session->vtable->get_mergeinfo(session, catalog, paths,
783 include_descendants, pool);
787 svn_ra_do_update3(svn_ra_session_t *session,
788 const svn_ra_reporter3_t **reporter,
790 svn_revnum_t revision_to_update_to,
791 const char *update_target,
793 svn_boolean_t send_copyfrom_args,
794 svn_boolean_t ignore_ancestry,
795 const svn_delta_editor_t *update_editor,
797 apr_pool_t *result_pool,
798 apr_pool_t *scratch_pool)
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,
807 update_editor, update_baton,
808 result_pool, scratch_pool);
812 svn_ra_do_switch3(svn_ra_session_t *session,
813 const svn_ra_reporter3_t **reporter,
815 svn_revnum_t revision_to_switch_to,
816 const char *switch_target,
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,
823 apr_pool_t *result_pool,
824 apr_pool_t *scratch_pool)
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,
836 result_pool, scratch_pool);
839 svn_error_t *svn_ra_do_status2(svn_ra_session_t *session,
840 const svn_ra_reporter3_t **reporter,
842 const char *status_target,
843 svn_revnum_t revision,
845 const svn_delta_editor_t *status_editor,
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);
857 svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
858 const svn_ra_reporter3_t **reporter,
860 svn_revnum_t revision,
861 const char *diff_target,
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,
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,
880 svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
881 const apr_array_header_t *paths,
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,
896 for (i = 0; i < paths->nelts; i++)
898 const char *path = APR_ARRAY_IDX(paths, i, const char *);
899 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
903 if (include_merged_revisions)
904 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
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);
912 svn_error_t *svn_ra_check_path(svn_ra_session_t *session,
914 svn_revnum_t revision,
915 svn_node_kind_t *kind,
918 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
919 return session->vtable->check_path(session, path, revision, kind, pool);
922 svn_error_t *svn_ra_stat(svn_ra_session_t *session,
924 svn_revnum_t revision,
925 svn_dirent_t **dirent,
928 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
929 return session->vtable->stat(session, path, revision, dirent, pool);
932 svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session,
936 SVN_ERR(session->vtable->get_uuid(session, uuid, pool));
937 *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL;
941 svn_error_t *svn_ra_get_uuid(svn_ra_session_t *session,
945 return session->vtable->get_uuid(session, uuid, pool);
948 svn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session,
952 SVN_ERR(session->vtable->get_repos_root(session, url, pool));
953 *url = *url ? apr_pstrdup(pool, *url) : NULL;
957 svn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session,
961 return session->vtable->get_repos_root(session, url, pool);
964 svn_error_t *svn_ra_get_locations(svn_ra_session_t *session,
965 apr_hash_t **locations,
967 svn_revnum_t peg_revision,
968 const apr_array_header_t *location_revisions,
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))
978 svn_error_clear(err);
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,
989 svn_ra_get_location_segments(svn_ra_session_t *session,
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,
1000 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1001 err = session->vtable->get_location_segments(session, path, peg_revision,
1003 receiver, receiver_baton, pool);
1004 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
1006 svn_error_clear(err);
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,
1012 receiver_baton, pool);
1017 svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session,
1021 svn_boolean_t include_merged_revisions,
1022 svn_file_rev_handler_t handler,
1023 void *handler_baton,
1028 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1030 if (include_merged_revisions)
1031 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
1035 svn_ra__assert_capable_server(session,
1036 SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
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))
1045 svn_error_clear(err);
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);
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,
1062 apr_hash_index_t *hi;
1064 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
1066 const char *path = svn__apr_hash_index_key(hi);
1068 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
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"));
1076 return session->vtable->lock(session, path_revs, comment, steal_lock,
1077 lock_func, lock_baton, 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,
1087 apr_hash_index_t *hi;
1089 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
1091 const char *path = svn__apr_hash_index_key(hi);
1093 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1096 return session->vtable->unlock(session, path_tokens, break_lock,
1097 lock_func, lock_baton, pool);
1100 svn_error_t *svn_ra_get_lock(svn_ra_session_t *session,
1105 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1106 return session->vtable->get_lock(session, lock, path, pool);
1109 svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session,
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);
1123 svn_error_t *svn_ra_get_locks(svn_ra_session_t *session,
1128 return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, 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,
1139 return session->vtable->replay(session, revision, low_water_mark,
1140 text_deltas, editor, edit_baton, 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)
1151 SVN__NOT_IMPLEMENTED();
1154 static svn_error_t *
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,
1163 apr_pool_t *scratch_pool)
1165 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1168 for (rev = start_revision ; rev <= end_revision ; rev++)
1170 const svn_delta_editor_t *editor;
1172 apr_hash_t *rev_props;
1174 svn_pool_clear(iterpool);
1176 SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool));
1178 SVN_ERR(revstart_func(rev, replay_baton,
1179 &editor, &edit_baton,
1182 SVN_ERR(svn_ra_replay(session, rev, low_water_mark,
1183 text_deltas, editor, edit_baton,
1185 SVN_ERR(revfinish_func(rev, replay_baton,
1190 svn_pool_destroy(iterpool);
1192 return SVN_NO_ERROR;
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,
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);
1212 if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED)))
1213 return svn_error_trace(err);
1215 svn_error_clear(err);
1216 return svn_error_trace(replay_range_from_replays(session, start_revision,
1222 replay_baton, 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,
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,
1238 apr_pool_t *scratch_pool)
1240 if (session->vtable->replay_range_ev2 == NULL)
1242 /* The specific RA layer does not have an implementation. Use our
1243 default shim over the normal replay editor. */
1245 /* This will call the Ev1 replay range handler with modified
1247 return svn_error_trace(svn_ra__use_replay_range_shim(
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));
1268 svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
1270 const char *capability,
1273 return session->vtable->has_capability(session, has, capability, pool);
1277 svn_ra_get_deleted_rev(svn_ra_session_t *session,
1279 svn_revnum_t peg_revision,
1280 svn_revnum_t end_revision,
1281 svn_revnum_t *revision_deleted,
1286 /* Path must be relative. */
1287 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
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,
1303 if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
1305 svn_error_clear(err);
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,
1316 svn_ra_get_inherited_props(svn_ra_session_t *session,
1317 apr_array_header_t **iprops,
1319 svn_revnum_t revision,
1320 apr_pool_t *result_pool,
1321 apr_pool_t *scratch_pool)
1323 svn_boolean_t iprop_capable;
1325 /* Path must be relative. */
1326 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1328 SVN_ERR(svn_ra_has_capability(session, &iprop_capable,
1329 SVN_RA_CAPABILITY_INHERITED_PROPS,
1334 SVN_ERR(session->vtable->get_inherited_props(session, iprops, path,
1335 revision, result_pool,
1340 /* Fallback for legacy servers. */
1341 SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops,
1342 result_pool, scratch_pool));
1345 return SVN_NO_ERROR;
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,
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,
1360 apr_pool_t *result_pool,
1361 apr_pool_t *scratch_pool)
1363 if (session->vtable->get_commit_ev2 == NULL)
1365 /* The specific RA layer does not have an implementation. Use our
1366 default shim over the normal commit editor. */
1368 /* Remap for RA layers exposing Ev1. */
1369 remap_commit_callback(&commit_callback, &commit_baton,
1370 session, commit_callback, commit_baton,
1373 return svn_error_trace(svn_ra__use_commit_shim(
1377 commit_callback, commit_baton,
1382 get_copysrc_kind_cb,
1384 session->cancel_func, session->cancel_baton,
1385 result_pool, scratch_pool));
1388 /* Note: no need to remap the callback for Ev2. RA layers providing this
1389 vtable entry should completely fill in commit_info. */
1391 return svn_error_trace(session->vtable->get_commit_ev2(
1395 commit_callback, commit_baton,
1400 get_copysrc_kind_cb,
1402 session->cancel_func, session->cancel_baton,
1403 result_pool, scratch_pool));
1408 svn_ra_print_modules(svn_stringbuf_t *output,
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);
1417 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1421 svn_pool_clear(iterpool);
1423 initfunc = defn->initfunc;
1425 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
1430 SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool));
1432 SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name));
1434 /* Note: if you change the formatting of the description,
1435 bear in mind that ra_svn's description has multiple lines when
1437 line = apr_psprintf(iterpool, "* ra_%s : %s\n",
1439 vtable->get_description(iterpool));
1440 svn_stringbuf_appendcstr(output, line);
1442 for (schemes = vtable->get_schemes(iterpool); *schemes != NULL;
1445 line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"),
1447 svn_stringbuf_appendcstr(output, line);
1452 svn_pool_destroy(iterpool);
1454 return SVN_NO_ERROR;
1459 svn_ra_print_ra_libraries(svn_stringbuf_t **descriptions,
1463 *descriptions = svn_stringbuf_create_empty(pool);
1464 return svn_ra_print_modules(*descriptions, pool);
1469 svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session,
1470 svn_delta_shim_callbacks_t *callbacks)
1472 SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks));
1473 return SVN_NO_ERROR;
1477 /* Return the library version number. */
1478 const svn_version_t *
1479 svn_ra_version(void)
1485 /*** Compatibility Interfaces **/
1487 svn_ra_init_ra_libs(void **ra_baton,
1491 return SVN_NO_ERROR;
1495 svn_ra_get_ra_library(svn_ra_plugin_t **library,
1500 const struct ra_lib_defn *defn;
1501 apr_pool_t *load_pool = ra_baton;
1502 apr_hash_t *ht = apr_hash_make(pool);
1504 /* Figure out which RA library key matches URL. */
1505 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1508 if ((scheme = has_scheme_of(defn->schemes, url)))
1510 svn_ra_init_func_t compat_initfunc = defn->compat_initfunc;
1512 if (! compat_initfunc)
1514 SVN_ERR(load_ra_module
1515 (NULL, &compat_initfunc, defn->ra_name, load_pool));
1517 if (! compat_initfunc)
1522 SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht));
1524 *library = svn_hash_gets(ht, scheme);
1526 /* The library may support just a subset of the schemes listed,
1527 so we have to check here too. */
1531 return check_ra_version((*library)->get_version(), scheme);
1535 /* Couldn't find a match... */
1537 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
1538 _("Unrecognized URL scheme '%s'"), url);
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"
1545 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_NEON
1547 svn_ra_dav_init(int abi_version,
1551 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1553 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_NEON */
1555 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SVN
1557 svn_ra_svn_init(int abi_version,
1561 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1563 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SVN */
1565 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL
1567 svn_ra_local_init(int abi_version,
1571 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1573 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_LOCAL */
1575 #ifndef SVN_LIBSVN_CLIENT_LINKS_RA_SERF
1577 svn_ra_serf_init(int abi_version,
1581 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1583 #endif /* ! SVN_LIBSVN_CLIENT_LINKS_RA_SERF */