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