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
qxmlstream_p.h
Go to the documentation of this file.
1// Copyright (C) 2016 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//
6// W A R N I N G
7// -------------
8//
9// This file is not part of the Qt API. It exists for the convenience
10// of other Qt classes. This header file may change from version to
11// version without notice, or even be removed.
12//
13// We mean it.
14//
15
16#include <QtCore/private/qglobal_p.h>
17#include <qstringconverter.h>
18#include <qxmlstream.h>
20#include <QtCore/qhash.h>
21#include <QCoreApplication> // Q_DECLARE_TR_FUNCTIONS
22
23
24#include <memory>
25#include <optional>
26
27#ifndef QXMLSTREAM_P_H
28#define QXMLSTREAM_P_H
29
30QT_BEGIN_NAMESPACE
31
32namespace QtPrivate {
33
35{
36public:
37 const QString *m_string = nullptr;
40
41 constexpr XmlStringRef() = default;
42 constexpr inline XmlStringRef(const QString *string, qsizetype pos, qsizetype length)
44 {
45 }
46 XmlStringRef(const QString *string)
47 : XmlStringRef(string, 0, string->size())
48 {
49 }
50
52 if (!m_string)
53 return QXmlString();
55 d.setBegin(d.data() + m_pos);
56 d.size = m_size;
57 return QXmlString(std::move(d));
58 }
59
60 void clear() { m_string = nullptr; m_pos = 0; m_size= 0; }
62 bool isEmpty() const { return m_size == 0; }
63 bool isNull() const { return !m_string; }
64 QString toString() const { return view().toString(); }
65
67 {
68 verify(pos, 0);
70 }
72 {
73 verify(pos, n);
74 return XmlStringRef(m_string, m_pos + pos, n);
75 }
76
88
89#define MAKE_MEMBER(name)
90 auto name () const noexcept { return view(). name (); }
91 MAKE_MEMBER(data)
92 MAKE_MEMBER(size)
93 MAKE_MEMBER(empty)
94 MAKE_MEMBER(begin)
95 MAKE_MEMBER(end)
96 MAKE_MEMBER(cbegin)
97 MAKE_MEMBER(cend)
98 MAKE_MEMBER(rbegin)
99 MAKE_MEMBER(rend)
100 MAKE_MEMBER(crbegin)
101 MAKE_MEMBER(crend)
102#undef MAKE_MEMBER
103
104#define MAKE_OP(op)
105 friend auto operator op(const XmlStringRef &lhs, const XmlStringRef &rhs) noexcept { return lhs.view() op rhs.view(); }
106 /*end*/
107 MAKE_OP(==)
108 MAKE_OP(!=)
109 MAKE_OP(<=)
110 MAKE_OP(>=)
111 MAKE_OP(<)
112 MAKE_OP(>)
113#undef MAKE_OP
114#define MAKE_OP(op)
115 friend auto operator op(const XmlStringRef &lhs, QStringView rhs) noexcept { return lhs.view() op rhs; }
116 friend auto operator op(QStringView lhs, const XmlStringRef &rhs) noexcept { return lhs op rhs.view(); }
117 /*end*/
118 MAKE_OP(==)
119 MAKE_OP(!=)
120 MAKE_OP(<=)
121 MAKE_OP(>=)
122 MAKE_OP(<)
123 MAKE_OP(>)
124#undef MAKE_OP
125
126private:
127 Q_ALWAYS_INLINE
128 void verify([[maybe_unused]] qsizetype pos = 0,
129 [[maybe_unused]] qsizetype n = 1) const
130 {
131 Q_PRE(pos >= 0);
132 Q_PRE(pos <= size());
133 Q_PRE(n >= 0);
134 Q_PRE(n <= size() - pos);
135 }
136};
137
138}
139
140using namespace QtPrivate;
141
142template <typename T> class QXmlStreamSimpleStack
143{
145
146 T *data;
147 qsizetype tos, cap;
148public:
150 : data(nullptr), tos(-1), cap(0)
151 {}
153 {
154 if (data) {
155 std::destroy_n(data, size());
156 free(data);
157 }
158 }
159
160 inline void reserve(qsizetype extraCapacity)
161 {
162 if (tos + extraCapacity + 1 > cap) {
163 cap = qMax(tos + extraCapacity + 1, cap << 1 );
164 void *ptr = realloc(static_cast<void *>(data), cap * sizeof(T));
165 Q_CHECK_PTR(ptr);
166 data = reinterpret_cast<T *>(ptr);
167 }
168 }
169
170 inline T &push() { reserve(1); return rawPush(); }
171 inline T &rawPush() { return *new (data + (++tos)) T; }
172 inline const T &top() const { return data[tos]; }
173 inline T &top() { return data[tos]; }
174 inline T pop() { T t = std::move(data[tos]); std::destroy_at(data + tos); --tos; return t; }
175 inline T &operator[](qsizetype index) { return data[index]; }
176 inline const T &at(qsizetype index) const { return data[index]; }
177 inline qsizetype size() const { return tos + 1; }
178 inline void resize(qsizetype s) { tos = s - 1; }
179 inline bool isEmpty() const { return tos < 0; }
180 inline void clear() { tos = -1; }
181
182 using const_iterator = const T*;
183 using iterator = T*;
184 T *begin() { return data; }
185 const T *begin() const { return data; }
186 const T *cbegin() const { return begin(); }
187 T *end() { return data + size(); }
188 const T *end() const { return data + size(); }
189 const T *cend() const { return end(); }
190};
191
196
199public:
205
214
215
222
224 {
225 qsizetype pos = tagStackStringStorageSize;
226 if (pos != tagStackStringStorage.size())
227 tagStackStringStorage.resize(pos);
228 s.visit([&](auto s) { tagStackStringStorage.append(s); });
229 qsizetype sz = (tagStackStringStorage.size() - pos);
230 tagStackStringStorageSize += sz;
231 return XmlStringRef(&tagStackStringStorage, pos, sz);
232 }
233
235
236
237 inline Tag tagStack_pop() {
238 Tag tag = tagStack.pop();
239 tagStackStringStorageSize = tag.tagStackStringStorageSize;
240 namespaceDeclarations.resize(tag.namespaceDeclarationsSize);
241 tagsDone = tagStack.isEmpty();
242 return tag;
243 }
244 inline Tag &tagStack_push() {
245 Tag &tag = tagStack.push();
246 tag.tagStackStringStorageSize = tagStackStringStorageSize;
247 tag.namespaceDeclarationsSize = namespaceDeclarations.size();
248 return tag;
249 }
250};
251
252#if QT_CONFIG(xmlstreamreader)
253class QXmlStreamEntityResolver;
254class QXmlStreamReaderPrivate : public QXmlStreamGrammar, public QXmlStreamPrivateTagStack
255{
256 QXmlStreamReader *q_ptr;
257 Q_DECLARE_PUBLIC(QXmlStreamReader)
258public:
259 QXmlStreamReaderPrivate(QXmlStreamReader *q);
260 ~QXmlStreamReaderPrivate();
261 void init();
262
263 struct BufferAndEncoding
264 {
265 QByteArray buffer;
266 // System means that we didn't specify the encoding
267 QStringDecoder::Encoding encoding;
268
269 BufferAndEncoding()
270 : buffer(), encoding(QStringDecoder::System)
271 {}
272 BufferAndEncoding(const QByteArray &b, QStringDecoder::Encoding e)
273 : buffer(b), encoding(e)
274 {}
275 };
276 QList<BufferAndEncoding> dataInfo;
277 QStringDecoder chunkDecoder;
278 void appendDataWithEncoding(const QByteArray &data, QStringDecoder::Encoding enc);
279 void addData(const QByteArray &data, QStringDecoder::Encoding enc);
280
281 QByteArray rawReadBuffer;
282 uchar firstByte;
283 qint64 nbytesread;
284 QString readBuffer;
285 qsizetype readBufferPos;
286 QXmlStreamSimpleStack<uint> putStack;
287 struct Entity {
288 Entity() = default;
289 Entity(const QString &name, const QString &value)
290 : name(name), value(value), external(false), unparsed(false), literal(false),
291 hasBeenParsed(false), isCurrentlyReferenced(false){}
292 static inline Entity createLiteral(QLatin1StringView name, QLatin1StringView value)
293 { Entity result(name, value); result.literal = result.hasBeenParsed = true; return result; }
294 QString name, value;
295 uint external : 1;
296 uint unparsed : 1;
297 uint literal : 1;
298 uint hasBeenParsed : 1;
299 uint isCurrentlyReferenced : 1;
300 };
301 // these hash tables use a QStringView as a key to avoid creating QStrings
302 // just for lookup. The keys are usually views into Entity::name and thus
303 // are guaranteed to have the same lifetime as the referenced data:
304 QHash<QStringView, Entity> entityHash;
305 QHash<QStringView, Entity> parameterEntityHash;
306 struct QEntityReference
307 {
308 QHash<QStringView, Entity> *hash;
309 QStringView name;
310 };
311 QXmlStreamSimpleStack<QEntityReference> entityReferenceStack;
312 int entityExpansionLimit = 4096;
313 int entityLength = 0;
314 inline bool referenceEntity(QHash<QStringView, Entity> *hash, Entity &entity)
315 {
316 Q_ASSERT(hash);
317 if (entity.isCurrentlyReferenced) {
318 raiseWellFormedError(QXmlStream::tr("Self-referencing entity detected."));
319 return false;
320 }
321 // entityLength represents the amount of additional characters the
322 // entity expands into (can be negative for e.g. &amp;). It's used to
323 // avoid DoS attacks through recursive entity expansions
324 entityLength += entity.value.size() - entity.name.size() - 2;
325 if (entityLength > entityExpansionLimit) {
326 raiseWellFormedError(QXmlStream::tr("Entity expands to more characters than the entity expansion limit."));
327 return false;
328 }
329 entity.isCurrentlyReferenced = true;
330 entityReferenceStack.push() = { hash, entity.name };
331 injectToken(ENTITY_DONE);
332 return true;
333 }
334
335
336 QIODevice *device;
337 bool deleteDevice;
338 QStringDecoder decoder;
339 bool atEnd;
340
341 enum class XmlContext
342 {
343 Prolog,
344 Body,
345 };
346
347 XmlContext currentContext = XmlContext::Prolog;
348 bool foundDTD = false;
349 bool isValidToken(QXmlStreamReader::TokenType type);
350 void checkToken();
351
352 /*!
353 \sa setType()
354 */
355 QXmlStreamReader::TokenType type;
356 QXmlStreamReader::Error error;
357 QString errorString;
358 QString unresolvedEntity;
359
360 qint64 lineNumber, lastLineStart, characterOffset;
361
362
363 void write(const QString &);
364 void write(const char *);
365
366
367 QXmlStreamAttributes attributes;
368 XmlStringRef namespaceForPrefix(QStringView prefix);
369 void resolveTag();
370 void resolvePublicNamespaces();
371 void resolveDtd();
372 uint resolveCharRef(int symbolIndex);
373 bool checkStartDocument();
374 void startDocument();
375 void parseError();
376 void checkPublicLiteral(QStringView publicId);
377
378 bool scanDtd;
379 XmlStringRef lastAttributeValue;
380 bool lastAttributeIsCData;
381 struct DtdAttribute {
382 XmlStringRef tagName;
383 XmlStringRef attributeQualifiedName;
384 XmlStringRef attributePrefix;
385 XmlStringRef attributeName;
386 XmlStringRef defaultValue;
387 bool isCDATA;
388 bool isNamespaceAttribute;
389 };
390 QXmlStreamSimpleStack<DtdAttribute> dtdAttributes;
391 struct NotationDeclaration {
392 XmlStringRef name;
393 XmlStringRef publicId;
394 XmlStringRef systemId;
395 };
396 QXmlStreamSimpleStack<NotationDeclaration> notationDeclarations;
397 QXmlStreamNotationDeclarations publicNotationDeclarations;
398 QXmlStreamNamespaceDeclarations publicNamespaceDeclarations;
399
400 struct EntityDeclaration {
401 XmlStringRef name;
402 XmlStringRef notationName;
403 XmlStringRef publicId;
404 XmlStringRef systemId;
405 XmlStringRef value;
406 bool parameter;
407 bool external;
408 inline void clear() {
409 name.clear();
410 notationName.clear();
411 publicId.clear();
412 systemId.clear();
413 value.clear();
414 parameter = external = false;
415 }
416 };
417 QXmlStreamSimpleStack<EntityDeclaration> entityDeclarations;
418 QXmlStreamEntityDeclarations publicEntityDeclarations;
419
420 XmlStringRef text;
421
422 XmlStringRef prefix, namespaceUri, qualifiedName, name;
423 XmlStringRef processingInstructionTarget, processingInstructionData;
424 XmlStringRef dtdName, dtdPublicId, dtdSystemId;
425 XmlStringRef documentVersion, documentEncoding;
426 uint isEmptyElement : 1;
427 uint isWhitespace : 1;
428 uint isCDATA : 1;
429 uint standalone : 1;
430 uint hasCheckedStartDocument : 1;
431 uint normalizeLiterals : 1;
432 uint hasSeenTag : 1;
433 uint inParseEntity : 1;
434 uint referenceToUnparsedEntityDetected : 1;
435 uint referenceToParameterEntityDetected : 1;
436 uint hasExternalDtdSubset : 1;
437 uint lockEncoding : 1;
438 uint namespaceProcessing : 1;
439 uint hasStandalone : 1; // TODO: expose in public API
440
441 int resumeReduction;
442 void resume(int rule);
443
444 inline bool entitiesMustBeDeclared() const {
445 return (!inParseEntity
446 && (standalone
447 || (!referenceToUnparsedEntityDetected
448 && !referenceToParameterEntityDetected // Errata 13 as of 2006-04-25
449 && !hasExternalDtdSubset)));
450 }
451
452 // qlalr parser
453 int tos;
454 int stack_size;
455 struct Value {
456 qsizetype pos; // offset into textBuffer
457 qsizetype len; // length incl. prefix (if any)
458 qint16 prefix; // prefix of a name (as in "prefix:name") limited to 4k in fastScanName()
459 ushort c;
460 };
461
462 Value *sym_stack;
463 int *state_stack;
464 inline void reallocateStack();
465 inline Value &sym(int index) const
466 { return sym_stack[tos + index - 1]; }
467 QString textBuffer;
468 inline void clearTextBuffer() {
469 if (!scanDtd) {
470 textBuffer.resize(0);
471 textBuffer.reserve(256);
472 }
473 }
474 struct Attribute {
475 Value key;
476 Value value;
477 };
478 QXmlStreamSimpleStack<Attribute> attributeStack;
479
480 inline XmlStringRef symString(int index) {
481 return symString(sym(index));
482 }
483 QStringView symView(int index) const
484 {
485 const Value &symbol = sym(index);
486 return QStringView(textBuffer.data() + symbol.pos, symbol.len).mid(symbol.prefix);
487 }
488 inline XmlStringRef symName(int index) {
489 return symName(sym(index));
490 }
491 inline XmlStringRef symPrefix(int index) {
492 return symPrefix(sym(index));
493 }
494 auto symQName(int index) {
495 struct R {
496 XmlStringRef m_data;
497 qsizetype m_prefix;
498
499 XmlStringRef all() const { return m_data; }
500 XmlStringRef qname() const { return m_data; }
501 XmlStringRef prefix() const
502 { return m_prefix ? m_data.sliced(0, m_prefix - 1) : XmlStringRef(); }
503 XmlStringRef localPart() const { return m_data.sliced(m_prefix); }
504
505 void resetBackingStorage(XmlStringRef newData) { m_data = newData; } // m_prefix stays!
506 };
507 const Value &symbol = sym(index);
508 return R{{&textBuffer, symbol.pos, symbol.len}, symbol.prefix};
509 }
510 inline XmlStringRef symString(const Value &symbol) {
511 return XmlStringRef(&textBuffer, symbol.pos + symbol.prefix, symbol.len - symbol.prefix);
512 }
513 inline XmlStringRef symName(const Value &symbol) {
514 return XmlStringRef(&textBuffer, symbol.pos, symbol.len);
515 }
516 inline XmlStringRef symPrefix(const Value &symbol) {
517 if (symbol.prefix)
518 return XmlStringRef(&textBuffer, symbol.pos, symbol.prefix - 1);
519 return XmlStringRef();
520 }
521
522 inline void clearSym() { Value &val = sym(1); val.pos = textBuffer.size(); val.len = 0; }
523
524
525 short token;
526 uint token_char;
527
528 uint filterCarriageReturn();
529 inline uint getChar();
530 inline uint peekChar();
531 inline void putChar(uint c) { putStack.push() = c; }
532 inline void putChar(QChar c) { putStack.push() = c.unicode(); }
533 void putString(QStringView s, qsizetype from = 0);
534 void putStringLiteral(QStringView s);
535 void putReplacement(QStringView s);
536 void putReplacementInAttributeValue(QStringView s);
537 uint getChar_helper();
538
539 bool scanUntil(const char *str, short tokenToInject = -1);
540 bool scanString(const char *str, short tokenToInject, bool requireSpace = true);
541 inline void injectToken(ushort tokenToInject) {
542 putChar(int(tokenToInject) << 16);
543 }
544
545 QString resolveUndeclaredEntity(const QString &name);
546 void parseEntity(const QString &value);
547 std::unique_ptr<QXmlStreamReaderPrivate> entityParser;
548
549 bool scanAfterLangleBang();
550 bool scanPublicOrSystem();
551 bool scanNData();
552 bool scanAfterDefaultDecl();
553 bool scanAttType();
554
555
556 // scan optimization functions. Not strictly necessary but LALR is
557 // not very well suited for scanning fast
558 qsizetype fastScanLiteralContent();
559 qsizetype fastScanSpace();
560 qsizetype fastScanContentCharList();
561 std::optional<qsizetype> fastScanName(Value *val = nullptr);
562 inline qsizetype fastScanNMTOKEN();
563
564
565 bool parse();
566 inline void consumeRule(int);
567
568 void raiseError(QXmlStreamReader::Error error, const QString& message = QString());
569 void raiseWellFormedError(const QString &message);
570 void raiseNamePrefixTooLongError();
571
572 QXmlStreamEntityResolver *entityResolver;
573
574private:
575 /*! \internal
576 Never assign to variable type directly. Instead use this function.
577
578 This prevents errors from being ignored.
579 */
580 inline void setType(const QXmlStreamReader::TokenType t)
581 {
582 if (type != QXmlStreamReader::Invalid)
583 type = t;
584 }
585};
586#endif // feature xmlstreamreader
587
588QT_END_NAMESPACE
589
590#endif // QXMLSTREAM_P_H
qsizetype initialTagStackStringStorageSize
XmlStringRef addToStringStorage(QAnyStringView s)
QXmlStreamSimpleStack< NamespaceDeclaration > namespaceDeclarations
QXmlStreamSimpleStack< Tag > tagStack
void resize(qsizetype s)
const T * cbegin() const
const T & top() const
void reserve(qsizetype extraCapacity)
const T * cend() const
const T * begin() const
const T & at(qsizetype index) const
T & operator[](qsizetype index)
qsizetype size() const
const T * end() const
XmlStringRef(const QString *string)
constexpr XmlStringRef()=default
const QString * m_string
constexpr XmlStringRef(const QString *string, qsizetype pos, qsizetype length)
#define MAKE_MEMBER(name)
#define MAKE_OP(op)
NamespaceDeclaration namespaceDeclaration