/* 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 #elif defined(HAVE_XMLTOK_XMLPARSE_H) #include #elif defined(HAVE_XML_XMLPARSE_H) #include #else #include #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