Return-Path: Delivered-To: apmail-apache-cvs-archive@apache.org Received: (qmail 89683 invoked by uid 500); 24 Feb 2001 14:17:26 -0000 Mailing-List: contact apache-cvs-help@apache.org; run by ezmlm Precedence: bulk Reply-To: new-httpd@apache.org list-help: list-unsubscribe: list-post: Delivered-To: mailing list apache-cvs@apache.org Received: (qmail 89672 invoked by uid 500); 24 Feb 2001 14:17:26 -0000 Delivered-To: apmail-httpd-2.0-cvs@apache.org Date: 24 Feb 2001 14:17:26 -0000 Message-ID: <20010224141726.89668.qmail@apache.org> From: gstein@apache.org To: httpd-2.0-cvs@apache.org Subject: cvs commit: httpd-2.0/server util_xml.c gstein 01/02/24 06:17:25 Modified: . configure.in build apu-conf.m4 . configure.in include apr_xml.h apu_compat.h xml Makefile.in include util_xml.h server util_xml.c Log: Shift XML handling code from Apache down into APRUTIL - teach APRUTIL how to find Expat and respond to --with-expat - Apache's configure points APRUTIL's configure at its srclib/expat-lite (this will go away; aprutil can work against installed expats or fallback to an expat bundled within aprutil) - shift some of the timing of INCLUDES and top_builddir processing in the APRUTIL configure.in script - expose the new apr_xml functions in apr_xml.h, apr_xml.c (by building it), and apu_compat.h - rewrite util_xml.[ch] in terms of apr_xml Revision Changes Path 1.126 +1 -1 httpd-2.0/configure.in Index: configure.in =================================================================== RCS file: /home/cvs/httpd-2.0/configure.in,v retrieving revision 1.125 retrieving revision 1.126 diff -u -u -r1.125 -r1.126 --- configure.in 2001/02/23 11:39:37 1.125 +++ configure.in 2001/02/24 14:17:24 1.126 @@ -252,7 +252,7 @@ dnl ## This allows APR to detect libraries like dl and tell Apache that it dnl ## needs to include or not include them. APR_SUBDIR_CONFIG(srclib/apr, "$apache_apr_flags --disable-shared") -APR_SUBDIR_CONFIG(srclib/apr-util, "--with-apr=../apr --disable-shared") +APR_SUBDIR_CONFIG(srclib/apr-util, "--with-apr=../apr --with-expat=../expat-lite --disable-shared") APR_SUBDIR_CONFIG(srclib/pcre) . ./srclib/apr/APRVARS 1.4 +98 -1 apr-util/build/apu-conf.m4 Index: apu-conf.m4 =================================================================== RCS file: /home/cvs/apr-util/build/apu-conf.m4,v retrieving revision 1.3 retrieving revision 1.4 diff -u -u -r1.3 -r1.4 --- apu-conf.m4 2001/01/25 04:37:39 1.3 +++ apu-conf.m4 2001/02/24 14:17:24 1.4 @@ -236,7 +236,104 @@ if test $apu_use_db = 1; then dnl ### use AC_CHECK_LIB? LIBS="$LIBS -l$db_lib" - APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $LIBS" + APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS -l$db_lib" fi +]) + +dnl +dnl APU_TEST_EXPAT(directory): test if Expat is located in the specified dir +dnl +dnl if present: sets expat_include_dir, expat_libs, possibly expat_old +dnl +AC_DEFUN(APU_TEST_EXPAT,[ + AC_MSG_CHECKING(for Expat in ifelse($2,,$1,$2)) + + if test -r "$1/lib/expat.h"; then + dnl Expat 1.95.* distribution + expat_include_dir="$1/lib" + expat_libs="$1/lib/libexpat.la" + elif test -r "$1/xmlparse.h"; then + dnl maybe an expat-lite. use this dir for both includes and libs + expat_include_dir="$1" + expat_libs="$1/libexpat.la" + expat_old=yes + elif test -r "$1/include/xmlparse.h"; then + dnl ### who is this? + expat_include_dir="$1/include" + expat_libs="-L$1/lib -lexpat" + expat_old=yes + elif test -r "$1/include/xml/xmlparse.h"; then + dnl ### who is this? + expat_include_dir="$1/include/xml" + expat_libs="-L$1/lib -lexpat" + expat_old=yes + elif test -r "$1/include/xmltok/xmlparse.h"; then + dnl Debian distribution + expat_include_dir="$1/include/xmltok" + expat_libs="-L$1/lib -lxmlparse -lxmltok" + expat_old=yes + elif test -r "$1/xmlparse/xmlparse.h"; then + dnl Expat 1.0 or 1.1 source directory + expat_include_dir="$1/xmlparse" + expat_libs="-L$1 -lexpat" + expat_old=yes + fi + dnl ### test for installed Expat 1.95.* distros + + if test -n "$expat_include_dir"; then + dnl ### more info about what we found there? version? using .la? + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi +]) + + +dnl +dnl APU_FIND_EXPAT: figure out where EXPAT is located (or use bundled) +dnl +AC_DEFUN(APU_FIND_EXPAT,[ + +AC_ARG_WITH([expat], +[ --with-expat=DIR specify Expat location], [ + if test "$withval" = "yes"; then + AC_MSG_ERROR([a directory must be specified for --with-expat]) + elif test "$withval" = "no"; then + AC_MSG_ERROR([Expat cannot be disabled (at this time)]) + else + abs_expatdir="`cd $withval && pwd`" + APU_TEST_EXPAT($abs_expatdir, $withval) + if test -z "$expat_include_dir"; then + AC_MSG_ERROR([Expat was not found (or recognized) in \"$withval\"]) + fi + fi +]) + +if test -z "$expat_include_dir"; then + for d in /usr /usr/local xml/expat ; do + APU_TEST_EXPAT($d) + if test -n "$expat_include_dir"; then + break + fi + done +fi +if test -z "$expat_include_dir"; then + AC_MSG_ERROR([could not locate Expat. use --with-expat]) +fi + +if test -n "$expat_old"; then + AC_DEFINE(APR_HAVE_OLD_EXPAT, 1, [define if Expat 1.0 or 1.1 was found]) +fi + +dnl special-case the bundled distribution (use absolute dirs) +if test "$expat_include_dir" = "xml/expat/lib"; then + expat_include_dir=$srcdir/xml/expat/lib + expat_libs=$top_builddir/xml/expat/lib/libexpat.la +fi + +INCLUDES="$INCLUDES -I$expat_include_dir" +LIBS="$LIBS $expat_libs" +APRUTIL_EXPORT_LIBS="$APRUTIL_EXPORT_LIBS $expat_libs" +dnl ### export the Expat includes? ]) 1.12 +15 -11 apr-util/configure.in Index: configure.in =================================================================== RCS file: /home/cvs/apr-util/configure.in,v retrieving revision 1.11 retrieving revision 1.12 diff -u -u -r1.11 -r1.12 --- configure.in 2001/01/25 04:37:39 1.11 +++ configure.in 2001/02/24 14:17:24 1.12 @@ -13,29 +13,33 @@ AC_CHECK_PROG(RM, rm, rm) dnl +dnl compute the top directory of the build +dnl note: this is needed for LIBTOOL and exporting the bundled Expat +dnl +top_builddir="`cd $srcdir ; pwd`" +AC_SUBST(top_builddir) + +dnl +dnl set up the compilation flags and stuff +dnl +INCLUDES="$INCLUDES -I\$(top_builddir)/include/private -I\$(top_builddir)/include" + +dnl dnl 1. Find the APR includes directory and (possibly) the source (base) dir. dnl 2. Determine what DBM backend type to use. +dnl 3. Find Expat dnl APU_FIND_APR APU_CHECK_DBM +APU_FIND_EXPAT -dnl -dnl compute the top directory of the build -dnl note: this substitution is needed for LIBTOOL -dnl -top_builddir="`cd $srcdir ; pwd`" -AC_SUBST(top_builddir) +INCLUDES="$INCLUDES -I$APR_INCLUDES" dnl dnl prep libtool dnl echo "performing libtool configuration..." AC_PROG_LIBTOOL - -dnl -dnl set up the compilation flags and stuff -dnl -INCLUDES="-I\$(top_builddir)/include/private -I\$(top_builddir)/include -I$APR_INCLUDES" dnl dnl grab flags from APR. 1.12 +0 -8 apr-util/include/apr_xml.h Index: apr_xml.h =================================================================== RCS file: /home/cvs/apr-util/include/apr_xml.h,v retrieving revision 1.11 retrieving revision 1.12 diff -u -u -r1.11 -r1.12 --- apr_xml.h 2001/02/19 01:15:30 1.11 +++ apr_xml.h 2001/02/24 14:17:25 1.12 @@ -52,8 +52,6 @@ * . */ -#if 0 - #ifndef APR_XML_H #define APR_XML_H @@ -66,8 +64,6 @@ extern "C" { #endif -#ifdef APR_NOT_AVAILABLE_YET - /** * @package Apache XML library */ @@ -354,12 +350,8 @@ const char *uri); #define APR_XML_GET_URI_ITEM(ary, i) (((const char * const *)(ary)->elts)[i]) -#endif /* APR_NOT_AVAILABLE_YET */ - #ifdef __cplusplus } #endif #endif /* APR_XML_H */ - -#endif /* 0 */ 1.5 +0 -2 apr-util/include/apu_compat.h Index: apu_compat.h =================================================================== RCS file: /home/cvs/apr-util/include/apu_compat.h,v retrieving revision 1.4 retrieving revision 1.5 diff -u -u -r1.4 -r1.5 --- apu_compat.h 2001/02/19 01:20:11 1.4 +++ apu_compat.h 2001/02/24 14:17:25 1.5 @@ -76,7 +76,6 @@ /* -------------------------------------------------------------------- * the following symbols were moved from httpd-2.0/.../util_xml.[ch] */ -#if 0 #define ap_text apr_text #define ap_text_header apr_text_header #define ap_text_append apr_text_append @@ -102,6 +101,5 @@ #define ap_xml_quote_elem apr_xml_quote_elem #define ap_xml_insert_uri apr_xml_insert_uri #define AP_XML_GET_URI_ITEM(a,i) APR_XML_GET_URI_ITEM(a,i) -#endif /* 0 */ #endif /* APU_COMPAT_H */ 1.4 +3 -0 apr-util/xml/Makefile.in Index: Makefile.in =================================================================== RCS file: /home/cvs/apr-util/xml/Makefile.in,v retrieving revision 1.3 retrieving revision 1.4 diff -u -u -r1.3 -r1.4 --- Makefile.in 2001/01/07 01:35:51 1.3 +++ Makefile.in 2001/02/24 14:17:25 1.4 @@ -1,2 +1,5 @@ + +TARGETS = apr_xml.lo + # bring in rules.mk for standard functionality @INCLUDE_RULES@ 1.8 +7 -242 httpd-2.0/include/util_xml.h Index: util_xml.h =================================================================== RCS file: /home/cvs/httpd-2.0/include/util_xml.h,v retrieving revision 1.7 retrieving revision 1.8 diff -u -u -r1.7 -r1.8 --- util_xml.h 2001/02/16 04:26:31 1.7 +++ util_xml.h 2001/02/24 14:17:25 1.8 @@ -55,263 +55,28 @@ #ifndef UTIL_XML_H #define UTIL_XML_H +#include "apr_xml.h" +#include "apu_compat.h" + +#include "httpd.h" + #ifdef __cplusplus extern "C" { #endif -#include "httpd.h" -#include "apr_lib.h" - /** * @package Apache XML library */ -/* -------------------------------------------------------------------- */ - -/* ### these will need to move at some point to a more logical spot */ - -/* simple strutures to keep a linked list of pieces of text */ -typedef struct ap_text ap_text; - -/** Structure to keep a linked list of pieces of text */ -struct ap_text { - /** The current piece of text */ - const char *text; - /** a pointer to the next piece of text - * @defvar ap_text *next */ - struct ap_text *next; -}; - -typedef struct ap_text_header ap_text_header; - -/** A list of pieces of text */ -struct ap_text_header { - /** The first piece of text in the list */ - ap_text *first; - /** The last piece of text in the list */ - ap_text *last; -}; - /** - * Append a piece of text to the end of a list - * @param p The pool to allocate out of - * @param hdr The text header to append to - * @param text The new text to append - * @deffunc void ap_text_append(apr_pool_t *p, ap_text_header *hdr, const char *text) - */ -AP_DECLARE(void) ap_text_append(apr_pool_t *p, ap_text_header *hdr, - const char *text); - - -/* -------------------------------------------------------------------- -** -** XML PARSING -*/ - -/* -** Qualified namespace values -** -** AP_XML_NS_DAV_ID -** We always insert the "DAV:" namespace URI at the head of the -** namespace array. This means that it will always be at ID==0, -** making it much easier to test for. -** -** AP_XML_NS_NONE -** This special ID is used for two situations: -** -** 1) The namespace prefix begins with "xml" (and we do not know -** what it means). Namespace prefixes with "xml" (any case) as -** their first three characters are reserved by the XML Namespaces -** specification for future use. mod_dav will pass these through -** unchanged. When this identifier is used, the prefix is LEFT in -** the element/attribute name. Downstream processing should not -** prepend another prefix. -** -** 2) The element/attribute does not have a namespace. -** -** a) No prefix was used, and a default namespace has not been -** defined. -** b) No prefix was used, and the default namespace was specified -** to mean "no namespace". This is done with a namespace -** declaration of: xmlns="" -** (this declaration is typically used to override a previous -** specification for the default namespace) -** -** In these cases, we need to record that the elem/attr has no -** namespace so that we will not attempt to prepend a prefix. -** All namespaces that are used will have a prefix assigned to -** them -- mod_dav will never set or use the default namespace -** when generating XML. This means that "no prefix" will always -** mean "no namespace". -** -** In both cases, the XML generation will avoid prepending a prefix. -** For the first case, this means the original prefix/name will be -** inserted into the output stream. For the latter case, it means -** the name will have no prefix, and since we never define a default -** namespace, this means it will have no namespace. -** -** Note: currently, mod_dav understands the "xmlns" prefix and the -** "xml:lang" attribute. These are handled specially (they aren't -** left within the XML tree), so the AP_XML_NS_NONE value won't ever -** really apply to these values. -*/ -#define AP_XML_NS_DAV_ID 0 /* namespace ID for "DAV:" */ -#define AP_XML_NS_NONE -10 /* no namespace for this elem/attr */ - -#define AP_XML_NS_ERROR_BASE -100 /* used only during processing */ -#define AP_XML_NS_IS_ERROR(e) ((e) <= AP_XML_NS_ERROR_BASE) - - -typedef struct ap_xml_attr ap_xml_attr; -typedef struct ap_xml_elem ap_xml_elem; -typedef struct ap_xml_doc ap_xml_doc; - -/** ap_xml_attr: holds a parsed XML attribute */ -struct ap_xml_attr { - /** attribute name */ - const char *name; - /** index into namespace array */ - int ns; - - /** attribute value */ - const char *value; - - /** next attribute - * @defvar ap_xml_attr *next */ - struct ap_xml_attr *next; -}; - -/** ap_xml_elem: holds a parsed XML element */ -struct ap_xml_elem { - /** element name */ - const char *name; - /** index into namespace array */ - int ns; - /** xml:lang for attrs/contents */ - const char *lang; - - /** cdata right after start tag */ - ap_text_header first_cdata; - /** cdata after MY end tag */ - ap_text_header following_cdata; - - /** parent element - * @defvar ap_xml_elem *parent */ - struct ap_xml_elem *parent; - /** next (sibling) element - * @defvar ap_xml_elem *next */ - struct ap_xml_elem *next; - /** first child element - * @defvar ap_xml_elem *first_child */ - struct ap_xml_elem *first_child; - /** first attribute - * @defvar ap_xml_attr *attr */ - struct ap_xml_attr *attr; - - /* used only during parsing */ - /** last child element - * @defvar ap_xml_elem *last_child */ - struct ap_xml_elem *last_child; - /** namespaces scoped by this elem - * @defvar ap_xml_ns_scope *ns_scope */ - struct ap_xml_ns_scope *ns_scope; - - /* used by modules during request processing */ - /** Place for modules to store private data */ - void *private; -}; - -#define AP_XML_ELEM_IS_EMPTY(e) ((e)->first_child == NULL && \ - (e)->first_cdata.first == NULL) - -/** ap_xml_doc: holds a parsed XML document */ -struct ap_xml_doc { - /** root element */ - ap_xml_elem *root; - /** array of namespaces used */ - apr_array_header_t *namespaces; -}; - -/** * Get XML post data and parse it * @param r The current request * @param pdoc The XML post data * @return HTTP status code - * @deffunc int ap_xml_parse_input(request_rec *r, ap_xml_doc **pdoc) + * @deffunc int ap_xml_parse_input(request_rec *r, apr_xml_doc **pdoc) */ -AP_DECLARE(int) ap_xml_parse_input(request_rec *r, ap_xml_doc **pdoc); +AP_DECLARE(int) ap_xml_parse_input(request_rec *r, apr_xml_doc **pdoc); - -/** - * Converts an XML element tree to flat text - * @param p The pool to allocate out of - * @param elem The XML element to convert - * @param style How to covert the XML. One of: - *
  - *     AP_XML_X2T_FULL                start tag, contents, end tag 
  - *     AP_XML_X2T_INNER               contents only 
  - *     AP_XML_X2T_LANG_INNER          xml:lang + inner contents 
  - *     AP_XML_X2T_FULL_NS_LANG        FULL + ns defns + xml:lang 
  - * 
