/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "SkXMLParser.h" #include "expat.h" #include "SkStream.h" #include "SkString.h" #include "SkTo.h" #include "SkTypes.h" static char const* const gErrorStrings[] = { "empty or missing file ", "unknown element ", "unknown attribute name ", "error in attribute value ", "duplicate ID ", "unknown error " }; SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1), fNativeCode(-1) { reset(); } SkXMLParserError::~SkXMLParserError() { // need a virtual destructor for our subclasses } void SkXMLParserError::getErrorString(SkString* str) const { SkASSERT(str); SkString temp; if (fCode != kNoError) { if ((unsigned)fCode < SK_ARRAY_COUNT(gErrorStrings)) temp.set(gErrorStrings[fCode - 1]); temp.append(fNoun); } else SkXMLParser::GetNativeErrorString(fNativeCode, &temp); str->append(temp); } void SkXMLParserError::reset() { fCode = kNoError; fLineNumber = -1; fNativeCode = -1; } //////////////// namespace { const XML_Memory_Handling_Suite sk_XML_alloc = { sk_malloc_throw, sk_realloc_throw, sk_free }; struct ParsingContext { ParsingContext(SkXMLParser* parser) : fParser(parser) , fXMLParser(XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr)) { } void flushText() { if (!fBufferedText.isEmpty()) { fParser->text(fBufferedText.c_str(), SkTo(fBufferedText.size())); fBufferedText.reset(); } } void appendText(const char* txt, size_t len) { fBufferedText.append(txt, len); } SkXMLParser* fParser; SkAutoTCallVProc, XML_ParserFree> fXMLParser; private: SkString fBufferedText; }; #define HANDLER_CONTEXT(arg, name) ParsingContext* name = static_cast(arg); void XMLCALL start_element_handler(void *data, const char* tag, const char** attributes) { HANDLER_CONTEXT(data, ctx); ctx->flushText(); ctx->fParser->startElement(tag); for (size_t i = 0; attributes[i]; i += 2) { ctx->fParser->addAttribute(attributes[i], attributes[i + 1]); } } void XMLCALL end_element_handler(void* data, const char* tag) { HANDLER_CONTEXT(data, ctx); ctx->flushText(); ctx->fParser->endElement(tag); } void XMLCALL text_handler(void *data, const char* txt, int len) { HANDLER_CONTEXT(data, ctx); ctx->appendText(txt, SkTo(len)); } void XMLCALL entity_decl_handler(void *data, 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) { HANDLER_CONTEXT(data, ctx); SkDebugf("'%s' entity declaration found, stopping processing", entityName); XML_StopParser(ctx->fXMLParser, XML_FALSE); } } // anonymous namespace SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(nullptr), fError(parserError) { } SkXMLParser::~SkXMLParser() { } bool SkXMLParser::parse(SkStream& docStream) { ParsingContext ctx(this); if (!ctx.fXMLParser) { SkDebugf("could not create XML parser\n"); return false; } XML_SetUserData(ctx.fXMLParser, &ctx); XML_SetElementHandler(ctx.fXMLParser, start_element_handler, end_element_handler); XML_SetCharacterDataHandler(ctx.fXMLParser, text_handler); // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340. XML_SetEntityDeclHandler(ctx.fXMLParser, entity_decl_handler); static const int kBufferSize = 512 SkDEBUGCODE( - 507); bool done = false; do { void* buffer = XML_GetBuffer(ctx.fXMLParser, kBufferSize); if (!buffer) { SkDebugf("could not buffer enough to continue\n"); return false; } size_t len = docStream.read(buffer, kBufferSize); done = docStream.isAtEnd(); XML_Status status = XML_ParseBuffer(ctx.fXMLParser, SkToS32(len), done); if (XML_STATUS_ERROR == status) { XML_Error error = XML_GetErrorCode(ctx.fXMLParser); int line = XML_GetCurrentLineNumber(ctx.fXMLParser); int column = XML_GetCurrentColumnNumber(ctx.fXMLParser); const XML_LChar* errorString = XML_ErrorString(error); SkDebugf("parse error @%d:%d: %d (%s).\n", line, column, error, errorString); return false; } } while (!done); return true; } bool SkXMLParser::parse(const char doc[], size_t len) { SkMemoryStream docStream(doc, len); return this->parse(docStream); } void SkXMLParser::GetNativeErrorString(int error, SkString* str) { } bool SkXMLParser::startElement(const char elem[]) { return this->onStartElement(elem); } bool SkXMLParser::addAttribute(const char name[], const char value[]) { return this->onAddAttribute(name, value); } bool SkXMLParser::endElement(const char elem[]) { return this->onEndElement(elem); } bool SkXMLParser::text(const char text[], int len) { return this->onText(text, len); } //////////////////////////////////////////////////////////////////////////////// bool SkXMLParser::onStartElement(const char elem[]) {return false; } bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; } bool SkXMLParser::onEndElement(const char elem[]) { return false; } bool SkXMLParser::onText(const char text[], int len) {return false; }