util.c revision 269847
1/* 2 * util.c : serf utility routines for ra_serf 3 * 4 * ==================================================================== 5 * Licensed to the Apache Software Foundation (ASF) under one 6 * or more contributor license agreements. See the NOTICE file 7 * distributed with this work for additional information 8 * regarding copyright ownership. The ASF licenses this file 9 * to you under the Apache License, Version 2.0 (the 10 * "License"); you may not use this file except in compliance 11 * with the License. You may obtain a copy of the License at 12 * 13 * http://www.apache.org/licenses/LICENSE-2.0 14 * 15 * Unless required by applicable law or agreed to in writing, 16 * software distributed under the License is distributed on an 17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 * KIND, either express or implied. See the License for the 19 * specific language governing permissions and limitations 20 * under the License. 21 * ==================================================================== 22 */ 23 24 25 26#include <assert.h> 27 28#define APR_WANT_STRFUNC 29#include <apr.h> 30#include <apr_want.h> 31 32#include <serf.h> 33#include <serf_bucket_types.h> 34 35#include <expat.h> 36 37#include "svn_hash.h" 38#include "svn_dirent_uri.h" 39#include "svn_path.h" 40#include "svn_private_config.h" 41#include "svn_string.h" 42#include "svn_xml.h" 43#include "svn_props.h" 44#include "svn_dirent_uri.h" 45 46#include "../libsvn_ra/ra_loader.h" 47#include "private/svn_dep_compat.h" 48#include "private/svn_fspath.h" 49#include "private/svn_subr_private.h" 50#include "private/svn_auth_private.h" 51#include "private/svn_cert.h" 52 53#include "ra_serf.h" 54 55 56/* Fix for older expat 1.95.x's that do not define 57 * XML_STATUS_OK/XML_STATUS_ERROR 58 */ 59#ifndef XML_STATUS_OK 60#define XML_STATUS_OK 1 61#define XML_STATUS_ERROR 0 62#endif 63 64#ifndef XML_VERSION_AT_LEAST 65#define XML_VERSION_AT_LEAST(major,minor,patch) \ 66(((major) < XML_MAJOR_VERSION) \ 67 || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ 68 || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ 69 (patch) <= XML_MICRO_VERSION)) 70#endif /* APR_VERSION_AT_LEAST */ 71 72#if XML_VERSION_AT_LEAST(1, 95, 8) 73#define EXPAT_HAS_STOPPARSER 74#endif 75 76/* Read/write chunks of this size into the spillbuf. */ 77#define PARSE_CHUNK_SIZE 8000 78 79/* We will store one megabyte in memory, before switching to store content 80 into a temporary file. */ 81#define SPILL_SIZE 1000000 82 83 84/* This structure records pending data for the parser in memory blocks, 85 and possibly into a temporary file if "too much" content arrives. */ 86struct svn_ra_serf__pending_t { 87 /* The spillbuf where we record the pending data. */ 88 svn_spillbuf_t *buf; 89 90 /* This flag is set when the network has reached EOF. The PENDING 91 processing can then properly detect when parsing has completed. */ 92 svn_boolean_t network_eof; 93}; 94 95#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \ 96 && svn_spillbuf__get_size((p)->buf) != 0) 97 98 99struct expat_ctx_t { 100 svn_ra_serf__xml_context_t *xmlctx; 101 XML_Parser parser; 102 svn_ra_serf__handler_t *handler; 103 104 svn_error_t *inner_error; 105 106 /* Do not use this pool for allocation. It is merely recorded for running 107 the cleanup handler. */ 108 apr_pool_t *cleanup_pool; 109}; 110 111 112static const apr_uint32_t serf_failure_map[][2] = 113{ 114 { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 115 { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 116 { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 117 { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 118}; 119 120/* Return a Subversion failure mask based on FAILURES, a serf SSL 121 failure mask. If anything in FAILURES is not directly mappable to 122 Subversion failures, set SVN_AUTH_SSL_OTHER in the returned mask. */ 123static apr_uint32_t 124ssl_convert_serf_failures(int failures) 125{ 126 apr_uint32_t svn_failures = 0; 127 apr_size_t i; 128 129 for (i = 0; i < sizeof(serf_failure_map) / (2 * sizeof(apr_uint32_t)); ++i) 130 { 131 if (failures & serf_failure_map[i][0]) 132 { 133 svn_failures |= serf_failure_map[i][1]; 134 failures &= ~serf_failure_map[i][0]; 135 } 136 } 137 138 /* Map any remaining failure bits to our OTHER bit. */ 139 if (failures) 140 { 141 svn_failures |= SVN_AUTH_SSL_OTHER; 142 } 143 144 return svn_failures; 145} 146 147 148static apr_status_t 149save_error(svn_ra_serf__session_t *session, 150 svn_error_t *err) 151{ 152 if (err || session->pending_error) 153 { 154 session->pending_error = svn_error_compose_create( 155 session->pending_error, 156 err); 157 return session->pending_error->apr_err; 158 } 159 160 return APR_SUCCESS; 161} 162 163 164/* Construct the realmstring, e.g. https://svn.collab.net:443. */ 165static const char * 166construct_realm(svn_ra_serf__session_t *session, 167 apr_pool_t *pool) 168{ 169 const char *realm; 170 apr_port_t port; 171 172 if (session->session_url.port_str) 173 { 174 port = session->session_url.port; 175 } 176 else 177 { 178 port = apr_uri_port_of_scheme(session->session_url.scheme); 179 } 180 181 realm = apr_psprintf(pool, "%s://%s:%d", 182 session->session_url.scheme, 183 session->session_url.hostname, 184 port); 185 186 return realm; 187} 188 189/* Convert a hash table containing the fields (as documented in X.509) of an 190 organisation to a string ORG, allocated in POOL. ORG is as returned by 191 serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 192static char * 193convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 194{ 195 const char *org_unit = svn_hash_gets(org, "OU"); 196 const char *org_name = svn_hash_gets(org, "O"); 197 const char *locality = svn_hash_gets(org, "L"); 198 const char *state = svn_hash_gets(org, "ST"); 199 const char *country = svn_hash_gets(org, "C"); 200 const char *email = svn_hash_gets(org, "E"); 201 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 202 203 if (org_unit) 204 { 205 svn_stringbuf_appendcstr(buf, org_unit); 206 svn_stringbuf_appendcstr(buf, ", "); 207 } 208 209 if (org_name) 210 { 211 svn_stringbuf_appendcstr(buf, org_name); 212 svn_stringbuf_appendcstr(buf, ", "); 213 } 214 215 if (locality) 216 { 217 svn_stringbuf_appendcstr(buf, locality); 218 svn_stringbuf_appendcstr(buf, ", "); 219 } 220 221 if (state) 222 { 223 svn_stringbuf_appendcstr(buf, state); 224 svn_stringbuf_appendcstr(buf, ", "); 225 } 226 227 if (country) 228 { 229 svn_stringbuf_appendcstr(buf, country); 230 svn_stringbuf_appendcstr(buf, ", "); 231 } 232 233 /* Chop ', ' if any. */ 234 svn_stringbuf_chop(buf, 2); 235 236 if (email) 237 { 238 svn_stringbuf_appendcstr(buf, "("); 239 svn_stringbuf_appendcstr(buf, email); 240 svn_stringbuf_appendcstr(buf, ")"); 241 } 242 243 return buf->data; 244} 245 246static void append_reason(svn_stringbuf_t *errmsg, const char *reason, int *reasons) 247{ 248 if (*reasons < 1) 249 svn_stringbuf_appendcstr(errmsg, _(": ")); 250 else 251 svn_stringbuf_appendcstr(errmsg, _(", ")); 252 svn_stringbuf_appendcstr(errmsg, reason); 253 (*reasons)++; 254} 255 256/* This function is called on receiving a ssl certificate of a server when 257 opening a https connection. It allows Subversion to override the initial 258 validation done by serf. 259 Serf provides us the @a baton as provided in the call to 260 serf_ssl_server_cert_callback_set. The result of serf's initial validation 261 of the certificate @a CERT is returned as a bitmask in FAILURES. */ 262static svn_error_t * 263ssl_server_cert(void *baton, int failures, 264 const serf_ssl_certificate_t *cert, 265 apr_pool_t *scratch_pool) 266{ 267 svn_ra_serf__connection_t *conn = baton; 268 svn_auth_ssl_server_cert_info_t cert_info; 269 svn_auth_cred_ssl_server_trust_t *server_creds = NULL; 270 svn_auth_iterstate_t *state; 271 const char *realmstring; 272 apr_uint32_t svn_failures; 273 apr_hash_t *issuer; 274 apr_hash_t *subject = NULL; 275 apr_hash_t *serf_cert = NULL; 276 void *creds; 277 278 svn_failures = (ssl_convert_serf_failures(failures) 279 | conn->server_cert_failures); 280 281 if (serf_ssl_cert_depth(cert) == 0) 282 { 283 /* If the depth is 0, the hostname must match the certificate. 284 285 ### This should really be handled by serf, which should pass an error 286 for this case, but that has backwards compatibility issues. */ 287 apr_array_header_t *san; 288 svn_boolean_t found_san_entry = FALSE; 289 svn_boolean_t found_matching_hostname = FALSE; 290 svn_string_t *actual_hostname = 291 svn_string_create(conn->session->session_url.hostname, scratch_pool); 292 293 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 294 295 san = svn_hash_gets(serf_cert, "subjectAltName"); 296 /* Try to find matching server name via subjectAltName first... */ 297 if (san) 298 { 299 int i; 300 found_san_entry = san->nelts > 0; 301 for (i = 0; i < san->nelts; i++) 302 { 303 const char *s = APR_ARRAY_IDX(san, i, const char*); 304 svn_string_t *cert_hostname = svn_string_create(s, scratch_pool); 305 306 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 307 { 308 found_matching_hostname = TRUE; 309 break; 310 } 311 } 312 } 313 314 /* Match server certificate CN with the hostname of the server iff 315 * we didn't find any subjectAltName fields and try to match them. 316 * Per RFC 2818 they are authoritative if present and CommonName 317 * should be ignored. */ 318 if (!found_matching_hostname && !found_san_entry) 319 { 320 const char *hostname = NULL; 321 322 subject = serf_ssl_cert_subject(cert, scratch_pool); 323 324 if (subject) 325 hostname = svn_hash_gets(subject, "CN"); 326 327 if (hostname) 328 { 329 svn_string_t *cert_hostname = svn_string_create(hostname, 330 scratch_pool); 331 332 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 333 { 334 found_matching_hostname = TRUE; 335 } 336 } 337 } 338 339 if (!found_matching_hostname) 340 svn_failures |= SVN_AUTH_SSL_CNMISMATCH; 341 } 342 343 if (!svn_failures) 344 return SVN_NO_ERROR; 345 346 /* Extract the info from the certificate */ 347 if (! subject) 348 subject = serf_ssl_cert_subject(cert, scratch_pool); 349 issuer = serf_ssl_cert_issuer(cert, scratch_pool); 350 if (! serf_cert) 351 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 352 353 cert_info.hostname = svn_hash_gets(subject, "CN"); 354 cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1"); 355 if (! cert_info.fingerprint) 356 cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>"); 357 cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore"); 358 if (! cert_info.valid_from) 359 cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]"); 360 cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter"); 361 if (! cert_info.valid_until) 362 cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]"); 363 cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 364 cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 365 366 /* Handle any non-server certs. */ 367 if (serf_ssl_cert_depth(cert) > 0) 368 { 369 svn_error_t *err; 370 371 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 372 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 373 &cert_info); 374 375 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 376 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 377 &svn_failures); 378 379 realmstring = apr_psprintf(scratch_pool, "AUTHORITY:%s", 380 cert_info.fingerprint); 381 382 err = svn_auth_first_credentials(&creds, &state, 383 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 384 realmstring, 385 conn->session->wc_callbacks->auth_baton, 386 scratch_pool); 387 388 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 389 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 390 391 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 392 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); 393 394 if (err) 395 { 396 if (err->apr_err != SVN_ERR_AUTHN_NO_PROVIDER) 397 return svn_error_trace(err); 398 399 /* No provider registered that handles server authorities */ 400 svn_error_clear(err); 401 creds = NULL; 402 } 403 404 if (creds) 405 { 406 server_creds = creds; 407 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 408 409 svn_failures &= ~server_creds->accepted_failures; 410 } 411 412 if (svn_failures) 413 conn->server_cert_failures |= svn_failures; 414 415 return APR_SUCCESS; 416 } 417 418 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 419 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 420 &svn_failures); 421 422 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 423 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 424 &cert_info); 425 426 realmstring = construct_realm(conn->session, conn->session->pool); 427 428 SVN_ERR(svn_auth_first_credentials(&creds, &state, 429 SVN_AUTH_CRED_SSL_SERVER_TRUST, 430 realmstring, 431 conn->session->wc_callbacks->auth_baton, 432 scratch_pool)); 433 if (creds) 434 { 435 server_creds = creds; 436 svn_failures &= ~server_creds->accepted_failures; 437 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 438 } 439 440 while (svn_failures && creds) 441 { 442 SVN_ERR(svn_auth_next_credentials(&creds, state, scratch_pool)); 443 444 if (creds) 445 { 446 server_creds = creds; 447 svn_failures &= ~server_creds->accepted_failures; 448 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 449 } 450 } 451 452 svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton, 453 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 454 455 /* Are there non accepted failures left? */ 456 if (svn_failures) 457 { 458 svn_stringbuf_t *errmsg; 459 int reasons = 0; 460 461 errmsg = svn_stringbuf_create( 462 _("Server SSL certificate verification failed"), 463 scratch_pool); 464 465 466 if (svn_failures & SVN_AUTH_SSL_NOTYETVALID) 467 append_reason(errmsg, _("certificate is not yet valid"), &reasons); 468 469 if (svn_failures & SVN_AUTH_SSL_EXPIRED) 470 append_reason(errmsg, _("certificate has expired"), &reasons); 471 472 if (svn_failures & SVN_AUTH_SSL_CNMISMATCH) 473 append_reason(errmsg, 474 _("certificate issued for a different hostname"), 475 &reasons); 476 477 if (svn_failures & SVN_AUTH_SSL_UNKNOWNCA) 478 append_reason(errmsg, _("issuer is not trusted"), &reasons); 479 480 if (svn_failures & SVN_AUTH_SSL_OTHER) 481 append_reason(errmsg, _("and other reason(s)"), &reasons); 482 483 return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, 484 errmsg->data); 485 } 486 487 return SVN_NO_ERROR; 488} 489 490/* Implements serf_ssl_need_server_cert_t for ssl_server_cert */ 491static apr_status_t 492ssl_server_cert_cb(void *baton, int failures, 493 const serf_ssl_certificate_t *cert) 494{ 495 svn_ra_serf__connection_t *conn = baton; 496 svn_ra_serf__session_t *session = conn->session; 497 apr_pool_t *subpool; 498 svn_error_t *err; 499 500 subpool = svn_pool_create(session->pool); 501 err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool)); 502 svn_pool_destroy(subpool); 503 504 return save_error(session, err); 505} 506 507static svn_error_t * 508load_authorities(svn_ra_serf__connection_t *conn, const char *authorities, 509 apr_pool_t *pool) 510{ 511 apr_array_header_t *files = svn_cstring_split(authorities, ";", 512 TRUE /* chop_whitespace */, 513 pool); 514 int i; 515 516 for (i = 0; i < files->nelts; ++i) 517 { 518 const char *file = APR_ARRAY_IDX(files, i, const char *); 519 serf_ssl_certificate_t *ca_cert; 520 apr_status_t status = serf_ssl_load_cert_file(&ca_cert, file, pool); 521 522 if (status == APR_SUCCESS) 523 status = serf_ssl_trust_cert(conn->ssl_context, ca_cert); 524 525 if (status != APR_SUCCESS) 526 { 527 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 528 _("Invalid config: unable to load certificate file '%s'"), 529 svn_dirent_local_style(file, pool)); 530 } 531 } 532 533 return SVN_NO_ERROR; 534} 535 536static svn_error_t * 537conn_setup(apr_socket_t *sock, 538 serf_bucket_t **read_bkt, 539 serf_bucket_t **write_bkt, 540 void *baton, 541 apr_pool_t *pool) 542{ 543 svn_ra_serf__connection_t *conn = baton; 544 545 *read_bkt = serf_context_bucket_socket_create(conn->session->context, 546 sock, conn->bkt_alloc); 547 548 if (conn->session->using_ssl) 549 { 550 /* input stream */ 551 *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context, 552 conn->bkt_alloc); 553 if (!conn->ssl_context) 554 { 555 conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt); 556 557 serf_ssl_set_hostname(conn->ssl_context, 558 conn->session->session_url.hostname); 559 560 serf_ssl_client_cert_provider_set(conn->ssl_context, 561 svn_ra_serf__handle_client_cert, 562 conn, conn->session->pool); 563 serf_ssl_client_cert_password_set(conn->ssl_context, 564 svn_ra_serf__handle_client_cert_pw, 565 conn, conn->session->pool); 566 serf_ssl_server_cert_callback_set(conn->ssl_context, 567 ssl_server_cert_cb, 568 conn); 569 570 /* See if the user wants us to trust "default" openssl CAs. */ 571 if (conn->session->trust_default_ca) 572 { 573 serf_ssl_use_default_certificates(conn->ssl_context); 574 } 575 /* Are there custom CAs to load? */ 576 if (conn->session->ssl_authorities) 577 { 578 SVN_ERR(load_authorities(conn, conn->session->ssl_authorities, 579 conn->session->pool)); 580 } 581 } 582 583 if (write_bkt) 584 { 585 /* output stream */ 586 *write_bkt = serf_bucket_ssl_encrypt_create(*write_bkt, 587 conn->ssl_context, 588 conn->bkt_alloc); 589 } 590 } 591 592 return SVN_NO_ERROR; 593} 594 595/* svn_ra_serf__conn_setup is a callback for serf. This function 596 creates a read bucket and will wrap the write bucket if SSL 597 is needed. */ 598apr_status_t 599svn_ra_serf__conn_setup(apr_socket_t *sock, 600 serf_bucket_t **read_bkt, 601 serf_bucket_t **write_bkt, 602 void *baton, 603 apr_pool_t *pool) 604{ 605 svn_ra_serf__connection_t *conn = baton; 606 svn_ra_serf__session_t *session = conn->session; 607 svn_error_t *err; 608 609 err = svn_error_trace(conn_setup(sock, 610 read_bkt, 611 write_bkt, 612 baton, 613 pool)); 614 return save_error(session, err); 615} 616 617 618/* Our default serf response acceptor. */ 619static serf_bucket_t * 620accept_response(serf_request_t *request, 621 serf_bucket_t *stream, 622 void *acceptor_baton, 623 apr_pool_t *pool) 624{ 625 serf_bucket_t *c; 626 serf_bucket_alloc_t *bkt_alloc; 627 628 bkt_alloc = serf_request_get_alloc(request); 629 c = serf_bucket_barrier_create(stream, bkt_alloc); 630 631 return serf_bucket_response_create(c, bkt_alloc); 632} 633 634 635/* Custom response acceptor for HEAD requests. */ 636static serf_bucket_t * 637accept_head(serf_request_t *request, 638 serf_bucket_t *stream, 639 void *acceptor_baton, 640 apr_pool_t *pool) 641{ 642 serf_bucket_t *response; 643 644 response = accept_response(request, stream, acceptor_baton, pool); 645 646 /* We know we shouldn't get a response body. */ 647 serf_bucket_response_set_head(response); 648 649 return response; 650} 651 652static svn_error_t * 653connection_closed(svn_ra_serf__connection_t *conn, 654 apr_status_t why, 655 apr_pool_t *pool) 656{ 657 if (why) 658 { 659 return svn_error_wrap_apr(why, NULL); 660 } 661 662 if (conn->session->using_ssl) 663 conn->ssl_context = NULL; 664 665 return SVN_NO_ERROR; 666} 667 668void 669svn_ra_serf__conn_closed(serf_connection_t *conn, 670 void *closed_baton, 671 apr_status_t why, 672 apr_pool_t *pool) 673{ 674 svn_ra_serf__connection_t *ra_conn = closed_baton; 675 svn_error_t *err; 676 677 err = svn_error_trace(connection_closed(ra_conn, why, pool)); 678 679 (void) save_error(ra_conn->session, err); 680} 681 682 683/* Implementation of svn_ra_serf__handle_client_cert */ 684static svn_error_t * 685handle_client_cert(void *data, 686 const char **cert_path, 687 apr_pool_t *pool) 688{ 689 svn_ra_serf__connection_t *conn = data; 690 svn_ra_serf__session_t *session = conn->session; 691 const char *realm; 692 void *creds; 693 694 *cert_path = NULL; 695 696 realm = construct_realm(session, session->pool); 697 698 if (!conn->ssl_client_auth_state) 699 { 700 SVN_ERR(svn_auth_first_credentials(&creds, 701 &conn->ssl_client_auth_state, 702 SVN_AUTH_CRED_SSL_CLIENT_CERT, 703 realm, 704 session->wc_callbacks->auth_baton, 705 pool)); 706 } 707 else 708 { 709 SVN_ERR(svn_auth_next_credentials(&creds, 710 conn->ssl_client_auth_state, 711 session->pool)); 712 } 713 714 if (creds) 715 { 716 svn_auth_cred_ssl_client_cert_t *client_creds; 717 client_creds = creds; 718 *cert_path = client_creds->cert_file; 719 } 720 721 return SVN_NO_ERROR; 722} 723 724/* Implements serf_ssl_need_client_cert_t for handle_client_cert */ 725apr_status_t svn_ra_serf__handle_client_cert(void *data, 726 const char **cert_path) 727{ 728 svn_ra_serf__connection_t *conn = data; 729 svn_ra_serf__session_t *session = conn->session; 730 svn_error_t *err; 731 732 err = svn_error_trace(handle_client_cert(data, cert_path, session->pool)); 733 734 return save_error(session, err); 735} 736 737/* Implementation for svn_ra_serf__handle_client_cert_pw */ 738static svn_error_t * 739handle_client_cert_pw(void *data, 740 const char *cert_path, 741 const char **password, 742 apr_pool_t *pool) 743{ 744 svn_ra_serf__connection_t *conn = data; 745 svn_ra_serf__session_t *session = conn->session; 746 void *creds; 747 748 *password = NULL; 749 750 if (!conn->ssl_client_pw_auth_state) 751 { 752 SVN_ERR(svn_auth_first_credentials(&creds, 753 &conn->ssl_client_pw_auth_state, 754 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 755 cert_path, 756 session->wc_callbacks->auth_baton, 757 pool)); 758 } 759 else 760 { 761 SVN_ERR(svn_auth_next_credentials(&creds, 762 conn->ssl_client_pw_auth_state, 763 pool)); 764 } 765 766 if (creds) 767 { 768 svn_auth_cred_ssl_client_cert_pw_t *pw_creds; 769 pw_creds = creds; 770 *password = pw_creds->password; 771 } 772 773 return APR_SUCCESS; 774} 775 776/* Implements serf_ssl_need_client_cert_pw_t for handle_client_cert_pw */ 777apr_status_t svn_ra_serf__handle_client_cert_pw(void *data, 778 const char *cert_path, 779 const char **password) 780{ 781 svn_ra_serf__connection_t *conn = data; 782 svn_ra_serf__session_t *session = conn->session; 783 svn_error_t *err; 784 785 err = svn_error_trace(handle_client_cert_pw(data, 786 cert_path, 787 password, 788 session->pool)); 789 790 return save_error(session, err); 791} 792 793 794/* 795 * Given a REQUEST on connection CONN, construct a request bucket for it, 796 * returning the bucket in *REQ_BKT. 797 * 798 * If HDRS_BKT is not-NULL, it will be set to a headers_bucket that 799 * corresponds to the new request. 800 * 801 * The request will be METHOD at URL. 802 * 803 * If BODY_BKT is not-NULL, it will be sent as the request body. 804 * 805 * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 806 * 807 * REQUEST_POOL should live for the duration of the request. Serf will 808 * construct this and provide it to the request_setup callback, so we 809 * should just use that one. 810 */ 811static svn_error_t * 812setup_serf_req(serf_request_t *request, 813 serf_bucket_t **req_bkt, 814 serf_bucket_t **hdrs_bkt, 815 svn_ra_serf__session_t *session, 816 const char *method, const char *url, 817 serf_bucket_t *body_bkt, const char *content_type, 818 const char *accept_encoding, 819 apr_pool_t *request_pool, 820 apr_pool_t *scratch_pool) 821{ 822 serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 823 824 svn_spillbuf_t *buf; 825 svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 826 827 if (set_CL && body_bkt != NULL) 828 { 829 /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if 830 it speaks HTTP/1.1 (and thus, chunked requests), or because the 831 server actually responded as only supporting HTTP/1.0. 832 833 We'll take the existing body_bkt, spool it into a spillbuf, and 834 then wrap a bucket around that spillbuf. The spillbuf will give 835 us the Content-Length value. */ 836 SVN_ERR(svn_ra_serf__copy_into_spillbuf(&buf, body_bkt, 837 request_pool, 838 scratch_pool)); 839 /* Destroy original bucket since it content is already copied 840 to spillbuf. */ 841 serf_bucket_destroy(body_bkt); 842 843 body_bkt = svn_ra_serf__create_sb_bucket(buf, allocator, 844 request_pool, 845 scratch_pool); 846 } 847 848 /* Create a request bucket. Note that this sucker is kind enough to 849 add a "Host" header for us. */ 850 *req_bkt = serf_request_bucket_request_create(request, method, url, 851 body_bkt, allocator); 852 853 /* Set the Content-Length value. This will also trigger an HTTP/1.0 854 request (rather than the default chunked request). */ 855 if (set_CL) 856 { 857 if (body_bkt == NULL) 858 serf_bucket_request_set_CL(*req_bkt, 0); 859 else 860 serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf)); 861 } 862 863 *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); 864 865 /* We use serf_bucket_headers_setn() because the USERAGENT has a 866 lifetime longer than this bucket. Thus, there is no need to copy 867 the header values. */ 868 serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent); 869 870 if (content_type) 871 { 872 serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type); 873 } 874 875 if (session->http10) 876 { 877 serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 878 } 879 880 if (accept_encoding) 881 { 882 serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 883 } 884 885 /* These headers need to be sent with every request; see issue #3255 886 ("mod_dav_svn does not pass client capabilities to start-commit 887 hooks") for why. */ 888 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 889 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 890 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); 891 892 return SVN_NO_ERROR; 893} 894 895svn_error_t * 896svn_ra_serf__context_run_wait(svn_boolean_t *done, 897 svn_ra_serf__session_t *sess, 898 apr_pool_t *scratch_pool) 899{ 900 apr_pool_t *iterpool; 901 apr_interval_time_t waittime_left = sess->timeout; 902 903 assert(sess->pending_error == SVN_NO_ERROR); 904 905 iterpool = svn_pool_create(scratch_pool); 906 while (!*done) 907 { 908 apr_status_t status; 909 svn_error_t *err; 910 int i; 911 912 svn_pool_clear(iterpool); 913 914 if (sess->cancel_func) 915 SVN_ERR((*sess->cancel_func)(sess->cancel_baton)); 916 917 status = serf_context_run(sess->context, 918 SVN_RA_SERF__CONTEXT_RUN_DURATION, 919 iterpool); 920 921 err = sess->pending_error; 922 sess->pending_error = SVN_NO_ERROR; 923 924 /* If the context duration timeout is up, we'll subtract that 925 duration from the total time alloted for such things. If 926 there's no time left, we fail with a message indicating that 927 the connection timed out. */ 928 if (APR_STATUS_IS_TIMEUP(status)) 929 { 930 status = 0; 931 932 if (sess->timeout) 933 { 934 if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 935 { 936 waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 937 } 938 else 939 { 940 return 941 svn_error_compose_create( 942 err, 943 svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 944 _("Connection timed out"))); 945 } 946 } 947 } 948 else 949 { 950 waittime_left = sess->timeout; 951 } 952 953 SVN_ERR(err); 954 if (status) 955 { 956 if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 957 { 958 /* apr can't translate subversion errors to text */ 959 SVN_ERR_W(svn_error_create(status, NULL, NULL), 960 _("Error running context")); 961 } 962 963 return svn_ra_serf__wrap_err(status, _("Error running context")); 964 } 965 966 /* Debugging purposes only! */ 967 for (i = 0; i < sess->num_conns; i++) 968 { 969 serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 970 } 971 } 972 svn_pool_destroy(iterpool); 973 974 return SVN_NO_ERROR; 975} 976 977 978svn_error_t * 979svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 980 apr_pool_t *scratch_pool) 981{ 982 svn_error_t *err; 983 984 /* Create a serf request based on HANDLER. */ 985 svn_ra_serf__request_create(handler); 986 987 /* Wait until the response logic marks its DONE status. */ 988 err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 989 scratch_pool); 990 991 /* A callback invocation has been canceled. In this simple case of 992 context_run_one, we can keep the ra-session operational by resetting 993 the connection. 994 995 If we don't do this, the next context run will notice that the connection 996 is still in the error state and will just return SVN_ERR_CEASE_INVOCATION 997 (=the last error for the connection) again */ 998 if (err && err->apr_err == SVN_ERR_CEASE_INVOCATION) 999 { 1000 apr_status_t status = serf_connection_reset(handler->conn->conn); 1001 1002 if (status) 1003 err = svn_error_compose_create(err, 1004 svn_ra_serf__wrap_err(status, NULL)); 1005 } 1006 1007 if (handler->server_error) 1008 { 1009 err = svn_error_compose_create(err, handler->server_error->error); 1010 handler->server_error = NULL; 1011 } 1012 1013 return svn_error_trace(err); 1014} 1015 1016 1017/* 1018 * Expat callback invoked on a start element tag for an error response. 1019 */ 1020static svn_error_t * 1021start_error(svn_ra_serf__xml_parser_t *parser, 1022 svn_ra_serf__dav_props_t name, 1023 const char **attrs, 1024 apr_pool_t *scratch_pool) 1025{ 1026 svn_ra_serf__server_error_t *ctx = parser->user_data; 1027 1028 if (!ctx->in_error && 1029 strcmp(name.namespace, "DAV:") == 0 && 1030 strcmp(name.name, "error") == 0) 1031 { 1032 ctx->in_error = TRUE; 1033 } 1034 else if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 1035 { 1036 const char *err_code; 1037 1038 err_code = svn_xml_get_attr_value("errcode", attrs); 1039 if (err_code) 1040 { 1041 apr_int64_t val; 1042 1043 SVN_ERR(svn_cstring_atoi64(&val, err_code)); 1044 ctx->error->apr_err = (apr_status_t)val; 1045 } 1046 1047 /* If there's no error code provided, or if the provided code is 1048 0 (which can happen sometimes depending on how the error is 1049 constructed on the server-side), just pick a generic error 1050 code to run with. */ 1051 if (! ctx->error->apr_err) 1052 { 1053 ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1054 } 1055 1056 /* Start collecting cdata. */ 1057 svn_stringbuf_setempty(ctx->cdata); 1058 ctx->collect_cdata = TRUE; 1059 } 1060 1061 return SVN_NO_ERROR; 1062} 1063 1064/* 1065 * Expat callback invoked on an end element tag for a PROPFIND response. 1066 */ 1067static svn_error_t * 1068end_error(svn_ra_serf__xml_parser_t *parser, 1069 svn_ra_serf__dav_props_t name, 1070 apr_pool_t *scratch_pool) 1071{ 1072 svn_ra_serf__server_error_t *ctx = parser->user_data; 1073 1074 if (ctx->in_error && 1075 strcmp(name.namespace, "DAV:") == 0 && 1076 strcmp(name.name, "error") == 0) 1077 { 1078 ctx->in_error = FALSE; 1079 } 1080 if (ctx->in_error && strcmp(name.name, "human-readable") == 0) 1081 { 1082 /* On the server dav_error_response_tag() will add a leading 1083 and trailing newline if DEBUG_CR is defined in mod_dav.h, 1084 so remove any such characters here. */ 1085 svn_stringbuf_strip_whitespace(ctx->cdata); 1086 1087 ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 1088 ctx->cdata->len); 1089 ctx->collect_cdata = FALSE; 1090 } 1091 1092 return SVN_NO_ERROR; 1093} 1094 1095/* 1096 * Expat callback invoked on CDATA elements in an error response. 1097 * 1098 * This callback can be called multiple times. 1099 */ 1100static svn_error_t * 1101cdata_error(svn_ra_serf__xml_parser_t *parser, 1102 const char *data, 1103 apr_size_t len, 1104 apr_pool_t *scratch_pool) 1105{ 1106 svn_ra_serf__server_error_t *ctx = parser->user_data; 1107 1108 if (ctx->collect_cdata) 1109 { 1110 svn_stringbuf_appendbytes(ctx->cdata, data, len); 1111 } 1112 1113 return SVN_NO_ERROR; 1114} 1115 1116 1117static apr_status_t 1118drain_bucket(serf_bucket_t *bucket) 1119{ 1120 /* Read whatever is in the bucket, and just drop it. */ 1121 while (1) 1122 { 1123 apr_status_t status; 1124 const char *data; 1125 apr_size_t len; 1126 1127 status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 1128 if (status) 1129 return status; 1130 } 1131} 1132 1133 1134static svn_ra_serf__server_error_t * 1135begin_error_parsing(svn_ra_serf__xml_start_element_t start, 1136 svn_ra_serf__xml_end_element_t end, 1137 svn_ra_serf__xml_cdata_chunk_handler_t cdata, 1138 apr_pool_t *result_pool) 1139{ 1140 svn_ra_serf__server_error_t *server_err; 1141 1142 server_err = apr_pcalloc(result_pool, sizeof(*server_err)); 1143 server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL); 1144 server_err->contains_precondition_error = FALSE; 1145 server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool); 1146 server_err->collect_cdata = FALSE; 1147 server_err->parser.pool = server_err->error->pool; 1148 server_err->parser.user_data = server_err; 1149 server_err->parser.start = start; 1150 server_err->parser.end = end; 1151 server_err->parser.cdata = cdata; 1152 server_err->parser.ignore_errors = TRUE; 1153 1154 return server_err; 1155} 1156 1157/* Implements svn_ra_serf__response_handler_t */ 1158svn_error_t * 1159svn_ra_serf__handle_discard_body(serf_request_t *request, 1160 serf_bucket_t *response, 1161 void *baton, 1162 apr_pool_t *pool) 1163{ 1164 apr_status_t status; 1165 1166 status = drain_bucket(response); 1167 if (status) 1168 return svn_ra_serf__wrap_err(status, NULL); 1169 1170 return SVN_NO_ERROR; 1171} 1172 1173apr_status_t 1174svn_ra_serf__response_discard_handler(serf_request_t *request, 1175 serf_bucket_t *response, 1176 void *baton, 1177 apr_pool_t *pool) 1178{ 1179 return drain_bucket(response); 1180} 1181 1182 1183/* Return the value of the RESPONSE's Location header if any, or NULL 1184 otherwise. */ 1185static const char * 1186response_get_location(serf_bucket_t *response, 1187 const char *base_url, 1188 apr_pool_t *result_pool, 1189 apr_pool_t *scratch_pool) 1190{ 1191 serf_bucket_t *headers; 1192 const char *location; 1193 1194 headers = serf_bucket_response_get_headers(response); 1195 location = serf_bucket_headers_get(headers, "Location"); 1196 if (location == NULL) 1197 return NULL; 1198 1199 /* The RFCs say we should have received a full url in LOCATION, but 1200 older apache versions and many custom web handlers just return a 1201 relative path here... 1202 1203 And we can't trust anything because it is network data. 1204 */ 1205 if (*location == '/') 1206 { 1207 apr_uri_t uri; 1208 apr_status_t status; 1209 1210 status = apr_uri_parse(scratch_pool, base_url, &uri); 1211 1212 if (status != APR_SUCCESS) 1213 return NULL; 1214 1215 /* Replace the path path with what we got */ 1216 uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool); 1217 1218 /* And make APR produce a proper full url for us */ 1219 location = apr_uri_unparse(scratch_pool, &uri, 0); 1220 1221 /* Fall through to ensure our canonicalization rules */ 1222 } 1223 else if (!svn_path_is_url(location)) 1224 { 1225 return NULL; /* Any other formats we should support? */ 1226 } 1227 1228 return svn_uri_canonicalize(location, result_pool); 1229} 1230 1231 1232/* Implements svn_ra_serf__response_handler_t */ 1233svn_error_t * 1234svn_ra_serf__expect_empty_body(serf_request_t *request, 1235 serf_bucket_t *response, 1236 void *baton, 1237 apr_pool_t *scratch_pool) 1238{ 1239 svn_ra_serf__handler_t *handler = baton; 1240 serf_bucket_t *hdrs; 1241 const char *val; 1242 1243 /* This function is just like handle_multistatus_only() except for the 1244 XML parsing callbacks. We want to look for the human-readable element. */ 1245 1246 /* We should see this just once, in order to initialize SERVER_ERROR. 1247 At that point, the core error processing will take over. If we choose 1248 not to parse an error, then we'll never return here (because we 1249 change the response handler). */ 1250 SVN_ERR_ASSERT(handler->server_error == NULL); 1251 1252 hdrs = serf_bucket_response_get_headers(response); 1253 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1254 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1255 { 1256 svn_ra_serf__server_error_t *server_err; 1257 1258 server_err = begin_error_parsing(start_error, end_error, cdata_error, 1259 handler->handler_pool); 1260 1261 /* Get the parser to set our DONE flag. */ 1262 server_err->parser.done = &handler->done; 1263 1264 handler->server_error = server_err; 1265 } 1266 else 1267 { 1268 /* The body was not text/xml, so we don't know what to do with it. 1269 Toss anything that arrives. */ 1270 handler->discard_body = TRUE; 1271 } 1272 1273 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1274 to call the response handler again. That will start up the XML parsing, 1275 or it will be dropped on the floor (per the decision above). */ 1276 return SVN_NO_ERROR; 1277} 1278 1279 1280/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric 1281 status code into *STATUS_CODE_OUT. Ignores leading whitespace. */ 1282static svn_error_t * 1283parse_dav_status(int *status_code_out, svn_stringbuf_t *buf, 1284 apr_pool_t *scratch_pool) 1285{ 1286 svn_error_t *err; 1287 const char *token; 1288 char *tok_status; 1289 svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool); 1290 1291 svn_stringbuf_strip_whitespace(temp_buf); 1292 token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status); 1293 if (token) 1294 token = apr_strtok(NULL, " \t\r\n", &tok_status); 1295 if (!token) 1296 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1297 _("Malformed DAV:status CDATA '%s'"), 1298 buf->data); 1299 err = svn_cstring_atoi(status_code_out, token); 1300 if (err) 1301 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err, 1302 _("Malformed DAV:status CDATA '%s'"), 1303 buf->data); 1304 1305 return SVN_NO_ERROR; 1306} 1307 1308/* 1309 * Expat callback invoked on a start element tag for a 207 response. 1310 */ 1311static svn_error_t * 1312start_207(svn_ra_serf__xml_parser_t *parser, 1313 svn_ra_serf__dav_props_t name, 1314 const char **attrs, 1315 apr_pool_t *scratch_pool) 1316{ 1317 svn_ra_serf__server_error_t *ctx = parser->user_data; 1318 1319 if (!ctx->in_error && 1320 strcmp(name.namespace, "DAV:") == 0 && 1321 strcmp(name.name, "multistatus") == 0) 1322 { 1323 ctx->in_error = TRUE; 1324 } 1325 else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1326 { 1327 /* Start collecting cdata. */ 1328 svn_stringbuf_setempty(ctx->cdata); 1329 ctx->collect_cdata = TRUE; 1330 } 1331 else if (ctx->in_error && 1332 strcmp(name.namespace, "DAV:") == 0 && 1333 strcmp(name.name, "status") == 0) 1334 { 1335 /* Start collecting cdata. */ 1336 svn_stringbuf_setempty(ctx->cdata); 1337 ctx->collect_cdata = TRUE; 1338 } 1339 1340 return SVN_NO_ERROR; 1341} 1342 1343/* 1344 * Expat callback invoked on an end element tag for a 207 response. 1345 */ 1346static svn_error_t * 1347end_207(svn_ra_serf__xml_parser_t *parser, 1348 svn_ra_serf__dav_props_t name, 1349 apr_pool_t *scratch_pool) 1350{ 1351 svn_ra_serf__server_error_t *ctx = parser->user_data; 1352 1353 if (ctx->in_error && 1354 strcmp(name.namespace, "DAV:") == 0 && 1355 strcmp(name.name, "multistatus") == 0) 1356 { 1357 ctx->in_error = FALSE; 1358 } 1359 if (ctx->in_error && strcmp(name.name, "responsedescription") == 0) 1360 { 1361 /* Remove leading newline added by DEBUG_CR on server */ 1362 svn_stringbuf_strip_whitespace(ctx->cdata); 1363 1364 ctx->collect_cdata = FALSE; 1365 ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data, 1366 ctx->cdata->len); 1367 if (ctx->contains_precondition_error) 1368 ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH; 1369 else 1370 ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 1371 } 1372 else if (ctx->in_error && 1373 strcmp(name.namespace, "DAV:") == 0 && 1374 strcmp(name.name, "status") == 0) 1375 { 1376 int status_code; 1377 1378 ctx->collect_cdata = FALSE; 1379 1380 SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool)); 1381 if (status_code == 412) 1382 ctx->contains_precondition_error = TRUE; 1383 } 1384 1385 return SVN_NO_ERROR; 1386} 1387 1388/* 1389 * Expat callback invoked on CDATA elements in a 207 response. 1390 * 1391 * This callback can be called multiple times. 1392 */ 1393static svn_error_t * 1394cdata_207(svn_ra_serf__xml_parser_t *parser, 1395 const char *data, 1396 apr_size_t len, 1397 apr_pool_t *scratch_pool) 1398{ 1399 svn_ra_serf__server_error_t *ctx = parser->user_data; 1400 1401 if (ctx->collect_cdata) 1402 { 1403 svn_stringbuf_appendbytes(ctx->cdata, data, len); 1404 } 1405 1406 return SVN_NO_ERROR; 1407} 1408 1409/* Implements svn_ra_serf__response_handler_t */ 1410svn_error_t * 1411svn_ra_serf__handle_multistatus_only(serf_request_t *request, 1412 serf_bucket_t *response, 1413 void *baton, 1414 apr_pool_t *scratch_pool) 1415{ 1416 svn_ra_serf__handler_t *handler = baton; 1417 1418 /* This function is just like expect_empty_body() except for the 1419 XML parsing callbacks. We are looking for very limited pieces of 1420 the multistatus response. */ 1421 1422 /* We should see this just once, in order to initialize SERVER_ERROR. 1423 At that point, the core error processing will take over. If we choose 1424 not to parse an error, then we'll never return here (because we 1425 change the response handler). */ 1426 SVN_ERR_ASSERT(handler->server_error == NULL); 1427 1428 { 1429 serf_bucket_t *hdrs; 1430 const char *val; 1431 1432 hdrs = serf_bucket_response_get_headers(response); 1433 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1434 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1435 { 1436 svn_ra_serf__server_error_t *server_err; 1437 1438 server_err = begin_error_parsing(start_207, end_207, cdata_207, 1439 handler->handler_pool); 1440 1441 /* Get the parser to set our DONE flag. */ 1442 server_err->parser.done = &handler->done; 1443 1444 handler->server_error = server_err; 1445 } 1446 else 1447 { 1448 /* The body was not text/xml, so we don't know what to do with it. 1449 Toss anything that arrives. */ 1450 handler->discard_body = TRUE; 1451 } 1452 } 1453 1454 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1455 to call the response handler again. That will start up the XML parsing, 1456 or it will be dropped on the floor (per the decision above). */ 1457 return SVN_NO_ERROR; 1458} 1459 1460 1461/* Conforms to Expat's XML_StartElementHandler */ 1462static void 1463start_xml(void *userData, const char *raw_name, const char **attrs) 1464{ 1465 svn_ra_serf__xml_parser_t *parser = userData; 1466 svn_ra_serf__dav_props_t name; 1467 apr_pool_t *scratch_pool; 1468 svn_error_t *err; 1469 1470 if (parser->error) 1471 return; 1472 1473 if (!parser->state) 1474 svn_ra_serf__xml_push_state(parser, 0); 1475 1476 /* ### get a real scratch_pool */ 1477 scratch_pool = parser->state->pool; 1478 1479 svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool); 1480 1481 svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1482 1483 err = parser->start(parser, name, attrs, scratch_pool); 1484 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1485 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1486 1487 parser->error = err; 1488} 1489 1490 1491/* Conforms to Expat's XML_EndElementHandler */ 1492static void 1493end_xml(void *userData, const char *raw_name) 1494{ 1495 svn_ra_serf__xml_parser_t *parser = userData; 1496 svn_ra_serf__dav_props_t name; 1497 svn_error_t *err; 1498 apr_pool_t *scratch_pool; 1499 1500 if (parser->error) 1501 return; 1502 1503 /* ### get a real scratch_pool */ 1504 scratch_pool = parser->state->pool; 1505 1506 svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name); 1507 1508 err = parser->end(parser, name, scratch_pool); 1509 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1510 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1511 1512 parser->error = err; 1513} 1514 1515 1516/* Conforms to Expat's XML_CharacterDataHandler */ 1517static void 1518cdata_xml(void *userData, const char *data, int len) 1519{ 1520 svn_ra_serf__xml_parser_t *parser = userData; 1521 svn_error_t *err; 1522 apr_pool_t *scratch_pool; 1523 1524 if (parser->error) 1525 return; 1526 1527 if (!parser->state) 1528 svn_ra_serf__xml_push_state(parser, 0); 1529 1530 /* ### get a real scratch_pool */ 1531 scratch_pool = parser->state->pool; 1532 1533 err = parser->cdata(parser, data, len, scratch_pool); 1534 if (err && !SERF_BUCKET_READ_ERROR(err->apr_err)) 1535 err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL); 1536 1537 parser->error = err; 1538} 1539 1540/* Flip the requisite bits in CTX to indicate that processing of the 1541 response is complete, adding the current "done item" to the list of 1542 completed items. */ 1543static void 1544add_done_item(svn_ra_serf__xml_parser_t *ctx) 1545{ 1546 /* Make sure we don't add to DONE_LIST twice. */ 1547 if (!*ctx->done) 1548 { 1549 *ctx->done = TRUE; 1550 if (ctx->done_list) 1551 { 1552 ctx->done_item->data = ctx->user_data; 1553 ctx->done_item->next = *ctx->done_list; 1554 *ctx->done_list = ctx->done_item; 1555 } 1556 } 1557} 1558 1559 1560static svn_error_t * 1561write_to_pending(svn_ra_serf__xml_parser_t *ctx, 1562 const char *data, 1563 apr_size_t len, 1564 apr_pool_t *scratch_pool) 1565{ 1566 if (ctx->pending == NULL) 1567 { 1568 ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending)); 1569 ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE, 1570 SPILL_SIZE, 1571 ctx->pool); 1572 } 1573 1574 /* Copy the data into one or more chunks in the spill buffer. */ 1575 return svn_error_trace(svn_spillbuf__write(ctx->pending->buf, 1576 data, len, 1577 scratch_pool)); 1578} 1579 1580 1581static svn_error_t * 1582inject_to_parser(svn_ra_serf__xml_parser_t *ctx, 1583 const char *data, 1584 apr_size_t len, 1585 const serf_status_line *sl) 1586{ 1587 int xml_status; 1588 1589 xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0); 1590 1591 if (! ctx->ignore_errors) 1592 { 1593 SVN_ERR(ctx->error); 1594 1595 if (xml_status != XML_STATUS_OK) 1596 { 1597 if (sl == NULL) 1598 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1599 _("XML parsing failed")); 1600 1601 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1602 _("XML parsing failed: (%d %s)"), 1603 sl->code, sl->reason); 1604 } 1605 } 1606 1607 return SVN_NO_ERROR; 1608} 1609 1610/* Apr pool cleanup handler to release an XML_Parser in success and error 1611 conditions */ 1612static apr_status_t 1613xml_parser_cleanup(void *baton) 1614{ 1615 XML_Parser *xmlp = baton; 1616 1617 if (*xmlp) 1618 { 1619 (void) XML_ParserFree(*xmlp); 1620 *xmlp = NULL; 1621 } 1622 1623 return APR_SUCCESS; 1624} 1625 1626/* Limit the amount of pending content to parse at once to < 100KB per 1627 iteration. This number is chosen somewhat arbitrarely. Making it lower 1628 will have a drastical negative impact on performance, whereas increasing it 1629 increases the risk for connection timeouts. 1630 */ 1631#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5 1632 1633svn_error_t * 1634svn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser, 1635 svn_boolean_t *network_eof, 1636 apr_pool_t *scratch_pool) 1637{ 1638 svn_boolean_t pending_empty = FALSE; 1639 apr_size_t cur_read = 0; 1640 1641 /* Fast path exit: already paused, nothing to do, or already done. */ 1642 if (parser->paused || parser->pending == NULL || *parser->done) 1643 { 1644 *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1645 return SVN_NO_ERROR; 1646 } 1647 1648 /* Parsing the pending conten in the spillbuf will result in many disc i/o 1649 operations. This can be so slow that we don't run the network event 1650 processing loop often enough, resulting in timed out connections. 1651 1652 So we limit the amounts of bytes parsed per iteration. 1653 */ 1654 while (cur_read < PENDING_TO_PARSE) 1655 { 1656 const char *data; 1657 apr_size_t len; 1658 1659 /* Get a block of content, stopping the loop when we run out. */ 1660 SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf, 1661 scratch_pool)); 1662 if (data) 1663 { 1664 /* Inject the content into the XML parser. */ 1665 SVN_ERR(inject_to_parser(parser, data, len, NULL)); 1666 1667 /* If the XML parsing callbacks paused us, then we're done for now. */ 1668 if (parser->paused) 1669 break; 1670 1671 cur_read += len; 1672 } 1673 else 1674 { 1675 /* The buffer is empty. */ 1676 pending_empty = TRUE; 1677 break; 1678 } 1679 } 1680 1681 /* If the PENDING structures are empty *and* we consumed all content from 1682 the network, then we're completely done with the parsing. */ 1683 if (pending_empty && 1684 parser->pending->network_eof) 1685 { 1686 int xml_status; 1687 SVN_ERR_ASSERT(parser->xmlp != NULL); 1688 1689 /* Tell the parser that no more content will be parsed. */ 1690 xml_status = XML_Parse(parser->xmlp, NULL, 0, 1); 1691 1692 apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup); 1693 parser->xmlp = NULL; 1694 1695 if (! parser->ignore_errors) 1696 { 1697 SVN_ERR(parser->error); 1698 1699 if (xml_status != XML_STATUS_OK) 1700 { 1701 return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 1702 _("XML parsing failed")); 1703 } 1704 } 1705 1706 add_done_item(parser); 1707 } 1708 1709 *network_eof = parser->pending ? parser->pending->network_eof : FALSE; 1710 1711 return SVN_NO_ERROR; 1712} 1713#undef PENDING_TO_PARSE 1714 1715 1716/* ### this is still broken conceptually. just shifting incrementally... */ 1717static svn_error_t * 1718handle_server_error(serf_request_t *request, 1719 serf_bucket_t *response, 1720 apr_pool_t *scratch_pool) 1721{ 1722 svn_ra_serf__server_error_t server_err = { 0 }; 1723 serf_bucket_t *hdrs; 1724 const char *val; 1725 apr_status_t err; 1726 1727 hdrs = serf_bucket_response_get_headers(response); 1728 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1729 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1730 { 1731 /* ### we should figure out how to reuse begin_error_parsing */ 1732 1733 server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL); 1734 server_err.contains_precondition_error = FALSE; 1735 server_err.cdata = svn_stringbuf_create_empty(scratch_pool); 1736 server_err.collect_cdata = FALSE; 1737 server_err.parser.pool = server_err.error->pool; 1738 server_err.parser.user_data = &server_err; 1739 server_err.parser.start = start_error; 1740 server_err.parser.end = end_error; 1741 server_err.parser.cdata = cdata_error; 1742 server_err.parser.done = &server_err.done; 1743 server_err.parser.ignore_errors = TRUE; 1744 1745 /* We don't care about any errors except for SERVER_ERR.ERROR */ 1746 svn_error_clear(svn_ra_serf__handle_xml_parser(request, 1747 response, 1748 &server_err.parser, 1749 scratch_pool)); 1750 1751 /* ### checking DONE is silly. the above only parses whatever has 1752 ### been received at the network interface. totally wrong. but 1753 ### it is what we have for now (maintaining historical code), 1754 ### until we fully migrate. */ 1755 if (server_err.done && server_err.error->apr_err == APR_SUCCESS) 1756 { 1757 svn_error_clear(server_err.error); 1758 server_err.error = SVN_NO_ERROR; 1759 } 1760 1761 return svn_error_trace(server_err.error); 1762 } 1763 1764 /* The only error that we will return is from the XML response body. 1765 Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to 1766 surface. */ 1767 err = drain_bucket(response); 1768 if (err && !SERF_BUCKET_READ_ERROR(err)) 1769 return svn_ra_serf__wrap_err(err, NULL); 1770 1771 return SVN_NO_ERROR; 1772} 1773 1774 1775/* Implements svn_ra_serf__response_handler_t */ 1776svn_error_t * 1777svn_ra_serf__handle_xml_parser(serf_request_t *request, 1778 serf_bucket_t *response, 1779 void *baton, 1780 apr_pool_t *pool) 1781{ 1782 serf_status_line sl; 1783 apr_status_t status; 1784 svn_ra_serf__xml_parser_t *ctx = baton; 1785 svn_error_t *err; 1786 1787 /* ### get the HANDLER rather than fetching this. */ 1788 status = serf_bucket_response_status(response, &sl); 1789 if (SERF_BUCKET_READ_ERROR(status)) 1790 { 1791 return svn_ra_serf__wrap_err(status, NULL); 1792 } 1793 1794 /* Woo-hoo. Nothing here to see. */ 1795 if (sl.code == 404 && !ctx->ignore_errors) 1796 { 1797 err = handle_server_error(request, response, pool); 1798 1799 if (err && APR_STATUS_IS_EOF(err->apr_err)) 1800 add_done_item(ctx); 1801 1802 return svn_error_trace(err); 1803 } 1804 1805 if (!ctx->xmlp) 1806 { 1807 ctx->xmlp = XML_ParserCreate(NULL); 1808 apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup, 1809 apr_pool_cleanup_null); 1810 XML_SetUserData(ctx->xmlp, ctx); 1811 XML_SetElementHandler(ctx->xmlp, start_xml, end_xml); 1812 if (ctx->cdata) 1813 { 1814 XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml); 1815 } 1816 } 1817 1818 while (1) 1819 { 1820 const char *data; 1821 apr_size_t len; 1822 1823 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 1824 if (SERF_BUCKET_READ_ERROR(status)) 1825 { 1826 return svn_ra_serf__wrap_err(status, NULL); 1827 } 1828 1829 /* Note: once the callbacks invoked by inject_to_parser() sets the 1830 PAUSED flag, then it will not be cleared. write_to_pending() will 1831 only save the content. Logic outside of serf_context_run() will 1832 clear that flag, as appropriate, along with processing the 1833 content that we have placed into the PENDING buffer. 1834 1835 We want to save arriving content into the PENDING structures if 1836 the parser has been paused, or we already have data in there (so 1837 the arriving data is appended, rather than injected out of order) */ 1838 if (ctx->paused || HAS_PENDING_DATA(ctx->pending)) 1839 { 1840 err = write_to_pending(ctx, data, len, pool); 1841 } 1842 else 1843 { 1844 err = inject_to_parser(ctx, data, len, &sl); 1845 if (err) 1846 { 1847 /* Should have no errors if IGNORE_ERRORS is set. */ 1848 SVN_ERR_ASSERT(!ctx->ignore_errors); 1849 } 1850 } 1851 if (err) 1852 { 1853 SVN_ERR_ASSERT(ctx->xmlp != NULL); 1854 1855 apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1856 add_done_item(ctx); 1857 return svn_error_trace(err); 1858 } 1859 1860 if (APR_STATUS_IS_EAGAIN(status)) 1861 { 1862 return svn_ra_serf__wrap_err(status, NULL); 1863 } 1864 1865 if (APR_STATUS_IS_EOF(status)) 1866 { 1867 if (ctx->pending != NULL) 1868 ctx->pending->network_eof = TRUE; 1869 1870 /* We just hit the end of the network content. If we have nothing 1871 in the PENDING structures, then we're completely done. */ 1872 if (!HAS_PENDING_DATA(ctx->pending)) 1873 { 1874 int xml_status; 1875 SVN_ERR_ASSERT(ctx->xmlp != NULL); 1876 1877 xml_status = XML_Parse(ctx->xmlp, NULL, 0, 1); 1878 1879 apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup); 1880 1881 if (! ctx->ignore_errors) 1882 { 1883 SVN_ERR(ctx->error); 1884 1885 if (xml_status != XML_STATUS_OK) 1886 { 1887 return svn_error_create( 1888 SVN_ERR_XML_MALFORMED, NULL, 1889 _("The XML response contains invalid XML")); 1890 } 1891 } 1892 1893 add_done_item(ctx); 1894 } 1895 1896 return svn_ra_serf__wrap_err(status, NULL); 1897 } 1898 1899 /* feed me! */ 1900 } 1901 /* not reached */ 1902} 1903 1904 1905apr_status_t 1906svn_ra_serf__credentials_callback(char **username, char **password, 1907 serf_request_t *request, void *baton, 1908 int code, const char *authn_type, 1909 const char *realm, 1910 apr_pool_t *pool) 1911{ 1912 svn_ra_serf__handler_t *handler = baton; 1913 svn_ra_serf__session_t *session = handler->session; 1914 void *creds; 1915 svn_auth_cred_simple_t *simple_creds; 1916 svn_error_t *err; 1917 1918 if (code == 401) 1919 { 1920 /* Use svn_auth_first_credentials if this is the first time we ask for 1921 credentials during this session OR if the last time we asked 1922 session->auth_state wasn't set (eg. if the credentials provider was 1923 cancelled by the user). */ 1924 if (!session->auth_state) 1925 { 1926 err = svn_auth_first_credentials(&creds, 1927 &session->auth_state, 1928 SVN_AUTH_CRED_SIMPLE, 1929 realm, 1930 session->wc_callbacks->auth_baton, 1931 session->pool); 1932 } 1933 else 1934 { 1935 err = svn_auth_next_credentials(&creds, 1936 session->auth_state, 1937 session->pool); 1938 } 1939 1940 if (err) 1941 { 1942 (void) save_error(session, err); 1943 return err->apr_err; 1944 } 1945 1946 session->auth_attempts++; 1947 1948 if (!creds || session->auth_attempts > 4) 1949 { 1950 /* No more credentials. */ 1951 (void) save_error(session, 1952 svn_error_create( 1953 SVN_ERR_AUTHN_FAILED, NULL, 1954 _("No more credentials or we tried too many " 1955 "times.\nAuthentication failed"))); 1956 return SVN_ERR_AUTHN_FAILED; 1957 } 1958 1959 simple_creds = creds; 1960 *username = apr_pstrdup(pool, simple_creds->username); 1961 *password = apr_pstrdup(pool, simple_creds->password); 1962 } 1963 else 1964 { 1965 *username = apr_pstrdup(pool, session->proxy_username); 1966 *password = apr_pstrdup(pool, session->proxy_password); 1967 1968 session->proxy_auth_attempts++; 1969 1970 if (!session->proxy_username || session->proxy_auth_attempts > 4) 1971 { 1972 /* No more credentials. */ 1973 (void) save_error(session, 1974 svn_error_create( 1975 SVN_ERR_AUTHN_FAILED, NULL, 1976 _("Proxy authentication failed"))); 1977 return SVN_ERR_AUTHN_FAILED; 1978 } 1979 } 1980 1981 handler->conn->last_status_code = code; 1982 1983 return APR_SUCCESS; 1984} 1985 1986/* Wait for HTTP response status and headers, and invoke HANDLER-> 1987 response_handler() to carry out operation-specific processing. 1988 Afterwards, check for connection close. 1989 1990 SERF_STATUS allows returning errors to serf without creating a 1991 subversion error object. 1992 */ 1993static svn_error_t * 1994handle_response(serf_request_t *request, 1995 serf_bucket_t *response, 1996 svn_ra_serf__handler_t *handler, 1997 apr_status_t *serf_status, 1998 apr_pool_t *scratch_pool) 1999{ 2000 apr_status_t status; 2001 svn_error_t *err; 2002 2003 /* ### need to verify whether this already gets init'd on every 2004 ### successful exit. for an error-exit, it will (properly) be 2005 ### ignored by the caller. */ 2006 *serf_status = APR_SUCCESS; 2007 2008 if (!response) 2009 { 2010 /* Uh-oh. Our connection died. */ 2011 if (handler->response_error) 2012 { 2013 /* Give a handler chance to prevent request requeue. */ 2014 SVN_ERR(handler->response_error(request, response, 0, 2015 handler->response_error_baton)); 2016 2017 svn_ra_serf__request_create(handler); 2018 } 2019 /* Response error callback is not configured. Requeue another request 2020 for this handler only if we didn't started to process body. 2021 Return error otherwise. */ 2022 else if (!handler->reading_body) 2023 { 2024 svn_ra_serf__request_create(handler); 2025 } 2026 else 2027 { 2028 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2029 _("%s request on '%s' failed"), 2030 handler->method, handler->path); 2031 } 2032 2033 return SVN_NO_ERROR; 2034 } 2035 2036 /* If we're reading the body, then skip all this preparation. */ 2037 if (handler->reading_body) 2038 goto process_body; 2039 2040 /* Copy the Status-Line info into HANDLER, if we don't yet have it. */ 2041 if (handler->sline.version == 0) 2042 { 2043 serf_status_line sl; 2044 2045 status = serf_bucket_response_status(response, &sl); 2046 if (status != APR_SUCCESS) 2047 { 2048 /* The response line is not (yet) ready, or some other error. */ 2049 *serf_status = status; 2050 return SVN_NO_ERROR; /* Handled by serf */ 2051 } 2052 2053 /* If we got APR_SUCCESS, then we should have Status-Line info. */ 2054 SVN_ERR_ASSERT(sl.version != 0); 2055 2056 handler->sline = sl; 2057 handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason); 2058 2059 /* HTTP/1.1? (or later) */ 2060 if (sl.version != SERF_HTTP_10) 2061 handler->session->http10 = FALSE; 2062 } 2063 2064 /* Keep reading from the network until we've read all the headers. */ 2065 status = serf_bucket_response_wait_for_headers(response); 2066 if (status) 2067 { 2068 /* The typical "error" will be APR_EAGAIN, meaning that more input 2069 from the network is required to complete the reading of the 2070 headers. */ 2071 if (!APR_STATUS_IS_EOF(status)) 2072 { 2073 /* Either the headers are not (yet) complete, or there really 2074 was an error. */ 2075 *serf_status = status; 2076 return SVN_NO_ERROR; 2077 } 2078 2079 /* wait_for_headers() will return EOF if there is no body in this 2080 response, or if we completely read the body. The latter is not 2081 true since we would have set READING_BODY to get the body read, 2082 and we would not be back to this code block. 2083 2084 It can also return EOF if we truly hit EOF while (say) processing 2085 the headers. aka Badness. */ 2086 2087 /* Cases where a lack of a response body (via EOF) is okay: 2088 * - A HEAD request 2089 * - 204/304 response 2090 * 2091 * Otherwise, if we get an EOF here, something went really wrong: either 2092 * the server closed on us early or we're reading too much. Either way, 2093 * scream loudly. 2094 */ 2095 if (strcmp(handler->method, "HEAD") != 0 2096 && handler->sline.code != 204 2097 && handler->sline.code != 304) 2098 { 2099 err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, 2100 svn_ra_serf__wrap_err(status, NULL), 2101 _("Premature EOF seen from server" 2102 " (http status=%d)"), 2103 handler->sline.code); 2104 2105 /* In case anything else arrives... discard it. */ 2106 handler->discard_body = TRUE; 2107 2108 return err; 2109 } 2110 } 2111 2112 /* ... and set up the header fields in HANDLER. */ 2113 handler->location = response_get_location(response, 2114 handler->session->session_url_str, 2115 handler->handler_pool, 2116 scratch_pool); 2117 2118 /* On the last request, we failed authentication. We succeeded this time, 2119 so let's save away these credentials. */ 2120 if (handler->conn->last_status_code == 401 && handler->sline.code < 400) 2121 { 2122 SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 2123 handler->session->pool)); 2124 handler->session->auth_attempts = 0; 2125 handler->session->auth_state = NULL; 2126 } 2127 handler->conn->last_status_code = handler->sline.code; 2128 2129 if (handler->sline.code == 405 2130 || handler->sline.code == 408 2131 || handler->sline.code == 409 2132 || handler->sline.code >= 500) 2133 { 2134 /* 405 Method Not allowed. 2135 408 Request Timeout 2136 409 Conflict: can indicate a hook error. 2137 5xx (Internal) Server error. */ 2138 serf_bucket_t *hdrs; 2139 const char *val; 2140 2141 hdrs = serf_bucket_response_get_headers(response); 2142 val = serf_bucket_headers_get(hdrs, "Content-Type"); 2143 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 2144 { 2145 svn_ra_serf__server_error_t *server_err; 2146 2147 server_err = begin_error_parsing(start_error, end_error, cdata_error, 2148 handler->handler_pool); 2149 /* Get the parser to set our DONE flag. */ 2150 server_err->parser.done = &handler->done; 2151 2152 handler->server_error = server_err; 2153 } 2154 else 2155 { 2156 handler->discard_body = TRUE; 2157 2158 if (!handler->session->pending_error) 2159 { 2160 apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED; 2161 2162 /* 405 == Method Not Allowed (Occurs when trying to lock a working 2163 copy path which no longer exists at HEAD in the repository. */ 2164 if (handler->sline.code == 405 2165 && strcmp(handler->method, "LOCK") == 0) 2166 apr_err = SVN_ERR_FS_OUT_OF_DATE; 2167 2168 handler->session->pending_error = 2169 svn_error_createf(apr_err, NULL, 2170 _("%s request on '%s' failed: %d %s"), 2171 handler->method, handler->path, 2172 handler->sline.code, handler->sline.reason); 2173 } 2174 } 2175 } 2176 2177 /* Stop processing the above, on every packet arrival. */ 2178 handler->reading_body = TRUE; 2179 2180 process_body: 2181 2182 /* We've been instructed to ignore the body. Drain whatever is present. */ 2183 if (handler->discard_body) 2184 { 2185 *serf_status = drain_bucket(response); 2186 2187 /* If the handler hasn't set done (which it shouldn't have) and 2188 we now have the EOF, go ahead and set it so that we can stop 2189 our context loops. 2190 */ 2191 if (!handler->done && APR_STATUS_IS_EOF(*serf_status)) 2192 handler->done = TRUE; 2193 2194 return SVN_NO_ERROR; 2195 } 2196 2197 /* If we are supposed to parse the body as a server_error, then do 2198 that now. */ 2199 if (handler->server_error != NULL) 2200 { 2201 err = svn_ra_serf__handle_xml_parser(request, response, 2202 &handler->server_error->parser, 2203 scratch_pool); 2204 2205 /* If we do not receive an error or it is a non-transient error, return 2206 immediately. 2207 2208 APR_EOF will be returned when parsing is complete. 2209 2210 APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through 2211 parsing and the network has no more data right now. If we receive that, 2212 clear the error and return - allowing serf to wait for more data. 2213 */ 2214 if (!err || SERF_BUCKET_READ_ERROR(err->apr_err)) 2215 return svn_error_trace(err); 2216 2217 if (!APR_STATUS_IS_EOF(err->apr_err)) 2218 { 2219 *serf_status = err->apr_err; 2220 svn_error_clear(err); 2221 return SVN_NO_ERROR; 2222 } 2223 2224 /* Clear the EOF. We don't need it. */ 2225 svn_error_clear(err); 2226 2227 /* If the parsing is done, and we did not extract an error, then 2228 simply toss everything, and anything else that might arrive. 2229 The higher-level code will need to investigate HANDLER->SLINE, 2230 as we have no further information for them. */ 2231 if (handler->done 2232 && handler->server_error->error->apr_err == APR_SUCCESS) 2233 { 2234 svn_error_clear(handler->server_error->error); 2235 2236 /* Stop parsing for a server error. */ 2237 handler->server_error = NULL; 2238 2239 /* If anything arrives after this, then just discard it. */ 2240 handler->discard_body = TRUE; 2241 } 2242 2243 *serf_status = APR_EOF; 2244 return SVN_NO_ERROR; 2245 } 2246 2247 /* Pass the body along to the registered response handler. */ 2248 err = handler->response_handler(request, response, 2249 handler->response_baton, 2250 scratch_pool); 2251 2252 if (err 2253 && (!SERF_BUCKET_READ_ERROR(err->apr_err) 2254 || APR_STATUS_IS_ECONNRESET(err->apr_err) 2255 || APR_STATUS_IS_ECONNABORTED(err->apr_err))) 2256 { 2257 /* These errors are special cased in serf 2258 ### We hope no handler returns these by accident. */ 2259 *serf_status = err->apr_err; 2260 svn_error_clear(err); 2261 return SVN_NO_ERROR; 2262 } 2263 2264 return svn_error_trace(err); 2265} 2266 2267 2268/* Implements serf_response_handler_t for handle_response. Storing 2269 errors in handler->session->pending_error if appropriate. */ 2270static apr_status_t 2271handle_response_cb(serf_request_t *request, 2272 serf_bucket_t *response, 2273 void *baton, 2274 apr_pool_t *scratch_pool) 2275{ 2276 svn_ra_serf__handler_t *handler = baton; 2277 svn_error_t *err; 2278 apr_status_t inner_status; 2279 apr_status_t outer_status; 2280 2281 err = svn_error_trace(handle_response(request, response, 2282 handler, &inner_status, 2283 scratch_pool)); 2284 2285 /* Select the right status value to return. */ 2286 outer_status = save_error(handler->session, err); 2287 if (!outer_status) 2288 outer_status = inner_status; 2289 2290 /* Make sure the DONE flag is set properly. */ 2291 if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) 2292 handler->done = TRUE; 2293 2294 return outer_status; 2295} 2296 2297/* Perform basic request setup, with special handling for HEAD requests, 2298 and finer-grained callbacks invoked (if non-NULL) to produce the request 2299 headers and body. */ 2300static svn_error_t * 2301setup_request(serf_request_t *request, 2302 svn_ra_serf__handler_t *handler, 2303 serf_bucket_t **req_bkt, 2304 apr_pool_t *request_pool, 2305 apr_pool_t *scratch_pool) 2306{ 2307 serf_bucket_t *body_bkt; 2308 serf_bucket_t *headers_bkt; 2309 const char *accept_encoding; 2310 2311 if (handler->body_delegate) 2312 { 2313 serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 2314 2315 /* ### should pass the scratch_pool */ 2316 SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, 2317 bkt_alloc, request_pool)); 2318 } 2319 else 2320 { 2321 body_bkt = NULL; 2322 } 2323 2324 if (handler->custom_accept_encoding) 2325 { 2326 accept_encoding = NULL; 2327 } 2328 else if (handler->session->using_compression) 2329 { 2330 /* Accept gzip compression if enabled. */ 2331 accept_encoding = "gzip"; 2332 } 2333 else 2334 { 2335 accept_encoding = NULL; 2336 } 2337 2338 SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 2339 handler->session, handler->method, handler->path, 2340 body_bkt, handler->body_type, accept_encoding, 2341 request_pool, scratch_pool)); 2342 2343 if (handler->header_delegate) 2344 { 2345 /* ### should pass the scratch_pool */ 2346 SVN_ERR(handler->header_delegate(headers_bkt, 2347 handler->header_delegate_baton, 2348 request_pool)); 2349 } 2350 2351 return APR_SUCCESS; 2352} 2353 2354/* Implements the serf_request_setup_t interface (which sets up both a 2355 request and its response handler callback). Handles errors for 2356 setup_request_cb */ 2357static apr_status_t 2358setup_request_cb(serf_request_t *request, 2359 void *setup_baton, 2360 serf_bucket_t **req_bkt, 2361 serf_response_acceptor_t *acceptor, 2362 void **acceptor_baton, 2363 serf_response_handler_t *s_handler, 2364 void **s_handler_baton, 2365 apr_pool_t *pool) 2366{ 2367 svn_ra_serf__handler_t *handler = setup_baton; 2368 svn_error_t *err; 2369 2370 /* ### construct a scratch_pool? serf gives us a pool that will live for 2371 ### the duration of the request. */ 2372 apr_pool_t *scratch_pool = pool; 2373 2374 if (strcmp(handler->method, "HEAD") == 0) 2375 *acceptor = accept_head; 2376 else 2377 *acceptor = accept_response; 2378 *acceptor_baton = handler->session; 2379 2380 *s_handler = handle_response_cb; 2381 *s_handler_baton = handler; 2382 2383 err = svn_error_trace(setup_request(request, handler, req_bkt, 2384 pool /* request_pool */, scratch_pool)); 2385 2386 return save_error(handler->session, err); 2387} 2388 2389void 2390svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) 2391{ 2392 SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL); 2393 2394 /* In case HANDLER is re-queued, reset the various transient fields. 2395 2396 ### prior to recent changes, HANDLER was constant. maybe we should 2397 ### break out these processing fields, apart from the request 2398 ### definition. */ 2399 handler->done = FALSE; 2400 handler->server_error = NULL; 2401 handler->sline.version = 0; 2402 handler->location = NULL; 2403 handler->reading_body = FALSE; 2404 handler->discard_body = FALSE; 2405 2406 /* ### do we ever alter the >response_handler? */ 2407 2408 /* ### do we need to hold onto the returned request object, or just 2409 ### not worry about it (the serf ctx will manage it). */ 2410 (void) serf_connection_request_create(handler->conn->conn, 2411 setup_request_cb, handler); 2412} 2413 2414 2415svn_error_t * 2416svn_ra_serf__discover_vcc(const char **vcc_url, 2417 svn_ra_serf__session_t *session, 2418 svn_ra_serf__connection_t *conn, 2419 apr_pool_t *pool) 2420{ 2421 const char *path; 2422 const char *relative_path; 2423 const char *uuid; 2424 2425 /* If we've already got the information our caller seeks, just return it. */ 2426 if (session->vcc_url && session->repos_root_str) 2427 { 2428 *vcc_url = session->vcc_url; 2429 return SVN_NO_ERROR; 2430 } 2431 2432 /* If no connection is provided, use the default one. */ 2433 if (! conn) 2434 { 2435 conn = session->conns[0]; 2436 } 2437 2438 path = session->session_url.path; 2439 *vcc_url = NULL; 2440 uuid = NULL; 2441 2442 do 2443 { 2444 apr_hash_t *props; 2445 svn_error_t *err; 2446 2447 err = svn_ra_serf__fetch_node_props(&props, conn, 2448 path, SVN_INVALID_REVNUM, 2449 base_props, pool, pool); 2450 if (! err) 2451 { 2452 apr_hash_t *ns_props; 2453 2454 ns_props = apr_hash_get(props, "DAV:", 4); 2455 *vcc_url = svn_prop_get_value(ns_props, 2456 "version-controlled-configuration"); 2457 2458 ns_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV); 2459 relative_path = svn_prop_get_value(ns_props, 2460 "baseline-relative-path"); 2461 uuid = svn_prop_get_value(ns_props, "repository-uuid"); 2462 break; 2463 } 2464 else 2465 { 2466 if ((err->apr_err != SVN_ERR_FS_NOT_FOUND) && 2467 (err->apr_err != SVN_ERR_RA_DAV_FORBIDDEN)) 2468 { 2469 return svn_error_trace(err); /* found a _real_ error */ 2470 } 2471 else 2472 { 2473 /* This happens when the file is missing in HEAD. */ 2474 svn_error_clear(err); 2475 2476 /* Okay, strip off a component from PATH. */ 2477 path = svn_urlpath__dirname(path, pool); 2478 2479 /* An error occurred on conns. serf 0.4.0 remembers that 2480 the connection had a problem. We need to reset it, in 2481 order to use it again. */ 2482 serf_connection_reset(conn->conn); 2483 } 2484 } 2485 } 2486 while ((path[0] != '\0') 2487 && (! (path[0] == '/' && path[1] == '\0'))); 2488 2489 if (!*vcc_url) 2490 { 2491 return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 2492 _("The PROPFIND response did not include the " 2493 "requested version-controlled-configuration " 2494 "value")); 2495 } 2496 2497 /* Store our VCC in our cache. */ 2498 if (!session->vcc_url) 2499 { 2500 session->vcc_url = apr_pstrdup(session->pool, *vcc_url); 2501 } 2502 2503 /* Update our cached repository root URL. */ 2504 if (!session->repos_root_str) 2505 { 2506 svn_stringbuf_t *url_buf; 2507 2508 url_buf = svn_stringbuf_create(path, pool); 2509 2510 svn_path_remove_components(url_buf, 2511 svn_path_component_count(relative_path)); 2512 2513 /* Now recreate the root_url. */ 2514 session->repos_root = session->session_url; 2515 session->repos_root.path = 2516 (char *)svn_fspath__canonicalize(url_buf->data, session->pool); 2517 session->repos_root_str = 2518 svn_urlpath__canonicalize(apr_uri_unparse(session->pool, 2519 &session->repos_root, 0), 2520 session->pool); 2521 } 2522 2523 /* Store the repository UUID in the cache. */ 2524 if (!session->uuid) 2525 { 2526 session->uuid = apr_pstrdup(session->pool, uuid); 2527 } 2528 2529 return SVN_NO_ERROR; 2530} 2531 2532svn_error_t * 2533svn_ra_serf__get_relative_path(const char **rel_path, 2534 const char *orig_path, 2535 svn_ra_serf__session_t *session, 2536 svn_ra_serf__connection_t *conn, 2537 apr_pool_t *pool) 2538{ 2539 const char *decoded_root, *decoded_orig; 2540 2541 if (! session->repos_root.path) 2542 { 2543 const char *vcc_url; 2544 2545 /* This should only happen if we haven't detected HTTP v2 2546 support from the server. */ 2547 assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 2548 2549 /* We don't actually care about the VCC_URL, but this API 2550 promises to populate the session's root-url cache, and that's 2551 what we really want. */ 2552 SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, 2553 conn ? conn : session->conns[0], 2554 pool)); 2555 } 2556 2557 decoded_root = svn_path_uri_decode(session->repos_root.path, pool); 2558 decoded_orig = svn_path_uri_decode(orig_path, pool); 2559 *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig); 2560 SVN_ERR_ASSERT(*rel_path != NULL); 2561 return SVN_NO_ERROR; 2562} 2563 2564svn_error_t * 2565svn_ra_serf__report_resource(const char **report_target, 2566 svn_ra_serf__session_t *session, 2567 svn_ra_serf__connection_t *conn, 2568 apr_pool_t *pool) 2569{ 2570 /* If we have HTTP v2 support, we want to report against the 'me' 2571 resource. */ 2572 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 2573 *report_target = apr_pstrdup(pool, session->me_resource); 2574 2575 /* Otherwise, we'll use the default VCC. */ 2576 else 2577 SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool)); 2578 2579 return SVN_NO_ERROR; 2580} 2581 2582svn_error_t * 2583svn_ra_serf__error_on_status(serf_status_line sline, 2584 const char *path, 2585 const char *location) 2586{ 2587 switch(sline.code) 2588 { 2589 case 301: 2590 case 302: 2591 case 307: 2592 return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, 2593 (sline.code == 301) 2594 ? _("Repository moved permanently to '%s';" 2595 " please relocate") 2596 : _("Repository moved temporarily to '%s';" 2597 " please relocate"), location); 2598 case 403: 2599 return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, 2600 _("Access to '%s' forbidden"), path); 2601 2602 case 404: 2603 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 2604 _("'%s' path not found"), path); 2605 case 423: 2606 return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, 2607 _("'%s': no lock token available"), path); 2608 2609 case 411: 2610 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2611 _("DAV request failed: 411 Content length required. The " 2612 "server or an intermediate proxy does not accept " 2613 "chunked encoding. Try setting 'http-chunked-requests' " 2614 "to 'auto' or 'no' in your client configuration.")); 2615 case 501: 2616 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 2617 _("The requested feature is not supported by " 2618 "'%s'"), path); 2619 } 2620 2621 if (sline.code >= 300) 2622 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 2623 _("Unexpected HTTP status %d '%s' on '%s'\n"), 2624 sline.code, sline.reason, path); 2625 2626 return SVN_NO_ERROR; 2627} 2628 2629svn_error_t * 2630svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, 2631 svn_delta_shim_callbacks_t *callbacks) 2632{ 2633 svn_ra_serf__session_t *session = ra_session->priv; 2634 2635 session->shim_callbacks = callbacks; 2636 return SVN_NO_ERROR; 2637} 2638 2639 2640/* Conforms to Expat's XML_StartElementHandler */ 2641static void 2642expat_start(void *userData, const char *raw_name, const char **attrs) 2643{ 2644 struct expat_ctx_t *ectx = userData; 2645 2646 if (ectx->inner_error != NULL) 2647 return; 2648 2649 ectx->inner_error = svn_error_trace( 2650 svn_ra_serf__xml_cb_start(ectx->xmlctx, 2651 raw_name, attrs)); 2652 2653#ifdef EXPAT_HAS_STOPPARSER 2654 if (ectx->inner_error) 2655 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2656#endif 2657} 2658 2659 2660/* Conforms to Expat's XML_EndElementHandler */ 2661static void 2662expat_end(void *userData, const char *raw_name) 2663{ 2664 struct expat_ctx_t *ectx = userData; 2665 2666 if (ectx->inner_error != NULL) 2667 return; 2668 2669 ectx->inner_error = svn_error_trace( 2670 svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name)); 2671 2672#ifdef EXPAT_HAS_STOPPARSER 2673 if (ectx->inner_error) 2674 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2675#endif 2676} 2677 2678 2679/* Conforms to Expat's XML_CharacterDataHandler */ 2680static void 2681expat_cdata(void *userData, const char *data, int len) 2682{ 2683 struct expat_ctx_t *ectx = userData; 2684 2685 if (ectx->inner_error != NULL) 2686 return; 2687 2688 ectx->inner_error = svn_error_trace( 2689 svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len)); 2690 2691#ifdef EXPAT_HAS_STOPPARSER 2692 if (ectx->inner_error) 2693 (void) XML_StopParser(ectx->parser, 0 /* resumable */); 2694#endif 2695} 2696 2697 2698/* Implements svn_ra_serf__response_handler_t */ 2699static svn_error_t * 2700expat_response_handler(serf_request_t *request, 2701 serf_bucket_t *response, 2702 void *baton, 2703 apr_pool_t *scratch_pool) 2704{ 2705 struct expat_ctx_t *ectx = baton; 2706 2707 if (!ectx->parser) 2708 { 2709 ectx->parser = XML_ParserCreate(NULL); 2710 apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, 2711 xml_parser_cleanup, apr_pool_cleanup_null); 2712 XML_SetUserData(ectx->parser, ectx); 2713 XML_SetElementHandler(ectx->parser, expat_start, expat_end); 2714 XML_SetCharacterDataHandler(ectx->parser, expat_cdata); 2715 } 2716 2717 /* ### TODO: sline.code < 200 should really be handled by the core */ 2718 if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)) 2719 { 2720 /* By deferring to expect_empty_body(), it will make a choice on 2721 how to handle the body. Whatever the decision, the core handler 2722 will take over, and we will not be called again. */ 2723 return svn_error_trace(svn_ra_serf__expect_empty_body( 2724 request, response, ectx->handler, 2725 scratch_pool)); 2726 } 2727 2728 while (1) 2729 { 2730 apr_status_t status; 2731 const char *data; 2732 apr_size_t len; 2733 int expat_status; 2734 2735 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); 2736 if (SERF_BUCKET_READ_ERROR(status)) 2737 return svn_ra_serf__wrap_err(status, NULL); 2738 2739#if 0 2740 /* ### move restart/skip into the core handler */ 2741 ectx->handler->read_size += len; 2742#endif 2743 2744 /* ### move PAUSED behavior to a new response handler that can feed 2745 ### an inner handler, or can pause for a while. */ 2746 2747 /* ### should we have an IGNORE_ERRORS flag like the v1 parser? */ 2748 2749 expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */); 2750 2751 /* We need to check INNER_ERROR first. This is an error from the 2752 callbacks that has been "dropped off" for us to retrieve. On 2753 current Expat parsers, we stop the parser when an error occurs, 2754 so we want to ignore EXPAT_STATUS (which reports the stoppage). 2755 2756 If an error is not present, THEN we go ahead and look for parsing 2757 errors. */ 2758 if (ectx->inner_error) 2759 { 2760 apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2761 xml_parser_cleanup); 2762 return svn_error_trace(ectx->inner_error); 2763 } 2764 if (expat_status == XML_STATUS_ERROR) 2765 return svn_error_createf(SVN_ERR_XML_MALFORMED, 2766 ectx->inner_error, 2767 _("The %s response contains invalid XML" 2768 " (%d %s)"), 2769 ectx->handler->method, 2770 ectx->handler->sline.code, 2771 ectx->handler->sline.reason); 2772 2773 /* The parsing went fine. What has the bucket told us? */ 2774 2775 if (APR_STATUS_IS_EOF(status)) 2776 { 2777 /* Tell expat we've reached the end of the content. Ignore the 2778 return status. We just don't care. */ 2779 (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */); 2780 2781 svn_ra_serf__xml_context_destroy(ectx->xmlctx); 2782 apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, 2783 xml_parser_cleanup); 2784 2785 /* ### should check XMLCTX to see if it has returned to the 2786 ### INITIAL state. we may have ended early... */ 2787 } 2788 2789 if (status && !SERF_BUCKET_READ_ERROR(status)) 2790 { 2791 return svn_ra_serf__wrap_err(status, NULL); 2792 } 2793 } 2794 2795 /* NOTREACHED */ 2796} 2797 2798 2799svn_ra_serf__handler_t * 2800svn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx, 2801 apr_pool_t *result_pool) 2802{ 2803 svn_ra_serf__handler_t *handler; 2804 struct expat_ctx_t *ectx; 2805 2806 ectx = apr_pcalloc(result_pool, sizeof(*ectx)); 2807 ectx->xmlctx = xmlctx; 2808 ectx->parser = NULL; 2809 ectx->cleanup_pool = result_pool; 2810 2811 2812 handler = apr_pcalloc(result_pool, sizeof(*handler)); 2813 handler->handler_pool = result_pool; 2814 handler->response_handler = expat_response_handler; 2815 handler->response_baton = ectx; 2816 2817 ectx->handler = handler; 2818 2819 return handler; 2820} 2821