2 * xml.c : standard XML parsing routines for ra_serf
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
31 #include "svn_pools.h"
35 #include "../libsvn_ra/ra_loader.h"
36 #include "svn_config.h"
37 #include "svn_delta.h"
40 #include "svn_private_config.h"
41 #include "private/svn_string_private.h"
46 /* Fix for older expat 1.95.x's that do not define
47 * XML_STATUS_OK/XML_STATUS_ERROR
50 #define XML_STATUS_OK 1
51 #define XML_STATUS_ERROR 0
54 #ifndef XML_VERSION_AT_LEAST
55 #define XML_VERSION_AT_LEAST(major,minor,patch) \
56 (((major) < XML_MAJOR_VERSION) \
57 || ((major) == XML_MAJOR_VERSION && (minor) < XML_MINOR_VERSION) \
58 || ((major) == XML_MAJOR_VERSION && (minor) == XML_MINOR_VERSION && \
59 (patch) <= XML_MICRO_VERSION))
60 #endif /* XML_VERSION_AT_LEAST */
62 /* Read/write chunks of this size into the spillbuf. */
63 #define PARSE_CHUNK_SIZE 8000
66 struct svn_ra_serf__xml_context_t {
67 /* Current state information. */
68 svn_ra_serf__xml_estate_t *current;
70 /* If WAITING >= then we are waiting for an element to close before
71 resuming events. The number stored here is the amount of nested
72 elements open. The Xml parser will make sure the document is well
76 /* The transition table. */
77 const svn_ra_serf__xml_transition_t *ttable;
79 /* The callback information. */
80 svn_ra_serf__xml_opened_t opened_cb;
81 svn_ra_serf__xml_closed_t closed_cb;
82 svn_ra_serf__xml_cdata_t cdata_cb;
85 /* Linked list of free states. */
86 svn_ra_serf__xml_estate_t *free_states;
89 /* Used to verify we are not re-entering a callback, specifically to
90 ensure SCRATCH_POOL is not cleared while an outer callback is
92 svn_boolean_t within_callback;
93 #define START_CALLBACK(xmlctx) \
95 svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx); \
96 SVN_ERR_ASSERT(!xmlctx__tmp->within_callback); \
97 xmlctx__tmp->within_callback = TRUE; \
99 #define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE)
101 #define START_CALLBACK(xmlctx) /* empty */
102 #define END_CALLBACK(xmlctx) /* empty */
103 #endif /* SVN_DEBUG */
105 apr_pool_t *scratch_pool;
109 /* Structure which represents an XML namespace. */
110 typedef struct svn_ra_serf__ns_t {
111 /* The assigned name. */
113 /* The full URL for this namespace. */
115 /* The next namespace in our list. */
116 struct svn_ra_serf__ns_t *next;
119 struct svn_ra_serf__xml_estate_t {
120 /* The current state value. */
123 /* The xml tag that opened this state. Waiting for the tag to close. */
124 svn_ra_serf__dav_props_t tag;
126 /* Should the CLOSED_CB function be called for custom processing when
127 this tag is closed? */
128 svn_boolean_t custom_close;
130 /* A pool may be constructed for this state. */
131 apr_pool_t *state_pool;
133 /* The namespaces extent for this state/element. This will start with
134 the parent's NS_LIST, and we will push new namespaces into our
135 local list. The parent will be unaffected by our locally-scoped data. */
136 svn_ra_serf__ns_t *ns_list;
138 /* Any collected attribute values. char * -> svn_string_t *. May be NULL
139 if no attributes have been collected. */
142 /* Any collected cdata. May be NULL if no cdata is being collected. */
143 svn_stringbuf_t *cdata;
145 /* Previous/outer state. */
146 svn_ra_serf__xml_estate_t *prev;
151 svn_ra_serf__xml_context_t *xmlctx;
153 svn_ra_serf__handler_t *handler;
154 const int *expected_status;
156 svn_error_t *inner_error;
158 /* Do not use this pool for allocation. It is merely recorded for running
159 the cleanup handler. */
160 apr_pool_t *cleanup_pool;
165 define_namespaces(svn_ra_serf__ns_t **ns_list,
166 const char *const *attrs,
167 apr_pool_t *(*get_pool)(void *baton),
170 const char *const *tmp_attrs = attrs;
172 for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
174 if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
176 const svn_ra_serf__ns_t *cur_ns;
177 svn_boolean_t found = FALSE;
180 /* The empty prefix, or a named-prefix. */
181 if (tmp_attrs[0][5] == ':')
182 prefix = &tmp_attrs[0][6];
186 /* Have we already defined this ns previously? */
187 for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
189 if (strcmp(cur_ns->xmlns, prefix) == 0)
199 svn_ra_serf__ns_t *new_ns;
202 pool = get_pool(baton);
205 new_ns = apr_palloc(pool, sizeof(*new_ns));
206 new_ns->xmlns = apr_pstrdup(pool, prefix);
207 new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
209 /* Push into the front of NS_LIST. Parent states will point
210 to later in the chain, so will be unaffected by
211 shadowing/other namespaces pushed onto NS_LIST. */
212 new_ns->next = *ns_list;
220 * Look up @a name in the @a ns_list list for previously declared namespace
223 * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple
224 * representing the expanded name.
227 expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
228 const svn_ra_serf__ns_t *ns_list,
233 colon = strchr(name, ':');
236 const svn_ra_serf__ns_t *ns;
238 for (ns = ns_list; ns; ns = ns->next)
240 if (strncmp(ns->xmlns, name, colon - name) == 0)
242 returned_prop_name->xmlns = ns->url;
243 returned_prop_name->name = colon + 1;
250 const svn_ra_serf__ns_t *ns;
252 for (ns = ns_list; ns; ns = ns->next)
256 returned_prop_name->xmlns = ns->url;
257 returned_prop_name->name = name;
263 /* If the prefix is not found, then the name is NOT within a
265 returned_prop_name->xmlns = "";
266 returned_prop_name->name = name;
270 #define XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
273 svn_ra_serf__add_xml_header_buckets(serf_bucket_t *agg_bucket,
274 serf_bucket_alloc_t *bkt_alloc)
278 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(XML_HEADER, sizeof(XML_HEADER) - 1,
280 serf_bucket_aggregate_append(agg_bucket, tmp);
284 svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
285 serf_bucket_alloc_t *bkt_alloc,
286 const char *tag, ...)
292 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
293 serf_bucket_aggregate_append(agg_bucket, tmp);
295 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
296 serf_bucket_aggregate_append(agg_bucket, tmp);
299 while ((key = va_arg(ap, char *)) != NULL)
301 const char *val = va_arg(ap, const char *);
304 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
305 serf_bucket_aggregate_append(agg_bucket, tmp);
307 tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
308 serf_bucket_aggregate_append(agg_bucket, tmp);
310 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
311 serf_bucket_aggregate_append(agg_bucket, tmp);
313 tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
314 serf_bucket_aggregate_append(agg_bucket, tmp);
316 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
317 serf_bucket_aggregate_append(agg_bucket, tmp);
322 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
323 serf_bucket_aggregate_append(agg_bucket, tmp);
327 svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket,
328 serf_bucket_alloc_t *bkt_alloc,
329 const char *tag, ...)
335 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
336 serf_bucket_aggregate_append(agg_bucket, tmp);
338 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
339 serf_bucket_aggregate_append(agg_bucket, tmp);
342 while ((key = va_arg(ap, char *)) != NULL)
344 const char *val = va_arg(ap, const char *);
347 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
348 serf_bucket_aggregate_append(agg_bucket, tmp);
350 tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
351 serf_bucket_aggregate_append(agg_bucket, tmp);
353 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
354 serf_bucket_aggregate_append(agg_bucket, tmp);
356 tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
357 serf_bucket_aggregate_append(agg_bucket, tmp);
359 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
360 serf_bucket_aggregate_append(agg_bucket, tmp);
365 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("/>", 2, bkt_alloc);
366 serf_bucket_aggregate_append(agg_bucket, tmp);
370 svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
371 serf_bucket_alloc_t *bkt_alloc,
376 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</", 2, bkt_alloc);
377 serf_bucket_aggregate_append(agg_bucket, tmp);
379 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
380 serf_bucket_aggregate_append(agg_bucket, tmp);
382 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
383 serf_bucket_aggregate_append(agg_bucket, tmp);
387 svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket,
388 serf_bucket_alloc_t *bkt_alloc,
389 const char *data, apr_size_t len)
391 const char *end = data + len;
392 const char *p = data, *q;
393 serf_bucket_t *tmp_bkt;
397 /* Find a character which needs to be quoted and append bytes up
398 to that point. Strictly speaking, '>' only needs to be
399 quoted if it follows "]]", but it's easier to quote it all
402 So, why are we escaping '\r' here? Well, according to the
403 XML spec, '\r\n' gets converted to '\n' during XML parsing.
404 Also, any '\r' not followed by '\n' is converted to '\n'. By
405 golly, if we say we want to escape a '\r', we want to make
406 sure it remains a '\r'! */
408 while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
412 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(p, q - p, bkt_alloc);
413 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
415 /* We may already be a winner. */
419 /* Append the entity reference for the character. */
422 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&", sizeof("&") - 1,
424 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
428 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("<", sizeof("<") - 1,
430 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
434 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(">", sizeof(">") - 1,
436 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
440 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(" ", sizeof(" ") - 1,
442 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
449 void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag,
451 serf_bucket_alloc_t *bkt_alloc)
453 svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, SVN_VA_NULL);
457 svn_ra_serf__add_cdata_len_buckets(agg_bucket, bkt_alloc,
458 value, strlen(value));
461 svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag);
464 /* Return a pool for XES to use for self-alloc (and other specifics). */
466 xes_pool(const svn_ra_serf__xml_estate_t *xes)
468 /* Move up through parent states looking for one with a pool. This
469 will always terminate since the initial state has a pool. */
470 while (xes->state_pool == NULL)
472 return xes->state_pool;
477 ensure_pool(svn_ra_serf__xml_estate_t *xes)
479 if (xes->state_pool == NULL)
480 xes->state_pool = svn_pool_create(xes_pool(xes));
484 /* This callback is used by define_namespaces() to wait until a pool is
485 required before constructing it. */
487 lazy_create_pool(void *baton)
489 svn_ra_serf__xml_estate_t *xes = baton;
492 return xes->state_pool;
496 svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx)
498 if (xmlctx->current->prev)
500 /* Probably unreachable as this would be an xml parser error */
501 return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
502 _("XML stream truncated: closing '%s' missing"),
503 xmlctx->current->tag.name);
505 else if (! xmlctx->free_states)
507 /* If we have no items on the free_states list, we didn't push anything,
508 which tells us that we found an empty xml body */
509 const svn_ra_serf__xml_transition_t *scan;
510 const svn_ra_serf__xml_transition_t *document = NULL;
513 for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
515 if (scan->from_state == XML_STATE_INITIAL)
517 if (document != NULL)
519 document = NULL; /* Multiple document elements defined */
527 msg = apr_psprintf(xmlctx->scratch_pool, "'%s' element not found",
530 msg = _("document element not found");
532 return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
533 _("XML stream truncated: %s"),
537 svn_pool_destroy(xmlctx->scratch_pool);
541 svn_ra_serf__xml_context_t *
542 svn_ra_serf__xml_context_create(
543 const svn_ra_serf__xml_transition_t *ttable,
544 svn_ra_serf__xml_opened_t opened_cb,
545 svn_ra_serf__xml_closed_t closed_cb,
546 svn_ra_serf__xml_cdata_t cdata_cb,
548 apr_pool_t *result_pool)
550 svn_ra_serf__xml_context_t *xmlctx;
551 svn_ra_serf__xml_estate_t *xes;
553 xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
554 xmlctx->ttable = ttable;
555 xmlctx->opened_cb = opened_cb;
556 xmlctx->closed_cb = closed_cb;
557 xmlctx->cdata_cb = cdata_cb;
558 xmlctx->baton = baton;
559 xmlctx->scratch_pool = svn_pool_create(result_pool);
561 xes = apr_pcalloc(result_pool, sizeof(*xes));
562 /* XES->STATE == 0 */
564 /* Child states may use this pool to allocate themselves. If a child
565 needs to collect information, then it will construct a subpool and
566 will use that to allocate itself and its collected data. */
567 xes->state_pool = result_pool;
569 xmlctx->current = xes;
576 svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
583 pool = xes->state_pool;
585 data = apr_hash_make(pool);
587 for (; xes != NULL; xes = xes->prev)
589 if (xes->attrs != NULL)
591 apr_hash_index_t *hi;
593 for (hi = apr_hash_first(pool, xes->attrs); hi;
594 hi = apr_hash_next(hi))
600 /* Parent name/value lifetimes are at least as long as POOL. */
601 apr_hash_this(hi, &key, &klen, &val);
602 apr_hash_set(data, key, klen, val);
606 if (xes->state == stop_state)
615 svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
620 svn_ra_serf__xml_estate_t *scan;
622 for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev)
625 SVN_ERR_ASSERT_NO_RETURN(scan != NULL);
627 /* Make sure the target state has a pool. */
630 /* ... and attribute storage. */
631 if (scan->attrs == NULL)
632 scan->attrs = apr_hash_make(scan->state_pool);
634 /* In all likelihood, NAME is a string constant. But we can't really
635 be sure. And it isn't like we're storing a billion of these into
637 svn_hash_sets(scan->attrs,
638 apr_pstrdup(scan->state_pool, name),
639 apr_pstrdup(scan->state_pool, value));
644 svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
646 /* If they asked for a pool, then ensure that we have one to provide. */
649 return xes->state_pool;
654 xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
655 const char *raw_name,
656 const char *const *attrs)
658 svn_ra_serf__xml_estate_t *current = xmlctx->current;
659 svn_ra_serf__dav_props_t elemname;
660 const svn_ra_serf__xml_transition_t *scan;
661 apr_pool_t *new_pool;
662 svn_ra_serf__xml_estate_t *new_xes;
664 /* If we're waiting for an element to close, then just ignore all
665 other element-opens. */
666 if (xmlctx->waiting > 0)
672 /* Look for xmlns: attributes. Lazily create the state pool if any
674 define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current);
676 expand_ns(&elemname, current->ns_list, raw_name);
678 for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
680 if (scan->from_state != current->state)
683 /* Wildcard tag match. */
684 if (*scan->name == '*')
687 /* Found a specific transition. */
688 if (strcmp(elemname.name, scan->name) == 0
689 && strcmp(elemname.xmlns, scan->ns) == 0)
692 if (scan->ns == NULL)
694 if (current->state == XML_STATE_INITIAL)
696 return svn_error_createf(
697 SVN_ERR_XML_UNEXPECTED_ELEMENT, NULL,
698 _("XML Parsing failed: Unexpected root element '%s'"),
702 xmlctx->waiting++; /* Start waiting for the close tag */
706 /* We should not be told to collect cdata if the closed_cb will not
708 SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
710 /* Found a transition. Make it happen. */
712 /* ### todo. push state */
714 /* ### how to use free states? */
715 /* This state should be allocated in the extent pool. If we will be
716 collecting information for this state, then construct a subpool.
718 ### potentially optimize away the subpool if none of the
719 ### attributes are present. subpools are cheap, tho... */
720 new_pool = xes_pool(current);
721 if (scan->collect_cdata || scan->collect_attrs[0])
723 new_pool = svn_pool_create(new_pool);
725 /* Prep the new state. */
726 new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
727 new_xes->state_pool = new_pool;
729 /* If we're supposed to collect cdata, then set up a buffer for
730 this. The existence of this buffer will instruct our cdata
731 callback to collect the cdata. */
732 if (scan->collect_cdata)
733 new_xes->cdata = svn_stringbuf_create_empty(new_pool);
735 if (scan->collect_attrs[0] != NULL)
737 const char *const *saveattr = &scan->collect_attrs[0];
739 new_xes->attrs = apr_hash_make(new_pool);
740 for (; *saveattr != NULL; ++saveattr)
745 if (**saveattr == '?')
747 name = *saveattr + 1;
748 value = svn_xml_get_attr_value(name, attrs);
753 value = svn_xml_get_attr_value(name, attrs);
755 return svn_error_createf(
756 SVN_ERR_XML_ATTRIB_NOT_FOUND,
758 _("Missing XML attribute '%s' on '%s' element"),
763 svn_hash_sets(new_xes->attrs, name,
764 apr_pstrdup(new_pool, value));
770 /* Prep the new state. */
771 new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
772 /* STATE_POOL remains NULL. */
775 /* Some basic copies to set up the new estate. */
776 new_xes->state = scan->to_state;
777 new_xes->tag.name = apr_pstrdup(new_pool, elemname.name);
778 new_xes->tag.xmlns = apr_pstrdup(new_pool, elemname.xmlns);
779 new_xes->custom_close = scan->custom_close;
781 /* Start with the parent's namespace set. */
782 new_xes->ns_list = current->ns_list;
784 /* The new state is prepared. Make it current. */
785 new_xes->prev = current;
786 xmlctx->current = new_xes;
788 if (xmlctx->opened_cb)
790 START_CALLBACK(xmlctx);
791 SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
792 new_xes->state, &new_xes->tag,
793 xmlctx->scratch_pool));
794 END_CALLBACK(xmlctx);
795 svn_pool_clear(xmlctx->scratch_pool);
803 xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
804 const char *raw_name)
806 svn_ra_serf__xml_estate_t *xes = xmlctx->current;
808 if (xmlctx->waiting > 0)
814 if (xes->custom_close)
816 const svn_string_t *cdata;
820 cdata = svn_stringbuf__morph_into_string(xes->cdata);
822 /* We might toss the pool holding this structure, but it could also
823 be within a parent pool. In any case, for safety's sake, disable
824 the stringbuf against future Badness. */
825 xes->cdata->pool = NULL;
831 START_CALLBACK(xmlctx);
832 SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
834 xmlctx->scratch_pool));
835 END_CALLBACK(xmlctx);
836 svn_pool_clear(xmlctx->scratch_pool);
840 xmlctx->current = xes->prev;
842 /* ### not everything should go on the free state list. XES may go
843 ### away with the state pool. */
844 xes->prev = xmlctx->free_states;
845 xmlctx->free_states = xes;
847 /* If there is a STATE_POOL, then toss it. This will get rid of as much
848 memory as possible. Potentially the XES (if we didn't create a pool
849 right away, then XES may be in a parent pool). */
851 svn_pool_destroy(xes->state_pool);
858 xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
862 /* If we are waiting for a closing tag, then we are uninterested in
863 the cdata. Just return. */
864 if (xmlctx->waiting > 0)
867 /* If the current state is collecting cdata, then copy the cdata. */
868 if (xmlctx->current->cdata != NULL)
870 svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
872 /* ... else if a CDATA_CB has been supplied, then invoke it for
874 else if (xmlctx->cdata_cb != NULL)
876 START_CALLBACK(xmlctx);
877 SVN_ERR(xmlctx->cdata_cb(xmlctx->current,
879 xmlctx->current->state,
881 xmlctx->scratch_pool));
882 END_CALLBACK(xmlctx);
883 svn_pool_clear(xmlctx->scratch_pool);
889 /* svn_error_t * wrapper around XML_Parse */
890 static APR_INLINE svn_error_t *
891 parse_xml(struct expat_ctx_t *ectx, const char *data, apr_size_t len, svn_boolean_t is_final)
893 int xml_status = XML_Parse(ectx->parser, data, (int)len, is_final);
897 if (xml_status == XML_STATUS_OK)
898 return ectx->inner_error;
900 xml_code = XML_GetErrorCode(ectx->parser);
902 #if XML_VERSION_AT_LEAST(1, 95, 8)
903 /* If we called XML_StopParser() expat will return an abort error. If we
904 have a better error stored we should ignore it as it will not help
905 the end-user to store it in the error chain. */
906 if (xml_code == XML_ERROR_ABORTED && ectx->inner_error)
907 return ectx->inner_error;
910 msg = XML_ErrorString(xml_code);
912 return svn_error_compose_create(
914 svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA,
915 svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
916 _("Malformed XML: %s"),
918 _("The XML response contains invalid XML")));
921 /* Apr pool cleanup handler to release an XML_Parser in success and error
924 xml_parser_cleanup(void *baton)
926 XML_Parser *xmlp = baton;
930 (void) XML_ParserFree(*xmlp);
937 /* Conforms to Expat's XML_StartElementHandler */
939 expat_start(void *userData, const char *raw_name, const char **attrs)
941 struct expat_ctx_t *ectx = userData;
943 if (ectx->inner_error != NULL)
946 ectx->inner_error = svn_error_trace(xml_cb_start(ectx->xmlctx,
949 #if XML_VERSION_AT_LEAST(1, 95, 8)
950 if (ectx->inner_error)
951 (void) XML_StopParser(ectx->parser, 0 /* resumable */);
956 /* Conforms to Expat's XML_EndElementHandler */
958 expat_end(void *userData, const char *raw_name)
960 struct expat_ctx_t *ectx = userData;
962 if (ectx->inner_error != NULL)
965 ectx->inner_error = svn_error_trace(xml_cb_end(ectx->xmlctx, raw_name));
967 #if XML_VERSION_AT_LEAST(1, 95, 8)
968 if (ectx->inner_error)
969 (void) XML_StopParser(ectx->parser, 0 /* resumable */);
974 /* Conforms to Expat's XML_CharacterDataHandler */
976 expat_cdata(void *userData, const char *data, int len)
978 struct expat_ctx_t *ectx = userData;
980 if (ectx->inner_error != NULL)
983 ectx->inner_error = svn_error_trace(xml_cb_cdata(ectx->xmlctx, data, len));
985 #if XML_VERSION_AT_LEAST(1, 95, 8)
986 if (ectx->inner_error)
987 (void) XML_StopParser(ectx->parser, 0 /* resumable */);
992 /* Implements svn_ra_serf__response_handler_t */
994 expat_response_handler(serf_request_t *request,
995 serf_bucket_t *response,
997 apr_pool_t *scratch_pool)
999 struct expat_ctx_t *ectx = baton;
1000 svn_boolean_t got_expected_status;
1002 if (ectx->expected_status)
1004 const int *status = ectx->expected_status;
1005 got_expected_status = FALSE;
1007 while (*status && ectx->handler->sline.code != *status)
1010 got_expected_status = (*status) != 0;
1013 got_expected_status = (ectx->handler->sline.code == 200);
1015 if (!ectx->handler->server_error
1016 && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
1017 || ! got_expected_status))
1019 /* By deferring to expect_empty_body(), it will make a choice on
1020 how to handle the body. Whatever the decision, the core handler
1021 will take over, and we will not be called again. */
1023 /* ### This handles xml bodies as svn-errors (returned via serf context
1024 ### loop), but ignores non-xml errors.
1026 Current code depends on this behavior and checks itself while other
1027 continues, and then verifies if work has been performed.
1029 ### TODO: Make error checking consistent */
1031 /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
1032 return svn_error_trace(svn_ra_serf__expect_empty_body(
1033 request, response, ectx->handler,
1039 ectx->parser = XML_ParserCreate(NULL);
1040 apr_pool_cleanup_register(ectx->cleanup_pool, &ectx->parser,
1041 xml_parser_cleanup, apr_pool_cleanup_null);
1042 XML_SetUserData(ectx->parser, ectx);
1043 XML_SetElementHandler(ectx->parser, expat_start, expat_end);
1044 XML_SetCharacterDataHandler(ectx->parser, expat_cdata);
1049 apr_status_t status;
1053 svn_boolean_t at_eof = FALSE;
1055 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
1056 if (SERF_BUCKET_READ_ERROR(status))
1057 return svn_ra_serf__wrap_err(status, NULL);
1058 else if (APR_STATUS_IS_EOF(status))
1061 err = parse_xml(ectx, data, len, at_eof /* isFinal */);
1065 /* Release xml parser state/tables. */
1066 apr_pool_cleanup_run(ectx->cleanup_pool, &ectx->parser,
1067 xml_parser_cleanup);
1072 /* The parsing went fine. What has the bucket told us? */
1075 /* Make sure we actually got xml and clean up after parsing */
1076 SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
1079 if (status && !SERF_BUCKET_READ_ERROR(status))
1081 return svn_ra_serf__wrap_err(status, NULL);
1089 svn_ra_serf__handler_t *
1090 svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session,
1091 svn_ra_serf__xml_context_t *xmlctx,
1092 const int *expected_status,
1093 apr_pool_t *result_pool)
1095 svn_ra_serf__handler_t *handler;
1096 struct expat_ctx_t *ectx;
1098 ectx = apr_pcalloc(result_pool, sizeof(*ectx));
1099 ectx->xmlctx = xmlctx;
1100 ectx->parser = NULL;
1101 ectx->expected_status = expected_status;
1102 ectx->cleanup_pool = result_pool;
1104 handler = svn_ra_serf__create_handler(session, result_pool);
1105 handler->response_handler = expat_response_handler;
1106 handler->response_baton = ectx;
1108 ectx->handler = handler;