diff options
Diffstat (limited to 'subversion/libsvn_ra_serf/serf.c')
-rw-r--r-- | subversion/libsvn_ra_serf/serf.c | 883 |
1 files changed, 335 insertions, 548 deletions
diff --git a/subversion/libsvn_ra_serf/serf.c b/subversion/libsvn_ra_serf/serf.c index 66f9962..3c47d5e 100644 --- a/subversion/libsvn_ra_serf/serf.c +++ b/subversion/libsvn_ra_serf/serf.c @@ -39,6 +39,7 @@ #include "svn_dirent_uri.h" #include "svn_hash.h" #include "svn_path.h" +#include "svn_props.h" #include "svn_time.h" #include "svn_version.h" @@ -63,7 +64,7 @@ ra_serf_version(void) #define RA_SERF_DESCRIPTION_VER \ N_("Module for accessing a repository via WebDAV protocol using serf.\n" \ - " - using serf %d.%d.%d") + " - using serf %d.%d.%d (compiled with %d.%d.%d)") /* Implements svn_ra__vtable_t.get_description(). */ static const char * @@ -72,7 +73,12 @@ ra_serf_get_description(apr_pool_t *pool) int major, minor, patch; serf_lib_version(&major, &minor, &patch); - return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch); + return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), + major, minor, patch, + SERF_MAJOR_VERSION, + SERF_MINOR_VERSION, + SERF_PATCH_VERSION + ); } /* Implements svn_ra__vtable_t.get_schemes(). */ @@ -144,10 +150,6 @@ load_http_auth_types(apr_pool_t *pool, svn_config_t *config, runtime configuration variable. */ #define DEFAULT_HTTP_TIMEOUT 600 -/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */ -#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests" - - static svn_error_t * load_config(svn_ra_serf__session_t *session, apr_hash_t *config_hash, @@ -161,6 +163,10 @@ load_config(svn_ra_serf__session_t *session, const char *exceptions; apr_port_t proxy_port; svn_tristate_t chunked_requests; +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + apr_int64_t log_components; + apr_int64_t log_level; +#endif if (config_hash) { @@ -179,17 +185,17 @@ load_config(svn_ra_serf__session_t *session, svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL, SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL); - if (session->wc_callbacks->auth_baton) + if (session->auth_baton) { if (config_client) { - svn_auth_set_parameter(session->wc_callbacks->auth_baton, + svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, config_client); } if (config) { - svn_auth_set_parameter(session->wc_callbacks->auth_baton, + svn_auth_set_parameter(session->auth_baton, SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, config); } @@ -237,18 +243,25 @@ load_config(svn_ra_serf__session_t *session, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); - /* Should we use chunked transfer encoding. */ + /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, SVN_CONFIG_SECTION_GLOBAL, - OPTION_HTTP_CHUNKED_REQUESTS, + SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", svn_tristate_unknown)); - if (config) - server_group = svn_config_find_group(config, - session->session_url.hostname, - SVN_CONFIG_SECTION_GROUPS, pool); - else - server_group = NULL; +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + SVN_ERR(svn_config_get_int64(config, &log_components, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, + SERF_LOGCOMP_NONE)); + SVN_ERR(svn_config_get_int64(config, &log_level, + SVN_CONFIG_SECTION_GLOBAL, + SVN_CONFIG_OPTION_SERF_LOG_LEVEL, + SERF_LOG_INFO)); +#endif + + server_group = svn_auth_get_parameter(session->auth_baton, + SVN_AUTH_PARAM_SERVER_GROUP); if (server_group) { @@ -259,9 +272,6 @@ load_config(svn_ra_serf__session_t *session, svn_config_get(config, &timeout_str, server_group, SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str); - svn_auth_set_parameter(session->wc_callbacks->auth_baton, - SVN_AUTH_PARAM_SERVER_GROUP, server_group); - /* Load the group proxy server settings, overriding global settings. We intentionally ignore 'http-proxy-exceptions' here because, well, if this site was an exception, why is @@ -300,12 +310,42 @@ load_config(svn_ra_serf__session_t *session, SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, session->max_connections)); - /* Should we use chunked transfer encoding. */ + /* Should we use chunked transfer encoding. */ SVN_ERR(svn_config_get_tristate(config, &chunked_requests, server_group, - OPTION_HTTP_CHUNKED_REQUESTS, + SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS, "auto", chunked_requests)); + +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + SVN_ERR(svn_config_get_int64(config, &log_components, + server_group, + SVN_CONFIG_OPTION_SERF_LOG_COMPONENTS, + log_components)); + SVN_ERR(svn_config_get_int64(config, &log_level, + server_group, + SVN_CONFIG_OPTION_SERF_LOG_LEVEL, + log_level)); +#endif + } + +#if SERF_VERSION_AT_LEAST(1, 4, 0) && !defined(SVN_SERF_NO_LOGGING) + if (log_components != SERF_LOGCOMP_NONE) + { + serf_log_output_t *output; + apr_status_t status; + + status = serf_logging_create_stream_output(&output, + session->context, + (apr_uint32_t)log_level, + (apr_uint32_t)log_components, + SERF_LOG_DEFAULT_LAYOUT, + stderr, + pool); + + if (!status) + serf_logging_add_output(session->context, output); } +#endif /* Don't allow the http-max-connections value to be larger than our compiled-in limit, or to be too small to operate. Broken @@ -439,8 +479,10 @@ svn_ra_serf__open(svn_ra_session_t *session, const char *session_URL, const svn_ra_callbacks2_t *callbacks, void *callback_baton, + svn_auth_baton_t *auth_baton, apr_hash_t *config, - apr_pool_t *pool) + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { apr_status_t status; svn_ra_serf__session_t *serf_sess; @@ -451,10 +493,15 @@ svn_ra_serf__open(svn_ra_session_t *session, if (corrected_url) *corrected_url = NULL; - serf_sess = apr_pcalloc(pool, sizeof(*serf_sess)); - serf_sess->pool = svn_pool_create(pool); + serf_sess = apr_pcalloc(result_pool, sizeof(*serf_sess)); + serf_sess->pool = result_pool; + if (config) + SVN_ERR(svn_config_copy_config(&serf_sess->config, config, result_pool)); + else + serf_sess->config = NULL; serf_sess->wc_callbacks = callbacks; serf_sess->wc_callback_baton = callback_baton; + serf_sess->auth_baton = auth_baton; serf_sess->progress_func = callbacks->progress_func; serf_sess->progress_baton = callbacks->progress_baton; serf_sess->cancel_func = callbacks->cancel_func; @@ -467,19 +514,8 @@ svn_ra_serf__open(svn_ra_session_t *session, serf_sess->pool)); - status = apr_uri_parse(serf_sess->pool, session_URL, &url); - if (status) - { - return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - _("Illegal URL '%s'"), - session_URL); - } - /* Depending the version of apr-util in use, for root paths url.path - will be NULL or "", where serf requires "/". */ - if (url.path == NULL || url.path[0] == '\0') - { - url.path = apr_pstrdup(serf_sess->pool, "/"); - } + SVN_ERR(svn_ra_serf__uri_parse(&url, session_URL, serf_sess->pool)); + if (!url.port) { url.port = apr_uri_port_of_scheme(url.scheme); @@ -511,13 +547,16 @@ svn_ra_serf__open(svn_ra_session_t *session, /* create the user agent string */ if (callbacks->get_client_string) - SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool)); + SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, + scratch_pool)); if (client_string) - serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ", - client_string, (char *)NULL); + serf_sess->useragent = apr_pstrcat(result_pool, + get_user_agent_string(scratch_pool), + " ", + client_string, SVN_VA_NULL); else - serf_sess->useragent = get_user_agent_string(pool); + serf_sess->useragent = get_user_agent_string(result_pool); /* go ahead and tell serf about the connection. */ status = @@ -538,7 +577,24 @@ svn_ra_serf__open(svn_ra_session_t *session, session->priv = serf_sess; - err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); + /* The following code explicitly works around a bug in serf <= r2319 / 1.3.8 + where serf doesn't report the request as failed/cancelled when the + authorization request handler fails to handle the request. + + As long as we allocate the request in a subpool of the serf connection + pool, we know that the handler is always cleaned before the connection. + + Luckily our caller now passes us two pools which handle this case. + */ +#if defined(SVN_DEBUG) && !SERF_VERSION_AT_LEAST(1,4,0) + /* Currently ensured by svn_ra_open4(). + If failing causes segfault in basic_tests.py 48, "basic auth test" */ + SVN_ERR_ASSERT((serf_sess->pool != scratch_pool) + && apr_pool_is_ancestor(serf_sess->pool, scratch_pool)); +#endif + + err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, + result_pool, scratch_pool); /* serf should produce a usable error code instead of APR_EGENERAL */ if (err && err->apr_err == APR_EGENERAL) @@ -552,20 +608,188 @@ svn_ra_serf__open(svn_ra_session_t *session, problems in any proxy. */ if ((corrected_url == NULL || *corrected_url == NULL) && serf_sess->detect_chunking && !serf_sess->http10) - SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool)); + SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, scratch_pool)); return SVN_NO_ERROR; } -/* Implements svn_ra__vtable_t.reparent(). */ +/* Implements svn_ra__vtable_t.dup_session */ static svn_error_t * +ra_serf_dup_session(svn_ra_session_t *new_session, + svn_ra_session_t *old_session, + const char *new_session_url, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + svn_ra_serf__session_t *old_sess = old_session->priv; + svn_ra_serf__session_t *new_sess; + apr_status_t status; + + new_sess = apr_pmemdup(result_pool, old_sess, sizeof(*new_sess)); + + new_sess->pool = result_pool; + + if (new_sess->config) + SVN_ERR(svn_config_copy_config(&new_sess->config, new_sess->config, + result_pool)); + + /* max_connections */ + /* using_ssl */ + /* using_compression */ + /* http10 */ + /* using_chunked_requests */ + /* detect_chunking */ + + if (new_sess->useragent) + new_sess->useragent = apr_pstrdup(result_pool, new_sess->useragent); + + if (new_sess->vcc_url) + new_sess->vcc_url = apr_pstrdup(result_pool, new_sess->vcc_url); + + new_sess->auth_state = NULL; + new_sess->auth_attempts = 0; + + /* Callback functions to get info from WC */ + /* wc_callbacks */ + /* wc_callback_baton */ + + /* progress_func */ + /* progress_baton */ + + /* cancel_func */ + /* cancel_baton */ + + /* shim_callbacks */ + + new_sess->pending_error = NULL; + + /* authn_types */ + + /* Keys and values are static */ + if (new_sess->capabilities) + new_sess->capabilities = apr_hash_copy(result_pool, new_sess->capabilities); + + if (new_sess->activity_collection_url) + { + new_sess->activity_collection_url + = apr_pstrdup(result_pool, new_sess->activity_collection_url); + } + + /* using_proxy */ + + if (new_sess->proxy_username) + { + new_sess->proxy_username + = apr_pstrdup(result_pool, new_sess->proxy_username); + } + + if (new_sess->proxy_password) + { + new_sess->proxy_username + = apr_pstrdup(result_pool, new_sess->proxy_password); + } + + new_sess->proxy_auth_attempts = 0; + + /* trust_default_ca */ + + if (new_sess->ssl_authorities) + { + new_sess->ssl_authorities = apr_pstrdup(result_pool, + new_sess->ssl_authorities); + } + + if (new_sess->uuid) + new_sess->uuid = apr_pstrdup(result_pool, new_sess->uuid); + + /* timeout */ + /* supports_deadprop_count */ + + if (new_sess->me_resource) + new_sess->me_resource = apr_pstrdup(result_pool, new_sess->me_resource); + if (new_sess->rev_stub) + new_sess->rev_stub = apr_pstrdup(result_pool, new_sess->rev_stub); + if (new_sess->txn_stub) + new_sess->txn_stub = apr_pstrdup(result_pool, new_sess->txn_stub); + if (new_sess->txn_root_stub) + new_sess->txn_root_stub = apr_pstrdup(result_pool, + new_sess->txn_root_stub); + if (new_sess->vtxn_stub) + new_sess->vtxn_stub = apr_pstrdup(result_pool, new_sess->vtxn_stub); + if (new_sess->vtxn_root_stub) + new_sess->vtxn_root_stub = apr_pstrdup(result_pool, + new_sess->vtxn_root_stub); + + /* Keys and values are static */ + if (new_sess->supported_posts) + new_sess->supported_posts = apr_hash_copy(result_pool, + new_sess->supported_posts); + + /* ### Can we copy this? */ + SVN_ERR(svn_ra_serf__blncache_create(&new_sess->blncache, + new_sess->pool)); + + if (new_sess->server_allows_bulk) + new_sess->server_allows_bulk = apr_pstrdup(result_pool, + new_sess->server_allows_bulk); + + new_sess->repos_root_str = apr_pstrdup(result_pool, + new_sess->repos_root_str); + SVN_ERR(svn_ra_serf__uri_parse(&new_sess->repos_root, + new_sess->repos_root_str, + result_pool)); + + new_sess->session_url_str = apr_pstrdup(result_pool, new_session_url); + + SVN_ERR(svn_ra_serf__uri_parse(&new_sess->session_url, + new_sess->session_url_str, + result_pool)); + + /* svn_boolean_t supports_inline_props */ + /* supports_rev_rsrc_replay */ + + new_sess->context = serf_context_create(result_pool); + + SVN_ERR(load_config(new_sess, old_sess->config, result_pool)); + + new_sess->conns[0] = apr_pcalloc(result_pool, + sizeof(*new_sess->conns[0])); + new_sess->conns[0]->bkt_alloc = + serf_bucket_allocator_create(result_pool, NULL, NULL); + new_sess->conns[0]->session = new_sess; + new_sess->conns[0]->last_status_code = -1; + + /* go ahead and tell serf about the connection. */ + status = + serf_connection_create2(&new_sess->conns[0]->conn, + new_sess->context, + new_sess->session_url, + svn_ra_serf__conn_setup, new_sess->conns[0], + svn_ra_serf__conn_closed, new_sess->conns[0], + result_pool); + if (status) + return svn_ra_serf__wrap_err(status, NULL); + + /* Set the progress callback. */ + serf_context_set_progress_cb(new_sess->context, svn_ra_serf__progress, + new_sess); + + new_sess->num_conns = 1; + new_sess->cur_conn = 0; + + new_session->priv = new_sess; + + return SVN_NO_ERROR; +} + +/* Implements svn_ra__vtable_t.reparent(). */ +svn_error_t * svn_ra_serf__reparent(svn_ra_session_t *ra_session, const char *url, apr_pool_t *pool) { svn_ra_serf__session_t *session = ra_session->priv; apr_uri_t new_url; - apr_status_t status; /* If it's the URL we already have, wave our hands and do nothing. */ if (strcmp(session->session_url_str, url) == 0) @@ -576,7 +800,7 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session, if (!session->repos_root_str) { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); } if (!svn_uri__is_ancestor(session->repos_root_str, url)) @@ -587,25 +811,11 @@ svn_ra_serf__reparent(svn_ra_session_t *ra_session, "URL '%s'"), url, session->repos_root_str); } - status = apr_uri_parse(pool, url, &new_url); - if (status) - { - return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, - _("Illegal repository URL '%s'"), url); - } + SVN_ERR(svn_ra_serf__uri_parse(&new_url, url, pool)); - /* Depending the version of apr-util in use, for root paths url.path - will be NULL or "", where serf requires "/". */ /* ### Maybe we should use a string buffer for these strings so we ### don't allocate memory in the session on every reparent? */ - if (new_url.path == NULL || new_url.path[0] == '\0') - { - session->session_url.path = apr_pstrdup(session->pool, "/"); - } - else - { - session->session_url.path = apr_pstrdup(session->pool, new_url.path); - } + session->session_url.path = apr_pstrdup(session->pool, new_url.path); session->session_url_str = apr_pstrdup(session->pool, url); return SVN_NO_ERROR; @@ -634,20 +844,24 @@ svn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session, latest_revnum, session, pool)); } -/* Implements svn_ra__vtable_t.rev_proplist(). */ +/* Implementation of svn_ra_serf__rev_proplist(). */ static svn_error_t * -svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, - svn_revnum_t rev, - apr_hash_t **ret_props, - apr_pool_t *pool) +serf__rev_proplist(svn_ra_session_t *ra_session, + svn_revnum_t rev, + const svn_ra_serf__dav_props_t *fetch_props, + apr_hash_t **ret_props, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) { svn_ra_serf__session_t *session = ra_session->priv; apr_hash_t *props; const char *propfind_path; + svn_ra_serf__handler_t *handler; if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) { - propfind_path = apr_psprintf(pool, "%s/%ld", session->rev_stub, rev); + propfind_path = apr_psprintf(scratch_pool, "%s/%ld", session->rev_stub, + rev); /* svn_ra_serf__retrieve_props() wants to added the revision as a Label to the PROPFIND, which isn't really necessary when @@ -658,507 +872,79 @@ svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, else { /* Use the VCC as the propfind target path. */ - SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool)); - } - - /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash. */ - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - propfind_path, rev, "0", all_props, - pool, pool)); - - SVN_ERR(svn_ra_serf__select_revprops(ret_props, propfind_path, rev, props, - pool, pool)); - - return SVN_NO_ERROR; -} - -/* Implements svn_ra__vtable_t.rev_prop(). */ -static svn_error_t * -svn_ra_serf__rev_prop(svn_ra_session_t *session, - svn_revnum_t rev, - const char *name, - svn_string_t **value, - apr_pool_t *pool) -{ - apr_hash_t *props; - - SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool)); - - *value = svn_hash_gets(props, name); - - return SVN_NO_ERROR; -} - -static svn_error_t * -fetch_path_props(apr_hash_t **props, - svn_ra_serf__session_t *session, - const char *session_relpath, - svn_revnum_t revision, - const svn_ra_serf__dav_props_t *desired_props, - apr_pool_t *result_pool, - apr_pool_t *scratch_pool) -{ - const char *url; - - url = session->session_url.path; - - /* If we have a relative path, append it. */ - if (session_relpath) - url = svn_path_url_add_component2(url, session_relpath, scratch_pool); - - /* If we were given a specific revision, get a URL that refers to that - specific revision (rather than floating with HEAD). */ - if (SVN_IS_VALID_REVNUM(revision)) - { - SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, - session, NULL /* conn */, - url, revision, - scratch_pool, scratch_pool)); - } - - /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant. - Or we started with SVN_INVALID_REVNUM and URL may be floating. */ - SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0], - url, SVN_INVALID_REVNUM, - desired_props, - result_pool, scratch_pool)); - - return SVN_NO_ERROR; -} - -/* Implements svn_ra__vtable_t.check_path(). */ -static svn_error_t * -svn_ra_serf__check_path(svn_ra_session_t *ra_session, - const char *rel_path, - svn_revnum_t revision, - svn_node_kind_t *kind, - apr_pool_t *pool) -{ - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_t *props; - - svn_error_t *err = fetch_path_props(&props, session, rel_path, - revision, check_path_props, - pool, pool); - - if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *kind = svn_node_none; + SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, + scratch_pool)); } - else - { - /* Any other error, raise to caller. */ - if (err) - return svn_error_trace(err); - SVN_ERR(svn_ra_serf__get_resource_type(kind, props)); - } - - return SVN_NO_ERROR; -} + props = apr_hash_make(result_pool); + SVN_ERR(svn_ra_serf__create_propfind_handler(&handler, session, + propfind_path, rev, "0", + fetch_props, + svn_ra_serf__deliver_svn_props, + props, + scratch_pool)); + SVN_ERR(svn_ra_serf__context_run_one(handler, scratch_pool)); -struct dirent_walker_baton_t { - /* Update the fields in this entry. */ - svn_dirent_t *entry; + svn_ra_serf__keep_only_regular_props(props, scratch_pool); - svn_tristate_t *supports_deadprop_count; - - /* If allocations are necessary, then use this pool. */ - apr_pool_t *result_pool; -}; - -static svn_error_t * -dirent_walker(void *baton, - const char *ns, - const char *name, - const svn_string_t *val, - apr_pool_t *scratch_pool) -{ - struct dirent_walker_baton_t *dwb = baton; - - if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) - { - dwb->entry->has_props = TRUE; - } - else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) - { - dwb->entry->has_props = TRUE; - } - else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) - { - if(strcmp(name, "deadprop-count") == 0) - { - if (*val->data) - { - apr_int64_t deadprop_count; - SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data)); - dwb->entry->has_props = deadprop_count > 0; - if (dwb->supports_deadprop_count) - *dwb->supports_deadprop_count = svn_tristate_true; - } - else if (dwb->supports_deadprop_count) - *dwb->supports_deadprop_count = svn_tristate_false; - } - } - else if (strcmp(ns, "DAV:") == 0) - { - if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) - { - dwb->entry->created_rev = SVN_STR_TO_REV(val->data); - } - else if (strcmp(name, "creator-displayname") == 0) - { - dwb->entry->last_author = val->data; - } - else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) - { - SVN_ERR(svn_time_from_cstring(&dwb->entry->time, - val->data, - dwb->result_pool)); - } - else if (strcmp(name, "getcontentlength") == 0) - { - /* 'getcontentlength' property is empty for directories. */ - if (val->len) - { - SVN_ERR(svn_cstring_atoi64(&dwb->entry->size, val->data)); - } - } - else if (strcmp(name, "resourcetype") == 0) - { - if (strcmp(val->data, "collection") == 0) - { - dwb->entry->kind = svn_node_dir; - } - else - { - dwb->entry->kind = svn_node_file; - } - } - } + *ret_props = props; return SVN_NO_ERROR; } -struct path_dirent_visitor_t { - apr_hash_t *full_paths; - apr_hash_t *base_paths; - const char *orig_path; - svn_tristate_t supports_deadprop_count; - apr_pool_t *result_pool; -}; - -static svn_error_t * -path_dirent_walker(void *baton, - const char *path, apr_ssize_t path_len, - const char *ns, apr_ssize_t ns_len, - const char *name, apr_ssize_t name_len, - const svn_string_t *val, - apr_pool_t *pool) -{ - struct path_dirent_visitor_t *dirents = baton; - struct dirent_walker_baton_t dwb; - svn_dirent_t *entry; - - /* Skip our original path. */ - if (strcmp(path, dirents->orig_path) == 0) - { - return SVN_NO_ERROR; - } - - entry = apr_hash_get(dirents->full_paths, path, path_len); - - if (!entry) - { - const char *base_name; - - entry = svn_dirent_create(pool); - - apr_hash_set(dirents->full_paths, path, path_len, entry); - - base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool), - pool); - - svn_hash_sets(dirents->base_paths, base_name, entry); - } - - dwb.entry = entry; - dwb.supports_deadprop_count = &dirents->supports_deadprop_count; - dwb.result_pool = dirents->result_pool; - return svn_error_trace(dirent_walker(&dwb, ns, name, val, pool)); -} - -static const svn_ra_serf__dav_props_t * -get_dirent_props(apr_uint32_t dirent_fields, - svn_ra_serf__session_t *session, - apr_pool_t *pool) -{ - svn_ra_serf__dav_props_t *prop; - apr_array_header_t *props = apr_array_make - (pool, 7, sizeof(svn_ra_serf__dav_props_t)); - - if (session->supports_deadprop_count != svn_tristate_false - || ! (dirent_fields & SVN_DIRENT_HAS_PROPS)) - { - if (dirent_fields & SVN_DIRENT_KIND) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "resourcetype"; - } - - if (dirent_fields & SVN_DIRENT_SIZE) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "getcontentlength"; - } - - if (dirent_fields & SVN_DIRENT_HAS_PROPS) - { - prop = apr_array_push(props); - prop->namespace = SVN_DAV_PROP_NS_DAV; - prop->name = "deadprop-count"; - } - - if (dirent_fields & SVN_DIRENT_CREATED_REV) - { - svn_ra_serf__dav_props_t *p = apr_array_push(props); - p->namespace = "DAV:"; - p->name = SVN_DAV__VERSION_NAME; - } - - if (dirent_fields & SVN_DIRENT_TIME) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = SVN_DAV__CREATIONDATE; - } - - if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) - { - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "creator-displayname"; - } - } - else - { - /* We found an old subversion server that can't handle - the deadprop-count property in the way we expect. - - The neon behavior is to retrieve all properties in this case */ - prop = apr_array_push(props); - prop->namespace = "DAV:"; - prop->name = "allprop"; - } - - prop = apr_array_push(props); - prop->namespace = NULL; - prop->name = NULL; - - return (svn_ra_serf__dav_props_t *) props->elts; -} - -/* Implements svn_ra__vtable_t.stat(). */ +/* Implements svn_ra__vtable_t.rev_proplist(). */ static svn_error_t * -svn_ra_serf__stat(svn_ra_session_t *ra_session, - const char *rel_path, - svn_revnum_t revision, - svn_dirent_t **dirent, - apr_pool_t *pool) +svn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, + svn_revnum_t rev, + apr_hash_t **ret_props, + apr_pool_t *result_pool) { - svn_ra_serf__session_t *session = ra_session->priv; - apr_hash_t *props; + apr_pool_t *scratch_pool = svn_pool_create(result_pool); svn_error_t *err; - struct dirent_walker_baton_t dwb; - svn_tristate_t deadprop_count = svn_tristate_unknown; - - err = fetch_path_props(&props, - session, rel_path, revision, - get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool, pool); - if (err) - { - if (err->apr_err == SVN_ERR_FS_NOT_FOUND) - { - svn_error_clear(err); - *dirent = NULL; - return SVN_NO_ERROR; - } - else - return svn_error_trace(err); - } - dwb.entry = svn_dirent_create(pool); - dwb.supports_deadprop_count = &deadprop_count; - dwb.result_pool = pool; - SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); + err = serf__rev_proplist(ra_session, rev, all_props, ret_props, + result_pool, scratch_pool); - if (deadprop_count == svn_tristate_false - && session->supports_deadprop_count == svn_tristate_unknown - && !dwb.entry->has_props) - { - /* We have to requery as the server didn't give us the right - information */ - session->supports_deadprop_count = svn_tristate_false; - - SVN_ERR(fetch_path_props(&props, - session, rel_path, SVN_INVALID_REVNUM, - get_dirent_props(SVN_DIRENT_ALL, session, pool), - pool, pool)); - - SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); - } - - if (deadprop_count != svn_tristate_unknown) - session->supports_deadprop_count = deadprop_count; - - *dirent = dwb.entry; - - return SVN_NO_ERROR; + svn_pool_destroy(scratch_pool); + return svn_error_trace(err); } -/* Reads the 'resourcetype' property from the list PROPS and checks if the - * resource at PATH@REVISION really is a directory. Returns - * SVN_ERR_FS_NOT_DIRECTORY if not. - */ -static svn_error_t * -resource_is_directory(apr_hash_t *props) -{ - svn_node_kind_t kind; - - SVN_ERR(svn_ra_serf__get_resource_type(&kind, props)); - - if (kind != svn_node_dir) - { - return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, - _("Can't get entries of non-directory")); - } - - return SVN_NO_ERROR; -} -/* Implements svn_ra__vtable_t.get_dir(). */ -static svn_error_t * -svn_ra_serf__get_dir(svn_ra_session_t *ra_session, - apr_hash_t **dirents, - svn_revnum_t *fetched_rev, - apr_hash_t **ret_props, - const char *rel_path, - svn_revnum_t revision, - apr_uint32_t dirent_fields, - apr_pool_t *pool) +/* Implements svn_ra__vtable_t.rev_prop(). */ +svn_error_t * +svn_ra_serf__rev_prop(svn_ra_session_t *session, + svn_revnum_t rev, + const char *name, + svn_string_t **value, + apr_pool_t *result_pool) { - svn_ra_serf__session_t *session = ra_session->priv; - const char *path; - - path = session->session_url.path; - - /* If we have a relative path, URI encode and append it. */ - if (rel_path) - { - path = svn_path_url_add_component2(path, rel_path, pool); - } - - /* If the user specified a peg revision other than HEAD, we have to fetch - the baseline collection url for that revision. If not, we can use the - public url. */ - if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) - { - SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev, - session, NULL /* conn */, - path, revision, - pool, pool)); - revision = SVN_INVALID_REVNUM; - } - /* REVISION is always SVN_INVALID_REVNUM */ - SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); - - /* If we're asked for children, fetch them now. */ - if (dirents) + apr_pool_t *scratch_pool = svn_pool_create(result_pool); + apr_hash_t *props; + svn_ra_serf__dav_props_t specific_props[2]; + const svn_ra_serf__dav_props_t *fetch_props = all_props; + + /* The DAV propfind doesn't allow property fetches for any property name + as there is no defined way to quote values. If we are just fetching a + "svn:property" we can safely do this. In other cases we just fetch all + revision properties and filter the right one out */ + if (strncmp(name, SVN_PROP_PREFIX, sizeof(SVN_PROP_PREFIX)-1) == 0 + && !strchr(name + sizeof(SVN_PROP_PREFIX)-1, ':')) { - struct path_dirent_visitor_t dirent_walk; - apr_hash_t *props; - const char *rtype; - - /* Always request node kind to check that path is really a - * directory. - */ - dirent_fields |= SVN_DIRENT_KIND; - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], - path, SVN_INVALID_REVNUM, "1", - get_dirent_props(dirent_fields, - session, pool), - pool, pool)); - - /* Check if the path is really a directory. */ - rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype"); - if (rtype == NULL || strcmp(rtype, "collection") != 0) - return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, - _("Can't get entries of non-directory")); - - /* We're going to create two hashes to help the walker along. - * We're going to return the 2nd one back to the caller as it - * will have the basenames it expects. - */ - dirent_walk.full_paths = apr_hash_make(pool); - dirent_walk.base_paths = apr_hash_make(pool); - dirent_walk.orig_path = svn_urlpath__canonicalize(path, pool); - dirent_walk.supports_deadprop_count = svn_tristate_unknown; - dirent_walk.result_pool = pool; - - SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, - path_dirent_walker, &dirent_walk, - pool)); - - if (dirent_walk.supports_deadprop_count == svn_tristate_false - && session->supports_deadprop_count == svn_tristate_unknown - && dirent_fields & SVN_DIRENT_HAS_PROPS) - { - /* We have to requery as the server didn't give us the right - information */ - session->supports_deadprop_count = svn_tristate_false; - SVN_ERR(svn_ra_serf__retrieve_props(&props, session, - session->conns[0], - path, SVN_INVALID_REVNUM, "1", - get_dirent_props(dirent_fields, - session, pool), - pool, pool)); - - apr_hash_clear(dirent_walk.full_paths); - apr_hash_clear(dirent_walk.base_paths); - - SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, - path_dirent_walker, - &dirent_walk, pool)); - } - - *dirents = dirent_walk.base_paths; + specific_props[0].xmlns = SVN_DAV_PROP_NS_SVN; + specific_props[0].name = name + sizeof(SVN_PROP_PREFIX)-1; + specific_props[1].xmlns = NULL; + specific_props[1].name = NULL; - if (dirent_walk.supports_deadprop_count != svn_tristate_unknown) - session->supports_deadprop_count = dirent_walk.supports_deadprop_count; + fetch_props = specific_props; } - /* If we're asked for the directory properties, fetch them too. */ - if (ret_props) - { - apr_hash_t *props; - - SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0], - path, SVN_INVALID_REVNUM, - all_props, - pool, pool)); + SVN_ERR(serf__rev_proplist(session, rev, fetch_props, &props, + result_pool, scratch_pool)); - /* Check if the path is really a directory. */ - SVN_ERR(resource_is_directory(props)); + *value = svn_hash_gets(props, name); - /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props() - ### put them into POOL, so we're okay. */ - SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool)); - } + svn_pool_destroy(scratch_pool); return SVN_NO_ERROR; } @@ -1173,7 +959,7 @@ svn_ra_serf__get_repos_root(svn_ra_session_t *ra_session, if (!session->repos_root_str) { const char *vcc_url; - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); } *url = session->repos_root_str; @@ -1209,7 +995,7 @@ svn_ra_serf__get_uuid(svn_ra_session_t *ra_session, /* We're not interested in vcc_url and relative_url, but this call also stores the repository's uuid in the session. */ - SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); + SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, pool)); if (!session->uuid) { return svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL, @@ -1229,6 +1015,7 @@ static const svn_ra__vtable_t serf_vtable = { ra_serf_get_description, ra_serf_get_schemes, svn_ra_serf__open, + ra_serf_dup_session, svn_ra_serf__reparent, svn_ra_serf__get_session_url, svn_ra_serf__get_latest_revnum, |