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