]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/subversion/subversion/libsvn_ra_serf/util.c
MFC r275385 (by bapt):
[FreeBSD/stable/10.git] / contrib / subversion / subversion / libsvn_ra_serf / util.c
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 \f
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
51 static 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. */
62 static apr_uint32_t
63 ssl_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
87 static apr_status_t
88 save_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. */
104 static const char *
105 construct_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(). */
131 static char *
132 convert_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
192 static 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. */
208 static svn_error_t *
209 ssl_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 */
436 static apr_status_t
437 ssl_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
452 static svn_error_t *
453 load_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
481 static svn_error_t *
482 conn_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. */
543 apr_status_t
544 svn_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.  */
564 static serf_bucket_t *
565 accept_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.  */
582 static serf_bucket_t *
583 accept_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
599 static svn_error_t *
600 connection_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
615 void
616 svn_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 */
631 static svn_error_t *
632 handle_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 */
672 apr_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 */
685 static svn_error_t *
686 handle_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 */
724 apr_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  */
761 static svn_error_t *
762 setup_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
854 svn_error_t *
855 svn_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
920 svn_error_t *
921 svn_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 */
960 static void
961 svn_ra_serf__unschedule_handler(svn_ra_serf__handler_t *handler)
962 {
963   serf_connection_reset(handler->conn->conn);
964   handler->scheduled = FALSE;
965 }
966
967 svn_error_t *
968 svn_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
994 static apr_status_t
995 drain_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 */
1014 svn_error_t *
1015 svn_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
1029 apr_status_t
1030 svn_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.  */
1041 static const char *
1042 response_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 */
1089 svn_error_t *
1090 svn_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
1137 apr_status_t
1138 svn_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    */
1225 static svn_error_t *
1226 handle_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. */
1443 static apr_status_t
1444 handle_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. */
1499 static svn_error_t *
1500 setup_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 */
1555 static apr_status_t
1556 setup_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
1589 void
1590 svn_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
1620 svn_error_t *
1621 svn_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
1726 svn_error_t *
1727 svn_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
1756 svn_error_t *
1757 svn_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
1773 svn_error_t *
1774 svn_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
1835 svn_error_t *
1836 svn_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
1869 svn_error_t *
1870 svn_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 */
1880 static svn_error_t *
1881 response_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. */
1918 static apr_status_t
1919 handler_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
1930 svn_ra_serf__handler_t *
1931 svn_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
1952 svn_error_t *
1953 svn_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 }