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
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
44
45 /* Read/write chunks of this size into the spillbuf. */
46 #define PARSE_CHUNK_SIZE 8000
47
48
49 struct svn_ra_serf__xml_context_t {
50 /* Current state information. */
51 svn_ra_serf__xml_estate_t *current;
52
53 /* If WAITING >= then we are waiting for an element to close before
54 resuming events. The number stored here is the amount of nested
55 elements open. The Xml parser will make sure the document is well
56 formed. */
57 int waiting;
58
59 /* The transition table. */
60 const svn_ra_serf__xml_transition_t *ttable;
61
62 /* The callback information. */
63 svn_ra_serf__xml_opened_t opened_cb;
64 svn_ra_serf__xml_closed_t closed_cb;
65 svn_ra_serf__xml_cdata_t cdata_cb;
66 void *baton;
67
68 /* Linked list of free states. */
69 svn_ra_serf__xml_estate_t *free_states;
70
71 #ifdef SVN_DEBUG
72 /* Used to verify we are not re-entering a callback, specifically to
73 ensure SCRATCH_POOL is not cleared while an outer callback is
74 trying to use it. */
75 svn_boolean_t within_callback;
76 #define START_CALLBACK(xmlctx) \
77 do { \
78 svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx); \
79 SVN_ERR_ASSERT(!xmlctx__tmp->within_callback); \
80 xmlctx__tmp->within_callback = TRUE; \
81 } while (0)
82 #define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE)
83 #else
84 #define START_CALLBACK(xmlctx) /* empty */
85 #define END_CALLBACK(xmlctx) /* empty */
86 #endif /* SVN_DEBUG */
87
88 apr_pool_t *scratch_pool;
89
90 };
91
92 /* Structure which represents an XML namespace. */
93 typedef struct svn_ra_serf__ns_t {
94 /* The assigned name. */
95 const char *xmlns;
96 /* The full URL for this namespace. */
97 const char *url;
98 /* The next namespace in our list. */
99 struct svn_ra_serf__ns_t *next;
100 } svn_ra_serf__ns_t;
101
102 struct svn_ra_serf__xml_estate_t {
103 /* The current state value. */
104 int state;
105
106 /* The xml tag that opened this state. Waiting for the tag to close. */
107 svn_ra_serf__dav_props_t tag;
108
109 /* Should the CLOSED_CB function be called for custom processing when
110 this tag is closed? */
111 svn_boolean_t custom_close;
112
113 /* A pool may be constructed for this state. */
114 apr_pool_t *state_pool;
115
116 /* The namespaces extent for this state/element. This will start with
117 the parent's NS_LIST, and we will push new namespaces into our
118 local list. The parent will be unaffected by our locally-scoped data. */
119 svn_ra_serf__ns_t *ns_list;
120
121 /* Any collected attribute values. char * -> svn_string_t *. May be NULL
122 if no attributes have been collected. */
123 apr_hash_t *attrs;
124
125 /* Any collected cdata. May be NULL if no cdata is being collected. */
126 svn_stringbuf_t *cdata;
127
128 /* Previous/outer state. */
129 svn_ra_serf__xml_estate_t *prev;
130
131 };
132
133 struct expat_ctx_t {
134 svn_ra_serf__xml_context_t *xmlctx;
135 svn_xml_parser_t *parser;
136 svn_ra_serf__handler_t *handler;
137 const int *expected_status;
138
139 /* Do not use this pool for allocation. It is merely recorded for running
140 the cleanup handler. */
141 apr_pool_t *cleanup_pool;
142 };
143
144
145 static void
define_namespaces(svn_ra_serf__ns_t ** ns_list,const char * const * attrs,apr_pool_t * (* get_pool)(void * baton),void * baton)146 define_namespaces(svn_ra_serf__ns_t **ns_list,
147 const char *const *attrs,
148 apr_pool_t *(*get_pool)(void *baton),
149 void *baton)
150 {
151 const char *const *tmp_attrs = attrs;
152
153 for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2)
154 {
155 if (strncmp(*tmp_attrs, "xmlns", 5) == 0)
156 {
157 const svn_ra_serf__ns_t *cur_ns;
158 svn_boolean_t found = FALSE;
159 const char *prefix;
160
161 /* The empty prefix, or a named-prefix. */
162 if (tmp_attrs[0][5] == ':')
163 prefix = &tmp_attrs[0][6];
164 else
165 prefix = "";
166
167 /* Have we already defined this ns previously? */
168 for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next)
169 {
170 if (strcmp(cur_ns->xmlns, prefix) == 0)
171 {
172 found = TRUE;
173 break;
174 }
175 }
176
177 if (!found)
178 {
179 apr_pool_t *pool;
180 svn_ra_serf__ns_t *new_ns;
181
182 if (get_pool)
183 pool = get_pool(baton);
184 else
185 pool = baton;
186 new_ns = apr_palloc(pool, sizeof(*new_ns));
187 new_ns->xmlns = apr_pstrdup(pool, prefix);
188 new_ns->url = apr_pstrdup(pool, tmp_attrs[1]);
189
190 /* Push into the front of NS_LIST. Parent states will point
191 to later in the chain, so will be unaffected by
192 shadowing/other namespaces pushed onto NS_LIST. */
193 new_ns->next = *ns_list;
194 *ns_list = new_ns;
195 }
196 }
197 }
198 }
199
200 /*
201 * Look up @a name in the @a ns_list list for previously declared namespace
202 * definitions.
203 *
204 * Return (in @a *returned_prop_name) a #svn_ra_serf__dav_props_t tuple
205 * representing the expanded name.
206 */
207 static void
expand_ns(svn_ra_serf__dav_props_t * returned_prop_name,const svn_ra_serf__ns_t * ns_list,const char * name)208 expand_ns(svn_ra_serf__dav_props_t *returned_prop_name,
209 const svn_ra_serf__ns_t *ns_list,
210 const char *name)
211 {
212 const char *colon;
213
214 colon = strchr(name, ':');
215 if (colon)
216 {
217 const svn_ra_serf__ns_t *ns;
218
219 for (ns = ns_list; ns; ns = ns->next)
220 {
221 if (strncmp(ns->xmlns, name, colon - name) == 0)
222 {
223 returned_prop_name->xmlns = ns->url;
224 returned_prop_name->name = colon + 1;
225 return;
226 }
227 }
228 }
229 else
230 {
231 const svn_ra_serf__ns_t *ns;
232
233 for (ns = ns_list; ns; ns = ns->next)
234 {
235 if (! ns->xmlns[0])
236 {
237 returned_prop_name->xmlns = ns->url;
238 returned_prop_name->name = name;
239 return;
240 }
241 }
242 }
243
244 /* If the prefix is not found, then the name is NOT within a
245 namespace. */
246 returned_prop_name->xmlns = "";
247 returned_prop_name->name = name;
248 }
249
250
251 #define XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
252
253 void
svn_ra_serf__add_xml_header_buckets(serf_bucket_t * agg_bucket,serf_bucket_alloc_t * bkt_alloc)254 svn_ra_serf__add_xml_header_buckets(serf_bucket_t *agg_bucket,
255 serf_bucket_alloc_t *bkt_alloc)
256 {
257 serf_bucket_t *tmp;
258
259 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(XML_HEADER, sizeof(XML_HEADER) - 1,
260 bkt_alloc);
261 serf_bucket_aggregate_append(agg_bucket, tmp);
262 }
263
264 void
svn_ra_serf__add_open_tag_buckets(serf_bucket_t * agg_bucket,serf_bucket_alloc_t * bkt_alloc,const char * tag,...)265 svn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket,
266 serf_bucket_alloc_t *bkt_alloc,
267 const char *tag, ...)
268 {
269 va_list ap;
270 const char *key;
271 serf_bucket_t *tmp;
272
273 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
274 serf_bucket_aggregate_append(agg_bucket, tmp);
275
276 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
277 serf_bucket_aggregate_append(agg_bucket, tmp);
278
279 va_start(ap, tag);
280 while ((key = va_arg(ap, char *)) != NULL)
281 {
282 const char *val = va_arg(ap, const char *);
283 if (val)
284 {
285 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
286 serf_bucket_aggregate_append(agg_bucket, tmp);
287
288 tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
289 serf_bucket_aggregate_append(agg_bucket, tmp);
290
291 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
292 serf_bucket_aggregate_append(agg_bucket, tmp);
293
294 tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
295 serf_bucket_aggregate_append(agg_bucket, tmp);
296
297 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
298 serf_bucket_aggregate_append(agg_bucket, tmp);
299 }
300 }
301 va_end(ap);
302
303 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
304 serf_bucket_aggregate_append(agg_bucket, tmp);
305 }
306
307 void
svn_ra_serf__add_empty_tag_buckets(serf_bucket_t * agg_bucket,serf_bucket_alloc_t * bkt_alloc,const char * tag,...)308 svn_ra_serf__add_empty_tag_buckets(serf_bucket_t *agg_bucket,
309 serf_bucket_alloc_t *bkt_alloc,
310 const char *tag, ...)
311 {
312 va_list ap;
313 const char *key;
314 serf_bucket_t *tmp;
315
316 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc);
317 serf_bucket_aggregate_append(agg_bucket, tmp);
318
319 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
320 serf_bucket_aggregate_append(agg_bucket, tmp);
321
322 va_start(ap, tag);
323 while ((key = va_arg(ap, char *)) != NULL)
324 {
325 const char *val = va_arg(ap, const char *);
326 if (val)
327 {
328 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc);
329 serf_bucket_aggregate_append(agg_bucket, tmp);
330
331 tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc);
332 serf_bucket_aggregate_append(agg_bucket, tmp);
333
334 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc);
335 serf_bucket_aggregate_append(agg_bucket, tmp);
336
337 tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc);
338 serf_bucket_aggregate_append(agg_bucket, tmp);
339
340 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc);
341 serf_bucket_aggregate_append(agg_bucket, tmp);
342 }
343 }
344 va_end(ap);
345
346 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("/>", 2, bkt_alloc);
347 serf_bucket_aggregate_append(agg_bucket, tmp);
348 }
349
350 void
svn_ra_serf__add_close_tag_buckets(serf_bucket_t * agg_bucket,serf_bucket_alloc_t * bkt_alloc,const char * tag)351 svn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket,
352 serf_bucket_alloc_t *bkt_alloc,
353 const char *tag)
354 {
355 serf_bucket_t *tmp;
356
357 tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</", 2, bkt_alloc);
358 serf_bucket_aggregate_append(agg_bucket, tmp);
359
360 tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc);
361 serf_bucket_aggregate_append(agg_bucket, tmp);
362
363 tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc);
364 serf_bucket_aggregate_append(agg_bucket, tmp);
365 }
366
367 void
svn_ra_serf__add_cdata_len_buckets(serf_bucket_t * agg_bucket,serf_bucket_alloc_t * bkt_alloc,const char * data,apr_size_t len)368 svn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket,
369 serf_bucket_alloc_t *bkt_alloc,
370 const char *data, apr_size_t len)
371 {
372 const char *end = data + len;
373 const char *p = data, *q;
374 serf_bucket_t *tmp_bkt;
375
376 while (1)
377 {
378 /* Find a character which needs to be quoted and append bytes up
379 to that point. Strictly speaking, '>' only needs to be
380 quoted if it follows "]]", but it's easier to quote it all
381 the time.
382
383 So, why are we escaping '\r' here? Well, according to the
384 XML spec, '\r\n' gets converted to '\n' during XML parsing.
385 Also, any '\r' not followed by '\n' is converted to '\n'. By
386 golly, if we say we want to escape a '\r', we want to make
387 sure it remains a '\r'! */
388 q = p;
389 while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r')
390 q++;
391
392
393 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(p, q - p, bkt_alloc);
394 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
395
396 /* We may already be a winner. */
397 if (q == end)
398 break;
399
400 /* Append the entity reference for the character. */
401 if (*q == '&')
402 {
403 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&", sizeof("&") - 1,
404 bkt_alloc);
405 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
406 }
407 else if (*q == '<')
408 {
409 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("<", sizeof("<") - 1,
410 bkt_alloc);
411 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
412 }
413 else if (*q == '>')
414 {
415 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(">", sizeof(">") - 1,
416 bkt_alloc);
417 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
418 }
419 else if (*q == '\r')
420 {
421 tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(" ", sizeof(" ") - 1,
422 bkt_alloc);
423 serf_bucket_aggregate_append(agg_bucket, tmp_bkt);
424 }
425
426 p = q + 1;
427 }
428 }
429
svn_ra_serf__add_tag_buckets(serf_bucket_t * agg_bucket,const char * tag,const char * value,serf_bucket_alloc_t * bkt_alloc)430 void svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag,
431 const char *value,
432 serf_bucket_alloc_t *bkt_alloc)
433 {
434 svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, SVN_VA_NULL);
435
436 if (value)
437 {
438 svn_ra_serf__add_cdata_len_buckets(agg_bucket, bkt_alloc,
439 value, strlen(value));
440 }
441
442 svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag);
443 }
444
445 /* Return a pool for XES to use for self-alloc (and other specifics). */
446 static apr_pool_t *
xes_pool(const svn_ra_serf__xml_estate_t * xes)447 xes_pool(const svn_ra_serf__xml_estate_t *xes)
448 {
449 /* Move up through parent states looking for one with a pool. This
450 will always terminate since the initial state has a pool. */
451 while (xes->state_pool == NULL)
452 xes = xes->prev;
453 return xes->state_pool;
454 }
455
456
457 static void
ensure_pool(svn_ra_serf__xml_estate_t * xes)458 ensure_pool(svn_ra_serf__xml_estate_t *xes)
459 {
460 if (xes->state_pool == NULL)
461 xes->state_pool = svn_pool_create(xes_pool(xes));
462 }
463
464
465 /* This callback is used by define_namespaces() to wait until a pool is
466 required before constructing it. */
467 static apr_pool_t *
lazy_create_pool(void * baton)468 lazy_create_pool(void *baton)
469 {
470 svn_ra_serf__xml_estate_t *xes = baton;
471
472 ensure_pool(xes);
473 return xes->state_pool;
474 }
475
476 svn_error_t *
svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t * xmlctx)477 svn_ra_serf__xml_context_done(svn_ra_serf__xml_context_t *xmlctx)
478 {
479 if (xmlctx->current->prev)
480 {
481 /* Probably unreachable as this would be an xml parser error */
482 return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
483 _("XML stream truncated: closing '%s' missing"),
484 xmlctx->current->tag.name);
485 }
486 else if (! xmlctx->free_states)
487 {
488 /* If we have no items on the free_states list, we didn't push anything,
489 which tells us that we found an empty xml body */
490 const svn_ra_serf__xml_transition_t *scan;
491 const svn_ra_serf__xml_transition_t *document = NULL;
492 const char *msg;
493
494 for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
495 {
496 if (scan->from_state == XML_STATE_INITIAL)
497 {
498 if (document != NULL)
499 {
500 document = NULL; /* Multiple document elements defined */
501 break;
502 }
503 document = scan;
504 }
505 }
506
507 if (document)
508 msg = apr_psprintf(xmlctx->scratch_pool, "'%s' element not found",
509 document->name);
510 else
511 msg = _("document element not found");
512
513 return svn_error_createf(SVN_ERR_XML_MALFORMED, NULL,
514 _("XML stream truncated: %s"),
515 msg);
516 }
517
518 svn_pool_destroy(xmlctx->scratch_pool);
519 return SVN_NO_ERROR;
520 }
521
522 svn_ra_serf__xml_context_t *
svn_ra_serf__xml_context_create(const svn_ra_serf__xml_transition_t * ttable,svn_ra_serf__xml_opened_t opened_cb,svn_ra_serf__xml_closed_t closed_cb,svn_ra_serf__xml_cdata_t cdata_cb,void * baton,apr_pool_t * result_pool)523 svn_ra_serf__xml_context_create(
524 const svn_ra_serf__xml_transition_t *ttable,
525 svn_ra_serf__xml_opened_t opened_cb,
526 svn_ra_serf__xml_closed_t closed_cb,
527 svn_ra_serf__xml_cdata_t cdata_cb,
528 void *baton,
529 apr_pool_t *result_pool)
530 {
531 svn_ra_serf__xml_context_t *xmlctx;
532 svn_ra_serf__xml_estate_t *xes;
533
534 xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx));
535 xmlctx->ttable = ttable;
536 xmlctx->opened_cb = opened_cb;
537 xmlctx->closed_cb = closed_cb;
538 xmlctx->cdata_cb = cdata_cb;
539 xmlctx->baton = baton;
540 xmlctx->scratch_pool = svn_pool_create(result_pool);
541
542 xes = apr_pcalloc(result_pool, sizeof(*xes));
543 /* XES->STATE == 0 */
544
545 /* Child states may use this pool to allocate themselves. If a child
546 needs to collect information, then it will construct a subpool and
547 will use that to allocate itself and its collected data. */
548 xes->state_pool = result_pool;
549
550 xmlctx->current = xes;
551
552 return xmlctx;
553 }
554
555
556 apr_hash_t *
svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t * xes,int stop_state)557 svn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes,
558 int stop_state)
559 {
560 apr_hash_t *data;
561 apr_pool_t *pool;
562
563 ensure_pool(xes);
564 pool = xes->state_pool;
565
566 data = apr_hash_make(pool);
567
568 for (; xes != NULL; xes = xes->prev)
569 {
570 if (xes->attrs != NULL)
571 {
572 apr_hash_index_t *hi;
573
574 for (hi = apr_hash_first(pool, xes->attrs); hi;
575 hi = apr_hash_next(hi))
576 {
577 const void *key;
578 apr_ssize_t klen;
579 void *val;
580
581 /* Parent name/value lifetimes are at least as long as POOL. */
582 apr_hash_this(hi, &key, &klen, &val);
583 apr_hash_set(data, key, klen, val);
584 }
585 }
586
587 if (xes->state == stop_state)
588 break;
589 }
590
591 return data;
592 }
593
594
595 void
svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t * xes,int state,const char * name,const char * value)596 svn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes,
597 int state,
598 const char *name,
599 const char *value)
600 {
601 svn_ra_serf__xml_estate_t *scan;
602
603 for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev)
604 /* pass */ ;
605
606 SVN_ERR_ASSERT_NO_RETURN(scan != NULL);
607
608 /* Make sure the target state has a pool. */
609 ensure_pool(scan);
610
611 /* ... and attribute storage. */
612 if (scan->attrs == NULL)
613 scan->attrs = apr_hash_make(scan->state_pool);
614
615 /* In all likelihood, NAME is a string constant. But we can't really
616 be sure. And it isn't like we're storing a billion of these into
617 the state pool. */
618 svn_hash_sets(scan->attrs,
619 apr_pstrdup(scan->state_pool, name),
620 apr_pstrdup(scan->state_pool, value));
621 }
622
623
624 apr_pool_t *
svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t * xes)625 svn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes)
626 {
627 /* If they asked for a pool, then ensure that we have one to provide. */
628 ensure_pool(xes);
629
630 return xes->state_pool;
631 }
632
633
634 static svn_error_t *
xml_cb_start(svn_ra_serf__xml_context_t * xmlctx,const char * raw_name,const char * const * attrs)635 xml_cb_start(svn_ra_serf__xml_context_t *xmlctx,
636 const char *raw_name,
637 const char *const *attrs)
638 {
639 svn_ra_serf__xml_estate_t *current = xmlctx->current;
640 svn_ra_serf__dav_props_t elemname;
641 const svn_ra_serf__xml_transition_t *scan;
642 apr_pool_t *new_pool;
643 svn_ra_serf__xml_estate_t *new_xes;
644
645 /* If we're waiting for an element to close, then just ignore all
646 other element-opens. */
647 if (xmlctx->waiting > 0)
648 {
649 xmlctx->waiting++;
650 return SVN_NO_ERROR;
651 }
652
653 /* Look for xmlns: attributes. Lazily create the state pool if any
654 were found. */
655 define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current);
656
657 expand_ns(&elemname, current->ns_list, raw_name);
658
659 for (scan = xmlctx->ttable; scan->ns != NULL; ++scan)
660 {
661 if (scan->from_state != current->state)
662 continue;
663
664 /* Wildcard tag match. */
665 if (*scan->name == '*')
666 break;
667
668 /* Found a specific transition. */
669 if (strcmp(elemname.name, scan->name) == 0
670 && strcmp(elemname.xmlns, scan->ns) == 0)
671 break;
672 }
673 if (scan->ns == NULL)
674 {
675 if (current->state == XML_STATE_INITIAL)
676 {
677 return svn_error_createf(
678 SVN_ERR_XML_UNEXPECTED_ELEMENT, NULL,
679 _("XML Parsing failed: Unexpected root element '%s'"),
680 elemname.name);
681 }
682
683 xmlctx->waiting++; /* Start waiting for the close tag */
684 return SVN_NO_ERROR;
685 }
686
687 /* We should not be told to collect cdata if the closed_cb will not
688 be called. */
689 SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close);
690
691 /* Found a transition. Make it happen. */
692
693 /* ### todo. push state */
694
695 /* ### how to use free states? */
696 /* This state should be allocated in the extent pool. If we will be
697 collecting information for this state, then construct a subpool.
698
699 ### potentially optimize away the subpool if none of the
700 ### attributes are present. subpools are cheap, tho... */
701 new_pool = xes_pool(current);
702 if (scan->collect_cdata || scan->collect_attrs[0])
703 {
704 new_pool = svn_pool_create(new_pool);
705
706 /* Prep the new state. */
707 new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
708 new_xes->state_pool = new_pool;
709
710 /* If we're supposed to collect cdata, then set up a buffer for
711 this. The existence of this buffer will instruct our cdata
712 callback to collect the cdata. */
713 if (scan->collect_cdata)
714 new_xes->cdata = svn_stringbuf_create_empty(new_pool);
715
716 if (scan->collect_attrs[0] != NULL)
717 {
718 const char *const *saveattr = &scan->collect_attrs[0];
719
720 new_xes->attrs = apr_hash_make(new_pool);
721 for (; *saveattr != NULL; ++saveattr)
722 {
723 const char *name;
724 const char *value;
725
726 if (**saveattr == '?')
727 {
728 name = *saveattr + 1;
729 value = svn_xml_get_attr_value(name, attrs);
730 }
731 else
732 {
733 name = *saveattr;
734 value = svn_xml_get_attr_value(name, attrs);
735 if (value == NULL)
736 return svn_error_createf(
737 SVN_ERR_XML_ATTRIB_NOT_FOUND,
738 NULL,
739 _("Missing XML attribute '%s' on '%s' element"),
740 name, scan->name);
741 }
742
743 if (value)
744 svn_hash_sets(new_xes->attrs, name,
745 apr_pstrdup(new_pool, value));
746 }
747 }
748 }
749 else
750 {
751 /* Prep the new state. */
752 new_xes = apr_pcalloc(new_pool, sizeof(*new_xes));
753 /* STATE_POOL remains NULL. */
754 }
755
756 /* Some basic copies to set up the new estate. */
757 new_xes->state = scan->to_state;
758 new_xes->tag.name = apr_pstrdup(new_pool, elemname.name);
759 new_xes->tag.xmlns = apr_pstrdup(new_pool, elemname.xmlns);
760 new_xes->custom_close = scan->custom_close;
761
762 /* Start with the parent's namespace set. */
763 new_xes->ns_list = current->ns_list;
764
765 /* The new state is prepared. Make it current. */
766 new_xes->prev = current;
767 xmlctx->current = new_xes;
768
769 if (xmlctx->opened_cb)
770 {
771 START_CALLBACK(xmlctx);
772 SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton,
773 new_xes->state, &new_xes->tag,
774 xmlctx->scratch_pool));
775 END_CALLBACK(xmlctx);
776 svn_pool_clear(xmlctx->scratch_pool);
777 }
778
779 return SVN_NO_ERROR;
780 }
781
782
783 static svn_error_t *
xml_cb_end(svn_ra_serf__xml_context_t * xmlctx,const char * raw_name)784 xml_cb_end(svn_ra_serf__xml_context_t *xmlctx,
785 const char *raw_name)
786 {
787 svn_ra_serf__xml_estate_t *xes = xmlctx->current;
788
789 if (xmlctx->waiting > 0)
790 {
791 xmlctx->waiting--;
792 return SVN_NO_ERROR;
793 }
794
795 if (xes->custom_close)
796 {
797 const svn_string_t *cdata;
798
799 if (xes->cdata)
800 {
801 cdata = svn_stringbuf__morph_into_string(xes->cdata);
802 #ifdef SVN_DEBUG
803 /* We might toss the pool holding this structure, but it could also
804 be within a parent pool. In any case, for safety's sake, disable
805 the stringbuf against future Badness. */
806 xes->cdata->pool = NULL;
807 #endif
808 }
809 else
810 cdata = NULL;
811
812 START_CALLBACK(xmlctx);
813 SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state,
814 cdata, xes->attrs,
815 xmlctx->scratch_pool));
816 END_CALLBACK(xmlctx);
817 svn_pool_clear(xmlctx->scratch_pool);
818 }
819
820 /* Pop the state. */
821 xmlctx->current = xes->prev;
822
823 /* ### not everything should go on the free state list. XES may go
824 ### away with the state pool. */
825 xes->prev = xmlctx->free_states;
826 xmlctx->free_states = xes;
827
828 /* If there is a STATE_POOL, then toss it. This will get rid of as much
829 memory as possible. Potentially the XES (if we didn't create a pool
830 right away, then XES may be in a parent pool). */
831 if (xes->state_pool)
832 svn_pool_destroy(xes->state_pool);
833
834 return SVN_NO_ERROR;
835 }
836
837
838 static svn_error_t *
xml_cb_cdata(svn_ra_serf__xml_context_t * xmlctx,const char * data,apr_size_t len)839 xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx,
840 const char *data,
841 apr_size_t len)
842 {
843 /* If we are waiting for a closing tag, then we are uninterested in
844 the cdata. Just return. */
845 if (xmlctx->waiting > 0)
846 return SVN_NO_ERROR;
847
848 /* If the current state is collecting cdata, then copy the cdata. */
849 if (xmlctx->current->cdata != NULL)
850 {
851 svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len);
852 }
853 /* ... else if a CDATA_CB has been supplied, then invoke it for
854 all states. */
855 else if (xmlctx->cdata_cb != NULL)
856 {
857 START_CALLBACK(xmlctx);
858 SVN_ERR(xmlctx->cdata_cb(xmlctx->current,
859 xmlctx->baton,
860 xmlctx->current->state,
861 data, len,
862 xmlctx->scratch_pool));
863 END_CALLBACK(xmlctx);
864 svn_pool_clear(xmlctx->scratch_pool);
865 }
866
867 return SVN_NO_ERROR;
868 }
869
870 /* Wrapper around svn_xml_parse */
871 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)872 parse_xml(struct expat_ctx_t *ectx, const char *data, apr_size_t len, svn_boolean_t is_final)
873 {
874 svn_error_t *err = svn_xml_parse(ectx->parser, data, len, is_final);
875
876 if (err && err->apr_err == SVN_ERR_XML_MALFORMED)
877 err = svn_error_create(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
878 _("The XML response contains invalid XML"));
879
880 return err;
881 }
882
883 /* Implements svn_xml_start_elem callback */
884 static void
expat_start(void * baton,const char * raw_name,const char ** attrs)885 expat_start(void *baton, const char *raw_name, const char **attrs)
886 {
887 struct expat_ctx_t *ectx = baton;
888 svn_error_t *err;
889
890 err = svn_error_trace(xml_cb_start(ectx->xmlctx, raw_name, attrs));
891
892 if (err)
893 svn_xml_signal_bailout(err, ectx->parser);
894 }
895
896
897 /* Implements svn_xml_end_elem callback */
898 static void
expat_end(void * baton,const char * raw_name)899 expat_end(void *baton, const char *raw_name)
900 {
901 struct expat_ctx_t *ectx = baton;
902 svn_error_t *err;
903
904 err = svn_error_trace(xml_cb_end(ectx->xmlctx, raw_name));
905
906 if (err)
907 svn_xml_signal_bailout(err, ectx->parser);
908 }
909
910
911 /* Implements svn_xml_char_data callback */
912 static void
expat_cdata(void * baton,const char * data,apr_size_t len)913 expat_cdata(void *baton, const char *data, apr_size_t len)
914 {
915 struct expat_ctx_t *ectx = baton;
916 svn_error_t *err;
917
918 err = svn_error_trace(xml_cb_cdata(ectx->xmlctx, data, len));
919
920 if (err)
921 svn_xml_signal_bailout(err, ectx->parser);
922 }
923
924
925 /* Implements svn_ra_serf__response_handler_t */
926 static svn_error_t *
expat_response_handler(serf_request_t * request,serf_bucket_t * response,void * baton,apr_pool_t * scratch_pool)927 expat_response_handler(serf_request_t *request,
928 serf_bucket_t *response,
929 void *baton,
930 apr_pool_t *scratch_pool)
931 {
932 struct expat_ctx_t *ectx = baton;
933 svn_boolean_t got_expected_status;
934
935 if (ectx->expected_status)
936 {
937 const int *status = ectx->expected_status;
938 got_expected_status = FALSE;
939
940 while (*status && ectx->handler->sline.code != *status)
941 status++;
942
943 got_expected_status = (*status) != 0;
944 }
945 else
946 got_expected_status = (ectx->handler->sline.code == 200);
947
948 if (!ectx->handler->server_error
949 && ((ectx->handler->sline.code < 200) || (ectx->handler->sline.code >= 300)
950 || ! got_expected_status))
951 {
952 /* By deferring to expect_empty_body(), it will make a choice on
953 how to handle the body. Whatever the decision, the core handler
954 will take over, and we will not be called again. */
955
956 /* ### This handles xml bodies as svn-errors (returned via serf context
957 ### loop), but ignores non-xml errors.
958
959 Current code depends on this behavior and checks itself while other
960 continues, and then verifies if work has been performed.
961
962 ### TODO: Make error checking consistent */
963
964 /* ### If !GOT_EXPECTED_STATUS, this should always produce an error */
965 return svn_error_trace(svn_ra_serf__expect_empty_body(
966 request, response, ectx->handler,
967 scratch_pool));
968 }
969
970 if (!ectx->parser)
971 {
972 ectx->parser = svn_xml_make_parser(ectx, expat_start, expat_end,
973 expat_cdata, ectx->cleanup_pool);
974 }
975
976 while (1)
977 {
978 apr_status_t status;
979 const char *data;
980 apr_size_t len;
981 svn_boolean_t at_eof = FALSE;
982
983 status = serf_bucket_read(response, PARSE_CHUNK_SIZE, &data, &len);
984 if (SERF_BUCKET_READ_ERROR(status))
985 return svn_ra_serf__wrap_err(status, NULL);
986 else if (APR_STATUS_IS_EOF(status))
987 at_eof = TRUE;
988
989 SVN_ERR(parse_xml(ectx, data, len, at_eof /* isFinal */));
990
991 /* The parsing went fine. What has the bucket told us? */
992 if (at_eof)
993 {
994 /* Make sure we actually got xml and clean up after parsing */
995 SVN_ERR(svn_ra_serf__xml_context_done(ectx->xmlctx));
996 }
997
998 if (status && !SERF_BUCKET_READ_ERROR(status))
999 {
1000 return svn_ra_serf__wrap_err(status, NULL);
1001 }
1002 }
1003
1004 /* NOTREACHED */
1005 }
1006
1007
1008 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)1009 svn_ra_serf__create_expat_handler(svn_ra_serf__session_t *session,
1010 svn_ra_serf__xml_context_t *xmlctx,
1011 const int *expected_status,
1012 apr_pool_t *result_pool)
1013 {
1014 svn_ra_serf__handler_t *handler;
1015 struct expat_ctx_t *ectx;
1016
1017 ectx = apr_pcalloc(result_pool, sizeof(*ectx));
1018 ectx->xmlctx = xmlctx;
1019 ectx->parser = NULL;
1020 ectx->expected_status = expected_status;
1021 ectx->cleanup_pool = result_pool;
1022
1023 handler = svn_ra_serf__create_handler(session, result_pool);
1024 handler->response_handler = expat_response_handler;
1025 handler->response_baton = ectx;
1026
1027 ectx->handler = handler;
1028
1029 return handler;
1030 }
1031