Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qdomhelpers.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:data-parser
4
5#include <QtXml/qtxmlglobal.h>
6
7#if QT_CONFIG(dom)
8
9#include "qdomhelpers_p.h"
10#include "qdom_p.h"
11
12#include <QtCore/qshareddata.h>
13#include "qxmlstream.h"
14#include "private/qxmlstream_p.h"
15
16QT_BEGIN_NAMESPACE
17
18using namespace Qt::StringLiterals;
19
20/**************************************************************
21 *
22 * QDomBuilder
23 *
24 **************************************************************/
25
26QDomBuilder::QDomBuilder(QDomDocumentPrivate *d, QXmlStreamReader *r,
27 QDomDocument::ParseOptions options)
28 : doc(d), node(d), reader(r), parseOptions(options)
29{
30 Q_ASSERT(doc);
31 Q_ASSERT(reader);
32}
33
34QDomBuilder::~QDomBuilder() {}
35
36bool QDomBuilder::endDocument()
37{
38 // ### is this really necessary? (rms)
39 if (node != doc)
40 return false;
41 return true;
42}
43
44void QDomBuilder::startDTD(QStringView name, QStringView publicId, QStringView systemId)
45{
46 QDomDocumentTypePrivate * const dt = doc->doctype();
47 Q_ASSERT(dt);
48 dt->name = name.toString();
49 dt->publicId = publicId.toString();
50 dt->systemId = systemId.toString();
51}
52
53QString QDomBuilder::dtdInternalSubset(const QString &dtd)
54{
55 // https://www.w3.org/TR/xml/#NT-intSubset
56 // doctypedecl: '<!DOCTYPE' S Name (S ExternalID)? S? ('[' intSubset ']' S?)? '>'
57 const QString &name = doc->doctype()->name;
58 QStringView tmp = QStringView(dtd).sliced(dtd.indexOf(name) + name.size());
59
60 const QString &publicId = doc->doctype()->publicId;
61 if (!publicId.isEmpty())
62 tmp = tmp.sliced(tmp.indexOf(publicId) + publicId.size());
63
64 const QString &systemId = doc->doctype()->systemId;
65 if (!systemId.isEmpty())
66 tmp = tmp.sliced(tmp.indexOf(systemId) + systemId.size());
67
68 const qsizetype obra = tmp.indexOf(u'[');
69 const qsizetype cbra = tmp.lastIndexOf(u']');
70 if (obra >= 0 && cbra >= 0)
71 return tmp.left(cbra).sliced(obra + 1).toString();
72
73 return QString();
74}
75
76bool QDomBuilder::parseDTD(const QString &dtd)
77{
78 doc->doctype()->internalSubset = dtdInternalSubset(dtd);
79 return true;
80}
81
82bool QDomBuilder::startElement(const QString &nsURI, const QString &qName,
83 const QXmlStreamAttributes &atts)
84{
85 const bool nsProcessing =
86 parseOptions.testFlag(QDomDocument::ParseOption::UseNamespaceProcessing);
87 QDomNodePrivate *n =
88 nsProcessing ? doc->createElementNS(nsURI, qName) : doc->createElement(qName);
89 if (!n)
90 return false;
91
92 n->setLocation(int(reader->lineNumber()), int(reader->columnNumber()));
93
94 node->appendChild(n);
95 node = n;
96
97 // attributes
98 for (const auto &attr : atts) {
99 auto domElement = static_cast<QDomElementPrivate *>(node);
100 if (nsProcessing) {
101 domElement->setAttributeNS(attr.namespaceUri().toString(),
102 attr.qualifiedName().toString(),
103 attr.value().toString());
104 } else {
105 domElement->setAttribute(attr.qualifiedName().toString(),
106 attr.value().toString());
107 }
108 }
109
110 return true;
111}
112
113bool QDomBuilder::endElement()
114{
115 if (!node || node == doc)
116 return false;
117 node = node->parent();
118
119 return true;
120}
121
122bool QDomBuilder::characters(const QString &characters, bool cdata)
123{
124 // No text as child of some document
125 if (node == doc)
126 return false;
127
128 QExplicitlySharedDataPointer<QDomNodePrivate> n;
129 if (cdata) {
130 n.reset(doc->createCDATASection(characters));
131 } else {
132 n.reset(doc->createTextNode(characters));
133 }
134 if (!n)
135 return false;
136 n->setLocation(int(reader->lineNumber()), int(reader->columnNumber()));
137 node->appendChild(n.get());
138
139 return true;
140}
141
142bool QDomBuilder::processingInstruction(const QString &target, const QString &data)
143{
144 QDomNodePrivate *n;
145 n = doc->createProcessingInstruction(target, data);
146 if (n) {
147 n->setLocation(int(reader->lineNumber()), int(reader->columnNumber()));
148 node->appendChild(n);
149 return true;
150 } else
151 return false;
152}
153
154bool QDomBuilder::skippedEntity(const QString &name)
155{
156 QDomNodePrivate *n = doc->createEntityReference(name);
157 if (!n)
158 return false;
159 n->setLocation(int(reader->lineNumber()), int(reader->columnNumber()));
160 node->appendChild(n);
161 return true;
162}
163
164void QDomBuilder::fatalError(const QString &message)
165{
166 parseResult.errorMessage = message;
167 parseResult.errorLine = reader->lineNumber();
168 parseResult.errorColumn = reader->columnNumber();
169}
170
171bool QDomBuilder::comment(const QString &characters)
172{
173 QDomNodePrivate *n;
174 n = doc->createComment(characters);
175 if (!n)
176 return false;
177 n->setLocation(int(reader->lineNumber()), int(reader->columnNumber()));
178 node->appendChild(n);
179 return true;
180}
181
182bool QDomBuilder::unparsedEntityDecl(const QString &name, const QString &publicId,
183 const QString &systemId, const QString &notationName)
184{
185 QDomEntityPrivate *e =
186 new QDomEntityPrivate(doc, nullptr, name, publicId, systemId, notationName);
187 // keep the refcount balanced: appendChild() does a ref anyway.
188 e->ref.deref();
189 doc->doctype()->appendChild(e);
190 return true;
191}
192
193bool QDomBuilder::externalEntityDecl(const QString &name, const QString &publicId,
194 const QString &systemId)
195{
196 return unparsedEntityDecl(name, publicId, systemId, QString());
197}
198
199bool QDomBuilder::notationDecl(const QString &name, const QString &publicId,
200 const QString &systemId)
201{
202 QDomNotationPrivate *n = new QDomNotationPrivate(doc, nullptr, name, publicId, systemId);
203 // keep the refcount balanced: appendChild() does a ref anyway.
204 n->ref.deref();
205 doc->doctype()->appendChild(n);
206 return true;
207}
208
209/**************************************************************
210 *
211 * QDomParser
212 *
213 **************************************************************/
214
215QDomParser::QDomParser(QDomDocumentPrivate *d, QXmlStreamReader *r,
216 QDomDocument::ParseOptions options)
217 : reader(r), domBuilder(d, r, options)
218{
219}
220
221bool QDomParser::parse()
222{
223 return parseProlog() && parseBody();
224}
225
226bool QDomParser::parseProlog()
227{
228 Q_ASSERT(reader);
229
230 while (!reader->atEnd()) {
231 reader->readNext();
232
233 if (reader->hasError()) {
234 domBuilder.fatalError(reader->errorString());
235 return false;
236 }
237
238 switch (reader->tokenType()) {
239 case QXmlStreamReader::StartDocument:
240 if (!reader->documentVersion().isEmpty()) {
241 QString value(u"version='"_s);
242 value += reader->documentVersion();
243 value += u'\'';
244 if (!reader->documentEncoding().isEmpty()) {
245 value += u" encoding='"_s;
246 value += reader->documentEncoding();
247 value += u'\'';
248 }
249 if (reader->isStandaloneDocument()) {
250 value += u" standalone='yes'"_s;
251 } else {
252 // Add the standalone attribute only if it was specified
253 if (reader->hasStandaloneDeclaration())
254 value += u" standalone='no'"_s;
255 }
256
257 if (!domBuilder.processingInstruction(u"xml"_s, value)) {
258 domBuilder.fatalError(
259 QDomParser::tr("Error occurred while processing XML declaration"));
260 return false;
261 }
262 }
263 break;
264 case QXmlStreamReader::DTD:
265 domBuilder.startDTD(reader->dtdName(),
266 reader->dtdPublicId(),
267 reader->dtdSystemId());
268 if constexpr (false) {
269 // ### kept for the translated string
270 domBuilder.fatalError(
271 QDomParser::tr("Error occurred while processing document type declaration"));
272 }
273 if (!domBuilder.parseDTD(reader->text().toString()))
274 return false;
275 if (!parseMarkupDecl())
276 return false;
277 break;
278 case QXmlStreamReader::Comment:
279 if (!domBuilder.comment(reader->text().toString())) {
280 domBuilder.fatalError(QDomParser::tr("Error occurred while processing comment"));
281 return false;
282 }
283 break;
284 case QXmlStreamReader::ProcessingInstruction:
285 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
286 reader->processingInstructionData().toString())) {
287 domBuilder.fatalError(
288 QDomParser::tr("Error occurred while processing a processing instruction"));
289 return false;
290 }
291 break;
292 default:
293 // If the token is none of the above, prolog processing is done.
294 return true;
295 }
296 }
297
298 return true;
299}
300
301bool QDomParser::parseBody()
302{
303 Q_ASSERT(reader);
304
305 while (!reader->atEnd() && !reader->hasError()) {
306 switch (reader->tokenType()) {
307 case QXmlStreamReader::StartElement:
308 if (!domBuilder.startElement(reader->namespaceUri().toString(),
309 reader->qualifiedName().toString(),
310 reader->attributes())) {
311 domBuilder.fatalError(
312 QDomParser::tr("Error occurred while processing a start element"));
313 return false;
314 }
315 break;
316 case QXmlStreamReader::EndElement:
317 if (!domBuilder.endElement()) {
318 domBuilder.fatalError(
319 QDomParser::tr("Error occurred while processing an end element"));
320 return false;
321 }
322 break;
323 case QXmlStreamReader::Characters:
324 // Skip the content if it contains only spacing characters,
325 // unless it's CDATA or PreserveSpacingOnlyNodes was specified.
326 if (reader->isCDATA() || domBuilder.preserveSpacingOnlyNodes()
327 || !(reader->isWhitespace() || reader->text().trimmed().isEmpty())) {
328 if (!domBuilder.characters(reader->text().toString(), reader->isCDATA())) {
329 domBuilder.fatalError(
330 QDomParser::tr("Error occurred while processing the element content"));
331 return false;
332 }
333 }
334 break;
335 case QXmlStreamReader::Comment:
336 if (!domBuilder.comment(reader->text().toString())) {
337 domBuilder.fatalError(QDomParser::tr("Error occurred while processing comments"));
338 return false;
339 }
340 break;
341 case QXmlStreamReader::ProcessingInstruction:
342 if (!domBuilder.processingInstruction(reader->processingInstructionTarget().toString(),
343 reader->processingInstructionData().toString())) {
344 domBuilder.fatalError(
345 QDomParser::tr("Error occurred while processing a processing instruction"));
346 return false;
347 }
348 break;
349 case QXmlStreamReader::EntityReference:
350 if (!domBuilder.skippedEntity(reader->name().toString())) {
351 domBuilder.fatalError(
352 QDomParser::tr("Error occurred while processing an entity reference"));
353 return false;
354 }
355 break;
356 default:
357 domBuilder.fatalError(QDomParser::tr("Unexpected token"));
358 return false;
359 }
360
361 reader->readNext();
362 }
363
364 if (reader->hasError()) {
365 domBuilder.fatalError(reader->errorString());
366 reader->readNext();
367 return false;
368 }
369
370 return true;
371}
372
373bool QDomParser::parseMarkupDecl()
374{
375 Q_ASSERT(reader);
376
377 const auto entities = reader->entityDeclarations();
378 for (const auto &entityDecl : entities) {
379 // Entity declarations are created only for External Entities. Internal Entities
380 // are parsed, and QXmlStreamReader handles the parsing itself and returns the
381 // parsed result. So we don't need to do anything for the Internal Entities.
382 if (!entityDecl.publicId().isEmpty() || !entityDecl.systemId().isEmpty()) {
383 // External Entity
384 if (!domBuilder.unparsedEntityDecl(entityDecl.name().toString(),
385 entityDecl.publicId().toString(),
386 entityDecl.systemId().toString(),
387 entityDecl.notationName().toString())) {
388 domBuilder.fatalError(
389 QDomParser::tr("Error occurred while processing entity declaration"));
390 return false;
391 }
392 }
393 }
394
395 const auto notations = reader->notationDeclarations();
396 for (const auto &notationDecl : notations) {
397 if (!domBuilder.notationDecl(notationDecl.name().toString(),
398 notationDecl.publicId().toString(),
399 notationDecl.systemId().toString())) {
400 domBuilder.fatalError(
401 QDomParser::tr("Error occurred while processing notation declaration"));
402 return false;
403 }
404 }
405
406 return true;
407}
408
409QT_END_NAMESPACE
410
411#endif // feature dom