155682Smarkm/* 2233294Sstas * util.c : serf utility routines for ra_serf 3233294Sstas * 4233294Sstas * ==================================================================== 555682Smarkm * Licensed to the Apache Software Foundation (ASF) under one 6233294Sstas * or more contributor license agreements. See the NOTICE file 7233294Sstas * distributed with this work for additional information 8233294Sstas * regarding copyright ownership. The ASF licenses this file 955682Smarkm * to you under the Apache License, Version 2.0 (the 10233294Sstas * "License"); you may not use this file except in compliance 11233294Sstas * with the License. You may obtain a copy of the License at 1255682Smarkm * 13233294Sstas * http://www.apache.org/licenses/LICENSE-2.0 14233294Sstas * 15233294Sstas * Unless required by applicable law or agreed to in writing, 1655682Smarkm * software distributed under the License is distributed on an 17233294Sstas * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18233294Sstas * KIND, either express or implied. See the License for the 19233294Sstas * specific language governing permissions and limitations 2055682Smarkm * under the License. 21233294Sstas * ==================================================================== 22233294Sstas */ 23233294Sstas 24233294Sstas 25233294Sstas 26233294Sstas#include <assert.h> 27233294Sstas 28233294Sstas#define APR_WANT_STRFUNC 29233294Sstas#include <apr.h> 30233294Sstas#include <apr_want.h> 31233294Sstas 3255682Smarkm#include <serf.h> 3355682Smarkm#include <serf_bucket_types.h> 3455682Smarkm 3555682Smarkm#include "svn_hash.h" 3655682Smarkm#include "svn_dirent_uri.h" 3755682Smarkm#include "svn_path.h" 3855682Smarkm#include "svn_private_config.h" 3955682Smarkm#include "svn_string.h" 4055682Smarkm#include "svn_props.h" 4155682Smarkm#include "svn_dirent_uri.h" 4255682Smarkm 43102644Snectar#include "../libsvn_ra/ra_loader.h" 44102644Snectar#include "private/svn_dep_compat.h" 4555682Smarkm#include "private/svn_fspath.h" 4655682Smarkm#include "private/svn_auth_private.h" 47233294Sstas#include "private/svn_cert.h" 48102644Snectar 4955682Smarkm#include "ra_serf.h" 5090926Snectar 5190926Snectarstatic const apr_uint32_t serf_failure_map[][2] = 5290926Snectar{ 5390926Snectar { SERF_SSL_CERT_NOTYETVALID, SVN_AUTH_SSL_NOTYETVALID }, 54233294Sstas { SERF_SSL_CERT_EXPIRED, SVN_AUTH_SSL_EXPIRED }, 55233294Sstas { SERF_SSL_CERT_SELF_SIGNED, SVN_AUTH_SSL_UNKNOWNCA }, 5655682Smarkm { SERF_SSL_CERT_UNKNOWNCA, SVN_AUTH_SSL_UNKNOWNCA } 5755682Smarkm}; 5855682Smarkm 5955682Smarkm/* Return a Subversion failure mask based on FAILURES, a serf SSL 6055682Smarkm failure mask. If anything in FAILURES is not directly mappable to 6155682Smarkm Subversion failures, set SVN_AUTH_SSL_OTHER in the returned mask. */ 6255682Smarkmstatic apr_uint32_t 6355682Smarkmssl_convert_serf_failures(int failures) 6455682Smarkm{ 65178825Sdfr apr_uint32_t svn_failures = 0; 6655682Smarkm apr_size_t i; 6755682Smarkm 6855682Smarkm for (i = 0; i < sizeof(serf_failure_map) / (2 * sizeof(apr_uint32_t)); ++i) 69178825Sdfr { 7055682Smarkm if (failures & serf_failure_map[i][0]) 7155682Smarkm { 7255682Smarkm svn_failures |= serf_failure_map[i][1]; 7355682Smarkm failures &= ~serf_failure_map[i][0]; 7455682Smarkm } 7555682Smarkm } 76178825Sdfr 7755682Smarkm /* Map any remaining failure bits to our OTHER bit. */ 78178825Sdfr if (failures) 7955682Smarkm { 8055682Smarkm svn_failures |= SVN_AUTH_SSL_OTHER; 8155682Smarkm } 8255682Smarkm 8355682Smarkm return svn_failures; 8455682Smarkm} 85102644Snectar 8655682Smarkm 8755682Smarkmstatic apr_status_t 88102644Snectarsave_error(svn_ra_serf__session_t *session, 8955682Smarkm svn_error_t *err) 90102644Snectar{ 91102644Snectar if (err || session->pending_error) 92102644Snectar { 93102644Snectar session->pending_error = svn_error_compose_create( 94102644Snectar session->pending_error, 95102644Snectar err); 9655682Smarkm return session->pending_error->apr_err; 9755682Smarkm } 9855682Smarkm 9955682Smarkm return APR_SUCCESS; 100102644Snectar} 101102644Snectar 102102644Snectar 10355682Smarkm/* Construct the realmstring, e.g. https://svn.collab.net:443. */ 104102644Snectarstatic const char * 10555682Smarkmconstruct_realm(svn_ra_serf__session_t *session, 106102644Snectar apr_pool_t *pool) 107102644Snectar{ 108102644Snectar const char *realm; 109102644Snectar apr_port_t port; 110102644Snectar 111102644Snectar if (session->session_url.port_str) 11255682Smarkm { 11355682Smarkm port = session->session_url.port; 11455682Smarkm } 11555682Smarkm else 11655682Smarkm { 11755682Smarkm port = apr_uri_port_of_scheme(session->session_url.scheme); 11855682Smarkm } 11955682Smarkm 12055682Smarkm realm = apr_psprintf(pool, "%s://%s:%d", 12155682Smarkm session->session_url.scheme, 12255682Smarkm session->session_url.hostname, 12355682Smarkm port); 12455682Smarkm 12555682Smarkm return realm; 12655682Smarkm} 12755682Smarkm 12855682Smarkm/* Convert a hash table containing the fields (as documented in X.509) of an 12955682Smarkm organisation to a string ORG, allocated in POOL. ORG is as returned by 13055682Smarkm serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */ 13155682Smarkmstatic char * 132178825Sdfrconvert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool) 13355682Smarkm{ 134178825Sdfr const char *cn = svn_hash_gets(org, "CN"); 13555682Smarkm const char *org_unit = svn_hash_gets(org, "OU"); 13655682Smarkm const char *org_name = svn_hash_gets(org, "O"); 13755682Smarkm const char *locality = svn_hash_gets(org, "L"); 13855682Smarkm const char *state = svn_hash_gets(org, "ST"); 13955682Smarkm const char *country = svn_hash_gets(org, "C"); 140233294Sstas const char *email = svn_hash_gets(org, "E"); 141233294Sstas svn_stringbuf_t *buf = svn_stringbuf_create_empty(pool); 142233294Sstas 143233294Sstas if (cn) 144233294Sstas { 145233294Sstas svn_stringbuf_appendcstr(buf, cn); 146233294Sstas svn_stringbuf_appendcstr(buf, ", "); 147233294Sstas } 148233294Sstas 149233294Sstas if (org_unit) 150233294Sstas { 15155682Smarkm svn_stringbuf_appendcstr(buf, org_unit); 152102644Snectar svn_stringbuf_appendcstr(buf, ", "); 15355682Smarkm } 154102644Snectar 15555682Smarkm if (org_name) 156102644Snectar { 157102644Snectar svn_stringbuf_appendcstr(buf, org_name); 158102644Snectar svn_stringbuf_appendcstr(buf, ", "); 159102644Snectar } 160102644Snectar 161102644Snectar if (locality) 16255682Smarkm { 16355682Smarkm svn_stringbuf_appendcstr(buf, locality); 16455682Smarkm svn_stringbuf_appendcstr(buf, ", "); 16555682Smarkm } 16655682Smarkm 16755682Smarkm if (state) 16855682Smarkm { 16955682Smarkm svn_stringbuf_appendcstr(buf, state); 17055682Smarkm svn_stringbuf_appendcstr(buf, ", "); 17155682Smarkm } 17255682Smarkm 17355682Smarkm if (country) 17455682Smarkm { 17590926Snectar svn_stringbuf_appendcstr(buf, country); 17655682Smarkm svn_stringbuf_appendcstr(buf, ", "); 17755682Smarkm } 17855682Smarkm 17955682Smarkm /* Chop ', ' if any. */ 18055682Smarkm svn_stringbuf_chop(buf, 2); 18155682Smarkm 18255682Smarkm if (email) 18355682Smarkm { 18455682Smarkm svn_stringbuf_appendcstr(buf, "("); 18555682Smarkm svn_stringbuf_appendcstr(buf, email); 18655682Smarkm svn_stringbuf_appendcstr(buf, ")"); 18755682Smarkm } 18855682Smarkm 18955682Smarkm return buf->data; 19055682Smarkm} 19155682Smarkm 19255682Smarkmstatic void append_reason(svn_stringbuf_t *errmsg, const char *reason, int *reasons) 19355682Smarkm{ 19455682Smarkm if (*reasons < 1) 19555682Smarkm svn_stringbuf_appendcstr(errmsg, _(": ")); 19655682Smarkm else 19755682Smarkm svn_stringbuf_appendcstr(errmsg, _(", ")); 19855682Smarkm svn_stringbuf_appendcstr(errmsg, reason); 199178825Sdfr (*reasons)++; 200178825Sdfr} 201178825Sdfr 202178825Sdfr/* This function is called on receiving a ssl certificate of a server when 203178825Sdfr opening a https connection. It allows Subversion to override the initial 204178825Sdfr validation done by serf. 205178825Sdfr Serf provides us the @a baton as provided in the call to 206178825Sdfr serf_ssl_server_cert_callback_set. The result of serf's initial validation 207233294Sstas of the certificate @a CERT is returned as a bitmask in FAILURES. */ 208233294Sstasstatic svn_error_t * 209178825Sdfrssl_server_cert(void *baton, int failures, 210178825Sdfr const serf_ssl_certificate_t *cert, 211178825Sdfr apr_pool_t *scratch_pool) 212178825Sdfr{ 213178825Sdfr svn_ra_serf__connection_t *conn = baton; 214178825Sdfr svn_auth_ssl_server_cert_info_t cert_info; 215178825Sdfr svn_auth_cred_ssl_server_trust_t *server_creds = NULL; 216178825Sdfr svn_auth_iterstate_t *state; 217178825Sdfr const char *realmstring; 218178825Sdfr apr_uint32_t svn_failures; 219178825Sdfr apr_hash_t *issuer; 220178825Sdfr apr_hash_t *subject = NULL; 221178825Sdfr apr_hash_t *serf_cert = NULL; 222178825Sdfr void *creds; 223178825Sdfr 224178825Sdfr svn_failures = (ssl_convert_serf_failures(failures) 225178825Sdfr | conn->server_cert_failures); 226178825Sdfr 227178825Sdfr if (serf_ssl_cert_depth(cert) == 0) 228178825Sdfr { 229178825Sdfr /* If the depth is 0, the hostname must match the certificate. 230178825Sdfr 231178825Sdfr ### This should really be handled by serf, which should pass an error 232178825Sdfr for this case, but that has backwards compatibility issues. */ 233178825Sdfr apr_array_header_t *san; 23455682Smarkm svn_boolean_t found_matching_hostname = FALSE; 23555682Smarkm svn_string_t *actual_hostname = 23655682Smarkm svn_string_create(conn->session->session_url.hostname, scratch_pool); 23755682Smarkm 23855682Smarkm serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 23955682Smarkm 24055682Smarkm san = svn_hash_gets(serf_cert, "subjectAltName"); 24155682Smarkm /* Match server certificate CN with the hostname of the server iff 24255682Smarkm * we didn't find any subjectAltName fields and try to match them. 24355682Smarkm * Per RFC 2818 they are authoritative if present and CommonName 24455682Smarkm * should be ignored. NOTE: This isn't 100% correct since serf 24555682Smarkm * only loads the subjectAltName hash with dNSNames, technically 24655682Smarkm * we should ignore the CommonName if any subjectAltName entry 24755682Smarkm * exists even if it is one we don't support. */ 24855682Smarkm if (san && san->nelts > 0) 24955682Smarkm { 25055682Smarkm int i; 25155682Smarkm for (i = 0; i < san->nelts; i++) 25255682Smarkm { 25355682Smarkm const char *s = APR_ARRAY_IDX(san, i, const char*); 25455682Smarkm svn_string_t *cert_hostname = svn_string_create(s, scratch_pool); 25555682Smarkm 25655682Smarkm if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 25755682Smarkm { 25855682Smarkm found_matching_hostname = TRUE; 25955682Smarkm break; 26055682Smarkm } 26155682Smarkm } 26255682Smarkm } 26355682Smarkm else 26455682Smarkm { 26555682Smarkm const char *hostname = NULL; 26655682Smarkm 26755682Smarkm subject = serf_ssl_cert_subject(cert, scratch_pool); 26855682Smarkm 26955682Smarkm if (subject) 27055682Smarkm hostname = svn_hash_gets(subject, "CN"); 27155682Smarkm 27255682Smarkm if (hostname) 27355682Smarkm { 27455682Smarkm svn_string_t *cert_hostname = svn_string_create(hostname, 27555682Smarkm scratch_pool); 27655682Smarkm 27755682Smarkm if (svn_cert__match_dns_identity(cert_hostname, actual_hostname)) 278102644Snectar { 27955682Smarkm found_matching_hostname = TRUE; 28055682Smarkm } 281102644Snectar } 28255682Smarkm } 283102644Snectar 284102644Snectar if (!found_matching_hostname) 285102644Snectar svn_failures |= SVN_AUTH_SSL_CNMISMATCH; 286102644Snectar } 287102644Snectar 288102644Snectar if (!svn_failures) 28955682Smarkm return SVN_NO_ERROR; 29055682Smarkm 29155682Smarkm /* Extract the info from the certificate */ 29255682Smarkm if (! subject) 29355682Smarkm subject = serf_ssl_cert_subject(cert, scratch_pool); 294102644Snectar issuer = serf_ssl_cert_issuer(cert, scratch_pool); 29555682Smarkm if (! serf_cert) 29655682Smarkm serf_cert = serf_ssl_cert_certificate(cert, scratch_pool); 297102644Snectar 29855682Smarkm cert_info.hostname = svn_hash_gets(subject, "CN"); 299102644Snectar cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1"); 300102644Snectar if (! cert_info.fingerprint) 301102644Snectar cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>"); 302102644Snectar cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore"); 303102644Snectar if (! cert_info.valid_from) 304102644Snectar cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]"); 30555682Smarkm cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter"); 30655682Smarkm if (! cert_info.valid_until) 30755682Smarkm cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]"); 30855682Smarkm cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool); 30955682Smarkm cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool); 31055682Smarkm 31155682Smarkm /* Handle any non-server certs. */ 31255682Smarkm if (serf_ssl_cert_depth(cert) > 0) 31355682Smarkm { 31455682Smarkm svn_error_t *err; 31555682Smarkm 316233294Sstas svn_auth_set_parameter(conn->session->auth_baton, 31755682Smarkm SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 31855682Smarkm &cert_info); 31955682Smarkm 32055682Smarkm svn_auth_set_parameter(conn->session->auth_baton, 32155682Smarkm SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 32255682Smarkm &svn_failures); 32355682Smarkm 324233294Sstas realmstring = apr_psprintf(scratch_pool, "AUTHORITY:%s", 325233294Sstas cert_info.fingerprint); 32655682Smarkm 32755682Smarkm err = svn_auth_first_credentials(&creds, &state, 32855682Smarkm SVN_AUTH_CRED_SSL_SERVER_AUTHORITY, 329233294Sstas realmstring, 330233294Sstas conn->session->auth_baton, 331233294Sstas scratch_pool); 332233294Sstas 333233294Sstas svn_auth_set_parameter(conn->session->auth_baton, 334233294Sstas SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 335233294Sstas 336233294Sstas svn_auth_set_parameter(conn->session->auth_baton, 337233294Sstas SVN_AUTH_PARAM_SSL_SERVER_FAILURES, NULL); 33855682Smarkm 339102644Snectar if (err) 34055682Smarkm { 341102644Snectar if (err->apr_err != SVN_ERR_AUTHN_NO_PROVIDER) 34255682Smarkm return svn_error_trace(err); 343102644Snectar 344102644Snectar /* No provider registered that handles server authorities */ 345102644Snectar svn_error_clear(err); 346102644Snectar creds = NULL; 347102644Snectar } 34855682Smarkm 34955682Smarkm if (creds) 35055682Smarkm { 35155682Smarkm server_creds = creds; 35255682Smarkm SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 35355682Smarkm 35455682Smarkm svn_failures &= ~server_creds->accepted_failures; 35555682Smarkm } 35655682Smarkm 357233294Sstas if (svn_failures) 35855682Smarkm conn->server_cert_failures |= svn_failures; 35955682Smarkm 36055682Smarkm return APR_SUCCESS; 36155682Smarkm } 36255682Smarkm 36355682Smarkm svn_auth_set_parameter(conn->session->auth_baton, 36455682Smarkm SVN_AUTH_PARAM_SSL_SERVER_FAILURES, 36555682Smarkm &svn_failures); 36655682Smarkm 36755682Smarkm svn_auth_set_parameter(conn->session->auth_baton, 36855682Smarkm SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, 36955682Smarkm &cert_info); 37055682Smarkm 37190926Snectar realmstring = construct_realm(conn->session, conn->session->pool); 37255682Smarkm 37355682Smarkm SVN_ERR(svn_auth_first_credentials(&creds, &state, 37455682Smarkm SVN_AUTH_CRED_SSL_SERVER_TRUST, 37590926Snectar realmstring, 37655682Smarkm conn->session->auth_baton, 37790926Snectar scratch_pool)); 37890926Snectar if (creds) 37990926Snectar { 38090926Snectar server_creds = creds; 38190926Snectar svn_failures &= ~server_creds->accepted_failures; 38290926Snectar SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 38390926Snectar } 38490926Snectar 38590926Snectar while (svn_failures && creds) 38655682Smarkm { 38755682Smarkm SVN_ERR(svn_auth_next_credentials(&creds, state, scratch_pool)); 38855682Smarkm 38955682Smarkm if (creds) 39055682Smarkm { 39155682Smarkm server_creds = creds; 39255682Smarkm svn_failures &= ~server_creds->accepted_failures; 39355682Smarkm SVN_ERR(svn_auth_save_credentials(state, scratch_pool)); 39455682Smarkm } 39555682Smarkm } 39655682Smarkm 39755682Smarkm svn_auth_set_parameter(conn->session->auth_baton, 398178825Sdfr SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL); 399178825Sdfr 400178825Sdfr /* Are there non accepted failures left? */ 401178825Sdfr if (svn_failures) 402178825Sdfr { 403178825Sdfr svn_stringbuf_t *errmsg; 404178825Sdfr int reasons = 0; 405178825Sdfr 406178825Sdfr errmsg = svn_stringbuf_create( 407233294Sstas _("Server SSL certificate verification failed"), 408233294Sstas scratch_pool); 409178825Sdfr 410178825Sdfr 411178825Sdfr if (svn_failures & SVN_AUTH_SSL_NOTYETVALID) 412178825Sdfr append_reason(errmsg, _("certificate is not yet valid"), &reasons); 413233294Sstas 414233294Sstas if (svn_failures & SVN_AUTH_SSL_EXPIRED) 415178825Sdfr append_reason(errmsg, _("certificate has expired"), &reasons); 416178825Sdfr 417178825Sdfr if (svn_failures & SVN_AUTH_SSL_CNMISMATCH) 418178825Sdfr append_reason(errmsg, 419178825Sdfr _("certificate issued for a different hostname"), 420178825Sdfr &reasons); 421178825Sdfr 422178825Sdfr if (svn_failures & SVN_AUTH_SSL_UNKNOWNCA) 423178825Sdfr append_reason(errmsg, _("issuer is not trusted"), &reasons); 424233294Sstas 425178825Sdfr if (svn_failures & SVN_AUTH_SSL_OTHER) 426178825Sdfr append_reason(errmsg, _("and other reason(s)"), &reasons); 427178825Sdfr 428178825Sdfr return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, 429178825Sdfr errmsg->data); 430178825Sdfr } 431178825Sdfr 432178825Sdfr return SVN_NO_ERROR; 433178825Sdfr} 434178825Sdfr 435178825Sdfr/* Implements serf_ssl_need_server_cert_t for ssl_server_cert */ 436178825Sdfrstatic apr_status_t 437178825Sdfrssl_server_cert_cb(void *baton, int failures, 438178825Sdfr const serf_ssl_certificate_t *cert) 439178825Sdfr{ 440178825Sdfr svn_ra_serf__connection_t *conn = baton; 441178825Sdfr svn_ra_serf__session_t *session = conn->session; 442178825Sdfr apr_pool_t *subpool; 443178825Sdfr svn_error_t *err; 444178825Sdfr 445178825Sdfr subpool = svn_pool_create(session->pool); 446178825Sdfr err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool)); 447178825Sdfr svn_pool_destroy(subpool); 448178825Sdfr 44955682Smarkm return save_error(session, err); 45055682Smarkm} 451233294Sstas 452233294Sstasstatic svn_error_t * 45355682Smarkmload_authorities(svn_ra_serf__connection_t *conn, const char *authorities, 45455682Smarkm apr_pool_t *pool) 45555682Smarkm{ 45655682Smarkm apr_array_header_t *files = svn_cstring_split(authorities, ";", 45790926Snectar TRUE /* chop_whitespace */, 45890926Snectar pool); 45990926Snectar int i; 46090926Snectar 46190926Snectar for (i = 0; i < files->nelts; ++i) 46290926Snectar { 46390926Snectar const char *file = APR_ARRAY_IDX(files, i, const char *); 46490926Snectar serf_ssl_certificate_t *ca_cert; 465233294Sstas apr_status_t status = serf_ssl_load_cert_file(&ca_cert, file, pool); 466102644Snectar 46790926Snectar if (status == APR_SUCCESS) 468178825Sdfr status = serf_ssl_trust_cert(conn->ssl_context, ca_cert); 469178825Sdfr 47090926Snectar if (status != APR_SUCCESS) 47190926Snectar { 472233294Sstas return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL, 47390926Snectar _("Invalid config: unable to load certificate file '%s'"), 47490926Snectar svn_dirent_local_style(file, pool)); 475233294Sstas } 47690926Snectar } 47790926Snectar 478178825Sdfr return SVN_NO_ERROR; 479178825Sdfr} 480178825Sdfr 481178825Sdfrstatic svn_error_t * 482178825Sdfrconn_setup(apr_socket_t *sock, 48390926Snectar serf_bucket_t **read_bkt, 484178825Sdfr serf_bucket_t **write_bkt, 485178825Sdfr void *baton, 486178825Sdfr apr_pool_t *pool) 487178825Sdfr{ 488178825Sdfr svn_ra_serf__connection_t *conn = baton; 489178825Sdfr 490178825Sdfr *read_bkt = serf_context_bucket_socket_create(conn->session->context, 491178825Sdfr sock, conn->bkt_alloc); 492178825Sdfr 493178825Sdfr if (conn->session->using_ssl) 494233294Sstas { 495178825Sdfr /* input stream */ 49690926Snectar *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context, 497178825Sdfr conn->bkt_alloc); 498178825Sdfr if (!conn->ssl_context) 499178825Sdfr { 500178825Sdfr conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt); 501178825Sdfr 502178825Sdfr serf_ssl_set_hostname(conn->ssl_context, 503178825Sdfr conn->session->session_url.hostname); 504178825Sdfr 505178825Sdfr serf_ssl_client_cert_provider_set(conn->ssl_context, 506178825Sdfr svn_ra_serf__handle_client_cert, 507178825Sdfr conn, conn->session->pool); 508178825Sdfr serf_ssl_client_cert_password_set(conn->ssl_context, 509178825Sdfr svn_ra_serf__handle_client_cert_pw, 510178825Sdfr conn, conn->session->pool); 511233294Sstas serf_ssl_server_cert_callback_set(conn->ssl_context, 512178825Sdfr ssl_server_cert_cb, 513178825Sdfr conn); 514178825Sdfr 515178825Sdfr /* See if the user wants us to trust "default" openssl CAs. */ 516178825Sdfr if (conn->session->trust_default_ca) 517178825Sdfr { 518178825Sdfr serf_ssl_use_default_certificates(conn->ssl_context); 519178825Sdfr } 520233294Sstas /* Are there custom CAs to load? */ 521178825Sdfr if (conn->session->ssl_authorities) 522178825Sdfr { 523178825Sdfr SVN_ERR(load_authorities(conn, conn->session->ssl_authorities, 524178825Sdfr conn->session->pool)); 525178825Sdfr } 526178825Sdfr } 527233294Sstas 528178825Sdfr if (write_bkt) 529178825Sdfr { 530178825Sdfr /* output stream */ 531178825Sdfr *write_bkt = serf_bucket_ssl_encrypt_create(*write_bkt, 532178825Sdfr conn->ssl_context, 533178825Sdfr conn->bkt_alloc); 534178825Sdfr } 535178825Sdfr } 536178825Sdfr 537178825Sdfr return SVN_NO_ERROR; 538178825Sdfr} 539178825Sdfr 54090926Snectar/* svn_ra_serf__conn_setup is a callback for serf. This function 54190926Snectar creates a read bucket and will wrap the write bucket if SSL 542178825Sdfr is needed. */ 543178825Sdfrapr_status_t 54490926Snectarsvn_ra_serf__conn_setup(apr_socket_t *sock, 54590926Snectar serf_bucket_t **read_bkt, 54690926Snectar serf_bucket_t **write_bkt, 54790926Snectar void *baton, 54890926Snectar apr_pool_t *pool) 54990926Snectar{ 550178825Sdfr svn_ra_serf__connection_t *conn = baton; 551178825Sdfr svn_ra_serf__session_t *session = conn->session; 552178825Sdfr svn_error_t *err; 55390926Snectar 554178825Sdfr err = svn_error_trace(conn_setup(sock, 555178825Sdfr read_bkt, 55690926Snectar write_bkt, 55790926Snectar baton, 55890926Snectar pool)); 55990926Snectar return save_error(session, err); 56090926Snectar} 56190926Snectar 56290926Snectar 56390926Snectar/* Our default serf response acceptor. */ 56490926Snectarstatic serf_bucket_t * 56590926Snectaraccept_response(serf_request_t *request, 56690926Snectar serf_bucket_t *stream, 567178825Sdfr void *acceptor_baton, 56890926Snectar apr_pool_t *pool) 56990926Snectar{ 57090926Snectar /* svn_ra_serf__handler_t *handler = acceptor_baton; */ 57190926Snectar serf_bucket_t *c; 57290926Snectar serf_bucket_alloc_t *bkt_alloc; 573233294Sstas 57490926Snectar bkt_alloc = serf_request_get_alloc(request); 57590926Snectar c = serf_bucket_barrier_create(stream, bkt_alloc); 57690926Snectar 57790926Snectar return serf_bucket_response_create(c, bkt_alloc); 57890926Snectar} 57990926Snectar 58090926Snectar 58190926Snectar/* Custom response acceptor for HEAD requests. */ 58290926Snectarstatic serf_bucket_t * 58390926Snectaraccept_head(serf_request_t *request, 58490926Snectar serf_bucket_t *stream, 58590926Snectar void *acceptor_baton, 58690926Snectar apr_pool_t *pool) 58790926Snectar{ 58890926Snectar /* svn_ra_serf__handler_t *handler = acceptor_baton; */ 58990926Snectar serf_bucket_t *response; 59090926Snectar 59190926Snectar response = accept_response(request, stream, acceptor_baton, pool); 59290926Snectar 59390926Snectar /* We know we shouldn't get a response body. */ 59490926Snectar serf_bucket_response_set_head(response); 59590926Snectar 59690926Snectar return response; 59790926Snectar} 59890926Snectar 59990926Snectarstatic svn_error_t * 60090926Snectarconnection_closed(svn_ra_serf__connection_t *conn, 60190926Snectar apr_status_t why, 60290926Snectar apr_pool_t *pool) 60390926Snectar{ 604178825Sdfr if (why) 60590926Snectar { 60690926Snectar return svn_ra_serf__wrap_err(why, NULL); 60790926Snectar } 60890926Snectar 609178825Sdfr if (conn->session->using_ssl) 610178825Sdfr conn->ssl_context = NULL; 611178825Sdfr 612178825Sdfr return SVN_NO_ERROR; 613233294Sstas} 614178825Sdfr 615178825Sdfrvoid 616178825Sdfrsvn_ra_serf__conn_closed(serf_connection_t *conn, 61790926Snectar void *closed_baton, 618178825Sdfr apr_status_t why, 619178825Sdfr apr_pool_t *pool) 620178825Sdfr{ 621178825Sdfr svn_ra_serf__connection_t *ra_conn = closed_baton; 62290926Snectar svn_error_t *err; 623178825Sdfr 62490926Snectar err = svn_error_trace(connection_closed(ra_conn, why, pool)); 625178825Sdfr 626178825Sdfr (void) save_error(ra_conn->session, err); 627178825Sdfr} 628178825Sdfr 62990926Snectar 630178825Sdfr/* Implementation of svn_ra_serf__handle_client_cert */ 631178825Sdfrstatic svn_error_t * 632178825Sdfrhandle_client_cert(void *data, 63390926Snectar const char **cert_path, 63490926Snectar apr_pool_t *pool) 63590926Snectar{ 63690926Snectar svn_ra_serf__connection_t *conn = data; 63790926Snectar svn_ra_serf__session_t *session = conn->session; 63890926Snectar const char *realm; 639233294Sstas void *creds; 640233294Sstas 641102644Snectar *cert_path = NULL; 64290926Snectar 64390926Snectar realm = construct_realm(session, session->pool); 64490926Snectar 64590926Snectar if (!conn->ssl_client_auth_state) 64690926Snectar { 64790926Snectar SVN_ERR(svn_auth_first_credentials(&creds, 64890926Snectar &conn->ssl_client_auth_state, 64990926Snectar SVN_AUTH_CRED_SSL_CLIENT_CERT, 65090926Snectar realm, 65190926Snectar session->auth_baton, 65290926Snectar pool)); 65390926Snectar } 65490926Snectar else 655233294Sstas { 65690926Snectar SVN_ERR(svn_auth_next_credentials(&creds, 657233294Sstas conn->ssl_client_auth_state, 658233294Sstas session->pool)); 659233294Sstas } 66090926Snectar 66190926Snectar if (creds) 66290926Snectar { 66390926Snectar svn_auth_cred_ssl_client_cert_t *client_creds; 66490926Snectar client_creds = creds; 66590926Snectar *cert_path = client_creds->cert_file; 66690926Snectar } 66790926Snectar 66890926Snectar return SVN_NO_ERROR; 66990926Snectar} 67090926Snectar 67190926Snectar/* Implements serf_ssl_need_client_cert_t for handle_client_cert */ 67290926Snectarapr_status_t svn_ra_serf__handle_client_cert(void *data, 67390926Snectar const char **cert_path) 67490926Snectar{ 67590926Snectar svn_ra_serf__connection_t *conn = data; 67690926Snectar svn_ra_serf__session_t *session = conn->session; 67790926Snectar svn_error_t *err; 67890926Snectar 679233294Sstas err = svn_error_trace(handle_client_cert(data, cert_path, session->pool)); 680233294Sstas 681120945Snectar return save_error(session, err); 682120945Snectar} 683120945Snectar 684178825Sdfr/* Implementation for svn_ra_serf__handle_client_cert_pw */ 685120945Snectarstatic svn_error_t * 686120945Snectarhandle_client_cert_pw(void *data, 687178825Sdfr const char *cert_path, 688178825Sdfr const char **password, 689178825Sdfr apr_pool_t *pool) 690178825Sdfr{ 691233294Sstas svn_ra_serf__connection_t *conn = data; 692233294Sstas svn_ra_serf__session_t *session = conn->session; 693233294Sstas void *creds; 694120945Snectar 695120945Snectar *password = NULL; 696120945Snectar 697120945Snectar if (!conn->ssl_client_pw_auth_state) 698120945Snectar { 699120945Snectar SVN_ERR(svn_auth_first_credentials(&creds, 700120945Snectar &conn->ssl_client_pw_auth_state, 701120945Snectar SVN_AUTH_CRED_SSL_CLIENT_CERT_PW, 702120945Snectar cert_path, 703120945Snectar session->auth_baton, 704120945Snectar pool)); 705120945Snectar } 706120945Snectar else 707120945Snectar { 708120945Snectar SVN_ERR(svn_auth_next_credentials(&creds, 709120945Snectar conn->ssl_client_pw_auth_state, 710178825Sdfr pool)); 711178825Sdfr } 712178825Sdfr 713178825Sdfr if (creds) 714178825Sdfr { 715178825Sdfr svn_auth_cred_ssl_client_cert_pw_t *pw_creds; 716178825Sdfr pw_creds = creds; 717178825Sdfr *password = pw_creds->password; 718120945Snectar } 719178825Sdfr 720178825Sdfr return APR_SUCCESS; 721178825Sdfr} 722178825Sdfr 723178825Sdfr/* Implements serf_ssl_need_client_cert_pw_t for handle_client_cert_pw */ 724178825Sdfrapr_status_t svn_ra_serf__handle_client_cert_pw(void *data, 725178825Sdfr const char *cert_path, 726178825Sdfr const char **password) 727178825Sdfr{ 728120945Snectar svn_ra_serf__connection_t *conn = data; 729120945Snectar svn_ra_serf__session_t *session = conn->session; 730120945Snectar svn_error_t *err; 73155682Smarkm 732233294Sstas err = svn_error_trace(handle_client_cert_pw(data, 733233294Sstas cert_path, 734233294Sstas password, 735233294Sstas session->pool)); 736233294Sstas 737233294Sstas return save_error(session, err); 738233294Sstas} 739233294Sstas 740233294Sstas 741233294Sstas/* 742233294Sstas * Given a REQUEST on connection CONN, construct a request bucket for it, 743233294Sstas * returning the bucket in *REQ_BKT. 744233294Sstas * 745233294Sstas * If HDRS_BKT is not-NULL, it will be set to a headers_bucket that 746233294Sstas * corresponds to the new request. 747233294Sstas * 748233294Sstas * The request will be METHOD at URL. 74955682Smarkm * 750233294Sstas * If BODY_BKT is not-NULL, it will be sent as the request body. 751233294Sstas * 752233294Sstas * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header. 753233294Sstas * 754233294Sstas * If DAV_HEADERS is non-zero, it will add standard DAV capabilites headers 755233294Sstas * to request. 756233294Sstas * 757233294Sstas * REQUEST_POOL should live for the duration of the request. Serf will 758233294Sstas * construct this and provide it to the request_setup callback, so we 759233294Sstas * should just use that one. 760233294Sstas */ 761233294Sstasstatic svn_error_t * 762233294Sstassetup_serf_req(serf_request_t *request, 763233294Sstas serf_bucket_t **req_bkt, 764233294Sstas serf_bucket_t **hdrs_bkt, 765233294Sstas svn_ra_serf__session_t *session, 766233294Sstas const char *method, const char *url, 76755682Smarkm serf_bucket_t *body_bkt, const char *content_type, 768233294Sstas const char *accept_encoding, 76990926Snectar svn_boolean_t dav_headers, 770233294Sstas apr_pool_t *request_pool, 771233294Sstas apr_pool_t *scratch_pool) 772233294Sstas{ 773233294Sstas serf_bucket_alloc_t *allocator = serf_request_get_alloc(request); 774233294Sstas 775233294Sstas svn_spillbuf_t *buf; 776233294Sstas svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests; 777233294Sstas 778233294Sstas if (set_CL && body_bkt != NULL) 779233294Sstas { 780233294Sstas /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if 781233294Sstas it speaks HTTP/1.1 (and thus, chunked requests), or because the 782233294Sstas server actually responded as only supporting HTTP/1.0. 783233294Sstas 784233294Sstas We'll take the existing body_bkt, spool it into a spillbuf, and 785233294Sstas then wrap a bucket around that spillbuf. The spillbuf will give 786233294Sstas us the Content-Length value. */ 787233294Sstas SVN_ERR(svn_ra_serf__copy_into_spillbuf(&buf, body_bkt, 788233294Sstas request_pool, 789233294Sstas scratch_pool)); 790233294Sstas /* Destroy original bucket since it content is already copied 791233294Sstas to spillbuf. */ 792233294Sstas serf_bucket_destroy(body_bkt); 793233294Sstas 794233294Sstas body_bkt = svn_ra_serf__create_sb_bucket(buf, allocator, 795233294Sstas request_pool, 796233294Sstas scratch_pool); 797233294Sstas } 798233294Sstas 799233294Sstas /* Create a request bucket. Note that this sucker is kind enough to 800233294Sstas add a "Host" header for us. */ 801233294Sstas *req_bkt = serf_request_bucket_request_create(request, method, url, 802233294Sstas body_bkt, allocator); 803233294Sstas 80455682Smarkm /* Set the Content-Length value. This will also trigger an HTTP/1.0 80555682Smarkm request (rather than the default chunked request). */ 80655682Smarkm if (set_CL) 80755682Smarkm { 80855682Smarkm if (body_bkt == NULL) 80955682Smarkm serf_bucket_request_set_CL(*req_bkt, 0); 81055682Smarkm else 81155682Smarkm serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf)); 81255682Smarkm } 81355682Smarkm 81455682Smarkm *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt); 81555682Smarkm 81655682Smarkm /* We use serf_bucket_headers_setn() because the USERAGENT has a 81755682Smarkm lifetime longer than this bucket. Thus, there is no need to copy 81855682Smarkm the header values. */ 81955682Smarkm serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent); 82055682Smarkm 82155682Smarkm if (content_type) 82255682Smarkm { 82355682Smarkm serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type); 82455682Smarkm } 82555682Smarkm 826233294Sstas if (session->http10) 82755682Smarkm { 82855682Smarkm serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive"); 82955682Smarkm } 83055682Smarkm 83155682Smarkm if (accept_encoding) 83255682Smarkm { 83355682Smarkm serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding); 83455682Smarkm } 83555682Smarkm 836178825Sdfr /* These headers need to be sent with every request that might need 837178825Sdfr capability processing (e.g. during commit, reports, etc.), see 838233294Sstas issue #3255 ("mod_dav_svn does not pass client capabilities to 839178825Sdfr start-commit hooks") for why. 840178825Sdfr 841178825Sdfr Some request types like GET/HEAD/PROPFIND are unaware of capability 842178825Sdfr handling; and in some cases the responses can even be cached by 843178825Sdfr proxies, so we don't have to send these hearders there. */ 844178825Sdfr if (dav_headers) 845178825Sdfr { 846178825Sdfr serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH); 847178825Sdfr serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO); 848178825Sdfr serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS); 849233294Sstas } 85078527Sassar 85178527Sassar return SVN_NO_ERROR; 85255682Smarkm} 85355682Smarkm 85478527Sassarsvn_error_t * 855233294Sstassvn_ra_serf__context_run(svn_ra_serf__session_t *sess, 856233294Sstas apr_interval_time_t *waittime_left, 857233294Sstas apr_pool_t *scratch_pool) 85855682Smarkm{ 85978527Sassar apr_status_t status; 86055682Smarkm svn_error_t *err; 86155682Smarkm assert(sess->pending_error == SVN_NO_ERROR); 86255682Smarkm 863178825Sdfr if (sess->cancel_func) 864178825Sdfr SVN_ERR(sess->cancel_func(sess->cancel_baton)); 865178825Sdfr 866178825Sdfr status = serf_context_run(sess->context, 867178825Sdfr SVN_RA_SERF__CONTEXT_RUN_DURATION, 868178825Sdfr scratch_pool); 869178825Sdfr 870178825Sdfr err = sess->pending_error; 871178825Sdfr sess->pending_error = SVN_NO_ERROR; 872178825Sdfr 873178825Sdfr /* If the context duration timeout is up, we'll subtract that 874178825Sdfr duration from the total time alloted for such things. If 875178825Sdfr there's no time left, we fail with a message indicating that 876178825Sdfr the connection timed out. */ 877233294Sstas if (APR_STATUS_IS_TIMEUP(status)) 87878527Sassar { 87978527Sassar status = 0; 88055682Smarkm 88155682Smarkm if (sess->timeout) 88278527Sassar { 883233294Sstas if (*waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION) 884233294Sstas { 885233294Sstas *waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION; 88655682Smarkm } 88778527Sassar else 88855682Smarkm { 88955682Smarkm return 89055682Smarkm svn_error_compose_create( 891178825Sdfr err, 892178825Sdfr svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL, 893178825Sdfr _("Connection timed out"))); 894178825Sdfr } 895178825Sdfr } 896178825Sdfr } 897178825Sdfr else 898178825Sdfr { 899178825Sdfr *waittime_left = sess->timeout; 900178825Sdfr } 901178825Sdfr 902178825Sdfr SVN_ERR(err); 903178825Sdfr if (status) 904178825Sdfr { 905178825Sdfr /* ### This omits SVN_WARNING, and possibly relies on the fact that 906178825Sdfr ### MAX(SERF_ERROR_*) < SVN_ERR_BAD_CATEGORY_START? */ 907178825Sdfr if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST) 908178825Sdfr { 909178825Sdfr /* apr can't translate subversion errors to text */ 910178825Sdfr SVN_ERR_W(svn_error_create(status, NULL, NULL), 911178825Sdfr _("Error running context")); 912233294Sstas } 91378527Sassar 91478527Sassar return svn_ra_serf__wrap_err(status, _("Error running context")); 91555682Smarkm } 916102644Snectar 91755682Smarkm return SVN_NO_ERROR; 91855682Smarkm} 91955682Smarkm 92055682Smarkmsvn_error_t * 92178527Sassarsvn_ra_serf__context_run_wait(svn_boolean_t *done, 922233294Sstas svn_ra_serf__session_t *sess, 923233294Sstas apr_pool_t *scratch_pool) 924233294Sstas{ 925233294Sstas apr_pool_t *iterpool; 92655682Smarkm apr_interval_time_t waittime_left = sess->timeout; 92778527Sassar 92890926Snectar assert(sess->pending_error == SVN_NO_ERROR); 929233294Sstas 930233294Sstas iterpool = svn_pool_create(scratch_pool); 931233294Sstas while (!*done) 932233294Sstas { 93390926Snectar int i; 93490926Snectar 93555682Smarkm svn_pool_clear(iterpool); 93655682Smarkm 93755682Smarkm SVN_ERR(svn_ra_serf__context_run(sess, &waittime_left, iterpool)); 93855682Smarkm 939178825Sdfr /* Debugging purposes only! */ 940178825Sdfr for (i = 0; i < sess->num_conns; i++) 941178825Sdfr { 942178825Sdfr serf_debug__closed_conn(sess->conns[i]->bkt_alloc); 943178825Sdfr } 944178825Sdfr } 945178825Sdfr svn_pool_destroy(iterpool); 946178825Sdfr 947178825Sdfr return SVN_NO_ERROR; 948233294Sstas} 94955682Smarkm 95055682Smarkm/* Ensure that a handler is no longer scheduled on the connection. 95155682Smarkm 95255682Smarkm Eventually serf will have a reliable way to cancel existing requests, 95355682Smarkm but currently it doesn't even have a way to relyable identify a request 95455682Smarkm after rescheduling, for auth reasons. 95555682Smarkm 95655682Smarkm So the only thing we can do today is reset the connection, which 95755682Smarkm will cancel all outstanding requests and prepare the connection 95855682Smarkm for re-use. 95955682Smarkm*/ 960178825Sdfrstatic void 961178825Sdfrsvn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler) 962178825Sdfr{ 963178825Sdfr serf_connection_reset(handler->conn->conn); 964178825Sdfr handler->scheduled = FALSE; 965178825Sdfr} 966178825Sdfr 967178825Sdfrsvn_error_t * 968178825Sdfrsvn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler, 969178825Sdfr apr_pool_t *scratch_pool) 970178825Sdfr{ 971178825Sdfr svn_error_t *err; 972233294Sstas 97355682Smarkm /* Create a serf request based on HANDLER. */ 97455682Smarkm svn_ra_serf__request_create(handler); 97555682Smarkm 97690926Snectar /* Wait until the response logic marks its DONE status. */ 97755682Smarkm err = svn_ra_serf__context_run_wait(&handler->done, handler->session, 97855682Smarkm scratch_pool); 97955682Smarkm 98055682Smarkm if (handler->scheduled) 981233294Sstas { 982233294Sstas /* We reset the connection (breaking pipelining, etc.), as 983233294Sstas if we didn't the next data would still be handled by this handler, 984233294Sstas which is done as far as our caller is concerned. */ 985233294Sstas svn_ra_serf__unschedule_handler(handler); 986233294Sstas } 987233294Sstas 988233294Sstas return svn_error_trace(err); 989233294Sstas} 990178825Sdfr 991178825Sdfr 992178825Sdfr 993178825Sdfr 994178825Sdfrstatic apr_status_t 995178825Sdfrdrain_bucket(serf_bucket_t *bucket) 996178825Sdfr{ 997178825Sdfr /* Read whatever is in the bucket, and just drop it. */ 998178825Sdfr while (1) 999178825Sdfr { 1000178825Sdfr apr_status_t status; 1001178825Sdfr const char *data; 1002178825Sdfr apr_size_t len; 1003178825Sdfr 1004178825Sdfr status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len); 1005178825Sdfr if (status) 1006178825Sdfr return status; 1007178825Sdfr } 1008178825Sdfr} 1009233294Sstas 101078527Sassar 101178527Sassar 1012102644Snectar 1013102644Snectar/* Implements svn_ra_serf__response_handler_t */ 101455682Smarkmsvn_error_t * 101555682Smarkmsvn_ra_serf__handle_discard_body(serf_request_t *request, 101655682Smarkm serf_bucket_t *response, 101778527Sassar void *baton, 1018233294Sstas apr_pool_t *pool) 1019233294Sstas{ 102055682Smarkm apr_status_t status; 102178527Sassar 102255682Smarkm status = drain_bucket(response); 102355682Smarkm if (status) 102455682Smarkm return svn_ra_serf__wrap_err(status, NULL); 102555682Smarkm 1026178825Sdfr return SVN_NO_ERROR; 1027178825Sdfr} 1028178825Sdfr 1029178825Sdfrapr_status_t 1030178825Sdfrsvn_ra_serf__response_discard_handler(serf_request_t *request, 1031178825Sdfr serf_bucket_t *response, 1032178825Sdfr void *baton, 1033178825Sdfr apr_pool_t *pool) 1034178825Sdfr{ 1035178825Sdfr return drain_bucket(response); 1036178825Sdfr} 1037178825Sdfr 1038178825Sdfr 1039178825Sdfr/* Return the value of the RESPONSE's Location header if any, or NULL 1040233294Sstas otherwise. */ 104178527Sassarstatic const char * 104278527Sassarresponse_get_location(serf_bucket_t *response, 104355682Smarkm const char *base_url, 104455682Smarkm apr_pool_t *result_pool, 104555682Smarkm apr_pool_t *scratch_pool) 104678527Sassar{ 1047233294Sstas serf_bucket_t *headers; 1048233294Sstas const char *location; 104955682Smarkm 105078527Sassar headers = serf_bucket_response_get_headers(response); 105155682Smarkm location = serf_bucket_headers_get(headers, "Location"); 105255682Smarkm if (location == NULL) 105355682Smarkm return NULL; 1054178825Sdfr 1055178825Sdfr /* The RFCs say we should have received a full url in LOCATION, but 1056178825Sdfr older apache versions and many custom web handlers just return a 1057178825Sdfr relative path here... 1058178825Sdfr 1059178825Sdfr And we can't trust anything because it is network data. 1060178825Sdfr */ 1061178825Sdfr if (*location == '/') 1062178825Sdfr { 1063178825Sdfr apr_uri_t uri; 1064178825Sdfr apr_status_t status; 1065178825Sdfr 1066178825Sdfr status = apr_uri_parse(scratch_pool, base_url, &uri); 1067178825Sdfr 1068178825Sdfr if (status != APR_SUCCESS) 1069178825Sdfr return NULL; 1070178825Sdfr 1071233294Sstas /* Replace the path path with what we got */ 107278527Sassar uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool); 107378527Sassar 107455682Smarkm /* And make APR produce a proper full url for us */ 1075102644Snectar location = apr_uri_unparse(scratch_pool, &uri, 0); 107655682Smarkm 107755682Smarkm /* Fall through to ensure our canonicalization rules */ 107855682Smarkm } 107955682Smarkm else if (!svn_path_is_url(location)) 108078527Sassar { 1081233294Sstas return NULL; /* Any other formats we should support? */ 1082233294Sstas } 108355682Smarkm 108478527Sassar return svn_uri_canonicalize(location, result_pool); 108555682Smarkm} 108655682Smarkm 108755682Smarkm 108855682Smarkm/* Implements svn_ra_serf__response_handler_t */ 108955682Smarkmsvn_error_t * 1090178825Sdfrsvn_ra_serf__expect_empty_body(serf_request_t *request, 1091178825Sdfr serf_bucket_t *response, 1092178825Sdfr void *baton, 1093178825Sdfr apr_pool_t *scratch_pool) 1094178825Sdfr{ 1095178825Sdfr svn_ra_serf__handler_t *handler = baton; 1096178825Sdfr serf_bucket_t *hdrs; 1097178825Sdfr const char *val; 1098178825Sdfr 1099178825Sdfr /* This function is just like handle_multistatus_only() except for the 1100178825Sdfr XML parsing callbacks. We want to look for the -readable element. */ 1101178825Sdfr 1102178825Sdfr /* We should see this just once, in order to initialize SERVER_ERROR. 1103178825Sdfr At that point, the core error processing will take over. If we choose 1104178825Sdfr not to parse an error, then we'll never return here (because we 1105178825Sdfr change the response handler). */ 1106233294Sstas SVN_ERR_ASSERT(handler->server_error == NULL); 1107233294Sstas 110855682Smarkm hdrs = serf_bucket_response_get_headers(response); 110955682Smarkm val = serf_bucket_headers_get(hdrs, "Content-Type"); 111055682Smarkm if (val 1111178825Sdfr && (handler->sline.code < 200 || handler->sline.code >= 300) 111255682Smarkm && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1113120945Snectar { 111455682Smarkm svn_ra_serf__server_error_t *server_err; 111590926Snectar 1116233294Sstas SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 111790926Snectar FALSE, 111855682Smarkm handler->handler_pool, 111955682Smarkm handler->handler_pool)); 1120233294Sstas 112190926Snectar handler->server_error = server_err; 112255682Smarkm } 112390926Snectar else 112455682Smarkm { 112555682Smarkm /* The body was not text/xml, or we got a success code. 1126233294Sstas Toss anything that arrives. */ 112790926Snectar handler->discard_body = TRUE; 112855682Smarkm } 112955682Smarkm 113055682Smarkm /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it 1131102644Snectar to call the response handler again. That will start up the XML parsing, 1132102644Snectar or it will be dropped on the floor (per the decision above). */ 113355682Smarkm return SVN_NO_ERROR; 113455682Smarkm} 1135102644Snectar 1136178825Sdfr 1137178825Sdfrapr_status_t 1138102644Snectarsvn_ra_serf__credentials_callback(char **username, char **password, 1139102644Snectar serf_request_t *request, void *baton, 114055682Smarkm int code, const char *authn_type, 114155682Smarkm const char *realm, 114255682Smarkm apr_pool_t *pool) 1143178825Sdfr{ 1144178825Sdfr svn_ra_serf__handler_t *handler = baton; 1145178825Sdfr svn_ra_serf__session_t *session = handler->session; 1146178825Sdfr void *creds; 1147178825Sdfr svn_auth_cred_simple_t *simple_creds; 1148178825Sdfr svn_error_t *err; 1149178825Sdfr 1150178825Sdfr if (code == 401) 1151178825Sdfr { 1152178825Sdfr /* Use svn_auth_first_credentials if this is the first time we ask for 1153178825Sdfr credentials during this session OR if the last time we asked 1154178825Sdfr session->auth_state wasn't set (eg. if the credentials provider was 1155178825Sdfr cancelled by the user). */ 1156233294Sstas if (!session->auth_state) 115755682Smarkm { 115855682Smarkm err = svn_auth_first_credentials(&creds, 115955682Smarkm &session->auth_state, 116055682Smarkm SVN_AUTH_CRED_SIMPLE, 116155682Smarkm realm, 116255682Smarkm session->auth_baton, 116355682Smarkm session->pool); 116478527Sassar } 116555682Smarkm else 1166178825Sdfr { 1167178825Sdfr err = svn_auth_next_credentials(&creds, 1168178825Sdfr session->auth_state, 116955682Smarkm session->pool); 117055682Smarkm } 1171102644Snectar 1172102644Snectar if (err) 117355682Smarkm { 1174178825Sdfr (void) save_error(session, err); 1175233294Sstas return err->apr_err; 1176233294Sstas } 1177178825Sdfr 1178178825Sdfr session->auth_attempts++; 1179102644Snectar 118055682Smarkm if (!creds || session->auth_attempts > 4) 118155682Smarkm { 118255682Smarkm /* No more credentials. */ 118355682Smarkm (void) save_error(session, 118455682Smarkm svn_error_create( 118555682Smarkm SVN_ERR_AUTHN_FAILED, NULL, 118678527Sassar _("No more credentials or we tried too many " 1187233294Sstas "times.\nAuthentication failed"))); 118878527Sassar return SVN_ERR_AUTHN_FAILED; 1189233294Sstas } 1190233294Sstas 1191233294Sstas simple_creds = creds; 1192233294Sstas *username = apr_pstrdup(pool, simple_creds->username); 119378527Sassar *password = apr_pstrdup(pool, simple_creds->password); 1194233294Sstas } 119555682Smarkm else 119655682Smarkm { 119755682Smarkm *username = apr_pstrdup(pool, session->proxy_username); 119855682Smarkm *password = apr_pstrdup(pool, session->proxy_password); 119955682Smarkm 1200178825Sdfr session->proxy_auth_attempts++; 1201233294Sstas 1202233294Sstas if (!session->proxy_username || session->proxy_auth_attempts > 4) 1203178825Sdfr { 1204178825Sdfr /* No more credentials. */ 1205178825Sdfr (void) save_error(session, 120655682Smarkm svn_error_create( 1207178825Sdfr SVN_ERR_AUTHN_FAILED, NULL, 1208102644Snectar _("Proxy authentication failed"))); 1209178825Sdfr return SVN_ERR_AUTHN_FAILED; 1210178825Sdfr } 1211233294Sstas } 1212233294Sstas 1213178825Sdfr handler->conn->last_status_code = code; 1214233294Sstas 1215233294Sstas return APR_SUCCESS; 1216178825Sdfr} 121755682Smarkm 121855682Smarkm/* Wait for HTTP response status and headers, and invoke HANDLER-> 121955682Smarkm response_handler() to carry out operation-specific processing. 122055682Smarkm Afterwards, check for connection close. 122190926Snectar 1222178825Sdfr SERF_STATUS allows returning errors to serf without creating a 1223178825Sdfr subversion error object. 1224178825Sdfr */ 1225233294Sstasstatic svn_error_t * 1226178825Sdfrhandle_response(serf_request_t *request, 1227178825Sdfr serf_bucket_t *response, 1228178825Sdfr svn_ra_serf__handler_t *handler, 1229178825Sdfr apr_status_t *serf_status, 1230178825Sdfr apr_pool_t *scratch_pool) 1231178825Sdfr{ 1232178825Sdfr apr_status_t status; 1233178825Sdfr svn_error_t *err; 1234178825Sdfr 1235178825Sdfr /* ### need to verify whether this already gets init'd on every 1236178825Sdfr ### successful exit. for an error-exit, it will (properly) be 1237233294Sstas ### ignored by the caller. */ 123890926Snectar *serf_status = APR_SUCCESS; 123990926Snectar 124090926Snectar if (!response) 124190926Snectar { 124290926Snectar /* Uh-oh. Our connection died. */ 124390926Snectar handler->scheduled = FALSE; 124490926Snectar 1245233294Sstas if (handler->response_error) 124690926Snectar { 1247233294Sstas /* Give a handler chance to prevent request requeue. */ 1248233294Sstas SVN_ERR(handler->response_error(request, response, 0, 1249233294Sstas handler->response_error_baton)); 125090926Snectar 125190926Snectar svn_ra_serf__request_create(handler); 1252233294Sstas } 1253233294Sstas /* Response error callback is not configured. Requeue another request 1254233294Sstas for this handler only if we didn't started to process body. 125590926Snectar Return error otherwise. */ 1256233294Sstas else if (!handler->reading_body) 1257233294Sstas { 1258233294Sstas svn_ra_serf__request_create(handler); 125990926Snectar } 126090926Snectar else 1261233294Sstas { 126290926Snectar return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL, 126390926Snectar _("%s request on '%s' failed"), 126490926Snectar handler->method, handler->path); 126590926Snectar } 126690926Snectar 126790926Snectar return SVN_NO_ERROR; 126890926Snectar } 126990926Snectar 127090926Snectar /* If we're reading the body, then skip all this preparation. */ 127190926Snectar if (handler->reading_body) 127290926Snectar goto process_body; 1273178825Sdfr 1274178825Sdfr /* Copy the Status-Line info into HANDLER, if we don't yet have it. */ 1275178825Sdfr if (handler->sline.version == 0) 1276178825Sdfr { 1277178825Sdfr serf_status_line sl; 1278178825Sdfr 1279178825Sdfr status = serf_bucket_response_status(response, &sl); 1280178825Sdfr if (status != APR_SUCCESS) 1281178825Sdfr { 1282178825Sdfr /* The response line is not (yet) ready, or some other error. */ 1283178825Sdfr *serf_status = status; 1284178825Sdfr return SVN_NO_ERROR; /* Handled by serf */ 1285178825Sdfr } 1286233294Sstas 128790926Snectar /* If we got APR_SUCCESS, then we should have Status-Line info. */ 128890926Snectar SVN_ERR_ASSERT(sl.version != 0); 128990926Snectar 129090926Snectar handler->sline = sl; 129190926Snectar handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason); 129290926Snectar 129390926Snectar /* HTTP/1.1? (or later) */ 1294178825Sdfr if (sl.version != SERF_HTTP_10) 1295178825Sdfr handler->session->http10 = FALSE; 1296178825Sdfr } 1297178825Sdfr 1298178825Sdfr /* Keep reading from the network until we've read all the headers. */ 1299178825Sdfr status = serf_bucket_response_wait_for_headers(response); 1300178825Sdfr if (status) 1301178825Sdfr { 1302178825Sdfr /* The typical "error" will be APR_EAGAIN, meaning that more input 1303178825Sdfr from the network is required to complete the reading of the 1304178825Sdfr headers. */ 1305178825Sdfr if (!APR_STATUS_IS_EOF(status)) 1306178825Sdfr { 1307233294Sstas /* Either the headers are not (yet) complete, or there really 130890926Snectar was an error. */ 130990926Snectar *serf_status = status; 131090926Snectar return SVN_NO_ERROR; 131190926Snectar } 1312233294Sstas 131390926Snectar /* wait_for_headers() will return EOF if there is no body in this 131490926Snectar response, or if we completely read the body. The latter is not 131590926Snectar true since we would have set READING_BODY to get the body read, 131690926Snectar and we would not be back to this code block. 131790926Snectar 131890926Snectar It can also return EOF if we truly hit EOF while (say) processing 131990926Snectar the headers. aka Badness. */ 1320178825Sdfr 1321178825Sdfr /* Cases where a lack of a response body (via EOF) is okay: 1322178825Sdfr * - A HEAD request 1323178825Sdfr * - 204/304 response 1324178825Sdfr * 1325178825Sdfr * Otherwise, if we get an EOF here, something went really wrong: either 1326178825Sdfr * the server closed on us early or we're reading too much. Either way, 1327178825Sdfr * scream loudly. 1328178825Sdfr */ 1329178825Sdfr if (strcmp(handler->method, "HEAD") != 0 1330178825Sdfr && handler->sline.code != 204 1331178825Sdfr && handler->sline.code != 304) 1332233294Sstas { 133390926Snectar err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, 133490926Snectar svn_ra_serf__wrap_err(status, NULL), 133590926Snectar _("Premature EOF seen from server" 1336178825Sdfr " (http status=%d)"), 133790926Snectar handler->sline.code); 133890926Snectar 133990926Snectar /* In case anything else arrives... discard it. */ 1340178825Sdfr handler->discard_body = TRUE; 134190926Snectar 134290926Snectar return err; 134390926Snectar } 1344178825Sdfr } 1345178825Sdfr 1346178825Sdfr /* ... and set up the header fields in HANDLER. */ 1347178825Sdfr handler->location = response_get_location(response, 1348178825Sdfr handler->session->session_url_str, 1349178825Sdfr handler->handler_pool, 1350178825Sdfr scratch_pool); 1351178825Sdfr 1352178825Sdfr /* On the last request, we failed authentication. We succeeded this time, 1353178825Sdfr so let's save away these credentials. */ 1354178825Sdfr if (handler->conn->last_status_code == 401 && handler->sline.code < 400) 1355178825Sdfr { 1356233294Sstas SVN_ERR(svn_auth_save_credentials(handler->session->auth_state, 135790926Snectar handler->session->pool)); 135890926Snectar handler->session->auth_attempts = 0; 135990926Snectar handler->session->auth_state = NULL; 1360233294Sstas } 136190926Snectar handler->conn->last_status_code = handler->sline.code; 136290926Snectar 136390926Snectar if (handler->sline.code >= 400) 1364178825Sdfr { 1365178825Sdfr /* 405 Method Not allowed. 136690926Snectar 408 Request Timeout 136790926Snectar 409 Conflict: can indicate a hook error. 136890926Snectar 5xx (Internal) Server error. */ 1369178825Sdfr serf_bucket_t *hdrs; 1370178825Sdfr const char *val; 1371178825Sdfr 1372178825Sdfr hdrs = serf_bucket_response_get_headers(response); 1373178825Sdfr val = serf_bucket_headers_get(hdrs, "Content-Type"); 1374178825Sdfr if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0) 1375178825Sdfr { 1376178825Sdfr svn_ra_serf__server_error_t *server_err; 1377178825Sdfr 1378178825Sdfr SVN_ERR(svn_ra_serf__setup_error_parsing(&server_err, handler, 1379178825Sdfr FALSE, 1380178825Sdfr handler->handler_pool, 1381178825Sdfr handler->handler_pool)); 1382233294Sstas 138390926Snectar handler->server_error = server_err; 138490926Snectar } 138590926Snectar else 138690926Snectar { 138790926Snectar handler->discard_body = TRUE; 138890926Snectar } 138990926Snectar } 139090926Snectar else if (handler->sline.code <= 199) 139190926Snectar { 139290926Snectar handler->discard_body = TRUE; 1393178825Sdfr } 1394178825Sdfr 1395178825Sdfr /* Stop processing the above, on every packet arrival. */ 1396178825Sdfr handler->reading_body = TRUE; 1397178825Sdfr 1398178825Sdfr process_body: 1399178825Sdfr 1400178825Sdfr /* We've been instructed to ignore the body. Drain whatever is present. */ 1401178825Sdfr if (handler->discard_body) 1402178825Sdfr { 1403178825Sdfr *serf_status = drain_bucket(response); 1404178825Sdfr 1405178825Sdfr return SVN_NO_ERROR; 1406233294Sstas } 140790926Snectar 140890926Snectar /* If we are supposed to parse the body as a server_error, then do 140990926Snectar that now. */ 141090926Snectar if (handler->server_error != NULL) 1411233294Sstas { 141290926Snectar return svn_error_trace( 141390926Snectar svn_ra_serf__handle_server_error(handler->server_error, 141490926Snectar handler, 141590926Snectar request, response, 141690926Snectar serf_status, 141790926Snectar scratch_pool)); 141890926Snectar } 141990926Snectar 1420178825Sdfr /* Pass the body along to the registered response handler. */ 1421178825Sdfr err = handler->response_handler(request, response, 1422178825Sdfr handler->response_baton, 1423178825Sdfr scratch_pool); 1424178825Sdfr 1425178825Sdfr if (err 1426178825Sdfr && (!SERF_BUCKET_READ_ERROR(err->apr_err) 1427178825Sdfr || APR_STATUS_IS_ECONNRESET(err->apr_err) 1428178825Sdfr || APR_STATUS_IS_ECONNABORTED(err->apr_err))) 1429178825Sdfr { 1430178825Sdfr /* These errors are special cased in serf 1431178825Sdfr ### We hope no handler returns these by accident. */ 1432178825Sdfr *serf_status = err->apr_err; 1433233294Sstas svn_error_clear(err); 143490926Snectar return SVN_NO_ERROR; 143590926Snectar } 143690926Snectar 143790926Snectar return svn_error_trace(err); 143890926Snectar} 143990926Snectar 1440233294Sstas 144190926Snectar/* Implements serf_response_handler_t for handle_response. Storing 144290926Snectar errors in handler->session->pending_error if appropriate. */ 144390926Snectarstatic apr_status_t 1444233294Sstashandle_response_cb(serf_request_t *request, 1445233294Sstas serf_bucket_t *response, 144690926Snectar void *baton, 144790926Snectar apr_pool_t *response_pool) 144890926Snectar{ 144990926Snectar svn_ra_serf__handler_t *handler = baton; 145090926Snectar svn_error_t *err; 145190926Snectar apr_status_t inner_status; 145290926Snectar apr_status_t outer_status; 1453233294Sstas apr_pool_t *scratch_pool = response_pool; /* Scratch pool needed? */ 1454233294Sstas 145590926Snectar err = svn_error_trace(handle_response(request, response, 145690926Snectar handler, &inner_status, 145790926Snectar scratch_pool)); 145890926Snectar 145990926Snectar /* Select the right status value to return. */ 146090926Snectar outer_status = save_error(handler->session, err); 146190926Snectar if (!outer_status) 146290926Snectar outer_status = inner_status; 146390926Snectar 1464178825Sdfr /* Make sure the DONE flag is set properly and requests are cleaned up. */ 146590926Snectar if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status)) 1466178825Sdfr { 1467178825Sdfr svn_ra_serf__session_t *sess = handler->session; 1468178825Sdfr handler->done = TRUE; 1469178825Sdfr handler->scheduled = FALSE; 1470178825Sdfr outer_status = APR_EOF; 1471178825Sdfr 1472178825Sdfr /* We use a cached handler->session here to allow handler to free the 1473178825Sdfr memory containing the handler */ 1474178825Sdfr save_error(sess, 147590926Snectar handler->done_delegate(request, handler->done_delegate_baton, 147690926Snectar scratch_pool)); 1477233294Sstas } 147890926Snectar else if (SERF_BUCKET_READ_ERROR(outer_status) 147990926Snectar && handler->session->pending_error) 148090926Snectar { 148190926Snectar handler->discard_body = TRUE; /* Discard further data */ 148290926Snectar handler->done = TRUE; /* Mark as done */ 148390926Snectar /* handler->scheduled is still TRUE, as we still expect data. 148490926Snectar If we would return an error outer-status the connection 148590926Snectar would have to be restarted. With scheduled still TRUE 148690926Snectar destroying the handler's pool will still reset the 1487233294Sstas connection, avoiding the posibility of returning 1488233294Sstas an error for this handler when a new request is 148990926Snectar scheduled. */ 149090926Snectar outer_status = APR_EAGAIN; /* Exit context loop */ 149190926Snectar } 149290926Snectar 149390926Snectar return outer_status; 1494233294Sstas} 1495233294Sstas 149690926Snectar/* Perform basic request setup, with special handling for HEAD requests, 1497178825Sdfr and finer-grained callbacks invoked (if non-NULL) to produce the request 149890926Snectar headers and body. */ 149990926Snectarstatic svn_error_t * 150090926Snectarsetup_request(serf_request_t *request, 150190926Snectar svn_ra_serf__handler_t *handler, 150290926Snectar serf_bucket_t **req_bkt, 150390926Snectar apr_pool_t *request_pool, 150490926Snectar apr_pool_t *scratch_pool) 150590926Snectar{ 150690926Snectar serf_bucket_t *body_bkt; 150790926Snectar serf_bucket_t *headers_bkt; 150890926Snectar const char *accept_encoding; 150990926Snectar 151090926Snectar if (handler->body_delegate) 151190926Snectar { 151290926Snectar serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request); 151390926Snectar 151490926Snectar SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton, 151590926Snectar bkt_alloc, request_pool, scratch_pool)); 151690926Snectar } 151790926Snectar else 151890926Snectar { 151990926Snectar body_bkt = NULL; 152090926Snectar } 152190926Snectar 152290926Snectar if (handler->custom_accept_encoding) 152390926Snectar { 152490926Snectar accept_encoding = NULL; 152590926Snectar } 152690926Snectar else if (handler->session->using_compression) 152790926Snectar { 1528178825Sdfr /* Accept gzip compression if enabled. */ 1529178825Sdfr accept_encoding = "gzip"; 1530178825Sdfr } 1531178825Sdfr else 1532178825Sdfr { 1533178825Sdfr accept_encoding = NULL; 1534178825Sdfr } 1535178825Sdfr 1536178825Sdfr SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt, 1537178825Sdfr handler->session, handler->method, handler->path, 1538178825Sdfr body_bkt, handler->body_type, accept_encoding, 1539178825Sdfr !handler->no_dav_headers, request_pool, 1540178825Sdfr scratch_pool)); 1541178825Sdfr 1542178825Sdfr if (handler->header_delegate) 1543178825Sdfr { 1544233294Sstas SVN_ERR(handler->header_delegate(headers_bkt, 1545178825Sdfr handler->header_delegate_baton, 1546178825Sdfr request_pool, scratch_pool)); 1547178825Sdfr } 1548178825Sdfr 1549178825Sdfr return SVN_NO_ERROR; 1550178825Sdfr} 1551178825Sdfr 1552178825Sdfr/* Implements the serf_request_setup_t interface (which sets up both a 1553178825Sdfr request and its response handler callback). Handles errors for 1554233294Sstas setup_request_cb */ 1555233294Sstasstatic apr_status_t 1556233294Sstassetup_request_cb(serf_request_t *request, 1557233294Sstas void *setup_baton, 1558178825Sdfr serf_bucket_t **req_bkt, 1559178825Sdfr 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