1251881Speter/* 2251881Speter * xml.c : standard XML parsing routines for ra_serf 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <apr_uri.h> 27251881Speter#include <serf.h> 28251881Speter 29251881Speter#include "svn_hash.h" 30251881Speter#include "svn_pools.h" 31251881Speter#include "svn_ra.h" 32251881Speter#include "svn_dav.h" 33251881Speter#include "svn_xml.h" 34251881Speter#include "../libsvn_ra/ra_loader.h" 35251881Speter#include "svn_config.h" 36251881Speter#include "svn_delta.h" 37251881Speter#include "svn_path.h" 38251881Speter 39251881Speter#include "svn_private_config.h" 40251881Speter#include "private/svn_string_private.h" 41251881Speter 42251881Speter#include "ra_serf.h" 43251881Speter 44251881Speter 45251881Speterstruct svn_ra_serf__xml_context_t { 46251881Speter /* Current state information. */ 47251881Speter svn_ra_serf__xml_estate_t *current; 48251881Speter 49251881Speter /* If WAITING.NAMESPACE != NULL, wait for NAMESPACE:NAME element to be 50251881Speter closed before looking for transitions from CURRENT->STATE. */ 51251881Speter svn_ra_serf__dav_props_t waiting; 52251881Speter 53251881Speter /* The transition table. */ 54251881Speter const svn_ra_serf__xml_transition_t *ttable; 55251881Speter 56251881Speter /* The callback information. */ 57251881Speter svn_ra_serf__xml_opened_t opened_cb; 58251881Speter svn_ra_serf__xml_closed_t closed_cb; 59251881Speter svn_ra_serf__xml_cdata_t cdata_cb; 60251881Speter void *baton; 61251881Speter 62251881Speter /* Linked list of free states. */ 63251881Speter svn_ra_serf__xml_estate_t *free_states; 64251881Speter 65251881Speter#ifdef SVN_DEBUG 66251881Speter /* Used to verify we are not re-entering a callback, specifically to 67251881Speter ensure SCRATCH_POOL is not cleared while an outer callback is 68251881Speter trying to use it. */ 69251881Speter svn_boolean_t within_callback; 70251881Speter#define START_CALLBACK(xmlctx) \ 71251881Speter do { \ 72251881Speter svn_ra_serf__xml_context_t *xmlctx__tmp = (xmlctx); \ 73251881Speter SVN_ERR_ASSERT(!xmlctx__tmp->within_callback); \ 74251881Speter xmlctx__tmp->within_callback = TRUE; \ 75251881Speter } while (0) 76251881Speter#define END_CALLBACK(xmlctx) ((xmlctx)->within_callback = FALSE) 77251881Speter#else 78251881Speter#define START_CALLBACK(xmlctx) /* empty */ 79251881Speter#define END_CALLBACK(xmlctx) /* empty */ 80251881Speter#endif /* SVN_DEBUG */ 81251881Speter 82251881Speter apr_pool_t *scratch_pool; 83251881Speter 84251881Speter}; 85251881Speter 86251881Speterstruct svn_ra_serf__xml_estate_t { 87251881Speter /* The current state value. */ 88251881Speter int state; 89251881Speter 90251881Speter /* The xml tag that opened this state. Waiting for the tag to close. */ 91251881Speter svn_ra_serf__dav_props_t tag; 92251881Speter 93251881Speter /* Should the CLOSED_CB function be called for custom processing when 94251881Speter this tag is closed? */ 95251881Speter svn_boolean_t custom_close; 96251881Speter 97251881Speter /* A pool may be constructed for this state. */ 98251881Speter apr_pool_t *state_pool; 99251881Speter 100251881Speter /* The namespaces extent for this state/element. This will start with 101251881Speter the parent's NS_LIST, and we will push new namespaces into our 102251881Speter local list. The parent will be unaffected by our locally-scoped data. */ 103251881Speter svn_ra_serf__ns_t *ns_list; 104251881Speter 105251881Speter /* Any collected attribute values. char * -> svn_string_t *. May be NULL 106251881Speter if no attributes have been collected. */ 107251881Speter apr_hash_t *attrs; 108251881Speter 109251881Speter /* Any collected cdata. May be NULL if no cdata is being collected. */ 110251881Speter svn_stringbuf_t *cdata; 111251881Speter 112251881Speter /* Previous/outer state. */ 113251881Speter svn_ra_serf__xml_estate_t *prev; 114251881Speter 115251881Speter}; 116251881Speter 117251881Speter 118251881Speterstatic void 119251881Speterdefine_namespaces(svn_ra_serf__ns_t **ns_list, 120251881Speter const char *const *attrs, 121251881Speter apr_pool_t *(*get_pool)(void *baton), 122251881Speter void *baton) 123251881Speter{ 124251881Speter const char *const *tmp_attrs = attrs; 125251881Speter 126251881Speter for (tmp_attrs = attrs; *tmp_attrs != NULL; tmp_attrs += 2) 127251881Speter { 128251881Speter if (strncmp(*tmp_attrs, "xmlns", 5) == 0) 129251881Speter { 130251881Speter const svn_ra_serf__ns_t *cur_ns; 131251881Speter svn_boolean_t found = FALSE; 132251881Speter const char *prefix; 133251881Speter 134251881Speter /* The empty prefix, or a named-prefix. */ 135251881Speter if (tmp_attrs[0][5] == ':') 136251881Speter prefix = &tmp_attrs[0][6]; 137251881Speter else 138251881Speter prefix = ""; 139251881Speter 140251881Speter /* Have we already defined this ns previously? */ 141251881Speter for (cur_ns = *ns_list; cur_ns; cur_ns = cur_ns->next) 142251881Speter { 143251881Speter if (strcmp(cur_ns->namespace, prefix) == 0) 144251881Speter { 145251881Speter found = TRUE; 146251881Speter break; 147251881Speter } 148251881Speter } 149251881Speter 150251881Speter if (!found) 151251881Speter { 152251881Speter apr_pool_t *pool; 153251881Speter svn_ra_serf__ns_t *new_ns; 154251881Speter 155251881Speter if (get_pool) 156251881Speter pool = get_pool(baton); 157251881Speter else 158251881Speter pool = baton; 159251881Speter new_ns = apr_palloc(pool, sizeof(*new_ns)); 160251881Speter new_ns->namespace = apr_pstrdup(pool, prefix); 161251881Speter new_ns->url = apr_pstrdup(pool, tmp_attrs[1]); 162251881Speter 163251881Speter /* Push into the front of NS_LIST. Parent states will point 164251881Speter to later in the chain, so will be unaffected by 165251881Speter shadowing/other namespaces pushed onto NS_LIST. */ 166251881Speter new_ns->next = *ns_list; 167251881Speter *ns_list = new_ns; 168251881Speter } 169251881Speter } 170251881Speter } 171251881Speter} 172251881Speter 173251881Speter 174251881Spetervoid 175251881Spetersvn_ra_serf__define_ns(svn_ra_serf__ns_t **ns_list, 176251881Speter const char *const *attrs, 177251881Speter apr_pool_t *result_pool) 178251881Speter{ 179251881Speter define_namespaces(ns_list, attrs, NULL /* get_pool */, result_pool); 180251881Speter} 181251881Speter 182251881Speter 183251881Speter/* 184251881Speter * Look up NAME in the NS_LIST list for previously declared namespace 185251881Speter * definitions and return a DAV_PROPS_T-tuple that has values. 186251881Speter */ 187251881Spetervoid 188251881Spetersvn_ra_serf__expand_ns(svn_ra_serf__dav_props_t *returned_prop_name, 189251881Speter const svn_ra_serf__ns_t *ns_list, 190251881Speter const char *name) 191251881Speter{ 192251881Speter const char *colon; 193251881Speter 194251881Speter colon = strchr(name, ':'); 195251881Speter if (colon) 196251881Speter { 197251881Speter const svn_ra_serf__ns_t *ns; 198251881Speter 199251881Speter for (ns = ns_list; ns; ns = ns->next) 200251881Speter { 201251881Speter if (strncmp(ns->namespace, name, colon - name) == 0) 202251881Speter { 203251881Speter returned_prop_name->namespace = ns->url; 204251881Speter returned_prop_name->name = colon + 1; 205251881Speter return; 206251881Speter } 207251881Speter } 208251881Speter } 209251881Speter else 210251881Speter { 211251881Speter const svn_ra_serf__ns_t *ns; 212251881Speter 213251881Speter for (ns = ns_list; ns; ns = ns->next) 214251881Speter { 215251881Speter if (! ns->namespace[0]) 216251881Speter { 217251881Speter returned_prop_name->namespace = ns->url; 218251881Speter returned_prop_name->name = name; 219251881Speter return; 220251881Speter } 221251881Speter } 222251881Speter } 223251881Speter 224251881Speter /* If the prefix is not found, then the name is NOT within a 225251881Speter namespace. */ 226251881Speter returned_prop_name->namespace = ""; 227251881Speter returned_prop_name->name = name; 228251881Speter} 229251881Speter 230251881Speter 231251881Speter#define XML_HEADER "<?xml version=\"1.0\" encoding=\"utf-8\"?>" 232251881Speter 233251881Spetervoid 234251881Spetersvn_ra_serf__add_xml_header_buckets(serf_bucket_t *agg_bucket, 235251881Speter serf_bucket_alloc_t *bkt_alloc) 236251881Speter{ 237251881Speter serf_bucket_t *tmp; 238251881Speter 239251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(XML_HEADER, sizeof(XML_HEADER) - 1, 240251881Speter bkt_alloc); 241251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 242251881Speter} 243251881Speter 244251881Spetervoid 245251881Spetersvn_ra_serf__add_open_tag_buckets(serf_bucket_t *agg_bucket, 246251881Speter serf_bucket_alloc_t *bkt_alloc, 247251881Speter const char *tag, ...) 248251881Speter{ 249251881Speter va_list ap; 250251881Speter const char *key; 251251881Speter serf_bucket_t *tmp; 252251881Speter 253251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("<", 1, bkt_alloc); 254251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 255251881Speter 256251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc); 257251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 258251881Speter 259251881Speter va_start(ap, tag); 260251881Speter while ((key = va_arg(ap, char *)) != NULL) 261251881Speter { 262251881Speter const char *val = va_arg(ap, const char *); 263251881Speter if (val) 264251881Speter { 265251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(" ", 1, bkt_alloc); 266251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 267251881Speter 268251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(key, bkt_alloc); 269251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 270251881Speter 271251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("=\"", 2, bkt_alloc); 272251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 273251881Speter 274251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(val, bkt_alloc); 275251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 276251881Speter 277251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("\"", 1, bkt_alloc); 278251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 279251881Speter } 280251881Speter } 281251881Speter va_end(ap); 282251881Speter 283251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc); 284251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 285251881Speter} 286251881Speter 287251881Spetervoid 288251881Spetersvn_ra_serf__add_close_tag_buckets(serf_bucket_t *agg_bucket, 289251881Speter serf_bucket_alloc_t *bkt_alloc, 290251881Speter const char *tag) 291251881Speter{ 292251881Speter serf_bucket_t *tmp; 293251881Speter 294251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN("</", 2, bkt_alloc); 295251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 296251881Speter 297251881Speter tmp = SERF_BUCKET_SIMPLE_STRING(tag, bkt_alloc); 298251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 299251881Speter 300251881Speter tmp = SERF_BUCKET_SIMPLE_STRING_LEN(">", 1, bkt_alloc); 301251881Speter serf_bucket_aggregate_append(agg_bucket, tmp); 302251881Speter} 303251881Speter 304251881Spetervoid 305251881Spetersvn_ra_serf__add_cdata_len_buckets(serf_bucket_t *agg_bucket, 306251881Speter serf_bucket_alloc_t *bkt_alloc, 307251881Speter const char *data, apr_size_t len) 308251881Speter{ 309251881Speter const char *end = data + len; 310251881Speter const char *p = data, *q; 311251881Speter serf_bucket_t *tmp_bkt; 312251881Speter 313251881Speter while (1) 314251881Speter { 315251881Speter /* Find a character which needs to be quoted and append bytes up 316251881Speter to that point. Strictly speaking, '>' only needs to be 317251881Speter quoted if it follows "]]", but it's easier to quote it all 318251881Speter the time. 319251881Speter 320251881Speter So, why are we escaping '\r' here? Well, according to the 321251881Speter XML spec, '\r\n' gets converted to '\n' during XML parsing. 322251881Speter Also, any '\r' not followed by '\n' is converted to '\n'. By 323251881Speter golly, if we say we want to escape a '\r', we want to make 324251881Speter sure it remains a '\r'! */ 325251881Speter q = p; 326251881Speter while (q < end && *q != '&' && *q != '<' && *q != '>' && *q != '\r') 327251881Speter q++; 328251881Speter 329251881Speter 330251881Speter tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(p, q - p, bkt_alloc); 331251881Speter serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 332251881Speter 333251881Speter /* We may already be a winner. */ 334251881Speter if (q == end) 335251881Speter break; 336251881Speter 337251881Speter /* Append the entity reference for the character. */ 338251881Speter if (*q == '&') 339251881Speter { 340251881Speter tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("&", sizeof("&") - 1, 341251881Speter bkt_alloc); 342251881Speter serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 343251881Speter } 344251881Speter else if (*q == '<') 345251881Speter { 346251881Speter tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN("<", sizeof("<") - 1, 347251881Speter bkt_alloc); 348251881Speter serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 349251881Speter } 350251881Speter else if (*q == '>') 351251881Speter { 352251881Speter tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(">", sizeof(">") - 1, 353251881Speter bkt_alloc); 354251881Speter serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 355251881Speter } 356251881Speter else if (*q == '\r') 357251881Speter { 358251881Speter tmp_bkt = SERF_BUCKET_SIMPLE_STRING_LEN(" ", sizeof(" ") - 1, 359251881Speter bkt_alloc); 360251881Speter serf_bucket_aggregate_append(agg_bucket, tmp_bkt); 361251881Speter } 362251881Speter 363251881Speter p = q + 1; 364251881Speter } 365251881Speter} 366251881Speter 367251881Spetervoid svn_ra_serf__add_tag_buckets(serf_bucket_t *agg_bucket, const char *tag, 368251881Speter const char *value, 369251881Speter serf_bucket_alloc_t *bkt_alloc) 370251881Speter{ 371251881Speter svn_ra_serf__add_open_tag_buckets(agg_bucket, bkt_alloc, tag, NULL); 372251881Speter 373251881Speter if (value) 374251881Speter { 375251881Speter svn_ra_serf__add_cdata_len_buckets(agg_bucket, bkt_alloc, 376251881Speter value, strlen(value)); 377251881Speter } 378251881Speter 379251881Speter svn_ra_serf__add_close_tag_buckets(agg_bucket, bkt_alloc, tag); 380251881Speter} 381251881Speter 382251881Spetervoid 383251881Spetersvn_ra_serf__xml_push_state(svn_ra_serf__xml_parser_t *parser, 384251881Speter int state) 385251881Speter{ 386251881Speter svn_ra_serf__xml_state_t *new_state; 387251881Speter 388251881Speter if (!parser->free_state) 389251881Speter { 390251881Speter new_state = apr_palloc(parser->pool, sizeof(*new_state)); 391251881Speter new_state->pool = svn_pool_create(parser->pool); 392251881Speter } 393251881Speter else 394251881Speter { 395251881Speter new_state = parser->free_state; 396251881Speter parser->free_state = parser->free_state->prev; 397251881Speter 398251881Speter svn_pool_clear(new_state->pool); 399251881Speter } 400251881Speter 401251881Speter if (parser->state) 402251881Speter { 403251881Speter new_state->private = parser->state->private; 404251881Speter new_state->ns_list = parser->state->ns_list; 405251881Speter } 406251881Speter else 407251881Speter { 408251881Speter new_state->private = NULL; 409251881Speter new_state->ns_list = NULL; 410251881Speter } 411251881Speter 412251881Speter new_state->current_state = state; 413251881Speter 414251881Speter /* Add it to the state chain. */ 415251881Speter new_state->prev = parser->state; 416251881Speter parser->state = new_state; 417251881Speter} 418251881Speter 419251881Spetervoid svn_ra_serf__xml_pop_state(svn_ra_serf__xml_parser_t *parser) 420251881Speter{ 421251881Speter svn_ra_serf__xml_state_t *cur_state; 422251881Speter 423251881Speter cur_state = parser->state; 424251881Speter parser->state = cur_state->prev; 425251881Speter cur_state->prev = parser->free_state; 426251881Speter parser->free_state = cur_state; 427251881Speter} 428251881Speter 429251881Speter 430251881Speter/* Return a pool for XES to use for self-alloc (and other specifics). */ 431251881Speterstatic apr_pool_t * 432251881Speterxes_pool(const svn_ra_serf__xml_estate_t *xes) 433251881Speter{ 434251881Speter /* Move up through parent states looking for one with a pool. This 435251881Speter will always terminate since the initial state has a pool. */ 436251881Speter while (xes->state_pool == NULL) 437251881Speter xes = xes->prev; 438251881Speter return xes->state_pool; 439251881Speter} 440251881Speter 441251881Speter 442251881Speterstatic void 443251881Speterensure_pool(svn_ra_serf__xml_estate_t *xes) 444251881Speter{ 445251881Speter if (xes->state_pool == NULL) 446251881Speter xes->state_pool = svn_pool_create(xes_pool(xes)); 447251881Speter} 448251881Speter 449251881Speter 450251881Speter/* This callback is used by define_namespaces() to wait until a pool is 451251881Speter required before constructing it. */ 452251881Speterstatic apr_pool_t * 453251881Speterlazy_create_pool(void *baton) 454251881Speter{ 455251881Speter svn_ra_serf__xml_estate_t *xes = baton; 456251881Speter 457251881Speter ensure_pool(xes); 458251881Speter return xes->state_pool; 459251881Speter} 460251881Speter 461251881Spetervoid 462251881Spetersvn_ra_serf__xml_context_destroy( 463251881Speter svn_ra_serf__xml_context_t *xmlctx) 464251881Speter{ 465251881Speter svn_pool_destroy(xmlctx->scratch_pool); 466251881Speter} 467251881Speter 468251881Spetersvn_ra_serf__xml_context_t * 469251881Spetersvn_ra_serf__xml_context_create( 470251881Speter const svn_ra_serf__xml_transition_t *ttable, 471251881Speter svn_ra_serf__xml_opened_t opened_cb, 472251881Speter svn_ra_serf__xml_closed_t closed_cb, 473251881Speter svn_ra_serf__xml_cdata_t cdata_cb, 474251881Speter void *baton, 475251881Speter apr_pool_t *result_pool) 476251881Speter{ 477251881Speter svn_ra_serf__xml_context_t *xmlctx; 478251881Speter svn_ra_serf__xml_estate_t *xes; 479251881Speter 480251881Speter xmlctx = apr_pcalloc(result_pool, sizeof(*xmlctx)); 481251881Speter xmlctx->ttable = ttable; 482251881Speter xmlctx->opened_cb = opened_cb; 483251881Speter xmlctx->closed_cb = closed_cb; 484251881Speter xmlctx->cdata_cb = cdata_cb; 485251881Speter xmlctx->baton = baton; 486251881Speter xmlctx->scratch_pool = svn_pool_create(result_pool); 487251881Speter 488251881Speter xes = apr_pcalloc(result_pool, sizeof(*xes)); 489251881Speter /* XES->STATE == 0 */ 490251881Speter 491251881Speter /* Child states may use this pool to allocate themselves. If a child 492251881Speter needs to collect information, then it will construct a subpool and 493251881Speter will use that to allocate itself and its collected data. */ 494251881Speter xes->state_pool = result_pool; 495251881Speter 496251881Speter xmlctx->current = xes; 497251881Speter 498251881Speter return xmlctx; 499251881Speter} 500251881Speter 501251881Speter 502251881Speterapr_hash_t * 503251881Spetersvn_ra_serf__xml_gather_since(svn_ra_serf__xml_estate_t *xes, 504251881Speter int stop_state) 505251881Speter{ 506251881Speter apr_hash_t *data; 507251881Speter apr_pool_t *pool; 508251881Speter 509251881Speter ensure_pool(xes); 510251881Speter pool = xes->state_pool; 511251881Speter 512251881Speter data = apr_hash_make(pool); 513251881Speter 514251881Speter for (; xes != NULL; xes = xes->prev) 515251881Speter { 516251881Speter if (xes->attrs != NULL) 517251881Speter { 518251881Speter apr_hash_index_t *hi; 519251881Speter 520251881Speter for (hi = apr_hash_first(pool, xes->attrs); hi; 521251881Speter hi = apr_hash_next(hi)) 522251881Speter { 523251881Speter const void *key; 524251881Speter apr_ssize_t klen; 525251881Speter void *val; 526251881Speter 527251881Speter /* Parent name/value lifetimes are at least as long as POOL. */ 528251881Speter apr_hash_this(hi, &key, &klen, &val); 529251881Speter apr_hash_set(data, key, klen, val); 530251881Speter } 531251881Speter } 532251881Speter 533251881Speter if (xes->state == stop_state) 534251881Speter break; 535251881Speter } 536251881Speter 537251881Speter return data; 538251881Speter} 539251881Speter 540251881Speter 541251881Spetervoid 542251881Spetersvn_ra_serf__xml_note(svn_ra_serf__xml_estate_t *xes, 543251881Speter int state, 544251881Speter const char *name, 545251881Speter const char *value) 546251881Speter{ 547251881Speter svn_ra_serf__xml_estate_t *scan; 548251881Speter 549251881Speter for (scan = xes; scan != NULL && scan->state != state; scan = scan->prev) 550251881Speter /* pass */ ; 551251881Speter 552251881Speter SVN_ERR_ASSERT_NO_RETURN(scan != NULL); 553251881Speter 554251881Speter /* Make sure the target state has a pool. */ 555251881Speter ensure_pool(scan); 556251881Speter 557251881Speter /* ... and attribute storage. */ 558251881Speter if (scan->attrs == NULL) 559251881Speter scan->attrs = apr_hash_make(scan->state_pool); 560251881Speter 561251881Speter /* In all likelihood, NAME is a string constant. But we can't really 562251881Speter be sure. And it isn't like we're storing a billion of these into 563251881Speter the state pool. */ 564251881Speter svn_hash_sets(scan->attrs, 565251881Speter apr_pstrdup(scan->state_pool, name), 566251881Speter apr_pstrdup(scan->state_pool, value)); 567251881Speter} 568251881Speter 569251881Speter 570251881Speterapr_pool_t * 571251881Spetersvn_ra_serf__xml_state_pool(svn_ra_serf__xml_estate_t *xes) 572251881Speter{ 573251881Speter /* If they asked for a pool, then ensure that we have one to provide. */ 574251881Speter ensure_pool(xes); 575251881Speter 576251881Speter return xes->state_pool; 577251881Speter} 578251881Speter 579251881Speter 580251881Spetersvn_error_t * 581251881Spetersvn_ra_serf__xml_cb_start(svn_ra_serf__xml_context_t *xmlctx, 582251881Speter const char *raw_name, 583251881Speter const char *const *attrs) 584251881Speter{ 585251881Speter svn_ra_serf__xml_estate_t *current = xmlctx->current; 586251881Speter svn_ra_serf__dav_props_t elemname; 587251881Speter const svn_ra_serf__xml_transition_t *scan; 588251881Speter apr_pool_t *new_pool; 589251881Speter svn_ra_serf__xml_estate_t *new_xes; 590251881Speter 591251881Speter /* If we're waiting for an element to close, then just ignore all 592251881Speter other element-opens. */ 593251881Speter if (xmlctx->waiting.namespace != NULL) 594251881Speter return SVN_NO_ERROR; 595251881Speter 596251881Speter /* Look for xmlns: attributes. Lazily create the state pool if any 597251881Speter were found. */ 598251881Speter define_namespaces(¤t->ns_list, attrs, lazy_create_pool, current); 599251881Speter 600251881Speter svn_ra_serf__expand_ns(&elemname, current->ns_list, raw_name); 601251881Speter 602251881Speter for (scan = xmlctx->ttable; scan->ns != NULL; ++scan) 603251881Speter { 604251881Speter if (scan->from_state != current->state) 605251881Speter continue; 606251881Speter 607251881Speter /* Wildcard tag match. */ 608251881Speter if (*scan->name == '*') 609251881Speter break; 610251881Speter 611251881Speter /* Found a specific transition. */ 612251881Speter if (strcmp(elemname.name, scan->name) == 0 613251881Speter && strcmp(elemname.namespace, scan->ns) == 0) 614251881Speter break; 615251881Speter } 616251881Speter if (scan->ns == NULL) 617251881Speter { 618253734Speter if (current->state == 0) 619253734Speter { 620253734Speter return svn_error_createf( 621253734Speter SVN_ERR_RA_DAV_MALFORMED_DATA, NULL, 622253734Speter _("XML Parsing failed: Unexpected root element '%s'"), 623253734Speter elemname.name); 624253734Speter } 625253734Speter 626251881Speter xmlctx->waiting = elemname; 627251881Speter /* ### return? */ 628251881Speter return SVN_NO_ERROR; 629251881Speter } 630251881Speter 631251881Speter /* We should not be told to collect cdata if the closed_cb will not 632251881Speter be called. */ 633251881Speter SVN_ERR_ASSERT(!scan->collect_cdata || scan->custom_close); 634251881Speter 635251881Speter /* Found a transition. Make it happen. */ 636251881Speter 637251881Speter /* ### todo. push state */ 638251881Speter 639251881Speter /* ### how to use free states? */ 640251881Speter /* This state should be allocated in the extent pool. If we will be 641251881Speter collecting information for this state, then construct a subpool. 642251881Speter 643251881Speter ### potentially optimize away the subpool if none of the 644251881Speter ### attributes are present. subpools are cheap, tho... */ 645251881Speter new_pool = xes_pool(current); 646251881Speter if (scan->collect_cdata || scan->collect_attrs[0]) 647251881Speter { 648251881Speter new_pool = svn_pool_create(new_pool); 649251881Speter 650251881Speter /* Prep the new state. */ 651251881Speter new_xes = apr_pcalloc(new_pool, sizeof(*new_xes)); 652251881Speter new_xes->state_pool = new_pool; 653251881Speter 654251881Speter /* If we're supposed to collect cdata, then set up a buffer for 655251881Speter this. The existence of this buffer will instruct our cdata 656251881Speter callback to collect the cdata. */ 657251881Speter if (scan->collect_cdata) 658251881Speter new_xes->cdata = svn_stringbuf_create_empty(new_pool); 659251881Speter 660251881Speter if (scan->collect_attrs[0] != NULL) 661251881Speter { 662251881Speter const char *const *saveattr = &scan->collect_attrs[0]; 663251881Speter 664251881Speter new_xes->attrs = apr_hash_make(new_pool); 665251881Speter for (; *saveattr != NULL; ++saveattr) 666251881Speter { 667251881Speter const char *name; 668251881Speter const char *value; 669251881Speter 670251881Speter if (**saveattr == '?') 671251881Speter { 672251881Speter name = *saveattr + 1; 673251881Speter value = svn_xml_get_attr_value(name, attrs); 674251881Speter } 675251881Speter else 676251881Speter { 677251881Speter name = *saveattr; 678251881Speter value = svn_xml_get_attr_value(name, attrs); 679251881Speter if (value == NULL) 680251881Speter return svn_error_createf(SVN_ERR_XML_ATTRIB_NOT_FOUND, 681251881Speter NULL, 682251881Speter _("Missing XML attribute: '%s'"), 683251881Speter name); 684251881Speter } 685251881Speter 686251881Speter if (value) 687251881Speter svn_hash_sets(new_xes->attrs, name, 688251881Speter apr_pstrdup(new_pool, value)); 689251881Speter } 690251881Speter } 691251881Speter } 692251881Speter else 693251881Speter { 694251881Speter /* Prep the new state. */ 695251881Speter new_xes = apr_pcalloc(new_pool, sizeof(*new_xes)); 696251881Speter /* STATE_POOL remains NULL. */ 697251881Speter } 698251881Speter 699251881Speter /* Some basic copies to set up the new estate. */ 700251881Speter new_xes->state = scan->to_state; 701251881Speter new_xes->tag.name = apr_pstrdup(new_pool, elemname.name); 702251881Speter new_xes->tag.namespace = apr_pstrdup(new_pool, elemname.namespace); 703251881Speter new_xes->custom_close = scan->custom_close; 704251881Speter 705251881Speter /* Start with the parent's namespace set. */ 706251881Speter new_xes->ns_list = current->ns_list; 707251881Speter 708251881Speter /* The new state is prepared. Make it current. */ 709251881Speter new_xes->prev = current; 710251881Speter xmlctx->current = new_xes; 711251881Speter 712251881Speter if (xmlctx->opened_cb) 713251881Speter { 714251881Speter START_CALLBACK(xmlctx); 715251881Speter SVN_ERR(xmlctx->opened_cb(new_xes, xmlctx->baton, 716251881Speter new_xes->state, &new_xes->tag, 717251881Speter xmlctx->scratch_pool)); 718251881Speter END_CALLBACK(xmlctx); 719251881Speter svn_pool_clear(xmlctx->scratch_pool); 720251881Speter } 721251881Speter 722251881Speter return SVN_NO_ERROR; 723251881Speter} 724251881Speter 725251881Speter 726251881Spetersvn_error_t * 727251881Spetersvn_ra_serf__xml_cb_end(svn_ra_serf__xml_context_t *xmlctx, 728251881Speter const char *raw_name) 729251881Speter{ 730251881Speter svn_ra_serf__xml_estate_t *xes = xmlctx->current; 731251881Speter svn_ra_serf__dav_props_t elemname; 732251881Speter 733251881Speter svn_ra_serf__expand_ns(&elemname, xes->ns_list, raw_name); 734251881Speter 735251881Speter if (xmlctx->waiting.namespace != NULL) 736251881Speter { 737251881Speter /* If this element is not the closer, then keep waiting... */ 738251881Speter if (strcmp(elemname.name, xmlctx->waiting.name) != 0 739251881Speter || strcmp(elemname.namespace, xmlctx->waiting.namespace) != 0) 740251881Speter return SVN_NO_ERROR; 741251881Speter 742251881Speter /* Found it. Stop waiting, and go back for more. */ 743251881Speter xmlctx->waiting.namespace = NULL; 744251881Speter return SVN_NO_ERROR; 745251881Speter } 746251881Speter 747251881Speter /* We should be looking at the same tag that opened the current state. 748251881Speter 749251881Speter Unknown elements are simply skipped, so we wouldn't reach this check. 750251881Speter 751251881Speter Known elements push a new state for a given tag. Some other elemname 752251881Speter would imply closing an ancestor tag (where did ours go?) or a spurious 753251881Speter tag closure. */ 754251881Speter if (strcmp(elemname.name, xes->tag.name) != 0 755251881Speter || strcmp(elemname.namespace, xes->tag.namespace) != 0) 756251881Speter return svn_error_create(SVN_ERR_XML_MALFORMED, NULL, 757251881Speter _("The response contains invalid XML")); 758251881Speter 759251881Speter if (xes->custom_close) 760251881Speter { 761251881Speter const svn_string_t *cdata; 762251881Speter 763251881Speter if (xes->cdata) 764251881Speter { 765251881Speter cdata = svn_stringbuf__morph_into_string(xes->cdata); 766251881Speter#ifdef SVN_DEBUG 767251881Speter /* We might toss the pool holding this structure, but it could also 768251881Speter be within a parent pool. In any case, for safety's sake, disable 769251881Speter the stringbuf against future Badness. */ 770251881Speter xes->cdata->pool = NULL; 771251881Speter#endif 772251881Speter } 773251881Speter else 774251881Speter cdata = NULL; 775251881Speter 776251881Speter START_CALLBACK(xmlctx); 777251881Speter SVN_ERR(xmlctx->closed_cb(xes, xmlctx->baton, xes->state, 778251881Speter cdata, xes->attrs, 779251881Speter xmlctx->scratch_pool)); 780251881Speter END_CALLBACK(xmlctx); 781251881Speter svn_pool_clear(xmlctx->scratch_pool); 782251881Speter } 783251881Speter 784251881Speter /* Pop the state. */ 785251881Speter xmlctx->current = xes->prev; 786251881Speter 787251881Speter /* ### not everything should go on the free state list. XES may go 788251881Speter ### away with the state pool. */ 789251881Speter xes->prev = xmlctx->free_states; 790251881Speter xmlctx->free_states = xes; 791251881Speter 792251881Speter /* If there is a STATE_POOL, then toss it. This will get rid of as much 793251881Speter memory as possible. Potentially the XES (if we didn't create a pool 794251881Speter right away, then XES may be in a parent pool). */ 795251881Speter if (xes->state_pool) 796251881Speter svn_pool_destroy(xes->state_pool); 797251881Speter 798251881Speter return SVN_NO_ERROR; 799251881Speter} 800251881Speter 801251881Speter 802251881Spetersvn_error_t * 803251881Spetersvn_ra_serf__xml_cb_cdata(svn_ra_serf__xml_context_t *xmlctx, 804251881Speter const char *data, 805251881Speter apr_size_t len) 806251881Speter{ 807251881Speter /* If we are waiting for a closing tag, then we are uninterested in 808251881Speter the cdata. Just return. */ 809251881Speter if (xmlctx->waiting.namespace != NULL) 810251881Speter return SVN_NO_ERROR; 811251881Speter 812251881Speter /* If the current state is collecting cdata, then copy the cdata. */ 813251881Speter if (xmlctx->current->cdata != NULL) 814251881Speter { 815251881Speter svn_stringbuf_appendbytes(xmlctx->current->cdata, data, len); 816251881Speter } 817251881Speter /* ... else if a CDATA_CB has been supplied, then invoke it for 818251881Speter all states. */ 819251881Speter else if (xmlctx->cdata_cb != NULL) 820251881Speter { 821251881Speter START_CALLBACK(xmlctx); 822251881Speter SVN_ERR(xmlctx->cdata_cb(xmlctx->current, 823251881Speter xmlctx->baton, 824251881Speter xmlctx->current->state, 825251881Speter data, len, 826251881Speter xmlctx->scratch_pool)); 827251881Speter END_CALLBACK(xmlctx); 828251881Speter svn_pool_clear(xmlctx->scratch_pool); 829251881Speter } 830251881Speter 831251881Speter return SVN_NO_ERROR; 832251881Speter} 833251881Speter 834