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"
39 #include "svn_types.h"
40 #include "svn_error.h"
41 #include "svn_error_codes.h"
42 #include "svn_pools.h"
43 #include "svn_delta.h"
48 #include "svn_props.h"
49 #include "svn_sorts.h"
51 #include "svn_config.h"
52 #include "ra_loader.h"
53 #include "deprecated.h"
55 #include "private/svn_auth_private.h"
56 #include "private/svn_ra_private.h"
57 #include "svn_private_config.h"
62 /* These are the URI schemes that the respective libraries *may* support.
63 * The schemes actually supported may be a subset of the schemes listed below.
64 * This can't be determine until the library is loaded.
65 * (Currently, this applies to the https scheme, which is only
66 * available if SSL is supported.) */
67 static const char * const dav_schemes[] = { "http", "https", NULL };
68 static const char * const svn_schemes[] = { "svn", NULL };
69 static const char * const local_schemes[] = { "file", NULL };
71 static const struct ra_lib_defn {
72 /* the name of this RA library (e.g. "neon" or "local") */
75 const char * const *schemes;
76 /* the initialization function if linked in; otherwise, NULL */
77 svn_ra__init_func_t initfunc;
78 svn_ra_init_func_t compat_initfunc;
83 #ifdef SVN_LIBSVN_RA_LINKS_RA_SVN
85 svn_ra_svn__deprecated_init
92 #ifdef SVN_LIBSVN_RA_LINKS_RA_LOCAL
94 svn_ra_local__deprecated_init
101 #ifdef SVN_LIBSVN_RA_LINKS_RA_SERF
103 svn_ra_serf__deprecated_init
107 /* ADD NEW RA IMPLEMENTATIONS HERE (as they're written) */
113 /* Ensure that the RA library NAME is loaded.
115 * If FUNC is non-NULL, set *FUNC to the address of the svn_ra_NAME__init
116 * function of the library.
118 * If COMPAT_FUNC is non-NULL, set *COMPAT_FUNC to the address of the
119 * svn_ra_NAME_init compatibility init function of the library.
121 * ### todo: Any RA libraries implemented from this point forward
122 * ### don't really need an svn_ra_NAME_init compatibility function.
123 * ### Currently, load_ra_module() will error if no such function is
124 * ### found, but it might be more friendly to simply set *COMPAT_FUNC
125 * ### to null (assuming COMPAT_FUNC itself is non-null).
128 load_ra_module(svn_ra__init_func_t *func,
129 svn_ra_init_func_t *compat_func,
130 const char *ra_name, apr_pool_t *pool)
137 #if defined(SVN_USE_DSO) && APR_HAS_DSO
139 apr_dso_handle_t *dso;
140 apr_dso_handle_sym_t symbol;
142 const char *funcname;
143 const char *compat_funcname;
146 libname = apr_psprintf(pool, "libsvn_ra_%s-" SVN_DSO_SUFFIX_FMT,
147 ra_name, SVN_VER_MAJOR, SVN_SOVERSION);
148 funcname = apr_psprintf(pool, "svn_ra_%s__init", ra_name);
149 compat_funcname = apr_psprintf(pool, "svn_ra_%s_init", ra_name);
151 /* find/load the specified library */
152 SVN_ERR(svn_dso_load(&dso, libname));
156 /* find the initialization routines */
159 status = apr_dso_sym(&symbol, dso, funcname);
162 return svn_error_wrap_apr(status,
163 _("'%s' does not define '%s()'"),
167 *func = (svn_ra__init_func_t) symbol;
172 status = apr_dso_sym(&symbol, dso, compat_funcname);
175 return svn_error_wrap_apr(status,
176 _("'%s' does not define '%s()'"),
177 libname, compat_funcname);
180 *compat_func = (svn_ra_init_func_t) symbol;
183 #endif /* APR_HAS_DSO */
188 /* If SCHEMES contains URL, return the scheme. Else, return NULL. */
190 has_scheme_of(const char * const *schemes, const char *url)
194 for ( ; *schemes != NULL; ++schemes)
196 const char *scheme = *schemes;
197 len = strlen(scheme);
198 /* Case-insensitive comparison, per RFC 2396 section 3.1. Allow
199 URL to contain a trailing "+foo" section in the scheme, since
200 that's how we specify tunnel schemes in ra_svn. */
201 if (strncasecmp(scheme, url, len) == 0 &&
202 (url[len] == ':' || url[len] == '+'))
209 /* Return an error if RA_VERSION doesn't match the version of this library.
210 Use SCHEME in the error message to describe the library that was loaded. */
212 check_ra_version(const svn_version_t *ra_version, const char *scheme)
214 const svn_version_t *my_version = svn_ra_version();
215 if (!svn_ver_equal(my_version, ra_version))
216 return svn_error_createf(SVN_ERR_VERSION_MISMATCH, NULL,
217 _("Mismatched RA version for '%s':"
219 " expected %d.%d.%d%s"),
221 my_version->major, my_version->minor,
222 my_version->patch, my_version->tag,
223 ra_version->major, ra_version->minor,
224 ra_version->patch, ra_version->tag);
229 /* -------------------------------------------------------------- */
231 /*** Public Interfaces ***/
233 svn_error_t *svn_ra_initialize(apr_pool_t *pool)
235 #if defined(SVN_USE_DSO) && APR_HAS_DSO
236 /* Ensure that DSO subsystem is initialized early as possible if
237 we're going to use it. */
238 SVN_ERR(svn_dso_initialize2());
243 /* Please note: the implementation of svn_ra_create_callbacks is
244 * duplicated in libsvn_ra/wrapper_template.h:compat_open() . This
245 * duplication is intentional, is there to avoid a circular
246 * dependancy, and is justified in great length in the code of
247 * compat_open() in libsvn_ra/wrapper_template.h. If you modify the
248 * implementation of svn_ra_create_callbacks(), be sure to keep the
249 * code in wrapper_template.h:compat_open() in sync with your
252 svn_ra_create_callbacks(svn_ra_callbacks2_t **callbacks,
255 *callbacks = apr_pcalloc(pool, sizeof(**callbacks));
259 svn_error_t *svn_ra_open5(svn_ra_session_t **session_p,
260 const char **corrected_url_p,
261 const char **redirect_url_p,
262 const char *repos_URL,
264 const svn_ra_callbacks2_t *callbacks,
265 void *callback_baton,
269 apr_pool_t *sesspool = svn_pool_create(pool);
270 apr_pool_t *scratch_pool = svn_pool_create(sesspool);
271 svn_ra_session_t *session;
272 const struct ra_lib_defn *defn;
273 const svn_ra__vtable_t *vtable = NULL;
275 apr_status_t apr_err;
277 #ifdef CHOOSABLE_DAV_MODULE
278 const char *http_library = DEFAULT_HTTP_LIBRARY;
280 svn_auth_baton_t *auth_baton;
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)
296 SVN_ERR(svn_auth__make_session_auth(&auth_baton,
297 callbacks->auth_baton, config,
299 sesspool, scratch_pool));
303 #ifdef CHOOSABLE_DAV_MODULE
306 svn_config_t *servers = NULL;
307 const char *server_group = NULL;
309 /* Grab the 'servers' config. */
310 servers = svn_hash_gets(config, SVN_CONFIG_CATEGORY_SERVERS);
313 /* First, look in the global section. */
315 /* Find out where we're about to connect to, and
316 * try to pick a server group based on the destination. */
317 server_group = svn_config_find_group(servers, repos_URI.hostname,
318 SVN_CONFIG_SECTION_GROUPS,
321 /* Now, which DAV-based RA method do we want to use today? */
323 = svn_config_get_server_setting(servers,
324 server_group, /* NULL is OK */
325 SVN_CONFIG_OPTION_HTTP_LIBRARY,
326 DEFAULT_HTTP_LIBRARY);
328 if (strcmp(http_library, "serf") != 0)
329 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
330 _("Invalid config: unknown HTTP library "
337 /* Find the library. */
338 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
342 if ((scheme = has_scheme_of(defn->schemes, repos_URL)))
344 svn_ra__init_func_t initfunc = defn->initfunc;
346 #ifdef CHOOSABLE_DAV_MODULE
347 if (defn->schemes == dav_schemes
348 && strcmp(defn->ra_name, http_library) != 0)
353 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
356 /* Library not found. */
359 SVN_ERR(initfunc(svn_ra_version(), &vtable, scratch_pool));
361 SVN_ERR(check_ra_version(vtable->get_version(), scheme));
363 if (! has_scheme_of(vtable->get_schemes(scratch_pool), repos_URL))
364 /* Library doesn't support the scheme at runtime. */
373 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
374 _("Unrecognized URL scheme for '%s'"),
377 /* Create the session object. */
378 session = apr_pcalloc(sesspool, sizeof(*session));
379 session->cancel_func = callbacks->cancel_func;
380 session->cancel_baton = callback_baton;
381 session->vtable = vtable;
382 session->pool = sesspool;
384 /* Ask the library to open the session. */
385 err = vtable->open_session(session, corrected_url_p, redirect_url_p,
387 callbacks, callback_baton, auth_baton,
388 config, sesspool, scratch_pool);
392 svn_pool_destroy(sesspool); /* Includes scratch_pool */
393 if (err->apr_err == SVN_ERR_RA_SESSION_URL_MISMATCH)
394 return svn_error_trace(err);
396 return svn_error_createf(
397 SVN_ERR_RA_CANNOT_CREATE_SESSION, err,
398 _("Unable to connect to a repository at URL '%s'"),
402 /* If the session open stuff detected a server-provided URL
403 correction (a 301 or 302 redirect response during the initial
404 OPTIONS request), then kill the session so the caller can decide
406 if (corrected_url_p && *corrected_url_p)
408 /* *session_p = NULL; */
409 *corrected_url_p = apr_pstrdup(pool, *corrected_url_p);
410 if (redirect_url_p && *redirect_url_p)
411 *redirect_url_p = apr_pstrdup(pool, *redirect_url_p);
412 svn_pool_destroy(sesspool); /* Includes scratch_pool */
416 if (vtable->set_svn_ra_open)
417 SVN_ERR(vtable->set_svn_ra_open(session, svn_ra_open5));
419 /* Check the UUID. */
422 const char *repository_uuid;
424 SVN_ERR(vtable->get_uuid(session, &repository_uuid, pool));
425 if (strcmp(uuid, repository_uuid) != 0)
427 /* Duplicate the uuid as it is allocated in sesspool */
428 repository_uuid = apr_pstrdup(pool, repository_uuid);
429 svn_pool_destroy(sesspool); /* includes scratch_pool */
430 return svn_error_createf(SVN_ERR_RA_UUID_MISMATCH, NULL,
431 _("Repository UUID '%s' doesn't match "
432 "expected UUID '%s'"),
433 repository_uuid, uuid);
437 svn_pool_destroy(scratch_pool);
438 *session_p = session;
443 svn_ra__dup_session(svn_ra_session_t **new_session,
444 svn_ra_session_t *old_session,
445 const char *session_url,
446 apr_pool_t *result_pool,
447 apr_pool_t *scratch_pool)
449 svn_ra_session_t *session;
455 /* This verifies in new_session_url is in the repository */
456 SVN_ERR(svn_ra_get_path_relative_to_root(old_session,
462 SVN_ERR(svn_ra_get_session_url(old_session, &session_url, scratch_pool));
464 /* Create the session object. */
465 session = apr_pcalloc(result_pool, sizeof(*session));
466 session->cancel_func = old_session->cancel_func;
467 session->cancel_baton = old_session->cancel_baton;
468 session->vtable = old_session->vtable;
469 session->pool = result_pool;
471 SVN_ERR(old_session->vtable->dup_session(session,
477 if (session->vtable->set_svn_ra_open)
478 SVN_ERR(session->vtable->set_svn_ra_open(session, svn_ra_open5));
480 *new_session = session;
484 svn_error_t *svn_ra_reparent(svn_ra_session_t *session,
488 const char *repos_root;
490 /* Make sure the new URL is in the same repository, so that the
491 implementations don't have to do it. */
492 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root, pool));
493 if (! svn_uri__is_ancestor(repos_root, url))
494 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
495 _("'%s' isn't in the same repository as '%s'"),
498 return session->vtable->reparent(session, url, pool);
501 svn_error_t *svn_ra_get_session_url(svn_ra_session_t *session,
505 return session->vtable->get_session_url(session, url, pool);
508 svn_error_t *svn_ra_get_path_relative_to_session(svn_ra_session_t *session,
509 const char **rel_path,
513 const char *sess_url;
515 SVN_ERR(session->vtable->get_session_url(session, &sess_url, pool));
516 *rel_path = svn_uri_skip_ancestor(sess_url, url, pool);
518 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
519 _("'%s' isn't a child of session URL '%s'"),
524 svn_error_t *svn_ra_get_path_relative_to_root(svn_ra_session_t *session,
525 const char **rel_path,
529 const char *root_url;
531 SVN_ERR(session->vtable->get_repos_root(session, &root_url, pool));
532 *rel_path = svn_uri_skip_ancestor(root_url, url, pool);
534 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
535 _("'%s' isn't a child of repository root "
541 svn_error_t *svn_ra_get_latest_revnum(svn_ra_session_t *session,
542 svn_revnum_t *latest_revnum,
545 return session->vtable->get_latest_revnum(session, latest_revnum, pool);
548 svn_error_t *svn_ra_get_dated_revision(svn_ra_session_t *session,
549 svn_revnum_t *revision,
553 return session->vtable->get_dated_revision(session, revision, tm, pool);
556 svn_error_t *svn_ra_change_rev_prop2(svn_ra_session_t *session,
559 const svn_string_t *const *old_value_p,
560 const svn_string_t *value,
563 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
565 /* If an old value was specified, make sure the server supports
569 svn_boolean_t has_atomic_revprops;
571 SVN_ERR(svn_ra_has_capability(session, &has_atomic_revprops,
572 SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
575 if (!has_atomic_revprops)
576 /* API violation. (Should be an ASSERT, but gstein talked me
578 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
579 _("Specifying 'old_value_p' is not allowed when "
580 "the '%s' capability is not advertised, and "
581 "could indicate a bug in your client"),
582 SVN_RA_CAPABILITY_ATOMIC_REVPROPS);
585 return session->vtable->change_rev_prop(session, rev, name,
586 old_value_p, value, pool);
589 svn_error_t *svn_ra_rev_proplist(svn_ra_session_t *session,
594 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
595 return session->vtable->rev_proplist(session, rev, props, pool);
598 svn_error_t *svn_ra_rev_prop(svn_ra_session_t *session,
601 svn_string_t **value,
604 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(rev));
605 return session->vtable->rev_prop(session, rev, name, value, pool);
608 svn_error_t *svn_ra_get_commit_editor3(svn_ra_session_t *session,
609 const svn_delta_editor_t **editor,
611 apr_hash_t *revprop_table,
612 svn_commit_callback2_t commit_callback,
614 apr_hash_t *lock_tokens,
615 svn_boolean_t keep_locks,
618 return session->vtable->get_commit_editor(session, editor, edit_baton,
620 commit_callback, commit_baton,
621 lock_tokens, keep_locks, pool);
624 svn_error_t *svn_ra_get_file(svn_ra_session_t *session,
626 svn_revnum_t revision,
627 svn_stream_t *stream,
628 svn_revnum_t *fetched_rev,
632 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
633 return session->vtable->get_file(session, path, revision, stream,
634 fetched_rev, props, pool);
637 svn_error_t *svn_ra_get_dir2(svn_ra_session_t *session,
638 apr_hash_t **dirents,
639 svn_revnum_t *fetched_rev,
642 svn_revnum_t revision,
643 apr_uint32_t dirent_fields,
646 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
647 return session->vtable->get_dir(session, dirents, fetched_rev, props,
648 path, revision, dirent_fields, pool);
652 svn_ra_list(svn_ra_session_t *session,
654 svn_revnum_t revision,
655 const apr_array_header_t *patterns,
657 apr_uint32_t dirent_fields,
658 svn_ra_dirent_receiver_t receiver,
659 void *receiver_baton,
660 apr_pool_t *scratch_pool)
662 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
663 if (!session->vtable->list)
664 return svn_error_create(SVN_ERR_UNSUPPORTED_FEATURE, NULL, NULL);
666 SVN_ERR(svn_ra__assert_capable_server(session, SVN_RA_CAPABILITY_LIST,
667 NULL, scratch_pool));
669 return session->vtable->list(session, path, revision, patterns, depth,
670 dirent_fields, receiver, receiver_baton,
674 svn_error_t *svn_ra_get_mergeinfo(svn_ra_session_t *session,
675 svn_mergeinfo_catalog_t *catalog,
676 const apr_array_header_t *paths,
677 svn_revnum_t revision,
678 svn_mergeinfo_inheritance_t inherit,
679 svn_boolean_t include_descendants,
685 /* Validate path format. */
686 for (i = 0; i < paths->nelts; i++)
688 const char *path = APR_ARRAY_IDX(paths, i, const char *);
689 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
692 /* Check server Merge Tracking capability. */
693 err = svn_ra__assert_mergeinfo_capable_server(session, NULL, pool);
700 return session->vtable->get_mergeinfo(session, catalog, paths,
702 include_descendants, pool);
706 svn_ra_do_update3(svn_ra_session_t *session,
707 const svn_ra_reporter3_t **reporter,
709 svn_revnum_t revision_to_update_to,
710 const char *update_target,
712 svn_boolean_t send_copyfrom_args,
713 svn_boolean_t ignore_ancestry,
714 const svn_delta_editor_t *update_editor,
716 apr_pool_t *result_pool,
717 apr_pool_t *scratch_pool)
719 SVN_ERR_ASSERT(svn_path_is_empty(update_target)
720 || svn_path_is_single_path_component(update_target));
721 return session->vtable->do_update(session,
722 reporter, report_baton,
723 revision_to_update_to, update_target,
724 depth, send_copyfrom_args,
726 update_editor, update_baton,
727 result_pool, scratch_pool);
731 svn_ra_do_switch3(svn_ra_session_t *session,
732 const svn_ra_reporter3_t **reporter,
734 svn_revnum_t revision_to_switch_to,
735 const char *switch_target,
737 const char *switch_url,
738 svn_boolean_t send_copyfrom_args,
739 svn_boolean_t ignore_ancestry,
740 const svn_delta_editor_t *switch_editor,
742 apr_pool_t *result_pool,
743 apr_pool_t *scratch_pool)
745 SVN_ERR_ASSERT(svn_path_is_empty(switch_target)
746 || svn_path_is_single_path_component(switch_target));
747 return session->vtable->do_switch(session,
748 reporter, report_baton,
749 revision_to_switch_to, switch_target,
755 result_pool, scratch_pool);
758 svn_error_t *svn_ra_do_status2(svn_ra_session_t *session,
759 const svn_ra_reporter3_t **reporter,
761 const char *status_target,
762 svn_revnum_t revision,
764 const svn_delta_editor_t *status_editor,
768 SVN_ERR_ASSERT(svn_path_is_empty(status_target)
769 || svn_path_is_single_path_component(status_target));
770 return session->vtable->do_status(session,
771 reporter, report_baton,
772 status_target, revision, depth,
773 status_editor, status_baton, pool);
776 svn_error_t *svn_ra_do_diff3(svn_ra_session_t *session,
777 const svn_ra_reporter3_t **reporter,
779 svn_revnum_t revision,
780 const char *diff_target,
782 svn_boolean_t ignore_ancestry,
783 svn_boolean_t text_deltas,
784 const char *versus_url,
785 const svn_delta_editor_t *diff_editor,
789 SVN_ERR_ASSERT(svn_path_is_empty(diff_target)
790 || svn_path_is_single_path_component(diff_target));
791 return session->vtable->do_diff(session,
792 reporter, report_baton,
793 revision, diff_target,
794 depth, ignore_ancestry,
795 text_deltas, versus_url, diff_editor,
799 svn_error_t *svn_ra_get_log2(svn_ra_session_t *session,
800 const apr_array_header_t *paths,
804 svn_boolean_t discover_changed_paths,
805 svn_boolean_t strict_node_history,
806 svn_boolean_t include_merged_revisions,
807 const apr_array_header_t *revprops,
808 svn_log_entry_receiver_t receiver,
809 void *receiver_baton,
815 for (i = 0; i < paths->nelts; i++)
817 const char *path = APR_ARRAY_IDX(paths, i, const char *);
818 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
822 if (include_merged_revisions)
823 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
825 return session->vtable->get_log(session, paths, start, end, limit,
826 discover_changed_paths, strict_node_history,
827 include_merged_revisions, revprops,
828 receiver, receiver_baton, pool);
831 svn_error_t *svn_ra_check_path(svn_ra_session_t *session,
833 svn_revnum_t revision,
834 svn_node_kind_t *kind,
837 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
838 return session->vtable->check_path(session, path, revision, kind, pool);
841 svn_error_t *svn_ra_stat(svn_ra_session_t *session,
843 svn_revnum_t revision,
844 svn_dirent_t **dirent,
848 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
849 err = session->vtable->stat(session, path, revision, dirent, pool);
851 /* svnserve before 1.2 doesn't support the above, so fall back on
852 a far less efficient, but still correct method. */
853 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
855 /* ### TODO: Find out if we can somehow move this code in libsvn_ra_svn.
857 apr_pool_t *scratch_pool = svn_pool_create(pool);
858 svn_node_kind_t kind;
860 svn_error_clear(err);
862 SVN_ERR(svn_ra_check_path(session, path, revision, &kind, scratch_pool));
864 if (kind != svn_node_none)
866 const char *repos_root_url;
867 const char *session_url;
869 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url,
871 SVN_ERR(svn_ra_get_session_url(session, &session_url,
874 if (!svn_path_is_empty(path))
875 session_url = svn_path_url_add_component2(session_url, path,
878 if (strcmp(session_url, repos_root_url) != 0)
880 svn_ra_session_t *parent_session;
881 apr_hash_t *parent_ents;
882 const char *parent_url, *base_name;
884 /* Open another session to the path's parent. This server
885 doesn't support svn_ra_reparent anyway, so don't try it. */
886 svn_uri_split(&parent_url, &base_name, session_url,
889 SVN_ERR(svn_ra__dup_session(&parent_session, session, parent_url,
890 scratch_pool, scratch_pool));
892 /* Get all parent's entries, no props. */
893 SVN_ERR(svn_ra_get_dir2(parent_session, &parent_ents, NULL,
894 NULL, "", revision, SVN_DIRENT_ALL,
897 /* Get the relevant entry. */
898 *dirent = svn_hash_gets(parent_ents, base_name);
901 *dirent = svn_dirent_dup(*dirent, pool);
906 const svn_string_t *val;
908 /* We can't get the directory entry for the repository root,
909 but we can still get the information we want.
910 The created-rev of the repository root must, by definition,
912 *dirent = apr_pcalloc(pool, sizeof(**dirent));
913 (*dirent)->kind = kind;
914 (*dirent)->size = SVN_INVALID_FILESIZE;
916 SVN_ERR(svn_ra_get_dir2(session, NULL, NULL, &props,
917 "", revision, 0 /* no dirent fields */,
919 (*dirent)->has_props = (apr_hash_count(props) != 0);
921 (*dirent)->created_rev = revision;
923 SVN_ERR(svn_ra_rev_proplist(session, revision, &props,
926 val = svn_hash_gets(props, SVN_PROP_REVISION_DATE);
928 SVN_ERR(svn_time_from_cstring(&(*dirent)->time, val->data,
931 val = svn_hash_gets(props, SVN_PROP_REVISION_AUTHOR);
932 (*dirent)->last_author = val ? apr_pstrdup(pool, val->data)
939 svn_pool_clear(scratch_pool);
947 svn_error_t *svn_ra_get_uuid2(svn_ra_session_t *session,
951 SVN_ERR(session->vtable->get_uuid(session, uuid, pool));
952 *uuid = *uuid ? apr_pstrdup(pool, *uuid) : NULL;
956 svn_error_t *svn_ra_get_uuid(svn_ra_session_t *session,
960 return session->vtable->get_uuid(session, uuid, pool);
963 svn_error_t *svn_ra_get_repos_root2(svn_ra_session_t *session,
967 SVN_ERR(session->vtable->get_repos_root(session, url, pool));
968 *url = *url ? apr_pstrdup(pool, *url) : NULL;
972 svn_error_t *svn_ra_get_repos_root(svn_ra_session_t *session,
976 return session->vtable->get_repos_root(session, url, pool);
979 svn_error_t *svn_ra_get_locations(svn_ra_session_t *session,
980 apr_hash_t **locations,
982 svn_revnum_t peg_revision,
983 const apr_array_header_t *location_revisions,
988 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(peg_revision));
989 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
990 err = session->vtable->get_locations(session, locations, path,
991 peg_revision, location_revisions, pool);
992 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
994 svn_error_clear(err);
996 /* Do it the slow way, using get-logs, for older servers. */
997 err = svn_ra__locations_from_log(session, locations, path,
998 peg_revision, location_revisions,
1005 svn_ra_get_location_segments(svn_ra_session_t *session,
1007 svn_revnum_t peg_revision,
1008 svn_revnum_t start_rev,
1009 svn_revnum_t end_rev,
1010 svn_location_segment_receiver_t receiver,
1011 void *receiver_baton,
1016 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1017 err = session->vtable->get_location_segments(session, path, peg_revision,
1019 receiver, receiver_baton, pool);
1020 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED))
1022 svn_error_clear(err);
1024 /* Do it the slow way, using get-logs, for older servers. */
1025 err = svn_ra__location_segments_from_log(session, path,
1026 peg_revision, start_rev,
1028 receiver_baton, pool);
1033 svn_error_t *svn_ra_get_file_revs2(svn_ra_session_t *session,
1037 svn_boolean_t include_merged_revisions,
1038 svn_file_rev_handler_t handler,
1039 void *handler_baton,
1044 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1046 if (include_merged_revisions)
1047 SVN_ERR(svn_ra__assert_mergeinfo_capable_server(session, NULL, pool));
1049 if (start > end || !SVN_IS_VALID_REVNUM(start))
1051 svn_ra__assert_capable_server(session,
1052 SVN_RA_CAPABILITY_GET_FILE_REVS_REVERSE,
1056 err = session->vtable->get_file_revs(session, path, start, end,
1057 include_merged_revisions,
1058 handler, handler_baton, pool);
1059 if (err && (err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
1060 && !include_merged_revisions)
1062 svn_error_clear(err);
1064 /* Do it the slow way, using get-logs, for older servers. */
1065 err = svn_ra__file_revs_from_log(session, path, start, end,
1066 handler, handler_baton, pool);
1068 return svn_error_trace(err);
1071 svn_error_t *svn_ra_lock(svn_ra_session_t *session,
1072 apr_hash_t *path_revs,
1073 const char *comment,
1074 svn_boolean_t steal_lock,
1075 svn_ra_lock_callback_t lock_func,
1079 apr_hash_index_t *hi;
1081 for (hi = apr_hash_first(pool, path_revs); hi; hi = apr_hash_next(hi))
1083 const char *path = apr_hash_this_key(hi);
1085 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1088 if (comment && ! svn_xml_is_xml_safe(comment, strlen(comment)))
1089 return svn_error_create
1090 (SVN_ERR_XML_UNESCAPABLE_DATA, NULL,
1091 _("Lock comment contains illegal characters"));
1093 return session->vtable->lock(session, path_revs, comment, steal_lock,
1094 lock_func, lock_baton, pool);
1097 svn_error_t *svn_ra_unlock(svn_ra_session_t *session,
1098 apr_hash_t *path_tokens,
1099 svn_boolean_t break_lock,
1100 svn_ra_lock_callback_t lock_func,
1104 apr_hash_index_t *hi;
1106 for (hi = apr_hash_first(pool, path_tokens); hi; hi = apr_hash_next(hi))
1108 const char *path = apr_hash_this_key(hi);
1110 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1113 return session->vtable->unlock(session, path_tokens, break_lock,
1114 lock_func, lock_baton, pool);
1117 svn_error_t *svn_ra_get_lock(svn_ra_session_t *session,
1122 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1123 return session->vtable->get_lock(session, lock, path, pool);
1126 svn_error_t *svn_ra_get_locks2(svn_ra_session_t *session,
1132 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1133 SVN_ERR_ASSERT((depth == svn_depth_empty) ||
1134 (depth == svn_depth_files) ||
1135 (depth == svn_depth_immediates) ||
1136 (depth == svn_depth_infinity));
1137 return session->vtable->get_locks(session, locks, path, depth, pool);
1140 svn_error_t *svn_ra_get_locks(svn_ra_session_t *session,
1145 return svn_ra_get_locks2(session, locks, path, svn_depth_infinity, pool);
1148 svn_error_t *svn_ra_replay(svn_ra_session_t *session,
1149 svn_revnum_t revision,
1150 svn_revnum_t low_water_mark,
1151 svn_boolean_t text_deltas,
1152 const svn_delta_editor_t *editor,
1156 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision)
1157 && SVN_IS_VALID_REVNUM(low_water_mark));
1158 return session->vtable->replay(session, revision, low_water_mark,
1159 text_deltas, editor, edit_baton, pool);
1163 svn_ra__replay_ev2(svn_ra_session_t *session,
1164 svn_revnum_t revision,
1165 svn_revnum_t low_water_mark,
1166 svn_boolean_t send_deltas,
1167 svn_editor_t *editor,
1168 apr_pool_t *scratch_pool)
1170 SVN__NOT_IMPLEMENTED();
1173 static svn_error_t *
1174 replay_range_from_replays(svn_ra_session_t *session,
1175 svn_revnum_t start_revision,
1176 svn_revnum_t end_revision,
1177 svn_revnum_t low_water_mark,
1178 svn_boolean_t text_deltas,
1179 svn_ra_replay_revstart_callback_t revstart_func,
1180 svn_ra_replay_revfinish_callback_t revfinish_func,
1182 apr_pool_t *scratch_pool)
1184 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
1187 for (rev = start_revision ; rev <= end_revision ; rev++)
1189 const svn_delta_editor_t *editor;
1191 apr_hash_t *rev_props;
1193 svn_pool_clear(iterpool);
1195 SVN_ERR(svn_ra_rev_proplist(session, rev, &rev_props, iterpool));
1197 SVN_ERR(revstart_func(rev, replay_baton,
1198 &editor, &edit_baton,
1201 SVN_ERR(svn_ra_replay(session, rev, low_water_mark,
1202 text_deltas, editor, edit_baton,
1204 SVN_ERR(revfinish_func(rev, replay_baton,
1209 svn_pool_destroy(iterpool);
1211 return SVN_NO_ERROR;
1215 svn_ra_replay_range(svn_ra_session_t *session,
1216 svn_revnum_t start_revision,
1217 svn_revnum_t end_revision,
1218 svn_revnum_t low_water_mark,
1219 svn_boolean_t text_deltas,
1220 svn_ra_replay_revstart_callback_t revstart_func,
1221 svn_ra_replay_revfinish_callback_t revfinish_func,
1227 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(start_revision)
1228 && SVN_IS_VALID_REVNUM(end_revision)
1229 && start_revision <= end_revision
1230 && SVN_IS_VALID_REVNUM(low_water_mark));
1233 session->vtable->replay_range(session, start_revision, end_revision,
1234 low_water_mark, text_deltas,
1235 revstart_func, revfinish_func,
1236 replay_baton, pool);
1238 if (!err || (err && (err->apr_err != SVN_ERR_RA_NOT_IMPLEMENTED)))
1239 return svn_error_trace(err);
1241 svn_error_clear(err);
1242 return svn_error_trace(replay_range_from_replays(session, start_revision,
1248 replay_baton, pool));
1252 svn_ra__replay_range_ev2(svn_ra_session_t *session,
1253 svn_revnum_t start_revision,
1254 svn_revnum_t end_revision,
1255 svn_revnum_t low_water_mark,
1256 svn_boolean_t send_deltas,
1257 svn_ra__replay_revstart_ev2_callback_t revstart_func,
1258 svn_ra__replay_revfinish_ev2_callback_t revfinish_func,
1260 svn_ra__provide_base_cb_t provide_base_cb,
1261 svn_ra__provide_props_cb_t provide_props_cb,
1262 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1264 apr_pool_t *scratch_pool)
1266 if (session->vtable->replay_range_ev2 == NULL)
1268 /* The specific RA layer does not have an implementation. Use our
1269 default shim over the normal replay editor. */
1271 /* This will call the Ev1 replay range handler with modified
1273 return svn_error_trace(svn_ra__use_replay_range_shim(
1288 return svn_error_trace(session->vtable->replay_range_ev2(
1289 session, start_revision, end_revision,
1290 low_water_mark, send_deltas, revstart_func,
1291 revfinish_func, replay_baton, scratch_pool));
1294 svn_error_t *svn_ra_has_capability(svn_ra_session_t *session,
1296 const char *capability,
1299 return session->vtable->has_capability(session, has, capability, pool);
1303 svn_ra_get_deleted_rev(svn_ra_session_t *session,
1305 svn_revnum_t peg_revision,
1306 svn_revnum_t end_revision,
1307 svn_revnum_t *revision_deleted,
1312 /* Path must be relative. */
1313 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1315 if (!SVN_IS_VALID_REVNUM(peg_revision))
1316 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1317 _("Invalid peg revision %ld"), peg_revision);
1318 if (!SVN_IS_VALID_REVNUM(end_revision))
1319 return svn_error_createf(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1320 _("Invalid end revision %ld"), end_revision);
1321 if (end_revision <= peg_revision)
1322 return svn_error_create(SVN_ERR_CLIENT_BAD_REVISION, NULL,
1323 _("Peg revision must precede end revision"));
1324 err = session->vtable->get_deleted_rev(session, path,
1329 if (err && (err->apr_err == SVN_ERR_UNSUPPORTED_FEATURE))
1331 svn_error_clear(err);
1333 /* Do it the slow way, using get-logs, for older servers. */
1334 err = svn_ra__get_deleted_rev_from_log(session, path, peg_revision,
1335 end_revision, revision_deleted,
1342 svn_ra_get_inherited_props(svn_ra_session_t *session,
1343 apr_array_header_t **iprops,
1345 svn_revnum_t revision,
1346 apr_pool_t *result_pool,
1347 apr_pool_t *scratch_pool)
1350 /* Path must be relative. */
1351 SVN_ERR_ASSERT(svn_relpath_is_canonical(path));
1353 err = session->vtable->get_inherited_props(session, iprops, path,
1354 revision, result_pool,
1357 if (err && err->apr_err == SVN_ERR_RA_NOT_IMPLEMENTED)
1359 svn_error_clear(err);
1361 /* Fallback for legacy servers. */
1362 SVN_ERR(svn_ra__get_inherited_props_walk(session, path, revision, iprops,
1363 result_pool, scratch_pool));
1368 return SVN_NO_ERROR;
1372 svn_ra__get_commit_ev2(svn_editor_t **editor,
1373 svn_ra_session_t *session,
1374 apr_hash_t *revprop_table,
1375 svn_commit_callback2_t commit_callback,
1377 apr_hash_t *lock_tokens,
1378 svn_boolean_t keep_locks,
1379 svn_ra__provide_base_cb_t provide_base_cb,
1380 svn_ra__provide_props_cb_t provide_props_cb,
1381 svn_ra__get_copysrc_kind_cb_t get_copysrc_kind_cb,
1383 apr_pool_t *result_pool,
1384 apr_pool_t *scratch_pool)
1386 if (session->vtable->get_commit_ev2 == NULL)
1388 /* The specific RA layer does not have an implementation. Use our
1389 default shim over the normal commit editor. */
1391 return svn_error_trace(svn_ra__use_commit_shim(
1395 commit_callback, commit_baton,
1400 get_copysrc_kind_cb,
1402 session->cancel_func, session->cancel_baton,
1403 result_pool, scratch_pool));
1406 /* Note: no need to remap the callback for Ev2. RA layers providing this
1407 vtable entry should completely fill in commit_info. */
1409 return svn_error_trace(session->vtable->get_commit_ev2(
1413 commit_callback, commit_baton,
1418 get_copysrc_kind_cb,
1420 session->cancel_func, session->cancel_baton,
1421 result_pool, scratch_pool));
1426 svn_ra_print_modules(svn_stringbuf_t *output,
1429 const struct ra_lib_defn *defn;
1430 const char * const *schemes;
1431 svn_ra__init_func_t initfunc;
1432 const svn_ra__vtable_t *vtable;
1433 apr_pool_t *iterpool = svn_pool_create(pool);
1435 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1439 svn_pool_clear(iterpool);
1441 initfunc = defn->initfunc;
1443 SVN_ERR(load_ra_module(&initfunc, NULL, defn->ra_name,
1448 SVN_ERR(initfunc(svn_ra_version(), &vtable, iterpool));
1450 SVN_ERR(check_ra_version(vtable->get_version(), defn->ra_name));
1452 /* Note: if you change the formatting of the description,
1453 bear in mind that ra_svn's description has multiple lines when
1455 line = apr_psprintf(iterpool, "* ra_%s : %s\n",
1457 vtable->get_description(iterpool));
1458 svn_stringbuf_appendcstr(output, line);
1460 for (schemes = vtable->get_schemes(iterpool); *schemes != NULL;
1463 line = apr_psprintf(iterpool, _(" - handles '%s' scheme\n"),
1465 svn_stringbuf_appendcstr(output, line);
1470 svn_pool_destroy(iterpool);
1472 return SVN_NO_ERROR;
1477 svn_ra_print_ra_libraries(svn_stringbuf_t **descriptions,
1481 *descriptions = svn_stringbuf_create_empty(pool);
1482 return svn_ra_print_modules(*descriptions, pool);
1487 svn_ra__register_editor_shim_callbacks(svn_ra_session_t *session,
1488 svn_delta_shim_callbacks_t *callbacks)
1490 SVN_ERR(session->vtable->register_editor_shim_callbacks(session, callbacks));
1491 return SVN_NO_ERROR;
1495 /* Return the library version number. */
1496 const svn_version_t *
1497 svn_ra_version(void)
1503 /*** Compatibility Interfaces **/
1505 svn_ra_init_ra_libs(void **ra_baton,
1509 return SVN_NO_ERROR;
1513 svn_ra_get_ra_library(svn_ra_plugin_t **library,
1518 const struct ra_lib_defn *defn;
1519 apr_pool_t *load_pool = ra_baton;
1520 apr_hash_t *ht = apr_hash_make(pool);
1522 /* Figure out which RA library key matches URL. */
1523 for (defn = ra_libraries; defn->ra_name != NULL; ++defn)
1526 if ((scheme = has_scheme_of(defn->schemes, url)))
1528 svn_ra_init_func_t compat_initfunc = defn->compat_initfunc;
1530 if (! compat_initfunc)
1532 SVN_ERR(load_ra_module
1533 (NULL, &compat_initfunc, defn->ra_name, load_pool));
1535 if (! compat_initfunc)
1540 SVN_ERR(compat_initfunc(SVN_RA_ABI_VERSION, load_pool, ht));
1542 *library = svn_hash_gets(ht, scheme);
1544 /* The library may support just a subset of the schemes listed,
1545 so we have to check here too. */
1549 return check_ra_version((*library)->get_version(), scheme);
1553 /* Couldn't find a match... */
1555 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL,
1556 _("Unrecognized URL scheme '%s'"), url);
1559 /* For each libsvn_ra_foo library that is not linked in, provide a default
1560 implementation for svn_ra_foo_init which returns a "not implemented"
1563 #ifndef SVN_LIBSVN_RA_LINKS_RA_NEON
1565 svn_ra_dav_init(int abi_version,
1569 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1571 #endif /* ! SVN_LIBSVN_RA_LINKS_RA_NEON */
1573 #ifndef SVN_LIBSVN_RA_LINKS_RA_SVN
1575 svn_ra_svn_init(int abi_version,
1579 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1581 #endif /* ! SVN_LIBSVN_RA_LINKS_RA_SVN */
1583 #ifndef SVN_LIBSVN_RA_LINKS_RA_LOCAL
1585 svn_ra_local_init(int abi_version,
1589 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1591 #endif /* ! SVN_LIBSVN_RA_LINKS_RA_LOCAL */
1593 #ifndef SVN_LIBSVN_RA_LINKS_RA_SERF
1595 svn_ra_serf_init(int abi_version,
1599 return svn_error_create(SVN_ERR_RA_NOT_IMPLEMENTED, NULL, NULL);
1601 #endif /* ! SVN_LIBSVN_RA_LINKS_RA_SERF */