]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - contrib/subversion/subversion/libsvn_ra_serf/xml.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / contrib / subversion / subversion / libsvn_ra_serf / xml.c
1 /*
2  * xml.c :  standard XML parsing routines for ra_serf
3  *
4  * ====================================================================
5  *    Licensed to the Apache Software Foundation (ASF) under one
6  *    or more contributor license agreements.  See the NOTICE file
7  *    distributed with this work for additional information
8  *    regarding copyright ownership.  The ASF licenses this file
9  *    to you under the Apache License, Version 2.0 (the
10  *    "License"); you may not use this file except in compliance
11  *    with the License.  You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  *    Unless required by applicable law or agreed to in writing,
16  *    software distributed under the License is distributed on an
17  *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18  *    KIND, either express or implied.  See the License for the
19  *    specific language governing permissions and limitations
20  *    under the License.
21  * ====================================================================
22  */
23
24
25 \f
26 #include <apr_uri.h>
27 #include <serf.h>
28
29 #include "svn_hash.h"
30 #include "svn_pools.h"
31 #include "svn_ra.h"
32 #include "svn_dav.h"
33 #include "svn_xml.h"
34 #include "../libsvn_ra/ra_loader.h"
35 #include "svn_config.h"
36 #include "svn_delta.h"
37 #include "svn_path.h"
38
39 #include "svn_private_config.h"
40 #include "private/svn_string_private.h"
41
42 #include "ra_serf.h"
43 \f
44
45 struct svn_ra_serf__xml_context_t {
46   /* Current state information.  */
47   svn_ra_serf__xml_estate_t *current;
48
49   /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be
50      closed before looking for transitions from CURRENT->STATE.  */
51   svn_ra_serf__dav_props_t waiting;
52
53   /* The transition table.  */
54   const svn_ra_serf__xml_transition_t *ttable;
55
56   /* The callback information.  */
57   svn_ra_serf__xml_opened_t opened_cb;
58   svn_ra_serf__xml_closed_t closed_cb;
59   svn_ra_serf__xml_cdata_t cdata_cb;
60   void *baton;
61
62   /* Linked list of free states.  */
63   svn_ra_serf__xml_estate_t *free_states;
64
65 #ifdef SVN_DEBUG
66   /* Used to verify we are not re-entering a callback, specifically to
67      ensure SCRATCH_POOL is not cleared while an outer callback is
68      trying to use it.  */
69   svn_boolean_t within_callback;
70 #define START_CALLBACK(xmlctx) \
71   do {                                                    \
72     svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx);   \
73     SVN_ERR_ASSERT(!xmlctx__tmp->within_callback);        \
74     xmlctx__tmp->within_callback = TRUE;                  \
75   } while (0)
76 #define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE)
77 #else
78 #define START_CALLBACK(xmlctx)  /* empty */
79 #define END_CALLBACK(xmlctx)  /* empty */
80 #endif /* SVN_DEBUG  */
81
82   apr_pool_t *scratch_pool;
83
84 };
85
86 struct svn_ra_serf__xml_estate_t {
87   /* The current state value.  */
88   int state;
89
90   /* The xml tag that opened this state. Waiting for the tag to close.  */
91   svn_ra_serf__dav_props_t tag;
92
93   /* Should the CLOSED_CB function be called for custom processing when
94      this tag is closed?  */
95   svn_boolean_t custom_close;
96
97   /* A pool may be constructed for this state.  */
98   apr_pool_t *state_pool;
99
100   /* The namespaces extent for this state/element. This will start with
101      the parent's NS_LIST, and we will push new namespaces into our
102      local list. The parent will be unaffected by our locally-scoped data. */
103   svn_ra_serf__ns_t *ns_list;
104
105   /* Any collected attribute values. char * -> svn_string_t *. May be NULL
106      if no attributes have been collected.  */
107   apr_hash_t *attrs;
108
109   /* Any collected cdata. May be NULL if no cdata is being collected.  */
110   svn_stringbuf_t *cdata;
111
112   /* Previous/outer state.  */
113   svn_ra_serf__xml_estate_t *prev;
114
115 };
116
117
118 static void
119 define_namespaces(svn_ra_serf__ns_t **ns_list,
120                   const char *const *attrs,
121                   apr_pool_t *(*get_pool)(void *baton),
122                   void *baton)
123 {
124   const char *const *tmp_attrs = attrs;
125
126   for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
127     {
128       if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
129         {
130           const svn_ra_serf__ns_t *cur_ns;
131           svn_boolean_t found = FALSE;
132           const char *prefix;
133
134           /* The empty prefix, or a named-prefix.  */
135           if (tmp_attrs[0][5] == ':')
136             prefix = &tmp_attrs[0][6];
137           else
138             prefix = "";
139
140           /* Have we already defined this ns previously? */
141           for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
142             {
143               if (strcmp(cur_ns->namespace, prefix) == 0)
144                 {
145                   found = TRUE;
146                   break;
147                 }
148             }
149
150           if (!found)
151             {
152               apr_pool_t *pool;
153               svn_ra_serf__ns_t *new_ns;
154
155               if (get_pool)
156                 pool = get_pool(baton);
157               else
158                 pool = baton;
159               new_ns = apr_palloc(pool, sizeof(*new_ns));
160               new_ns->namespace = apr_pstrdup(pool, prefix);
161               new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
162
163               /* Push into the front of NS_LIST. Parent states will point
164                  to later in the chain, so will be unaffected by
165                  shadowing/other namespaces pushed onto NS_LIST.  */
166               new_ns->next = *ns_list;
167               *ns_list = new_ns;
168             }
169         }
170     }
171 }
172
173
174 void
175 svn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list,
176                        const char *const *attrs,
177                        apr_pool_t *result_pool)
178 {
179   define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool);
180 }
181
182
183 /*
184  * Look up NAME in the NS_LIST list for previously declared namespace
185  * definitions and return a DAV_PROPS_T-tuple that has values.
186  */
187 void
188 svn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
189                        const svn_ra_serf__ns_t *ns_list,
190                        const char *name)
191 {
192   const char *colon;
193
194   colon = strchr(name, ':');
195   if (colon)
196     {
197       const svn_ra_serf__ns_t *ns;
198
199       for (ns = ns_list; ns; ns = ns->next)
200         {
201           if (strncmp(ns->namespace, name, colon - name) == 0)
202             {
203               returned_prop_name->namespace = ns->url;
204               returned_prop_name->name = colon + 1;
205               return;
206             }
207         }
208     }
209   else
210     {
211       const svn_ra_serf__ns_t *ns;
212
213       for (ns = ns_list; ns; ns = ns->next)
214         {
215           if (! ns->namespace[0])
216             {
217               returned_prop_name->namespace = ns->url;
218               returned_prop_name->name = name;
219               return;
220             }
221         }
222     }
223
224   /* If the prefix is not found, then the name is NOT within a
225      namespace.  */
226   returned_prop_name->namespace = "";
227   returned_prop_name->name = name;
228 }
229
230
231 #define XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
232
233 void
234 svn_ra_serf__add_xml_header_buckets(serf_bucket_t *agg_bucket,
235                                     serf_bucket_alloc_t *bkt_alloc)
236 {
237   serf_bucket_t *tmp;
238
239   tmp = SERF_BUCKET_SIMPLE_STRING_LEN(XML_HEADER, sizeof(XML_HEADER) - 1,
240                                       bkt_alloc);
241   serf_bucket_aggregate_append(agg_bucket, tmp);
242 }
243
244 void
245 svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
246                                   serf_bucket_alloc_t *bkt_alloc,
247                                   const char *tag, ...)
248 {
249   va_list ap;
250   const char *key;
251   serf_bucket_t *tmp;
252
253   tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
254   serf_bucket_aggregate_append(agg_bucket, tmp);
255
256   tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
257   serf_bucket_aggregate_append(agg_bucket, tmp);
258
259   va_start(ap, tag);
260   while ((key = va_arg(ap, char *)) != NULL)
261     {
262       const char *val = va_arg(ap, const char *);
263       if (val)
264         {
265           tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
266           serf_bucket_aggregate_append(agg_bucket, tmp);
267
268           tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
269           serf_bucket_aggregate_append(agg_bucket, tmp);
270
271           tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
272           serf_bucket_aggregate_append(agg_bucket, tmp);
273
274           tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
275           serf_bucket_aggregate_append(agg_bucket, tmp);
276
277           tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
278           serf_bucket_aggregate_append(agg_bucket, tmp);
279         }
280     }
281   va_end(ap);
282
283   tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
284   serf_bucket_aggregate_append(agg_bucket, tmp);
285 }
286
287 void
288 svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
289                                    serf_bucket_alloc_t *bkt_alloc,
290                                    const char *tag)
291 {
292   serf_bucket_t *tmp;
293
294   tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</", 2, bkt_alloc);
295   serf_bucket_aggregate_append(agg_bucket, tmp);
296
297   tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
298   serf_bucket_aggregate_append(agg_bucket, tmp);
299
300   tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
301   serf_bucket_aggregate_append(agg_bucket, tmp);
302 }
303
304 void
305 svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket,
306                                    serf_bucket_alloc_t *bkt_alloc,
307                                    const char *data, apr_size_t len)
308 {
309   const char *end = data + len;
310   const char *p = data, *q;
311   serf_bucket_t *tmp_bkt;
312
313   while (1)
314     {
315       /* Find a character which needs to be quoted and append bytes up
316          to that point.  Strictly speaking, '>' only needs to be
317          quoted if it follows "]]", but it's easier to quote it all
318          the time.
319
320          So, why are we escaping '\r' here?  Well, according to the
321          XML spec, '\r\n' gets converted to '\n' during XML parsing.
322          Also, any '\r' not followed by '\n' is converted to '\n'.  By
323          golly, if we say we want to escape a '\r', we want to make
324          sure it remains a '\r'!  */
325       q = p;
326       while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
327         q++;
328
329
330       tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(p, q - p, bkt_alloc);
331       serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
332
333       /* We may already be a winner.  */
334       if (q == end)
335         break;
336
337       /* Append the entity reference for the character.  */
338       if (*q == '&')
339         {
340           tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&amp;", sizeof("&amp;") - 1,
341                                                   bkt_alloc);
342           serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
343         }
344       else if (*q == '<')
345         {
346           tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&lt;", sizeof("&lt;") - 1,
347                                                   bkt_alloc);
348           serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
349         }
350       else if (*q == '>')
351         {
352           tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&gt;", sizeof("&gt;") - 1,
353                                                   bkt_alloc);
354           serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
355         }
356       else if (*q == '\r')
357         {
358           tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&#13;", sizeof("&#13;") - 1,
359                                                   bkt_alloc);
360           serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
361         }
362
363       p = q + 1;
364     }
365 }
366
367 void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag,
368                                   const char *value,
369                                   serf_bucket_alloc_t *bkt_alloc)
370 {
371   svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, NULL);
372
373   if (value)
374     {
375       svn_ra_serf__add_cdata_len_buckets(agg_bucket, bkt_alloc,
376                                          value, strlen(value));
377     }
378
379   svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag);
380 }
381
382 void
383 svn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser,
384                             int state)
385 {
386   svn_ra_serf__xml_state_t *new_state;
387
388   if (!parser->free_state)
389     {
390       new_state = apr_palloc(parser->pool, sizeof(*new_state));
391       new_state->pool = svn_pool_create(parser->pool);
392     }
393   else
394     {
395       new_state = parser->free_state;
396       parser->free_state = parser->free_state->prev;
397
398       svn_pool_clear(new_state->pool);
399     }
400
401   if (parser->state)
402     {
403       new_state->private = parser->state->private;
404       new_state->ns_list = parser->state->ns_list;
405     }
406   else
407     {
408       new_state->private = NULL;
409       new_state->ns_list = NULL;
410     }
411
412   new_state->current_state = state;
413
414   /* Add it to the state chain. */
415   new_state->prev = parser->state;
416   parser->state = new_state;
417 }
418
419 void svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser)
420 {
421   svn_ra_serf__xml_state_t *cur_state;
422
423   cur_state = parser->state;
424   parser->state = cur_state->prev;
425   cur_state->prev = parser->free_state;
426   parser->free_state = cur_state;
427 }
428
429
430 /* Return a pool for XES to use for self-alloc (and other specifics).  */
431 static apr_pool_t *
432 xes_pool(const svn_ra_serf__xml_estate_t *xes)
433 {
434   /* Move up through parent states looking for one with a pool. This
435      will always terminate since the initial state has a pool.  */
436   while (xes->state_pool == NULL)
437     xes = xes->prev;
438   return xes->state_pool;
439 }
440
441
442 static void
443 ensure_pool(svn_ra_serf__xml_estate_t *xes)
444 {
445   if (xes->state_pool == NULL)
446     xes->state_pool = svn_pool_create(xes_pool(xes));
447 }
448
449
450 /* This callback is used by define_namespaces() to wait until a pool is
451    required before constructing it.  */
452 static apr_pool_t *
453 lazy_create_pool(void *baton)
454 {
455   svn_ra_serf__xml_estate_t *xes = baton;
456
457   ensure_pool(xes);
458   return xes->state_pool;
459 }
460
461 void
462 svn_ra_serf__xml_context_destroy(
463   svn_ra_serf__xml_context_t *xmlctx)
464 {
465   svn_pool_destroy(xmlctx->scratch_pool);
466 }
467
468 svn_ra_serf__xml_context_t *
469 svn_ra_serf__xml_context_create(
470   const svn_ra_serf__xml_transition_t *ttable,
471   svn_ra_serf__xml_opened_t opened_cb,
472   svn_ra_serf__xml_closed_t closed_cb,
473   svn_ra_serf__xml_cdata_t cdata_cb,
474   void *baton,
475   apr_pool_t *result_pool)
476 {
477   svn_ra_serf__xml_context_t *xmlctx;
478   svn_ra_serf__xml_estate_t *xes;
479
480   xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
481   xmlctx->ttable = ttable;
482   xmlctx->opened_cb = opened_cb;
483   xmlctx->closed_cb = closed_cb;
484   xmlctx->cdata_cb = cdata_cb;
485   xmlctx->baton = baton;
486   xmlctx->scratch_pool = svn_pool_create(result_pool);
487
488   xes = apr_pcalloc(result_pool, sizeof(*xes));
489   /* XES->STATE == 0  */
490
491   /* Child states may use this pool to allocate themselves. If a child
492      needs to collect information, then it will construct a subpool and
493      will use that to allocate itself and its collected data.  */
494   xes->state_pool = result_pool;
495
496   xmlctx->current = xes;
497
498   return xmlctx;
499 }
500
501
502 apr_hash_t *
503 svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
504                               int stop_state)
505 {
506   apr_hash_t *data;
507   apr_pool_t *pool;
508
509   ensure_pool(xes);
510   pool = xes->state_pool;
511
512   data = apr_hash_make(pool);
513
514   for (; xes != NULL; xes = xes->prev)
515     {
516       if (xes->attrs != NULL)
517         {
518           apr_hash_index_t *hi;
519
520           for (hi = apr_hash_first(pool, xes->attrs); hi;
521                hi = apr_hash_next(hi))
522             {
523               const void *key;
524               apr_ssize_t klen;
525               void *val;
526
527               /* Parent name/value lifetimes are at least as long as POOL.  */
528               apr_hash_this(hi, &key, &klen, &val);
529               apr_hash_set(data, key, klen, val);
530             }
531         }
532
533       if (xes->state == stop_state)
534         break;
535     }
536
537   return data;
538 }
539
540
541 void
542 svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
543                       int state,
544                       const char *name,
545                       const char *value)
546 {
547   svn_ra_serf__xml_estate_t *scan;
548
549   for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev)
550     /* pass */ ;
551
552   SVN_ERR_ASSERT_NO_RETURN(scan != NULL);
553
554   /* Make sure the target state has a pool.  */
555   ensure_pool(scan);
556
557   /* ... and attribute storage.  */
558   if (scan->attrs == NULL)
559     scan->attrs = apr_hash_make(scan->state_pool);
560
561   /* In all likelihood, NAME is a string constant. But we can't really
562      be sure. And it isn't like we're storing a billion of these into
563      the state pool.  */
564   svn_hash_sets(scan->attrs,
565                 apr_pstrdup(scan->state_pool, name),
566                 apr_pstrdup(scan->state_pool, value));
567 }
568
569
570 apr_pool_t *
571 svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
572 {
573   /* If they asked for a pool, then ensure that we have one to provide.  */
574   ensure_pool(xes);
575
576   return xes->state_pool;
577 }
578
579
580 svn_error_t *
581 svn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
582                           const char *raw_name,
583                           const char *const *attrs)
584 {
585   svn_ra_serf__xml_estate_t *current = xmlctx->current;
586   svn_ra_serf__dav_props_t elemname;
587   const svn_ra_serf__xml_transition_t *scan;
588   apr_pool_t *new_pool;
589   svn_ra_serf__xml_estate_t *new_xes;
590
591   /* If we're waiting for an element to close, then just ignore all
592      other element-opens.  */
593   if (xmlctx->waiting.namespace != NULL)
594     return SVN_NO_ERROR;
595
596   /* Look for xmlns: attributes. Lazily create the state pool if any
597      were found.  */
598   define_namespaces(&current->ns_list, attrs, lazy_create_pool, current);
599
600   svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name);
601
602   for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
603     {
604       if (scan->from_state != current->state)
605         continue;
606
607       /* Wildcard tag match.  */
608       if (*scan->name == '*')
609         break;
610
611       /* Found a specific transition.  */
612       if (strcmp(elemname.name, scan->name) == 0
613           && strcmp(elemname.namespace, scan->ns) == 0)
614         break;
615     }
616   if (scan->ns == NULL)
617     {
618       if (current->state == 0)
619         {
620           return svn_error_createf(
621                         SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
622                         _("XML Parsing failed: Unexpected root element '%s'"),
623                         elemname.name);
624         }
625
626       xmlctx->waiting = elemname;
627       /* ### return?  */
628       return SVN_NO_ERROR;
629     }
630
631   /* We should not be told to collect cdata if the closed_cb will not
632      be called.  */
633   SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
634
635   /* Found a transition. Make it happen.  */
636
637   /* ### todo. push state  */
638
639   /* ### how to use free states?  */
640   /* This state should be allocated in the extent pool. If we will be
641      collecting information for this state, then construct a subpool.
642
643      ### potentially optimize away the subpool if none of the
644      ### attributes are present. subpools are cheap, tho...  */
645   new_pool = xes_pool(current);
646   if (scan->collect_cdata || scan->collect_attrs[0])
647     {
648       new_pool = svn_pool_create(new_pool);
649
650       /* Prep the new state.  */
651       new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
652       new_xes->state_pool = new_pool;
653
654       /* If we're supposed to collect cdata, then set up a buffer for
655          this. The existence of this buffer will instruct our cdata
656          callback to collect the cdata.  */
657       if (scan->collect_cdata)
658         new_xes->cdata = svn_stringbuf_create_empty(new_pool);
659
660       if (scan->collect_attrs[0] != NULL)
661         {
662           const char *const *saveattr = &scan->collect_attrs[0];
663
664           new_xes->attrs = apr_hash_make(new_pool);
665           for (; *saveattr != NULL; ++saveattr)
666             {
667               const char *name;
668               const char *value;
669
670               if (**saveattr == '?')
671                 {
672                   name = *saveattr + 1;
673                   value = svn_xml_get_attr_value(name, attrs);
674                 }
675               else
676                 {
677                   name = *saveattr;
678                   value = svn_xml_get_attr_value(name, attrs);
679                   if (value == NULL)
680                     return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND,
681                                              NULL,
682                                              _("Missing XML attribute: '%s'"),
683                                              name);
684                 }
685
686               if (value)
687                 svn_hash_sets(new_xes->attrs, name,
688                               apr_pstrdup(new_pool, value));
689             }
690         }
691     }
692   else
693     {
694       /* Prep the new state.  */
695       new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
696       /* STATE_POOL remains NULL.  */
697     }
698
699   /* Some basic copies to set up the new estate.  */
700   new_xes->state = scan->to_state;
701   new_xes->tag.name = apr_pstrdup(new_pool, elemname.name);
702   new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace);
703   new_xes->custom_close = scan->custom_close;
704
705   /* Start with the parent's namespace set.  */
706   new_xes->ns_list = current->ns_list;
707
708   /* The new state is prepared. Make it current.  */
709   new_xes->prev = current;
710   xmlctx->current = new_xes;
711
712   if (xmlctx->opened_cb)
713     {
714       START_CALLBACK(xmlctx);
715       SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
716                                 new_xes->state, &new_xes->tag,
717                                 xmlctx->scratch_pool));
718       END_CALLBACK(xmlctx);
719       svn_pool_clear(xmlctx->scratch_pool);
720     }
721
722   return SVN_NO_ERROR;
723 }
724
725
726 svn_error_t *
727 svn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
728                         const char *raw_name)
729 {
730   svn_ra_serf__xml_estate_t *xes = xmlctx->current;
731   svn_ra_serf__dav_props_t elemname;
732
733   svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name);
734
735   if (xmlctx->waiting.namespace != NULL)
736     {
737       /* If this element is not the closer, then keep waiting... */
738       if (strcmp(elemname.name, xmlctx->waiting.name) != 0
739           || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0)
740         return SVN_NO_ERROR;
741
742       /* Found it. Stop waiting, and go back for more.  */
743       xmlctx->waiting.namespace = NULL;
744       return SVN_NO_ERROR;
745     }
746
747   /* We should be looking at the same tag that opened the current state.
748
749      Unknown elements are simply skipped, so we wouldn't reach this check.
750
751      Known elements push a new state for a given tag. Some other elemname
752      would imply closing an ancestor tag (where did ours go?) or a spurious
753      tag closure.  */
754   if (strcmp(elemname.name, xes->tag.name) != 0
755       || strcmp(elemname.namespace, xes->tag.namespace) != 0)
756     return svn_error_create(SVN_ERR_XML_MALFORMED, NULL,
757                             _("The response contains invalid XML"));
758
759   if (xes->custom_close)
760     {
761       const svn_string_t *cdata;
762
763       if (xes->cdata)
764         {
765           cdata = svn_stringbuf__morph_into_string(xes->cdata);
766 #ifdef SVN_DEBUG
767           /* We might toss the pool holding this structure, but it could also
768              be within a parent pool. In any case, for safety's sake, disable
769              the stringbuf against future Badness.  */
770           xes->cdata->pool = NULL;
771 #endif
772         }
773       else
774         cdata = NULL;
775
776       START_CALLBACK(xmlctx);
777       SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
778                                 cdata, xes->attrs,
779                                 xmlctx->scratch_pool));
780       END_CALLBACK(xmlctx);
781       svn_pool_clear(xmlctx->scratch_pool);
782     }
783
784   /* Pop the state.  */
785   xmlctx->current = xes->prev;
786
787   /* ### not everything should go on the free state list. XES may go
788      ### away with the state pool.  */
789   xes->prev = xmlctx->free_states;
790   xmlctx->free_states = xes;
791
792   /* If there is a STATE_POOL, then toss it. This will get rid of as much
793      memory as possible. Potentially the XES (if we didn't create a pool
794      right away, then XES may be in a parent pool).  */
795   if (xes->state_pool)
796     svn_pool_destroy(xes->state_pool);
797
798   return SVN_NO_ERROR;
799 }
800
801
802 svn_error_t *
803 svn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
804                           const char *data,
805                           apr_size_t len)
806 {
807   /* If we are waiting for a closing tag, then we are uninterested in
808      the cdata. Just return.  */
809   if (xmlctx->waiting.namespace != NULL)
810     return SVN_NO_ERROR;
811
812   /* If the current state is collecting cdata, then copy the cdata.  */
813   if (xmlctx->current->cdata != NULL)
814     {
815       svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
816     }
817   /* ... else if a CDATA_CB has been supplied, then invoke it for
818      all states.  */
819   else if (xmlctx->cdata_cb != NULL)
820     {
821       START_CALLBACK(xmlctx);
822       SVN_ERR(xmlctx->cdata_cb(xmlctx->current,
823                                xmlctx->baton,
824                                xmlctx->current->state,
825                                data, len,
826                                xmlctx->scratch_pool));
827       END_CALLBACK(xmlctx);
828       svn_pool_clear(xmlctx->scratch_pool);
829     }
830
831   return SVN_NO_ERROR;
832 }
833