1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2006 The Android Open Source Project
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkStream.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTo.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/xml/SkXMLParser.h"
14*c8dee2aaSAndroid Build Coastguard Worker
15*c8dee2aaSAndroid Build Coastguard Worker #include <expat.h>
16*c8dee2aaSAndroid Build Coastguard Worker
17*c8dee2aaSAndroid Build Coastguard Worker #include <vector>
18*c8dee2aaSAndroid Build Coastguard Worker
19*c8dee2aaSAndroid Build Coastguard Worker static char const* const gErrorStrings[] = {
20*c8dee2aaSAndroid Build Coastguard Worker "empty or missing file ",
21*c8dee2aaSAndroid Build Coastguard Worker "unknown element ",
22*c8dee2aaSAndroid Build Coastguard Worker "unknown attribute name ",
23*c8dee2aaSAndroid Build Coastguard Worker "error in attribute value ",
24*c8dee2aaSAndroid Build Coastguard Worker "duplicate ID ",
25*c8dee2aaSAndroid Build Coastguard Worker "unknown error "
26*c8dee2aaSAndroid Build Coastguard Worker };
27*c8dee2aaSAndroid Build Coastguard Worker
SkXMLParserError()28*c8dee2aaSAndroid Build Coastguard Worker SkXMLParserError::SkXMLParserError() : fCode(kNoError), fLineNumber(-1),
29*c8dee2aaSAndroid Build Coastguard Worker fNativeCode(-1)
30*c8dee2aaSAndroid Build Coastguard Worker {
31*c8dee2aaSAndroid Build Coastguard Worker reset();
32*c8dee2aaSAndroid Build Coastguard Worker }
33*c8dee2aaSAndroid Build Coastguard Worker
~SkXMLParserError()34*c8dee2aaSAndroid Build Coastguard Worker SkXMLParserError::~SkXMLParserError()
35*c8dee2aaSAndroid Build Coastguard Worker {
36*c8dee2aaSAndroid Build Coastguard Worker // need a virtual destructor for our subclasses
37*c8dee2aaSAndroid Build Coastguard Worker }
38*c8dee2aaSAndroid Build Coastguard Worker
getErrorString(SkString * str) const39*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParserError::getErrorString(SkString* str) const
40*c8dee2aaSAndroid Build Coastguard Worker {
41*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(str);
42*c8dee2aaSAndroid Build Coastguard Worker SkString temp;
43*c8dee2aaSAndroid Build Coastguard Worker if (fCode != kNoError) {
44*c8dee2aaSAndroid Build Coastguard Worker if ((unsigned)fCode < std::size(gErrorStrings))
45*c8dee2aaSAndroid Build Coastguard Worker temp.set(gErrorStrings[fCode - 1]);
46*c8dee2aaSAndroid Build Coastguard Worker temp.append(fNoun);
47*c8dee2aaSAndroid Build Coastguard Worker } else
48*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser::GetNativeErrorString(fNativeCode, &temp);
49*c8dee2aaSAndroid Build Coastguard Worker str->append(temp);
50*c8dee2aaSAndroid Build Coastguard Worker }
51*c8dee2aaSAndroid Build Coastguard Worker
reset()52*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParserError::reset() {
53*c8dee2aaSAndroid Build Coastguard Worker fCode = kNoError;
54*c8dee2aaSAndroid Build Coastguard Worker fLineNumber = -1;
55*c8dee2aaSAndroid Build Coastguard Worker fNativeCode = -1;
56*c8dee2aaSAndroid Build Coastguard Worker }
57*c8dee2aaSAndroid Build Coastguard Worker
58*c8dee2aaSAndroid Build Coastguard Worker ////////////////
59*c8dee2aaSAndroid Build Coastguard Worker
60*c8dee2aaSAndroid Build Coastguard Worker namespace {
61*c8dee2aaSAndroid Build Coastguard Worker
62*c8dee2aaSAndroid Build Coastguard Worker constexpr const void* kHashSeed = &kHashSeed;
63*c8dee2aaSAndroid Build Coastguard Worker
64*c8dee2aaSAndroid Build Coastguard Worker const XML_Memory_Handling_Suite sk_XML_alloc = {
65*c8dee2aaSAndroid Build Coastguard Worker sk_malloc_throw,
66*c8dee2aaSAndroid Build Coastguard Worker sk_realloc_throw,
67*c8dee2aaSAndroid Build Coastguard Worker sk_free
68*c8dee2aaSAndroid Build Coastguard Worker };
69*c8dee2aaSAndroid Build Coastguard Worker
70*c8dee2aaSAndroid Build Coastguard Worker struct ParsingContext {
ParsingContext__anonbcbf8b420111::ParsingContext71*c8dee2aaSAndroid Build Coastguard Worker ParsingContext(SkXMLParser* parser)
72*c8dee2aaSAndroid Build Coastguard Worker : fParser(parser)
73*c8dee2aaSAndroid Build Coastguard Worker , fXMLParser(XML_ParserCreate_MM(nullptr, &sk_XML_alloc, nullptr)) { }
74*c8dee2aaSAndroid Build Coastguard Worker
flushText__anonbcbf8b420111::ParsingContext75*c8dee2aaSAndroid Build Coastguard Worker void flushText() {
76*c8dee2aaSAndroid Build Coastguard Worker if (!fBufferedText.empty()) {
77*c8dee2aaSAndroid Build Coastguard Worker fParser->text(fBufferedText.data(), SkTo<int>(fBufferedText.size()));
78*c8dee2aaSAndroid Build Coastguard Worker fBufferedText.clear();
79*c8dee2aaSAndroid Build Coastguard Worker }
80*c8dee2aaSAndroid Build Coastguard Worker }
81*c8dee2aaSAndroid Build Coastguard Worker
appendText__anonbcbf8b420111::ParsingContext82*c8dee2aaSAndroid Build Coastguard Worker void appendText(const char* txt, size_t len) {
83*c8dee2aaSAndroid Build Coastguard Worker fBufferedText.insert(fBufferedText.end(), txt, &txt[len]);
84*c8dee2aaSAndroid Build Coastguard Worker }
85*c8dee2aaSAndroid Build Coastguard Worker
86*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser* fParser;
87*c8dee2aaSAndroid Build Coastguard Worker SkAutoTCallVProc<std::remove_pointer_t<XML_Parser>, XML_ParserFree> fXMLParser;
88*c8dee2aaSAndroid Build Coastguard Worker
89*c8dee2aaSAndroid Build Coastguard Worker private:
90*c8dee2aaSAndroid Build Coastguard Worker std::vector<char> fBufferedText;
91*c8dee2aaSAndroid Build Coastguard Worker };
92*c8dee2aaSAndroid Build Coastguard Worker
93*c8dee2aaSAndroid Build Coastguard Worker #define HANDLER_CONTEXT(arg, name) ParsingContext* name = static_cast<ParsingContext*>(arg)
94*c8dee2aaSAndroid Build Coastguard Worker
start_element_handler(void * data,const char * tag,const char ** attributes)95*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL start_element_handler(void *data, const char* tag, const char** attributes) {
96*c8dee2aaSAndroid Build Coastguard Worker HANDLER_CONTEXT(data, ctx);
97*c8dee2aaSAndroid Build Coastguard Worker ctx->flushText();
98*c8dee2aaSAndroid Build Coastguard Worker
99*c8dee2aaSAndroid Build Coastguard Worker ctx->fParser->startElement(tag);
100*c8dee2aaSAndroid Build Coastguard Worker
101*c8dee2aaSAndroid Build Coastguard Worker for (size_t i = 0; attributes[i]; i += 2) {
102*c8dee2aaSAndroid Build Coastguard Worker ctx->fParser->addAttribute(attributes[i], attributes[i + 1]);
103*c8dee2aaSAndroid Build Coastguard Worker }
104*c8dee2aaSAndroid Build Coastguard Worker }
105*c8dee2aaSAndroid Build Coastguard Worker
end_element_handler(void * data,const char * tag)106*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL end_element_handler(void* data, const char* tag) {
107*c8dee2aaSAndroid Build Coastguard Worker HANDLER_CONTEXT(data, ctx);
108*c8dee2aaSAndroid Build Coastguard Worker ctx->flushText();
109*c8dee2aaSAndroid Build Coastguard Worker
110*c8dee2aaSAndroid Build Coastguard Worker ctx->fParser->endElement(tag);
111*c8dee2aaSAndroid Build Coastguard Worker }
112*c8dee2aaSAndroid Build Coastguard Worker
text_handler(void * data,const char * txt,int len)113*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL text_handler(void *data, const char* txt, int len) {
114*c8dee2aaSAndroid Build Coastguard Worker HANDLER_CONTEXT(data, ctx);
115*c8dee2aaSAndroid Build Coastguard Worker
116*c8dee2aaSAndroid Build Coastguard Worker ctx->appendText(txt, SkTo<size_t>(len));
117*c8dee2aaSAndroid Build Coastguard Worker }
118*c8dee2aaSAndroid Build Coastguard Worker
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)119*c8dee2aaSAndroid Build Coastguard Worker void XMLCALL entity_decl_handler(void *data,
120*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *entityName,
121*c8dee2aaSAndroid Build Coastguard Worker int is_parameter_entity,
122*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *value,
123*c8dee2aaSAndroid Build Coastguard Worker int value_length,
124*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *base,
125*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *systemId,
126*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *publicId,
127*c8dee2aaSAndroid Build Coastguard Worker const XML_Char *notationName) {
128*c8dee2aaSAndroid Build Coastguard Worker HANDLER_CONTEXT(data, ctx);
129*c8dee2aaSAndroid Build Coastguard Worker
130*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGF("'%s' entity declaration found, stopping processing", entityName);
131*c8dee2aaSAndroid Build Coastguard Worker XML_StopParser(ctx->fXMLParser, XML_FALSE);
132*c8dee2aaSAndroid Build Coastguard Worker }
133*c8dee2aaSAndroid Build Coastguard Worker
134*c8dee2aaSAndroid Build Coastguard Worker } // anonymous namespace
135*c8dee2aaSAndroid Build Coastguard Worker
SkXMLParser(SkXMLParserError * parserError)136*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser::SkXMLParser(SkXMLParserError* parserError) : fParser(nullptr), fError(parserError)
137*c8dee2aaSAndroid Build Coastguard Worker {
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker
~SkXMLParser()140*c8dee2aaSAndroid Build Coastguard Worker SkXMLParser::~SkXMLParser()
141*c8dee2aaSAndroid Build Coastguard Worker {
142*c8dee2aaSAndroid Build Coastguard Worker }
143*c8dee2aaSAndroid Build Coastguard Worker
parse(SkStream & docStream)144*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::parse(SkStream& docStream)
145*c8dee2aaSAndroid Build Coastguard Worker {
146*c8dee2aaSAndroid Build Coastguard Worker ParsingContext ctx(this);
147*c8dee2aaSAndroid Build Coastguard Worker if (!ctx.fXMLParser) {
148*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGF("could not create XML parser\n");
149*c8dee2aaSAndroid Build Coastguard Worker return false;
150*c8dee2aaSAndroid Build Coastguard Worker }
151*c8dee2aaSAndroid Build Coastguard Worker
152*c8dee2aaSAndroid Build Coastguard Worker // Avoid calls to rand_s if this is not set. This seed helps prevent DOS
153*c8dee2aaSAndroid Build Coastguard Worker // with a known hash sequence so an address is sufficient. The provided
154*c8dee2aaSAndroid Build Coastguard Worker // seed should not be zero as that results in a call to rand_s.
155*c8dee2aaSAndroid Build Coastguard Worker unsigned long seed = static_cast<unsigned long>(
156*c8dee2aaSAndroid Build Coastguard Worker reinterpret_cast<size_t>(kHashSeed) & 0xFFFFFFFF);
157*c8dee2aaSAndroid Build Coastguard Worker XML_SetHashSalt(ctx.fXMLParser, seed ? seed : 1);
158*c8dee2aaSAndroid Build Coastguard Worker
159*c8dee2aaSAndroid Build Coastguard Worker XML_SetUserData(ctx.fXMLParser, &ctx);
160*c8dee2aaSAndroid Build Coastguard Worker XML_SetElementHandler(ctx.fXMLParser, start_element_handler, end_element_handler);
161*c8dee2aaSAndroid Build Coastguard Worker XML_SetCharacterDataHandler(ctx.fXMLParser, text_handler);
162*c8dee2aaSAndroid Build Coastguard Worker
163*c8dee2aaSAndroid Build Coastguard Worker // Disable entity processing, to inhibit internal entity expansion. See expat CVE-2013-0340.
164*c8dee2aaSAndroid Build Coastguard Worker XML_SetEntityDeclHandler(ctx.fXMLParser, entity_decl_handler);
165*c8dee2aaSAndroid Build Coastguard Worker
166*c8dee2aaSAndroid Build Coastguard Worker XML_Status status = XML_STATUS_OK;
167*c8dee2aaSAndroid Build Coastguard Worker if (docStream.getMemoryBase() && docStream.hasLength()) {
168*c8dee2aaSAndroid Build Coastguard Worker const char* base = reinterpret_cast<const char*>(docStream.getMemoryBase());
169*c8dee2aaSAndroid Build Coastguard Worker status = XML_Parse(ctx.fXMLParser,
170*c8dee2aaSAndroid Build Coastguard Worker base + docStream.getPosition(),
171*c8dee2aaSAndroid Build Coastguard Worker docStream.getLength() - docStream.getPosition(),
172*c8dee2aaSAndroid Build Coastguard Worker true);
173*c8dee2aaSAndroid Build Coastguard Worker } else {
174*c8dee2aaSAndroid Build Coastguard Worker static constexpr int kBufferSize = 4096;
175*c8dee2aaSAndroid Build Coastguard Worker bool done = false;
176*c8dee2aaSAndroid Build Coastguard Worker do {
177*c8dee2aaSAndroid Build Coastguard Worker void* buffer = XML_GetBuffer(ctx.fXMLParser, kBufferSize);
178*c8dee2aaSAndroid Build Coastguard Worker if (!buffer) {
179*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGF("could not buffer enough to continue\n");
180*c8dee2aaSAndroid Build Coastguard Worker return false;
181*c8dee2aaSAndroid Build Coastguard Worker }
182*c8dee2aaSAndroid Build Coastguard Worker
183*c8dee2aaSAndroid Build Coastguard Worker size_t len = docStream.read(buffer, kBufferSize);
184*c8dee2aaSAndroid Build Coastguard Worker done = docStream.isAtEnd();
185*c8dee2aaSAndroid Build Coastguard Worker status = XML_ParseBuffer(ctx.fXMLParser, SkToS32(len), done);
186*c8dee2aaSAndroid Build Coastguard Worker if (XML_STATUS_ERROR == status) {
187*c8dee2aaSAndroid Build Coastguard Worker break;
188*c8dee2aaSAndroid Build Coastguard Worker }
189*c8dee2aaSAndroid Build Coastguard Worker } while (!done);
190*c8dee2aaSAndroid Build Coastguard Worker }
191*c8dee2aaSAndroid Build Coastguard Worker if (XML_STATUS_ERROR == status) {
192*c8dee2aaSAndroid Build Coastguard Worker #if defined(SK_DEBUG)
193*c8dee2aaSAndroid Build Coastguard Worker XML_Error error = XML_GetErrorCode(ctx.fXMLParser);
194*c8dee2aaSAndroid Build Coastguard Worker int line = XML_GetCurrentLineNumber(ctx.fXMLParser);
195*c8dee2aaSAndroid Build Coastguard Worker int column = XML_GetCurrentColumnNumber(ctx.fXMLParser);
196*c8dee2aaSAndroid Build Coastguard Worker const XML_LChar* errorString = XML_ErrorString(error);
197*c8dee2aaSAndroid Build Coastguard Worker SkDEBUGF("parse error @%d:%d: %d (%s).\n", line, column, error, errorString);
198*c8dee2aaSAndroid Build Coastguard Worker #endif
199*c8dee2aaSAndroid Build Coastguard Worker return false;
200*c8dee2aaSAndroid Build Coastguard Worker }
201*c8dee2aaSAndroid Build Coastguard Worker
202*c8dee2aaSAndroid Build Coastguard Worker return true;
203*c8dee2aaSAndroid Build Coastguard Worker }
204*c8dee2aaSAndroid Build Coastguard Worker
parse(const char doc[],size_t len)205*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::parse(const char doc[], size_t len)
206*c8dee2aaSAndroid Build Coastguard Worker {
207*c8dee2aaSAndroid Build Coastguard Worker SkMemoryStream docStream(doc, len);
208*c8dee2aaSAndroid Build Coastguard Worker return this->parse(docStream);
209*c8dee2aaSAndroid Build Coastguard Worker }
210*c8dee2aaSAndroid Build Coastguard Worker
GetNativeErrorString(int error,SkString * str)211*c8dee2aaSAndroid Build Coastguard Worker void SkXMLParser::GetNativeErrorString(int error, SkString* str)
212*c8dee2aaSAndroid Build Coastguard Worker {
213*c8dee2aaSAndroid Build Coastguard Worker
214*c8dee2aaSAndroid Build Coastguard Worker }
215*c8dee2aaSAndroid Build Coastguard Worker
startElement(const char elem[])216*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::startElement(const char elem[])
217*c8dee2aaSAndroid Build Coastguard Worker {
218*c8dee2aaSAndroid Build Coastguard Worker return this->onStartElement(elem);
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker
addAttribute(const char name[],const char value[])221*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::addAttribute(const char name[], const char value[])
222*c8dee2aaSAndroid Build Coastguard Worker {
223*c8dee2aaSAndroid Build Coastguard Worker return this->onAddAttribute(name, value);
224*c8dee2aaSAndroid Build Coastguard Worker }
225*c8dee2aaSAndroid Build Coastguard Worker
endElement(const char elem[])226*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::endElement(const char elem[])
227*c8dee2aaSAndroid Build Coastguard Worker {
228*c8dee2aaSAndroid Build Coastguard Worker return this->onEndElement(elem);
229*c8dee2aaSAndroid Build Coastguard Worker }
230*c8dee2aaSAndroid Build Coastguard Worker
text(const char text[],int len)231*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::text(const char text[], int len)
232*c8dee2aaSAndroid Build Coastguard Worker {
233*c8dee2aaSAndroid Build Coastguard Worker return this->onText(text, len);
234*c8dee2aaSAndroid Build Coastguard Worker }
235*c8dee2aaSAndroid Build Coastguard Worker
236*c8dee2aaSAndroid Build Coastguard Worker ////////////////////////////////////////////////////////////////////////////////
237*c8dee2aaSAndroid Build Coastguard Worker
onStartElement(const char elem[])238*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onStartElement(const char elem[]) {return false; }
onAddAttribute(const char name[],const char value[])239*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onAddAttribute(const char name[], const char value[]) {return false; }
onEndElement(const char elem[])240*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onEndElement(const char elem[]) { return false; }
onText(const char text[],int len)241*c8dee2aaSAndroid Build Coastguard Worker bool SkXMLParser::onText(const char text[], int len) {return false; }
242