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("&amp;", sizeof("&amp;") - 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("&lt;", sizeof("&lt;") - 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("&gt;", sizeof("&gt;") - 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("&#13;", sizeof("&#13;") - 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(&current->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