1251881Speter/* 2251881Speter * serf.c : entry point for ra_serf 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#define APR_WANT_STRFUNC 27251881Speter#include <apr_want.h> 28251881Speter 29251881Speter#include <apr_uri.h> 30251881Speter#include <serf.h> 31251881Speter 32251881Speter#include "svn_pools.h" 33251881Speter#include "svn_ra.h" 34251881Speter#include "svn_dav.h" 35251881Speter#include "svn_xml.h" 36251881Speter#include "../libsvn_ra/ra_loader.h" 37251881Speter#include "svn_config.h" 38251881Speter#include "svn_delta.h" 39251881Speter#include "svn_dirent_uri.h" 40251881Speter#include "svn_hash.h" 41251881Speter#include "svn_path.h" 42251881Speter#include "svn_time.h" 43251881Speter#include "svn_version.h" 44251881Speter 45251881Speter#include "private/svn_dav_protocol.h" 46251881Speter#include "private/svn_dep_compat.h" 47251881Speter#include "private/svn_fspath.h" 48251881Speter#include "private/svn_subr_private.h" 49251881Speter#include "svn_private_config.h" 50251881Speter 51251881Speter#include "ra_serf.h" 52251881Speter 53251881Speter 54251881Speter/* Implements svn_ra__vtable_t.get_version(). */ 55251881Speterstatic const svn_version_t * 56251881Speterra_serf_version(void) 57251881Speter{ 58251881Speter SVN_VERSION_BODY; 59251881Speter} 60251881Speter 61251881Speter#define RA_SERF_DESCRIPTION \ 62251881Speter N_("Module for accessing a repository via WebDAV protocol using serf.") 63251881Speter 64262253Speter#define RA_SERF_DESCRIPTION_VER \ 65262253Speter N_("Module for accessing a repository via WebDAV protocol using serf.\n" \ 66262253Speter " - using serf %d.%d.%d") 67262253Speter 68251881Speter/* Implements svn_ra__vtable_t.get_description(). */ 69251881Speterstatic const char * 70262253Speterra_serf_get_description(apr_pool_t *pool) 71251881Speter{ 72262253Speter int major, minor, patch; 73262253Speter 74262253Speter serf_lib_version(&major, &minor, &patch); 75262253Speter return apr_psprintf(pool, _(RA_SERF_DESCRIPTION_VER), major, minor, patch); 76251881Speter} 77251881Speter 78251881Speter/* Implements svn_ra__vtable_t.get_schemes(). */ 79251881Speterstatic const char * const * 80251881Speterra_serf_get_schemes(apr_pool_t *pool) 81251881Speter{ 82251881Speter static const char *serf_ssl[] = { "http", "https", NULL }; 83251881Speter#if 0 84251881Speter /* ### Temporary: to shut up a warning. */ 85251881Speter static const char *serf_no_ssl[] = { "http", NULL }; 86251881Speter#endif 87251881Speter 88251881Speter /* TODO: Runtime detection. */ 89251881Speter return serf_ssl; 90251881Speter} 91251881Speter 92251881Speter/* Load the setting http-auth-types from the global or server specific 93251881Speter section, parse its value and set the types of authentication we should 94251881Speter accept from the server. */ 95251881Speterstatic svn_error_t * 96251881Speterload_http_auth_types(apr_pool_t *pool, svn_config_t *config, 97251881Speter const char *server_group, 98251881Speter int *authn_types) 99251881Speter{ 100251881Speter const char *http_auth_types = NULL; 101251881Speter *authn_types = SERF_AUTHN_NONE; 102251881Speter 103251881Speter svn_config_get(config, &http_auth_types, SVN_CONFIG_SECTION_GLOBAL, 104251881Speter SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, NULL); 105251881Speter 106251881Speter if (server_group) 107251881Speter { 108251881Speter svn_config_get(config, &http_auth_types, server_group, 109251881Speter SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, http_auth_types); 110251881Speter } 111251881Speter 112251881Speter if (http_auth_types) 113251881Speter { 114251881Speter char *token; 115251881Speter char *auth_types_list = apr_palloc(pool, strlen(http_auth_types) + 1); 116251881Speter apr_collapse_spaces(auth_types_list, http_auth_types); 117251881Speter while ((token = svn_cstring_tokenize(";", &auth_types_list)) != NULL) 118251881Speter { 119251881Speter if (svn_cstring_casecmp("basic", token) == 0) 120251881Speter *authn_types |= SERF_AUTHN_BASIC; 121251881Speter else if (svn_cstring_casecmp("digest", token) == 0) 122251881Speter *authn_types |= SERF_AUTHN_DIGEST; 123251881Speter else if (svn_cstring_casecmp("ntlm", token) == 0) 124251881Speter *authn_types |= SERF_AUTHN_NTLM; 125251881Speter else if (svn_cstring_casecmp("negotiate", token) == 0) 126251881Speter *authn_types |= SERF_AUTHN_NEGOTIATE; 127251881Speter else 128251881Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 129251881Speter _("Invalid config: unknown %s " 130251881Speter "'%s'"), 131251881Speter SVN_CONFIG_OPTION_HTTP_AUTH_TYPES, token); 132251881Speter } 133251881Speter } 134251881Speter else 135251881Speter { 136251881Speter /* Nothing specified by the user, so accept all types. */ 137251881Speter *authn_types = SERF_AUTHN_ALL; 138251881Speter } 139251881Speter 140251881Speter return SVN_NO_ERROR; 141251881Speter} 142251881Speter 143251881Speter/* Default HTTP timeout (in seconds); overridden by the 'http-timeout' 144251881Speter runtime configuration variable. */ 145251881Speter#define DEFAULT_HTTP_TIMEOUT 600 146251881Speter 147253734Speter/* Private symbol for the 1.9-public SVN_CONFIG_OPTION_HTTP_CHUNKED_REQUESTS */ 148253734Speter#define OPTION_HTTP_CHUNKED_REQUESTS "http-chunked-requests" 149253734Speter 150253734Speter 151251881Speterstatic svn_error_t * 152251881Speterload_config(svn_ra_serf__session_t *session, 153251881Speter apr_hash_t *config_hash, 154251881Speter apr_pool_t *pool) 155251881Speter{ 156251881Speter svn_config_t *config, *config_client; 157251881Speter const char *server_group; 158251881Speter const char *proxy_host = NULL; 159251881Speter const char *port_str = NULL; 160251881Speter const char *timeout_str = NULL; 161251881Speter const char *exceptions; 162251881Speter apr_port_t proxy_port; 163253734Speter svn_tristate_t chunked_requests; 164251881Speter 165251881Speter if (config_hash) 166251881Speter { 167251881Speter config = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_SERVERS); 168251881Speter config_client = svn_hash_gets(config_hash, SVN_CONFIG_CATEGORY_CONFIG); 169251881Speter } 170251881Speter else 171251881Speter { 172251881Speter config = NULL; 173251881Speter config_client = NULL; 174251881Speter } 175251881Speter 176251881Speter SVN_ERR(svn_config_get_bool(config, &session->using_compression, 177251881Speter SVN_CONFIG_SECTION_GLOBAL, 178251881Speter SVN_CONFIG_OPTION_HTTP_COMPRESSION, TRUE)); 179251881Speter svn_config_get(config, &timeout_str, SVN_CONFIG_SECTION_GLOBAL, 180251881Speter SVN_CONFIG_OPTION_HTTP_TIMEOUT, NULL); 181251881Speter 182251881Speter if (session->wc_callbacks->auth_baton) 183251881Speter { 184251881Speter if (config_client) 185251881Speter { 186251881Speter svn_auth_set_parameter(session->wc_callbacks->auth_baton, 187251881Speter SVN_AUTH_PARAM_CONFIG_CATEGORY_CONFIG, 188251881Speter config_client); 189251881Speter } 190251881Speter if (config) 191251881Speter { 192251881Speter svn_auth_set_parameter(session->wc_callbacks->auth_baton, 193251881Speter SVN_AUTH_PARAM_CONFIG_CATEGORY_SERVERS, 194251881Speter config); 195251881Speter } 196251881Speter } 197251881Speter 198251881Speter /* Use the default proxy-specific settings if and only if 199251881Speter "http-proxy-exceptions" is not set to exclude this host. */ 200251881Speter svn_config_get(config, &exceptions, SVN_CONFIG_SECTION_GLOBAL, 201251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_EXCEPTIONS, ""); 202251881Speter if (! svn_cstring_match_glob_list(session->session_url.hostname, 203251881Speter svn_cstring_split(exceptions, ",", 204251881Speter TRUE, pool))) 205251881Speter { 206251881Speter svn_config_get(config, &proxy_host, SVN_CONFIG_SECTION_GLOBAL, 207251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_HOST, NULL); 208251881Speter svn_config_get(config, &port_str, SVN_CONFIG_SECTION_GLOBAL, 209251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_PORT, NULL); 210251881Speter svn_config_get(config, &session->proxy_username, 211251881Speter SVN_CONFIG_SECTION_GLOBAL, 212251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, NULL); 213251881Speter svn_config_get(config, &session->proxy_password, 214251881Speter SVN_CONFIG_SECTION_GLOBAL, 215251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, NULL); 216251881Speter } 217251881Speter 218251881Speter /* Load the global ssl settings, if set. */ 219251881Speter SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, 220251881Speter SVN_CONFIG_SECTION_GLOBAL, 221251881Speter SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, 222251881Speter TRUE)); 223251881Speter svn_config_get(config, &session->ssl_authorities, SVN_CONFIG_SECTION_GLOBAL, 224251881Speter SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, NULL); 225251881Speter 226251881Speter /* If set, read the flag that tells us to do bulk updates or not. Defaults 227251881Speter to skelta updates. */ 228251881Speter SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, 229251881Speter SVN_CONFIG_SECTION_GLOBAL, 230251881Speter SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, 231251881Speter "auto", 232251881Speter svn_tristate_unknown)); 233251881Speter 234251881Speter /* Load the maximum number of parallel session connections. */ 235251881Speter SVN_ERR(svn_config_get_int64(config, &session->max_connections, 236251881Speter SVN_CONFIG_SECTION_GLOBAL, 237251881Speter SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 238251881Speter SVN_CONFIG_DEFAULT_OPTION_HTTP_MAX_CONNECTIONS)); 239251881Speter 240253734Speter /* Should we use chunked transfer encoding. */ 241253734Speter SVN_ERR(svn_config_get_tristate(config, &chunked_requests, 242253734Speter SVN_CONFIG_SECTION_GLOBAL, 243253734Speter OPTION_HTTP_CHUNKED_REQUESTS, 244253734Speter "auto", svn_tristate_unknown)); 245253734Speter 246251881Speter if (config) 247251881Speter server_group = svn_config_find_group(config, 248251881Speter session->session_url.hostname, 249251881Speter SVN_CONFIG_SECTION_GROUPS, pool); 250251881Speter else 251251881Speter server_group = NULL; 252251881Speter 253251881Speter if (server_group) 254251881Speter { 255251881Speter SVN_ERR(svn_config_get_bool(config, &session->using_compression, 256251881Speter server_group, 257251881Speter SVN_CONFIG_OPTION_HTTP_COMPRESSION, 258251881Speter session->using_compression)); 259251881Speter svn_config_get(config, &timeout_str, server_group, 260251881Speter SVN_CONFIG_OPTION_HTTP_TIMEOUT, timeout_str); 261251881Speter 262251881Speter svn_auth_set_parameter(session->wc_callbacks->auth_baton, 263251881Speter SVN_AUTH_PARAM_SERVER_GROUP, server_group); 264251881Speter 265251881Speter /* Load the group proxy server settings, overriding global 266251881Speter settings. We intentionally ignore 'http-proxy-exceptions' 267251881Speter here because, well, if this site was an exception, why is 268251881Speter there a per-server proxy configuration for it? */ 269251881Speter svn_config_get(config, &proxy_host, server_group, 270251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_HOST, proxy_host); 271251881Speter svn_config_get(config, &port_str, server_group, 272251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_PORT, port_str); 273251881Speter svn_config_get(config, &session->proxy_username, server_group, 274251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_USERNAME, 275251881Speter session->proxy_username); 276251881Speter svn_config_get(config, &session->proxy_password, server_group, 277251881Speter SVN_CONFIG_OPTION_HTTP_PROXY_PASSWORD, 278251881Speter session->proxy_password); 279251881Speter 280251881Speter /* Load the group ssl settings. */ 281251881Speter SVN_ERR(svn_config_get_bool(config, &session->trust_default_ca, 282251881Speter server_group, 283251881Speter SVN_CONFIG_OPTION_SSL_TRUST_DEFAULT_CA, 284251881Speter session->trust_default_ca)); 285251881Speter svn_config_get(config, &session->ssl_authorities, server_group, 286251881Speter SVN_CONFIG_OPTION_SSL_AUTHORITY_FILES, 287251881Speter session->ssl_authorities); 288251881Speter 289251881Speter /* Load the group bulk updates flag. */ 290251881Speter SVN_ERR(svn_config_get_tristate(config, &session->bulk_updates, 291251881Speter server_group, 292251881Speter SVN_CONFIG_OPTION_HTTP_BULK_UPDATES, 293251881Speter "auto", 294251881Speter session->bulk_updates)); 295251881Speter 296251881Speter /* Load the maximum number of parallel session connections, 297251881Speter overriding global values. */ 298251881Speter SVN_ERR(svn_config_get_int64(config, &session->max_connections, 299251881Speter server_group, 300251881Speter SVN_CONFIG_OPTION_HTTP_MAX_CONNECTIONS, 301251881Speter session->max_connections)); 302253734Speter 303253734Speter /* Should we use chunked transfer encoding. */ 304253734Speter SVN_ERR(svn_config_get_tristate(config, &chunked_requests, 305253734Speter server_group, 306253734Speter OPTION_HTTP_CHUNKED_REQUESTS, 307253734Speter "auto", chunked_requests)); 308251881Speter } 309251881Speter 310251881Speter /* Don't allow the http-max-connections value to be larger than our 311251881Speter compiled-in limit, or to be too small to operate. Broken 312251881Speter functionality and angry administrators are equally undesirable. */ 313251881Speter if (session->max_connections > SVN_RA_SERF__MAX_CONNECTIONS_LIMIT) 314251881Speter session->max_connections = SVN_RA_SERF__MAX_CONNECTIONS_LIMIT; 315251881Speter if (session->max_connections < 2) 316251881Speter session->max_connections = 2; 317251881Speter 318251881Speter /* Parse the connection timeout value, if any. */ 319251881Speter session->timeout = apr_time_from_sec(DEFAULT_HTTP_TIMEOUT); 320251881Speter if (timeout_str) 321251881Speter { 322251881Speter char *endstr; 323251881Speter const long int timeout = strtol(timeout_str, &endstr, 10); 324251881Speter 325251881Speter if (*endstr) 326251881Speter return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, 327251881Speter _("Invalid config: illegal character in " 328251881Speter "timeout value")); 329251881Speter if (timeout < 0) 330251881Speter return svn_error_create(SVN_ERR_BAD_CONFIG_VALUE, NULL, 331251881Speter _("Invalid config: negative timeout value")); 332251881Speter session->timeout = apr_time_from_sec(timeout); 333251881Speter } 334251881Speter SVN_ERR_ASSERT(session->timeout >= 0); 335251881Speter 336251881Speter /* Convert the proxy port value, if any. */ 337251881Speter if (port_str) 338251881Speter { 339251881Speter char *endstr; 340251881Speter const long int port = strtol(port_str, &endstr, 10); 341251881Speter 342251881Speter if (*endstr) 343251881Speter return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, 344251881Speter _("Invalid URL: illegal character in proxy " 345251881Speter "port number")); 346251881Speter if (port < 0) 347251881Speter return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, 348251881Speter _("Invalid URL: negative proxy port number")); 349251881Speter if (port > 65535) 350251881Speter return svn_error_create(SVN_ERR_RA_ILLEGAL_URL, NULL, 351251881Speter _("Invalid URL: proxy port number greater " 352251881Speter "than maximum TCP port number 65535")); 353251881Speter proxy_port = (apr_port_t) port; 354251881Speter } 355251881Speter else 356251881Speter { 357251881Speter proxy_port = 80; 358251881Speter } 359251881Speter 360251881Speter if (proxy_host) 361251881Speter { 362251881Speter apr_sockaddr_t *proxy_addr; 363251881Speter apr_status_t status; 364251881Speter 365251881Speter status = apr_sockaddr_info_get(&proxy_addr, proxy_host, 366251881Speter APR_UNSPEC, proxy_port, 0, 367251881Speter session->pool); 368251881Speter if (status) 369251881Speter { 370251881Speter return svn_ra_serf__wrap_err( 371251881Speter status, _("Could not resolve proxy server '%s'"), 372251881Speter proxy_host); 373251881Speter } 374251881Speter session->using_proxy = TRUE; 375251881Speter serf_config_proxy(session->context, proxy_addr); 376251881Speter } 377251881Speter else 378251881Speter { 379251881Speter session->using_proxy = FALSE; 380251881Speter } 381251881Speter 382253734Speter /* Setup detect_chunking and using_chunked_requests based on 383253734Speter * the chunked_requests tristate */ 384253734Speter if (chunked_requests == svn_tristate_unknown) 385253734Speter { 386253734Speter session->detect_chunking = TRUE; 387253734Speter session->using_chunked_requests = TRUE; 388253734Speter } 389253734Speter else if (chunked_requests == svn_tristate_true) 390253734Speter { 391253734Speter session->detect_chunking = FALSE; 392253734Speter session->using_chunked_requests = TRUE; 393253734Speter } 394253734Speter else /* chunked_requests == svn_tristate_false */ 395253734Speter { 396253734Speter session->detect_chunking = FALSE; 397253734Speter session->using_chunked_requests = FALSE; 398253734Speter } 399253734Speter 400251881Speter /* Setup authentication. */ 401251881Speter SVN_ERR(load_http_auth_types(pool, config, server_group, 402251881Speter &session->authn_types)); 403251881Speter serf_config_authn_types(session->context, session->authn_types); 404251881Speter serf_config_credentials_callback(session->context, 405251881Speter svn_ra_serf__credentials_callback); 406251881Speter 407251881Speter return SVN_NO_ERROR; 408251881Speter} 409251881Speter#undef DEFAULT_HTTP_TIMEOUT 410251881Speter 411251881Speterstatic void 412251881Spetersvn_ra_serf__progress(void *progress_baton, apr_off_t read, apr_off_t written) 413251881Speter{ 414251881Speter const svn_ra_serf__session_t *serf_sess = progress_baton; 415251881Speter if (serf_sess->progress_func) 416251881Speter { 417251881Speter serf_sess->progress_func(read + written, -1, 418251881Speter serf_sess->progress_baton, 419251881Speter serf_sess->pool); 420251881Speter } 421251881Speter} 422251881Speter 423262253Speter/** Our User-Agent string. */ 424262253Speterstatic const char * 425262253Speterget_user_agent_string(apr_pool_t *pool) 426262253Speter{ 427262253Speter int major, minor, patch; 428262253Speter serf_lib_version(&major, &minor, &patch); 429262253Speter 430262253Speter return apr_psprintf(pool, "SVN/%s (%s) serf/%d.%d.%d", 431262253Speter SVN_VER_NUMBER, SVN_BUILD_TARGET, 432262253Speter major, minor, patch); 433262253Speter} 434262253Speter 435251881Speter/* Implements svn_ra__vtable_t.open_session(). */ 436251881Speterstatic svn_error_t * 437251881Spetersvn_ra_serf__open(svn_ra_session_t *session, 438251881Speter const char **corrected_url, 439251881Speter const char *session_URL, 440251881Speter const svn_ra_callbacks2_t *callbacks, 441251881Speter void *callback_baton, 442251881Speter apr_hash_t *config, 443251881Speter apr_pool_t *pool) 444251881Speter{ 445251881Speter apr_status_t status; 446251881Speter svn_ra_serf__session_t *serf_sess; 447251881Speter apr_uri_t url; 448251881Speter const char *client_string = NULL; 449253734Speter svn_error_t *err; 450251881Speter 451251881Speter if (corrected_url) 452251881Speter *corrected_url = NULL; 453251881Speter 454251881Speter serf_sess = apr_pcalloc(pool, sizeof(*serf_sess)); 455251881Speter serf_sess->pool = svn_pool_create(pool); 456251881Speter serf_sess->wc_callbacks = callbacks; 457251881Speter serf_sess->wc_callback_baton = callback_baton; 458251881Speter serf_sess->progress_func = callbacks->progress_func; 459251881Speter serf_sess->progress_baton = callbacks->progress_baton; 460251881Speter serf_sess->cancel_func = callbacks->cancel_func; 461251881Speter serf_sess->cancel_baton = callback_baton; 462251881Speter 463251881Speter /* todo: reuse serf context across sessions */ 464251881Speter serf_sess->context = serf_context_create(serf_sess->pool); 465251881Speter 466251881Speter SVN_ERR(svn_ra_serf__blncache_create(&serf_sess->blncache, 467251881Speter serf_sess->pool)); 468251881Speter 469251881Speter 470251881Speter status = apr_uri_parse(serf_sess->pool, session_URL, &url); 471251881Speter if (status) 472251881Speter { 473251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 474251881Speter _("Illegal URL '%s'"), 475251881Speter session_URL); 476251881Speter } 477251881Speter /* Depending the version of apr-util in use, for root paths url.path 478251881Speter will be NULL or "", where serf requires "/". */ 479251881Speter if (url.path == NULL || url.path[0] == '\0') 480251881Speter { 481251881Speter url.path = apr_pstrdup(serf_sess->pool, "/"); 482251881Speter } 483251881Speter if (!url.port) 484251881Speter { 485251881Speter url.port = apr_uri_port_of_scheme(url.scheme); 486251881Speter } 487251881Speter serf_sess->session_url = url; 488251881Speter serf_sess->session_url_str = apr_pstrdup(serf_sess->pool, session_URL); 489251881Speter serf_sess->using_ssl = (svn_cstring_casecmp(url.scheme, "https") == 0); 490251881Speter 491251881Speter serf_sess->supports_deadprop_count = svn_tristate_unknown; 492251881Speter 493251881Speter serf_sess->capabilities = apr_hash_make(serf_sess->pool); 494251881Speter 495251881Speter /* We have to assume that the server only supports HTTP/1.0. Once it's clear 496251881Speter HTTP/1.1 is supported, we can upgrade. */ 497251881Speter serf_sess->http10 = TRUE; 498251881Speter 499253734Speter /* If we switch to HTTP/1.1, then we will use chunked requests. We may disable 500253734Speter this, if we find an intervening proxy does not support chunked requests. */ 501253734Speter serf_sess->using_chunked_requests = TRUE; 502253734Speter 503251881Speter SVN_ERR(load_config(serf_sess, config, serf_sess->pool)); 504251881Speter 505251881Speter serf_sess->conns[0] = apr_pcalloc(serf_sess->pool, 506251881Speter sizeof(*serf_sess->conns[0])); 507251881Speter serf_sess->conns[0]->bkt_alloc = 508251881Speter serf_bucket_allocator_create(serf_sess->pool, NULL, NULL); 509251881Speter serf_sess->conns[0]->session = serf_sess; 510251881Speter serf_sess->conns[0]->last_status_code = -1; 511251881Speter 512251881Speter /* create the user agent string */ 513251881Speter if (callbacks->get_client_string) 514251881Speter SVN_ERR(callbacks->get_client_string(callback_baton, &client_string, pool)); 515251881Speter 516251881Speter if (client_string) 517262253Speter serf_sess->useragent = apr_pstrcat(pool, get_user_agent_string(pool), " ", 518251881Speter client_string, (char *)NULL); 519251881Speter else 520262253Speter serf_sess->useragent = get_user_agent_string(pool); 521251881Speter 522251881Speter /* go ahead and tell serf about the connection. */ 523251881Speter status = 524251881Speter serf_connection_create2(&serf_sess->conns[0]->conn, 525251881Speter serf_sess->context, 526251881Speter url, 527251881Speter svn_ra_serf__conn_setup, serf_sess->conns[0], 528251881Speter svn_ra_serf__conn_closed, serf_sess->conns[0], 529251881Speter serf_sess->pool); 530251881Speter if (status) 531251881Speter return svn_ra_serf__wrap_err(status, NULL); 532251881Speter 533251881Speter /* Set the progress callback. */ 534251881Speter serf_context_set_progress_cb(serf_sess->context, svn_ra_serf__progress, 535251881Speter serf_sess); 536251881Speter 537251881Speter serf_sess->num_conns = 1; 538251881Speter 539251881Speter session->priv = serf_sess; 540251881Speter 541253734Speter err = svn_ra_serf__exchange_capabilities(serf_sess, corrected_url, pool); 542253734Speter 543253734Speter /* serf should produce a usable error code instead of APR_EGENERAL */ 544253734Speter if (err && err->apr_err == APR_EGENERAL) 545253734Speter err = svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, err, 546253734Speter _("Connection to '%s' failed"), session_URL); 547253734Speter SVN_ERR(err); 548253734Speter 549253734Speter /* We have set up a useful connection (that doesn't indication a redirect). 550253734Speter If we've been told there is possibly a worrisome proxy in our path to the 551253734Speter server AND we switched to HTTP/1.1 (chunked requests), then probe for 552253734Speter problems in any proxy. */ 553253734Speter if ((corrected_url == NULL || *corrected_url == NULL) 554253734Speter && serf_sess->detect_chunking && !serf_sess->http10) 555253734Speter SVN_ERR(svn_ra_serf__probe_proxy(serf_sess, pool)); 556253734Speter 557253734Speter return SVN_NO_ERROR; 558251881Speter} 559251881Speter 560251881Speter/* Implements svn_ra__vtable_t.reparent(). */ 561251881Speterstatic svn_error_t * 562251881Spetersvn_ra_serf__reparent(svn_ra_session_t *ra_session, 563251881Speter const char *url, 564251881Speter apr_pool_t *pool) 565251881Speter{ 566251881Speter svn_ra_serf__session_t *session = ra_session->priv; 567251881Speter apr_uri_t new_url; 568251881Speter apr_status_t status; 569251881Speter 570251881Speter /* If it's the URL we already have, wave our hands and do nothing. */ 571251881Speter if (strcmp(session->session_url_str, url) == 0) 572251881Speter { 573251881Speter return SVN_NO_ERROR; 574251881Speter } 575251881Speter 576251881Speter if (!session->repos_root_str) 577251881Speter { 578251881Speter const char *vcc_url; 579251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); 580251881Speter } 581251881Speter 582251881Speter if (!svn_uri__is_ancestor(session->repos_root_str, url)) 583251881Speter { 584251881Speter return svn_error_createf( 585251881Speter SVN_ERR_RA_ILLEGAL_URL, NULL, 586251881Speter _("URL '%s' is not a child of the session's repository root " 587251881Speter "URL '%s'"), url, session->repos_root_str); 588251881Speter } 589251881Speter 590251881Speter status = apr_uri_parse(pool, url, &new_url); 591251881Speter if (status) 592251881Speter { 593251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 594251881Speter _("Illegal repository URL '%s'"), url); 595251881Speter } 596251881Speter 597251881Speter /* Depending the version of apr-util in use, for root paths url.path 598251881Speter will be NULL or "", where serf requires "/". */ 599251881Speter /* ### Maybe we should use a string buffer for these strings so we 600251881Speter ### don't allocate memory in the session on every reparent? */ 601251881Speter if (new_url.path == NULL || new_url.path[0] == '\0') 602251881Speter { 603251881Speter session->session_url.path = apr_pstrdup(session->pool, "/"); 604251881Speter } 605251881Speter else 606251881Speter { 607251881Speter session->session_url.path = apr_pstrdup(session->pool, new_url.path); 608251881Speter } 609251881Speter session->session_url_str = apr_pstrdup(session->pool, url); 610251881Speter 611251881Speter return SVN_NO_ERROR; 612251881Speter} 613251881Speter 614251881Speter/* Implements svn_ra__vtable_t.get_session_url(). */ 615251881Speterstatic svn_error_t * 616251881Spetersvn_ra_serf__get_session_url(svn_ra_session_t *ra_session, 617251881Speter const char **url, 618251881Speter apr_pool_t *pool) 619251881Speter{ 620251881Speter svn_ra_serf__session_t *session = ra_session->priv; 621251881Speter *url = apr_pstrdup(pool, session->session_url_str); 622251881Speter return SVN_NO_ERROR; 623251881Speter} 624251881Speter 625251881Speter/* Implements svn_ra__vtable_t.get_latest_revnum(). */ 626251881Speterstatic svn_error_t * 627251881Spetersvn_ra_serf__get_latest_revnum(svn_ra_session_t *ra_session, 628251881Speter svn_revnum_t *latest_revnum, 629251881Speter apr_pool_t *pool) 630251881Speter{ 631251881Speter svn_ra_serf__session_t *session = ra_session->priv; 632251881Speter 633251881Speter return svn_error_trace(svn_ra_serf__get_youngest_revnum( 634251881Speter latest_revnum, session, pool)); 635251881Speter} 636251881Speter 637251881Speter/* Implements svn_ra__vtable_t.rev_proplist(). */ 638251881Speterstatic svn_error_t * 639251881Spetersvn_ra_serf__rev_proplist(svn_ra_session_t *ra_session, 640251881Speter svn_revnum_t rev, 641251881Speter apr_hash_t **ret_props, 642251881Speter apr_pool_t *pool) 643251881Speter{ 644251881Speter svn_ra_serf__session_t *session = ra_session->priv; 645251881Speter apr_hash_t *props; 646251881Speter const char *propfind_path; 647251881Speter 648251881Speter if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 649251881Speter { 650251881Speter propfind_path = apr_psprintf(pool, "%s/%ld", session->rev_stub, rev); 651251881Speter 652251881Speter /* svn_ra_serf__retrieve_props() wants to added the revision as 653251881Speter a Label to the PROPFIND, which isn't really necessary when 654251881Speter querying a rev-stub URI. *Shrug* Probably okay to leave the 655251881Speter Label, but whatever. */ 656251881Speter rev = SVN_INVALID_REVNUM; 657251881Speter } 658251881Speter else 659251881Speter { 660251881Speter /* Use the VCC as the propfind target path. */ 661251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&propfind_path, session, NULL, pool)); 662251881Speter } 663251881Speter 664251881Speter /* ### fix: fetch hash of *just* the PATH@REV props. no nested hash. */ 665251881Speter SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], 666251881Speter propfind_path, rev, "0", all_props, 667251881Speter pool, pool)); 668251881Speter 669251881Speter SVN_ERR(svn_ra_serf__select_revprops(ret_props, propfind_path, rev, props, 670251881Speter pool, pool)); 671251881Speter 672251881Speter return SVN_NO_ERROR; 673251881Speter} 674251881Speter 675251881Speter/* Implements svn_ra__vtable_t.rev_prop(). */ 676251881Speterstatic svn_error_t * 677251881Spetersvn_ra_serf__rev_prop(svn_ra_session_t *session, 678251881Speter svn_revnum_t rev, 679251881Speter const char *name, 680251881Speter svn_string_t **value, 681251881Speter apr_pool_t *pool) 682251881Speter{ 683251881Speter apr_hash_t *props; 684251881Speter 685251881Speter SVN_ERR(svn_ra_serf__rev_proplist(session, rev, &props, pool)); 686251881Speter 687251881Speter *value = svn_hash_gets(props, name); 688251881Speter 689251881Speter return SVN_NO_ERROR; 690251881Speter} 691251881Speter 692251881Speterstatic svn_error_t * 693251881Speterfetch_path_props(apr_hash_t **props, 694251881Speter svn_ra_serf__session_t *session, 695251881Speter const char *session_relpath, 696251881Speter svn_revnum_t revision, 697251881Speter const svn_ra_serf__dav_props_t *desired_props, 698251881Speter apr_pool_t *result_pool, 699251881Speter apr_pool_t *scratch_pool) 700251881Speter{ 701251881Speter const char *url; 702251881Speter 703251881Speter url = session->session_url.path; 704251881Speter 705251881Speter /* If we have a relative path, append it. */ 706251881Speter if (session_relpath) 707251881Speter url = svn_path_url_add_component2(url, session_relpath, scratch_pool); 708251881Speter 709251881Speter /* If we were given a specific revision, get a URL that refers to that 710251881Speter specific revision (rather than floating with HEAD). */ 711251881Speter if (SVN_IS_VALID_REVNUM(revision)) 712251881Speter { 713251881Speter SVN_ERR(svn_ra_serf__get_stable_url(&url, NULL /* latest_revnum */, 714251881Speter session, NULL /* conn */, 715251881Speter url, revision, 716251881Speter scratch_pool, scratch_pool)); 717251881Speter } 718251881Speter 719251881Speter /* URL is stable, so we use SVN_INVALID_REVNUM since it is now irrelevant. 720251881Speter Or we started with SVN_INVALID_REVNUM and URL may be floating. */ 721251881Speter SVN_ERR(svn_ra_serf__fetch_node_props(props, session->conns[0], 722251881Speter url, SVN_INVALID_REVNUM, 723251881Speter desired_props, 724251881Speter result_pool, scratch_pool)); 725251881Speter 726251881Speter return SVN_NO_ERROR; 727251881Speter} 728251881Speter 729251881Speter/* Implements svn_ra__vtable_t.check_path(). */ 730251881Speterstatic svn_error_t * 731251881Spetersvn_ra_serf__check_path(svn_ra_session_t *ra_session, 732251881Speter const char *rel_path, 733251881Speter svn_revnum_t revision, 734251881Speter svn_node_kind_t *kind, 735251881Speter apr_pool_t *pool) 736251881Speter{ 737251881Speter svn_ra_serf__session_t *session = ra_session->priv; 738251881Speter apr_hash_t *props; 739251881Speter 740251881Speter svn_error_t *err = fetch_path_props(&props, session, rel_path, 741251881Speter revision, check_path_props, 742251881Speter pool, pool); 743251881Speter 744251881Speter if (err && err->apr_err == SVN_ERR_FS_NOT_FOUND) 745251881Speter { 746251881Speter svn_error_clear(err); 747251881Speter *kind = svn_node_none; 748251881Speter } 749251881Speter else 750251881Speter { 751251881Speter /* Any other error, raise to caller. */ 752251881Speter if (err) 753251881Speter return svn_error_trace(err); 754251881Speter 755251881Speter SVN_ERR(svn_ra_serf__get_resource_type(kind, props)); 756251881Speter } 757251881Speter 758251881Speter return SVN_NO_ERROR; 759251881Speter} 760251881Speter 761251881Speter 762251881Speterstruct dirent_walker_baton_t { 763251881Speter /* Update the fields in this entry. */ 764251881Speter svn_dirent_t *entry; 765251881Speter 766251881Speter svn_tristate_t *supports_deadprop_count; 767251881Speter 768251881Speter /* If allocations are necessary, then use this pool. */ 769251881Speter apr_pool_t *result_pool; 770251881Speter}; 771251881Speter 772251881Speterstatic svn_error_t * 773251881Speterdirent_walker(void *baton, 774251881Speter const char *ns, 775251881Speter const char *name, 776251881Speter const svn_string_t *val, 777251881Speter apr_pool_t *scratch_pool) 778251881Speter{ 779251881Speter struct dirent_walker_baton_t *dwb = baton; 780251881Speter 781251881Speter if (strcmp(ns, SVN_DAV_PROP_NS_CUSTOM) == 0) 782251881Speter { 783251881Speter dwb->entry->has_props = TRUE; 784251881Speter } 785251881Speter else if (strcmp(ns, SVN_DAV_PROP_NS_SVN) == 0) 786251881Speter { 787251881Speter dwb->entry->has_props = TRUE; 788251881Speter } 789251881Speter else if (strcmp(ns, SVN_DAV_PROP_NS_DAV) == 0) 790251881Speter { 791251881Speter if(strcmp(name, "deadprop-count") == 0) 792251881Speter { 793251881Speter if (*val->data) 794251881Speter { 795251881Speter apr_int64_t deadprop_count; 796251881Speter SVN_ERR(svn_cstring_atoi64(&deadprop_count, val->data)); 797251881Speter dwb->entry->has_props = deadprop_count > 0; 798251881Speter if (dwb->supports_deadprop_count) 799251881Speter *dwb->supports_deadprop_count = svn_tristate_true; 800251881Speter } 801251881Speter else if (dwb->supports_deadprop_count) 802251881Speter *dwb->supports_deadprop_count = svn_tristate_false; 803251881Speter } 804251881Speter } 805251881Speter else if (strcmp(ns, "DAV:") == 0) 806251881Speter { 807251881Speter if (strcmp(name, SVN_DAV__VERSION_NAME) == 0) 808251881Speter { 809251881Speter dwb->entry->created_rev = SVN_STR_TO_REV(val->data); 810251881Speter } 811251881Speter else if (strcmp(name, "creator-displayname") == 0) 812251881Speter { 813251881Speter dwb->entry->last_author = val->data; 814251881Speter } 815251881Speter else if (strcmp(name, SVN_DAV__CREATIONDATE) == 0) 816251881Speter { 817251881Speter SVN_ERR(svn_time_from_cstring(&dwb->entry->time, 818251881Speter val->data, 819251881Speter dwb->result_pool)); 820251881Speter } 821251881Speter else if (strcmp(name, "getcontentlength") == 0) 822251881Speter { 823251881Speter /* 'getcontentlength' property is empty for directories. */ 824251881Speter if (val->len) 825251881Speter { 826251881Speter SVN_ERR(svn_cstring_atoi64(&dwb->entry->size, val->data)); 827251881Speter } 828251881Speter } 829251881Speter else if (strcmp(name, "resourcetype") == 0) 830251881Speter { 831251881Speter if (strcmp(val->data, "collection") == 0) 832251881Speter { 833251881Speter dwb->entry->kind = svn_node_dir; 834251881Speter } 835251881Speter else 836251881Speter { 837251881Speter dwb->entry->kind = svn_node_file; 838251881Speter } 839251881Speter } 840251881Speter } 841251881Speter 842251881Speter return SVN_NO_ERROR; 843251881Speter} 844251881Speter 845251881Speterstruct path_dirent_visitor_t { 846251881Speter apr_hash_t *full_paths; 847251881Speter apr_hash_t *base_paths; 848251881Speter const char *orig_path; 849251881Speter svn_tristate_t supports_deadprop_count; 850251881Speter apr_pool_t *result_pool; 851251881Speter}; 852251881Speter 853251881Speterstatic svn_error_t * 854251881Speterpath_dirent_walker(void *baton, 855251881Speter const char *path, apr_ssize_t path_len, 856251881Speter const char *ns, apr_ssize_t ns_len, 857251881Speter const char *name, apr_ssize_t name_len, 858251881Speter const svn_string_t *val, 859251881Speter apr_pool_t *pool) 860251881Speter{ 861251881Speter struct path_dirent_visitor_t *dirents = baton; 862251881Speter struct dirent_walker_baton_t dwb; 863251881Speter svn_dirent_t *entry; 864251881Speter 865251881Speter /* Skip our original path. */ 866251881Speter if (strcmp(path, dirents->orig_path) == 0) 867251881Speter { 868251881Speter return SVN_NO_ERROR; 869251881Speter } 870251881Speter 871251881Speter entry = apr_hash_get(dirents->full_paths, path, path_len); 872251881Speter 873251881Speter if (!entry) 874251881Speter { 875251881Speter const char *base_name; 876251881Speter 877251881Speter entry = svn_dirent_create(pool); 878251881Speter 879251881Speter apr_hash_set(dirents->full_paths, path, path_len, entry); 880251881Speter 881251881Speter base_name = svn_path_uri_decode(svn_urlpath__basename(path, pool), 882251881Speter pool); 883251881Speter 884251881Speter svn_hash_sets(dirents->base_paths, base_name, entry); 885251881Speter } 886251881Speter 887251881Speter dwb.entry = entry; 888251881Speter dwb.supports_deadprop_count = &dirents->supports_deadprop_count; 889251881Speter dwb.result_pool = dirents->result_pool; 890251881Speter return svn_error_trace(dirent_walker(&dwb, ns, name, val, pool)); 891251881Speter} 892251881Speter 893251881Speterstatic const svn_ra_serf__dav_props_t * 894251881Speterget_dirent_props(apr_uint32_t dirent_fields, 895251881Speter svn_ra_serf__session_t *session, 896251881Speter apr_pool_t *pool) 897251881Speter{ 898251881Speter svn_ra_serf__dav_props_t *prop; 899251881Speter apr_array_header_t *props = apr_array_make 900251881Speter (pool, 7, sizeof(svn_ra_serf__dav_props_t)); 901251881Speter 902251881Speter if (session->supports_deadprop_count != svn_tristate_false 903251881Speter || ! (dirent_fields & SVN_DIRENT_HAS_PROPS)) 904251881Speter { 905251881Speter if (dirent_fields & SVN_DIRENT_KIND) 906251881Speter { 907251881Speter prop = apr_array_push(props); 908251881Speter prop->namespace = "DAV:"; 909251881Speter prop->name = "resourcetype"; 910251881Speter } 911251881Speter 912251881Speter if (dirent_fields & SVN_DIRENT_SIZE) 913251881Speter { 914251881Speter prop = apr_array_push(props); 915251881Speter prop->namespace = "DAV:"; 916251881Speter prop->name = "getcontentlength"; 917251881Speter } 918251881Speter 919251881Speter if (dirent_fields & SVN_DIRENT_HAS_PROPS) 920251881Speter { 921251881Speter prop = apr_array_push(props); 922251881Speter prop->namespace = SVN_DAV_PROP_NS_DAV; 923251881Speter prop->name = "deadprop-count"; 924251881Speter } 925251881Speter 926251881Speter if (dirent_fields & SVN_DIRENT_CREATED_REV) 927251881Speter { 928251881Speter svn_ra_serf__dav_props_t *p = apr_array_push(props); 929251881Speter p->namespace = "DAV:"; 930251881Speter p->name = SVN_DAV__VERSION_NAME; 931251881Speter } 932251881Speter 933251881Speter if (dirent_fields & SVN_DIRENT_TIME) 934251881Speter { 935251881Speter prop = apr_array_push(props); 936251881Speter prop->namespace = "DAV:"; 937251881Speter prop->name = SVN_DAV__CREATIONDATE; 938251881Speter } 939251881Speter 940251881Speter if (dirent_fields & SVN_DIRENT_LAST_AUTHOR) 941251881Speter { 942251881Speter prop = apr_array_push(props); 943251881Speter prop->namespace = "DAV:"; 944251881Speter prop->name = "creator-displayname"; 945251881Speter } 946251881Speter } 947251881Speter else 948251881Speter { 949251881Speter /* We found an old subversion server that can't handle 950251881Speter the deadprop-count property in the way we expect. 951251881Speter 952251881Speter The neon behavior is to retrieve all properties in this case */ 953251881Speter prop = apr_array_push(props); 954251881Speter prop->namespace = "DAV:"; 955251881Speter prop->name = "allprop"; 956251881Speter } 957251881Speter 958251881Speter prop = apr_array_push(props); 959251881Speter prop->namespace = NULL; 960251881Speter prop->name = NULL; 961251881Speter 962251881Speter return (svn_ra_serf__dav_props_t *) props->elts; 963251881Speter} 964251881Speter 965251881Speter/* Implements svn_ra__vtable_t.stat(). */ 966251881Speterstatic svn_error_t * 967251881Spetersvn_ra_serf__stat(svn_ra_session_t *ra_session, 968251881Speter const char *rel_path, 969251881Speter svn_revnum_t revision, 970251881Speter svn_dirent_t **dirent, 971251881Speter apr_pool_t *pool) 972251881Speter{ 973251881Speter svn_ra_serf__session_t *session = ra_session->priv; 974251881Speter apr_hash_t *props; 975251881Speter svn_error_t *err; 976251881Speter struct dirent_walker_baton_t dwb; 977251881Speter svn_tristate_t deadprop_count = svn_tristate_unknown; 978251881Speter 979251881Speter err = fetch_path_props(&props, 980251881Speter session, rel_path, revision, 981251881Speter get_dirent_props(SVN_DIRENT_ALL, session, pool), 982251881Speter pool, pool); 983251881Speter if (err) 984251881Speter { 985251881Speter if (err->apr_err == SVN_ERR_FS_NOT_FOUND) 986251881Speter { 987251881Speter svn_error_clear(err); 988251881Speter *dirent = NULL; 989251881Speter return SVN_NO_ERROR; 990251881Speter } 991251881Speter else 992251881Speter return svn_error_trace(err); 993251881Speter } 994251881Speter 995251881Speter dwb.entry = svn_dirent_create(pool); 996251881Speter dwb.supports_deadprop_count = &deadprop_count; 997251881Speter dwb.result_pool = pool; 998251881Speter SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); 999251881Speter 1000251881Speter if (deadprop_count == svn_tristate_false 1001251881Speter && session->supports_deadprop_count == svn_tristate_unknown 1002251881Speter && !dwb.entry->has_props) 1003251881Speter { 1004251881Speter /* We have to requery as the server didn't give us the right 1005251881Speter information */ 1006251881Speter session->supports_deadprop_count = svn_tristate_false; 1007251881Speter 1008251881Speter SVN_ERR(fetch_path_props(&props, 1009251881Speter session, rel_path, SVN_INVALID_REVNUM, 1010251881Speter get_dirent_props(SVN_DIRENT_ALL, session, pool), 1011251881Speter pool, pool)); 1012251881Speter 1013251881Speter SVN_ERR(svn_ra_serf__walk_node_props(props, dirent_walker, &dwb, pool)); 1014251881Speter } 1015251881Speter 1016251881Speter if (deadprop_count != svn_tristate_unknown) 1017251881Speter session->supports_deadprop_count = deadprop_count; 1018251881Speter 1019251881Speter *dirent = dwb.entry; 1020251881Speter 1021251881Speter return SVN_NO_ERROR; 1022251881Speter} 1023251881Speter 1024251881Speter/* Reads the 'resourcetype' property from the list PROPS and checks if the 1025251881Speter * resource at PATH@REVISION really is a directory. Returns 1026251881Speter * SVN_ERR_FS_NOT_DIRECTORY if not. 1027251881Speter */ 1028251881Speterstatic svn_error_t * 1029251881Speterresource_is_directory(apr_hash_t *props) 1030251881Speter{ 1031251881Speter svn_node_kind_t kind; 1032251881Speter 1033251881Speter SVN_ERR(svn_ra_serf__get_resource_type(&kind, props)); 1034251881Speter 1035251881Speter if (kind != svn_node_dir) 1036251881Speter { 1037251881Speter return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 1038251881Speter _("Can't get entries of non-directory")); 1039251881Speter } 1040251881Speter 1041251881Speter return SVN_NO_ERROR; 1042251881Speter} 1043251881Speter 1044251881Speter/* Implements svn_ra__vtable_t.get_dir(). */ 1045251881Speterstatic svn_error_t * 1046251881Spetersvn_ra_serf__get_dir(svn_ra_session_t *ra_session, 1047251881Speter apr_hash_t **dirents, 1048251881Speter svn_revnum_t *fetched_rev, 1049251881Speter apr_hash_t **ret_props, 1050251881Speter const char *rel_path, 1051251881Speter svn_revnum_t revision, 1052251881Speter apr_uint32_t dirent_fields, 1053251881Speter apr_pool_t *pool) 1054251881Speter{ 1055251881Speter svn_ra_serf__session_t *session = ra_session->priv; 1056251881Speter const char *path; 1057251881Speter 1058251881Speter path = session->session_url.path; 1059251881Speter 1060251881Speter /* If we have a relative path, URI encode and append it. */ 1061251881Speter if (rel_path) 1062251881Speter { 1063251881Speter path = svn_path_url_add_component2(path, rel_path, pool); 1064251881Speter } 1065251881Speter 1066251881Speter /* If the user specified a peg revision other than HEAD, we have to fetch 1067251881Speter the baseline collection url for that revision. If not, we can use the 1068251881Speter public url. */ 1069251881Speter if (SVN_IS_VALID_REVNUM(revision) || fetched_rev) 1070251881Speter { 1071251881Speter SVN_ERR(svn_ra_serf__get_stable_url(&path, fetched_rev, 1072251881Speter session, NULL /* conn */, 1073251881Speter path, revision, 1074251881Speter pool, pool)); 1075251881Speter revision = SVN_INVALID_REVNUM; 1076251881Speter } 1077251881Speter /* REVISION is always SVN_INVALID_REVNUM */ 1078251881Speter SVN_ERR_ASSERT(!SVN_IS_VALID_REVNUM(revision)); 1079251881Speter 1080251881Speter /* If we're asked for children, fetch them now. */ 1081251881Speter if (dirents) 1082251881Speter { 1083251881Speter struct path_dirent_visitor_t dirent_walk; 1084251881Speter apr_hash_t *props; 1085251881Speter const char *rtype; 1086251881Speter 1087251881Speter /* Always request node kind to check that path is really a 1088251881Speter * directory. 1089251881Speter */ 1090251881Speter dirent_fields |= SVN_DIRENT_KIND; 1091251881Speter SVN_ERR(svn_ra_serf__retrieve_props(&props, session, session->conns[0], 1092251881Speter path, SVN_INVALID_REVNUM, "1", 1093251881Speter get_dirent_props(dirent_fields, 1094251881Speter session, pool), 1095251881Speter pool, pool)); 1096251881Speter 1097251881Speter /* Check if the path is really a directory. */ 1098251881Speter rtype = svn_ra_serf__get_prop(props, path, "DAV:", "resourcetype"); 1099251881Speter if (rtype == NULL || strcmp(rtype, "collection") != 0) 1100251881Speter return svn_error_create(SVN_ERR_FS_NOT_DIRECTORY, NULL, 1101251881Speter _("Can't get entries of non-directory")); 1102251881Speter 1103251881Speter /* We're going to create two hashes to help the walker along. 1104251881Speter * We're going to return the 2nd one back to the caller as it 1105251881Speter * will have the basenames it expects. 1106251881Speter */ 1107251881Speter dirent_walk.full_paths = apr_hash_make(pool); 1108251881Speter dirent_walk.base_paths = apr_hash_make(pool); 1109251881Speter dirent_walk.orig_path = svn_urlpath__canonicalize(path, pool); 1110251881Speter dirent_walk.supports_deadprop_count = svn_tristate_unknown; 1111251881Speter dirent_walk.result_pool = pool; 1112251881Speter 1113251881Speter SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, 1114251881Speter path_dirent_walker, &dirent_walk, 1115251881Speter pool)); 1116251881Speter 1117251881Speter if (dirent_walk.supports_deadprop_count == svn_tristate_false 1118251881Speter && session->supports_deadprop_count == svn_tristate_unknown 1119251881Speter && dirent_fields & SVN_DIRENT_HAS_PROPS) 1120251881Speter { 1121251881Speter /* We have to requery as the server didn't give us the right 1122251881Speter information */ 1123251881Speter session->supports_deadprop_count = svn_tristate_false; 1124251881Speter SVN_ERR(svn_ra_serf__retrieve_props(&props, session, 1125251881Speter session->conns[0], 1126251881Speter path, SVN_INVALID_REVNUM, "1", 1127251881Speter get_dirent_props(dirent_fields, 1128251881Speter session, pool), 1129251881Speter pool, pool)); 1130251881Speter 1131251881Speter apr_hash_clear(dirent_walk.full_paths); 1132251881Speter apr_hash_clear(dirent_walk.base_paths); 1133251881Speter 1134251881Speter SVN_ERR(svn_ra_serf__walk_all_paths(props, SVN_INVALID_REVNUM, 1135251881Speter path_dirent_walker, 1136251881Speter &dirent_walk, pool)); 1137251881Speter } 1138251881Speter 1139251881Speter *dirents = dirent_walk.base_paths; 1140251881Speter 1141251881Speter if (dirent_walk.supports_deadprop_count != svn_tristate_unknown) 1142251881Speter session->supports_deadprop_count = dirent_walk.supports_deadprop_count; 1143251881Speter } 1144251881Speter 1145251881Speter /* If we're asked for the directory properties, fetch them too. */ 1146251881Speter if (ret_props) 1147251881Speter { 1148251881Speter apr_hash_t *props; 1149251881Speter 1150251881Speter SVN_ERR(svn_ra_serf__fetch_node_props(&props, session->conns[0], 1151251881Speter path, SVN_INVALID_REVNUM, 1152251881Speter all_props, 1153251881Speter pool, pool)); 1154251881Speter 1155251881Speter /* Check if the path is really a directory. */ 1156251881Speter SVN_ERR(resource_is_directory(props)); 1157251881Speter 1158251881Speter /* ### flatten_props() does not copy PROPVALUE, but fetch_node_props() 1159251881Speter ### put them into POOL, so we're okay. */ 1160251881Speter SVN_ERR(svn_ra_serf__flatten_props(ret_props, props, pool, pool)); 1161251881Speter } 1162251881Speter 1163251881Speter return SVN_NO_ERROR; 1164251881Speter} 1165251881Speter 1166251881Spetersvn_error_t * 1167251881Spetersvn_ra_serf__get_repos_root(svn_ra_session_t *ra_session, 1168251881Speter const char **url, 1169251881Speter apr_pool_t *pool) 1170251881Speter{ 1171251881Speter svn_ra_serf__session_t *session = ra_session->priv; 1172251881Speter 1173251881Speter if (!session->repos_root_str) 1174251881Speter { 1175251881Speter const char *vcc_url; 1176251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); 1177251881Speter } 1178251881Speter 1179251881Speter *url = session->repos_root_str; 1180251881Speter return SVN_NO_ERROR; 1181251881Speter} 1182251881Speter 1183251881Speter/* TODO: to fetch the uuid from the repository, we need: 1184251881Speter 1. a path that exists in HEAD 1185251881Speter 2. a path that's readable 1186251881Speter 1187251881Speter get_uuid handles the case where a path doesn't exist in HEAD and also the 1188251881Speter case where the root of the repository is not readable. 1189251881Speter However, it does not handle the case where we're fetching path not existing 1190251881Speter in HEAD of a repository with unreadable root directory. 1191251881Speter 1192251881Speter Implements svn_ra__vtable_t.get_uuid(). 1193251881Speter */ 1194251881Speterstatic svn_error_t * 1195251881Spetersvn_ra_serf__get_uuid(svn_ra_session_t *ra_session, 1196251881Speter const char **uuid, 1197251881Speter apr_pool_t *pool) 1198251881Speter{ 1199251881Speter svn_ra_serf__session_t *session = ra_session->priv; 1200251881Speter 1201251881Speter if (!session->uuid) 1202251881Speter { 1203251881Speter const char *vcc_url; 1204251881Speter 1205251881Speter /* We should never get here if we have HTTP v2 support, because 1206251881Speter any server with that support should be transmitting the 1207251881Speter UUID in the initial OPTIONS response. */ 1208251881Speter SVN_ERR_ASSERT(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 1209251881Speter 1210251881Speter /* We're not interested in vcc_url and relative_url, but this call also 1211251881Speter stores the repository's uuid in the session. */ 1212251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, NULL, pool)); 1213251881Speter if (!session->uuid) 1214251881Speter { 1215251881Speter return svn_error_create(SVN_ERR_RA_DAV_RESPONSE_HEADER_BADNESS, NULL, 1216251881Speter _("The UUID property was not found on the " 1217251881Speter "resource or any of its parents")); 1218251881Speter } 1219251881Speter } 1220251881Speter 1221251881Speter *uuid = session->uuid; 1222251881Speter 1223251881Speter return SVN_NO_ERROR; 1224251881Speter} 1225251881Speter 1226251881Speter 1227251881Speterstatic const svn_ra__vtable_t serf_vtable = { 1228251881Speter ra_serf_version, 1229251881Speter ra_serf_get_description, 1230251881Speter ra_serf_get_schemes, 1231251881Speter svn_ra_serf__open, 1232251881Speter svn_ra_serf__reparent, 1233251881Speter svn_ra_serf__get_session_url, 1234251881Speter svn_ra_serf__get_latest_revnum, 1235251881Speter svn_ra_serf__get_dated_revision, 1236251881Speter svn_ra_serf__change_rev_prop, 1237251881Speter svn_ra_serf__rev_proplist, 1238251881Speter svn_ra_serf__rev_prop, 1239251881Speter svn_ra_serf__get_commit_editor, 1240251881Speter svn_ra_serf__get_file, 1241251881Speter svn_ra_serf__get_dir, 1242251881Speter svn_ra_serf__get_mergeinfo, 1243251881Speter svn_ra_serf__do_update, 1244251881Speter svn_ra_serf__do_switch, 1245251881Speter svn_ra_serf__do_status, 1246251881Speter svn_ra_serf__do_diff, 1247251881Speter svn_ra_serf__get_log, 1248251881Speter svn_ra_serf__check_path, 1249251881Speter svn_ra_serf__stat, 1250251881Speter svn_ra_serf__get_uuid, 1251251881Speter svn_ra_serf__get_repos_root, 1252251881Speter svn_ra_serf__get_locations, 1253251881Speter svn_ra_serf__get_location_segments, 1254251881Speter svn_ra_serf__get_file_revs, 1255251881Speter svn_ra_serf__lock, 1256251881Speter svn_ra_serf__unlock, 1257251881Speter svn_ra_serf__get_lock, 1258251881Speter svn_ra_serf__get_locks, 1259251881Speter svn_ra_serf__replay, 1260251881Speter svn_ra_serf__has_capability, 1261251881Speter svn_ra_serf__replay_range, 1262251881Speter svn_ra_serf__get_deleted_rev, 1263251881Speter svn_ra_serf__register_editor_shim_callbacks, 1264251881Speter svn_ra_serf__get_inherited_props 1265251881Speter}; 1266251881Speter 1267251881Spetersvn_error_t * 1268251881Spetersvn_ra_serf__init(const svn_version_t *loader_version, 1269251881Speter const svn_ra__vtable_t **vtable, 1270251881Speter apr_pool_t *pool) 1271251881Speter{ 1272251881Speter static const svn_version_checklist_t checklist[] = 1273251881Speter { 1274251881Speter { "svn_subr", svn_subr_version }, 1275251881Speter { "svn_delta", svn_delta_version }, 1276251881Speter { NULL, NULL } 1277251881Speter }; 1278251881Speter int serf_major; 1279251881Speter int serf_minor; 1280251881Speter int serf_patch; 1281251881Speter 1282262253Speter SVN_ERR(svn_ver_check_list2(ra_serf_version(), checklist, svn_ver_equal)); 1283251881Speter 1284251881Speter /* Simplified version check to make sure we can safely use the 1285251881Speter VTABLE parameter. The RA loader does a more exhaustive check. */ 1286251881Speter if (loader_version->major != SVN_VER_MAJOR) 1287251881Speter { 1288251881Speter return svn_error_createf( 1289251881Speter SVN_ERR_VERSION_MISMATCH, NULL, 1290251881Speter _("Unsupported RA loader version (%d) for ra_serf"), 1291251881Speter loader_version->major); 1292251881Speter } 1293251881Speter 1294251881Speter /* Make sure that we have loaded a compatible library: the MAJOR must 1295251881Speter match, and the minor must be at *least* what we compiled against. 1296251881Speter The patch level is simply ignored. */ 1297251881Speter serf_lib_version(&serf_major, &serf_minor, &serf_patch); 1298251881Speter if (serf_major != SERF_MAJOR_VERSION 1299251881Speter || serf_minor < SERF_MINOR_VERSION) 1300251881Speter { 1301251881Speter return svn_error_createf( 1302251881Speter /* ### should return a unique error */ 1303251881Speter SVN_ERR_VERSION_MISMATCH, NULL, 1304251881Speter _("ra_serf was compiled for serf %d.%d.%d but loaded " 1305251881Speter "an incompatible %d.%d.%d library"), 1306251881Speter SERF_MAJOR_VERSION, SERF_MINOR_VERSION, SERF_PATCH_VERSION, 1307251881Speter serf_major, serf_minor, serf_patch); 1308251881Speter } 1309251881Speter 1310251881Speter *vtable = &serf_vtable; 1311251881Speter 1312251881Speter return SVN_NO_ERROR; 1313251881Speter} 1314251881Speter 1315251881Speter/* Compatibility wrapper for pre-1.2 subversions. Needed? */ 1316251881Speter#define NAME "ra_serf" 1317251881Speter#define DESCRIPTION RA_SERF_DESCRIPTION 1318251881Speter#define VTBL serf_vtable 1319251881Speter#define INITFUNC svn_ra_serf__init 1320251881Speter#define COMPAT_INITFUNC svn_ra_serf_init 1321251881Speter#include "../libsvn_ra/wrapper_template.h" 1322