util.c revision 299742
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 "svn_hash.h" 36#include "svn_dirent_uri.h" 37#include "svn_path.h" 38#include "svn_private_config.h" 39#include "svn_string.h" 40#include "svn_props.h" 41#include "svn_dirent_uri.h" 42 43#include "../libsvn_ra/ra_loader.h" 44#include "private/svn_dep_compat.h" 45#include "private/svn_fspath.h" 46#include "private/svn_auth_private.h" 47#include "private/svn_cert.h" 48 49#include "ra_serf.h" 50 51static const apr_uint32_t serf_failure_map[][2] = 52{ 53 { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 54 { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 55 { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 56 { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 57}; 58 59/* Return a Subversion failure mask based on FAILURES, a serf SSL 60 failure mask. If anything in FAILURES is not directly mappable to 61 Subversion failures, set SVN_AUTH_SSL_OTHER in the returned mask. */ 62static apr_uint32_t 63ssl_convert_serf_failures(int failures) 64{ 65 apr_uint32_t svn_failures = 0; 66 apr_size_t i; 67 68 for (i = 0; i < sizeof(serf_failure_map) / (2 * sizeof(apr_uint32_t)); ++i) 69 { 70 if (failures & serf_failure_map[i][0]) 71 { 72 svn_failures |= serf_failure_map[i][1]; 73 failures &= ~serf_failure_map[i][0]; 74 } 75 } 76 77 /* Map any remaining failure bits to our OTHER bit. */ 78 if (failures) 79 { 80 svn_failures |= SVN_AUTH_SSL_OTHER; 81 } 82 83 return svn_failures; 84} 85 86 87static apr_status_t 88save_error(svn_ra_serf__session_t *session, 89 svn_error_t *err) 90{ 91 if (err || session->pending_error) 92 { 93 session->pending_error = svn_error_compose_create( 94 session->pending_error, 95 err); 96 return session->pending_error->apr_err; 97 } 98 99 return APR_SUCCESS; 100} 101 102 103/* Construct the realmstring, e.g. https://svn.collab.net:443. */ 104static const char * 105construct_realm(svn_ra_serf__session_t *session, 106 apr_pool_t *pool) 107{ 108 const char *realm; 109 apr_port_t port; 110 111 if (session->session_url.port_str) 112 { 113 port = session->session_url.port; 114 } 115 else 116 { 117 port = apr_uri_port_of_scheme(session->session_url.scheme); 118 } 119 120 realm = apr_psprintf(pool, "%s://%s:%d", 121 session->session_url.scheme, 122 session->session_url.hostname, 123 port); 124 125 return realm; 126} 127 128/* Convert a hash table containing the fields (as documented in X.509) of an 129 organisation to a string ORG, allocated in POOL. ORG is as returned by 130 serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 131static char * 132convert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 133{ 134 const char *cn = svn_hash_gets(org, "CN"); 135 const char *org_unit = svn_hash_gets(org, "OU"); 136 const char *org_name = svn_hash_gets(org, "O"); 137 const char *locality = svn_hash_gets(org, "L"); 138 const char *state = svn_hash_gets(org, "ST"); 139 const char *country = svn_hash_gets(org, "C"); 140 const char *email = svn_hash_gets(org, "E"); 141 svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 142 143 if (cn) 144 { 145 svn_stringbuf_appendcstr(buf, cn); 146 svn_stringbuf_appendcstr(buf, ", "); 147 } 148 149 if (org_unit) 150 { 151 svn_stringbuf_appendcstr(buf, org_unit); 152 svn_stringbuf_appendcstr(buf, ", "); 153 } 154 155 if (org_name) 156 { 157 svn_stringbuf_appendcstr(buf, org_name); 158 svn_stringbuf_appendcstr(buf, ", "); 159 } 160 161 if (locality) 162 { 163 svn_stringbuf_appendcstr(buf, locality); 164 svn_stringbuf_appendcstr(buf, ", "); 165 } 166 167 if (state) 168 { 169 svn_stringbuf_appendcstr(buf, state); 170 svn_stringbuf_appendcstr(buf, ", "); 171 } 172 173 if (country) 174 { 175 svn_stringbuf_appendcstr(buf, country); 176 svn_stringbuf_appendcstr(buf, ", "); 177 } 178 179 /* Chop ', ' if any. */ 180 svn_stringbuf_chop(buf, 2); 181 182 if (email) 183 { 184 svn_stringbuf_appendcstr(buf, "("); 185 svn_stringbuf_appendcstr(buf, email); 186 svn_stringbuf_appendcstr(buf, ")"); 187 } 188 189 return buf->data; 190} 191 192static void append_reason(svn_stringbuf_t *errmsg, const char *reason, int *reasons) 193{ 194 if (*reasons < 1) 195 svn_stringbuf_appendcstr(errmsg, _(": ")); 196 else 197 svn_stringbuf_appendcstr(errmsg, _(", ")); 198 svn_stringbuf_appendcstr(errmsg, reason); 199 (*reasons)++; 200} 201 202/* This function is called on receiving a ssl certificate of a server when 203 opening a https connection. It allows Subversion to override the initial 204 validation done by serf. 205 Serf provides us the @a baton as provided in the call to 206 serf_ssl_server_cert_callback_set. The result of serf's initial validation 207 of the certificate @a CERT is returned as a bitmask in FAILURES. */ 208static svn_error_t * 209ssl_server_cert(void *baton, int failures, 210 const serf_ssl_certificate_t *cert, 211 apr_pool_t *scratch_pool) 212{ 213 svn_ra_serf__connection_t *conn = baton; 214 svn_auth_ssl_server_cert_info_t cert_info; 215 svn_auth_cred_ssl_server_trust_t *server_creds = NULL; 216 svn_auth_iterstate_t *state; 217 const char *realmstring; 218 apr_uint32_t svn_failures; 219 apr_hash_t *issuer; 220 apr_hash_t *subject = NULL; 221 apr_hash_t *serf_cert = NULL; 222 void *creds; 223 224 svn_failures = (ssl_convert_serf_failures(failures) 225 | conn->server_cert_failures); 226 227 if (serf_ssl_cert_depth(cert) == 0) 228 { 229 /* If the depth is 0, the hostname must match the certificate. 230 231 ### This should really be handled by serf, which should pass an error 232 for this case, but that has backwards compatibility issues. */ 233 apr_array_header_t *san; 234 svn_boolean_t found_matching_hostname = FALSE; 235 svn_string_t *actual_hostname = 236 svn_string_create(conn->session->session_url.hostname, scratch_pool); 237 238 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 239 240 san = svn_hash_gets(serf_cert, "subjectAltName"); 241 /* Match server certificate CN with the hostname of the server iff 242 * we didn't find any subjectAltName fields and try to match them. 243 * Per RFC 2818 they are authoritative if present and CommonName 244 * should be ignored. NOTE: This isn't 100% correct since serf 245 * only loads the subjectAltName hash with dNSNames, technically 246 * we should ignore the CommonName if any subjectAltName entry 247 * exists even if it is one we don't support. */ 248 if (san && san->nelts > 0) 249 { 250 int i; 251 for (i = 0; i < san->nelts; i++) 252 { 253 const char *s = APR_ARRAY_IDX(san, i, const char*); 254 svn_string_t *cert_hostname = svn_string_create(s, scratch_pool); 255 256 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 257 { 258 found_matching_hostname = TRUE; 259 break; 260 } 261 } 262 } 263 else 264 { 265 const char *hostname = NULL; 266 267 subject = serf_ssl_cert_subject(cert, scratch_pool); 268 269 if (subject) 270 hostname = svn_hash_gets(subject, "CN"); 271 272 if (hostname) 273 { 274 svn_string_t *cert_hostname = svn_string_create(hostname, 275 scratch_pool); 276 277 if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 278 { 279 found_matching_hostname = TRUE; 280 } 281 } 282 } 283 284 if (!found_matching_hostname) 285 svn_failures |= SVN_AUTH_SSL_CNMISMATCH; 286 } 287 288 if (!svn_failures) 289 return SVN_NO_ERROR; 290 291 /* Extract the info from the certificate */ 292 if (! subject) 293 subject = serf_ssl_cert_subject(cert, scratch_pool); 294 issuer = serf_ssl_cert_issuer(cert, scratch_pool); 295 if (! serf_cert) 296 serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 297 298 cert_info.hostname = svn_hash_gets(subject, "CN"); 299 cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1"); 300 if (! cert_info.fingerprint) 301 cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>"); 302 cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore"); 303 if (! cert_info.valid_from) 304 cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]"); 305 cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter"); 306 if (! cert_info.valid_until) 307 cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]"); 308 cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 309 cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 310 311 /* Handle any non-server certs. */ 312 if (serf_ssl_cert_depth(cert) > 0) 313 { 314 svn_error_t *err; 315 316 svn_auth_set_parameter(conn->session->auth_baton, 317 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 318 &cert_info); 319 320 svn_auth_set_parameter(conn->session->auth_baton, 321 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 322 &svn_failures); 323 324 realmstring = apr_psprintf(scratch_pool, "AUTHORITY:%s", 325 cert_info.fingerprint); 326 327 err = svn_auth_first_credentials(&creds, &state, 328 SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 329 realmstring, 330 conn->session->auth_baton, 331 scratch_pool); 332 333 svn_auth_set_parameter(conn->session->auth_baton, 334 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 335 336 svn_auth_set_parameter(conn->session->auth_baton, 337 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); 338 339 if (err) 340 { 341 if (err->apr_err != SVN_ERR_AUTHN_NO_PROVIDER) 342 return svn_error_trace(err); 343 344 /* No provider registered that handles server authorities */ 345 svn_error_clear(err); 346 creds = NULL; 347 } 348 349 if (creds) 350 { 351 server_creds = creds; 352 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 353 354 svn_failures &= ~server_creds->accepted_failures; 355 } 356 357 if (svn_failures) 358 conn->server_cert_failures |= svn_failures; 359 360 return APR_SUCCESS; 361 } 362 363 svn_auth_set_parameter(conn->session->auth_baton, 364 SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 365 &svn_failures); 366 367 svn_auth_set_parameter(conn->session->auth_baton, 368 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 369 &cert_info); 370 371 realmstring = construct_realm(conn->session, conn->session->pool); 372 373 SVN_ERR(svn_auth_first_credentials(&creds, &state, 374 SVN_AUTH_CRED_SSL_SERVER_TRUST, 375 realmstring, 376 conn->session->auth_baton, 377 scratch_pool)); 378 if (creds) 379 { 380 server_creds = creds; 381 svn_failures &= ~server_creds->accepted_failures; 382 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 383 } 384 385 while (svn_failures && creds) 386 { 387 SVN_ERR(svn_auth_next_credentials(&creds, state, scratch_pool)); 388 389 if (creds) 390 { 391 server_creds = creds; 392 svn_failures &= ~server_creds->accepted_failures; 393 SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 394 } 395 } 396 397 svn_auth_set_parameter(conn->session->auth_baton, 398 SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 399 400 /* Are there non accepted failures left? */ 401 if (svn_failures) 402 { 403 svn_stringbuf_t *errmsg; 404 int reasons = 0; 405 406 errmsg = svn_stringbuf_create( 407 _("Server SSL certificate verification failed"), 408 scratch_pool); 409 410 411 if (svn_failures & SVN_AUTH_SSL_NOTYETVALID) 412 append_reason(errmsg, _("certificate is not yet valid"), &reasons); 413 414 if (svn_failures & SVN_AUTH_SSL_EXPIRED) 415 append_reason(errmsg, _("certificate has expired"), &reasons); 416 417 if (svn_failures & SVN_AUTH_SSL_CNMISMATCH) 418 append_reason(errmsg, 419 _("certificate issued for a different hostname"), 420 &reasons); 421 422 if (svn_failures & SVN_AUTH_SSL_UNKNOWNCA) 423 append_reason(errmsg, _("issuer is not trusted"), &reasons); 424 425 if (svn_failures & SVN_AUTH_SSL_OTHER) 426 append_reason(errmsg, _("and other reason(s)"), &reasons); 427 428 return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, 429 errmsg->data); 430 } 431 432 return SVN_NO_ERROR; 433} 434 435/* Implements serf_ssl_need_server_cert_t for ssl_server_cert */ 436static apr_status_t 437ssl_server_cert_cb(void *baton, int failures, 438 const serf_ssl_certificate_t *cert) 439{ 440 svn_ra_serf__connection_t *conn = baton; 441 svn_ra_serf__session_t *session = conn->session; 442 apr_pool_t *subpool; 443 svn_error_t *err; 444 445 subpool = svn_pool_create(session->pool); 446 err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool)); 447 svn_pool_destroy(subpool); 448 449 return save_error(session, err); 450} 451 452static svn_error_t * 453load_authorities(svn_ra_serf__connection_t *conn, const char *authorities, 454 apr_pool_t *pool) 455{ 456 apr_array_header_t *files = svn_cstring_split(authorities, ";", 457 TRUE /* chop_whitespace */, 458 pool); 459 int i; 460 461 for (i = 0; i < files->nelts; ++i) 462 { 463 const char *file = APR_ARRAY_IDX(files, i, const char *); 464 serf_ssl_certificate_t *ca_cert; 465 apr_status_t status = serf_ssl_load_cert_file(&ca_cert, file, pool); 466 467 if (status == APR_SUCCESS) 468 status = serf_ssl_trust_cert(conn->ssl_context, ca_cert); 469 470 if (status != APR_SUCCESS) 471 { 472 return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 473 _("Invalid config: unable to load certificate file '%s'"), 474 svn_dirent_local_style(file, pool)); 475 } 476 } 477 478 return SVN_NO_ERROR; 479} 480 481static svn_error_t * 482conn_setup(apr_socket_t *sock, 483 serf_bucket_t **read_bkt, 484 serf_bucket_t **write_bkt, 485 void *baton, 486 apr_pool_t *pool) 487{ 488 svn_ra_serf__connection_t *conn = baton; 489 490 *read_bkt = serf_context_bucket_socket_create(conn->session->context, 491 sock, conn->bkt_alloc); 492 493 if (conn->session->using_ssl) 494 { 495 /* input stream */ 496 *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context, 497 conn->bkt_alloc); 498 if (!conn->ssl_context) 499 { 500 conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt); 501 502 serf_ssl_set_hostname(conn->ssl_context, 503 conn->session->session_url.hostname); 504 505 serf_ssl_client_cert_provider_set(conn->ssl_context, 506 svn_ra_serf__handle_client_cert, 507 conn, conn->session->pool); 508 serf_ssl_client_cert_password_set(conn->ssl_context, 509 svn_ra_serf__handle_client_cert_pw, 510 conn, conn->session->pool); 511 serf_ssl_server_cert_callback_set(conn->ssl_context, 512 ssl_server_cert_cb, 513 conn); 514 515 /* See if the user wants us to trust "default" openssl CAs. */ 516 if (conn->session->trust_default_ca) 517 { 518 serf_ssl_use_default_certificates(conn->ssl_context); 519 } 520 /* Are there custom CAs to load? */ 521 if (conn->session->ssl_authorities) 522 { 523 SVN_ERR(load_authorities(conn, conn->session->ssl_authorities, 524 conn->session->pool)); 525 } 526 } 527 528 if (write_bkt) 529 { 530 /* output stream */ 531 *write_bkt = serf_bucket_ssl_encrypt_create(*write_bkt, 532 conn->ssl_context, 533 conn->bkt_alloc); 534 } 535 } 536 537 return SVN_NO_ERROR; 538} 539 540/* svn_ra_serf__conn_setup is a callback for serf. This function 541 creates a read bucket and will wrap the write bucket if SSL 542 is needed. */ 543apr_status_t 544svn_ra_serf__conn_setup(apr_socket_t *sock, 545 serf_bucket_t **read_bkt, 546 serf_bucket_t **write_bkt, 547 void *baton, 548 apr_pool_t *pool) 549{ 550 svn_ra_serf__connection_t *conn = baton; 551 svn_ra_serf__session_t *session = conn->session; 552 svn_error_t *err; 553 554 err = svn_error_trace(conn_setup(sock, 555 read_bkt, 556 write_bkt, 557 baton, 558 pool)); 559 return save_error(session, err); 560} 561 562 563/* Our default serf response acceptor. */ 564static serf_bucket_t * 565accept_response(serf_request_t *request, 566 serf_bucket_t *stream, 567 void *acceptor_baton, 568 apr_pool_t *pool) 569{ 570 /* svn_ra_serf__handler_t *handler = acceptor_baton; */ 571 serf_bucket_t *c; 572 serf_bucket_alloc_t *bkt_alloc; 573 574 bkt_alloc = serf_request_get_alloc(request); 575 c = serf_bucket_barrier_create(stream, bkt_alloc); 576 577 return serf_bucket_response_create(c, bkt_alloc); 578} 579 580 581/* Custom response acceptor for HEAD requests. */ 582static serf_bucket_t * 583accept_head(serf_request_t *request, 584 serf_bucket_t *stream, 585 void *acceptor_baton, 586 apr_pool_t *pool) 587{ 588 /* svn_ra_serf__handler_t *handler = acceptor_baton; */ 589 serf_bucket_t *response; 590 591 response = accept_response(request, stream, acceptor_baton, pool); 592 593 /* We know we shouldn't get a response body. */ 594 serf_bucket_response_set_head(response); 595 596 return response; 597} 598 599static svn_error_t * 600connection_closed(svn_ra_serf__connection_t *conn, 601 apr_status_t why, 602 apr_pool_t *pool) 603{ 604 if (why) 605 { 606 return svn_ra_serf__wrap_err(why, NULL); 607 } 608 609 if (conn->session->using_ssl) 610 conn->ssl_context = NULL; 611 612 return SVN_NO_ERROR; 613} 614 615void 616svn_ra_serf__conn_closed(serf_connection_t *conn, 617 void *closed_baton, 618 apr_status_t why, 619 apr_pool_t *pool) 620{ 621 svn_ra_serf__connection_t *ra_conn = closed_baton; 622 svn_error_t *err; 623 624 err = svn_error_trace(connection_closed(ra_conn, why, pool)); 625 626 (void) save_error(ra_conn->session, err); 627} 628 629 630/* Implementation of svn_ra_serf__handle_client_cert */ 631static svn_error_t * 632handle_client_cert(void *data, 633 const char **cert_path, 634 apr_pool_t *pool) 635{ 636 svn_ra_serf__connection_t *conn = data; 637 svn_ra_serf__session_t *session = conn->session; 638 const char *realm; 639 void *creds; 640 641 *cert_path = NULL; 642 643 realm = construct_realm(session, session->pool); 644 645 if (!conn->ssl_client_auth_state) 646 { 647 SVN_ERR(svn_auth_first_credentials(&creds, 648 &conn->ssl_client_auth_state, 649 SVN_AUTH_CRED_SSL_CLIENT_CERT, 650 realm, 651 session->auth_baton, 652 pool)); 653 } 654 else 655 { 656 SVN_ERR(svn_auth_next_credentials(&creds, 657 conn->ssl_client_auth_state, 658 session->pool)); 659 } 660 661 if (creds) 662 { 663 svn_auth_cred_ssl_client_cert_t *client_creds; 664 client_creds = creds; 665 *cert_path = client_creds->cert_file; 666 } 667 668 return SVN_NO_ERROR; 669} 670 671/* Implements serf_ssl_need_client_cert_t for handle_client_cert */ 672apr_status_t svn_ra_serf__handle_client_cert(void *data, 673 const char **cert_path) 674{ 675 svn_ra_serf__connection_t *conn = data; 676 svn_ra_serf__session_t *session = conn->session; 677 svn_error_t *err; 678 679 err = svn_error_trace(handle_client_cert(data, cert_path, session->pool)); 680 681 return save_error(session, err); 682} 683 684/* Implementation for svn_ra_serf__handle_client_cert_pw */ 685static svn_error_t * 686handle_client_cert_pw(void *data, 687 const char *cert_path, 688 const char **password, 689 apr_pool_t *pool) 690{ 691 svn_ra_serf__connection_t *conn = data; 692 svn_ra_serf__session_t *session = conn->session; 693 void *creds; 694 695 *password = NULL; 696 697 if (!conn->ssl_client_pw_auth_state) 698 { 699 SVN_ERR(svn_auth_first_credentials(&creds, 700 &conn->ssl_client_pw_auth_state, 701 SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 702 cert_path, 703 session->auth_baton, 704 pool)); 705 } 706 else 707 { 708 SVN_ERR(svn_auth_next_credentials(&creds, 709 conn->ssl_client_pw_auth_state, 710 pool)); 711 } 712 713 if (creds) 714 { 715 svn_auth_cred_ssl_client_cert_pw_t *pw_creds; 716 pw_creds = creds; 717 *password = pw_creds->password; 718 } 719 720 return APR_SUCCESS; 721} 722 723/* Implements serf_ssl_need_client_cert_pw_t for handle_client_cert_pw */ 724apr_status_t svn_ra_serf__handle_client_cert_pw(void *data, 725 const char *cert_path, 726 const char **password) 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_pw(data, 733 cert_path, 734 password, 735 session->pool)); 736 737 return save_error(session, err); 738} 739 740 741/* 742 * Given a REQUEST on connection CONN, construct a request bucket for it, 743 * returning the bucket in *REQ_BKT. 744 * 745 * If HDRS_BKT is not-NULL, it will be set to a headers_bucket that 746 * corresponds to the new request. 747 * 748 * The request will be METHOD at URL. 749 * 750 * If BODY_BKT is not-NULL, it will be sent as the request body. 751 * 752 * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 753 * 754 * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers 755 * to request. 756 * 757 * REQUEST_POOL should live for the duration of the request. Serf will 758 * construct this and provide it to the request_setup callback, so we 759 * should just use that one. 760 */ 761static svn_error_t * 762setup_serf_req(serf_request_t *request, 763 serf_bucket_t **req_bkt, 764 serf_bucket_t **hdrs_bkt, 765 svn_ra_serf__session_t *session, 766 const char *method, const char *url, 767 serf_bucket_t *body_bkt, const char *content_type, 768 const char *accept_encoding, 769 svn_boolean_t dav_headers, 770 apr_pool_t *request_pool, 771 apr_pool_t *scratch_pool) 772{ 773 serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 774 775 svn_spillbuf_t *buf; 776 svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 777 778 if (set_CL && body_bkt != NULL) 779 { 780 /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if 781 it speaks HTTP/1.1 (and thus, chunked requests), or because the 782 server actually responded as only supporting HTTP/1.0. 783 784 We'll take the existing body_bkt, spool it into a spillbuf, and 785 then wrap a bucket around that spillbuf. The spillbuf will give 786 us the Content-Length value. */ 787 SVN_ERR(svn_ra_serf__copy_into_spillbuf(&buf, body_bkt, 788 request_pool, 789 scratch_pool)); 790 /* Destroy original bucket since it content is already copied 791 to spillbuf. */ 792 serf_bucket_destroy(body_bkt); 793 794 body_bkt = svn_ra_serf__create_sb_bucket(buf, allocator, 795 request_pool, 796 scratch_pool); 797 } 798 799 /* Create a request bucket. Note that this sucker is kind enough to 800 add a "Host" header for us. */ 801 *req_bkt = serf_request_bucket_request_create(request, method, url, 802 body_bkt, allocator); 803 804 /* Set the Content-Length value. This will also trigger an HTTP/1.0 805 request (rather than the default chunked request). */ 806 if (set_CL) 807 { 808 if (body_bkt == NULL) 809 serf_bucket_request_set_CL(*req_bkt, 0); 810 else 811 serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf)); 812 } 813 814 *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); 815 816 /* We use serf_bucket_headers_setn() because the USERAGENT has a 817 lifetime longer than this bucket. Thus, there is no need to copy 818 the header values. */ 819 serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent); 820 821 if (content_type) 822 { 823 serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type); 824 } 825 826 if (session->http10) 827 { 828 serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 829 } 830 831 if (accept_encoding) 832 { 833 serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 834 } 835 836 /* These headers need to be sent with every request that might need 837 capability processing (e.g. during commit, reports, etc.), see 838 issue #3255 ("mod_dav_svn does not pass client capabilities to 839 start-commit hooks") for why. 840 841 Some request types like GET/HEAD/PROPFIND are unaware of capability 842 handling; and in some cases the responses can even be cached by 843 proxies, so we don't have to send these hearders there. */ 844 if (dav_headers) 845 { 846 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 847 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 848 serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); 849 } 850 851 return SVN_NO_ERROR; 852} 853 854svn_error_t * 855svn_ra_serf__context_run(svn_ra_serf__session_t *sess, 856 apr_interval_time_t *waittime_left, 857 apr_pool_t *scratch_pool) 858{ 859 apr_status_t status; 860 svn_error_t *err; 861 assert(sess->pending_error == SVN_NO_ERROR); 862 863 if (sess->cancel_func) 864 SVN_ERR(sess->cancel_func(sess->cancel_baton)); 865 866 status = serf_context_run(sess->context, 867 SVN_RA_SERF__CONTEXT_RUN_DURATION, 868 scratch_pool); 869 870 err = sess->pending_error; 871 sess->pending_error = SVN_NO_ERROR; 872 873 /* If the context duration timeout is up, we'll subtract that 874 duration from the total time alloted for such things. If 875 there's no time left, we fail with a message indicating that 876 the connection timed out. */ 877 if (APR_STATUS_IS_TIMEUP(status)) 878 { 879 status = 0; 880 881 if (sess->timeout) 882 { 883 if (*waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 884 { 885 *waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 886 } 887 else 888 { 889 return 890 svn_error_compose_create( 891 err, 892 svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 893 _("Connection timed out"))); 894 } 895 } 896 } 897 else 898 { 899 *waittime_left = sess->timeout; 900 } 901 902 SVN_ERR(err); 903 if (status) 904 { 905 /* ### This omits SVN_WARNING, and possibly relies on the fact that 906 ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */ 907 if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 908 { 909 /* apr can't translate subversion errors to text */ 910 SVN_ERR_W(svn_error_create(status, NULL, NULL), 911 _("Error running context")); 912 } 913 914 return svn_ra_serf__wrap_err(status, _("Error running context")); 915 } 916 917 return SVN_NO_ERROR; 918} 919 920svn_error_t * 921svn_ra_serf__context_run_wait(svn_boolean_t *done, 922 svn_ra_serf__session_t *sess, 923 apr_pool_t *scratch_pool) 924{ 925 apr_pool_t *iterpool; 926 apr_interval_time_t waittime_left = sess->timeout; 927 928 assert(sess->pending_error == SVN_NO_ERROR); 929 930 iterpool = svn_pool_create(scratch_pool); 931 while (!*done) 932 { 933 int i; 934 935 svn_pool_clear(iterpool); 936 937 SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool)); 938 939 /* Debugging purposes only! */ 940 for (i = 0; i < sess->num_conns; i++) 941 { 942 serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 943 } 944 } 945 svn_pool_destroy(iterpool); 946 947 return SVN_NO_ERROR; 948} 949 950/* Ensure that a handler is no longer scheduled on the connection. 951 952 Eventually serf will have a reliable way to cancel existing requests, 953 but currently it doesn't even have a way to relyable identify a request 954 after rescheduling, for auth reasons. 955 956 So the only thing we can do today is reset the connection, which 957 will cancel all outstanding requests and prepare the connection 958 for re-use. 959*/ 960static void 961svn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler) 962{ 963 serf_connection_reset(handler->conn->conn); 964 handler->scheduled = FALSE; 965} 966 967svn_error_t * 968svn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 969 apr_pool_t *scratch_pool) 970{ 971 svn_error_t *err; 972 973 /* Create a serf request based on HANDLER. */ 974 svn_ra_serf__request_create(handler); 975 976 /* Wait until the response logic marks its DONE status. */ 977 err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 978 scratch_pool); 979 980 if (handler->scheduled) 981 { 982 /* We reset the connection (breaking pipelining, etc.), as 983 if we didn't the next data would still be handled by this handler, 984 which is done as far as our caller is concerned. */ 985 svn_ra_serf__unschedule_handler(handler); 986 } 987 988 return svn_error_trace(err); 989} 990 991 992 993 994static apr_status_t 995drain_bucket(serf_bucket_t *bucket) 996{ 997 /* Read whatever is in the bucket, and just drop it. */ 998 while (1) 999 { 1000 apr_status_t status; 1001 const char *data; 1002 apr_size_t len; 1003 1004 status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 1005 if (status) 1006 return status; 1007 } 1008} 1009 1010 1011 1012 1013/* Implements svn_ra_serf__response_handler_t */ 1014svn_error_t * 1015svn_ra_serf__handle_discard_body(serf_request_t *request, 1016 serf_bucket_t *response, 1017 void *baton, 1018 apr_pool_t *pool) 1019{ 1020 apr_status_t status; 1021 1022 status = drain_bucket(response); 1023 if (status) 1024 return svn_ra_serf__wrap_err(status, NULL); 1025 1026 return SVN_NO_ERROR; 1027} 1028 1029apr_status_t 1030svn_ra_serf__response_discard_handler(serf_request_t *request, 1031 serf_bucket_t *response, 1032 void *baton, 1033 apr_pool_t *pool) 1034{ 1035 return drain_bucket(response); 1036} 1037 1038 1039/* Return the value of the RESPONSE's Location header if any, or NULL 1040 otherwise. */ 1041static const char * 1042response_get_location(serf_bucket_t *response, 1043 const char *base_url, 1044 apr_pool_t *result_pool, 1045 apr_pool_t *scratch_pool) 1046{ 1047 serf_bucket_t *headers; 1048 const char *location; 1049 1050 headers = serf_bucket_response_get_headers(response); 1051 location = serf_bucket_headers_get(headers, "Location"); 1052 if (location == NULL) 1053 return NULL; 1054 1055 /* The RFCs say we should have received a full url in LOCATION, but 1056 older apache versions and many custom web handlers just return a 1057 relative path here... 1058 1059 And we can't trust anything because it is network data. 1060 */ 1061 if (*location == '/') 1062 { 1063 apr_uri_t uri; 1064 apr_status_t status; 1065 1066 status = apr_uri_parse(scratch_pool, base_url, &uri); 1067 1068 if (status != APR_SUCCESS) 1069 return NULL; 1070 1071 /* Replace the path path with what we got */ 1072 uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool); 1073 1074 /* And make APR produce a proper full url for us */ 1075 location = apr_uri_unparse(scratch_pool, &uri, 0); 1076 1077 /* Fall through to ensure our canonicalization rules */ 1078 } 1079 else if (!svn_path_is_url(location)) 1080 { 1081 return NULL; /* Any other formats we should support? */ 1082 } 1083 1084 return svn_uri_canonicalize(location, result_pool); 1085} 1086 1087 1088/* Implements svn_ra_serf__response_handler_t */ 1089svn_error_t * 1090svn_ra_serf__expect_empty_body(serf_request_t *request, 1091 serf_bucket_t *response, 1092 void *baton, 1093 apr_pool_t *scratch_pool) 1094{ 1095 svn_ra_serf__handler_t *handler = baton; 1096 serf_bucket_t *hdrs; 1097 const char *val; 1098 1099 /* This function is just like handle_multistatus_only() except for the 1100 XML parsing callbacks. We want to look for the -readable element. */ 1101 1102 /* We should see this just once, in order to initialize SERVER_ERROR. 1103 At that point, the core error processing will take over. If we choose 1104 not to parse an error, then we'll never return here (because we 1105 change the response handler). */ 1106 SVN_ERR_ASSERT(handler->server_error == NULL); 1107 1108 hdrs = serf_bucket_response_get_headers(response); 1109 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1110 if (val 1111 && (handler->sline.code < 200 || handler->sline.code >= 300) 1112 && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1113 { 1114 svn_ra_serf__server_error_t *server_err; 1115 1116 SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 1117 FALSE, 1118 handler->handler_pool, 1119 handler->handler_pool)); 1120 1121 handler->server_error = server_err; 1122 } 1123 else 1124 { 1125 /* The body was not text/xml, or we got a success code. 1126 Toss anything that arrives. */ 1127 handler->discard_body = TRUE; 1128 } 1129 1130 /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1131 to call the response handler again. That will start up the XML parsing, 1132 or it will be dropped on the floor (per the decision above). */ 1133 return SVN_NO_ERROR; 1134} 1135 1136 1137apr_status_t 1138svn_ra_serf__credentials_callback(char **username, char **password, 1139 serf_request_t *request, void *baton, 1140 int code, const char *authn_type, 1141 const char *realm, 1142 apr_pool_t *pool) 1143{ 1144 svn_ra_serf__handler_t *handler = baton; 1145 svn_ra_serf__session_t *session = handler->session; 1146 void *creds; 1147 svn_auth_cred_simple_t *simple_creds; 1148 svn_error_t *err; 1149 1150 if (code == 401) 1151 { 1152 /* Use svn_auth_first_credentials if this is the first time we ask for 1153 credentials during this session OR if the last time we asked 1154 session->auth_state wasn't set (eg. if the credentials provider was 1155 cancelled by the user). */ 1156 if (!session->auth_state) 1157 { 1158 err = svn_auth_first_credentials(&creds, 1159 &session->auth_state, 1160 SVN_AUTH_CRED_SIMPLE, 1161 realm, 1162 session->auth_baton, 1163 session->pool); 1164 } 1165 else 1166 { 1167 err = svn_auth_next_credentials(&creds, 1168 session->auth_state, 1169 session->pool); 1170 } 1171 1172 if (err) 1173 { 1174 (void) save_error(session, err); 1175 return err->apr_err; 1176 } 1177 1178 session->auth_attempts++; 1179 1180 if (!creds || session->auth_attempts > 4) 1181 { 1182 /* No more credentials. */ 1183 (void) save_error(session, 1184 svn_error_create( 1185 SVN_ERR_AUTHN_FAILED, NULL, 1186 _("No more credentials or we tried too many " 1187 "times.\nAuthentication failed"))); 1188 return SVN_ERR_AUTHN_FAILED; 1189 } 1190 1191 simple_creds = creds; 1192 *username = apr_pstrdup(pool, simple_creds->username); 1193 *password = apr_pstrdup(pool, simple_creds->password); 1194 } 1195 else 1196 { 1197 *username = apr_pstrdup(pool, session->proxy_username); 1198 *password = apr_pstrdup(pool, session->proxy_password); 1199 1200 session->proxy_auth_attempts++; 1201 1202 if (!session->proxy_username || session->proxy_auth_attempts > 4) 1203 { 1204 /* No more credentials. */ 1205 (void) save_error(session, 1206 svn_error_create( 1207 SVN_ERR_AUTHN_FAILED, NULL, 1208 _("Proxy authentication failed"))); 1209 return SVN_ERR_AUTHN_FAILED; 1210 } 1211 } 1212 1213 handler->conn->last_status_code = code; 1214 1215 return APR_SUCCESS; 1216} 1217 1218/* Wait for HTTP response status and headers, and invoke HANDLER-> 1219 response_handler() to carry out operation-specific processing. 1220 Afterwards, check for connection close. 1221 1222 SERF_STATUS allows returning errors to serf without creating a 1223 subversion error object. 1224 */ 1225static svn_error_t * 1226handle_response(serf_request_t *request, 1227 serf_bucket_t *response, 1228 svn_ra_serf__handler_t *handler, 1229 apr_status_t *serf_status, 1230 apr_pool_t *scratch_pool) 1231{ 1232 apr_status_t status; 1233 svn_error_t *err; 1234 1235 /* ### need to verify whether this already gets init'd on every 1236 ### successful exit. for an error-exit, it will (properly) be 1237 ### ignored by the caller. */ 1238 *serf_status = APR_SUCCESS; 1239 1240 if (!response) 1241 { 1242 /* Uh-oh. Our connection died. */ 1243 handler->scheduled = FALSE; 1244 1245 if (handler->response_error) 1246 { 1247 /* Give a handler chance to prevent request requeue. */ 1248 SVN_ERR(handler->response_error(request, response, 0, 1249 handler->response_error_baton)); 1250 1251 svn_ra_serf__request_create(handler); 1252 } 1253 /* Response error callback is not configured. Requeue another request 1254 for this handler only if we didn't started to process body. 1255 Return error otherwise. */ 1256 else if (!handler->reading_body) 1257 { 1258 svn_ra_serf__request_create(handler); 1259 } 1260 else 1261 { 1262 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1263 _("%s request on '%s' failed"), 1264 handler->method, handler->path); 1265 } 1266 1267 return SVN_NO_ERROR; 1268 } 1269 1270 /* If we're reading the body, then skip all this preparation. */ 1271 if (handler->reading_body) 1272 goto process_body; 1273 1274 /* Copy the Status-Line info into HANDLER, if we don't yet have it. */ 1275 if (handler->sline.version == 0) 1276 { 1277 serf_status_line sl; 1278 1279 status = serf_bucket_response_status(response, &sl); 1280 if (status != APR_SUCCESS) 1281 { 1282 /* The response line is not (yet) ready, or some other error. */ 1283 *serf_status = status; 1284 return SVN_NO_ERROR; /* Handled by serf */ 1285 } 1286 1287 /* If we got APR_SUCCESS, then we should have Status-Line info. */ 1288 SVN_ERR_ASSERT(sl.version != 0); 1289 1290 handler->sline = sl; 1291 handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason); 1292 1293 /* HTTP/1.1? (or later) */ 1294 if (sl.version != SERF_HTTP_10) 1295 handler->session->http10 = FALSE; 1296 } 1297 1298 /* Keep reading from the network until we've read all the headers. */ 1299 status = serf_bucket_response_wait_for_headers(response); 1300 if (status) 1301 { 1302 /* The typical "error" will be APR_EAGAIN, meaning that more input 1303 from the network is required to complete the reading of the 1304 headers. */ 1305 if (!APR_STATUS_IS_EOF(status)) 1306 { 1307 /* Either the headers are not (yet) complete, or there really 1308 was an error. */ 1309 *serf_status = status; 1310 return SVN_NO_ERROR; 1311 } 1312 1313 /* wait_for_headers() will return EOF if there is no body in this 1314 response, or if we completely read the body. The latter is not 1315 true since we would have set READING_BODY to get the body read, 1316 and we would not be back to this code block. 1317 1318 It can also return EOF if we truly hit EOF while (say) processing 1319 the headers. aka Badness. */ 1320 1321 /* Cases where a lack of a response body (via EOF) is okay: 1322 * - A HEAD request 1323 * - 204/304 response 1324 * 1325 * Otherwise, if we get an EOF here, something went really wrong: either 1326 * the server closed on us early or we're reading too much. Either way, 1327 * scream loudly. 1328 */ 1329 if (strcmp(handler->method, "HEAD") != 0 1330 && handler->sline.code != 204 1331 && handler->sline.code != 304) 1332 { 1333 err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, 1334 svn_ra_serf__wrap_err(status, NULL), 1335 _("Premature EOF seen from server" 1336 " (http status=%d)"), 1337 handler->sline.code); 1338 1339 /* In case anything else arrives... discard it. */ 1340 handler->discard_body = TRUE; 1341 1342 return err; 1343 } 1344 } 1345 1346 /* ... and set up the header fields in HANDLER. */ 1347 handler->location = response_get_location(response, 1348 handler->session->session_url_str, 1349 handler->handler_pool, 1350 scratch_pool); 1351 1352 /* On the last request, we failed authentication. We succeeded this time, 1353 so let's save away these credentials. */ 1354 if (handler->conn->last_status_code == 401 && handler->sline.code < 400) 1355 { 1356 SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 1357 handler->session->pool)); 1358 handler->session->auth_attempts = 0; 1359 handler->session->auth_state = NULL; 1360 } 1361 handler->conn->last_status_code = handler->sline.code; 1362 1363 if (handler->sline.code >= 400) 1364 { 1365 /* 405 Method Not allowed. 1366 408 Request Timeout 1367 409 Conflict: can indicate a hook error. 1368 5xx (Internal) Server error. */ 1369 serf_bucket_t *hdrs; 1370 const char *val; 1371 1372 hdrs = serf_bucket_response_get_headers(response); 1373 val = serf_bucket_headers_get(hdrs, "Content-Type"); 1374 if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1375 { 1376 svn_ra_serf__server_error_t *server_err; 1377 1378 SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 1379 FALSE, 1380 handler->handler_pool, 1381 handler->handler_pool)); 1382 1383 handler->server_error = server_err; 1384 } 1385 else 1386 { 1387 handler->discard_body = TRUE; 1388 } 1389 } 1390 else if (handler->sline.code <= 199) 1391 { 1392 handler->discard_body = TRUE; 1393 } 1394 1395 /* Stop processing the above, on every packet arrival. */ 1396 handler->reading_body = TRUE; 1397 1398 process_body: 1399 1400 /* We've been instructed to ignore the body. Drain whatever is present. */ 1401 if (handler->discard_body) 1402 { 1403 *serf_status = drain_bucket(response); 1404 1405 return SVN_NO_ERROR; 1406 } 1407 1408 /* If we are supposed to parse the body as a server_error, then do 1409 that now. */ 1410 if (handler->server_error != NULL) 1411 { 1412 return svn_error_trace( 1413 svn_ra_serf__handle_server_error(handler->server_error, 1414 handler, 1415 request, response, 1416 serf_status, 1417 scratch_pool)); 1418 } 1419 1420 /* Pass the body along to the registered response handler. */ 1421 err = handler->response_handler(request, response, 1422 handler->response_baton, 1423 scratch_pool); 1424 1425 if (err 1426 && (!SERF_BUCKET_READ_ERROR(err->apr_err) 1427 || APR_STATUS_IS_ECONNRESET(err->apr_err) 1428 || APR_STATUS_IS_ECONNABORTED(err->apr_err))) 1429 { 1430 /* These errors are special cased in serf 1431 ### We hope no handler returns these by accident. */ 1432 *serf_status = err->apr_err; 1433 svn_error_clear(err); 1434 return SVN_NO_ERROR; 1435 } 1436 1437 return svn_error_trace(err); 1438} 1439 1440 1441/* Implements serf_response_handler_t for handle_response. Storing 1442 errors in handler->session->pending_error if appropriate. */ 1443static apr_status_t 1444handle_response_cb(serf_request_t *request, 1445 serf_bucket_t *response, 1446 void *baton, 1447 apr_pool_t *response_pool) 1448{ 1449 svn_ra_serf__handler_t *handler = baton; 1450 svn_error_t *err; 1451 apr_status_t inner_status; 1452 apr_status_t outer_status; 1453 apr_pool_t *scratch_pool = response_pool; /* Scratch pool needed? */ 1454 1455 err = svn_error_trace(handle_response(request, response, 1456 handler, &inner_status, 1457 scratch_pool)); 1458 1459 /* Select the right status value to return. */ 1460 outer_status = save_error(handler->session, err); 1461 if (!outer_status) 1462 outer_status = inner_status; 1463 1464 /* Make sure the DONE flag is set properly and requests are cleaned up. */ 1465 if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) 1466 { 1467 svn_ra_serf__session_t *sess = handler->session; 1468 handler->done = TRUE; 1469 handler->scheduled = FALSE; 1470 outer_status = APR_EOF; 1471 1472 /* We use a cached handler->session here to allow handler to free the 1473 memory containing the handler */ 1474 save_error(sess, 1475 handler->done_delegate(request, handler->done_delegate_baton, 1476 scratch_pool)); 1477 } 1478 else if (SERF_BUCKET_READ_ERROR(outer_status) 1479 && handler->session->pending_error) 1480 { 1481 handler->discard_body = TRUE; /* Discard further data */ 1482 handler->done = TRUE; /* Mark as done */ 1483 /* handler->scheduled is still TRUE, as we still expect data. 1484 If we would return an error outer-status the connection 1485 would have to be restarted. With scheduled still TRUE 1486 destroying the handler's pool will still reset the 1487 connection, avoiding the posibility of returning 1488 an error for this handler when a new request is 1489 scheduled. */ 1490 outer_status = APR_EAGAIN; /* Exit context loop */ 1491 } 1492 1493 return outer_status; 1494} 1495 1496/* Perform basic request setup, with special handling for HEAD requests, 1497 and finer-grained callbacks invoked (if non-NULL) to produce the request 1498 headers and body. */ 1499static svn_error_t * 1500setup_request(serf_request_t *request, 1501 svn_ra_serf__handler_t *handler, 1502 serf_bucket_t **req_bkt, 1503 apr_pool_t *request_pool, 1504 apr_pool_t *scratch_pool) 1505{ 1506 serf_bucket_t *body_bkt; 1507 serf_bucket_t *headers_bkt; 1508 const char *accept_encoding; 1509 1510 if (handler->body_delegate) 1511 { 1512 serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 1513 1514 SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, 1515 bkt_alloc, request_pool, scratch_pool)); 1516 } 1517 else 1518 { 1519 body_bkt = NULL; 1520 } 1521 1522 if (handler->custom_accept_encoding) 1523 { 1524 accept_encoding = NULL; 1525 } 1526 else if (handler->session->using_compression) 1527 { 1528 /* Accept gzip compression if enabled. */ 1529 accept_encoding = "gzip"; 1530 } 1531 else 1532 { 1533 accept_encoding = NULL; 1534 } 1535 1536 SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 1537 handler->session, handler->method, handler->path, 1538 body_bkt, handler->body_type, accept_encoding, 1539 !handler->no_dav_headers, request_pool, 1540 scratch_pool)); 1541 1542 if (handler->header_delegate) 1543 { 1544 SVN_ERR(handler->header_delegate(headers_bkt, 1545 handler->header_delegate_baton, 1546 request_pool, scratch_pool)); 1547 } 1548 1549 return SVN_NO_ERROR; 1550} 1551 1552/* Implements the serf_request_setup_t interface (which sets up both a 1553 request and its response handler callback). Handles errors for 1554 setup_request_cb */ 1555static apr_status_t 1556setup_request_cb(serf_request_t *request, 1557 void *setup_baton, 1558 serf_bucket_t **req_bkt, 1559 serf_response_acceptor_t *acceptor, 1560 void **acceptor_baton, 1561 serf_response_handler_t *s_handler, 1562 void **s_handler_baton, 1563 apr_pool_t *request_pool) 1564{ 1565 svn_ra_serf__handler_t *handler = setup_baton; 1566 apr_pool_t *scratch_pool; 1567 svn_error_t *err; 1568 1569 /* Construct a scratch_pool? serf gives us a pool that will live for 1570 the duration of the request. But requests are retried in some cases */ 1571 scratch_pool = svn_pool_create(request_pool); 1572 1573 if (strcmp(handler->method, "HEAD") == 0) 1574 *acceptor = accept_head; 1575 else 1576 *acceptor = accept_response; 1577 *acceptor_baton = handler; 1578 1579 *s_handler = handle_response_cb; 1580 *s_handler_baton = handler; 1581 1582 err = svn_error_trace(setup_request(request, handler, req_bkt, 1583 request_pool, scratch_pool)); 1584 1585 svn_pool_destroy(scratch_pool); 1586 return save_error(handler->session, err); 1587} 1588 1589void 1590svn_ra_serf__request_create(svn_ra_serf__handler_t *handler) 1591{ 1592 SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL 1593 && !handler->scheduled); 1594 1595 /* In case HANDLER is re-queued, reset the various transient fields. */ 1596 handler->done = FALSE; 1597 handler->server_error = NULL; 1598 handler->sline.version = 0; 1599 handler->location = NULL; 1600 handler->reading_body = FALSE; 1601 handler->discard_body = FALSE; 1602 handler->scheduled = TRUE; 1603 1604 /* Keeping track of the returned request object would be nice, but doesn't 1605 work the way we would expect in ra_serf.. 1606 1607 Serf sometimes creates a new request for us (and destroys the old one) 1608 without telling, like when authentication failed (401/407 response. 1609 1610 We 'just' trust serf to do the right thing and expect it to tell us 1611 when the state of the request changes. 1612 1613 ### I fixed a request leak in serf in r2258 on auth failures. 1614 */ 1615 (void) serf_connection_request_create(handler->conn->conn, 1616 setup_request_cb, handler); 1617} 1618 1619 1620svn_error_t * 1621svn_ra_serf__discover_vcc(const char **vcc_url, 1622 svn_ra_serf__session_t *session, 1623 apr_pool_t *scratch_pool) 1624{ 1625 const char *path; 1626 const char *relative_path; 1627 const char *uuid; 1628 1629 /* If we've already got the information our caller seeks, just return it. */ 1630 if (session->vcc_url && session->repos_root_str) 1631 { 1632 *vcc_url = session->vcc_url; 1633 return SVN_NO_ERROR; 1634 } 1635 1636 path = session->session_url.path; 1637 *vcc_url = NULL; 1638 uuid = NULL; 1639 1640 do 1641 { 1642 apr_hash_t *props; 1643 svn_error_t *err; 1644 1645 err = svn_ra_serf__fetch_node_props(&props, session, 1646 path, SVN_INVALID_REVNUM, 1647 base_props, 1648 scratch_pool, scratch_pool); 1649 if (! err) 1650 { 1651 apr_hash_t *ns_props; 1652 1653 ns_props = apr_hash_get(props, "DAV:", 4); 1654 *vcc_url = svn_prop_get_value(ns_props, 1655 "version-controlled-configuration"); 1656 1657 ns_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV); 1658 relative_path = svn_prop_get_value(ns_props, 1659 "baseline-relative-path"); 1660 uuid = svn_prop_get_value(ns_props, "repository-uuid"); 1661 break; 1662 } 1663 else 1664 { 1665 if ((err->apr_err != SVN_ERR_FS_NOT_FOUND) && 1666 (err->apr_err != SVN_ERR_RA_DAV_FORBIDDEN)) 1667 { 1668 return svn_error_trace(err); /* found a _real_ error */ 1669 } 1670 else 1671 { 1672 /* This happens when the file is missing in HEAD. */ 1673 svn_error_clear(err); 1674 1675 /* Okay, strip off a component from PATH. */ 1676 path = svn_urlpath__dirname(path, scratch_pool); 1677 } 1678 } 1679 } 1680 while ((path[0] != '\0') 1681 && (! (path[0] == '/' && path[1] == '\0'))); 1682 1683 if (!*vcc_url) 1684 { 1685 return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL, 1686 _("The PROPFIND response did not include the " 1687 "requested version-controlled-configuration " 1688 "value")); 1689 } 1690 1691 /* Store our VCC in our cache. */ 1692 if (!session->vcc_url) 1693 { 1694 session->vcc_url = apr_pstrdup(session->pool, *vcc_url); 1695 } 1696 1697 /* Update our cached repository root URL. */ 1698 if (!session->repos_root_str) 1699 { 1700 svn_stringbuf_t *url_buf; 1701 1702 url_buf = svn_stringbuf_create(path, scratch_pool); 1703 1704 svn_path_remove_components(url_buf, 1705 svn_path_component_count(relative_path)); 1706 1707 /* Now recreate the root_url. */ 1708 session->repos_root = session->session_url; 1709 session->repos_root.path = 1710 (char *)svn_fspath__canonicalize(url_buf->data, session->pool); 1711 session->repos_root_str = 1712 svn_urlpath__canonicalize(apr_uri_unparse(session->pool, 1713 &session->repos_root, 0), 1714 session->pool); 1715 } 1716 1717 /* Store the repository UUID in the cache. */ 1718 if (!session->uuid) 1719 { 1720 session->uuid = apr_pstrdup(session->pool, uuid); 1721 } 1722 1723 return SVN_NO_ERROR; 1724} 1725 1726svn_error_t * 1727svn_ra_serf__get_relative_path(const char **rel_path, 1728 const char *orig_path, 1729 svn_ra_serf__session_t *session, 1730 apr_pool_t *pool) 1731{ 1732 const char *decoded_root, *decoded_orig; 1733 1734 if (! session->repos_root.path) 1735 { 1736 const char *vcc_url; 1737 1738 /* This should only happen if we haven't detected HTTP v2 1739 support from the server. */ 1740 assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)); 1741 1742 /* We don't actually care about the VCC_URL, but this API 1743 promises to populate the session's root-url cache, and that's 1744 what we really want. */ 1745 SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session, 1746 pool)); 1747 } 1748 1749 decoded_root = svn_path_uri_decode(session->repos_root.path, pool); 1750 decoded_orig = svn_path_uri_decode(orig_path, pool); 1751 *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig); 1752 SVN_ERR_ASSERT(*rel_path != NULL); 1753 return SVN_NO_ERROR; 1754} 1755 1756svn_error_t * 1757svn_ra_serf__report_resource(const char **report_target, 1758 svn_ra_serf__session_t *session, 1759 apr_pool_t *pool) 1760{ 1761 /* If we have HTTP v2 support, we want to report against the 'me' 1762 resource. */ 1763 if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session)) 1764 *report_target = apr_pstrdup(pool, session->me_resource); 1765 1766 /* Otherwise, we'll use the default VCC. */ 1767 else 1768 SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, pool)); 1769 1770 return SVN_NO_ERROR; 1771} 1772 1773svn_error_t * 1774svn_ra_serf__error_on_status(serf_status_line sline, 1775 const char *path, 1776 const char *location) 1777{ 1778 switch(sline.code) 1779 { 1780 case 301: 1781 case 302: 1782 case 303: 1783 case 307: 1784 case 308: 1785 return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL, 1786 (sline.code == 301) 1787 ? _("Repository moved permanently to '%s'") 1788 : _("Repository moved temporarily to '%s'"), 1789 location); 1790 case 403: 1791 return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL, 1792 _("Access to '%s' forbidden"), path); 1793 1794 case 404: 1795 return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL, 1796 _("'%s' path not found"), path); 1797 case 405: 1798 return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, 1799 _("HTTP method is not allowed on '%s'"), 1800 path); 1801 case 409: 1802 return svn_error_createf(SVN_ERR_FS_CONFLICT, NULL, 1803 _("'%s' conflicts"), path); 1804 case 412: 1805 return svn_error_createf(SVN_ERR_RA_DAV_PRECONDITION_FAILED, NULL, 1806 _("Precondition on '%s' failed"), path); 1807 case 423: 1808 return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL, 1809 _("'%s': no lock token available"), path); 1810 1811 case 411: 1812 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1813 _("DAV request failed: 411 Content length required. The " 1814 "server or an intermediate proxy does not accept " 1815 "chunked encoding. Try setting 'http-chunked-requests' " 1816 "to 'auto' or 'no' in your client configuration.")); 1817 case 500: 1818 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1819 _("Unexpected server error %d '%s' on '%s'"), 1820 sline.code, sline.reason, path); 1821 case 501: 1822 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL, 1823 _("The requested feature is not supported by " 1824 "'%s'"), path); 1825 } 1826 1827 if (sline.code >= 300 || sline.code <= 199) 1828 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1829 _("Unexpected HTTP status %d '%s' on '%s'"), 1830 sline.code, sline.reason, path); 1831 1832 return SVN_NO_ERROR; 1833} 1834 1835svn_error_t * 1836svn_ra_serf__unexpected_status(svn_ra_serf__handler_t *handler) 1837{ 1838 /* Is it a standard error status? */ 1839 if (handler->sline.code != 405) 1840 SVN_ERR(svn_ra_serf__error_on_status(handler->sline, 1841 handler->path, 1842 handler->location)); 1843 1844 switch (handler->sline.code) 1845 { 1846 case 201: 1847 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1848 _("Path '%s' unexpectedly created"), 1849 handler->path); 1850 case 204: 1851 return svn_error_createf(SVN_ERR_FS_ALREADY_EXISTS, NULL, 1852 _("Path '%s' already exists"), 1853 handler->path); 1854 1855 case 405: 1856 return svn_error_createf(SVN_ERR_RA_DAV_METHOD_NOT_ALLOWED, NULL, 1857 _("The HTTP method '%s' is not allowed" 1858 " on '%s'"), 1859 handler->method, handler->path); 1860 default: 1861 return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 1862 _("Unexpected HTTP status %d '%s' on '%s' " 1863 "request to '%s'"), 1864 handler->sline.code, handler->sline.reason, 1865 handler->method, handler->path); 1866 } 1867} 1868 1869svn_error_t * 1870svn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session, 1871 svn_delta_shim_callbacks_t *callbacks) 1872{ 1873 svn_ra_serf__session_t *session = ra_session->priv; 1874 1875 session->shim_callbacks = callbacks; 1876 return SVN_NO_ERROR; 1877} 1878 1879/* Shared/standard done_delegate handler */ 1880static svn_error_t * 1881response_done(serf_request_t *request, 1882 void *handler_baton, 1883 apr_pool_t *scratch_pool) 1884{ 1885 svn_ra_serf__handler_t *handler = handler_baton; 1886 1887 assert(handler->done); 1888 1889 if (handler->no_fail_on_http_failure_status) 1890 return SVN_NO_ERROR; 1891 1892 if (handler->server_error) 1893 return svn_ra_serf__server_error_create(handler, scratch_pool); 1894 1895 if (handler->sline.code >= 400 || handler->sline.code <= 199) 1896 { 1897 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); 1898 } 1899 1900 if ((handler->sline.code >= 300 && handler->sline.code < 399) 1901 && !handler->no_fail_on_http_redirect_status) 1902 { 1903 return svn_error_trace(svn_ra_serf__unexpected_status(handler)); 1904 } 1905 1906 return SVN_NO_ERROR; 1907} 1908 1909/* Pool cleanup handler for request handlers. 1910 1911 If a serf context run stops for some outside error, like when the user 1912 cancels a request via ^C in the context loop, the handler is still 1913 registered in the serf context. With the pool cleanup there would be 1914 handlers registered in no freed memory. 1915 1916 This fallback kills the connection for this case, which will make serf 1917 unregister any outstanding requests on it. */ 1918static apr_status_t 1919handler_cleanup(void *baton) 1920{ 1921 svn_ra_serf__handler_t *handler = baton; 1922 if (handler->scheduled) 1923 { 1924 svn_ra_serf__unschedule_handler(handler); 1925 } 1926 1927 return APR_SUCCESS; 1928} 1929 1930svn_ra_serf__handler_t * 1931svn_ra_serf__create_handler(svn_ra_serf__session_t *session, 1932 apr_pool_t *result_pool) 1933{ 1934 svn_ra_serf__handler_t *handler; 1935 1936 handler = apr_pcalloc(result_pool, sizeof(*handler)); 1937 handler->handler_pool = result_pool; 1938 1939 apr_pool_cleanup_register(result_pool, handler, handler_cleanup, 1940 apr_pool_cleanup_null); 1941 1942 handler->session = session; 1943 handler->conn = session->conns[0]; 1944 1945 /* Setup the default done handler, to handle server errors */ 1946 handler->done_delegate_baton = handler; 1947 handler->done_delegate = response_done; 1948 1949 return handler; 1950} 1951 1952svn_error_t * 1953svn_ra_serf__uri_parse(apr_uri_t *uri, 1954 const char *url_str, 1955 apr_pool_t *result_pool) 1956{ 1957 apr_status_t status; 1958 1959 status = apr_uri_parse(result_pool, url_str, uri); 1960 if (status) 1961 { 1962 /* Do not use returned error status in error message because currently 1963 apr_uri_parse() returns APR_EGENERAL for all parsing errors. */ 1964 return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 1965 _("Illegal URL '%s'"), 1966 url_str); 1967 } 1968 1969 /* Depending the version of apr-util in use, for root paths uri.path 1970 will be NULL or "", where serf requires "/". */ 1971 if (uri->path == NULL || uri->path[0] == '\0') 1972 { 1973 uri->path = apr_pstrdup(result_pool, "/"); 1974 } 1975 1976 return SVN_NO_ERROR; 1977} 1978