1251881Speter/*
2251881Speter * util.c : serf utility routines for ra_serf
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter
26251881Speter#include <assert.h>
27251881Speter
28251881Speter#define APR_WANT_STRFUNC
29251881Speter#include <apr.h>
30251881Speter#include <apr_want.h>
31251881Speter#include <apr_fnmatch.h>
32251881Speter
33251881Speter#include <serf.h>
34251881Speter#include <serf_bucket_types.h>
35251881Speter
36251881Speter#include <expat.h>
37251881Speter
38251881Speter#include "svn_hash.h"
39251881Speter#include "svn_dirent_uri.h"
40251881Speter#include "svn_path.h"
41251881Speter#include "svn_private_config.h"
42251881Speter#include "svn_string.h"
43251881Speter#include "svn_xml.h"
44251881Speter#include "svn_props.h"
45251881Speter#include "svn_dirent_uri.h"
46251881Speter
47251881Speter#include "../libsvn_ra/ra_loader.h"
48251881Speter#include "private/svn_dep_compat.h"
49251881Speter#include "private/svn_fspath.h"
50251881Speter#include "private/svn_subr_private.h"
51251881Speter
52251881Speter#include "ra_serf.h"
53251881Speter
54251881Speter
55251881Speter/* Fix for older expat 1.95.x's that do not define
56251881Speter * XML_STATUS_OK/XML_STATUS_ERROR
57251881Speter */
58251881Speter#ifndef XML_STATUS_OK
59251881Speter#define XML_STATUS_OK    1
60251881Speter#define XML_STATUS_ERROR 0
61251881Speter#endif
62251881Speter
63251881Speter#ifndef XML_VERSION_AT_LEAST
64251881Speter#define XML_VERSION_AT_LEAST(major,minor,patch)                  \
65251881Speter(((major) < XML_MAJOR_VERSION)                                       \
66251881Speter || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION)    \
67251881Speter || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \
68251881Speter     (patch) <= XML_MICRO_VERSION))
69251881Speter#endif /* APR_VERSION_AT_LEAST */
70251881Speter
71251881Speter#if XML_VERSION_AT_LEAST(1, 95, 8)
72251881Speter#define EXPAT_HAS_STOPPARSER
73251881Speter#endif
74251881Speter
75251881Speter/* Read/write chunks of this size into the spillbuf.  */
76251881Speter#define PARSE_CHUNK_SIZE 8000
77251881Speter
78251881Speter/* We will store one megabyte in memory, before switching to store content
79251881Speter   into a temporary file.  */
80251881Speter#define SPILL_SIZE 1000000
81251881Speter
82251881Speter
83251881Speter/* This structure records pending data for the parser in memory blocks,
84251881Speter   and possibly into a temporary file if "too much" content arrives.  */
85251881Speterstruct svn_ra_serf__pending_t {
86251881Speter  /* The spillbuf where we record the pending data.  */
87251881Speter  svn_spillbuf_t *buf;
88251881Speter
89251881Speter  /* This flag is set when the network has reached EOF. The PENDING
90251881Speter     processing can then properly detect when parsing has completed.  */
91251881Speter  svn_boolean_t network_eof;
92251881Speter};
93251881Speter
94251881Speter#define HAS_PENDING_DATA(p) ((p) != NULL && (p)->buf != NULL \
95251881Speter                             && svn_spillbuf__get_size((p)->buf) != 0)
96251881Speter
97251881Speter
98251881Speterstruct expat_ctx_t {
99251881Speter  svn_ra_serf__xml_context_t *xmlctx;
100251881Speter  XML_Parser parser;
101251881Speter  svn_ra_serf__handler_t *handler;
102251881Speter
103251881Speter  svn_error_t *inner_error;
104251881Speter
105251881Speter  /* Do not use this pool for allocation. It is merely recorded for running
106251881Speter     the cleanup handler.  */
107251881Speter  apr_pool_t *cleanup_pool;
108251881Speter};
109251881Speter
110251881Speter
111251881Speterstatic const apr_uint32_t serf_failure_map[][2] =
112251881Speter{
113251881Speter  { SERF_SSL_CERT_NOTYETVALID,   SVN_AUTH_SSL_NOTYETVALID },
114251881Speter  { SERF_SSL_CERT_EXPIRED,       SVN_AUTH_SSL_EXPIRED },
115251881Speter  { SERF_SSL_CERT_SELF_SIGNED,   SVN_AUTH_SSL_UNKNOWNCA },
116251881Speter  { SERF_SSL_CERT_UNKNOWNCA,     SVN_AUTH_SSL_UNKNOWNCA }
117251881Speter};
118251881Speter
119251881Speter/* Return a Subversion failure mask based on FAILURES, a serf SSL
120251881Speter   failure mask.  If anything in FAILURES is not directly mappable to
121251881Speter   Subversion failures, set SVN_AUTH_SSL_OTHER in the returned mask. */
122251881Speterstatic apr_uint32_t
123251881Speterssl_convert_serf_failures(int failures)
124251881Speter{
125251881Speter  apr_uint32_t svn_failures = 0;
126251881Speter  apr_size_t i;
127251881Speter
128251881Speter  for (i = 0; i < sizeof(serf_failure_map) / (2 * sizeof(apr_uint32_t)); ++i)
129251881Speter    {
130251881Speter      if (failures & serf_failure_map[i][0])
131251881Speter        {
132251881Speter          svn_failures |= serf_failure_map[i][1];
133251881Speter          failures &= ~serf_failure_map[i][0];
134251881Speter        }
135251881Speter    }
136251881Speter
137251881Speter  /* Map any remaining failure bits to our OTHER bit. */
138251881Speter  if (failures)
139251881Speter    {
140251881Speter      svn_failures |= SVN_AUTH_SSL_OTHER;
141251881Speter    }
142251881Speter
143251881Speter  return svn_failures;
144251881Speter}
145251881Speter
146251881Speter
147251881Speterstatic apr_status_t
148251881Spetersave_error(svn_ra_serf__session_t *session,
149251881Speter           svn_error_t *err)
150251881Speter{
151251881Speter  if (err || session->pending_error)
152251881Speter    {
153251881Speter      session->pending_error = svn_error_compose_create(
154251881Speter                                  session->pending_error,
155251881Speter                                  err);
156251881Speter      return session->pending_error->apr_err;
157251881Speter    }
158251881Speter
159251881Speter  return APR_SUCCESS;
160251881Speter}
161251881Speter
162251881Speter
163251881Speter/* Construct the realmstring, e.g. https://svn.collab.net:443. */
164251881Speterstatic const char *
165251881Speterconstruct_realm(svn_ra_serf__session_t *session,
166251881Speter                apr_pool_t *pool)
167251881Speter{
168251881Speter  const char *realm;
169251881Speter  apr_port_t port;
170251881Speter
171251881Speter  if (session->session_url.port_str)
172251881Speter    {
173251881Speter      port = session->session_url.port;
174251881Speter    }
175251881Speter  else
176251881Speter    {
177251881Speter      port = apr_uri_port_of_scheme(session->session_url.scheme);
178251881Speter    }
179251881Speter
180251881Speter  realm = apr_psprintf(pool, "%s://%s:%d",
181251881Speter                       session->session_url.scheme,
182251881Speter                       session->session_url.hostname,
183251881Speter                       port);
184251881Speter
185251881Speter  return realm;
186251881Speter}
187251881Speter
188251881Speter/* Convert a hash table containing the fields (as documented in X.509) of an
189251881Speter   organisation to a string ORG, allocated in POOL. ORG is as returned by
190251881Speter   serf_ssl_cert_issuer() and serf_ssl_cert_subject(). */
191251881Speterstatic char *
192251881Speterconvert_organisation_to_str(apr_hash_t *org, apr_pool_t *pool)
193251881Speter{
194251881Speter  return apr_psprintf(pool, "%s, %s, %s, %s, %s (%s)",
195251881Speter                      (char*)svn_hash_gets(org, "OU"),
196251881Speter                      (char*)svn_hash_gets(org, "O"),
197251881Speter                      (char*)svn_hash_gets(org, "L"),
198251881Speter                      (char*)svn_hash_gets(org, "ST"),
199251881Speter                      (char*)svn_hash_gets(org, "C"),
200251881Speter                      (char*)svn_hash_gets(org, "E"));
201251881Speter}
202251881Speter
203251881Speter/* This function is called on receiving a ssl certificate of a server when
204251881Speter   opening a https connection. It allows Subversion to override the initial
205251881Speter   validation done by serf.
206251881Speter   Serf provides us the @a baton as provided in the call to
207251881Speter   serf_ssl_server_cert_callback_set. The result of serf's initial validation
208251881Speter   of the certificate @a CERT is returned as a bitmask in FAILURES. */
209251881Speterstatic svn_error_t *
210251881Speterssl_server_cert(void *baton, int failures,
211251881Speter                const serf_ssl_certificate_t *cert,
212251881Speter                apr_pool_t *scratch_pool)
213251881Speter{
214251881Speter  svn_ra_serf__connection_t *conn = baton;
215251881Speter  svn_auth_ssl_server_cert_info_t cert_info;
216251881Speter  svn_auth_cred_ssl_server_trust_t *server_creds = NULL;
217251881Speter  svn_auth_iterstate_t *state;
218251881Speter  const char *realmstring;
219251881Speter  apr_uint32_t svn_failures;
220251881Speter  apr_hash_t *issuer, *subject, *serf_cert;
221251881Speter  apr_array_header_t *san;
222251881Speter  void *creds;
223251881Speter  int found_matching_hostname = 0;
224251881Speter
225251881Speter  /* Implicitly approve any non-server certs. */
226251881Speter  if (serf_ssl_cert_depth(cert) > 0)
227251881Speter    {
228251881Speter      if (failures)
229251881Speter        conn->server_cert_failures |= ssl_convert_serf_failures(failures);
230251881Speter      return APR_SUCCESS;
231251881Speter    }
232251881Speter
233251881Speter  /* Extract the info from the certificate */
234251881Speter  subject = serf_ssl_cert_subject(cert, scratch_pool);
235251881Speter  issuer = serf_ssl_cert_issuer(cert, scratch_pool);
236251881Speter  serf_cert = serf_ssl_cert_certificate(cert, scratch_pool);
237251881Speter
238251881Speter  cert_info.hostname = svn_hash_gets(subject, "CN");
239251881Speter  san = svn_hash_gets(serf_cert, "subjectAltName");
240251881Speter  cert_info.fingerprint = svn_hash_gets(serf_cert, "sha1");
241251881Speter  if (! cert_info.fingerprint)
242251881Speter    cert_info.fingerprint = apr_pstrdup(scratch_pool, "<unknown>");
243251881Speter  cert_info.valid_from = svn_hash_gets(serf_cert, "notBefore");
244251881Speter  if (! cert_info.valid_from)
245251881Speter    cert_info.valid_from = apr_pstrdup(scratch_pool, "[invalid date]");
246251881Speter  cert_info.valid_until = svn_hash_gets(serf_cert, "notAfter");
247251881Speter  if (! cert_info.valid_until)
248251881Speter    cert_info.valid_until = apr_pstrdup(scratch_pool, "[invalid date]");
249251881Speter  cert_info.issuer_dname = convert_organisation_to_str(issuer, scratch_pool);
250251881Speter  cert_info.ascii_cert = serf_ssl_cert_export(cert, scratch_pool);
251251881Speter
252251881Speter  svn_failures = (ssl_convert_serf_failures(failures)
253251881Speter                  | conn->server_cert_failures);
254251881Speter
255251881Speter  /* Try to find matching server name via subjectAltName first... */
256251881Speter  if (san) {
257251881Speter      int i;
258251881Speter      for (i = 0; i < san->nelts; i++) {
259251881Speter          char *s = APR_ARRAY_IDX(san, i, char*);
260251881Speter          if (apr_fnmatch(s, conn->session->session_url.hostname,
261251881Speter                          APR_FNM_PERIOD) == APR_SUCCESS) {
262251881Speter              found_matching_hostname = 1;
263251881Speter              cert_info.hostname = s;
264251881Speter              break;
265251881Speter          }
266251881Speter      }
267251881Speter  }
268251881Speter
269251881Speter  /* Match server certificate CN with the hostname of the server */
270251881Speter  if (!found_matching_hostname && cert_info.hostname)
271251881Speter    {
272251881Speter      if (apr_fnmatch(cert_info.hostname, conn->session->session_url.hostname,
273251881Speter                      APR_FNM_PERIOD) == APR_FNM_NOMATCH)
274251881Speter        {
275251881Speter          svn_failures |= SVN_AUTH_SSL_CNMISMATCH;
276251881Speter        }
277251881Speter    }
278251881Speter
279251881Speter  svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
280251881Speter                         SVN_AUTH_PARAM_SSL_SERVER_FAILURES,
281251881Speter                         &svn_failures);
282251881Speter
283251881Speter  svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
284251881Speter                         SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO,
285251881Speter                         &cert_info);
286251881Speter
287251881Speter  realmstring = construct_realm(conn->session, conn->session->pool);
288251881Speter
289251881Speter  SVN_ERR(svn_auth_first_credentials(&creds, &state,
290251881Speter                                     SVN_AUTH_CRED_SSL_SERVER_TRUST,
291251881Speter                                     realmstring,
292251881Speter                                     conn->session->wc_callbacks->auth_baton,
293251881Speter                                     scratch_pool));
294251881Speter  if (creds)
295251881Speter    {
296251881Speter      server_creds = creds;
297251881Speter      SVN_ERR(svn_auth_save_credentials(state, scratch_pool));
298251881Speter    }
299251881Speter
300251881Speter  svn_auth_set_parameter(conn->session->wc_callbacks->auth_baton,
301251881Speter                         SVN_AUTH_PARAM_SSL_SERVER_CERT_INFO, NULL);
302251881Speter
303251881Speter  if (!server_creds)
304251881Speter    return svn_error_create(SVN_ERR_RA_SERF_SSL_CERT_UNTRUSTED, NULL, NULL);
305251881Speter
306251881Speter  return SVN_NO_ERROR;
307251881Speter}
308251881Speter
309251881Speter/* Implements serf_ssl_need_server_cert_t for ssl_server_cert */
310251881Speterstatic apr_status_t
311251881Speterssl_server_cert_cb(void *baton, int failures,
312251881Speter                const serf_ssl_certificate_t *cert)
313251881Speter{
314251881Speter  svn_ra_serf__connection_t *conn = baton;
315251881Speter  svn_ra_serf__session_t *session = conn->session;
316251881Speter  apr_pool_t *subpool;
317251881Speter  svn_error_t *err;
318251881Speter
319251881Speter  subpool = svn_pool_create(session->pool);
320251881Speter  err = svn_error_trace(ssl_server_cert(baton, failures, cert, subpool));
321251881Speter  svn_pool_destroy(subpool);
322251881Speter
323251881Speter  return save_error(session, err);
324251881Speter}
325251881Speter
326251881Speterstatic svn_error_t *
327251881Speterload_authorities(svn_ra_serf__connection_t *conn, const char *authorities,
328251881Speter                 apr_pool_t *pool)
329251881Speter{
330251881Speter  apr_array_header_t *files = svn_cstring_split(authorities, ";",
331251881Speter                                                TRUE /* chop_whitespace */,
332251881Speter                                                pool);
333251881Speter  int i;
334251881Speter
335251881Speter  for (i = 0; i < files->nelts; ++i)
336251881Speter    {
337251881Speter      const char *file = APR_ARRAY_IDX(files, i, const char *);
338251881Speter      serf_ssl_certificate_t *ca_cert;
339251881Speter      apr_status_t status = serf_ssl_load_cert_file(&ca_cert, file, pool);
340251881Speter
341251881Speter      if (status == APR_SUCCESS)
342251881Speter        status = serf_ssl_trust_cert(conn->ssl_context, ca_cert);
343251881Speter
344251881Speter      if (status != APR_SUCCESS)
345251881Speter        {
346251881Speter          return svn_error_createf(SVN_ERR_BAD_CONFIG_VALUE, NULL,
347251881Speter             _("Invalid config: unable to load certificate file '%s'"),
348251881Speter             svn_dirent_local_style(file, pool));
349251881Speter        }
350251881Speter    }
351251881Speter
352251881Speter  return SVN_NO_ERROR;
353251881Speter}
354251881Speter
355251881Speterstatic svn_error_t *
356251881Speterconn_setup(apr_socket_t *sock,
357251881Speter           serf_bucket_t **read_bkt,
358251881Speter           serf_bucket_t **write_bkt,
359251881Speter           void *baton,
360251881Speter           apr_pool_t *pool)
361251881Speter{
362251881Speter  svn_ra_serf__connection_t *conn = baton;
363251881Speter
364251881Speter  *read_bkt = serf_context_bucket_socket_create(conn->session->context,
365251881Speter                                               sock, conn->bkt_alloc);
366251881Speter
367251881Speter  if (conn->session->using_ssl)
368251881Speter    {
369251881Speter      /* input stream */
370251881Speter      *read_bkt = serf_bucket_ssl_decrypt_create(*read_bkt, conn->ssl_context,
371251881Speter                                                 conn->bkt_alloc);
372251881Speter      if (!conn->ssl_context)
373251881Speter        {
374251881Speter          conn->ssl_context = serf_bucket_ssl_encrypt_context_get(*read_bkt);
375251881Speter
376251881Speter          serf_ssl_set_hostname(conn->ssl_context,
377251881Speter                                conn->session->session_url.hostname);
378251881Speter
379251881Speter          serf_ssl_client_cert_provider_set(conn->ssl_context,
380251881Speter                                            svn_ra_serf__handle_client_cert,
381251881Speter                                            conn, conn->session->pool);
382251881Speter          serf_ssl_client_cert_password_set(conn->ssl_context,
383251881Speter                                            svn_ra_serf__handle_client_cert_pw,
384251881Speter                                            conn, conn->session->pool);
385251881Speter          serf_ssl_server_cert_callback_set(conn->ssl_context,
386251881Speter                                            ssl_server_cert_cb,
387251881Speter                                            conn);
388251881Speter
389251881Speter          /* See if the user wants us to trust "default" openssl CAs. */
390251881Speter          if (conn->session->trust_default_ca)
391251881Speter            {
392251881Speter              serf_ssl_use_default_certificates(conn->ssl_context);
393251881Speter            }
394251881Speter          /* Are there custom CAs to load? */
395251881Speter          if (conn->session->ssl_authorities)
396251881Speter            {
397251881Speter              SVN_ERR(load_authorities(conn, conn->session->ssl_authorities,
398251881Speter                                       conn->session->pool));
399251881Speter            }
400251881Speter        }
401251881Speter
402251881Speter      if (write_bkt)
403251881Speter        {
404251881Speter          /* output stream */
405251881Speter          *write_bkt = serf_bucket_ssl_encrypt_create(*write_bkt,
406251881Speter                                                      conn->ssl_context,
407251881Speter                                                      conn->bkt_alloc);
408251881Speter        }
409251881Speter    }
410251881Speter
411251881Speter  return SVN_NO_ERROR;
412251881Speter}
413251881Speter
414251881Speter/* svn_ra_serf__conn_setup is a callback for serf. This function
415251881Speter   creates a read bucket and will wrap the write bucket if SSL
416251881Speter   is needed. */
417251881Speterapr_status_t
418251881Spetersvn_ra_serf__conn_setup(apr_socket_t *sock,
419251881Speter                        serf_bucket_t **read_bkt,
420251881Speter                        serf_bucket_t **write_bkt,
421251881Speter                        void *baton,
422251881Speter                        apr_pool_t *pool)
423251881Speter{
424251881Speter  svn_ra_serf__connection_t *conn = baton;
425251881Speter  svn_ra_serf__session_t *session = conn->session;
426251881Speter  svn_error_t *err;
427251881Speter
428251881Speter  err = svn_error_trace(conn_setup(sock,
429251881Speter                                   read_bkt,
430251881Speter                                   write_bkt,
431251881Speter                                   baton,
432251881Speter                                   pool));
433251881Speter  return save_error(session, err);
434251881Speter}
435251881Speter
436251881Speter
437251881Speter/* Our default serf response acceptor.  */
438251881Speterstatic serf_bucket_t *
439251881Speteraccept_response(serf_request_t *request,
440251881Speter                serf_bucket_t *stream,
441251881Speter                void *acceptor_baton,
442251881Speter                apr_pool_t *pool)
443251881Speter{
444251881Speter  serf_bucket_t *c;
445251881Speter  serf_bucket_alloc_t *bkt_alloc;
446251881Speter
447251881Speter  bkt_alloc = serf_request_get_alloc(request);
448251881Speter  c = serf_bucket_barrier_create(stream, bkt_alloc);
449251881Speter
450251881Speter  return serf_bucket_response_create(c, bkt_alloc);
451251881Speter}
452251881Speter
453251881Speter
454251881Speter/* Custom response acceptor for HEAD requests.  */
455251881Speterstatic serf_bucket_t *
456251881Speteraccept_head(serf_request_t *request,
457251881Speter            serf_bucket_t *stream,
458251881Speter            void *acceptor_baton,
459251881Speter            apr_pool_t *pool)
460251881Speter{
461251881Speter  serf_bucket_t *response;
462251881Speter
463251881Speter  response = accept_response(request, stream, acceptor_baton, pool);
464251881Speter
465251881Speter  /* We know we shouldn't get a response body. */
466251881Speter  serf_bucket_response_set_head(response);
467251881Speter
468251881Speter  return response;
469251881Speter}
470251881Speter
471251881Speterstatic svn_error_t *
472251881Speterconnection_closed(svn_ra_serf__connection_t *conn,
473251881Speter                  apr_status_t why,
474251881Speter                  apr_pool_t *pool)
475251881Speter{
476251881Speter  if (why)
477251881Speter    {
478253734Speter      return svn_error_wrap_apr(why, NULL);
479251881Speter    }
480251881Speter
481251881Speter  if (conn->session->using_ssl)
482251881Speter    conn->ssl_context = NULL;
483251881Speter
484251881Speter  return SVN_NO_ERROR;
485251881Speter}
486251881Speter
487251881Spetervoid
488251881Spetersvn_ra_serf__conn_closed(serf_connection_t *conn,
489251881Speter                         void *closed_baton,
490251881Speter                         apr_status_t why,
491251881Speter                         apr_pool_t *pool)
492251881Speter{
493251881Speter  svn_ra_serf__connection_t *ra_conn = closed_baton;
494251881Speter  svn_error_t *err;
495251881Speter
496251881Speter  err = svn_error_trace(connection_closed(ra_conn, why, pool));
497251881Speter
498251881Speter  (void) save_error(ra_conn->session, err);
499251881Speter}
500251881Speter
501251881Speter
502251881Speter/* Implementation of svn_ra_serf__handle_client_cert */
503251881Speterstatic svn_error_t *
504251881Speterhandle_client_cert(void *data,
505251881Speter                   const char **cert_path,
506251881Speter                   apr_pool_t *pool)
507251881Speter{
508251881Speter    svn_ra_serf__connection_t *conn = data;
509251881Speter    svn_ra_serf__session_t *session = conn->session;
510251881Speter    const char *realm;
511251881Speter    void *creds;
512251881Speter
513251881Speter    *cert_path = NULL;
514251881Speter
515251881Speter    realm = construct_realm(session, session->pool);
516251881Speter
517251881Speter    if (!conn->ssl_client_auth_state)
518251881Speter      {
519251881Speter        SVN_ERR(svn_auth_first_credentials(&creds,
520251881Speter                                           &conn->ssl_client_auth_state,
521251881Speter                                           SVN_AUTH_CRED_SSL_CLIENT_CERT,
522251881Speter                                           realm,
523251881Speter                                           session->wc_callbacks->auth_baton,
524251881Speter                                           pool));
525251881Speter      }
526251881Speter    else
527251881Speter      {
528251881Speter        SVN_ERR(svn_auth_next_credentials(&creds,
529251881Speter                                          conn->ssl_client_auth_state,
530251881Speter                                          session->pool));
531251881Speter      }
532251881Speter
533251881Speter    if (creds)
534251881Speter      {
535251881Speter        svn_auth_cred_ssl_client_cert_t *client_creds;
536251881Speter        client_creds = creds;
537251881Speter        *cert_path = client_creds->cert_file;
538251881Speter      }
539251881Speter
540251881Speter    return SVN_NO_ERROR;
541251881Speter}
542251881Speter
543251881Speter/* Implements serf_ssl_need_client_cert_t for handle_client_cert */
544251881Speterapr_status_t svn_ra_serf__handle_client_cert(void *data,
545251881Speter                                             const char **cert_path)
546251881Speter{
547251881Speter  svn_ra_serf__connection_t *conn = data;
548251881Speter  svn_ra_serf__session_t *session = conn->session;
549251881Speter  svn_error_t *err;
550251881Speter
551251881Speter  err = svn_error_trace(handle_client_cert(data, cert_path, session->pool));
552251881Speter
553251881Speter  return save_error(session, err);
554251881Speter}
555251881Speter
556251881Speter/* Implementation for svn_ra_serf__handle_client_cert_pw */
557251881Speterstatic svn_error_t *
558251881Speterhandle_client_cert_pw(void *data,
559251881Speter                      const char *cert_path,
560251881Speter                      const char **password,
561251881Speter                      apr_pool_t *pool)
562251881Speter{
563251881Speter    svn_ra_serf__connection_t *conn = data;
564251881Speter    svn_ra_serf__session_t *session = conn->session;
565251881Speter    void *creds;
566251881Speter
567251881Speter    *password = NULL;
568251881Speter
569251881Speter    if (!conn->ssl_client_pw_auth_state)
570251881Speter      {
571251881Speter        SVN_ERR(svn_auth_first_credentials(&creds,
572251881Speter                                           &conn->ssl_client_pw_auth_state,
573251881Speter                                           SVN_AUTH_CRED_SSL_CLIENT_CERT_PW,
574251881Speter                                           cert_path,
575251881Speter                                           session->wc_callbacks->auth_baton,
576251881Speter                                           pool));
577251881Speter      }
578251881Speter    else
579251881Speter      {
580251881Speter        SVN_ERR(svn_auth_next_credentials(&creds,
581251881Speter                                          conn->ssl_client_pw_auth_state,
582251881Speter                                          pool));
583251881Speter      }
584251881Speter
585251881Speter    if (creds)
586251881Speter      {
587251881Speter        svn_auth_cred_ssl_client_cert_pw_t *pw_creds;
588251881Speter        pw_creds = creds;
589251881Speter        *password = pw_creds->password;
590251881Speter      }
591251881Speter
592251881Speter    return APR_SUCCESS;
593251881Speter}
594251881Speter
595251881Speter/* Implements serf_ssl_need_client_cert_pw_t for handle_client_cert_pw */
596251881Speterapr_status_t svn_ra_serf__handle_client_cert_pw(void *data,
597251881Speter                                                const char *cert_path,
598251881Speter                                                const char **password)
599251881Speter{
600251881Speter  svn_ra_serf__connection_t *conn = data;
601251881Speter  svn_ra_serf__session_t *session = conn->session;
602251881Speter  svn_error_t *err;
603251881Speter
604251881Speter  err = svn_error_trace(handle_client_cert_pw(data,
605251881Speter                                              cert_path,
606251881Speter                                              password,
607251881Speter                                              session->pool));
608251881Speter
609251881Speter  return save_error(session, err);
610251881Speter}
611251881Speter
612251881Speter
613251881Speter/*
614251881Speter * Given a REQUEST on connection CONN, construct a request bucket for it,
615251881Speter * returning the bucket in *REQ_BKT.
616251881Speter *
617251881Speter * If HDRS_BKT is not-NULL, it will be set to a headers_bucket that
618251881Speter * corresponds to the new request.
619251881Speter *
620251881Speter * The request will be METHOD at URL.
621251881Speter *
622251881Speter * If BODY_BKT is not-NULL, it will be sent as the request body.
623251881Speter *
624251881Speter * If CONTENT_TYPE is not-NULL, it will be sent as the Content-Type header.
625251881Speter *
626251881Speter * REQUEST_POOL should live for the duration of the request. Serf will
627251881Speter * construct this and provide it to the request_setup callback, so we
628251881Speter * should just use that one.
629251881Speter */
630251881Speterstatic svn_error_t *
631251881Spetersetup_serf_req(serf_request_t *request,
632251881Speter               serf_bucket_t **req_bkt,
633251881Speter               serf_bucket_t **hdrs_bkt,
634251881Speter               svn_ra_serf__session_t *session,
635251881Speter               const char *method, const char *url,
636251881Speter               serf_bucket_t *body_bkt, const char *content_type,
637251881Speter               const char *accept_encoding,
638251881Speter               apr_pool_t *request_pool,
639251881Speter               apr_pool_t *scratch_pool)
640251881Speter{
641251881Speter  serf_bucket_alloc_t *allocator = serf_request_get_alloc(request);
642251881Speter
643251881Speter  svn_spillbuf_t *buf;
644253734Speter  svn_boolean_t set_CL = session->http10 || !session->using_chunked_requests;
645251881Speter
646253734Speter  if (set_CL && body_bkt != NULL)
647251881Speter    {
648251881Speter      /* Ugh. Use HTTP/1.0 to talk to the server because we don't know if
649251881Speter         it speaks HTTP/1.1 (and thus, chunked requests), or because the
650251881Speter         server actually responded as only supporting HTTP/1.0.
651251881Speter
652251881Speter         We'll take the existing body_bkt, spool it into a spillbuf, and
653251881Speter         then wrap a bucket around that spillbuf. The spillbuf will give
654251881Speter         us the Content-Length value.  */
655251881Speter      SVN_ERR(svn_ra_serf__copy_into_spillbuf(&buf, body_bkt,
656251881Speter                                              request_pool,
657251881Speter                                              scratch_pool));
658251881Speter      /* Destroy original bucket since it content is already copied
659251881Speter         to spillbuf. */
660251881Speter      serf_bucket_destroy(body_bkt);
661251881Speter
662251881Speter      body_bkt = svn_ra_serf__create_sb_bucket(buf, allocator,
663251881Speter                                               request_pool,
664251881Speter                                               scratch_pool);
665251881Speter    }
666251881Speter
667251881Speter  /* Create a request bucket.  Note that this sucker is kind enough to
668251881Speter     add a "Host" header for us.  */
669251881Speter  *req_bkt = serf_request_bucket_request_create(request, method, url,
670251881Speter                                                body_bkt, allocator);
671251881Speter
672251881Speter  /* Set the Content-Length value. This will also trigger an HTTP/1.0
673251881Speter     request (rather than the default chunked request).  */
674253734Speter  if (set_CL)
675251881Speter    {
676251881Speter      if (body_bkt == NULL)
677251881Speter        serf_bucket_request_set_CL(*req_bkt, 0);
678251881Speter      else
679251881Speter        serf_bucket_request_set_CL(*req_bkt, svn_spillbuf__get_size(buf));
680251881Speter    }
681251881Speter
682251881Speter  *hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
683251881Speter
684251881Speter  /* We use serf_bucket_headers_setn() because the USERAGENT has a
685251881Speter     lifetime longer than this bucket. Thus, there is no need to copy
686251881Speter     the header values.  */
687251881Speter  serf_bucket_headers_setn(*hdrs_bkt, "User-Agent", session->useragent);
688251881Speter
689251881Speter  if (content_type)
690251881Speter    {
691251881Speter      serf_bucket_headers_setn(*hdrs_bkt, "Content-Type", content_type);
692251881Speter    }
693251881Speter
694251881Speter  if (session->http10)
695253734Speter    {
696251881Speter      serf_bucket_headers_setn(*hdrs_bkt, "Connection", "keep-alive");
697253734Speter    }
698251881Speter
699251881Speter  if (accept_encoding)
700251881Speter    {
701251881Speter      serf_bucket_headers_setn(*hdrs_bkt, "Accept-Encoding", accept_encoding);
702251881Speter    }
703251881Speter
704251881Speter  /* These headers need to be sent with every request; see issue #3255
705251881Speter     ("mod_dav_svn does not pass client capabilities to start-commit
706251881Speter     hooks") for why. */
707251881Speter  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_DEPTH);
708251881Speter  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_MERGEINFO);
709251881Speter  serf_bucket_headers_setn(*hdrs_bkt, "DAV", SVN_DAV_NS_DAV_SVN_LOG_REVPROPS);
710251881Speter
711251881Speter  return SVN_NO_ERROR;
712251881Speter}
713251881Speter
714251881Spetersvn_error_t *
715251881Spetersvn_ra_serf__context_run_wait(svn_boolean_t *done,
716251881Speter                              svn_ra_serf__session_t *sess,
717251881Speter                              apr_pool_t *scratch_pool)
718251881Speter{
719251881Speter  apr_pool_t *iterpool;
720251881Speter  apr_interval_time_t waittime_left = sess->timeout;
721251881Speter
722251881Speter  assert(sess->pending_error == SVN_NO_ERROR);
723251881Speter
724251881Speter  iterpool = svn_pool_create(scratch_pool);
725251881Speter  while (!*done)
726251881Speter    {
727251881Speter      apr_status_t status;
728251881Speter      svn_error_t *err;
729251881Speter      int i;
730251881Speter
731251881Speter      svn_pool_clear(iterpool);
732251881Speter
733251881Speter      if (sess->cancel_func)
734251881Speter        SVN_ERR((*sess->cancel_func)(sess->cancel_baton));
735251881Speter
736251881Speter      status = serf_context_run(sess->context,
737251881Speter                                SVN_RA_SERF__CONTEXT_RUN_DURATION,
738251881Speter                                iterpool);
739251881Speter
740251881Speter      err = sess->pending_error;
741251881Speter      sess->pending_error = SVN_NO_ERROR;
742251881Speter
743251881Speter      /* If the context duration timeout is up, we'll subtract that
744251881Speter         duration from the total time alloted for such things.  If
745251881Speter         there's no time left, we fail with a message indicating that
746251881Speter         the connection timed out.  */
747251881Speter      if (APR_STATUS_IS_TIMEUP(status))
748251881Speter        {
749251881Speter          svn_error_clear(err);
750251881Speter          err = SVN_NO_ERROR;
751251881Speter          status = 0;
752251881Speter
753251881Speter          if (sess->timeout)
754251881Speter            {
755251881Speter              if (waittime_left > SVN_RA_SERF__CONTEXT_RUN_DURATION)
756251881Speter                {
757251881Speter                  waittime_left -= SVN_RA_SERF__CONTEXT_RUN_DURATION;
758251881Speter                }
759251881Speter              else
760251881Speter                {
761251881Speter                  return svn_error_create(SVN_ERR_RA_DAV_CONN_TIMEOUT, NULL,
762251881Speter                                          _("Connection timed out"));
763251881Speter                }
764251881Speter            }
765251881Speter        }
766251881Speter      else
767251881Speter        {
768251881Speter          waittime_left = sess->timeout;
769251881Speter        }
770251881Speter
771251881Speter      SVN_ERR(err);
772251881Speter      if (status)
773251881Speter        {
774251881Speter          if (status >= SVN_ERR_BAD_CATEGORY_START && status < SVN_ERR_LAST)
775251881Speter            {
776251881Speter              /* apr can't translate subversion errors to text */
777251881Speter              SVN_ERR_W(svn_error_create(status, NULL, NULL),
778251881Speter                        _("Error running context"));
779251881Speter            }
780251881Speter
781251881Speter          return svn_ra_serf__wrap_err(status, _("Error running context"));
782251881Speter        }
783251881Speter
784251881Speter      /* Debugging purposes only! */
785251881Speter      for (i = 0; i < sess->num_conns; i++)
786251881Speter        {
787251881Speter          serf_debug__closed_conn(sess->conns[i]->bkt_alloc);
788251881Speter        }
789251881Speter    }
790251881Speter  svn_pool_destroy(iterpool);
791251881Speter
792251881Speter  return SVN_NO_ERROR;
793251881Speter}
794251881Speter
795251881Speter
796251881Spetersvn_error_t *
797251881Spetersvn_ra_serf__context_run_one(svn_ra_serf__handler_t *handler,
798251881Speter                             apr_pool_t *scratch_pool)
799251881Speter{
800251881Speter  svn_error_t *err;
801251881Speter
802251881Speter  /* Create a serf request based on HANDLER.  */
803251881Speter  svn_ra_serf__request_create(handler);
804251881Speter
805251881Speter  /* Wait until the response logic marks its DONE status.  */
806251881Speter  err = svn_ra_serf__context_run_wait(&handler->done, handler->session,
807251881Speter                                      scratch_pool);
808251881Speter  if (handler->server_error)
809251881Speter    {
810251881Speter      err = svn_error_compose_create(err, handler->server_error->error);
811251881Speter      handler->server_error = NULL;
812251881Speter    }
813251881Speter
814251881Speter  return svn_error_trace(err);
815251881Speter}
816251881Speter
817251881Speter
818251881Speter/*
819251881Speter * Expat callback invoked on a start element tag for an error response.
820251881Speter */
821251881Speterstatic svn_error_t *
822251881Speterstart_error(svn_ra_serf__xml_parser_t *parser,
823251881Speter            svn_ra_serf__dav_props_t name,
824251881Speter            const char **attrs,
825251881Speter            apr_pool_t *scratch_pool)
826251881Speter{
827251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
828251881Speter
829251881Speter  if (!ctx->in_error &&
830251881Speter      strcmp(name.namespace, "DAV:") == 0 &&
831251881Speter      strcmp(name.name, "error") == 0)
832251881Speter    {
833251881Speter      ctx->in_error = TRUE;
834251881Speter    }
835251881Speter  else if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
836251881Speter    {
837251881Speter      const char *err_code;
838251881Speter
839251881Speter      err_code = svn_xml_get_attr_value("errcode", attrs);
840251881Speter      if (err_code)
841251881Speter        {
842251881Speter          apr_int64_t val;
843251881Speter
844251881Speter          SVN_ERR(svn_cstring_atoi64(&val, err_code));
845251881Speter          ctx->error->apr_err = (apr_status_t)val;
846251881Speter        }
847251881Speter
848251881Speter      /* If there's no error code provided, or if the provided code is
849251881Speter         0 (which can happen sometimes depending on how the error is
850251881Speter         constructed on the server-side), just pick a generic error
851251881Speter         code to run with. */
852251881Speter      if (! ctx->error->apr_err)
853251881Speter        {
854251881Speter          ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
855251881Speter        }
856251881Speter
857251881Speter      /* Start collecting cdata. */
858251881Speter      svn_stringbuf_setempty(ctx->cdata);
859251881Speter      ctx->collect_cdata = TRUE;
860251881Speter    }
861251881Speter
862251881Speter  return SVN_NO_ERROR;
863251881Speter}
864251881Speter
865251881Speter/*
866251881Speter * Expat callback invoked on an end element tag for a PROPFIND response.
867251881Speter */
868251881Speterstatic svn_error_t *
869251881Speterend_error(svn_ra_serf__xml_parser_t *parser,
870251881Speter          svn_ra_serf__dav_props_t name,
871251881Speter          apr_pool_t *scratch_pool)
872251881Speter{
873251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
874251881Speter
875251881Speter  if (ctx->in_error &&
876251881Speter      strcmp(name.namespace, "DAV:") == 0 &&
877251881Speter      strcmp(name.name, "error") == 0)
878251881Speter    {
879251881Speter      ctx->in_error = FALSE;
880251881Speter    }
881251881Speter  if (ctx->in_error && strcmp(name.name, "human-readable") == 0)
882251881Speter    {
883251881Speter      /* On the server dav_error_response_tag() will add a leading
884251881Speter         and trailing newline if DEBUG_CR is defined in mod_dav.h,
885251881Speter         so remove any such characters here. */
886251881Speter      svn_stringbuf_strip_whitespace(ctx->cdata);
887251881Speter
888251881Speter      ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
889251881Speter                                           ctx->cdata->len);
890251881Speter      ctx->collect_cdata = FALSE;
891251881Speter    }
892251881Speter
893251881Speter  return SVN_NO_ERROR;
894251881Speter}
895251881Speter
896251881Speter/*
897251881Speter * Expat callback invoked on CDATA elements in an error response.
898251881Speter *
899251881Speter * This callback can be called multiple times.
900251881Speter */
901251881Speterstatic svn_error_t *
902251881Spetercdata_error(svn_ra_serf__xml_parser_t *parser,
903251881Speter            const char *data,
904251881Speter            apr_size_t len,
905251881Speter            apr_pool_t *scratch_pool)
906251881Speter{
907251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
908251881Speter
909251881Speter  if (ctx->collect_cdata)
910251881Speter    {
911251881Speter      svn_stringbuf_appendbytes(ctx->cdata, data, len);
912251881Speter    }
913251881Speter
914251881Speter  return SVN_NO_ERROR;
915251881Speter}
916251881Speter
917251881Speter
918251881Speterstatic apr_status_t
919251881Speterdrain_bucket(serf_bucket_t *bucket)
920251881Speter{
921251881Speter  /* Read whatever is in the bucket, and just drop it.  */
922251881Speter  while (1)
923251881Speter    {
924251881Speter      apr_status_t status;
925251881Speter      const char *data;
926251881Speter      apr_size_t len;
927251881Speter
928251881Speter      status = serf_bucket_read(bucket, SERF_READ_ALL_AVAIL, &data, &len);
929251881Speter      if (status)
930251881Speter        return status;
931251881Speter    }
932251881Speter}
933251881Speter
934251881Speter
935251881Speterstatic svn_ra_serf__server_error_t *
936251881Speterbegin_error_parsing(svn_ra_serf__xml_start_element_t start,
937251881Speter                    svn_ra_serf__xml_end_element_t end,
938251881Speter                    svn_ra_serf__xml_cdata_chunk_handler_t cdata,
939251881Speter                    apr_pool_t *result_pool)
940251881Speter{
941251881Speter  svn_ra_serf__server_error_t *server_err;
942251881Speter
943251881Speter  server_err = apr_pcalloc(result_pool, sizeof(*server_err));
944251881Speter  server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
945251881Speter  server_err->contains_precondition_error = FALSE;
946251881Speter  server_err->cdata = svn_stringbuf_create_empty(server_err->error->pool);
947251881Speter  server_err->collect_cdata = FALSE;
948251881Speter  server_err->parser.pool = server_err->error->pool;
949251881Speter  server_err->parser.user_data = server_err;
950251881Speter  server_err->parser.start = start;
951251881Speter  server_err->parser.end = end;
952251881Speter  server_err->parser.cdata = cdata;
953251881Speter  server_err->parser.ignore_errors = TRUE;
954251881Speter
955251881Speter  return server_err;
956251881Speter}
957251881Speter
958251881Speter/* Implements svn_ra_serf__response_handler_t */
959251881Spetersvn_error_t *
960251881Spetersvn_ra_serf__handle_discard_body(serf_request_t *request,
961251881Speter                                 serf_bucket_t *response,
962251881Speter                                 void *baton,
963251881Speter                                 apr_pool_t *pool)
964251881Speter{
965251881Speter  apr_status_t status;
966251881Speter
967251881Speter  status = drain_bucket(response);
968251881Speter  if (status)
969251881Speter    return svn_ra_serf__wrap_err(status, NULL);
970251881Speter
971251881Speter  return SVN_NO_ERROR;
972251881Speter}
973251881Speter
974251881Speterapr_status_t
975251881Spetersvn_ra_serf__response_discard_handler(serf_request_t *request,
976251881Speter                                      serf_bucket_t *response,
977251881Speter                                      void *baton,
978251881Speter                                      apr_pool_t *pool)
979251881Speter{
980251881Speter  return drain_bucket(response);
981251881Speter}
982251881Speter
983251881Speter
984251881Speter/* Return the value of the RESPONSE's Location header if any, or NULL
985251881Speter   otherwise.  */
986251881Speterstatic const char *
987251881Speterresponse_get_location(serf_bucket_t *response,
988251881Speter                      const char *base_url,
989251881Speter                      apr_pool_t *result_pool,
990251881Speter                      apr_pool_t *scratch_pool)
991251881Speter{
992251881Speter  serf_bucket_t *headers;
993251881Speter  const char *location;
994251881Speter
995251881Speter  headers = serf_bucket_response_get_headers(response);
996251881Speter  location = serf_bucket_headers_get(headers, "Location");
997251881Speter  if (location == NULL)
998251881Speter    return NULL;
999251881Speter
1000251881Speter  /* The RFCs say we should have received a full url in LOCATION, but
1001251881Speter     older apache versions and many custom web handlers just return a
1002251881Speter     relative path here...
1003251881Speter
1004251881Speter     And we can't trust anything because it is network data.
1005251881Speter   */
1006251881Speter  if (*location == '/')
1007251881Speter    {
1008251881Speter      apr_uri_t uri;
1009251881Speter      apr_status_t status;
1010251881Speter
1011251881Speter      status = apr_uri_parse(scratch_pool, base_url, &uri);
1012251881Speter
1013251881Speter      if (status != APR_SUCCESS)
1014251881Speter        return NULL;
1015251881Speter
1016251881Speter      /* Replace the path path with what we got */
1017251881Speter      uri.path = (char*)svn_urlpath__canonicalize(location, scratch_pool);
1018251881Speter
1019251881Speter      /* And make APR produce a proper full url for us */
1020251881Speter      location = apr_uri_unparse(scratch_pool, &uri, 0);
1021251881Speter
1022251881Speter      /* Fall through to ensure our canonicalization rules */
1023251881Speter    }
1024251881Speter  else if (!svn_path_is_url(location))
1025251881Speter    {
1026251881Speter      return NULL; /* Any other formats we should support? */
1027251881Speter    }
1028251881Speter
1029251881Speter  return svn_uri_canonicalize(location, result_pool);
1030251881Speter}
1031251881Speter
1032251881Speter
1033251881Speter/* Implements svn_ra_serf__response_handler_t */
1034251881Spetersvn_error_t *
1035251881Spetersvn_ra_serf__expect_empty_body(serf_request_t *request,
1036251881Speter                               serf_bucket_t *response,
1037251881Speter                               void *baton,
1038251881Speter                               apr_pool_t *scratch_pool)
1039251881Speter{
1040251881Speter  svn_ra_serf__handler_t *handler = baton;
1041251881Speter  serf_bucket_t *hdrs;
1042251881Speter  const char *val;
1043251881Speter
1044251881Speter  /* This function is just like handle_multistatus_only() except for the
1045251881Speter     XML parsing callbacks. We want to look for the human-readable element.  */
1046251881Speter
1047251881Speter  /* We should see this just once, in order to initialize SERVER_ERROR.
1048251881Speter     At that point, the core error processing will take over. If we choose
1049251881Speter     not to parse an error, then we'll never return here (because we
1050251881Speter     change the response handler).  */
1051251881Speter  SVN_ERR_ASSERT(handler->server_error == NULL);
1052251881Speter
1053251881Speter  hdrs = serf_bucket_response_get_headers(response);
1054251881Speter  val = serf_bucket_headers_get(hdrs, "Content-Type");
1055251881Speter  if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
1056251881Speter    {
1057251881Speter      svn_ra_serf__server_error_t *server_err;
1058251881Speter
1059251881Speter      server_err = begin_error_parsing(start_error, end_error, cdata_error,
1060251881Speter                                       handler->handler_pool);
1061251881Speter
1062251881Speter      /* Get the parser to set our DONE flag.  */
1063251881Speter      server_err->parser.done = &handler->done;
1064251881Speter
1065251881Speter      handler->server_error = server_err;
1066251881Speter    }
1067251881Speter  else
1068251881Speter    {
1069251881Speter      /* The body was not text/xml, so we don't know what to do with it.
1070251881Speter         Toss anything that arrives.  */
1071251881Speter      handler->discard_body = TRUE;
1072251881Speter    }
1073251881Speter
1074251881Speter  /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
1075251881Speter     to call the response handler again. That will start up the XML parsing,
1076251881Speter     or it will be dropped on the floor (per the decision above).  */
1077251881Speter  return SVN_NO_ERROR;
1078251881Speter}
1079251881Speter
1080251881Speter
1081251881Speter/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
1082251881Speter   status code into *STATUS_CODE_OUT.  Ignores leading whitespace. */
1083251881Speterstatic svn_error_t *
1084251881Speterparse_dav_status(int *status_code_out, svn_stringbuf_t *buf,
1085251881Speter                 apr_pool_t *scratch_pool)
1086251881Speter{
1087251881Speter  svn_error_t *err;
1088251881Speter  const char *token;
1089251881Speter  char *tok_status;
1090251881Speter  svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool);
1091251881Speter
1092251881Speter  svn_stringbuf_strip_whitespace(temp_buf);
1093251881Speter  token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
1094251881Speter  if (token)
1095251881Speter    token = apr_strtok(NULL, " \t\r\n", &tok_status);
1096251881Speter  if (!token)
1097251881Speter    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1098251881Speter                             _("Malformed DAV:status CDATA '%s'"),
1099251881Speter                             buf->data);
1100251881Speter  err = svn_cstring_atoi(status_code_out, token);
1101251881Speter  if (err)
1102251881Speter    return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
1103251881Speter                             _("Malformed DAV:status CDATA '%s'"),
1104251881Speter                             buf->data);
1105251881Speter
1106251881Speter  return SVN_NO_ERROR;
1107251881Speter}
1108251881Speter
1109251881Speter/*
1110251881Speter * Expat callback invoked on a start element tag for a 207 response.
1111251881Speter */
1112251881Speterstatic svn_error_t *
1113251881Speterstart_207(svn_ra_serf__xml_parser_t *parser,
1114251881Speter          svn_ra_serf__dav_props_t name,
1115251881Speter          const char **attrs,
1116251881Speter          apr_pool_t *scratch_pool)
1117251881Speter{
1118251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
1119251881Speter
1120251881Speter  if (!ctx->in_error &&
1121251881Speter      strcmp(name.namespace, "DAV:") == 0 &&
1122251881Speter      strcmp(name.name, "multistatus") == 0)
1123251881Speter    {
1124251881Speter      ctx->in_error = TRUE;
1125251881Speter    }
1126251881Speter  else if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
1127251881Speter    {
1128251881Speter      /* Start collecting cdata. */
1129251881Speter      svn_stringbuf_setempty(ctx->cdata);
1130251881Speter      ctx->collect_cdata = TRUE;
1131251881Speter    }
1132251881Speter  else if (ctx->in_error &&
1133251881Speter           strcmp(name.namespace, "DAV:") == 0 &&
1134251881Speter           strcmp(name.name, "status") == 0)
1135251881Speter    {
1136251881Speter      /* Start collecting cdata. */
1137251881Speter      svn_stringbuf_setempty(ctx->cdata);
1138251881Speter      ctx->collect_cdata = TRUE;
1139251881Speter    }
1140251881Speter
1141251881Speter  return SVN_NO_ERROR;
1142251881Speter}
1143251881Speter
1144251881Speter/*
1145251881Speter * Expat callback invoked on an end element tag for a 207 response.
1146251881Speter */
1147251881Speterstatic svn_error_t *
1148251881Speterend_207(svn_ra_serf__xml_parser_t *parser,
1149251881Speter        svn_ra_serf__dav_props_t name,
1150251881Speter        apr_pool_t *scratch_pool)
1151251881Speter{
1152251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
1153251881Speter
1154251881Speter  if (ctx->in_error &&
1155251881Speter      strcmp(name.namespace, "DAV:") == 0 &&
1156251881Speter      strcmp(name.name, "multistatus") == 0)
1157251881Speter    {
1158251881Speter      ctx->in_error = FALSE;
1159251881Speter    }
1160251881Speter  if (ctx->in_error && strcmp(name.name, "responsedescription") == 0)
1161251881Speter    {
1162251881Speter      /* Remove leading newline added by DEBUG_CR on server */
1163251881Speter      svn_stringbuf_strip_whitespace(ctx->cdata);
1164251881Speter
1165251881Speter      ctx->collect_cdata = FALSE;
1166251881Speter      ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
1167251881Speter                                           ctx->cdata->len);
1168251881Speter      if (ctx->contains_precondition_error)
1169251881Speter        ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
1170251881Speter      else
1171251881Speter        ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
1172251881Speter    }
1173251881Speter  else if (ctx->in_error &&
1174251881Speter           strcmp(name.namespace, "DAV:") == 0 &&
1175251881Speter           strcmp(name.name, "status") == 0)
1176251881Speter    {
1177251881Speter      int status_code;
1178251881Speter
1179251881Speter      ctx->collect_cdata = FALSE;
1180251881Speter
1181251881Speter      SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool));
1182251881Speter      if (status_code == 412)
1183251881Speter        ctx->contains_precondition_error = TRUE;
1184251881Speter    }
1185251881Speter
1186251881Speter  return SVN_NO_ERROR;
1187251881Speter}
1188251881Speter
1189251881Speter/*
1190251881Speter * Expat callback invoked on CDATA elements in a 207 response.
1191251881Speter *
1192251881Speter * This callback can be called multiple times.
1193251881Speter */
1194251881Speterstatic svn_error_t *
1195251881Spetercdata_207(svn_ra_serf__xml_parser_t *parser,
1196251881Speter          const char *data,
1197251881Speter          apr_size_t len,
1198251881Speter          apr_pool_t *scratch_pool)
1199251881Speter{
1200251881Speter  svn_ra_serf__server_error_t *ctx = parser->user_data;
1201251881Speter
1202251881Speter  if (ctx->collect_cdata)
1203251881Speter    {
1204251881Speter      svn_stringbuf_appendbytes(ctx->cdata, data, len);
1205251881Speter    }
1206251881Speter
1207251881Speter  return SVN_NO_ERROR;
1208251881Speter}
1209251881Speter
1210251881Speter/* Implements svn_ra_serf__response_handler_t */
1211251881Spetersvn_error_t *
1212251881Spetersvn_ra_serf__handle_multistatus_only(serf_request_t *request,
1213251881Speter                                     serf_bucket_t *response,
1214251881Speter                                     void *baton,
1215251881Speter                                     apr_pool_t *scratch_pool)
1216251881Speter{
1217251881Speter  svn_ra_serf__handler_t *handler = baton;
1218251881Speter
1219251881Speter  /* This function is just like expect_empty_body() except for the
1220251881Speter     XML parsing callbacks. We are looking for very limited pieces of
1221251881Speter     the multistatus response.  */
1222251881Speter
1223251881Speter  /* We should see this just once, in order to initialize SERVER_ERROR.
1224251881Speter     At that point, the core error processing will take over. If we choose
1225251881Speter     not to parse an error, then we'll never return here (because we
1226251881Speter     change the response handler).  */
1227251881Speter  SVN_ERR_ASSERT(handler->server_error == NULL);
1228251881Speter
1229251881Speter    {
1230251881Speter      serf_bucket_t *hdrs;
1231251881Speter      const char *val;
1232251881Speter
1233251881Speter      hdrs = serf_bucket_response_get_headers(response);
1234251881Speter      val = serf_bucket_headers_get(hdrs, "Content-Type");
1235251881Speter      if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
1236251881Speter        {
1237251881Speter          svn_ra_serf__server_error_t *server_err;
1238251881Speter
1239251881Speter          server_err = begin_error_parsing(start_207, end_207, cdata_207,
1240251881Speter                                           handler->handler_pool);
1241251881Speter
1242251881Speter          /* Get the parser to set our DONE flag.  */
1243251881Speter          server_err->parser.done = &handler->done;
1244251881Speter
1245251881Speter          handler->server_error = server_err;
1246251881Speter        }
1247251881Speter      else
1248251881Speter        {
1249251881Speter          /* The body was not text/xml, so we don't know what to do with it.
1250251881Speter             Toss anything that arrives.  */
1251251881Speter          handler->discard_body = TRUE;
1252251881Speter        }
1253251881Speter    }
1254251881Speter
1255251881Speter  /* Returning SVN_NO_ERROR will return APR_SUCCESS to serf, which tells it
1256251881Speter     to call the response handler again. That will start up the XML parsing,
1257251881Speter     or it will be dropped on the floor (per the decision above).  */
1258251881Speter  return SVN_NO_ERROR;
1259251881Speter}
1260251881Speter
1261251881Speter
1262251881Speter/* Conforms to Expat's XML_StartElementHandler  */
1263251881Speterstatic void
1264251881Speterstart_xml(void *userData, const char *raw_name, const char **attrs)
1265251881Speter{
1266251881Speter  svn_ra_serf__xml_parser_t *parser = userData;
1267251881Speter  svn_ra_serf__dav_props_t name;
1268251881Speter  apr_pool_t *scratch_pool;
1269251881Speter  svn_error_t *err;
1270251881Speter
1271251881Speter  if (parser->error)
1272251881Speter    return;
1273251881Speter
1274251881Speter  if (!parser->state)
1275251881Speter    svn_ra_serf__xml_push_state(parser, 0);
1276251881Speter
1277251881Speter  /* ### get a real scratch_pool  */
1278251881Speter  scratch_pool = parser->state->pool;
1279251881Speter
1280251881Speter  svn_ra_serf__define_ns(&parser->state->ns_list, attrs, parser->state->pool);
1281251881Speter
1282251881Speter  svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
1283251881Speter
1284251881Speter  err = parser->start(parser, name, attrs, scratch_pool);
1285251881Speter  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
1286251881Speter    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
1287251881Speter
1288251881Speter  parser->error = err;
1289251881Speter}
1290251881Speter
1291251881Speter
1292251881Speter/* Conforms to Expat's XML_EndElementHandler  */
1293251881Speterstatic void
1294251881Speterend_xml(void *userData, const char *raw_name)
1295251881Speter{
1296251881Speter  svn_ra_serf__xml_parser_t *parser = userData;
1297251881Speter  svn_ra_serf__dav_props_t name;
1298251881Speter  svn_error_t *err;
1299251881Speter  apr_pool_t *scratch_pool;
1300251881Speter
1301251881Speter  if (parser->error)
1302251881Speter    return;
1303251881Speter
1304251881Speter  /* ### get a real scratch_pool  */
1305251881Speter  scratch_pool = parser->state->pool;
1306251881Speter
1307251881Speter  svn_ra_serf__expand_ns(&name, parser->state->ns_list, raw_name);
1308251881Speter
1309251881Speter  err = parser->end(parser, name, scratch_pool);
1310251881Speter  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
1311251881Speter    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
1312251881Speter
1313251881Speter  parser->error = err;
1314251881Speter}
1315251881Speter
1316251881Speter
1317251881Speter/* Conforms to Expat's XML_CharacterDataHandler  */
1318251881Speterstatic void
1319251881Spetercdata_xml(void *userData, const char *data, int len)
1320251881Speter{
1321251881Speter  svn_ra_serf__xml_parser_t *parser = userData;
1322251881Speter  svn_error_t *err;
1323251881Speter  apr_pool_t *scratch_pool;
1324251881Speter
1325251881Speter  if (parser->error)
1326251881Speter    return;
1327251881Speter
1328251881Speter  if (!parser->state)
1329251881Speter    svn_ra_serf__xml_push_state(parser, 0);
1330251881Speter
1331251881Speter  /* ### get a real scratch_pool  */
1332251881Speter  scratch_pool = parser->state->pool;
1333251881Speter
1334251881Speter  err = parser->cdata(parser, data, len, scratch_pool);
1335251881Speter  if (err && !SERF_BUCKET_READ_ERROR(err->apr_err))
1336251881Speter    err = svn_error_create(SVN_ERR_RA_SERF_WRAPPED_ERROR, err, NULL);
1337251881Speter
1338251881Speter  parser->error = err;
1339251881Speter}
1340251881Speter
1341251881Speter/* Flip the requisite bits in CTX to indicate that processing of the
1342251881Speter   response is complete, adding the current "done item" to the list of
1343251881Speter   completed items. */
1344251881Speterstatic void
1345251881Speteradd_done_item(svn_ra_serf__xml_parser_t *ctx)
1346251881Speter{
1347251881Speter  /* Make sure we don't add to DONE_LIST twice.  */
1348251881Speter  if (!*ctx->done)
1349251881Speter    {
1350251881Speter      *ctx->done = TRUE;
1351251881Speter      if (ctx->done_list)
1352251881Speter        {
1353251881Speter          ctx->done_item->data = ctx->user_data;
1354251881Speter          ctx->done_item->next = *ctx->done_list;
1355251881Speter          *ctx->done_list = ctx->done_item;
1356251881Speter        }
1357251881Speter    }
1358251881Speter}
1359251881Speter
1360251881Speter
1361251881Speterstatic svn_error_t *
1362251881Speterwrite_to_pending(svn_ra_serf__xml_parser_t *ctx,
1363251881Speter                 const char *data,
1364251881Speter                 apr_size_t len,
1365251881Speter                 apr_pool_t *scratch_pool)
1366251881Speter{
1367251881Speter  if (ctx->pending == NULL)
1368251881Speter    {
1369251881Speter      ctx->pending = apr_pcalloc(ctx->pool, sizeof(*ctx->pending));
1370251881Speter      ctx->pending->buf = svn_spillbuf__create(PARSE_CHUNK_SIZE,
1371251881Speter                                               SPILL_SIZE,
1372251881Speter                                               ctx->pool);
1373251881Speter    }
1374251881Speter
1375251881Speter  /* Copy the data into one or more chunks in the spill buffer.  */
1376251881Speter  return svn_error_trace(svn_spillbuf__write(ctx->pending->buf,
1377251881Speter                                             data, len,
1378251881Speter                                             scratch_pool));
1379251881Speter}
1380251881Speter
1381251881Speter
1382251881Speterstatic svn_error_t *
1383251881Speterinject_to_parser(svn_ra_serf__xml_parser_t *ctx,
1384251881Speter                 const char *data,
1385251881Speter                 apr_size_t len,
1386251881Speter                 const serf_status_line *sl)
1387251881Speter{
1388251881Speter  int xml_status;
1389251881Speter
1390251881Speter  xml_status = XML_Parse(ctx->xmlp, data, (int) len, 0);
1391251881Speter  if (xml_status == XML_STATUS_ERROR && !ctx->ignore_errors)
1392251881Speter    {
1393251881Speter      if (sl == NULL)
1394251881Speter        return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1395251881Speter                                 _("XML parsing failed"));
1396251881Speter
1397251881Speter      return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
1398251881Speter                               _("XML parsing failed: (%d %s)"),
1399251881Speter                               sl->code, sl->reason);
1400251881Speter    }
1401251881Speter
1402251881Speter  if (ctx->error && !ctx->ignore_errors)
1403251881Speter    return svn_error_trace(ctx->error);
1404251881Speter
1405251881Speter  return SVN_NO_ERROR;
1406251881Speter}
1407251881Speter
1408251881Speter/* Apr pool cleanup handler to release an XML_Parser in success and error
1409251881Speter   conditions */
1410251881Speterstatic apr_status_t
1411251881Speterxml_parser_cleanup(void *baton)
1412251881Speter{
1413251881Speter  XML_Parser *xmlp = baton;
1414251881Speter
1415251881Speter  if (*xmlp)
1416251881Speter    {
1417251881Speter      (void) XML_ParserFree(*xmlp);
1418251881Speter      *xmlp = NULL;
1419251881Speter    }
1420251881Speter
1421251881Speter  return APR_SUCCESS;
1422251881Speter}
1423251881Speter
1424251881Speter/* Limit the amount of pending content to parse at once to < 100KB per
1425251881Speter   iteration. This number is chosen somewhat arbitrarely. Making it lower
1426251881Speter   will have a drastical negative impact on performance, whereas increasing it
1427251881Speter   increases the risk for connection timeouts.
1428251881Speter */
1429251881Speter#define PENDING_TO_PARSE PARSE_CHUNK_SIZE * 5
1430251881Speter
1431251881Spetersvn_error_t *
1432251881Spetersvn_ra_serf__process_pending(svn_ra_serf__xml_parser_t *parser,
1433251881Speter                             svn_boolean_t *network_eof,
1434251881Speter                             apr_pool_t *scratch_pool)
1435251881Speter{
1436251881Speter  svn_boolean_t pending_empty = FALSE;
1437251881Speter  apr_size_t cur_read = 0;
1438251881Speter
1439251881Speter  /* Fast path exit: already paused, nothing to do, or already done.  */
1440251881Speter  if (parser->paused || parser->pending == NULL || *parser->done)
1441251881Speter    {
1442251881Speter      *network_eof = parser->pending ? parser->pending->network_eof : FALSE;
1443251881Speter      return SVN_NO_ERROR;
1444251881Speter    }
1445251881Speter
1446251881Speter  /* Parsing the pending conten in the spillbuf will result in many disc i/o
1447251881Speter     operations. This can be so slow that we don't run the network event
1448251881Speter     processing loop often enough, resulting in timed out connections.
1449251881Speter
1450251881Speter     So we limit the amounts of bytes parsed per iteration.
1451251881Speter   */
1452251881Speter  while (cur_read < PENDING_TO_PARSE)
1453251881Speter    {
1454251881Speter      const char *data;
1455251881Speter      apr_size_t len;
1456251881Speter
1457251881Speter      /* Get a block of content, stopping the loop when we run out.  */
1458251881Speter      SVN_ERR(svn_spillbuf__read(&data, &len, parser->pending->buf,
1459251881Speter                             scratch_pool));
1460251881Speter      if (data)
1461251881Speter        {
1462251881Speter          /* Inject the content into the XML parser.  */
1463251881Speter          SVN_ERR(inject_to_parser(parser, data, len, NULL));
1464251881Speter
1465251881Speter          /* If the XML parsing callbacks paused us, then we're done for now.  */
1466251881Speter          if (parser->paused)
1467251881Speter            break;
1468251881Speter
1469251881Speter          cur_read += len;
1470251881Speter        }
1471251881Speter      else
1472251881Speter        {
1473251881Speter          /* The buffer is empty. */
1474251881Speter          pending_empty = TRUE;
1475251881Speter          break;
1476251881Speter        }
1477251881Speter    }
1478251881Speter
1479251881Speter  /* If the PENDING structures are empty *and* we consumed all content from
1480251881Speter     the network, then we're completely done with the parsing.  */
1481251881Speter  if (pending_empty &&
1482251881Speter      parser->pending->network_eof)
1483251881Speter    {
1484251881Speter      SVN_ERR_ASSERT(parser->xmlp != NULL);
1485251881Speter
1486251881Speter      /* Tell the parser that no more content will be parsed. Ignore the
1487251881Speter         return status. We just don't care.  */
1488251881Speter      (void) XML_Parse(parser->xmlp, NULL, 0, 1);
1489251881Speter
1490251881Speter      apr_pool_cleanup_run(parser->pool, &parser->xmlp, xml_parser_cleanup);
1491251881Speter      parser->xmlp = NULL;
1492251881Speter      add_done_item(parser);
1493251881Speter    }
1494251881Speter
1495251881Speter  *network_eof = parser->pending ? parser->pending->network_eof : FALSE;
1496251881Speter
1497251881Speter  return SVN_NO_ERROR;
1498251881Speter}
1499251881Speter#undef PENDING_TO_PARSE
1500251881Speter
1501251881Speter
1502251881Speter/* ### this is still broken conceptually. just shifting incrementally... */
1503251881Speterstatic svn_error_t *
1504251881Speterhandle_server_error(serf_request_t *request,
1505251881Speter                    serf_bucket_t *response,
1506251881Speter                    apr_pool_t *scratch_pool)
1507251881Speter{
1508251881Speter  svn_ra_serf__server_error_t server_err = { 0 };
1509251881Speter  serf_bucket_t *hdrs;
1510251881Speter  const char *val;
1511251881Speter  apr_status_t err;
1512251881Speter
1513251881Speter  hdrs = serf_bucket_response_get_headers(response);
1514251881Speter  val = serf_bucket_headers_get(hdrs, "Content-Type");
1515251881Speter  if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
1516251881Speter    {
1517251881Speter      /* ### we should figure out how to reuse begin_error_parsing  */
1518251881Speter
1519251881Speter      server_err.error = svn_error_create(APR_SUCCESS, NULL, NULL);
1520251881Speter      server_err.contains_precondition_error = FALSE;
1521251881Speter      server_err.cdata = svn_stringbuf_create_empty(scratch_pool);
1522251881Speter      server_err.collect_cdata = FALSE;
1523251881Speter      server_err.parser.pool = server_err.error->pool;
1524251881Speter      server_err.parser.user_data = &server_err;
1525251881Speter      server_err.parser.start = start_error;
1526251881Speter      server_err.parser.end = end_error;
1527251881Speter      server_err.parser.cdata = cdata_error;
1528251881Speter      server_err.parser.done = &server_err.done;
1529251881Speter      server_err.parser.ignore_errors = TRUE;
1530251881Speter
1531251881Speter      /* We don't care about any errors except for SERVER_ERR.ERROR  */
1532251881Speter      svn_error_clear(svn_ra_serf__handle_xml_parser(request,
1533251881Speter                                                     response,
1534251881Speter                                                     &server_err.parser,
1535251881Speter                                                     scratch_pool));
1536251881Speter
1537251881Speter      /* ### checking DONE is silly. the above only parses whatever has
1538251881Speter         ### been received at the network interface. totally wrong. but
1539251881Speter         ### it is what we have for now (maintaining historical code),
1540251881Speter         ### until we fully migrate.  */
1541251881Speter      if (server_err.done && server_err.error->apr_err == APR_SUCCESS)
1542251881Speter        {
1543251881Speter          svn_error_clear(server_err.error);
1544251881Speter          server_err.error = SVN_NO_ERROR;
1545251881Speter        }
1546251881Speter
1547251881Speter      return svn_error_trace(server_err.error);
1548251881Speter    }
1549251881Speter
1550251881Speter  /* The only error that we will return is from the XML response body.
1551251881Speter     Otherwise, ignore the entire body but allow SUCCESS/EOF/EAGAIN to
1552251881Speter     surface. */
1553251881Speter  err = drain_bucket(response);
1554251881Speter  if (err && !SERF_BUCKET_READ_ERROR(err))
1555251881Speter    return svn_ra_serf__wrap_err(err, NULL);
1556251881Speter
1557251881Speter  return SVN_NO_ERROR;
1558251881Speter}
1559251881Speter
1560251881Speter
1561251881Speter/* Implements svn_ra_serf__response_handler_t */
1562251881Spetersvn_error_t *
1563251881Spetersvn_ra_serf__handle_xml_parser(serf_request_t *request,
1564251881Speter                               serf_bucket_t *response,
1565251881Speter                               void *baton,
1566251881Speter                               apr_pool_t *pool)
1567251881Speter{
1568251881Speter  serf_status_line sl;
1569251881Speter  apr_status_t status;
1570251881Speter  svn_ra_serf__xml_parser_t *ctx = baton;
1571251881Speter  svn_error_t *err;
1572251881Speter
1573251881Speter  /* ### get the HANDLER rather than fetching this.  */
1574251881Speter  status = serf_bucket_response_status(response, &sl);
1575251881Speter  if (SERF_BUCKET_READ_ERROR(status))
1576251881Speter    {
1577251881Speter      return svn_ra_serf__wrap_err(status, NULL);
1578251881Speter    }
1579251881Speter
1580251881Speter  /* Woo-hoo.  Nothing here to see.  */
1581251881Speter  if (sl.code == 404 && !ctx->ignore_errors)
1582251881Speter    {
1583251881Speter      err = handle_server_error(request, response, pool);
1584251881Speter
1585251881Speter      if (err && APR_STATUS_IS_EOF(err->apr_err))
1586251881Speter        add_done_item(ctx);
1587251881Speter
1588251881Speter      return svn_error_trace(err);
1589251881Speter    }
1590251881Speter
1591251881Speter  if (ctx->headers_baton == NULL)
1592251881Speter    ctx->headers_baton = serf_bucket_response_get_headers(response);
1593251881Speter  else if (ctx->headers_baton != serf_bucket_response_get_headers(response))
1594251881Speter    {
1595251881Speter      /* We got a new response to an existing parser...
1596251881Speter         This tells us the connection has restarted and we should continue
1597251881Speter         where we stopped last time.
1598251881Speter       */
1599251881Speter
1600251881Speter      /* Is this a second attempt?? */
1601251881Speter      if (!ctx->skip_size)
1602251881Speter        ctx->skip_size = ctx->read_size;
1603251881Speter
1604251881Speter      ctx->read_size = 0; /* New request, nothing read */
1605251881Speter    }
1606251881Speter
1607251881Speter  if (!ctx->xmlp)
1608251881Speter    {
1609251881Speter      ctx->xmlp = XML_ParserCreate(NULL);
1610251881Speter      apr_pool_cleanup_register(ctx->pool, &ctx->xmlp, xml_parser_cleanup,
1611251881Speter                                apr_pool_cleanup_null);
1612251881Speter      XML_SetUserData(ctx->xmlp, ctx);
1613251881Speter      XML_SetElementHandler(ctx->xmlp, start_xml, end_xml);
1614251881Speter      if (ctx->cdata)
1615251881Speter        {
1616251881Speter          XML_SetCharacterDataHandler(ctx->xmlp, cdata_xml);
1617251881Speter        }
1618251881Speter    }
1619251881Speter
1620251881Speter  while (1)
1621251881Speter    {
1622251881Speter      const char *data;
1623251881Speter      apr_size_t len;
1624251881Speter
1625251881Speter      status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
1626251881Speter
1627251881Speter      if (SERF_BUCKET_READ_ERROR(status))
1628251881Speter        {
1629251881Speter          return svn_ra_serf__wrap_err(status, NULL);
1630251881Speter        }
1631251881Speter
1632251881Speter      ctx->read_size += len;
1633251881Speter
1634251881Speter      if (ctx->skip_size)
1635251881Speter        {
1636251881Speter          /* Handle restarted requests correctly: Skip what we already read */
1637251881Speter          apr_size_t skip;
1638251881Speter
1639251881Speter          if (ctx->skip_size >= ctx->read_size)
1640251881Speter            {
1641251881Speter            /* Eek.  What did the file shrink or something? */
1642251881Speter              if (APR_STATUS_IS_EOF(status))
1643251881Speter                {
1644251881Speter                  SVN_ERR_MALFUNCTION();
1645251881Speter                }
1646251881Speter
1647251881Speter              /* Skip on to the next iteration of this loop. */
1648251881Speter              if (APR_STATUS_IS_EAGAIN(status))
1649251881Speter                {
1650251881Speter                  return svn_ra_serf__wrap_err(status, NULL);
1651251881Speter                }
1652251881Speter              continue;
1653251881Speter            }
1654251881Speter
1655251881Speter          skip = (apr_size_t)(len - (ctx->read_size - ctx->skip_size));
1656251881Speter          data += skip;
1657251881Speter          len -= skip;
1658251881Speter          ctx->skip_size = 0;
1659251881Speter        }
1660251881Speter
1661251881Speter      /* Note: once the callbacks invoked by inject_to_parser() sets the
1662251881Speter         PAUSED flag, then it will not be cleared. write_to_pending() will
1663251881Speter         only save the content. Logic outside of serf_context_run() will
1664251881Speter         clear that flag, as appropriate, along with processing the
1665251881Speter         content that we have placed into the PENDING buffer.
1666251881Speter
1667251881Speter         We want to save arriving content into the PENDING structures if
1668251881Speter         the parser has been paused, or we already have data in there (so
1669251881Speter         the arriving data is appended, rather than injected out of order)  */
1670251881Speter      if (ctx->paused || HAS_PENDING_DATA(ctx->pending))
1671251881Speter        {
1672251881Speter          err = write_to_pending(ctx, data, len, pool);
1673251881Speter        }
1674251881Speter      else
1675251881Speter        {
1676251881Speter          err = inject_to_parser(ctx, data, len, &sl);
1677251881Speter          if (err)
1678251881Speter            {
1679251881Speter              /* Should have no errors if IGNORE_ERRORS is set.  */
1680251881Speter              SVN_ERR_ASSERT(!ctx->ignore_errors);
1681251881Speter            }
1682251881Speter        }
1683251881Speter      if (err)
1684251881Speter        {
1685251881Speter          SVN_ERR_ASSERT(ctx->xmlp != NULL);
1686251881Speter
1687251881Speter          apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
1688251881Speter          add_done_item(ctx);
1689251881Speter          return svn_error_trace(err);
1690251881Speter        }
1691251881Speter
1692251881Speter      if (APR_STATUS_IS_EAGAIN(status))
1693251881Speter        {
1694251881Speter          return svn_ra_serf__wrap_err(status, NULL);
1695251881Speter        }
1696251881Speter
1697251881Speter      if (APR_STATUS_IS_EOF(status))
1698251881Speter        {
1699251881Speter          if (ctx->pending != NULL)
1700251881Speter            ctx->pending->network_eof = TRUE;
1701251881Speter
1702251881Speter          /* We just hit the end of the network content. If we have nothing
1703251881Speter             in the PENDING structures, then we're completely done.  */
1704251881Speter          if (!HAS_PENDING_DATA(ctx->pending))
1705251881Speter            {
1706251881Speter              SVN_ERR_ASSERT(ctx->xmlp != NULL);
1707251881Speter
1708251881Speter              /* Ignore the return status. We just don't care.  */
1709251881Speter              (void) XML_Parse(ctx->xmlp, NULL, 0, 1);
1710251881Speter
1711251881Speter              apr_pool_cleanup_run(ctx->pool, &ctx->xmlp, xml_parser_cleanup);
1712251881Speter              add_done_item(ctx);
1713251881Speter            }
1714251881Speter
1715251881Speter          return svn_ra_serf__wrap_err(status, NULL);
1716251881Speter        }
1717251881Speter
1718251881Speter      /* feed me! */
1719251881Speter    }
1720251881Speter  /* not reached */
1721251881Speter}
1722251881Speter
1723251881Speter
1724251881Speterapr_status_t
1725251881Spetersvn_ra_serf__credentials_callback(char **username, char **password,
1726251881Speter                                  serf_request_t *request, void *baton,
1727251881Speter                                  int code, const char *authn_type,
1728251881Speter                                  const char *realm,
1729251881Speter                                  apr_pool_t *pool)
1730251881Speter{
1731251881Speter  svn_ra_serf__handler_t *handler = baton;
1732251881Speter  svn_ra_serf__session_t *session = handler->session;
1733251881Speter  void *creds;
1734251881Speter  svn_auth_cred_simple_t *simple_creds;
1735251881Speter  svn_error_t *err;
1736251881Speter
1737251881Speter  if (code == 401)
1738251881Speter    {
1739251881Speter      /* Use svn_auth_first_credentials if this is the first time we ask for
1740251881Speter         credentials during this session OR if the last time we asked
1741251881Speter         session->auth_state wasn't set (eg. if the credentials provider was
1742251881Speter         cancelled by the user). */
1743251881Speter      if (!session->auth_state)
1744251881Speter        {
1745251881Speter          err = svn_auth_first_credentials(&creds,
1746251881Speter                                           &session->auth_state,
1747251881Speter                                           SVN_AUTH_CRED_SIMPLE,
1748251881Speter                                           realm,
1749251881Speter                                           session->wc_callbacks->auth_baton,
1750251881Speter                                           session->pool);
1751251881Speter        }
1752251881Speter      else
1753251881Speter        {
1754251881Speter          err = svn_auth_next_credentials(&creds,
1755251881Speter                                          session->auth_state,
1756251881Speter                                          session->pool);
1757251881Speter        }
1758251881Speter
1759251881Speter      if (err)
1760251881Speter        {
1761251881Speter          (void) save_error(session, err);
1762251881Speter          return err->apr_err;
1763251881Speter        }
1764251881Speter
1765251881Speter      session->auth_attempts++;
1766251881Speter
1767251881Speter      if (!creds || session->auth_attempts > 4)
1768251881Speter        {
1769251881Speter          /* No more credentials. */
1770251881Speter          (void) save_error(session,
1771251881Speter                            svn_error_create(
1772251881Speter                              SVN_ERR_AUTHN_FAILED, NULL,
1773251881Speter                              _("No more credentials or we tried too many "
1774251881Speter                                "times.\nAuthentication failed")));
1775251881Speter          return SVN_ERR_AUTHN_FAILED;
1776251881Speter        }
1777251881Speter
1778251881Speter      simple_creds = creds;
1779251881Speter      *username = apr_pstrdup(pool, simple_creds->username);
1780251881Speter      *password = apr_pstrdup(pool, simple_creds->password);
1781251881Speter    }
1782251881Speter  else
1783251881Speter    {
1784251881Speter      *username = apr_pstrdup(pool, session->proxy_username);
1785251881Speter      *password = apr_pstrdup(pool, session->proxy_password);
1786251881Speter
1787251881Speter      session->proxy_auth_attempts++;
1788251881Speter
1789251881Speter      if (!session->proxy_username || session->proxy_auth_attempts > 4)
1790251881Speter        {
1791251881Speter          /* No more credentials. */
1792251881Speter          (void) save_error(session,
1793251881Speter                            svn_error_create(
1794251881Speter                              SVN_ERR_AUTHN_FAILED, NULL,
1795251881Speter                              _("Proxy authentication failed")));
1796251881Speter          return SVN_ERR_AUTHN_FAILED;
1797251881Speter        }
1798251881Speter    }
1799251881Speter
1800251881Speter  handler->conn->last_status_code = code;
1801251881Speter
1802251881Speter  return APR_SUCCESS;
1803251881Speter}
1804251881Speter
1805251881Speter/* Wait for HTTP response status and headers, and invoke HANDLER->
1806251881Speter   response_handler() to carry out operation-specific processing.
1807251881Speter   Afterwards, check for connection close.
1808251881Speter
1809251881Speter   SERF_STATUS allows returning errors to serf without creating a
1810251881Speter   subversion error object.
1811251881Speter   */
1812251881Speterstatic svn_error_t *
1813251881Speterhandle_response(serf_request_t *request,
1814251881Speter                serf_bucket_t *response,
1815251881Speter                svn_ra_serf__handler_t *handler,
1816251881Speter                apr_status_t *serf_status,
1817251881Speter                apr_pool_t *scratch_pool)
1818251881Speter{
1819251881Speter  apr_status_t status;
1820251881Speter  svn_error_t *err;
1821251881Speter
1822251881Speter  /* ### need to verify whether this already gets init'd on every
1823251881Speter     ### successful exit. for an error-exit, it will (properly) be
1824251881Speter     ### ignored by the caller.  */
1825251881Speter  *serf_status = APR_SUCCESS;
1826251881Speter
1827251881Speter  if (!response)
1828251881Speter    {
1829251881Speter      /* Uh-oh. Our connection died.  */
1830251881Speter      if (handler->response_error)
1831251881Speter        SVN_ERR(handler->response_error(request, response, 0,
1832251881Speter                                        handler->response_error_baton));
1833251881Speter
1834251881Speter      /* Requeue another request for this handler.
1835251881Speter         ### how do we know if the handler can deal with this?!  */
1836251881Speter      svn_ra_serf__request_create(handler);
1837251881Speter
1838251881Speter      return SVN_NO_ERROR;
1839251881Speter    }
1840251881Speter
1841251881Speter  /* If we're reading the body, then skip all this preparation.  */
1842251881Speter  if (handler->reading_body)
1843251881Speter    goto process_body;
1844251881Speter
1845251881Speter  /* Copy the Status-Line info into HANDLER, if we don't yet have it.  */
1846251881Speter  if (handler->sline.version == 0)
1847251881Speter    {
1848251881Speter      serf_status_line sl;
1849251881Speter
1850251881Speter      status = serf_bucket_response_status(response, &sl);
1851251881Speter      if (status != APR_SUCCESS)
1852251881Speter        {
1853251881Speter          /* The response line is not (yet) ready, or some other error.  */
1854251881Speter          *serf_status = status;
1855251881Speter          return SVN_NO_ERROR; /* Handled by serf */
1856251881Speter        }
1857251881Speter
1858251881Speter      /* If we got APR_SUCCESS, then we should have Status-Line info.  */
1859251881Speter      SVN_ERR_ASSERT(sl.version != 0);
1860251881Speter
1861251881Speter      handler->sline = sl;
1862251881Speter      handler->sline.reason = apr_pstrdup(handler->handler_pool, sl.reason);
1863251881Speter
1864251881Speter      /* HTTP/1.1? (or later)  */
1865251881Speter      if (sl.version != SERF_HTTP_10)
1866251881Speter        handler->session->http10 = FALSE;
1867251881Speter    }
1868251881Speter
1869251881Speter  /* Keep reading from the network until we've read all the headers.  */
1870251881Speter  status = serf_bucket_response_wait_for_headers(response);
1871251881Speter  if (status)
1872251881Speter    {
1873251881Speter      /* The typical "error" will be APR_EAGAIN, meaning that more input
1874251881Speter         from the network is required to complete the reading of the
1875251881Speter         headers.  */
1876251881Speter      if (!APR_STATUS_IS_EOF(status))
1877251881Speter        {
1878251881Speter          /* Either the headers are not (yet) complete, or there really
1879251881Speter             was an error.  */
1880251881Speter          *serf_status = status;
1881251881Speter          return SVN_NO_ERROR;
1882251881Speter        }
1883251881Speter
1884251881Speter      /* wait_for_headers() will return EOF if there is no body in this
1885251881Speter         response, or if we completely read the body. The latter is not
1886251881Speter         true since we would have set READING_BODY to get the body read,
1887251881Speter         and we would not be back to this code block.
1888251881Speter
1889251881Speter         It can also return EOF if we truly hit EOF while (say) processing
1890251881Speter         the headers. aka Badness.  */
1891251881Speter
1892251881Speter      /* Cases where a lack of a response body (via EOF) is okay:
1893251881Speter       *  - A HEAD request
1894251881Speter       *  - 204/304 response
1895251881Speter       *
1896251881Speter       * Otherwise, if we get an EOF here, something went really wrong: either
1897251881Speter       * the server closed on us early or we're reading too much.  Either way,
1898251881Speter       * scream loudly.
1899251881Speter       */
1900251881Speter      if (strcmp(handler->method, "HEAD") != 0
1901251881Speter          && handler->sline.code != 204
1902251881Speter          && handler->sline.code != 304)
1903251881Speter        {
1904251881Speter          err = svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA,
1905251881Speter                                  svn_ra_serf__wrap_err(status, NULL),
1906251881Speter                                  _("Premature EOF seen from server"
1907251881Speter                                    " (http status=%d)"),
1908251881Speter                                  handler->sline.code);
1909251881Speter
1910251881Speter          /* In case anything else arrives... discard it.  */
1911251881Speter          handler->discard_body = TRUE;
1912251881Speter
1913251881Speter          return err;
1914251881Speter        }
1915251881Speter    }
1916251881Speter
1917251881Speter  /* ... and set up the header fields in HANDLER.  */
1918251881Speter  handler->location = response_get_location(response,
1919251881Speter                                            handler->session->session_url_str,
1920251881Speter                                            handler->handler_pool,
1921251881Speter                                            scratch_pool);
1922251881Speter
1923251881Speter  /* On the last request, we failed authentication. We succeeded this time,
1924251881Speter     so let's save away these credentials.  */
1925251881Speter  if (handler->conn->last_status_code == 401 && handler->sline.code < 400)
1926251881Speter    {
1927251881Speter      SVN_ERR(svn_auth_save_credentials(handler->session->auth_state,
1928251881Speter                                        handler->session->pool));
1929251881Speter      handler->session->auth_attempts = 0;
1930251881Speter      handler->session->auth_state = NULL;
1931251881Speter    }
1932251881Speter  handler->conn->last_status_code = handler->sline.code;
1933251881Speter
1934251881Speter  if (handler->sline.code == 405
1935251881Speter      || handler->sline.code == 408
1936251881Speter      || handler->sline.code == 409
1937251881Speter      || handler->sline.code >= 500)
1938251881Speter    {
1939251881Speter      /* 405 Method Not allowed.
1940251881Speter         408 Request Timeout
1941251881Speter         409 Conflict: can indicate a hook error.
1942251881Speter         5xx (Internal) Server error. */
1943251881Speter      serf_bucket_t *hdrs;
1944251881Speter      const char *val;
1945251881Speter
1946251881Speter      hdrs = serf_bucket_response_get_headers(response);
1947251881Speter      val = serf_bucket_headers_get(hdrs, "Content-Type");
1948251881Speter      if (val && strncasecmp(val, "text/xml", sizeof("text/xml") - 1) == 0)
1949251881Speter        {
1950251881Speter          svn_ra_serf__server_error_t *server_err;
1951251881Speter
1952251881Speter          server_err = begin_error_parsing(start_error, end_error, cdata_error,
1953251881Speter                                           handler->handler_pool);
1954251881Speter          /* Get the parser to set our DONE flag.  */
1955251881Speter          server_err->parser.done = &handler->done;
1956251881Speter
1957251881Speter          handler->server_error = server_err;
1958251881Speter        }
1959251881Speter      else
1960251881Speter        {
1961251881Speter          handler->discard_body = TRUE;
1962251881Speter
1963251881Speter          if (!handler->session->pending_error)
1964251881Speter            {
1965251881Speter              apr_status_t apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
1966251881Speter
1967251881Speter              /* 405 == Method Not Allowed (Occurs when trying to lock a working
1968251881Speter                copy path which no longer exists at HEAD in the repository. */
1969251881Speter              if (handler->sline.code == 405
1970251881Speter                  && strcmp(handler->method, "LOCK") == 0)
1971251881Speter                apr_err = SVN_ERR_FS_OUT_OF_DATE;
1972251881Speter
1973251881Speter              handler->session->pending_error =
1974251881Speter                  svn_error_createf(apr_err, NULL,
1975251881Speter                                    _("%s request on '%s' failed: %d %s"),
1976251881Speter                                   handler->method, handler->path,
1977251881Speter                                   handler->sline.code, handler->sline.reason);
1978251881Speter            }
1979251881Speter        }
1980251881Speter    }
1981251881Speter
1982251881Speter  /* Stop processing the above, on every packet arrival.  */
1983251881Speter  handler->reading_body = TRUE;
1984251881Speter
1985251881Speter process_body:
1986251881Speter
1987251881Speter  /* We've been instructed to ignore the body. Drain whatever is present.  */
1988251881Speter  if (handler->discard_body)
1989251881Speter    {
1990251881Speter      *serf_status = drain_bucket(response);
1991251881Speter
1992251881Speter      /* If the handler hasn't set done (which it shouldn't have) and
1993251881Speter         we now have the EOF, go ahead and set it so that we can stop
1994251881Speter         our context loops.
1995251881Speter       */
1996251881Speter      if (!handler->done && APR_STATUS_IS_EOF(*serf_status))
1997251881Speter          handler->done = TRUE;
1998251881Speter
1999251881Speter      return SVN_NO_ERROR;
2000251881Speter    }
2001251881Speter
2002251881Speter  /* If we are supposed to parse the body as a server_error, then do
2003251881Speter     that now.  */
2004251881Speter  if (handler->server_error != NULL)
2005251881Speter    {
2006251881Speter      err = svn_ra_serf__handle_xml_parser(request, response,
2007251881Speter                                           &handler->server_error->parser,
2008251881Speter                                           scratch_pool);
2009251881Speter
2010251881Speter      /* If we do not receive an error or it is a non-transient error, return
2011251881Speter         immediately.
2012251881Speter
2013251881Speter         APR_EOF will be returned when parsing is complete.
2014251881Speter
2015251881Speter         APR_EAGAIN & WAIT_CONN may be intermittently returned as we proceed through
2016251881Speter         parsing and the network has no more data right now.  If we receive that,
2017251881Speter         clear the error and return - allowing serf to wait for more data.
2018251881Speter         */
2019251881Speter      if (!err || SERF_BUCKET_READ_ERROR(err->apr_err))
2020251881Speter        return svn_error_trace(err);
2021251881Speter
2022251881Speter      if (!APR_STATUS_IS_EOF(err->apr_err))
2023251881Speter        {
2024251881Speter          *serf_status = err->apr_err;
2025251881Speter          svn_error_clear(err);
2026251881Speter          return SVN_NO_ERROR;
2027251881Speter        }
2028251881Speter
2029251881Speter      /* Clear the EOF. We don't need it.  */
2030251881Speter      svn_error_clear(err);
2031251881Speter
2032251881Speter      /* If the parsing is done, and we did not extract an error, then
2033251881Speter         simply toss everything, and anything else that might arrive.
2034251881Speter         The higher-level code will need to investigate HANDLER->SLINE,
2035251881Speter         as we have no further information for them.  */
2036251881Speter      if (handler->done
2037251881Speter          && handler->server_error->error->apr_err == APR_SUCCESS)
2038251881Speter        {
2039251881Speter          svn_error_clear(handler->server_error->error);
2040251881Speter
2041251881Speter          /* Stop parsing for a server error.  */
2042251881Speter          handler->server_error = NULL;
2043251881Speter
2044251881Speter          /* If anything arrives after this, then just discard it.  */
2045251881Speter          handler->discard_body = TRUE;
2046251881Speter        }
2047251881Speter
2048251881Speter      *serf_status = APR_EOF;
2049251881Speter      return SVN_NO_ERROR;
2050251881Speter    }
2051251881Speter
2052251881Speter  /* Pass the body along to the registered response handler.  */
2053251881Speter  err = handler->response_handler(request, response,
2054251881Speter                                  handler->response_baton,
2055251881Speter                                  scratch_pool);
2056251881Speter
2057251881Speter  if (err
2058251881Speter      && (!SERF_BUCKET_READ_ERROR(err->apr_err)
2059251881Speter          || APR_STATUS_IS_ECONNRESET(err->apr_err)
2060251881Speter          || APR_STATUS_IS_ECONNABORTED(err->apr_err)))
2061251881Speter    {
2062251881Speter      /* These errors are special cased in serf
2063251881Speter         ### We hope no handler returns these by accident. */
2064251881Speter      *serf_status = err->apr_err;
2065251881Speter      svn_error_clear(err);
2066251881Speter      return SVN_NO_ERROR;
2067251881Speter    }
2068251881Speter
2069251881Speter  return svn_error_trace(err);
2070251881Speter}
2071251881Speter
2072251881Speter
2073251881Speter/* Implements serf_response_handler_t for handle_response. Storing
2074251881Speter   errors in handler->session->pending_error if appropriate. */
2075251881Speterstatic apr_status_t
2076251881Speterhandle_response_cb(serf_request_t *request,
2077251881Speter                   serf_bucket_t *response,
2078251881Speter                   void *baton,
2079251881Speter                   apr_pool_t *scratch_pool)
2080251881Speter{
2081251881Speter  svn_ra_serf__handler_t *handler = baton;
2082251881Speter  svn_error_t *err;
2083251881Speter  apr_status_t inner_status;
2084251881Speter  apr_status_t outer_status;
2085251881Speter
2086251881Speter  err = svn_error_trace(handle_response(request, response,
2087251881Speter                                        handler, &inner_status,
2088251881Speter                                        scratch_pool));
2089251881Speter
2090251881Speter  /* Select the right status value to return.  */
2091251881Speter  outer_status = save_error(handler->session, err);
2092251881Speter  if (!outer_status)
2093251881Speter    outer_status = inner_status;
2094251881Speter
2095251881Speter  /* Make sure the DONE flag is set properly.  */
2096251881Speter  if (APR_STATUS_IS_EOF(outer_status) || APR_STATUS_IS_EOF(inner_status))
2097251881Speter    handler->done = TRUE;
2098251881Speter
2099251881Speter  return outer_status;
2100251881Speter}
2101251881Speter
2102251881Speter/* Perform basic request setup, with special handling for HEAD requests,
2103251881Speter   and finer-grained callbacks invoked (if non-NULL) to produce the request
2104251881Speter   headers and body. */
2105251881Speterstatic svn_error_t *
2106251881Spetersetup_request(serf_request_t *request,
2107251881Speter              svn_ra_serf__handler_t *handler,
2108251881Speter              serf_bucket_t **req_bkt,
2109251881Speter              apr_pool_t *request_pool,
2110251881Speter              apr_pool_t *scratch_pool)
2111251881Speter{
2112251881Speter  serf_bucket_t *body_bkt;
2113251881Speter  serf_bucket_t *headers_bkt;
2114251881Speter  const char *accept_encoding;
2115251881Speter
2116251881Speter  if (handler->body_delegate)
2117251881Speter    {
2118251881Speter      serf_bucket_alloc_t *bkt_alloc = serf_request_get_alloc(request);
2119251881Speter
2120251881Speter      /* ### should pass the scratch_pool  */
2121251881Speter      SVN_ERR(handler->body_delegate(&body_bkt, handler->body_delegate_baton,
2122251881Speter                                     bkt_alloc, request_pool));
2123251881Speter    }
2124251881Speter  else
2125251881Speter    {
2126251881Speter      body_bkt = NULL;
2127251881Speter    }
2128251881Speter
2129251881Speter  if (handler->custom_accept_encoding)
2130251881Speter    {
2131251881Speter      accept_encoding = NULL;
2132251881Speter    }
2133251881Speter  else if (handler->session->using_compression)
2134251881Speter    {
2135251881Speter      /* Accept gzip compression if enabled. */
2136251881Speter      accept_encoding = "gzip";
2137251881Speter    }
2138251881Speter  else
2139251881Speter    {
2140251881Speter      accept_encoding = NULL;
2141251881Speter    }
2142251881Speter
2143251881Speter  SVN_ERR(setup_serf_req(request, req_bkt, &headers_bkt,
2144251881Speter                         handler->session, handler->method, handler->path,
2145251881Speter                         body_bkt, handler->body_type, accept_encoding,
2146251881Speter                         request_pool, scratch_pool));
2147251881Speter
2148251881Speter  if (handler->header_delegate)
2149251881Speter    {
2150251881Speter      /* ### should pass the scratch_pool  */
2151251881Speter      SVN_ERR(handler->header_delegate(headers_bkt,
2152251881Speter                                       handler->header_delegate_baton,
2153251881Speter                                       request_pool));
2154251881Speter    }
2155251881Speter
2156251881Speter  return APR_SUCCESS;
2157251881Speter}
2158251881Speter
2159251881Speter/* Implements the serf_request_setup_t interface (which sets up both a
2160251881Speter   request and its response handler callback). Handles errors for
2161251881Speter   setup_request_cb */
2162251881Speterstatic apr_status_t
2163251881Spetersetup_request_cb(serf_request_t *request,
2164251881Speter              void *setup_baton,
2165251881Speter              serf_bucket_t **req_bkt,
2166251881Speter              serf_response_acceptor_t *acceptor,
2167251881Speter              void **acceptor_baton,
2168251881Speter              serf_response_handler_t *s_handler,
2169251881Speter              void **s_handler_baton,
2170251881Speter              apr_pool_t *pool)
2171251881Speter{
2172251881Speter  svn_ra_serf__handler_t *handler = setup_baton;
2173251881Speter  svn_error_t *err;
2174251881Speter
2175251881Speter  /* ### construct a scratch_pool? serf gives us a pool that will live for
2176251881Speter     ### the duration of the request.  */
2177251881Speter  apr_pool_t *scratch_pool = pool;
2178251881Speter
2179251881Speter  if (strcmp(handler->method, "HEAD") == 0)
2180251881Speter    *acceptor = accept_head;
2181251881Speter  else
2182251881Speter    *acceptor = accept_response;
2183251881Speter  *acceptor_baton = handler->session;
2184251881Speter
2185251881Speter  *s_handler = handle_response_cb;
2186251881Speter  *s_handler_baton = handler;
2187251881Speter
2188251881Speter  err = svn_error_trace(setup_request(request, handler, req_bkt,
2189251881Speter                                      pool /* request_pool */, scratch_pool));
2190251881Speter
2191251881Speter  return save_error(handler->session, err);
2192251881Speter}
2193251881Speter
2194251881Spetervoid
2195251881Spetersvn_ra_serf__request_create(svn_ra_serf__handler_t *handler)
2196251881Speter{
2197251881Speter  SVN_ERR_ASSERT_NO_RETURN(handler->handler_pool != NULL);
2198251881Speter
2199251881Speter  /* In case HANDLER is re-queued, reset the various transient fields.
2200251881Speter
2201251881Speter     ### prior to recent changes, HANDLER was constant. maybe we should
2202251881Speter     ### break out these processing fields, apart from the request
2203251881Speter     ### definition.  */
2204251881Speter  handler->done = FALSE;
2205251881Speter  handler->server_error = NULL;
2206251881Speter  handler->sline.version = 0;
2207251881Speter  handler->location = NULL;
2208251881Speter  handler->reading_body = FALSE;
2209251881Speter  handler->discard_body = FALSE;
2210251881Speter
2211251881Speter  /* ### do we ever alter the >response_handler?  */
2212251881Speter
2213251881Speter  /* ### do we need to hold onto the returned request object, or just
2214251881Speter     ### not worry about it (the serf ctx will manage it).  */
2215251881Speter  (void) serf_connection_request_create(handler->conn->conn,
2216251881Speter                                        setup_request_cb, handler);
2217251881Speter}
2218251881Speter
2219251881Speter
2220251881Spetersvn_error_t *
2221251881Spetersvn_ra_serf__discover_vcc(const char **vcc_url,
2222251881Speter                          svn_ra_serf__session_t *session,
2223251881Speter                          svn_ra_serf__connection_t *conn,
2224251881Speter                          apr_pool_t *pool)
2225251881Speter{
2226251881Speter  const char *path;
2227251881Speter  const char *relative_path;
2228251881Speter  const char *uuid;
2229251881Speter
2230251881Speter  /* If we've already got the information our caller seeks, just return it.  */
2231251881Speter  if (session->vcc_url && session->repos_root_str)
2232251881Speter    {
2233251881Speter      *vcc_url = session->vcc_url;
2234251881Speter      return SVN_NO_ERROR;
2235251881Speter    }
2236251881Speter
2237251881Speter  /* If no connection is provided, use the default one. */
2238251881Speter  if (! conn)
2239251881Speter    {
2240251881Speter      conn = session->conns[0];
2241251881Speter    }
2242251881Speter
2243251881Speter  path = session->session_url.path;
2244251881Speter  *vcc_url = NULL;
2245251881Speter  uuid = NULL;
2246251881Speter
2247251881Speter  do
2248251881Speter    {
2249251881Speter      apr_hash_t *props;
2250251881Speter      svn_error_t *err;
2251251881Speter
2252251881Speter      err = svn_ra_serf__fetch_node_props(&props, conn,
2253251881Speter                                          path, SVN_INVALID_REVNUM,
2254251881Speter                                          base_props, pool, pool);
2255251881Speter      if (! err)
2256251881Speter        {
2257251881Speter          apr_hash_t *ns_props;
2258251881Speter
2259251881Speter          ns_props = apr_hash_get(props, "DAV:", 4);
2260251881Speter          *vcc_url = svn_prop_get_value(ns_props,
2261251881Speter                                        "version-controlled-configuration");
2262251881Speter
2263251881Speter          ns_props = svn_hash_gets(props, SVN_DAV_PROP_NS_DAV);
2264251881Speter          relative_path = svn_prop_get_value(ns_props,
2265251881Speter                                             "baseline-relative-path");
2266251881Speter          uuid = svn_prop_get_value(ns_props, "repository-uuid");
2267251881Speter          break;
2268251881Speter        }
2269251881Speter      else
2270251881Speter        {
2271251881Speter          if ((err->apr_err != SVN_ERR_FS_NOT_FOUND) &&
2272251881Speter              (err->apr_err != SVN_ERR_RA_DAV_FORBIDDEN))
2273251881Speter            {
2274251881Speter              return svn_error_trace(err);  /* found a _real_ error */
2275251881Speter            }
2276251881Speter          else
2277251881Speter            {
2278251881Speter              /* This happens when the file is missing in HEAD. */
2279251881Speter              svn_error_clear(err);
2280251881Speter
2281251881Speter              /* Okay, strip off a component from PATH. */
2282251881Speter              path = svn_urlpath__dirname(path, pool);
2283251881Speter
2284251881Speter              /* An error occurred on conns. serf 0.4.0 remembers that
2285251881Speter                 the connection had a problem. We need to reset it, in
2286251881Speter                 order to use it again.  */
2287251881Speter              serf_connection_reset(conn->conn);
2288251881Speter            }
2289251881Speter        }
2290251881Speter    }
2291251881Speter  while ((path[0] != '\0')
2292251881Speter         && (! (path[0] == '/' && path[1] == '\0')));
2293251881Speter
2294251881Speter  if (!*vcc_url)
2295251881Speter    {
2296251881Speter      return svn_error_create(SVN_ERR_RA_DAV_OPTIONS_REQ_FAILED, NULL,
2297251881Speter                              _("The PROPFIND response did not include the "
2298251881Speter                                "requested version-controlled-configuration "
2299251881Speter                                "value"));
2300251881Speter    }
2301251881Speter
2302251881Speter  /* Store our VCC in our cache. */
2303251881Speter  if (!session->vcc_url)
2304251881Speter    {
2305251881Speter      session->vcc_url = apr_pstrdup(session->pool, *vcc_url);
2306251881Speter    }
2307251881Speter
2308251881Speter  /* Update our cached repository root URL. */
2309251881Speter  if (!session->repos_root_str)
2310251881Speter    {
2311251881Speter      svn_stringbuf_t *url_buf;
2312251881Speter
2313251881Speter      url_buf = svn_stringbuf_create(path, pool);
2314251881Speter
2315251881Speter      svn_path_remove_components(url_buf,
2316251881Speter                                 svn_path_component_count(relative_path));
2317251881Speter
2318251881Speter      /* Now recreate the root_url. */
2319251881Speter      session->repos_root = session->session_url;
2320251881Speter      session->repos_root.path =
2321251881Speter        (char *)svn_fspath__canonicalize(url_buf->data, session->pool);
2322251881Speter      session->repos_root_str =
2323251881Speter        svn_urlpath__canonicalize(apr_uri_unparse(session->pool,
2324251881Speter                                                  &session->repos_root, 0),
2325251881Speter                                  session->pool);
2326251881Speter    }
2327251881Speter
2328251881Speter  /* Store the repository UUID in the cache. */
2329251881Speter  if (!session->uuid)
2330251881Speter    {
2331251881Speter      session->uuid = apr_pstrdup(session->pool, uuid);
2332251881Speter    }
2333251881Speter
2334251881Speter  return SVN_NO_ERROR;
2335251881Speter}
2336251881Speter
2337251881Spetersvn_error_t *
2338251881Spetersvn_ra_serf__get_relative_path(const char **rel_path,
2339251881Speter                               const char *orig_path,
2340251881Speter                               svn_ra_serf__session_t *session,
2341251881Speter                               svn_ra_serf__connection_t *conn,
2342251881Speter                               apr_pool_t *pool)
2343251881Speter{
2344251881Speter  const char *decoded_root, *decoded_orig;
2345251881Speter
2346251881Speter  if (! session->repos_root.path)
2347251881Speter    {
2348251881Speter      const char *vcc_url;
2349251881Speter
2350251881Speter      /* This should only happen if we haven't detected HTTP v2
2351251881Speter         support from the server.  */
2352251881Speter      assert(! SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session));
2353251881Speter
2354251881Speter      /* We don't actually care about the VCC_URL, but this API
2355251881Speter         promises to populate the session's root-url cache, and that's
2356251881Speter         what we really want. */
2357251881Speter      SVN_ERR(svn_ra_serf__discover_vcc(&vcc_url, session,
2358251881Speter                                        conn ? conn : session->conns[0],
2359251881Speter                                        pool));
2360251881Speter    }
2361251881Speter
2362251881Speter  decoded_root = svn_path_uri_decode(session->repos_root.path, pool);
2363251881Speter  decoded_orig = svn_path_uri_decode(orig_path, pool);
2364251881Speter  *rel_path = svn_urlpath__skip_ancestor(decoded_root, decoded_orig);
2365251881Speter  SVN_ERR_ASSERT(*rel_path != NULL);
2366251881Speter  return SVN_NO_ERROR;
2367251881Speter}
2368251881Speter
2369251881Spetersvn_error_t *
2370251881Spetersvn_ra_serf__report_resource(const char **report_target,
2371251881Speter                             svn_ra_serf__session_t *session,
2372251881Speter                             svn_ra_serf__connection_t *conn,
2373251881Speter                             apr_pool_t *pool)
2374251881Speter{
2375251881Speter  /* If we have HTTP v2 support, we want to report against the 'me'
2376251881Speter     resource. */
2377251881Speter  if (SVN_RA_SERF__HAVE_HTTPV2_SUPPORT(session))
2378251881Speter    *report_target = apr_pstrdup(pool, session->me_resource);
2379251881Speter
2380251881Speter  /* Otherwise, we'll use the default VCC. */
2381251881Speter  else
2382251881Speter    SVN_ERR(svn_ra_serf__discover_vcc(report_target, session, conn, pool));
2383251881Speter
2384251881Speter  return SVN_NO_ERROR;
2385251881Speter}
2386251881Speter
2387251881Spetersvn_error_t *
2388253734Spetersvn_ra_serf__error_on_status(serf_status_line sline,
2389251881Speter                             const char *path,
2390251881Speter                             const char *location)
2391251881Speter{
2392253734Speter  switch(sline.code)
2393251881Speter    {
2394251881Speter      case 301:
2395251881Speter      case 302:
2396251881Speter      case 307:
2397251881Speter        return svn_error_createf(SVN_ERR_RA_DAV_RELOCATED, NULL,
2398253734Speter                                 (sline.code == 301)
2399251881Speter                                 ? _("Repository moved permanently to '%s';"
2400251881Speter                                     " please relocate")
2401251881Speter                                 : _("Repository moved temporarily to '%s';"
2402251881Speter                                     " please relocate"), location);
2403251881Speter      case 403:
2404251881Speter        return svn_error_createf(SVN_ERR_RA_DAV_FORBIDDEN, NULL,
2405251881Speter                                 _("Access to '%s' forbidden"), path);
2406251881Speter
2407251881Speter      case 404:
2408251881Speter        return svn_error_createf(SVN_ERR_FS_NOT_FOUND, NULL,
2409251881Speter                                 _("'%s' path not found"), path);
2410251881Speter      case 423:
2411251881Speter        return svn_error_createf(SVN_ERR_FS_NO_LOCK_TOKEN, NULL,
2412251881Speter                                 _("'%s': no lock token available"), path);
2413253734Speter
2414253734Speter      case 411:
2415253734Speter        return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
2416253734Speter                    _("DAV request failed: 411 Content length required. The "
2417253734Speter                      "server or an intermediate proxy does not accept "
2418253734Speter                      "chunked encoding. Try setting 'http-chunked-requests' "
2419253734Speter                      "to 'auto' or 'no' in your client configuration."));
2420251881Speter    }
2421251881Speter
2422253734Speter  if (sline.code >= 300)
2423253734Speter    return svn_error_createf(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
2424253734Speter                             _("Unexpected HTTP status %d '%s' on '%s'\n"),
2425253734Speter                             sline.code, sline.reason, path);
2426253734Speter
2427251881Speter  return SVN_NO_ERROR;
2428251881Speter}
2429251881Speter
2430251881Spetersvn_error_t *
2431251881Spetersvn_ra_serf__register_editor_shim_callbacks(svn_ra_session_t *ra_session,
2432251881Speter                                    svn_delta_shim_callbacks_t *callbacks)
2433251881Speter{
2434251881Speter  svn_ra_serf__session_t *session = ra_session->priv;
2435251881Speter
2436251881Speter  session->shim_callbacks = callbacks;
2437251881Speter  return SVN_NO_ERROR;
2438251881Speter}
2439251881Speter
2440251881Speter
2441251881Speter/* Conforms to Expat's XML_StartElementHandler  */
2442251881Speterstatic void
2443251881Speterexpat_start(void *userData, const char *raw_name, const char **attrs)
2444251881Speter{
2445251881Speter  struct expat_ctx_t *ectx = userData;
2446251881Speter
2447251881Speter  if (ectx->inner_error != NULL)
2448251881Speter    return;
2449251881Speter
2450251881Speter  ectx->inner_error = svn_error_trace(
2451251881Speter                        svn_ra_serf__xml_cb_start(ectx->xmlctx,
2452251881Speter                                                  raw_name, attrs));
2453251881Speter
2454251881Speter#ifdef EXPAT_HAS_STOPPARSER
2455251881Speter  if (ectx->inner_error)
2456251881Speter    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
2457251881Speter#endif
2458251881Speter}
2459251881Speter
2460251881Speter
2461251881Speter/* Conforms to Expat's XML_EndElementHandler  */
2462251881Speterstatic void
2463251881Speterexpat_end(void *userData, const char *raw_name)
2464251881Speter{
2465251881Speter  struct expat_ctx_t *ectx = userData;
2466251881Speter
2467251881Speter  if (ectx->inner_error != NULL)
2468251881Speter    return;
2469251881Speter
2470251881Speter  ectx->inner_error = svn_error_trace(
2471251881Speter                        svn_ra_serf__xml_cb_end(ectx->xmlctx, raw_name));
2472251881Speter
2473251881Speter#ifdef EXPAT_HAS_STOPPARSER
2474251881Speter  if (ectx->inner_error)
2475251881Speter    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
2476251881Speter#endif
2477251881Speter}
2478251881Speter
2479251881Speter
2480251881Speter/* Conforms to Expat's XML_CharacterDataHandler  */
2481251881Speterstatic void
2482251881Speterexpat_cdata(void *userData, const char *data, int len)
2483251881Speter{
2484251881Speter  struct expat_ctx_t *ectx = userData;
2485251881Speter
2486251881Speter  if (ectx->inner_error != NULL)
2487251881Speter    return;
2488251881Speter
2489251881Speter  ectx->inner_error = svn_error_trace(
2490251881Speter                        svn_ra_serf__xml_cb_cdata(ectx->xmlctx, data, len));
2491251881Speter
2492251881Speter#ifdef EXPAT_HAS_STOPPARSER
2493251881Speter  if (ectx->inner_error)
2494251881Speter    (void) XML_StopParser(ectx->parser, 0 /* resumable */);
2495251881Speter#endif
2496251881Speter}
2497251881Speter
2498251881Speter
2499251881Speter/* Implements svn_ra_serf__response_handler_t */
2500251881Speterstatic svn_error_t *
2501251881Speterexpat_response_handler(serf_request_t *request,
2502251881Speter                       serf_bucket_t *response,
2503251881Speter                       void *baton,
2504251881Speter                       apr_pool_t *scratch_pool)
2505251881Speter{
2506251881Speter  struct expat_ctx_t *ectx = baton;
2507251881Speter
2508251881Speter  if (!ectx->parser)
2509251881Speter    {
2510251881Speter      ectx->parser = XML_ParserCreate(NULL);
2511251881Speter      apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
2512251881Speter                                xml_parser_cleanup, apr_pool_cleanup_null);
2513251881Speter      XML_SetUserData(ectx->parser, ectx);
2514251881Speter      XML_SetElementHandler(ectx->parser, expat_start, expat_end);
2515251881Speter      XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
2516251881Speter    }
2517251881Speter
2518251881Speter  /* ### TODO: sline.code < 200 should really be handled by the core */
2519251881Speter  if ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300))
2520251881Speter    {
2521251881Speter      /* By deferring to expect_empty_body(), it will make a choice on
2522251881Speter         how to handle the body. Whatever the decision, the core handler
2523251881Speter         will take over, and we will not be called again.  */
2524251881Speter      return svn_error_trace(svn_ra_serf__expect_empty_body(
2525251881Speter                               request, response, ectx->handler,
2526251881Speter                               scratch_pool));
2527251881Speter    }
2528251881Speter
2529251881Speter  while (1)
2530251881Speter    {
2531251881Speter      apr_status_t status;
2532251881Speter      const char *data;
2533251881Speter      apr_size_t len;
2534251881Speter      int expat_status;
2535251881Speter
2536251881Speter      status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
2537251881Speter      if (SERF_BUCKET_READ_ERROR(status))
2538251881Speter        return svn_ra_serf__wrap_err(status, NULL);
2539251881Speter
2540251881Speter#if 0
2541251881Speter      /* ### move restart/skip into the core handler  */
2542251881Speter      ectx->handler->read_size += len;
2543251881Speter#endif
2544251881Speter
2545251881Speter      /* ### move PAUSED behavior to a new response handler that can feed
2546251881Speter         ### an inner handler, or can pause for a while.  */
2547251881Speter
2548251881Speter      /* ### should we have an IGNORE_ERRORS flag like the v1 parser?  */
2549251881Speter
2550251881Speter      expat_status = XML_Parse(ectx->parser, data, (int)len, 0 /* isFinal */);
2551251881Speter
2552251881Speter      /* We need to check INNER_ERROR first. This is an error from the
2553251881Speter         callbacks that has been "dropped off" for us to retrieve. On
2554251881Speter         current Expat parsers, we stop the parser when an error occurs,
2555251881Speter         so we want to ignore EXPAT_STATUS (which reports the stoppage).
2556251881Speter
2557251881Speter         If an error is not present, THEN we go ahead and look for parsing
2558251881Speter         errors.  */
2559251881Speter      if (ectx->inner_error)
2560251881Speter        {
2561251881Speter          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
2562251881Speter                               xml_parser_cleanup);
2563251881Speter          return svn_error_trace(ectx->inner_error);
2564251881Speter        }
2565251881Speter      if (expat_status == XML_STATUS_ERROR)
2566251881Speter        return svn_error_createf(SVN_ERR_XML_MALFORMED,
2567251881Speter                                 ectx->inner_error,
2568251881Speter                                 _("The %s response contains invalid XML"
2569251881Speter                                   " (%d %s)"),
2570251881Speter                                 ectx->handler->method,
2571251881Speter                                 ectx->handler->sline.code,
2572251881Speter                                 ectx->handler->sline.reason);
2573251881Speter
2574251881Speter      /* The parsing went fine. What has the bucket told us?  */
2575251881Speter
2576251881Speter      if (APR_STATUS_IS_EOF(status))
2577251881Speter        {
2578251881Speter          /* Tell expat we've reached the end of the content. Ignore the
2579251881Speter             return status. We just don't care.  */
2580251881Speter          (void) XML_Parse(ectx->parser, NULL, 0, 1 /* isFinal */);
2581251881Speter
2582251881Speter          svn_ra_serf__xml_context_destroy(ectx->xmlctx);
2583251881Speter          apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
2584251881Speter                               xml_parser_cleanup);
2585251881Speter
2586251881Speter          /* ### should check XMLCTX to see if it has returned to the
2587251881Speter             ### INITIAL state. we may have ended early...  */
2588251881Speter        }
2589251881Speter
2590251881Speter      if (status && !SERF_BUCKET_READ_ERROR(status))
2591251881Speter        {
2592251881Speter          return svn_ra_serf__wrap_err(status, NULL);
2593251881Speter        }
2594251881Speter    }
2595251881Speter
2596251881Speter  /* NOTREACHED */
2597251881Speter}
2598251881Speter
2599251881Speter
2600251881Spetersvn_ra_serf__handler_t *
2601251881Spetersvn_ra_serf__create_expat_handler(svn_ra_serf__xml_context_t *xmlctx,
2602251881Speter                                  apr_pool_t *result_pool)
2603251881Speter{
2604251881Speter  svn_ra_serf__handler_t *handler;
2605251881Speter  struct expat_ctx_t *ectx;
2606251881Speter
2607251881Speter  ectx = apr_pcalloc(result_pool, sizeof(*ectx));
2608251881Speter  ectx->xmlctx = xmlctx;
2609251881Speter  ectx->parser = NULL;
2610251881Speter  ectx->cleanup_pool = result_pool;
2611251881Speter
2612251881Speter
2613251881Speter  handler = apr_pcalloc(result_pool, sizeof(*handler));
2614251881Speter  handler->handler_pool = result_pool;
2615251881Speter  handler->response_handler = expat_response_handler;
2616251881Speter  handler->response_baton = ectx;
2617251881Speter
2618251881Speter  ectx->handler = handler;
2619251881Speter
2620251881Speter  return handler;
2621251881Speter}
2622