1251881Speter/* 2251881Speter * util.c : serf utility routines 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#include <assert.h> 27251881Speter 28251881Speter#define APR_WANT_STRFUNC 29251881Speter#include <apr.h> 30251881Speter#include <apr_want.h> 31251881Speter#include <apr_fnmatch.h> 32251881Speter 33251881Speter#include <serf.h> 34251881Speter#include <serf_bucket_types.h> 35251881Speter 36251881Speter#include <expat.h> 37251881Speter 38251881Speter#include "svn_hash.h" 39251881Speter#include "svn_dirent_uri.h" 40251881Speter#include "svn_path.h" 41251881Speter#include "svn_private_config.h" 42251881Speter#include "svn_string.h" 43251881Speter#include "svn_xml.h" 44251881Speter#include "svn_props.h" 45251881Speter#include "svn_dirent_uri.h" 46251881Speter 47251881Speter#include "../libsvn_ra/ra_loader.h" 48251881Speter#include "private/svn_dep_compat.h" 49251881Speter#include "private/svn_fspath.h" 50251881Speter#include "private/svn_subr_private.h" 51251881Speter 52251881Speter#include "ra_serf.h" 53251881Speter 54251881Speter 55251881Speter/* Fix for older expat 1.95.x's that do not define 56251881Speter * XML_STATUS_OK/XML_STATUS_ERROR 57251881Speter */ 58251881Speter#ifndef XML_STATUS_OK 59251881Speter#define XML_STATUS_OK 1 60251881Speter#define XML_STATUS_ERROR 0 61251881Speter#endif 62251881Speter 63251881Speter#ifndef XML_VERSION_AT_LEAST 64251881Speter#define XML_VERSION_AT_LEAST(major,minor,patch) \ 65251881Speter(((major) < XML_MAJOR_VERSION) \ 66251881Speter || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ 67251881Speter || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ 68251881Speter (patch) <= XML_MICRO_VERSION)) 69251881Speter#endif /* APR_VERSION_AT_LEAST */ 70251881Speter 71251881Speter#if XML_VERSION_AT_LEAST(1, 95, 8) 72251881Speter#define EXPAT_HAS_STOPPARSER 73251881Speter#endif 74251881Speter 75251881Speter/* Read/write chunks of this size into the spillbuf. */ 76251881Speter#define PARSE_CHUNK_SIZE 8000 77251881Speter 78251881Speter/* We will store one megabyte in memory, before switching to store content 79251881Speter into a temporary file. */ 80251881Speter#define SPILL_SIZE 1000000 81251881Speter 82251881Speter 83251881Speter/* This structure records pending data for the parser in memory blocks, 84251881Speter and possibly into a temporary file if "too much" content arrives. */ 85251881Speterstruct svn_ra_serf__pending_t { 86251881Speter /* The spillbuf where we record the pending data. */ 87251881Speter svn_spillbuf_t *buf; 88251881Speter 89251881Speter /* This flag is set when the network has reached EOF. The PENDING 90251881Speter processing can then properly detect when parsing has completed. */ 91251881Speter svn_boolean_t network_eof; 92251881Speter}; 93251881Speter 94251881Speter#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \ 95251881Speter && svn_spillbuf__get_size((p)->buf) != 0) 96251881Speter 97251881Speter 98251881Speterstruct expat_ctx_t { 99251881Speter svn_ra_serf__xml_context_t *xmlctx; 100251881Speter XML_Parser parser; 101251881Speter svn_ra_serf__handler_t *handler; 102251881Speter 103251881Speter svn_error_t *inner_error; 104251881Speter 105251881Speter /* Do not use this pool for allocation. It is merely recorded for running 106251881Speter the cleanup handler. */ 107251881Speter apr_pool_t *cleanup_pool; 108251881Speter}; 109251881Speter 110251881Speter 111251881Speterstatic const apr_uint32_t serf_failure_map[][2] = 112251881Speter{ 113251881Speter { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 114251881Speter { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 115251881Speter { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 116251881Speter { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 117251881Speter}; 118251881Speter 119251881Speter/* Return a Subversion failure mask based on FAILURES, a serf SSL 120251881Speter failure mask. If anything in FAILURES is not directly mappable to 121251881Speter Subversion failures, set SVN_AUTH_SSL_OTHER in the returned mask. */ 122251881Speterstatic apr_uint32_t 123251881Speterssl_convert_serf_failures(int failures) 124251881Speter{ 125251881Speter apr_uint32_t svn_failures = 0; 126251881Speter apr_size_t i; 127251881Speter 128251881Speter for (i = 0; i < sizeof(serf_failure_map) / (2 * sizeof(apr_uint32_t)); ++i) 129251881Speter { 130251881Speter if (failures & serf_failure_map[i][0]) 131251881Speter { 132251881Speter svn_failures |= serf_failure_map[i][1]; 133251881Speter failures &= ~serf_failure_map[i][0]; 134251881Speter } 135251881Speter } 136251881Speter 137251881Speter /* Map any remaining failure bits to our OTHER bit. */ 138251881Speter if (failures) 139251881Speter { 140251881Speter svn_failures |= SVN_AUTH_SSL_OTHER; 141251881Speter } 142251881Speter 143251881Speter return svn_failures; 144251881Speter} 145251881Speter 146251881Speter 147251881Speterstatic apr_status_t 148251881Spetersave_error(svn_ra_serf__session_t *session, 149251881Speter svn_error_t *err) 150251881Speter{ 151251881Speter if (err || session->pending_error) 152251881Speter { 153251881Speter session->pending_error = svn_error_compose_create( 154251881Speter session->pending_error, 155251881Speter err); 156251881Speter return session->pending_error->apr_err; 157251881Speter } 158251881Speter 159251881Speter return APR_SUCCESS; 160251881Speter} 161251881Speter 162251881Speter 163251881Speter/* Construct the realmstring, e.g. https://svn.collab.net:443. */ 164251881Speterstatic const char * 165251881Speterconstruct_realm(svn_ra_serf__session_t *session, 166251881Speter apr_pool_t *pool) 167251881Speter{ 168251881Speter const char *realm; 169251881Speter apr_port_t port; 170251881Speter 171251881Speter if (session->session_url.port_str) 172251881Speter { 173251881Speter port = session->session_url.port; 174251881Speter } 175251881Speter else 176251881Speter { 177251881Speter port = apr_uri_port_of_scheme(session->session_url.scheme); 178251881Speter } 179251881Speter 180251881Speter realm = apr_psprintf(pool, "%s://%s:%d", 181251881Speter session->session_url.scheme, 182251881Speter session->session_url.hostname, 183251881Speter port); 184251881Speter 185251881Speter return realm; 186251881Speter} 187251881Speter 188251881Speter/* Convert a hash table containing the fields (as documented in X.509) of an 189251881Speter organisation to a string ORG, allocated in POOL. ORG is as returned by 190251881Speter serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 191251881Speterstatic char * 192251881Speterconvert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 193251881Speter{ 194251881Speter return apr_psprintf(pool, "%s, %s, %s, %s, %s (%s)", 195251881Speter (char*)svn_hash_gets(org, "OU"), 196251881Speter (char*)svn_hash_gets(org, "O"), 197251881Speter (char*)svn_hash_gets(org, "L"), 198251881Speter (char*)svn_hash_gets(org, "ST"), 199251881Speter (char*)svn_hash_gets(org, "C"), 200251881Speter (char*)svn_hash_gets(org, "E")); 201251881Speter} 202251881Speter 203251881Speter/* This function is called on receiving a ssl certificate of a server when 204251881Speter opening a https connection. It allows Subversion to override the initial 205251881Speter validation done by serf. 206251881Speter Serf provides us the @a baton as provided in the call to 207251881Speter serf_ssl_server_cert_callback_set. The result of serf's initial validation 208251881Speter of the certificate @a CERT is returned as a bitmask in FAILURES. */ 209251881Speterstatic svn_error_t * 210251881Speterssl_server_cert(void *baton, int failures, 211251881Speter const serf_ssl_certificate_t *cert, 212251881Speter apr_pool_t *scratch_pool) 213251881Speter{ 214251881Speter svn_ra_serf__connection_t *conn = baton; 215251881Speter svn_auth_ssl_server_cert_info_t cert_info; 216251881Speter svn_auth_cred_ssl_server_trust_t *server_creds = NULL; 217251881Speter svn_auth_iterstate_t *state; 218251881Speter const char *realmstring; 219251881Speter apr_uint32_t svn_failures; 220251881Speter apr_hash_t *issuer, *subject, *serf_cert; 221251881Speter apr_array_header_t *san; 222251881Speter void *creds; 223251881Speter int found_matching_hostname = 0; 224251881Speter 225251881Speter /* Implicitly approve any non-server certs. */ 226251881Speter if (serf_ssl_cert_depth(cert) > 0) 227251881Speter { 228251881Speter if (failures) 229251881Speter conn->server_cert_failures |= ssl_convert_serf_failures(failures); 230251881Speter return APR_SUCCESS; 231251881Speter } 232251881Speter 233251881Speter /* Extract the info from the certificate */ 234251881Speter subject = serf_ssl_cert_subject(cert, scratch_pool); 235251881Speter issuer = serf_ssl_cert_issuer(cert, scratch_pool); 236251881Speter serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 237251881Speter 238251881Speter cert_info.hostname = svn_hash_gets(subject, "CN"); 239251881Speter san = svn_hash_gets(serf_cert, "subjectAltName"); 240251881Speter cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1"); 241251881Speter if (! cert_info.fingerprint) 242251881Speter cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>"); 243251881Speter cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore"); 244251881Speter if (! cert_info.valid_from) 245251881Speter cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]"); 246251881Speter cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter"); 247251881Speter if (! cert_info.valid_until) 248251881Speter cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]"); 249251881Speter cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 250251881Speter cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 251251881Speter 252251881Speter svn_failures = (ssl_convert_serf_failures(failures) 253251881Speter | conn->server_cert_failures); 254251881Speter 255251881Speter /* Try to find matching server name via subjectAltName first... */ 256251881Speter if (san) { 257251881Speter int i; 258251881Speter for (i = 0; i < san->nelts; i++) { 259251881Speter char *s = APR_ARRAY_IDX(san, i, char*); 260251881Speter if (apr_fnmatch(s, conn->session->session_url.hostname, 261251881Speter APR_FNM_PERIOD) == APR_SUCCESS) { 262251881Speter found_matching_hostname = 1; 263251881Speter cert_info.hostname = s; 264251881Speter break; 265251881Speter } 266251881Speter } 267251881Speter } 268251881Speter 269251881Speter /* Match server certificate CN with the hostname of the server */ 270251881Speter if (!found_matching_hostname && cert_info.hostname) 271251881Speter { 272251881Speter if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname, 273251881Speter APR_FNM_PERIOD) == APR_FNM_NOMATCH) 274251881Speter { 275251881Speter svn_failures |= SVN_AUTH_SSL_CNMISMATCH; 276251881Speter } 277251881Speter } 278251881Speter 279251881Speter svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 280251881Speter SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 281251881Speter &svn_failures); 282251881Speter 283251881Speter svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 284251881Speter SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 285251881Speter &cert_info); 286251881Speter 287251881Speter realmstring = construct_realm(conn->session, conn->session->pool); 288251881Speter 289251881Speter SVN_ERR(svn_auth_first_credentials(&creds, &state, 290251881Speter SVN_AUTH_CRED_SSL_SERVER_TRUST, 291251881Speter realmstring, 292251881Speter conn->session->wc_callbacks->auth_baton, 293251881Speter scratch_pool)); 294251881Speter if (creds) 295251881Speter { 296251881Speter server_creds = creds; 297251881Speter SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 298251881Speter } 299251881Speter 300251881Speter svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 301251881Speter SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 302251881Speter 303251881Speter if (!server_creds) 304251881Speter return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, NULL); 305251881Speter 306251881Speter return SVN_NO_ERROR; 307251881Speter} 308251881Speter 309251881Speter/* Implements serf_ssl_need_server_cert_t for ssl_server_cert */ 310251881Speterstatic apr_status_t 311251881Speterssl_server_cert_cb(void *baton, int failures, 312251881Speter const serf_ssl_certificate_t *cert) 313251881Speter{ 314251881Speter svn_ra_serf__connection_t *conn = baton; 315251881Speter svn_ra_serf__session_t *session = conn->session; 316251881Speter apr_pool_t *subpool; 317251881Speter svn_error_t *err; 318251881Speter 319251881Speter subpool = svn_pool_create(session->pool); 320251881Speter err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool)); 321251881Speter svn_pool_destroy(subpool); 322251881Speter 323251881Speter return save_error(session, err); 324251881Speter} 325251881Speter 326251881Speterstatic svn_error_t * 327251881Speterload_authorities(svn_ra_serf__connection_t *conn, const char *authorities, 328251881Speter apr_pool_t *pool) 329251881Speter{ 330251881Speter apr_array_header_t *files = svn_cstring_split(authorities, ";", 331251881Speter TRUE /* chop_whitespace */, 332251881Speter pool); 333251881Speter int i; 334251881Speter 335251881Speter for (i = 0; i < files->nelts; ++i) 336251881Speter { 337251881Speter const char *file = APR_ARRAY_IDX(files, i, const char *); 338251881Speter serf_ssl_certificate_t *ca_cert; 339251881Speter apr_status_t status = serf_ssl_load_cert_file(&ca_cert, file, pool); 340251881Speter 341251881Speter if (status == APR_SUCCESS) 342251881Speter status = serf_ssl_trust_cert(conn->ssl_context, ca_cert); 343251881Speter 344251881Speter if (status != APR_SUCCESS) 345251881Speter { 346251881Speter return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 347251881Speter _("Invalid config: unable to load certificate file '%s'"), 348251881Speter svn_dirent_local_style(file, pool)); 349251881Speter } 350251881Speter } 351251881Speter 352251881Speter return SVN_NO_ERROR; 353251881Speter} 354251881Speter 355251881Speterstatic svn_error_t * 356251881Speterconn_setup(apr_socket_t *sock, 357251881Speter serf_bucket_t **read_bkt, 358251881Speter serf_bucket_t **write_bkt, 359251881Speter void *baton, 360251881Speter apr_pool_t *pool) 361251881Speter{ 362251881Speter svn_ra_serf__connection_t *conn = baton; 363251881Speter 364251881Speter *read_bkt = serf_context_bucket_socket_create(conn->session->context, 365251881Speter sock, conn->bkt_alloc); 366251881Speter 367251881Speter if (conn->session->using_ssl) 368251881Speter { 369251881Speter /* input stream */ 370251881Speter *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context, 371251881Speter conn->bkt_alloc); 372251881Speter if (!conn->ssl_context) 373251881Speter { 374251881Speter conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt); 375251881Speter 376251881Speter serf_ssl_set_hostname(conn->ssl_context, 377251881Speter conn->session->session_url.hostname); 378251881Speter 379251881Speter serf_ssl_client_cert_provider_set(conn->ssl_context, 380251881Speter svn_ra_serf__handle_client_cert, 381251881Speter conn, conn->session->pool); 382251881Speter serf_ssl_client_cert_password_set(conn->ssl_context, 383251881Speter svn_ra_serf__handle_client_cert_pw, 384251881Speter conn, conn->session->pool); 385251881Speter serf_ssl_server_cert_callback_set(conn->ssl_context, 386251881Speter ssl_server_cert_cb, 387251881Speter conn); 388251881Speter 389251881Speter /* See if the user wants us to trust "default" openssl CAs. */ 390251881Speter if (conn->session->trust_default_ca) 391251881Speter { 392251881Speter serf_ssl_use_default_certificates(conn->ssl_context); 393251881Speter } 394251881Speter /* Are there custom CAs to load? */ 395251881Speter if (conn->session->ssl_authorities) 396251881Speter { 397251881Speter SVN_ERR(load_authorities(conn, conn->session->ssl_authorities, 398251881Speter conn->session->pool)); 399251881Speter } 400251881Speter } 401251881Speter 402251881Speter if (write_bkt) 403251881Speter { 404251881Speter /* output stream */ 405251881Speter *write_bkt = serf_bucket_ssl_encrypt_create(*write_bkt, 406251881Speter conn->ssl_context, 407251881Speter conn->bkt_alloc); 408251881Speter } 409251881Speter } 410251881Speter 411251881Speter return SVN_NO_ERROR; 412251881Speter} 413251881Speter 414251881Speter/* svn_ra_serf__conn_setup is a callback for serf. This function 415251881Speter creates a read bucket and will wrap the write bucket if SSL 416251881Speter is needed. */ 417251881Speterapr_status_t 418251881Spetersvn_ra_serf__conn_setup(apr_socket_t *sock, 419251881Speter serf_bucket_t **read_bkt, 420251881Speter serf_bucket_t **write_bkt, 421251881Speter void *baton, 422251881Speter apr_pool_t *pool) 423251881Speter{ 424251881Speter svn_ra_serf__connection_t *conn = baton; 425251881Speter svn_ra_serf__session_t *session = conn->session; 426251881Speter svn_error_t *err; 427251881Speter 428251881Speter err = svn_error_trace(conn_setup(sock, 429251881Speter read_bkt, 430251881Speter write_bkt, 431251881Speter baton, 432251881Speter pool)); 433251881Speter return save_error(session, err); 434251881Speter} 435251881Speter 436251881Speter 437251881Speter/* Our default serf response acceptor. */ 438251881Speterstatic serf_bucket_t * 439251881Speteraccept_response(serf_request_t *request, 440251881Speter serf_bucket_t *stream, 441251881Speter void *acceptor_baton, 442251881Speter apr_pool_t *pool) 443251881Speter{ 444251881Speter serf_bucket_t *c; 445251881Speter serf_bucket_alloc_t *bkt_alloc; 446251881Speter 447251881Speter bkt_alloc = serf_request_get_alloc(request); 448251881Speter c = serf_bucket_barrier_create(stream, bkt_alloc); 449251881Speter 450251881Speter return serf_bucket_response_create(c, bkt_alloc); 451251881Speter} 452251881Speter 453251881Speter 454251881Speter/* Custom response acceptor for HEAD requests. */ 455251881Speterstatic serf_bucket_t * 456251881Speteraccept_head(serf_request_t *request, 457251881Speter serf_bucket_t *stream, 458251881Speter void *acceptor_baton, 459251881Speter apr_pool_t *pool) 460251881Speter{ 461251881Speter serf_bucket_t *response; 462251881Speter 463251881Speter response = accept_response(request, stream, acceptor_baton, pool); 464251881Speter 465251881Speter /* We know we shouldn't get a response body. */ 466251881Speter serf_bucket_response_set_head(response); 467251881Speter 468251881Speter return response; 469251881Speter} 470251881Speter 471251881Speterstatic svn_error_t * 472251881Speterconnection_closed(svn_ra_serf__connection_t *conn, 473251881Speter apr_status_t why, 474251881Speter apr_pool_t *pool) 475251881Speter{ 476251881Speter if (why) 477251881Speter { 478253734Speter return svn_error_wrap_apr(why, NULL); 479251881Speter } 480251881Speter 481251881Speter if (conn->session->using_ssl) 482251881Speter conn->ssl_context = NULL; 483251881Speter 484251881Speter return SVN_NO_ERROR; 485251881Speter} 486251881Speter 487251881Spetervoid 488251881Spetersvn_ra_serf__conn_closed(serf_connection_t *conn, 489251881Speter void *closed_baton, 490251881Speter apr_status_t why, 491251881Speter apr_pool_t *pool) 492251881Speter{ 493251881Speter svn_ra_serf__connection_t *ra_conn = closed_baton; 494251881Speter svn_error_t *err; 495251881Speter 496251881Speter err = svn_error_trace(connection_closed(ra_conn, why, pool)); 497251881Speter 498251881Speter (void) save_error(ra_conn->session, err); 499251881Speter} 500251881Speter 501251881Speter 502251881Speter/* Implementation of svn_ra_serf__handle_client_cert */ 503251881Speterstatic svn_error_t * 504251881Speterhandle_client_cert(void *data, 505251881Speter const char **cert_path, 506251881Speter apr_pool_t *pool) 507251881Speter{ 508251881Speter svn_ra_serf__connection_t *conn = data; 509251881Speter svn_ra_serf__session_t *session = conn->session; 510251881Speter const char *realm; 511251881Speter void *creds; 512251881Speter 513251881Speter *cert_path = NULL; 514251881Speter 515251881Speter realm = construct_realm(session, session->pool); 516251881Speter 517251881Speter if (!conn->ssl_client_auth_state) 518251881Speter { 519251881Speter SVN_ERR(svn_auth_first_credentials(&creds, 520251881Speter &conn->ssl_client_auth_state, 521251881Speter SVN_AUTH_CRED_SSL_CLIENT_CERT, 522251881Speter realm, 523251881Speter session->wc_callbacks->auth_baton, 524251881Speter pool)); 525251881Speter } 526251881Speter else 527251881Speter { 528251881Speter SVN_ERR(svn_auth_next_credentials(&creds, 529251881Speter conn->ssl_client_auth_state, 530251881Speter session->pool)); 531251881Speter } 532251881Speter 533251881Speter if (creds) 534251881Speter { 535251881Speter svn_auth_cred_ssl_client_cert_t *client_creds; 536251881Speter client_creds = creds; 537251881Speter *cert_path = client_creds->cert_file; 538251881Speter } 539251881Speter 540251881Speter return SVN_NO_ERROR; 541251881Speter} 542251881Speter 543251881Speter/* Implements serf_ssl_need_client_cert_t for handle_client_cert */ 544251881Speterapr_status_t svn_ra_serf__handle_client_cert(void *data, 545251881Speter const char **cert_path) 546251881Speter{ 547251881Speter svn_ra_serf__connection_t *conn = data; 548251881Speter svn_ra_serf__session_t *session = conn->session; 549251881Speter svn_error_t *err; 550251881Speter 551251881Speter err = svn_error_trace(handle_client_cert(data, cert_path, session->pool)); 552251881Speter 553251881Speter return save_error(session, err); 554251881Speter} 555251881Speter 556251881Speter/* Implementation for svn_ra_serf__handle_client_cert_pw */ 557251881Speterstatic svn_error_t * 558251881Speterhandle_client_cert_pw(void *data, 559251881Speter const char *cert_path, 560251881Speter const char **password, 561251881Speter apr_pool_t *pool) 562251881Speter{ 563251881Speter svn_ra_serf__connection_t *conn = data; 564251881Speter svn_ra_serf__session_t *session = conn->session; 565251881Speter void *creds; 566251881Speter 567251881Speter *password = NULL; 568251881Speter 569251881Speter if (!conn->ssl_client_pw_auth_state) 570251881Speter { 571251881Speter SVN_ERR(svn_auth_first_credentials(&creds, 572251881Speter &conn->ssl_client_pw_auth_state, 573251881Speter SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 574251881Speter cert_path, 575251881Speter session->wc_callbacks->auth_baton, 576251881Speter pool)); 577251881Speter } 578251881Speter else 579251881Speter { 580251881Speter SVN_ERR(svn_auth_next_credentials(&creds, 581251881Speter conn->ssl_client_pw_auth_state, 582251881Speter pool)); 583251881Speter } 584251881Speter 585251881Speter if (creds) 586251881Speter { 587251881Speter svn_auth_cred_ssl_client_cert_pw_t *pw_creds; 588251881Speter pw_creds = creds; 589251881Speter *password = pw_creds->password; 590251881Speter } 591251881Speter 592251881Speter return APR_SUCCESS; 593251881Speter} 594251881Speter 595251881Speter/* Implements serf_ssl_need_client_cert_pw_t for handle_client_cert_pw */ 596251881Speterapr_status_t svn_ra_serf__handle_client_cert_pw(void *data, 597251881Speter const char *cert_path, 598251881Speter const char **password) 599251881Speter{ 600251881Speter svn_ra_serf__connection_t *conn = data; 601251881Speter svn_ra_serf__session_t *session = conn->session; 602251881Speter svn_error_t *err; 603251881Speter 604251881Speter err = svn_error_trace(handle_client_cert_pw(data, 605251881Speter cert_path, 606251881Speter password, 607251881Speter session->pool)); 608251881Speter 609251881Speter return save_error(session, err); 610251881Speter} 611251881Speter 612251881Speter 613251881Speter/* 614251881Speter * Given a REQUEST on connection CONN, construct a request bucket for it, 615251881Speter * returning the bucket in *REQ_BKT. 616251881Speter * 617251881Speter * If HDRS_BKT is not-NULL, it will be set to a headers_bucket that 618251881Speter * corresponds to the new request. 619251881Speter * 620251881Speter * The request will be METHOD at URL. 621251881Speter * 622251881Speter * If BODY_BKT is not-NULL, it will be sent as the request body. 623251881Speter * 624251881Speter * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 625251881Speter * 626251881Speter * REQUEST_POOL should live for the duration of the request. Serf will 627251881Speter * construct this and provide it to the request_setup callback, so we 628251881Speter * should just use that one. 629251881Speter */ 630251881Speterstatic svn_error_t * 631251881Spetersetup_serf_req(serf_request_t *request, 632251881Speter serf_bucket_t **req_bkt, 633251881Speter serf_bucket_t **hdrs_bkt, 634251881Speter svn_ra_serf__session_t *session, 635251881Speter const char *method, const char *url, 636251881Speter serf_bucket_t *body_bkt, const char *content_type, 637251881Speter const char *accept_encoding, 638251881Speter apr_pool_t *request_pool, 639251881Speter apr_pool_t *scratch_pool) 640251881Speter{ 641251881Speter serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 642251881Speter 643251881Speter svn_spillbuf_t *buf; 644253734Speter svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 645251881Speter 646253734Speter if (set_CL && body_bkt != NULL) 647251881Speter { 648251881Speter /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if 649251881Speter it speaks HTTP/1.1 (and thus, chunked requests), or because the 650251881Speter server actually responded as only supporting HTTP/1.0. 651251881Speter 652251881Speter We'll take the existing body_bkt, spool it into a spillbuf, and 653251881Speter then wrap a bucket around that spillbuf. The spillbuf will give 654251881Speter us the Content-Length value. */ 655251881Speter SVN_ERR(svn_ra_serf__copy_into_spillbuf(&buf, body_bkt, 656251881Speter request_pool, 657251881Speter scratch_pool)); 658251881Speter /* Destroy original bucket since it content is already copied 659251881Speter to spillbuf. */ 660251881Speter serf_bucket_destroy(body_bkt); 661251881Speter 662251881Speter body_bkt = svn_ra_serf__create_sb_bucket(buf, allocator, 663251881Speter request_pool, 664251881Speter scratch_pool); 665251881Speter } 666251881Speter 667251881Speter /* Create a request bucket. Note that this sucker is kind enough to 668251881Speter add a "Host" header for us. */ 669251881Speter *req_bkt = serf_request_bucket_request_create(request, method, url, 670251881Speter body_bkt, allocator); 671251881Speter 672251881Speter /* Set the Content-Length value. This will also trigger an HTTP/1.0 673251881Speter request (rather than the default chunked request). */ 674253734Speter if (set_CL) 675251881Speter { 676251881Speter if (body_bkt == NULL) 677251881Speter serf_bucket_request_set_CL(*req_bkt, 0); 678251881Speter else 679251881Speter serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf)); 680251881Speter } 681251881Speter 682251881Speter *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); 683251881Speter 684251881Speter /* We use serf_bucket_headers_setn() because the USERAGENT has a 685251881Speter lifetime longer than this bucket. Thus, there is no need to copy 686251881Speter the header values. */ 687251881Speter serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent); 688251881Speter 689251881Speter if (content_type) 690251881Speter { 691251881Speter serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type); 692251881Speter } 693251881Speter 694251881Speter if (session->http10) 695253734Speter { 696251881Speter serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 697253734Speter } 698251881Speter 699251881Speter if (accept_encoding) 700251881Speter { 701251881Speter serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 702251881Speter } 703251881Speter 704251881Speter /* These headers need to be sent with every request; see issue #3255 705251881Speter ("mod_dav_svn does not pass client capabilities to start-commit 706251881Speter hooks") for why. */ 707251881Speter serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 708251881Speter serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 709251881Speter serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); 710251881Speter 711251881Speter return SVN_NO_ERROR; 712251881Speter} 713251881Speter 714251881Spetersvn_error_t * 715251881Spetersvn_ra_serf__context_run_wait(svn_boolean_t *done, 716251881Speter svn_ra_serf__session_t *sess, 717251881Speter apr_pool_t *scratch_pool) 718251881Speter{ 719251881Speter apr_pool_t *iterpool; 720251881Speter apr_interval_time_t waittime_left = sess->timeout; 721251881Speter 722251881Speter assert(sess->pending_error == SVN_NO_ERROR); 723251881Speter 724251881Speter iterpool = svn_pool_create(scratch_pool); 725251881Speter while (!*done) 726251881Speter { 727251881Speter apr_status_t status; 728251881Speter svn_error_t *err; 729251881Speter int i; 730251881Speter 731251881Speter svn_pool_clear(iterpool); 732251881Speter 733251881Speter if (sess->cancel_func) 734251881Speter SVN_ERR((*sess->cancel_func)(sess->cancel_baton)); 735251881Speter 736251881Speter status = serf_context_run(sess->context, 737251881Speter SVN_RA_SERF__CONTEXT_RUN_DURATION, 738251881Speter iterpool); 739251881Speter 740251881Speter err = sess->pending_error; 741251881Speter sess->pending_error = SVN_NO_ERROR; 742251881Speter 743251881Speter /* If the context duration timeout is up, we'll subtract that 744251881Speter duration from the total time alloted for such things. If 745251881Speter there's no time left, we fail with a message indicating that 746251881Speter the connection timed out. */ 747251881Speter if (APR_STATUS_IS_TIMEUP(status)) 748251881Speter { 749251881Speter svn_error_clear(err); 750251881Speter err = SVN_NO_ERROR; 751251881Speter status = 0; 752251881Speter 753251881Speter if (sess->timeout) 754251881Speter { 755251881Speter if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 756251881Speter { 757251881Speter waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 758251881Speter } 759251881Speter else 760251881Speter { 761251881Speter return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 762251881Speter _("Connection timed out")); 763251881Speter } 764251881Speter } 765251881Speter } 766251881Speter else 767251881Speter { 768251881Speter waittime_left = sess->timeout; 769251881Speter } 770251881Speter 771251881Speter SVN_ERR(err); 772251881Speter if (status) 773251881Speter { 774251881Speter if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 775251881Speter { 776251881Speter /* apr can't translate subversion errors to text */ 777251881Speter SVN_ERR_W(svn_error_create(status, NULL, NULL), 778251881Speter _("Error running context")); 779251881Speter } 780251881Speter 781251881Speter return svn_ra_serf__wrap_err(status, _("Error running context")); 782251881Speter } 783251881Speter 784251881Speter /* Debugging purposes only! */ 785251881Speter for (i = 0; i < sess->num_conns; i++) 786251881Speter { 787251881Speter serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 788251881Speter } 789251881Speter } 790251881Speter svn_pool_destroy(iterpool); 791251881Speter 792251881Speter return SVN_NO_ERROR; 793251881Speter} 794251881Speter 795251881Speter 796251881Spetersvn_error_t * 797251881Spetersvn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 798251881Speter apr_pool_t *scratch_pool) 799251881Speter{ 800251881Speter svn_error_t *err; 801251881Speter 802251881Speter /* Create a serf request based on HANDLER. */ 803251881Speter svn_ra_serf__request_create(handler); 804251881Speter 805251881Speter /* Wait until the response logic marks its DONE status. */ 806251881Speter err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 807251881Speter scratch_pool); 808251881Speter if (handler->server_error) 809251881Speter { 810251881Speter err = svn_error_compose_create(err, handler->server_error->error); 811251881Speter handler->server_error = NULL; 812251881Speter } 813251881Speter 814251881Speter return svn_error_trace(err); 815251881Speter} 816251881Speter 817251881Speter 818251881Speter/* 819251881Speter * Expat callback invoked on a start element tag for an error response. 820251881Speter */ 821251881Speterstatic svn_error_t * 822251881Speterstart_error(svn_ra_serf__xml_parser_t *parser, 823251881Speter svn_ra_serf__dav_props_t name, 824251881Speter const char **attrs, 825251881Speter apr_pool_t *scratch_pool) 826251881Speter{ 827251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 828251881Speter 829251881Speter if (!ctx->in_error && 830251881Speter strcmp(name.namespace, "DAV:") == 0 && 831251881Speter strcmp(name.name, "error") == 0) 832251881Speter { 833251881Speter ctx->in_error = TRUE; 834251881Speter } 835251881Speter else if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 836251881Speter { 837251881Speter const char *err_code; 838251881Speter 839251881Speter err_code = svn_xml_get_attr_value("errcode", attrs); 840251881Speter if (err_code) 841251881Speter { 842251881Speter apr_int64_t val; 843251881Speter 844251881Speter SVN_ERR(svn_cstring_atoi64(&val, err_code)); 845251881Speter ctx->error->apr_err = (apr_status_t)val; 846251881Speter } 847251881Speter 848251881Speter /* If there's no error code provided, or if the provided code is 849251881Speter 0 (which can happen sometimes depending on how the error is 850251881Speter constructed on the server-side), just pick a generic error 851251881Speter code to run with. */ 852251881Speter if (! ctx->error->apr_err) 853251881Speter { 854251881Speter ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 855251881Speter } 856251881Speter 857251881Speter /* Start collecting cdata. */ 858251881Speter svn_stringbuf_setempty(ctx->cdata); 859251881Speter ctx->collect_cdata = TRUE; 860251881Speter } 861251881Speter 862251881Speter return SVN_NO_ERROR; 863251881Speter} 864251881Speter 865251881Speter/* 866251881Speter * Expat callback invoked on an end element tag for a PROPFIND response. 867251881Speter */ 868251881Speterstatic svn_error_t * 869251881Speterend_error(svn_ra_serf__xml_parser_t *parser, 870251881Speter svn_ra_serf__dav_props_t name, 871251881Speter apr_pool_t *scratch_pool) 872251881Speter{ 873251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 874251881Speter 875251881Speter if (ctx->in_error && 876251881Speter strcmp(name.namespace, "DAV:") == 0 && 877251881Speter strcmp(name.name, "error") == 0) 878251881Speter { 879251881Speter ctx->in_error = FALSE; 880251881Speter } 881251881Speter if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 882251881Speter { 883251881Speter /* On the server dav_error_response_tag() will add a leading 884251881Speter and trailing newline if DEBUG_CR is defined in mod_dav.h, 885251881Speter so remove any such characters here. */ 886251881Speter svn_stringbuf_strip_whitespace(ctx->cdata); 887251881Speter 888251881Speter ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 889251881Speter ctx->cdata->len); 890251881Speter ctx->collect_cdata = FALSE; 891251881Speter } 892251881Speter 893251881Speter return SVN_NO_ERROR; 894251881Speter} 895251881Speter 896251881Speter/* 897251881Speter * Expat callback invoked on CDATA elements in an error response. 898251881Speter * 899251881Speter * This callback can be called multiple times. 900251881Speter */ 901251881Speterstatic svn_error_t * 902251881Spetercdata_error(svn_ra_serf__xml_parser_t *parser, 903251881Speter const char *data, 904251881Speter apr_size_t len, 905251881Speter apr_pool_t *scratch_pool) 906251881Speter{ 907251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 908251881Speter 909251881Speter if (ctx->collect_cdata) 910251881Speter { 911251881Speter svn_stringbuf_appendbytes(ctx->cdata, data, len); 912251881Speter } 913251881Speter 914251881Speter return SVN_NO_ERROR; 915251881Speter} 916251881Speter 917251881Speter 918251881Speterstatic apr_status_t 919251881Speterdrain_bucket(serf_bucket_t *bucket) 920251881Speter{ 921251881Speter /* Read whatever is in the bucket, and just drop it. */ 922251881Speter while (1) 923251881Speter { 924251881Speter apr_status_t status; 925251881Speter const char *data; 926251881Speter apr_size_t len; 927251881Speter 928251881Speter status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 929251881Speter if (status) 930251881Speter return status; 931251881Speter } 932251881Speter} 933251881Speter 934251881Speter 935251881Speterstatic svn_ra_serf__server_error_t * 936251881Speterbegin_error_parsing(svn_ra_serf__xml_start_element_t start, 937251881Speter svn_ra_serf__xml_end_element_t end, 938251881Speter svn_ra_serf__xml_cdata_chunk_handler_t cdata, 939251881Speter apr_pool_t *result_pool) 940251881Speter{ 941251881Speter svn_ra_serf__server_error_t *server_err; 942251881Speter 943251881Speter server_err = apr_pcalloc(result_pool, sizeof(*server_err)); 944251881Speter server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL); 945251881Speter server_err->contains_precondition_error = FALSE; 946251881Speter server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool); 947251881Speter server_err->collect_cdata = FALSE; 948251881Speter server_err->parser.pool = server_err->error->pool; 949251881Speter server_err->parser.user_data = server_err; 950251881Speter server_err->parser.start = start; 951251881Speter server_err->parser.end = end; 952251881Speter server_err->parser.cdata = cdata; 953251881Speter server_err->parser.ignore_errors = TRUE; 954251881Speter 955251881Speter return server_err; 956251881Speter} 957251881Speter 958251881Speter/* Implements svn_ra_serf__response_handler_t */ 959251881Spetersvn_error_t * 960251881Spetersvn_ra_serf__handle_discard_body(serf_request_t *request, 961251881Speter serf_bucket_t *response, 962251881Speter void *baton, 963251881Speter apr_pool_t *pool) 964251881Speter{ 965251881Speter apr_status_t status; 966251881Speter 967251881Speter status = drain_bucket(response); 968251881Speter if (status) 969251881Speter return svn_ra_serf__wrap_err(status, NULL); 970251881Speter 971251881Speter return SVN_NO_ERROR; 972251881Speter} 973251881Speter 974251881Speterapr_status_t 975251881Spetersvn_ra_serf__response_discard_handler(serf_request_t *request, 976251881Speter serf_bucket_t *response, 977251881Speter void *baton, 978251881Speter apr_pool_t *pool) 979251881Speter{ 980251881Speter return drain_bucket(response); 981251881Speter} 982251881Speter 983251881Speter 984251881Speter/* Return the value of the RESPONSE's Location header if any, or NULL 985251881Speter otherwise. */ 986251881Speterstatic const char * 987251881Speterresponse_get_location(serf_bucket_t *response, 988251881Speter const char *base_url, 989251881Speter apr_pool_t *result_pool, 990251881Speter apr_pool_t *scratch_pool) 991251881Speter{ 992251881Speter serf_bucket_t *headers; 993251881Speter const char *location; 994251881Speter 995251881Speter headers = serf_bucket_response_get_headers(response); 996251881Speter location = serf_bucket_headers_get(headers, "Location"); 997251881Speter if (location == NULL) 998251881Speter return NULL; 999251881Speter 1000251881Speter /* The RFCs say we should have received a full url in LOCATION, but 1001251881Speter older apache versions and many custom web handlers just return a 1002251881Speter relative path here... 1003251881Speter 1004251881Speter And we can't trust anything because it is network data. 1005251881Speter */ 1006251881Speter if (*location == '/') 1007251881Speter { 1008251881Speter apr_uri_t uri; 1009251881Speter apr_status_t status; 1010251881Speter 1011251881Speter status = apr_uri_parse(scratch_pool, base_url, &uri); 1012251881Speter 1013251881Speter if (status != APR_SUCCESS) 1014251881Speter return NULL; 1015251881Speter 1016251881Speter /* Replace the path path with what we got */ 1017251881Speter uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool); 1018251881Speter 1019251881Speter /* And make APR produce a proper full url for us */ 1020251881Speter location = apr_uri_unparse(scratch_pool, &uri, 0); 1021251881Speter 1022251881Speter /* Fall through to ensure our canonicalization rules */ 1023251881Speter } 1024251881Speter else if (!svn_path_is_url(location)) 1025251881Speter { 1026251881Speter return NULL; /* Any other formats we should support? */ 1027251881Speter } 1028251881Speter 1029251881Speter return svn_uri_canonicalize(location, result_pool); 1030251881Speter} 1031251881Speter 1032251881Speter 1033251881Speter/* Implements svn_ra_serf__response_handler_t */ 1034251881Spetersvn_error_t * 1035251881Spetersvn_ra_serf__expect_empty_body(serf_request_t *request, 1036251881Speter serf_bucket_t *response, 1037251881Speter void *baton, 1038251881Speter apr_pool_t *scratch_pool) 1039251881Speter{ 1040251881Speter svn_ra_serf__handler_t *handler = baton; 1041251881Speter serf_bucket_t *hdrs; 1042251881Speter const char *val; 1043251881Speter 1044251881Speter /* This function is just like handle_multistatus_only() except for the 1045251881Speter XML parsing callbacks. We want to look for the human-readable element. */ 1046251881Speter 1047251881Speter /* We should see this just once, in order to initialize SERVER_ERROR. 1048251881Speter At that point, the core error processing will take over. If we choose 1049251881Speter not to parse an error, then we'll never return here (because we 1050251881Speter change the response handler). */ 1051251881Speter SVN_ERR_ASSERT(handler->server_error == NULL); 1052251881Speter 1053251881Speter hdrs = serf_bucket_response_get_headers(response); 1054251881Speter val = serf_bucket_headers_get(hdrs, "Content-Type"); 1055251881Speter if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1056251881Speter { 1057251881Speter svn_ra_serf__server_error_t *server_err; 1058251881Speter 1059251881Speter server_err = begin_error_parsing(start_error, end_error, cdata_error, 1060251881Speter handler->handler_pool); 1061251881Speter 1062251881Speter /* Get the parser to set our DONE flag. */ 1063251881Speter server_err->parser.done = &handler->done; 1064251881Speter 1065251881Speter handler->server_error = server_err; 1066251881Speter } 1067251881Speter else 1068251881Speter { 1069251881Speter /* The body was not text/xml, so we don't know what to do with it. 1070251881Speter Toss anything that arrives. */ 1071251881Speter handler->discard_body = TRUE; 1072251881Speter } 1073251881Speter 1074251881Speter /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1075251881Speter to call the response handler again. That will start up the XML parsing, 1076251881Speter or it will be dropped on the floor (per the decision above). */ 1077251881Speter return SVN_NO_ERROR; 1078251881Speter} 1079251881Speter 1080251881Speter 1081251881Speter/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric 1082251881Speter status code into *STATUS_CODE_OUT. Ignores leading whitespace. */ 1083251881Speterstatic svn_error_t * 1084251881Speterparse_dav_status(int *status_code_out, svn_stringbuf_t *buf, 1085251881Speter apr_pool_t *scratch_pool) 1086251881Speter{ 1087251881Speter svn_error_t *err; 1088251881Speter const char *token; 1089251881Speter char *tok_status; 1090251881Speter svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool); 1091251881Speter 1092251881Speter svn_stringbuf_strip_whitespace(temp_buf); 1093251881Speter token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status); 1094251881Speter if (token) 1095251881Speter token = apr_strtok(NULL, " \t\r\n", &tok_status); 1096251881Speter if (!token) 1097251881Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1098251881Speter _("Malformed DAV:status CDATA '%s'"), 1099251881Speter buf->data); 1100251881Speter err = svn_cstring_atoi(status_code_out, token); 1101251881Speter if (err) 1102251881Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err, 1103251881Speter _("Malformed DAV:status CDATA '%s'"), 1104251881Speter buf->data); 1105251881Speter 1106251881Speter return SVN_NO_ERROR; 1107251881Speter} 1108251881Speter 1109251881Speter/* 1110251881Speter * Expat callback invoked on a start element tag for a 207 response. 1111251881Speter */ 1112251881Speterstatic svn_error_t * 1113251881Speterstart_207(svn_ra_serf__xml_parser_t *parser, 1114251881Speter svn_ra_serf__dav_props_t name, 1115251881Speter const char **attrs, 1116251881Speter apr_pool_t *scratch_pool) 1117251881Speter{ 1118251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 1119251881Speter 1120251881Speter if (!ctx->in_error && 1121251881Speter strcmp(name.namespace, "DAV:") == 0 && 1122251881Speter strcmp(name.name, "multistatus") == 0) 1123251881Speter { 1124251881Speter ctx->in_error = TRUE; 1125251881Speter } 1126251881Speter else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1127251881Speter { 1128251881Speter /* Start collecting cdata. */ 1129251881Speter svn_stringbuf_setempty(ctx->cdata); 1130251881Speter ctx->collect_cdata = TRUE; 1131251881Speter } 1132251881Speter else if (ctx->in_error && 1133251881Speter strcmp(name.namespace, "DAV:") == 0 && 1134251881Speter strcmp(name.name, "status") == 0) 1135251881Speter { 1136251881Speter /* Start collecting cdata. */ 1137251881Speter svn_stringbuf_setempty(ctx->cdata); 1138251881Speter ctx->collect_cdata = TRUE; 1139251881Speter } 1140251881Speter 1141251881Speter return SVN_NO_ERROR; 1142251881Speter} 1143251881Speter 1144251881Speter/* 1145251881Speter * Expat callback invoked on an end element tag for a 207 response. 1146251881Speter */ 1147251881Speterstatic svn_error_t * 1148251881Speterend_207(svn_ra_serf__xml_parser_t *parser, 1149251881Speter svn_ra_serf__dav_props_t name, 1150251881Speter apr_pool_t *scratch_pool) 1151251881Speter{ 1152251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 1153251881Speter 1154251881Speter if (ctx->in_error && 1155251881Speter strcmp(name.namespace, "DAV:") == 0 && 1156251881Speter strcmp(name.name, "multistatus") == 0) 1157251881Speter { 1158251881Speter ctx->in_error = FALSE; 1159251881Speter } 1160251881Speter if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1161251881Speter { 1162251881Speter /* Remove leading newline added by DEBUG_CR on server */ 1163251881Speter svn_stringbuf_strip_whitespace(ctx->cdata); 1164251881Speter 1165251881Speter ctx->collect_cdata = FALSE; 1166251881Speter ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 1167251881Speter ctx->cdata->len); 1168251881Speter if (ctx->contains_precondition_error) 1169251881Speter ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH; 1170251881Speter else 1171251881Speter ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1172251881Speter } 1173251881Speter else if (ctx->in_error && 1174251881Speter strcmp(name.namespace, "DAV:") == 0 && 1175251881Speter strcmp(name.name, "status") == 0) 1176251881Speter { 1177251881Speter int status_code; 1178251881Speter 1179251881Speter ctx->collect_cdata = FALSE; 1180251881Speter 1181251881Speter SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool)); 1182251881Speter if (status_code == 412) 1183251881Speter ctx->contains_precondition_error = TRUE; 1184251881Speter } 1185251881Speter 1186251881Speter return SVN_NO_ERROR; 1187251881Speter} 1188251881Speter 1189251881Speter/* 1190251881Speter * Expat callback invoked on CDATA elements in a 207 response. 1191251881Speter * 1192251881Speter * This callback can be called multiple times. 1193251881Speter */ 1194251881Speterstatic svn_error_t * 1195251881Spetercdata_207(svn_ra_serf__xml_parser_t *parser, 1196251881Speter const char *data, 1197251881Speter apr_size_t len, 1198251881Speter apr_pool_t *scratch_pool) 1199251881Speter{ 1200251881Speter svn_ra_serf__server_error_t *ctx = parser->user_data; 1201251881Speter 1202251881Speter if (ctx->collect_cdata) 1203251881Speter { 1204251881Speter svn_stringbuf_appendbytes(ctx->cdata, data, len); 1205251881Speter } 1206251881Speter 1207251881Speter return SVN_NO_ERROR; 1208251881Speter} 1209251881Speter 1210251881Speter/* Implements svn_ra_serf__response_handler_t */ 1211251881Spetersvn_error_t * 1212251881Spetersvn_ra_serf__handle_multistatus_only(serf_request_t *request, 1213251881Speter serf_bucket_t *response, 1214251881Speter void *baton, 1215251881Speter apr_pool_t *scratch_pool) 1216251881Speter{ 1217251881Speter svn_ra_serf__handler_t *handler = baton; 1218251881Speter 1219251881Speter /* This function is just like expect_empty_body() except for the 1220251881Speter XML parsing callbacks. We are looking for very limited pieces of 1221251881Speter the multistatus response. */ 1222251881Speter 1223251881Speter /* We should see this just once, in order to initialize SERVER_ERROR. 1224251881Speter At that point, the core error processing will take over. If we choose 1225251881Speter not to parse an error, then we'll never return here (because we 1226251881Speter change the response handler). */ 1227251881Speter SVN_ERR_ASSERT(handler->server_error == NULL); 1228251881Speter 1229251881Speter { 1230251881Speter serf_bucket_t *hdrs; 1231251881Speter const char *val; 1232251881Speter 1233251881Speter hdrs = serf_bucket_response_get_headers(response); 1234251881Speter val = serf_bucket_headers_get(hdrs, "Content-Type"); 1235251881Speter if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1236251881Speter { 1237251881Speter svn_ra_serf__server_error_t *server_err; 1238251881Speter 1239251881Speter server_err = begin_error_parsing(start_207, end_207, cdata_207, 1240251881Speter handler->handler_pool); 1241251881Speter 1242251881Speter /* Get the parser to set our DONE flag. */ 1243251881Speter server_err->parser.done = &handler->done; 1244251881Speter 1245251881Speter handler->server_error = server_err; 1246251881Speter } 1247251881Speter else 1248251881Speter { 1249251881Speter /* The body was not text/xml, so we don't know what to do with it. 1250251881Speter Toss anything that arrives. */ 1251251881Speter handler->discard_body = TRUE; 1252251881Speter } 1253251881Speter } 1254251881Speter 1255251881Speter /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1256251881Speter to call the response handler again. That will start up the XML parsing, 1257251881Speter or it will be dropped on the floor (per the decision above). */ 1258251881Speter return SVN_NO_ERROR; 1259251881Speter} 1260251881Speter 1261251881Speter 1262251881Speter/* Conforms to Expat's XML_StartElementHandler */ 1263251881Speterstatic void 1264251881Speterstart_xml(void *userData, const char *raw_name, const char **attrs) 1265251881Speter{ 1266251881Speter svn_ra_serf__xml_parser_t *parser = userData; 1267251881Speter svn_ra_serf__dav_props_t name; 1268251881Speter apr_pool_t *scratch_pool; 1269251881Speter svn_error_t *err; 1270251881Speter 1271251881Speter if (parser->error) 1272251881Speter return; 1273251881Speter 1274251881Speter if (!parser->state) 1275251881Speter svn_ra_serf__xml_push_state(parser, 0); 1276251881Speter 1277251881Speter /* ### get a real scratch_pool */ 1278251881Speter scratch_pool = parser->state->pool; 1279251881Speter 1280251881Speter svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool); 1281251881Speter 1282251881Speter svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1283251881Speter 1284251881Speter err = parser->start(parser, name, attrs, scratch_pool); 1285251881Speter if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1286251881Speter err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1287251881Speter 1288251881Speter parser->error = err; 1289251881Speter} 1290251881Speter 1291251881Speter 1292251881Speter/* Conforms to Expat's XML_EndElementHandler */ 1293251881Speterstatic void 1294251881Speterend_xml(void *userData, const char *raw_name) 1295251881Speter{ 1296251881Speter svn_ra_serf__xml_parser_t *parser = userData; 1297251881Speter svn_ra_serf__dav_props_t name; 1298251881Speter svn_error_t *err; 1299251881Speter apr_pool_t *scratch_pool; 1300251881Speter 1301251881Speter if (parser->error) 1302251881Speter return; 1303251881Speter 1304251881Speter /* ### get a real scratch_pool */ 1305251881Speter scratch_pool = parser->state->pool; 1306251881Speter 1307251881Speter svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1308251881Speter 1309251881Speter err = parser->end(parser, name, scratch_pool); 1310251881Speter if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1311251881Speter err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1312251881Speter 1313251881Speter parser->error = err; 1314251881Speter} 1315251881Speter 1316251881Speter 1317251881Speter/* Conforms to Expat's XML_CharacterDataHandler */ 1318251881Speterstatic void 1319251881Spetercdata_xml(void *userData, const char *data, int len) 1320251881Speter{ 1321251881Speter svn_ra_serf__xml_parser_t *parser = userData; 1322251881Speter svn_error_t *err; 1323251881Speter apr_pool_t *scratch_pool; 1324251881Speter 1325251881Speter if (parser->error) 1326251881Speter return; 1327251881Speter 1328251881Speter if (!parser->state) 1329251881Speter svn_ra_serf__xml_push_state(parser, 0); 1330251881Speter 1331251881Speter /* ### get a real scratch_pool */ 1332251881Speter scratch_pool = parser->state->pool; 1333251881Speter 1334251881Speter err = parser->cdata(parser, data, len, scratch_pool); 1335251881Speter if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1336251881Speter err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1337251881Speter 1338251881Speter parser->error = err; 1339251881Speter} 1340251881Speter 1341251881Speter/* Flip the requisite bits in CTX to indicate that processing of the 1342251881Speter response is complete, adding the current "done item" to the list of 1343251881Speter completed items. */ 1344251881Speterstatic void 1345251881Speteradd_done_item(svn_ra_serf__xml_parser_t *ctx) 1346251881Speter{ 1347251881Speter /* Make sure we don't add to DONE_LIST twice. */ 1348251881Speter if (!*ctx->done) 1349251881Speter { 1350251881Speter *ctx->done = TRUE; 1351251881Speter if (ctx->done_list) 1352251881Speter { 1353251881Speter ctx->done_item->data = ctx->user_data; 1354251881Speter ctx->done_item->next = *ctx->done_list; 1355251881Speter *ctx->done_list = ctx->done_item; 1356251881Speter } 1357251881Speter } 1358251881Speter} 1359251881Speter 1360251881Speter 1361251881Speterstatic svn_error_t * 1362251881Speterwrite_to_pending(svn_ra_serf__xml_parser_t *ctx, 1363251881Speter const char *data, 1364251881Speter apr_size_t len, 1365251881Speter apr_pool_t *scratch_pool) 1366251881Speter{ 1367251881Speter if (ctx->pending == NULL) 1368251881Speter { 1369251881Speter ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending)); 1370251881Speter ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE, 1371251881Speter SPILL_SIZE, 1372251881Speter ctx->pool); 1373251881Speter } 1374251881Speter 1375251881Speter /* Copy the data into one or more chunks in the spill buffer. */ 1376251881Speter return svn_error_trace(svn_spillbuf__write(ctx->pending->buf, 1377251881Speter data, len, 1378251881Speter scratch_pool)); 1379251881Speter} 1380251881Speter 1381251881Speter 1382251881Speterstatic svn_error_t * 1383251881Speterinject_to_parser(svn_ra_serf__xml_parser_t *ctx, 1384251881Speter const char *data, 1385251881Speter apr_size_t len, 1386251881Speter const serf_status_line *sl) 1387251881Speter{ 1388251881Speter int xml_status; 1389251881Speter 1390251881Speter xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0); 1391251881Speter if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors) 1392251881Speter { 1393251881Speter if (sl == NULL) 1394251881Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1395251881Speter _("XML parsing failed")); 1396251881Speter 1397251881Speter return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1398251881Speter _("XML parsing failed: (%d %s)"), 1399251881Speter sl->code, sl->reason); 1400251881Speter } 1401251881Speter 1402251881Speter if (ctx->error && !ctx->ignore_errors) 1403251881Speter return svn_error_trace(ctx->error); 1404251881Speter 1405251881Speter return SVN_NO_ERROR; 1406251881Speter} 1407251881Speter 1408251881Speter/* Apr pool cleanup handler to release an XML_Parser in success and error 1409251881Speter conditions */ 1410251881Speterstatic apr_status_t 1411251881Speterxml_parser_cleanup(void *baton) 1412251881Speter{ 1413251881Speter XML_Parser *xmlp = baton; 1414251881Speter 1415251881Speter if (*xmlp) 1416251881Speter { 1417251881Speter (void) XML_ParserFree(*xmlp); 1418251881Speter *xmlp = NULL; 1419251881Speter } 1420251881Speter 1421251881Speter return APR_SUCCESS; 1422251881Speter} 1423251881Speter 1424251881Speter/* Limit the amount of pending content to parse at once to < 100KB per 1425251881Speter iteration. This number is chosen somewhat arbitrarely. Making it lower 1426251881Speter will have a drastical negative impact on performance, whereas increasing it 1427251881Speter increases the risk for connection timeouts. 1428251881Speter */ 1429251881Speter#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5 1430251881Speter 1431251881Spetersvn_error_t * 1432251881Spetersvn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, 1433251881Speter svn_boolean_t *network_eof, 1434251881Speter apr_pool_t *scratch_pool) 1435251881Speter{ 1436251881Speter svn_boolean_t pending_empty = FALSE; 1437251881Speter apr_size_t cur_read = 0; 1438251881Speter 1439251881Speter /* Fast path exit: already paused, nothing to do, or already done. */ 1440251881Speter if (parser->paused || parser->pending == NULL || *parser->done) 1441251881Speter { 1442251881Speter *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1443251881Speter return SVN_NO_ERROR; 1444251881Speter } 1445251881Speter 1446251881Speter /* Parsing the pending conten in the spillbuf will result in many disc i/o 1447251881Speter operations. This can be so slow that we don't run the network event 1448251881Speter processing loop often enough, resulting in timed out connections. 1449251881Speter 1450251881Speter So we limit the amounts of bytes parsed per iteration. 1451251881Speter */ 1452251881Speter while (cur_read < PENDING_TO_PARSE) 1453251881Speter { 1454251881Speter const char *data; 1455251881Speter apr_size_t len; 1456251881Speter 1457251881Speter /* Get a block of content, stopping the loop when we run out. */ 1458251881Speter SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf, 1459251881Speter scratch_pool)); 1460251881Speter if (data) 1461251881Speter { 1462251881Speter /* Inject the content into the XML parser. */ 1463251881Speter SVN_ERR(inject_to_parser(parser, data, len, NULL)); 1464251881Speter 1465251881Speter /* If the XML parsing callbacks paused us, then we're done for now. */ 1466251881Speter if (parser->paused) 1467251881Speter break; 1468251881Speter 1469251881Speter cur_read += len; 1470251881Speter } 1471251881Speter else 1472251881Speter { 1473251881Speter /* The buffer is empty. */ 1474251881Speter pending_empty = TRUE; 1475251881Speter break; 1476251881Speter } 1477251881Speter } 1478251881Speter 1479251881Speter /* If the PENDING structures are empty *and* we consumed all content from 1480251881Speter the network, then we're completely done with the parsing. */ 1481251881Speter if (pending_empty && 1482251881Speter parser->pending->network_eof) 1483251881Speter { 1484251881Speter SVN_ERR_ASSERT(parser->xmlp != NULL); 1485251881Speter 1486251881Speter /* Tell the parser that no more content will be parsed. Ignore the 1487251881Speter return status. We just don't care. */ 1488251881Speter (void) XML_Parse(parser->xmlp, NULL, 0, 1); 1489251881Speter 1490251881Speter apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup); 1491251881Speter parser->xmlp = NULL; 1492251881Speter add_done_item(parser); 1493251881Speter } 1494251881Speter 1495251881Speter *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1496251881Speter 1497251881Speter return SVN_NO_ERROR; 1498251881Speter} 1499251881Speter#undef PENDING_TO_PARSE 1500251881Speter 1501251881Speter 1502251881Speter/* ### this is still broken conceptually. just shifting incrementally... */ 1503251881Speterstatic svn_error_t * 1504251881Speterhandle_server_error(serf_request_t *request, 1505251881Speter serf_bucket_t *response, 1506251881Speter apr_pool_t *scratch_pool) 1507251881Speter{ 1508251881Speter svn_ra_serf__server_error_t server_err = { 0 }; 1509251881Speter serf_bucket_t *hdrs; 1510251881Speter const char *val; 1511251881Speter apr_status_t err; 1512251881Speter 1513251881Speter hdrs = serf_bucket_response_get_headers(response); 1514251881Speter val = serf_bucket_headers_get(hdrs, "Content-Type"); 1515251881Speter if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1516251881Speter { 1517251881Speter /* ### we should figure out how to reuse begin_error_parsing */ 1518251881Speter 1519251881Speter server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL); 1520251881Speter server_err.contains_precondition_error = FALSE; 1521251881Speter server_err.cdata = svn_stringbuf_create_empty(scratch_pool); 1522251881Speter server_err.collect_cdata = FALSE; 1523251881Speter server_err.parser.pool = server_err.error->pool; 1524251881Speter server_err.parser.user_data = &server_err; 1525251881Speter server_err.parser.start = start_error; 1526251881Speter server_err.parser.end = end_error; 1527251881Speter server_err.parser.cdata = cdata_error; 1528251881Speter server_err.parser.done = &server_err.done; 1529251881Speter server_err.parser.ignore_errors = TRUE; 1530251881Speter 1531251881Speter /* We don't care about any errors except for SERVER_ERR.ERROR */ 1532251881Speter svn_error_clear(svn_ra_serf__handle_xml_parser(request, 1533251881Speter response, 1534251881Speter &server_err.parser, 1535251881Speter scratch_pool)); 1536251881Speter 1537251881Speter /* ### checking DONE is silly. the above only parses whatever has 1538251881Speter ### been received at the network interface. totally wrong. but 1539251881Speter ### it is what we have for now (maintaining historical code), 1540251881Speter ### until we fully migrate. */ 1541251881Speter if (server_err.done && server_err.error->apr_err == APR_SUCCESS) 1542251881Speter { 1543251881Speter svn_error_clear(server_err.error); 1544251881Speter server_err.error = SVN_NO_ERROR; 1545251881Speter } 1546251881Speter 1547251881Speter return svn_error_trace(server_err.error); 1548251881Speter } 1549251881Speter 1550251881Speter /* The only error that we will return is from the XML response body. 1551251881Speter Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to 1552251881Speter surface. */ 1553251881Speter err = drain_bucket(response); 1554251881Speter if (err && !SERF_BUCKET_READ_ERROR(err)) 1555251881Speter return svn_ra_serf__wrap_err(err, NULL); 1556251881Speter 1557251881Speter return SVN_NO_ERROR; 1558251881Speter} 1559251881Speter 1560251881Speter 1561251881Speter/* Implements svn_ra_serf__response_handler_t */ 1562251881Spetersvn_error_t * 1563251881Spetersvn_ra_serf__handle_xml_parser(serf_request_t *request, 1564251881Speter serf_bucket_t *response, 1565251881Speter void *baton, 1566251881Speter apr_pool_t *pool) 1567251881Speter{ 1568251881Speter serf_status_line sl; 1569251881Speter apr_status_t status; 1570251881Speter svn_ra_serf__xml_parser_t *ctx = baton; 1571251881Speter svn_error_t *err; 1572251881Speter 1573251881Speter /* ### get the HANDLER rather than fetching this. */ 1574251881Speter status = serf_bucket_response_status(response, &sl); 1575251881Speter if (SERF_BUCKET_READ_ERROR(status)) 1576251881Speter { 1577251881Speter return svn_ra_serf__wrap_err(status, NULL); 1578251881Speter } 1579251881Speter 1580251881Speter /* Woo-hoo. Nothing here to see. */ 1581251881Speter if (sl.code == 404 && !ctx->ignore_errors) 1582251881Speter { 1583251881Speter err = handle_server_error(request, response, pool); 1584251881Speter 1585251881Speter if (err && APR_STATUS_IS_EOF(err->apr_err)) 1586251881Speter add_done_item(ctx); 1587251881Speter 1588251881Speter return svn_error_trace(err); 1589251881Speter } 1590251881Speter 1591251881Speter if (ctx->headers_baton == NULL) 1592251881Speter ctx->headers_baton = serf_bucket_response_get_headers(response); 1593251881Speter else if (ctx->headers_baton != serf_bucket_response_get_headers(response)) 1594251881Speter { 1595251881Speter /* We got a new response to an existing parser... 1596251881Speter This tells us the connection has restarted and we should continue 1597251881Speter where we stopped last time. 1598251881Speter */ 1599251881Speter 1600251881Speter /* Is this a second attempt?? */ 1601251881Speter if (!ctx->skip_size) 1602251881Speter ctx->skip_size = ctx->read_size; 1603251881Speter 1604251881Speter ctx->read_size = 0; /* New request, nothing read */ 1605251881Speter } 1606251881Speter 1607251881Speter if (!ctx->xmlp) 1608251881Speter { 1609251881Speter ctx->xmlp = XML_ParserCreate(NULL); 1610251881Speter apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup, 1611251881Speter apr_pool_cleanup_null); 1612251881Speter XML_SetUserData(ctx->xmlp, ctx); 1613251881Speter XML_SetElementHandler(ctx->xmlp, start_xml, end_xml); 1614251881Speter if (ctx->cdata) 1615251881Speter { 1616251881Speter XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml); 1617251881Speter } 1618251881Speter } 1619251881Speter 1620251881Speter while (1) 1621251881Speter { 1622251881Speter const char *data; 1623251881Speter apr_size_t len; 1624251881Speter 1625251881Speter status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 1626251881Speter 1627251881Speter if (SERF_BUCKET_READ_ERROR(status)) 1628251881Speter { 1629251881Speter return svn_ra_serf__wrap_err(status, NULL); 1630251881Speter } 1631251881Speter 1632251881Speter ctx->read_size += len; 1633251881Speter 1634251881Speter if (ctx->skip_size) 1635251881Speter { 1636251881Speter /* Handle restarted requests correctly: Skip what we already read */ 1637251881Speter apr_size_t skip; 1638251881Speter 1639251881Speter if (ctx->skip_size >= ctx->read_size) 1640251881Speter { 1641251881Speter /* Eek. What did the file shrink or something? */ 1642251881Speter if (APR_STATUS_IS_EOF(status)) 1643251881Speter { 1644251881Speter SVN_ERR_MALFUNCTION(); 1645251881Speter } 1646251881Speter 1647251881Speter /* Skip on to the next iteration of this loop. */ 1648251881Speter if (APR_STATUS_IS_EAGAIN(status)) 1649251881Speter { 1650251881Speter return svn_ra_serf__wrap_err(status, NULL); 1651251881Speter } 1652251881Speter continue; 1653251881Speter } 1654251881Speter 1655251881Speter skip = (apr_size_t)(len - (ctx->read_size - ctx->skip_size)); 1656251881Speter data += skip; 1657251881Speter len -= skip; 1658251881Speter ctx->skip_size = 0; 1659251881Speter } 1660251881Speter 1661251881Speter /* Note: once the callbacks invoked by inject_to_parser() sets the 1662251881Speter PAUSED flag, then it will not be cleared. write_to_pending() will 1663251881Speter only save the content. Logic outside of serf_context_run() will 1664251881Speter clear that flag, as appropriate, along with processing the 1665251881Speter content that we have placed into the PENDING buffer. 1666251881Speter 1667251881Speter We want to save arriving content into the PENDING structures if 1668251881Speter the parser has been paused, or we already have data in there (so 1669251881Speter the arriving data is appended, rather than injected out of order) */ 1670251881Speter if (ctx->paused || HAS_PENDING_DATA(ctx->pending)) 1671251881Speter { 1672251881Speter err = write_to_pending(ctx, data, len, pool); 1673251881Speter } 1674251881Speter else 1675251881Speter { 1676251881Speter err = inject_to_parser(ctx, data, len, &sl); 1677251881Speter if (err) 1678251881Speter { 1679251881Speter /* Should have no errors if IGNORE_ERRORS is set. */ 1680251881Speter SVN_ERR_ASSERT(!ctx->ignore_errors); 1681251881Speter } 1682251881Speter } 1683251881Speter if (err) 1684251881Speter { 1685251881Speter SVN_ERR_ASSERT(ctx->xmlp != NULL); 1686251881Speter 1687251881Speter apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1688251881Speter add_done_item(ctx); 1689251881Speter return svn_error_trace(err); 1690251881Speter } 1691251881Speter 1692251881Speter if (APR_STATUS_IS_EAGAIN(status)) 1693251881Speter { 1694251881Speter return svn_ra_serf__wrap_err(status, NULL); 1695251881Speter } 1696251881Speter 1697251881Speter if (APR_STATUS_IS_EOF(status)) 1698251881Speter { 1699251881Speter if (ctx->pending != NULL) 1700251881Speter ctx->pending->network_eof = TRUE; 1701251881Speter 1702251881Speter /* We just hit the end of the network content. If we have nothing 1703251881Speter in the PENDING structures, then we're completely done. */ 1704251881Speter if (!HAS_PENDING_DATA(ctx->pending)) 1705251881Speter { 1706251881Speter SVN_ERR_ASSERT(ctx->xmlp != NULL); 1707251881Speter 1708251881Speter /* Ignore the return status. We just don't care. */ 1709251881Speter (void) XML_Parse(ctx->xmlp, NULL, 0, 1); 1710251881Speter 1711251881Speter apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1712251881Speter add_done_item(ctx); 1713251881Speter } 1714251881Speter 1715251881Speter return svn_ra_serf__wrap_err(status, NULL); 1716251881Speter } 1717251881Speter 1718251881Speter /* feed me! */ 1719251881Speter } 1720251881Speter /* not reached */ 1721251881Speter} 1722251881Speter 1723251881Speter 1724251881Speterapr_status_t 1725251881Spetersvn_ra_serf__credentials_callback(char **username, char **password, 1726251881Speter serf_request_t *request, void *baton, 1727251881Speter int code, const char *authn_type, 1728251881Speter const char *realm, 1729251881Speter apr_pool_t *pool) 1730251881Speter{ 1731251881Speter svn_ra_serf__handler_t *handler = baton; 1732251881Speter svn_ra_serf__session_t *session = handler->session; 1733251881Speter void *creds; 1734251881Speter svn_auth_cred_simple_t *simple_creds; 1735251881Speter svn_error_t *err; 1736251881Speter 1737251881Speter if (code == 401) 1738251881Speter { 1739251881Speter /* Use svn_auth_first_credentials if this is the first time we ask for 1740251881Speter credentials during this session OR if the last time we asked 1741251881Speter session->auth_state wasn't set (eg. if the credentials provider was 1742251881Speter cancelled by the user). */ 1743251881Speter if (!session->auth_state) 1744251881Speter { 1745251881Speter err = svn_auth_first_credentials(&creds, 1746251881Speter &session->auth_state, 1747251881Speter SVN_AUTH_CRED_SIMPLE, 1748251881Speter realm, 1749251881Speter session->wc_callbacks->auth_baton, 1750251881Speter session->pool); 1751251881Speter } 1752251881Speter else 1753251881Speter { 1754251881Speter err = svn_auth_next_credentials(&creds, 1755251881Speter session->auth_state, 1756251881Speter session->pool); 1757251881Speter } 1758251881Speter 1759251881Speter if (err) 1760251881Speter { 1761251881Speter (void) save_error(session, err); 1762251881Speter return err->apr_err; 1763251881Speter } 1764251881Speter 1765251881Speter session->auth_attempts++; 1766251881Speter 1767251881Speter if (!creds || session->auth_attempts > 4) 1768251881Speter { 1769251881Speter /* No more credentials. */ 1770251881Speter (void) save_error(session, 1771251881Speter svn_error_create( 1772251881Speter SVN_ERR_AUTHN_FAILED, NULL, 1773251881Speter _("No more credentials or we tried too many " 1774251881Speter "times.\nAuthentication failed"))); 1775251881Speter return SVN_ERR_AUTHN_FAILED; 1776251881Speter } 1777251881Speter 1778251881Speter simple_creds = creds; 1779251881Speter *username = apr_pstrdup(pool, simple_creds->username); 1780251881Speter *password = apr_pstrdup(pool, simple_creds->password); 1781251881Speter } 1782251881Speter else 1783251881Speter { 1784251881Speter *username = apr_pstrdup(pool, session->proxy_username); 1785251881Speter *password = apr_pstrdup(pool, session->proxy_password); 1786251881Speter 1787251881Speter session->proxy_auth_attempts++; 1788251881Speter 1789251881Speter if (!session->proxy_username || session->proxy_auth_attempts > 4) 1790251881Speter { 1791251881Speter /* No more credentials. */ 1792251881Speter (void) save_error(session, 1793251881Speter svn_error_create( 1794251881Speter SVN_ERR_AUTHN_FAILED, NULL, 1795251881Speter _("Proxy authentication failed"))); 1796251881Speter return SVN_ERR_AUTHN_FAILED; 1797251881Speter } 1798251881Speter } 1799251881Speter 1800251881Speter handler->conn->last_status_code = code; 1801251881Speter 1802251881Speter return APR_SUCCESS; 1803251881Speter} 1804251881Speter 1805251881Speter/* Wait for HTTP response status and headers, and invoke HANDLER-> 1806251881Speter response_handler() to carry out operation-specific processing. 1807251881Speter Afterwards, check for connection close. 1808251881Speter 1809251881Speter SERF_STATUS allows returning errors to serf without creating a 1810251881Speter subversion error object. 1811251881Speter */ 1812251881Speterstatic svn_error_t * 1813251881Speterhandle_response(serf_request_t *request, 1814251881Speter serf_bucket_t *response, 1815251881Speter svn_ra_serf__handler_t *handler, 1816251881Speter apr_status_t *serf_status, 1817251881Speter apr_pool_t *scratch_pool) 1818251881Speter{ 1819251881Speter apr_status_t status; 1820251881Speter svn_error_t *err; 1821251881Speter 1822251881Speter /* ### need to verify whether this already gets init'd on every 1823251881Speter ### successful exit. for an error-exit, it will (properly) be 1824251881Speter ### ignored by the caller. */ 1825251881Speter *serf_status = APR_SUCCESS; 1826251881Speter 1827251881Speter if (!response) 1828251881Speter { 1829251881Speter /* Uh-oh. Our connection died. */ 1830251881Speter if (handler->response_error) 1831251881Speter SVN_ERR(handler->response_error(request, response, 0, 1832251881Speter handler->response_error_baton)); 1833251881Speter 1834251881Speter /* Requeue another request for this handler. 1835251881Speter ### how do we know if the handler can deal with this?! */ 1836251881Speter svn_ra_serf__request_create(handler); 1837251881Speter 1838251881Speter return SVN_NO_ERROR; 1839251881Speter } 1840251881Speter 1841251881Speter /* If we're reading the body, then skip all this preparation. */ 1842251881Speter if (handler->reading_body) 1843251881Speter goto process_body; 1844251881Speter 1845251881Speter /* Copy the Status-Line info into HANDLER, if we don't yet have it. */ 1846251881Speter if (handler->sline.version == 0) 1847251881Speter { 1848251881Speter serf_status_line sl; 1849251881Speter 1850251881Speter status = serf_bucket_response_status(response, &sl); 1851251881Speter if (status != APR_SUCCESS) 1852251881Speter { 1853251881Speter /* The response line is not (yet) ready, or some other error. */ 1854251881Speter *serf_status = status; 1855251881Speter return SVN_NO_ERROR; /* Handled by serf */ 1856251881Speter } 1857251881Speter 1858251881Speter /* If we got APR_SUCCESS, then we should have Status-Line info. */ 1859251881Speter SVN_ERR_ASSERT(sl.version != 0); 1860251881Speter 1861251881Speter handler->sline = sl; 1862251881Speter handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason); 1863251881Speter 1864251881Speter /* HTTP/1.1? (or later) */ 1865251881Speter if (sl.version != SERF_HTTP_10) 1866251881Speter handler->session->http10 = FALSE; 1867251881Speter } 1868251881Speter 1869251881Speter /* Keep reading from the network until we've read all the headers. */ 1870251881Speter status = serf_bucket_response_wait_for_headers(response); 1871251881Speter if (status) 1872251881Speter { 1873251881Speter /* The typical "error" will be APR_EAGAIN, meaning that more input 1874251881Speter from the network is required to complete the reading of the 1875251881Speter headers. */ 1876251881Speter if (!APR_STATUS_IS_EOF(status)) 1877251881Speter { 1878251881Speter /* Either the headers are not (yet) complete, or there really 1879251881Speter was an error. */ 1880251881Speter *serf_status = status; 1881251881Speter return SVN_NO_ERROR; 1882251881Speter } 1883251881Speter 1884251881Speter /* wait_for_headers() will return EOF if there is no body in this 1885251881Speter response, or if we completely read the body. The latter is not 1886251881Speter true since we would have set READING_BODY to get the body read, 1887251881Speter and we would not be back to this code block. 1888251881Speter 1889251881Speter It can also return EOF if we truly hit EOF while (say) processing 1890251881Speter the headers. aka Badness. */ 1891251881Speter 1892251881Speter /* Cases where a lack of a response body (via EOF) is okay: 1893251881Speter * - A HEAD request 1894251881Speter * - 204/304 response 1895251881Speter * 1896251881Speter * Otherwise, if we get an EOF here, something went really wrong: either 1897251881Speter * the server closed on us early or we're reading too much. Either way, 1898251881Speter * scream loudly. 1899251881Speter */ 1900251881Speter if (strcmp(handler->method, "HEAD") != 0 1901251881Speter && handler->sline.code != 204 1902251881Speter && handler->sline.code != 304) 1903251881Speter { 1904251881Speter err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, 1905251881Speter svn_ra_serf__wrap_err(status, NULL), 1906251881Speter _("Premature EOF seen from server" 1907251881Speter " (http status=%d)"), 1908251881Speter handler->sline.code); 1909251881Speter 1910251881Speter /* In case anything else arrives... discard it. */ 1911251881Speter handler->discard_body = TRUE; 1912251881Speter 1913251881Speter return err; 1914251881Speter } 1915251881Speter } 1916251881Speter 1917251881Speter /* ... and set up the header fields in HANDLER. */ 1918251881Speter handler->location = response_get_location(response, 1919251881Speter handler->session->session_url_str, 1920251881Speter handler->handler_pool, 1921251881Speter scratch_pool); 1922251881Speter 1923251881Speter /* On the last request, we failed authentication. We succeeded this time, 1924251881Speter so let's save away these credentials. */ 1925251881Speter if (handler->conn->last_status_code == 401 && handler->sline.code < 400) 1926251881Speter { 1927251881Speter SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 1928251881Speter handler->session->pool)); 1929251881Speter handler->session->auth_attempts = 0; 1930251881Speter handler->session->auth_state = NULL; 1931251881Speter } 1932251881Speter handler->conn->last_status_code = handler->sline.code; 1933251881Speter 1934251881Speter if (handler->sline.code == 405 1935251881Speter || handler->sline.code == 408 1936251881Speter || handler->sline.code == 409 1937251881Speter || handler->sline.code >= 500) 1938251881Speter { 1939251881Speter /* 405 Method Not allowed. 1940251881Speter 408 Request Timeout 1941251881Speter 409 Conflict: can indicate a hook error. 1942251881Speter 5xx (Internal) Server error. */ 1943251881Speter serf_bucket_t *hdrs; 1944251881Speter const char *val; 1945251881Speter 1946251881Speter hdrs = serf_bucket_response_get_headers(response); 1947251881Speter val = serf_bucket_headers_get(hdrs, "Content-Type"); 1948251881Speter if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1949251881Speter { 1950251881Speter svn_ra_serf__server_error_t *server_err; 1951251881Speter 1952251881Speter server_err = begin_error_parsing(start_error, end_error, cdata_error, 1953251881Speter handler->handler_pool); 1954251881Speter /* Get the parser to set our DONE flag. */ 1955251881Speter server_err->parser.done = &handler->done; 1956251881Speter 1957251881Speter handler->server_error = server_err; 1958251881Speter } 1959251881Speter else 1960251881Speter { 1961251881Speter handler->discard_body = TRUE; 1962251881Speter 1963251881Speter if (!handler->session->pending_error) 1964251881Speter { 1965251881Speter apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1966251881Speter 1967251881Speter /* 405 == Method Not Allowed (Occurs when trying to lock a working 1968251881Speter copy path which no longer exists at HEAD in the repository. */ 1969251881Speter if (handler->sline.code == 405 1970251881Speter && strcmp(handler->method, "LOCK") == 0) 1971251881Speter apr_err = SVN_ERR_FS_OUT_OF_DATE; 1972251881Speter 1973251881Speter handler->session->pending_error = 1974251881Speter svn_error_createf(apr_err, NULL, 1975251881Speter _("%s request on '%s' failed: %d %s"), 1976251881Speter handler->method, handler->path, 1977251881Speter handler->sline.code, handler->sline.reason); 1978251881Speter } 1979251881Speter } 1980251881Speter } 1981251881Speter 1982251881Speter /* Stop processing the above, on every packet arrival. */ 1983251881Speter handler->reading_body = TRUE; 1984251881Speter 1985251881Speter process_body: 1986251881Speter 1987251881Speter /* We've been instructed to ignore the body. Drain whatever is present. */ 1988251881Speter if (handler->discard_body) 1989251881Speter { 1990251881Speter *serf_status = drain_bucket(response); 1991251881Speter 1992251881Speter /* If the handler hasn't set done (which it shouldn't have) and 1993251881Speter we now have the EOF, go ahead and set it so that we can stop 1994251881Speter our context loops. 1995251881Speter */ 1996251881Speter if (!handler->done && APR_STATUS_IS_EOF(*serf_status)) 1997251881Speter handler->done = TRUE; 1998251881Speter 1999251881Speter return SVN_NO_ERROR; 2000251881Speter } 2001251881Speter 2002251881Speter /* If we are supposed to parse the body as a server_error, then do 2003251881Speter that now. */ 2004251881Speter if (handler->server_error != NULL) 2005251881Speter { 2006251881Speter err = svn_ra_serf__handle_xml_parser(request, response, 2007251881Speter &handler->server_error->parser, 2008251881Speter scratch_pool); 2009251881Speter 2010251881Speter /* If we do not receive an error or it is a non-transient error, return 2011251881Speter immediately. 2012251881Speter 2013251881Speter APR_EOF will be returned when parsing is complete. 2014251881Speter 2015251881Speter APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through 2016251881Speter parsing and the network has no more data right now. If we receive that, 2017251881Speter clear the error and return - allowing serf to wait for more data. 2018251881Speter */ 2019251881Speter if (!err || SERF_BUCKET_READ_ERROR(err->apr_err)) 2020251881Speter return svn_error_trace(err); 2021251881Speter 2022251881Speter if (!APR_STATUS_IS_EOF(err->apr_err)) 2023251881Speter { 2024251881Speter *serf_status = err->apr_err; 2025251881Speter svn_error_clear(err); 2026251881Speter return SVN_NO_ERROR; 2027251881Speter } 2028251881Speter 2029251881Speter /* Clear the EOF. We don't need it. */ 2030251881Speter svn_error_clear(err); 2031251881Speter 2032251881Speter /* If the parsing is done, and we did not extract an error, then 2033251881Speter simply toss everything, and anything else that might arrive. 2034251881Speter The higher-level code will need to investigate HANDLER->SLINE, 2035251881Speter as we have no further information for them. */ 2036251881Speter if (handler->done 2037251881Speter && handler->server_error->error->apr_err == APR_SUCCESS) 2038251881Speter { 2039251881Speter svn_error_clear(handler->server_error->error); 2040251881Speter 2041251881Speter /* Stop parsing for a server error. */ 2042251881Speter handler->server_error = NULL; 2043251881Speter 2044251881Speter /* If anything arrives after this, then just discard it. */ 2045251881Speter handler->discard_body = TRUE; 2046251881Speter } 2047251881Speter 2048251881Speter *serf_status = APR_EOF; 2049251881Speter return SVN_NO_ERROR; 2050251881Speter } 2051251881Speter 2052251881Speter /* Pass the body along to the registered response handler. */ 2053251881Speter err = handler->response_handler(request, response, 2054251881Speter handler->response_baton, 2055251881Speter scratch_pool); 2056251881Speter 2057251881Speter if (err 2058251881Speter && (!SERF_BUCKET_READ_ERROR(err->apr_err) 2059251881Speter || APR_STATUS_IS_ECONNRESET(err->apr_err) 2060251881Speter || APR_STATUS_IS_ECONNABORTED(err->apr_err))) 2061251881Speter { 2062251881Speter /* These errors are special cased in serf 2063251881Speter ### We hope no handler returns these by accident. */ 2064251881Speter *serf_status = err->apr_err; 2065251881Speter svn_error_clear(err); 2066251881Speter return SVN_NO_ERROR; 2067251881Speter } 2068251881Speter 2069251881Speter return svn_error_trace(err); 2070251881Speter} 2071251881Speter 2072251881Speter 2073251881Speter/* Implements serf_response_handler_t for handle_response. Storing 2074251881Speter errors in handler->session->pending_error if appropriate. */ 2075251881Speterstatic apr_status_t 2076251881Speterhandle_response_cb(serf_request_t *request, 2077251881Speter serf_bucket_t *response, 2078251881Speter void *baton, 2079251881Speter apr_pool_t *scratch_pool) 2080251881Speter{ 2081251881Speter svn_ra_serf__handler_t *handler = baton; 2082251881Speter svn_error_t *err; 2083251881Speter apr_status_t inner_status; 2084251881Speter apr_status_t outer_status; 2085251881Speter 2086251881Speter err = svn_error_trace(handle_response(request, response, 2087251881Speter handler, &inner_status, 2088251881Speter scratch_pool)); 2089251881Speter 2090251881Speter /* Select the right status value to return. */ 2091251881Speter outer_status = save_error(handler->session, err); 2092251881Speter if (!outer_status) 2093251881Speter outer_status = inner_status; 2094251881Speter 2095251881Speter /* Make sure the DONE flag is set properly. */ 2096251881Speter if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) 2097251881Speter handler->done = TRUE; 2098251881Speter 2099251881Speter return outer_status; 2100251881Speter} 2101251881Speter 2102251881Speter/* Perform basic request setup, with special handling for HEAD requests, 2103251881Speter and finer-grained callbacks invoked (if non-NULL) to produce the request 2104251881Speter headers and body. */ 2105251881Speterstatic svn_error_t * 2106251881Spetersetup_request(serf_request_t *request, 2107251881Speter svn_ra_serf__handler_t *handler, 2108251881Speter serf_bucket_t **req_bkt, 2109251881Speter apr_pool_t *request_pool, 2110251881Speter apr_pool_t *scratch_pool) 2111251881Speter{ 2112251881Speter serf_bucket_t *body_bkt; 2113251881Speter serf_bucket_t *headers_bkt; 2114251881Speter const char *accept_encoding; 2115251881Speter 2116251881Speter if (handler->body_delegate) 2117251881Speter { 2118251881Speter serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 2119251881Speter 2120251881Speter /* ### should pass the scratch_pool */ 2121251881Speter SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, 2122251881Speter bkt_alloc, request_pool)); 2123251881Speter } 2124251881Speter else 2125251881Speter { 2126251881Speter body_bkt = NULL; 2127251881Speter } 2128251881Speter 2129251881Speter if (handler->custom_accept_encoding) 2130251881Speter { 2131251881Speter accept_encoding = NULL; 2132251881Speter } 2133251881Speter else if (handler->session->using_compression) 2134251881Speter { 2135251881Speter /* Accept gzip compression if enabled. */ 2136251881Speter accept_encoding = "gzip"; 2137251881Speter } 2138251881Speter else 2139251881Speter { 2140251881Speter accept_encoding = NULL; 2141251881Speter } 2142251881Speter 2143251881Speter SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 2144251881Speter handler->session, handler->method, handler->path, 2145251881Speter body_bkt, handler->body_type, accept_encoding, 2146251881Speter request_pool, scratch_pool)); 2147251881Speter 2148251881Speter if (handler->header_delegate) 2149251881Speter { 2150251881Speter /* ### should pass the scratch_pool */ 2151251881Speter SVN_ERR(handler->header_delegate(headers_bkt, 2152251881Speter handler->header_delegate_baton, 2153251881Speter request_pool)); 2154251881Speter } 2155251881Speter 2156251881Speter return APR_SUCCESS; 2157251881Speter} 2158251881Speter 2159251881Speter/* Implements the serf_request_setup_t interface (which sets up both a 2160251881Speter request and its response handler callback). Handles errors for 2161251881Speter setup_request_cb */ 2162251881Speterstatic apr_status_t 2163251881Spetersetup_request_cb(serf_request_t *request, 2164251881Speter void *setup_baton, 2165251881Speter serf_bucket_t **req_bkt, 2166251881Speter serf_response_acceptor_t *acceptor, 2167251881Speter void **acceptor_baton, 2168251881Speter serf_response_handler_t *s_handler, 2169251881Speter void **s_handler_baton, 2170251881Speter apr_pool_t *pool) 2171251881Speter{ 2172251881Speter svn_ra_serf__handler_t *handler = setup_baton; 2173251881Speter svn_error_t *err; 2174251881Speter 2175251881Speter /* ### construct a scratch_pool? serf gives us a pool that will live for 2176251881Speter ### the duration of the request. */ 2177251881Speter apr_pool_t *scratch_pool = pool; 2178251881Speter 2179251881Speter if (strcmp(handler->method, "HEAD") == 0) 2180251881Speter *acceptor = accept_head; 2181251881Speter else 2182251881Speter *acceptor = accept_response; 2183251881Speter *acceptor_baton = handler->session; 2184251881Speter 2185251881Speter *s_handler = handle_response_cb; 2186251881Speter *s_handler_baton = handler; 2187251881Speter 2188251881Speter err = svn_error_trace(setup_request(request, handler, req_bkt, 2189251881Speter pool /* request_pool */, scratch_pool)); 2190251881Speter 2191251881Speter return save_error(handler->session, err); 2192251881Speter} 2193251881Speter 2194251881Spetervoid 2195251881Spetersvn_ra_serf__request_create(svn_ra_serf__handler_t *handler) 2196251881Speter{ 2197251881Speter SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL); 2198251881Speter 2199251881Speter /* In case HANDLER is re-queued, reset the various transient fields. 2200251881Speter 2201251881Speter ### prior to recent changes, HANDLER was constant. maybe we should 2202251881Speter ### break out these processing fields, apart from the request 2203251881Speter ### definition. */ 2204251881Speter handler->done = FALSE; 2205251881Speter handler->server_error = NULL; 2206251881Speter handler->sline.version = 0; 2207251881Speter handler->location = NULL; 2208251881Speter handler->reading_body = FALSE; 2209251881Speter handler->discard_body = FALSE; 2210251881Speter 2211251881Speter /* ### do we ever alter the >response_handler? */ 2212251881Speter 2213251881Speter /* ### do we need to hold onto the returned request object, or just 2214251881Speter ### not worry about it (the serf ctx will manage it). */ 2215251881Speter (void) serf_connection_request_create(handler->conn->conn, 2216251881Speter setup_request_cb, handler); 2217251881Speter} 2218251881Speter 2219251881Speter 2220251881Spetersvn_error_t * 2221251881Spetersvn_ra_serf__discover_vcc(const char **vcc_url, 2222251881Speter svn_ra_serf__session_t *session, 2223251881Speter svn_ra_serf__connection_t *conn, 2224251881Speter apr_pool_t *pool) 2225251881Speter{ 2226251881Speter const char *path; 2227251881Speter const char *relative_path; 2228251881Speter const char *uuid; 2229251881Speter 2230251881Speter /* If we've already got the information our caller seeks, just return it. */ 2231251881Speter if (session->vcc_url && session->repos_root_str) 2232251881Speter { 2233251881Speter *vcc_url = session->vcc_url; 2234251881Speter return SVN_NO_ERROR; 2235251881Speter } 2236251881Speter 2237251881Speter /* If no connection is provided, use the default one. */ 2238251881Speter if (! conn) 2239251881Speter { 2240251881Speter conn = session->conns[0]; 2241251881Speter } 2242251881Speter 2243251881Speter path = session->session_url.path; 2244251881Speter *vcc_url = NULL; 2245251881Speter uuid = NULL; 2246251881Speter 2247251881Speter do 2248251881Speter { 2249251881Speter apr_hash_t *props; 2250251881Speter svn_error_t *err; 2251251881Speter 2252251881Speter err = svn_ra_serf__fetch_node_props(&props, conn, 2253251881Speter path, SVN_INVALID_REVNUM, 2254251881Speter base_props, pool, pool); 2255251881Speter if (! err) 2256251881Speter { 2257251881Speter apr_hash_t *ns_props; 2258251881Speter 2259251881Speter ns_props = apr_hash_get(props, "DAV:", 4); 2260251881Speter *vcc_url = svn_prop_get_value(ns_props, 2261251881Speter "version-controlled-configuration"); 2262251881Speter 2263251881Speter ns_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV); 2264251881Speter relative_path = svn_prop_get_value(ns_props, 2265251881Speter "baseline-relative-path"); 2266251881Speter uuid = svn_prop_get_value(ns_props, "repository-uuid"); 2267251881Speter break; 2268251881Speter } 2269251881Speter else 2270251881Speter { 2271251881Speter if ((err->apr_err != SVN_ERR_FS_NOT_FOUND) && 2272251881Speter (err->apr_err != SVN_ERR_RA_DAV_FORBIDDEN)) 2273251881Speter { 2274251881Speter return svn_error_trace(err); /* found a _real_ error */ 2275251881Speter } 2276251881Speter else 2277251881Speter { 2278251881Speter /* This happens when the file is missing in HEAD. */ 2279251881Speter svn_error_clear(err); 2280251881Speter 2281251881Speter /* Okay, strip off a component from PATH. */ 2282251881Speter path = svn_urlpath__dirname(path, pool); 2283251881Speter 2284251881Speter /* An error occurred on conns. serf 0.4.0 remembers that 2285251881Speter the connection had a problem. We need to reset it, in 2286251881Speter order to use it again. */ 2287251881Speter serf_connection_reset(conn->conn); 2288251881Speter } 2289251881Speter } 2290251881Speter } 2291251881Speter while ((path[0] != '\0') 2292251881Speter && (! (path[0] == '/' && path[1] == '\0'))); 2293251881Speter 2294251881Speter if (!*vcc_url) 2295251881Speter { 2296251881Speter return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 2297251881Speter _("The PROPFIND response did not include the " 2298251881Speter "requested version-controlled-configuration " 2299251881Speter "value")); 2300251881Speter } 2301251881Speter 2302251881Speter /* Store our VCC in our cache. */ 2303251881Speter if (!session->vcc_url) 2304251881Speter { 2305251881Speter session->vcc_url = apr_pstrdup(session->pool, *vcc_url); 2306251881Speter } 2307251881Speter 2308251881Speter /* Update our cached repository root URL. */ 2309251881Speter if (!session->repos_root_str) 2310251881Speter { 2311251881Speter svn_stringbuf_t *url_buf; 2312251881Speter 2313251881Speter url_buf = svn_stringbuf_create(path, pool); 2314251881Speter 2315251881Speter svn_path_remove_components(url_buf, 2316251881Speter svn_path_component_count(relative_path)); 2317251881Speter 2318251881Speter /* Now recreate the root_url. */ 2319251881Speter session->repos_root = session->session_url; 2320251881Speter session->repos_root.path = 2321251881Speter (char *)svn_fspath__canonicalize(url_buf->data, session->pool); 2322251881Speter session->repos_root_str = 2323251881Speter svn_urlpath__canonicalize(apr_uri_unparse(session->pool, 2324251881Speter &session->repos_root, 0), 2325251881Speter session->pool); 2326251881Speter } 2327251881Speter 2328251881Speter /* Store the repository UUID in the cache. */ 2329251881Speter if (!session->uuid) 2330251881Speter { 2331251881Speter session->uuid = apr_pstrdup(session->pool, uuid); 2332251881Speter } 2333251881Speter 2334251881Speter return SVN_NO_ERROR; 2335251881Speter} 2336251881Speter 2337251881Spetersvn_error_t * 2338251881Spetersvn_ra_serf__get_relative_path(const char **rel_path, 2339251881Speter const char *orig_path, 2340251881Speter svn_ra_serf__session_t *session, 2341251881Speter svn_ra_serf__connection_t *conn, 2342251881Speter apr_pool_t *pool) 2343251881Speter{ 2344251881Speter const char *decoded_root, *decoded_orig; 2345251881Speter 2346251881Speter if (! session->repos_root.path) 2347251881Speter { 2348251881Speter const char *vcc_url; 2349251881Speter 2350251881Speter /* This should only happen if we haven't detected HTTP v2 2351251881Speter support from the server. */ 2352251881Speter assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 2353251881Speter 2354251881Speter /* We don't actually care about the VCC_URL, but this API 2355251881Speter promises to populate the session's root-url cache, and that's 2356251881Speter what we really want. */ 2357251881Speter SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, 2358251881Speter conn ? conn : session->conns[0], 2359251881Speter pool)); 2360251881Speter } 2361251881Speter 2362251881Speter decoded_root = svn_path_uri_decode(session->repos_root.path, pool); 2363251881Speter decoded_orig = svn_path_uri_decode(orig_path, pool); 2364251881Speter *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig); 2365251881Speter SVN_ERR_ASSERT(*rel_path != NULL); 2366251881Speter return SVN_NO_ERROR; 2367251881Speter} 2368251881Speter 2369251881Spetersvn_error_t * 2370251881Spetersvn_ra_serf__report_resource(const char **report_target, 2371251881Speter svn_ra_serf__session_t *session, 2372251881Speter svn_ra_serf__connection_t *conn, 2373251881Speter apr_pool_t *pool) 2374251881Speter{ 2375251881Speter /* If we have HTTP v2 support, we want to report against the 'me' 2376251881Speter resource. */ 2377251881Speter if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 2378251881Speter *report_target = apr_pstrdup(pool, session->me_resource); 2379251881Speter 2380251881Speter /* Otherwise, we'll use the default VCC. */ 2381251881Speter else 2382251881Speter SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool)); 2383251881Speter 2384251881Speter return SVN_NO_ERROR; 2385251881Speter} 2386251881Speter 2387251881Spetersvn_error_t * 2388253734Spetersvn_ra_serf__error_on_status(serf_status_line sline, 2389251881Speter const char *path, 2390251881Speter const char *location) 2391251881Speter{ 2392253734Speter switch(sline.code) 2393251881Speter { 2394251881Speter case 301: 2395251881Speter case 302: 2396251881Speter case 307: 2397251881Speter return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, 2398253734Speter (sline.code == 301) 2399251881Speter ? _("Repository moved permanently to '%s';" 2400251881Speter " please relocate") 2401251881Speter : _("Repository moved temporarily to '%s';" 2402251881Speter " please relocate"), location); 2403251881Speter case 403: 2404251881Speter return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, 2405251881Speter _("Access to '%s' forbidden"), path); 2406251881Speter 2407251881Speter case 404: 2408251881Speter return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 2409251881Speter _("'%s' path not found"), path); 2410251881Speter case 423: 2411251881Speter return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, 2412251881Speter _("'%s': no lock token available"), path); 2413253734Speter 2414253734Speter case 411: 2415253734Speter return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2416253734Speter _("DAV request failed: 411 Content length required. The " 2417253734Speter "server or an intermediate proxy does not accept " 2418253734Speter "chunked encoding. Try setting 'http-chunked-requests' " 2419253734Speter "to 'auto' or 'no' in your client configuration.")); 2420251881Speter } 2421251881Speter 2422253734Speter if (sline.code >= 300) 2423253734Speter return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2424253734Speter _("Unexpected HTTP status %d '%s' on '%s'\n"), 2425253734Speter sline.code, sline.reason, path); 2426253734Speter 2427251881Speter return SVN_NO_ERROR; 2428251881Speter} 2429251881Speter 2430251881Spetersvn_error_t * 2431251881Spetersvn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, 2432251881Speter svn_delta_shim_callbacks_t *callbacks) 2433251881Speter{ 2434251881Speter svn_ra_serf__session_t *session = ra_session->priv; 2435251881Speter 2436251881Speter session->shim_callbacks = callbacks; 2437251881Speter return SVN_NO_ERROR; 2438251881Speter} 2439251881Speter 2440251881Speter 2441251881Speter/* Conforms to Expat's XML_StartElementHandler */ 2442251881Speterstatic void 2443251881Speterexpat_start(void *userData, const char *raw_name, const char **attrs) 2444251881Speter{ 2445251881Speter struct expat_ctx_t *ectx = userData; 2446251881Speter 2447251881Speter if (ectx->inner_error != NULL) 2448251881Speter return; 2449251881Speter 2450251881Speter ectx->inner_error = svn_error_trace( 2451251881Speter svn_ra_serf__xml_cb_start(ectx->xmlctx, 2452251881Speter raw_name, attrs)); 2453251881Speter 2454251881Speter#ifdef EXPAT_HAS_STOPPARSER 2455251881Speter if (ectx->inner_error) 2456251881Speter (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2457251881Speter#endif 2458251881Speter} 2459251881Speter 2460251881Speter 2461251881Speter/* Conforms to Expat's XML_EndElementHandler */ 2462251881Speterstatic void 2463251881Speterexpat_end(void *userData, const char *raw_name) 2464251881Speter{ 2465251881Speter struct expat_ctx_t *ectx = userData; 2466251881Speter 2467251881Speter if (ectx->inner_error != NULL) 2468251881Speter return; 2469251881Speter 2470251881Speter ectx->inner_error = svn_error_trace( 2471251881Speter svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name)); 2472251881Speter 2473251881Speter#ifdef EXPAT_HAS_STOPPARSER 2474251881Speter if (ectx->inner_error) 2475251881Speter (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2476251881Speter#endif 2477251881Speter} 2478251881Speter 2479251881Speter 2480251881Speter/* Conforms to Expat's XML_CharacterDataHandler */ 2481251881Speterstatic void 2482251881Speterexpat_cdata(void *userData, const char *data, int len) 2483251881Speter{ 2484251881Speter struct expat_ctx_t *ectx = userData; 2485251881Speter 2486251881Speter if (ectx->inner_error != NULL) 2487251881Speter return; 2488251881Speter 2489251881Speter ectx->inner_error = svn_error_trace( 2490251881Speter svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len)); 2491251881Speter 2492251881Speter#ifdef EXPAT_HAS_STOPPARSER 2493251881Speter if (ectx->inner_error) 2494251881Speter (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2495251881Speter#endif 2496251881Speter} 2497251881Speter 2498251881Speter 2499251881Speter/* Implements svn_ra_serf__response_handler_t */ 2500251881Speterstatic svn_error_t * 2501251881Speterexpat_response_handler(serf_request_t *request, 2502251881Speter serf_bucket_t *response, 2503251881Speter void *baton, 2504251881Speter apr_pool_t *scratch_pool) 2505251881Speter{ 2506251881Speter struct expat_ctx_t *ectx = baton; 2507251881Speter 2508251881Speter if (!ectx->parser) 2509251881Speter { 2510251881Speter ectx->parser = XML_ParserCreate(NULL); 2511251881Speter apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, 2512251881Speter xml_parser_cleanup, apr_pool_cleanup_null); 2513251881Speter XML_SetUserData(ectx->parser, ectx); 2514251881Speter XML_SetElementHandler(ectx->parser, expat_start, expat_end); 2515251881Speter XML_SetCharacterDataHandler(ectx->parser, expat_cdata); 2516251881Speter } 2517251881Speter 2518251881Speter /* ### TODO: sline.code < 200 should really be handled by the core */ 2519251881Speter if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)) 2520251881Speter { 2521251881Speter /* By deferring to expect_empty_body(), it will make a choice on 2522251881Speter how to handle the body. Whatever the decision, the core handler 2523251881Speter will take over, and we will not be called again. */ 2524251881Speter return svn_error_trace(svn_ra_serf__expect_empty_body( 2525251881Speter request, response, ectx->handler, 2526251881Speter scratch_pool)); 2527251881Speter } 2528251881Speter 2529251881Speter while (1) 2530251881Speter { 2531251881Speter apr_status_t status; 2532251881Speter const char *data; 2533251881Speter apr_size_t len; 2534251881Speter int expat_status; 2535251881Speter 2536251881Speter status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 2537251881Speter if (SERF_BUCKET_READ_ERROR(status)) 2538251881Speter return svn_ra_serf__wrap_err(status, NULL); 2539251881Speter 2540251881Speter#if 0 2541251881Speter /* ### move restart/skip into the core handler */ 2542251881Speter ectx->handler->read_size += len; 2543251881Speter#endif 2544251881Speter 2545251881Speter /* ### move PAUSED behavior to a new response handler that can feed 2546251881Speter ### an inner handler, or can pause for a while. */ 2547251881Speter 2548251881Speter /* ### should we have an IGNORE_ERRORS flag like the v1 parser? */ 2549251881Speter 2550251881Speter expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */); 2551251881Speter 2552251881Speter /* We need to check INNER_ERROR first. This is an error from the 2553251881Speter callbacks that has been "dropped off" for us to retrieve. On 2554251881Speter current Expat parsers, we stop the parser when an error occurs, 2555251881Speter so we want to ignore EXPAT_STATUS (which reports the stoppage). 2556251881Speter 2557251881Speter If an error is not present, THEN we go ahead and look for parsing 2558251881Speter errors. */ 2559251881Speter if (ectx->inner_error) 2560251881Speter { 2561251881Speter apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2562251881Speter xml_parser_cleanup); 2563251881Speter return svn_error_trace(ectx->inner_error); 2564251881Speter } 2565251881Speter if (expat_status == XML_STATUS_ERROR) 2566251881Speter return svn_error_createf(SVN_ERR_XML_MALFORMED, 2567251881Speter ectx->inner_error, 2568251881Speter _("The %s response contains invalid XML" 2569251881Speter " (%d %s)"), 2570251881Speter ectx->handler->method, 2571251881Speter ectx->handler->sline.code, 2572251881Speter ectx->handler->sline.reason); 2573251881Speter 2574251881Speter /* The parsing went fine. What has the bucket told us? */ 2575251881Speter 2576251881Speter if (APR_STATUS_IS_EOF(status)) 2577251881Speter { 2578251881Speter /* Tell expat we've reached the end of the content. Ignore the 2579251881Speter return status. We just don't care. */ 2580251881Speter (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */); 2581251881Speter 2582251881Speter svn_ra_serf__xml_context_destroy(ectx->xmlctx); 2583251881Speter apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2584251881Speter xml_parser_cleanup); 2585251881Speter 2586251881Speter /* ### should check XMLCTX to see if it has returned to the 2587251881Speter ### INITIAL state. we may have ended early... */ 2588251881Speter } 2589251881Speter 2590251881Speter if (status && !SERF_BUCKET_READ_ERROR(status)) 2591251881Speter { 2592251881Speter return svn_ra_serf__wrap_err(status, NULL); 2593251881Speter } 2594251881Speter } 2595251881Speter 2596251881Speter /* NOTREACHED */ 2597251881Speter} 2598251881Speter 2599251881Speter 2600251881Spetersvn_ra_serf__handler_t * 2601251881Spetersvn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx, 2602251881Speter apr_pool_t *result_pool) 2603251881Speter{ 2604251881Speter svn_ra_serf__handler_t *handler; 2605251881Speter struct expat_ctx_t *ectx; 2606251881Speter 2607251881Speter ectx = apr_pcalloc(result_pool, sizeof(*ectx)); 2608251881Speter ectx->xmlctx = xmlctx; 2609251881Speter ectx->parser = NULL; 2610251881Speter ectx->cleanup_pool = result_pool; 2611251881Speter 2612251881Speter 2613251881Speter handler = apr_pcalloc(result_pool, sizeof(*handler)); 2614251881Speter handler->handler_pool = result_pool; 2615251881Speter handler->response_handler = expat_response_handler; 2616251881Speter handler->response_baton = ectx; 2617251881Speter 2618251881Speter ectx->handler = handler; 2619251881Speter 2620251881Speter return handler; 2621251881Speter} 2622