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 45struct 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 86struct 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 118static void 119define_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 174void 175svn_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 */ 187void 188svn_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 233void 234svn_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 244void 245svn_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 287void 288svn_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 304void 305svn_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("&", sizeof("&") - 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("<", sizeof("<") - 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(">", sizeof(">") - 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(" ", sizeof(" ") - 1, 359 bkt_alloc); 360 serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 361 } 362 363 p = q + 1; 364 } 365} 366 367void 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 382void 383svn_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 419void 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). */ 431static apr_pool_t * 432xes_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 442static void 443ensure_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. */ 452static apr_pool_t * 453lazy_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 461void 462svn_ra_serf__xml_context_destroy( 463 svn_ra_serf__xml_context_t *xmlctx) 464{ 465 svn_pool_destroy(xmlctx->scratch_pool); 466} 467 468svn_ra_serf__xml_context_t * 469svn_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 502apr_hash_t * 503svn_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 541void 542svn_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 570apr_pool_t * 571svn_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 580svn_error_t * 581svn_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(¤t->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 726svn_error_t * 727svn_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 802svn_error_t * 803svn_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 834