diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_ra_serf/xml.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_ra_serf/xml.c')
-rw-r--r-- | subversion/libsvn_ra_serf/xml.c | 556 |
1 files changed, 432 insertions, 124 deletions
diff --git a/subversion/libsvn_ra_serf/xml.c b/subversion/libsvn_ra_serf/xml.c index a95eacc..1a98857 100644 --- a/subversion/libsvn_ra_serf/xml.c +++ b/subversion/libsvn_ra_serf/xml.c @@ -24,6 +24,7 @@ #include <apr_uri.h> +#include <expat.h> #include <serf.h> #include "svn_hash.h" @@ -42,13 +43,35 @@ #include "ra_serf.h" +/* Fix for older expat 1.95.x's that do not define + * XML_STATUS_OK/XML_STATUS_ERROR + */ +#ifndef XML_STATUS_OK +#define XML_STATUS_OK 1 +#define XML_STATUS_ERROR 0 +#endif + +#ifndef XML_VERSION_AT_LEAST +#define XML_VERSION_AT_LEAST(major,minor,patch) \ +(((major) < XML_MAJOR_VERSION) \ + || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \ + || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \ + (patch) <= XML_MICRO_VERSION)) +#endif /* XML_VERSION_AT_LEAST */ + +/* Read/write chunks of this size into the spillbuf. */ +#define PARSE_CHUNK_SIZE 8000 + + struct svn_ra_serf__xml_context_t { /* Current state information. */ svn_ra_serf__xml_estate_t *current; - /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be - closed before looking for transitions from CURRENT->STATE. */ - svn_ra_serf__dav_props_t waiting; + /* If WAITING >= then we are waiting for an element to close before + resuming events. The number stored here is the amount of nested + elements open. The Xml parser will make sure the document is well + formed. */ + int waiting; /* The transition table. */ const svn_ra_serf__xml_transition_t *ttable; @@ -83,6 +106,16 @@ struct svn_ra_serf__xml_context_t { }; +/* Structure which represents an XML namespace. */ +typedef struct svn_ra_serf__ns_t { + /* The assigned name. */ + const char *xmlns; + /* The full URL for this namespace. */ + const char *url; + /* The next namespace in our list. */ + struct svn_ra_serf__ns_t *next; +} svn_ra_serf__ns_t; + struct svn_ra_serf__xml_estate_t { /* The current state value. */ int state; @@ -114,6 +147,19 @@ struct svn_ra_serf__xml_estate_t { }; +struct expat_ctx_t { + svn_ra_serf__xml_context_t *xmlctx; + XML_Parser parser; + svn_ra_serf__handler_t *handler; + const int *expected_status; + + svn_error_t *inner_error; + + /* Do not use this pool for allocation. It is merely recorded for running + the cleanup handler. */ + apr_pool_t *cleanup_pool; +}; + static void define_namespaces(svn_ra_serf__ns_t **ns_list, @@ -140,7 +186,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, /* Have we already defined this ns previously? */ for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next) { - if (strcmp(cur_ns->namespace, prefix) == 0) + if (strcmp(cur_ns->xmlns, prefix) == 0) { found = TRUE; break; @@ -157,7 +203,7 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, else pool = baton; new_ns = apr_palloc(pool, sizeof(*new_ns)); - new_ns->namespace = apr_pstrdup(pool, prefix); + new_ns->xmlns = apr_pstrdup(pool, prefix); new_ns->url = apr_pstrdup(pool, tmp_attrs[1]); /* Push into the front of NS_LIST. Parent states will point @@ -170,22 +216,15 @@ define_namespaces(svn_ra_serf__ns_t **ns_list, } } - -void -svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list, - const char *const *attrs, - apr_pool_t *result_pool) -{ - define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool); -} - - /* - * Look up NAME in the NS_LIST list for previously declared namespace - * definitions and return a DAV_PROPS_T-tuple that has values. + * Look up @a name in the @a ns_list list for previously declared namespace + * definitions. + * + * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple + * representing the expanded name. */ -void -svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, +static void +expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, const svn_ra_serf__ns_t *ns_list, const char *name) { @@ -198,9 +237,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, for (ns = ns_list; ns; ns = ns->next) { - if (strncmp(ns->namespace, name, colon - name) == 0) + if (strncmp(ns->xmlns, name, colon - name) == 0) { - returned_prop_name->namespace = ns->url; + returned_prop_name->xmlns = ns->url; returned_prop_name->name = colon + 1; return; } @@ -212,9 +251,9 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, for (ns = ns_list; ns; ns = ns->next) { - if (! ns->namespace[0]) + if (! ns->xmlns[0]) { - returned_prop_name->namespace = ns->url; + returned_prop_name->xmlns = ns->url; returned_prop_name->name = name; return; } @@ -223,7 +262,7 @@ svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, /* If the prefix is not found, then the name is NOT within a namespace. */ - returned_prop_name->namespace = ""; + returned_prop_name->xmlns = ""; returned_prop_name->name = name; } @@ -285,6 +324,49 @@ svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket, } void +svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket, + serf_bucket_alloc_t *bkt_alloc, + const char *tag, ...) +{ + va_list ap; + const char *key; + serf_bucket_t *tmp; + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + va_start(ap, tag); + while ((key = va_arg(ap, char *)) != NULL) + { + const char *val = va_arg(ap, const char *); + if (val) + { + tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); + } + } + va_end(ap); + + tmp = SERF_BUCKET_SIMPLE_STRING_LEN("/>", 2, bkt_alloc); + serf_bucket_aggregate_append(agg_bucket, tmp); +} + +void svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket, serf_bucket_alloc_t *bkt_alloc, const char *tag) @@ -368,7 +450,7 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag, const char *value, serf_bucket_alloc_t *bkt_alloc) { - svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, NULL); + svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, SVN_VA_NULL); if (value) { @@ -379,54 +461,6 @@ void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag, svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag); } -void -svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser, - int state) -{ - svn_ra_serf__xml_state_t *new_state; - - if (!parser->free_state) - { - new_state = apr_palloc(parser->pool, sizeof(*new_state)); - new_state->pool = svn_pool_create(parser->pool); - } - else - { - new_state = parser->free_state; - parser->free_state = parser->free_state->prev; - - svn_pool_clear(new_state->pool); - } - - if (parser->state) - { - new_state->private = parser->state->private; - new_state->ns_list = parser->state->ns_list; - } - else - { - new_state->private = NULL; - new_state->ns_list = NULL; - } - - new_state->current_state = state; - - /* Add it to the state chain. */ - new_state->prev = parser->state; - parser->state = new_state; -} - -void svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser) -{ - svn_ra_serf__xml_state_t *cur_state; - - cur_state = parser->state; - parser->state = cur_state->prev; - cur_state->prev = parser->free_state; - parser->free_state = cur_state; -} - - /* Return a pool for XES to use for self-alloc (and other specifics). */ static apr_pool_t * xes_pool(const svn_ra_serf__xml_estate_t *xes) @@ -458,11 +492,50 @@ lazy_create_pool(void *baton) return xes->state_pool; } -void -svn_ra_serf__xml_context_destroy( - svn_ra_serf__xml_context_t *xmlctx) +svn_error_t * +svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx) { + if (xmlctx->current->prev) + { + /* Probably unreachable as this would be an xml parser error */ + return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("XML stream truncated: closing '%s' missing"), + xmlctx->current->tag.name); + } + else if (! xmlctx->free_states) + { + /* If we have no items on the free_states list, we didn't push anything, + which tells us that we found an empty xml body */ + const svn_ra_serf__xml_transition_t *scan; + const svn_ra_serf__xml_transition_t *document = NULL; + const char *msg; + + for (scan = xmlctx->ttable; scan->ns != NULL; ++scan) + { + if (scan->from_state == XML_STATE_INITIAL) + { + if (document != NULL) + { + document = NULL; /* Multiple document elements defined */ + break; + } + document = scan; + } + } + + if (document) + msg = apr_psprintf(xmlctx->scratch_pool, "'%s' element not found", + document->name); + else + msg = _("document element not found"); + + return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("XML stream truncated: %s"), + msg); + } + svn_pool_destroy(xmlctx->scratch_pool); + return SVN_NO_ERROR; } svn_ra_serf__xml_context_t * @@ -577,10 +650,10 @@ svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes) } -svn_error_t * -svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name, - const char *const *attrs) +static svn_error_t * +xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, + const char *raw_name, + const char *const *attrs) { svn_ra_serf__xml_estate_t *current = xmlctx->current; svn_ra_serf__dav_props_t elemname; @@ -590,14 +663,17 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* If we're waiting for an element to close, then just ignore all other element-opens. */ - if (xmlctx->waiting.namespace != NULL) - return SVN_NO_ERROR; + if (xmlctx->waiting > 0) + { + xmlctx->waiting++; + return SVN_NO_ERROR; + } /* Look for xmlns: attributes. Lazily create the state pool if any were found. */ define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current); - svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name); + expand_ns(&elemname, current->ns_list, raw_name); for (scan = xmlctx->ttable; scan->ns != NULL; ++scan) { @@ -610,21 +686,20 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* Found a specific transition. */ if (strcmp(elemname.name, scan->name) == 0 - && strcmp(elemname.namespace, scan->ns) == 0) + && strcmp(elemname.xmlns, scan->ns) == 0) break; } if (scan->ns == NULL) { - if (current->state == 0) + if (current->state == XML_STATE_INITIAL) { return svn_error_createf( - SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, + SVN_ERR_XML_UNEXPECTED_ELEMENT, NULL, _("XML Parsing failed: Unexpected root element '%s'"), elemname.name); } - xmlctx->waiting = elemname; - /* ### return? */ + xmlctx->waiting++; /* Start waiting for the close tag */ return SVN_NO_ERROR; } @@ -677,10 +752,11 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, name = *saveattr; value = svn_xml_get_attr_value(name, attrs); if (value == NULL) - return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND, - NULL, - _("Missing XML attribute: '%s'"), - name); + return svn_error_createf( + SVN_ERR_XML_ATTRIB_NOT_FOUND, + NULL, + _("Missing XML attribute '%s' on '%s' element"), + name, scan->name); } if (value) @@ -699,7 +775,7 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, /* Some basic copies to set up the new estate. */ new_xes->state = scan->to_state; new_xes->tag.name = apr_pstrdup(new_pool, elemname.name); - new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace); + new_xes->tag.xmlns = apr_pstrdup(new_pool, elemname.xmlns); new_xes->custom_close = scan->custom_close; /* Start with the parent's namespace set. */ @@ -723,39 +799,18 @@ svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, } -svn_error_t * -svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, - const char *raw_name) +static svn_error_t * +xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, + const char *raw_name) { svn_ra_serf__xml_estate_t *xes = xmlctx->current; - svn_ra_serf__dav_props_t elemname; - svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name); - - if (xmlctx->waiting.namespace != NULL) + if (xmlctx->waiting > 0) { - /* If this element is not the closer, then keep waiting... */ - if (strcmp(elemname.name, xmlctx->waiting.name) != 0 - || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0) - return SVN_NO_ERROR; - - /* Found it. Stop waiting, and go back for more. */ - xmlctx->waiting.namespace = NULL; + xmlctx->waiting--; return SVN_NO_ERROR; } - /* We should be looking at the same tag that opened the current state. - - Unknown elements are simply skipped, so we wouldn't reach this check. - - Known elements push a new state for a given tag. Some other elemname - would imply closing an ancestor tag (where did ours go?) or a spurious - tag closure. */ - if (strcmp(elemname.name, xes->tag.name) != 0 - || strcmp(elemname.namespace, xes->tag.namespace) != 0) - return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, - _("The response contains invalid XML")); - if (xes->custom_close) { const svn_string_t *cdata; @@ -799,14 +854,14 @@ svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, } -svn_error_t * -svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, - const char *data, - apr_size_t len) +static svn_error_t * +xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, + const char *data, + apr_size_t len) { /* If we are waiting for a closing tag, then we are uninterested in the cdata. Just return. */ - if (xmlctx->waiting.namespace != NULL) + if (xmlctx->waiting > 0) return SVN_NO_ERROR; /* If the current state is collecting cdata, then copy the cdata. */ @@ -831,3 +886,256 @@ svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, return SVN_NO_ERROR; } +/* svn_error_t * wrapper around XML_Parse */ +static APR_INLINE svn_error_t * +parse_xml(struct expat_ctx_t *ectx, const char *data, apr_size_t len, svn_boolean_t is_final) +{ + int xml_status = XML_Parse(ectx->parser, data, (int)len, is_final); + const char *msg; + int xml_code; + + if (xml_status == XML_STATUS_OK) + return ectx->inner_error; + + xml_code = XML_GetErrorCode(ectx->parser); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + /* If we called XML_StopParser() expat will return an abort error. If we + have a better error stored we should ignore it as it will not help + the end-user to store it in the error chain. */ + if (xml_code == XML_ERROR_ABORTED && ectx->inner_error) + return ectx->inner_error; +#endif + + msg = XML_ErrorString(xml_code); + + return svn_error_compose_create( + ectx->inner_error, + svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, + svn_error_createf(SVN_ERR_XML_MALFORMED, NULL, + _("Malformed XML: %s"), + msg), + _("The XML response contains invalid XML"))); +} + +/* Apr pool cleanup handler to release an XML_Parser in success and error + conditions */ +static apr_status_t +xml_parser_cleanup(void *baton) +{ + XML_Parser *xmlp = baton; + + if (*xmlp) + { + (void) XML_ParserFree(*xmlp); + *xmlp = NULL; + } + + return APR_SUCCESS; +} + +/* Conforms to Expat's XML_StartElementHandler */ +static void +expat_start(void *userData, const char *raw_name, const char **attrs) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_start(ectx->xmlctx, + raw_name, attrs)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + + +/* Conforms to Expat's XML_EndElementHandler */ +static void +expat_end(void *userData, const char *raw_name) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_end(ectx->xmlctx, raw_name)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + + +/* Conforms to Expat's XML_CharacterDataHandler */ +static void +expat_cdata(void *userData, const char *data, int len) +{ + struct expat_ctx_t *ectx = userData; + + if (ectx->inner_error != NULL) + return; + + ectx->inner_error = svn_error_trace(xml_cb_cdata(ectx->xmlctx, data, len)); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + if (ectx->inner_error) + (void) XML_StopParser(ectx->parser, 0 /* resumable */); +#endif +} + +#if XML_VERSION_AT_LEAST(1, 95, 8) +static void +expat_entity_declaration(void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName) +{ + struct expat_ctx_t *ectx = userData; + + /* Stop the parser if an entity declaration is hit. */ + XML_StopParser(ectx->parser, 0 /* resumable */); +} +#else +/* A noop default_handler. */ +static void +expat_default_handler(void *userData, const XML_Char *s, int len) +{ +} +#endif + +/* Implements svn_ra_serf__response_handler_t */ +static svn_error_t * +expat_response_handler(serf_request_t *request, + serf_bucket_t *response, + void *baton, + apr_pool_t *scratch_pool) +{ + struct expat_ctx_t *ectx = baton; + svn_boolean_t got_expected_status; + + if (ectx->expected_status) + { + const int *status = ectx->expected_status; + got_expected_status = FALSE; + + while (*status && ectx->handler->sline.code != *status) + status++; + + got_expected_status = (*status) != 0; + } + else + got_expected_status = (ectx->handler->sline.code == 200); + + if (!ectx->handler->server_error + && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300) + || ! got_expected_status)) + { + /* By deferring to expect_empty_body(), it will make a choice on + how to handle the body. Whatever the decision, the core handler + will take over, and we will not be called again. */ + + /* ### This handles xml bodies as svn-errors (returned via serf context + ### loop), but ignores non-xml errors. + + Current code depends on this behavior and checks itself while other + continues, and then verifies if work has been performed. + + ### TODO: Make error checking consistent */ + + /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */ + return svn_error_trace(svn_ra_serf__expect_empty_body( + request, response, ectx->handler, + scratch_pool)); + } + + if (!ectx->parser) + { + ectx->parser = XML_ParserCreate(NULL); + apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser, + xml_parser_cleanup, apr_pool_cleanup_null); + XML_SetUserData(ectx->parser, ectx); + XML_SetElementHandler(ectx->parser, expat_start, expat_end); + XML_SetCharacterDataHandler(ectx->parser, expat_cdata); + +#if XML_VERSION_AT_LEAST(1, 95, 8) + XML_SetEntityDeclHandler(ectx->parser, expat_entity_declaration); +#else + XML_SetDefaultHandler(ectx->parser, expat_default_handler); +#endif + } + + while (1) + { + apr_status_t status; + const char *data; + apr_size_t len; + svn_error_t *err; + svn_boolean_t at_eof = FALSE; + + status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len); + if (SERF_BUCKET_READ_ERROR(status)) + return svn_ra_serf__wrap_err(status, NULL); + else if (APR_STATUS_IS_EOF(status)) + at_eof = TRUE; + + err = parse_xml(ectx, data, len, at_eof /* isFinal */); + + if (at_eof || err) + { + /* Release xml parser state/tables. */ + apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser, + xml_parser_cleanup); + } + + SVN_ERR(err); + + /* The parsing went fine. What has the bucket told us? */ + if (at_eof) + { + /* Make sure we actually got xml and clean up after parsing */ + SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx)); + } + + if (status && !SERF_BUCKET_READ_ERROR(status)) + { + return svn_ra_serf__wrap_err(status, NULL); + } + } + + /* NOTREACHED */ +} + + +svn_ra_serf__handler_t * +svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session, + svn_ra_serf__xml_context_t *xmlctx, + const int *expected_status, + apr_pool_t *result_pool) +{ + svn_ra_serf__handler_t *handler; + struct expat_ctx_t *ectx; + + ectx = apr_pcalloc(result_pool, sizeof(*ectx)); + ectx->xmlctx = xmlctx; + ectx->parser = NULL; + ectx->expected_status = expected_status; + ectx->cleanup_pool = result_pool; + + handler = svn_ra_serf__create_handler(session, result_pool); + handler->response_handler = expat_response_handler; + handler->response_baton = ectx; + + ectx->handler = handler; + + return handler; +} |