blob: 1df279988cf7a366cdfc98e55d676226d42da698 [file] [log] [blame]
/* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "apr.h"
#if APU_USE_EXPAT
#include "apr_xml.h"
#if defined(HAVE_XMLPARSE_XMLPARSE_H)
#include <xmlparse/xmlparse.h>
#elif defined(HAVE_XMLTOK_XMLPARSE_H)
#include <xmltok/xmlparse.h>
#elif defined(HAVE_XML_XMLPARSE_H)
#include <xml/xmlparse.h>
#else
#include <expat.h>
#endif
typedef enum XML_Error XML_Error;
#include "apr_xml_internal.h"
static apr_status_t cleanup_parser(void *ctx)
{
apr_xml_parser *parser = ctx;
XML_ParserFree(parser->xp);
parser->xp = NULL;
return APR_SUCCESS;
}
static apr_status_t do_parse(apr_xml_parser *parser,
const char *data, apr_size_t len,
int is_final)
{
if (parser->xp == NULL) {
parser->error = APR_XML_ERROR_PARSE_DONE;
}
else {
int rv = XML_Parse(parser->xp, data, (int)len, is_final);
if (rv == 0) {
parser->error = APR_XML_ERROR_EXPAT;
parser->xp_err = XML_GetErrorCode(parser->xp);
parser->xp_msg = XML_ErrorString(parser->xp_err);
}
}
/* ### better error code? */
return parser->error ? APR_EGENERAL : APR_SUCCESS;
}
static XMLParserImpl xml_parser_expat = {
do_parse,
cleanup_parser
};
XMLParserImpl* apr_xml_get_parser_impl(void) { return &xml_parser_expat; }
static const char APR_KW_DAV[] = { 0x44, 0x41, 0x56, 0x3A, '\0' };
#if XML_MAJOR_VERSION > 1
/* Stop the parser if an entity declaration is hit. */
static void entity_declaration(void *userData, const XML_Char *entityName,
int is_parameter_entity, const XML_Char *value,
int value_length, const XML_Char *base,
const XML_Char *systemId, const XML_Char *publicId,
const XML_Char *notationName)
{
apr_xml_parser *parser = userData;
XML_StopParser(parser->xp, XML_FALSE);
}
#else
/* A noop default_handler. */
static void default_handler(void *userData, const XML_Char *s, int len)
{
}
#endif
apr_xml_parser* apr_xml_parser_create_internal(apr_pool_t *pool,
void *start_func, void *end_func, void *cdata_func)
{
apr_xml_parser *parser = apr_pcalloc(pool, sizeof(*parser));
parser->impl = apr_xml_get_parser_impl();
parser->p = pool;
parser->doc = apr_pcalloc(pool, sizeof(*parser->doc));
parser->doc->namespaces = apr_array_make(pool, 5, sizeof(const char *));
/* ### is there a way to avoid hard-coding this? */
apr_xml_insert_uri(parser->doc->namespaces, APR_KW_DAV);
parser->xp = XML_ParserCreate(NULL);
if (parser->xp == NULL) {
(*apr_pool_abort_get(pool))(APR_ENOMEM);
return NULL;
}
apr_pool_cleanup_register(pool, parser, cleanup_parser,
apr_pool_cleanup_null);
XML_SetUserData(parser->xp, parser);
XML_SetElementHandler(parser->xp, start_func, end_func);
XML_SetCharacterDataHandler(parser->xp, cdata_func);
/* Prevent the "billion laughs" attack against expat by disabling
* internal entity expansion. With 2.x, forcibly stop the parser
* if an entity is declared - this is safer and a more obvious
* failure mode. With older versions, installing a noop
* DefaultHandler means that internal entities will be expanded as
* the empty string, which is also sufficient to prevent the
* attack. */
#if XML_MAJOR_VERSION > 1
XML_SetEntityDeclHandler(parser->xp, entity_declaration);
#else
XML_SetDefaultHandler(parser->xp, default_handler);
#endif
return parser;
}
#endif