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