- * @param namespaces The namespace of the current XML element - * @param ns_map Namespace mapping - * @param pbuf Buffer to put the converted text into - * @param psize Size of the converted text - * @deffunc void ap_xml_to_text(apr_pool_t *p, const ap_xml_elem *elem, int style, apr_array_header_t *namespaces, int *ns_map, const char **pbuf, size_t *psize); - */ -AP_DECLARE(void) ap_xml_to_text(apr_pool_t *p, const ap_xml_elem *elem, - int style, apr_array_header_t *namespaces, - int *ns_map, const char **pbuf, - apr_size_t *psize); - -/* style argument values: */ -#define AP_XML_X2T_FULL 0 /* start tag, contents, end tag */ -#define AP_XML_X2T_INNER 1 /* contents only */ -#define AP_XML_X2T_LANG_INNER 2 /* xml:lang + inner contents */ -#define AP_XML_X2T_FULL_NS_LANG 3 /* FULL + ns defns + xml:lang */ - -/** - * empty XML element - * @param p The pool to allocate out of - * @param elem The XML element to empty - * @return the string that was stored in the XML element - * @deffunc const char *ap_xml_empty_elem(apr_pool_t *p, const ap_xml_elem *elem) - */ -AP_DECLARE(const char *) ap_xml_empty_elem(apr_pool_t *p, - const ap_xml_elem *elem); - -/** - * quote an XML string - * Replace '<', '>', and '&' with '<', '>', and '&'. - * @param p The pool to allocate out of - * @param s The string to quote - * @param quotes If quotes is true, then replace '"' with '"'. - * @return The quoted string - * @deffunc const char *ap_xml_quote_string(apr_pool_t *p, const char *s, int quotes) - */ -AP_DECLARE(const char *) ap_xml_quote_string(apr_pool_t *p, const char *s, - int quotes); - -/** - * Quote an XML element - * @param p The pool to allocate out of - * @param elem The element to quote - * @deffunc void ap_xml_quote_elem(apr_pool_t *p, ap_xml_elem *elem) - */ -AP_DECLARE(void) ap_xml_quote_elem(apr_pool_t *p, ap_xml_elem *elem); - -/* manage an array of unique URIs: ap_xml_insert_uri() and AP_XML_URI_ITEM() */ - -/** - * return the URI's (existing) index, or insert it and return a new index - * @param uri_array array to insert into - * @param uri The uri to insert - * @return int The uri's index - * @deffunc int ap_xml_insert_uri(apr_array_header_t *uri_array, const char *uri) - */ -AP_DECLARE(int) ap_xml_insert_uri(apr_array_header_t *uri_array, - const char *uri); -#define AP_XML_GET_URI_ITEM(ary, i) (((const char * const *)(ary)->elts)[i]) #ifdef __cplusplus } 1.16 +22 -757 httpd-2.0/server/util_xml.c Index: util_xml.c =================================================================== RCS file: /home/cvs/httpd-2.0/server/util_xml.c,v retrieving revision 1.15 retrieving revision 1.16 diff -u -u -r1.15 -r1.16 --- util_xml.c 2001/02/16 04:26:48 1.15 +++ util_xml.c 2001/02/24 14:17:25 1.16 @@ -52,20 +52,7 @@ * . */ -/* -** DAV extension module for Apache 2.0.* -** - XML parser for the body of a request -*/ - -/* James Clark's Expat parser */ -#include "xmlparse.h" - -#include "apr.h" -#include "apr_strings.h" - -#define APR_WANT_STDIO /* for fprintf(), sprintf() */ -#define APR_WANT_STRFUNC -#include "apr_want.h" +#include "apr_xml.h" #include "httpd.h" #include "http_protocol.h" @@ -75,300 +62,15 @@ #include "util_xml.h" -#define DEBUG_CR "\r\n" +#define READ_BLOCKSIZE 2048 /* used for reading input blocks */ -#define AP_XML_READ_BLOCKSIZE 2048 /* used for reading input blocks */ -/* errors related to namespace processing */ -#define AP_XML_NS_ERROR_UNKNOWN_PREFIX (AP_XML_NS_ERROR_BASE) - -/* test for a namespace prefix that begins with [Xx][Mm][Ll] */ -#define AP_XML_NS_IS_RESERVED(name) \ - ( (name[0] == 'X' || name[0] == 'x') && \ - (name[1] == 'M' || name[1] == 'm') && \ - (name[2] == 'L' || name[2] == 'l') ) - - -/* content for parsing */ -typedef struct ap_xml_ctx { - ap_xml_doc *doc; /* the doc we're parsing */ - apr_pool_t *p; /* the pool we allocate from */ - ap_xml_elem *cur_elem; /* current element */ - - int error; /* an error has occurred */ - /* errors may be AP_XML_NS_ERROR_* or other private errors which will - be defined here (none yet) */ - -} ap_xml_ctx; - -/* struct for scoping namespace declarations */ -typedef struct ap_xml_ns_scope { - const char *prefix; /* prefix used for this ns */ - int ns; /* index into namespace table */ - int emptyURI; /* the namespace URI is the empty string */ - struct ap_xml_ns_scope *next; /* next scoped namespace */ -} ap_xml_ns_scope; - - -/* return namespace table index for a given prefix */ -static int find_prefix(ap_xml_ctx *ctx, const char *prefix) -{ - ap_xml_elem *elem = ctx->cur_elem; - - /* - ** Walk up the tree, looking for a namespace scope that defines this - ** prefix. - */ - for (; elem; elem = elem->parent) { - ap_xml_ns_scope *ns_scope = elem->ns_scope; - - for (ns_scope = elem->ns_scope; ns_scope; ns_scope = ns_scope->next) { - if (strcmp(prefix, ns_scope->prefix) == 0) { - if (ns_scope->emptyURI) { - /* - ** It is possible to set the default namespace to an - ** empty URI string; this resets the default namespace - ** to mean "no namespace." We just found the prefix - ** refers to an empty URI, so return "no namespace." - */ - return AP_XML_NS_NONE; - } - - return ns_scope->ns; - } - } - } - - /* - * If the prefix is empty (""), this means that a prefix was not - * specified in the element/attribute. The search that was performed - * just above did not locate a default namespace URI (which is stored - * into ns_scope with an empty prefix). This means the element/attribute - * has "no namespace". We have a reserved value for this. - */ - if (*prefix == '\0') { - return AP_XML_NS_NONE; - } - - /* not found */ - return AP_XML_NS_ERROR_UNKNOWN_PREFIX; -} - -static void start_handler(void *userdata, const char *name, const char **attrs) +AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc) { - ap_xml_ctx *ctx = userdata; - ap_xml_elem *elem; - ap_xml_attr *attr; - ap_xml_attr *prev; - char *colon; - const char *quoted; - char *elem_name; - - /* punt once we find an error */ - if (ctx->error) - return; - - elem = apr_pcalloc(ctx->p, sizeof(*elem)); - - /* prep the element */ - elem->name = elem_name = apr_pstrdup(ctx->p, name); - - /* fill in the attributes (note: ends up in reverse order) */ - while (*attrs) { - attr = apr_palloc(ctx->p, sizeof(*attr)); - attr->name = apr_pstrdup(ctx->p, *attrs++); - attr->value = apr_pstrdup(ctx->p, *attrs++); - attr->next = elem->attr; - elem->attr = attr; - } - - /* hook the element into the tree */ - if (ctx->cur_elem == NULL) { - /* no current element; this also becomes the root */ - ctx->cur_elem = ctx->doc->root = elem; - } - else { - /* this element appeared within the current elem */ - elem->parent = ctx->cur_elem; - - /* set up the child/sibling links */ - if (elem->parent->last_child == NULL) { - /* no first child either */ - elem->parent->first_child = elem->parent->last_child = elem; - } - else { - /* hook onto the end of the parent's children */ - elem->parent->last_child->next = elem; - elem->parent->last_child = elem; - } - - /* this element is now the current element */ - ctx->cur_elem = elem; - } - - /* scan the attributes for namespace declarations */ - for (prev = NULL, attr = elem->attr; - attr; - attr = attr->next) { - if (strncmp(attr->name, "xmlns", 5) == 0) { - const char *prefix = &attr->name[5]; - ap_xml_ns_scope *ns_scope; - - /* test for xmlns:foo= form and xmlns= form */ - if (*prefix == ':') - ++prefix; - else if (*prefix != '\0') { - /* advance "prev" since "attr" is still present */ - prev = attr; - continue; - } - - /* quote the URI before we ever start working with it */ - quoted = ap_xml_quote_string(ctx->p, attr->value, 1); - - /* build and insert the new scope */ - ns_scope = apr_pcalloc(ctx->p, sizeof(*ns_scope)); - ns_scope->prefix = prefix; - ns_scope->ns = ap_xml_insert_uri(ctx->doc->namespaces, quoted); - ns_scope->emptyURI = *quoted == '\0'; - ns_scope->next = elem->ns_scope; - elem->ns_scope = ns_scope; - - /* remove this attribute from the element */ - if (prev == NULL) - elem->attr = attr->next; - else - prev->next = attr->next; - - /* Note: prev will not be advanced since we just removed "attr" */ - } - else if (strcmp(attr->name, "xml:lang") == 0) { - /* save away the language (in quoted form) */ - elem->lang = ap_xml_quote_string(ctx->p, attr->value, 1); - - /* remove this attribute from the element */ - if (prev == NULL) - elem->attr = attr->next; - else - prev->next = attr->next; - - /* Note: prev will not be advanced since we just removed "attr" */ - } - else { - /* advance "prev" since "attr" is still present */ - prev = attr; - } - } - - /* - ** If an xml:lang attribute didn't exist (lang==NULL), then copy the - ** language from the parent element (if present). - ** - ** NOTE: elem_size() *depends* upon this pointer equality. - */ - if (elem->lang == NULL && elem->parent != NULL) - elem->lang = elem->parent->lang; - - /* adjust the element's namespace */ - colon = ap_strchr(elem_name, ':'); - if (colon == NULL) { - /* - * The element is using the default namespace, which will always - * be found. Either it will be "no namespace", or a default - * namespace URI has been specified at some point. - */ - elem->ns = find_prefix(ctx, ""); - } - else if (AP_XML_NS_IS_RESERVED(elem->name)) { - elem->ns = AP_XML_NS_NONE; - } - else { - *colon = '\0'; - elem->ns = find_prefix(ctx, elem->name); - elem->name = colon + 1; - - if (AP_XML_NS_IS_ERROR(elem->ns)) { - ctx->error = elem->ns; - return; - } - } - - /* adjust all remaining attributes' namespaces */ - for (attr = elem->attr; attr; attr = attr->next) { - /* - * ap_xml_attr defines this as "const" but we dup'd it, so we - * know that we can change it. a bit hacky, but the existing - * structure def is best. - */ - char *attr_name = (char *)attr->name; - - colon = ap_strchr(attr_name, ':'); - if (colon == NULL) { - /* - * Attributes do NOT use the default namespace. Therefore, - * we place them into the "no namespace" category. - */ - attr->ns = AP_XML_NS_NONE; - } - else if (AP_XML_NS_IS_RESERVED(attr->name)) { - attr->ns = AP_XML_NS_NONE; - } - else { - *colon = '\0'; - attr->ns = find_prefix(ctx, attr->name); - attr->name = colon + 1; - - if (AP_XML_NS_IS_ERROR(attr->ns)) { - ctx->error = attr->ns; - return; - } - } - } -} - -static void end_handler(void *userdata, const char *name) -{ - ap_xml_ctx *ctx = userdata; - - /* punt once we find an error */ - if (ctx->error) - return; - - /* pop up one level */ - ctx->cur_elem = ctx->cur_elem->parent; -} - -static void cdata_handler(void *userdata, const char *data, int len) -{ - ap_xml_ctx *ctx = userdata; - ap_xml_elem *elem; - ap_text_header *hdr; - const char *s; - - /* punt once we find an error */ - if (ctx->error) - return; - - elem = ctx->cur_elem; - s = apr_pstrndup(ctx->p, data, len); - - if (elem->last_child == NULL) { - /* no children yet. this cdata follows the start tag */ - hdr = &elem->first_cdata; - } - else { - /* child elements exist. this cdata follows the last child. */ - hdr = &elem->last_child->following_cdata; - } - - ap_text_append(ctx->p, hdr, s); -} - -AP_DECLARE(int) ap_xml_parse_input(request_rec * r, ap_xml_doc **pdoc) -{ + apr_xml_parser *parser; int result; - ap_xml_ctx ctx = { 0 }; - XML_Parser parser; + apr_status_t status; + char errbuf[200]; if ((result = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK) return result; @@ -377,38 +79,20 @@ *pdoc = NULL; return OK; } - - ctx.p = r->pool; - ctx.doc = apr_pcalloc(ctx.p, sizeof(*ctx.doc)); - - ctx.doc->namespaces = apr_array_make(ctx.p, 5, sizeof(const char *)); - ap_xml_insert_uri(ctx.doc->namespaces, "DAV:"); - - /* ### we should get the encoding from Content-Encoding */ - parser = XML_ParserCreate(NULL); - if (parser == NULL) { - /* ### anything better to do? */ - fprintf(stderr, "Ouch! XML_ParserCreate() failed!\n"); - exit(1); - } - XML_SetUserData(parser, (void *) &ctx); - XML_SetElementHandler(parser, start_handler, end_handler); - XML_SetCharacterDataHandler(parser, cdata_handler); + parser = apr_xml_parser_create(r->pool); if (ap_should_client_block(r)) { long len; char *buffer; - char end; - int rv; apr_size_t total_read = 0; apr_size_t limit_xml_body = ap_get_limit_xml_body(r); /* allocate our working buffer */ - buffer = apr_palloc(r->pool, AP_XML_READ_BLOCKSIZE); + buffer = apr_palloc(r->pool, READ_BLOCKSIZE); /* read the body, stuffing it into the parser */ - while ((len = ap_get_client_block(r, buffer, AP_XML_READ_BLOCKSIZE)) > 0) { + while ((len = ap_get_client_block(r, buffer, READ_BLOCKSIZE)) > 0) { total_read += len; if (limit_xml_body && total_read > limit_xml_body) { ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, @@ -417,453 +101,34 @@ goto read_error; } - rv = XML_Parse(parser, buffer, len, 0); - if (rv == 0) + status = apr_xml_parser_feed(parser, buffer, len); + if (status) goto parser_error; } if (len == -1) { /* ap_get_client_block() has logged an error */ goto read_error; } - - /* tell the parser that we're done */ - rv = XML_Parse(parser, &end, 0, 1); - if (rv == 0) - goto parser_error; - } - - XML_ParserFree(parser); - - if (ctx.error) { - switch (ctx.error) { - case AP_XML_NS_ERROR_UNKNOWN_PREFIX: - ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, - "An undefined namespace prefix was used."); - break; - - default: - ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, - "There was an error within the XML request body."); - break; - } - - /* Apache will supply a default error, plus the error log above. */ - return HTTP_BAD_REQUEST; } - /* ### assert: ctx.cur_elem == NULL */ + /* tell the parser that we're done */ + status = apr_xml_parser_done(parser, pdoc); + if (status) + goto parser_error; - *pdoc = ctx.doc; - return OK; parser_error: - { - enum XML_Error err = XML_GetErrorCode(parser); - - /* ### fix this error message (default vs special) */ - ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, - "XML parser error code: %s (%d).", - XML_ErrorString(err), err); + (void) apr_xml_parser_geterror(parser, errbuf, sizeof(errbuf)); + ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r, + "%s", errbuf); - XML_ParserFree(parser); + /* FALLTHRU */ - /* Apache will supply a default error, plus the error log above. */ - return HTTP_BAD_REQUEST; - } - read_error: - XML_ParserFree(parser); + /* make sure the parser is terminated */ + (void) apr_xml_parser_done(parser, NULL); - /* Apache will supply a default error, plus whatever was logged. */ + /* Apache will supply a default error, plus the error log above. */ return HTTP_BAD_REQUEST; -} - -AP_DECLARE(void) ap_text_append(apr_pool_t * p, ap_text_header *hdr, - const char *text) -{ - ap_text *t = apr_palloc(p, sizeof(*t)); - - t->text = text; - t->next = NULL; - - if (hdr->first == NULL) { - /* no text elements yet */ - hdr->first = hdr->last = t; - } - else { - /* append to the last text element */ - hdr->last->next = t; - hdr->last = t; - } -} - - -/* --------------------------------------------------------------- -** -** XML UTILITY FUNCTIONS -*/ - -/* -** ap_xml_quote_string: quote an XML string -** -** Replace '<', '>', and '&' with '<', '>', and '&'. -** If quotes is true, then replace '"' with '"'. -** -** quotes is typically set to true for XML strings that will occur within -** double quotes -- attribute values. -*/ -AP_DECLARE(const char *) ap_xml_quote_string(apr_pool_t *p, const char *s, - int quotes) -{ - const char *scan; - apr_size_t len = 0; - apr_size_t extra = 0; - char *qstr; - char *qscan; - char c; - - for (scan = s; (c = *scan) != '\0'; ++scan, ++len) { - if (c == '<' || c == '>') - extra += 3; /* < or > */ - else if (c == '&') - extra += 4; /* & */ - else if (quotes && c == '"') - extra += 5; /* " */ - } - - /* nothing to do? */ - if (extra == 0) - return s; - - qstr = apr_palloc(p, len + extra + 1); - for (scan = s, qscan = qstr; (c = *scan) != '\0'; ++scan) { - if (c == '<') { - *qscan++ = '&'; - *qscan++ = 'l'; - *qscan++ = 't'; - *qscan++ = ';'; - } - else if (c == '>') { - *qscan++ = '&'; - *qscan++ = 'g'; - *qscan++ = 't'; - *qscan++ = ';'; - } - else if (c == '&') { - *qscan++ = '&'; - *qscan++ = 'a'; - *qscan++ = 'm'; - *qscan++ = 'p'; - *qscan++ = ';'; - } - else if (quotes && c == '"') { - *qscan++ = '&'; - *qscan++ = 'q'; - *qscan++ = 'u'; - *qscan++ = 'o'; - *qscan++ = 't'; - *qscan++ = ';'; - } - else { - *qscan++ = c; - } - } - - *qscan = '\0'; - return qstr; -} - -/* how many characters for the given integer? */ -#define AP_XML_NS_LEN(ns) ((ns) < 10 ? 1 : (ns) < 100 ? 2 : (ns) < 1000 ? 3 : \ - (ns) < 10000 ? 4 : (ns) < 100000 ? 5 : \ - (ns) < 1000000 ? 6 : (ns) < 10000000 ? 7 : \ - (ns) < 100000000 ? 8 : (ns) < 1000000000 ? 9 : 10) - -static apr_size_t text_size(const ap_text *t) -{ - apr_size_t size = 0; - - for (; t; t = t->next) - size += strlen(t->text); - return size; -} - -static apr_size_t elem_size(const ap_xml_elem *elem, int style, - apr_array_header_t *namespaces, int *ns_map) -{ - apr_size_t size; - - if (style == AP_XML_X2T_FULL || style == AP_XML_X2T_FULL_NS_LANG) { - const ap_xml_attr *attr; - - size = 0; - - if (style == AP_XML_X2T_FULL_NS_LANG) { - int i; - - /* - ** The outer element will contain xmlns:ns%d="%s" attributes - ** and an xml:lang attribute, if applicable. - */ - - for (i = namespaces->nelts; i--;) { - /* compute size of: ' xmlns:ns%d="%s"' */ - size += (9 + AP_XML_NS_LEN(i) + 2 + - strlen(AP_XML_GET_URI_ITEM(namespaces, i)) + 1); - } - - if (elem->lang != NULL) { - /* compute size of: ' xml:lang="%s"' */ - size += 11 + strlen(elem->lang) + 1; - } - } - - if (elem->ns == AP_XML_NS_NONE) { - /* compute size of: <%s> */ - size += 1 + strlen(elem->name) + 1; - } - else { - int ns = ns_map ? ns_map[elem->ns] : elem->ns; - - /* compute size of: */ - size += 3 + AP_XML_NS_LEN(ns) + 1 + strlen(elem->name) + 1; - } - - if (AP_XML_ELEM_IS_EMPTY(elem)) { - /* insert a closing "/" */ - size += 1; - } - else { - /* - * two of above plus "/": - * ... - * OR <%s> ... - */ - size = 2 * size + 1; - } - - for (attr = elem->attr; attr; attr = attr->next) { - if (attr->ns == AP_XML_NS_NONE) { - /* compute size of: ' %s="%s"' */ - size += 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1; - } - else { - /* compute size of: ' ns%d:%s="%s"' */ - size += 3 + AP_XML_NS_LEN(attr->ns) + 1 + strlen(attr->name) + 2 + strlen(attr->value) + 1; - } - } - - /* - ** If the element has an xml:lang value that is *different* from - ** its parent, then add the thing in: ' xml:lang="%s"'. - ** - ** NOTE: we take advantage of the pointer equality established by - ** the parsing for "inheriting" the xml:lang values from parents. - */ - if (elem->lang != NULL && - (elem->parent == NULL || elem->lang != elem->parent->lang)) { - size += 11 + strlen(elem->lang) + 1; - } - } - else if (style == AP_XML_X2T_LANG_INNER) { - /* - * This style prepends the xml:lang value plus a null terminator. - * If a lang value is not present, then we insert a null term. - */ - size = elem->lang ? strlen(elem->lang) + 1 : 1; - } - else - size = 0; - - size += text_size(elem->first_cdata.first); - - for (elem = elem->first_child; elem; elem = elem->next) { - /* the size of the child element plus the CDATA that follows it */ - size += (elem_size(elem, AP_XML_X2T_FULL, NULL, ns_map) + - text_size(elem->following_cdata.first)); - } - - return size; -} - -static char *write_text(char *s, const ap_text *t) -{ - for (; t; t = t->next) { - apr_size_t len = strlen(t->text); - memcpy(s, t->text, len); - s += len; - } - return s; -} - -static char *write_elem(char *s, const ap_xml_elem *elem, int style, - apr_array_header_t *namespaces, int *ns_map) -{ - const ap_xml_elem *child; - apr_size_t len; - int ns; - - if (style == AP_XML_X2T_FULL || style == AP_XML_X2T_FULL_NS_LANG) { - int empty = AP_XML_ELEM_IS_EMPTY(elem); - const ap_xml_attr *attr; - - if (elem->ns == AP_XML_NS_NONE) { - len = sprintf(s, "<%s", elem->name); - } - else { - ns = ns_map ? ns_map[elem->ns] : elem->ns; - len = sprintf(s, "name); - } - s += len; - - for (attr = elem->attr; attr; attr = attr->next) { - if (attr->ns == AP_XML_NS_NONE) - len = sprintf(s, " %s=\"%s\"", attr->name, attr->value); - else - len = sprintf(s, " ns%d:%s=\"%s\"", attr->ns, attr->name, attr->value); - s += len; - } - - /* add the xml:lang value if necessary */ - if (elem->lang != NULL && - (style == AP_XML_X2T_FULL_NS_LANG || - elem->parent == NULL || - elem->lang != elem->parent->lang)) { - len = sprintf(s, " xml:lang=\"%s\"", elem->lang); - s += len; - } - - /* add namespace definitions, if required */ - if (style == AP_XML_X2T_FULL_NS_LANG) { - int i; - - for (i = namespaces->nelts; i--;) { - len = sprintf(s, " xmlns:ns%d=\"%s\"", i, - AP_XML_GET_URI_ITEM(namespaces, i)); - s += len; - } - } - - /* no more to do. close it up and go. */ - if (empty) { - *s++ = '/'; - *s++ = '>'; - return s; - } - - /* just close it */ - *s++ = '>'; - } - else if (style == AP_XML_X2T_LANG_INNER) { - /* prepend the xml:lang value */ - if (elem->lang != NULL) { - len = strlen(elem->lang); - memcpy(s, elem->lang, len); - s += len; - } - *s++ = '\0'; - } - - s = write_text(s, elem->first_cdata.first); - - for (child = elem->first_child; child; child = child->next) { - s = write_elem(s, child, AP_XML_X2T_FULL, NULL, ns_map); - s = write_text(s, child->following_cdata.first); - } - - if (style == AP_XML_X2T_FULL || style == AP_XML_X2T_FULL_NS_LANG) { - if (elem->ns == AP_XML_NS_NONE) { - len = sprintf(s, "", elem->name); - } - else { - ns = ns_map ? ns_map[elem->ns] : elem->ns; - len = sprintf(s, "", ns, elem->name); - } - s += len; - } - - return s; -} - -AP_DECLARE(void) ap_xml_quote_elem(apr_pool_t *p, ap_xml_elem *elem) -{ - ap_text *scan_txt; - ap_xml_attr *scan_attr; - ap_xml_elem *scan_elem; - - /* convert the element's text */ - for (scan_txt = elem->first_cdata.first; - scan_txt != NULL; - scan_txt = scan_txt->next) { - scan_txt->text = ap_xml_quote_string(p, scan_txt->text, 0); - } - for (scan_txt = elem->following_cdata.first; - scan_txt != NULL; - scan_txt = scan_txt->next) { - scan_txt->text = ap_xml_quote_string(p, scan_txt->text, 0); - } - - /* convert the attribute values */ - for (scan_attr = elem->attr; - scan_attr != NULL; - scan_attr = scan_attr->next) { - scan_attr->value = ap_xml_quote_string(p, scan_attr->value, 1); - } - - /* convert the child elements */ - for (scan_elem = elem->first_child; - scan_elem != NULL; - scan_elem = scan_elem->next) { - ap_xml_quote_elem(p, scan_elem); - } -} - -/* convert an element to a text string */ -AP_DECLARE(void) ap_xml_to_text(apr_pool_t * p, const ap_xml_elem *elem, - int style, apr_array_header_t *namespaces, - int *ns_map, const char **pbuf, - apr_size_t *psize) -{ - /* get the exact size, plus a null terminator */ - apr_size_t size = elem_size(elem, style, namespaces, ns_map) + 1; - char *s = apr_palloc(p, size); - - (void) write_elem(s, elem, style, namespaces, ns_map); - s[size - 1] = '\0'; - - *pbuf = s; - if (psize) - *psize = size; -} - -AP_DECLARE(const char *) ap_xml_empty_elem(apr_pool_t * p, - const ap_xml_elem *elem) -{ - if (elem->ns == AP_XML_NS_NONE) { - /* - * The prefix (xml...) is already within the prop name, or - * the element simply has no prefix. - */ - return apr_psprintf(p, "<%s/>" DEBUG_CR, elem->name); - } - - return apr_psprintf(p, "" DEBUG_CR, elem->ns, elem->name); -} - -/* return the URI's (existing) index, or insert it and return a new index */ -AP_DECLARE(int) ap_xml_insert_uri(apr_array_header_t *uri_array, - const char *uri) -{ - int i; - const char **pelt; - - for (i = uri_array->nelts; i--;) { - if (strcmp(uri, AP_XML_GET_URI_ITEM(uri_array, i)) == 0) - return i; - } - - pelt = apr_array_push(uri_array); - *pelt = uri; /* assume uri is const or in a pool */ - return uri_array->nelts - 1; }