Revision control

++ // Copyright (C) 2021 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant
++
++ #include "qtypedjson_p.h"
++ #include <QtCore/QLoggingCategory>
++ #include <QtCore/qjsondocument.h>
++ #include <QtCore/qjsonvalue.h>
++ #include <QtCore/qjsonarray.h>
++ #include <cstring>
++
++ QT_BEGIN_NAMESPACE
++
++ namespace QTypedJson {
++
++ Q_LOGGING_CATEGORY(jsonRpcLog, "qt.jsonrpc");
++
++ Reader::Reader(const QJsonValue &v)
++ : m_p(new ReaderPrivate { QList({ ValueStack { v, QString(), -1, 0 } }) })
++ {
++ }
++
++ Reader::~Reader()
++ {
++ for (const QString &msg : std::as_const(m_p->errorMessages))
++ qCWarning(jsonRpcLog) << msg;
++ delete m_p;
++ }
++
++ QStringList Reader::errorMessages()
++ {
++ return m_p->errorMessages;
++ }
++
++ void Reader::clearErrorMessages()
++ {
++ m_p->errorMessages.clear();
++ }
++
++ void Reader::handleBasic(bool &el)
++ {
++ if (currentValue().isBool())
++ el = currentValue().toBool();
++ else
++ warnMissing(u"bool");
++ }
++
++ void Reader::handleBasic(QByteArray &el)
++ {
++ if (currentValue().isString())
++ el = currentValue().toString().toUtf8();
++ else
++ warnMissing(u"string");
++ }
++
++ void Reader::handleConstString(QByteArrayView el)
++ {
++ if (!currentValue().isString() || el != currentValue().toString().toUtf8())
++ warnMissing(u"string");
++ }
++
++ void Reader::handleBasic(int &el)
++ {
++ if (currentValue().isDouble())
++ el = currentValue().toInt(el);
++ else
++ warnMissing(u"int");
++ }
++
++ void Reader::handleBasic(unsigned int &el)
++ {
++ if (currentValue().isDouble()) {
++ double currentInt = currentValue().toDouble(el);
++ if (currentInt < 0 || currentInt > std::numeric_limits<uint>::max()) {
++ warn(QStringLiteral("Value %1 does not fit the unsigned int range of [0, %2].")
++ .arg(QString::number(currentInt),
++ QString::number(std::numeric_limits<uint>::max())));
++ return;
++ }
++ el = static_cast<unsigned int>(currentInt);
++ } else {
++ warnMissing(u"unsigned int");
++ }
++ }
++
++ void Reader::handleBasic(double &el)
++ {
++ if (currentValue().isDouble())
++ el = currentValue().toDouble();
++ else
++ warnMissing(u"double");
++ }
++
++ void Reader::handleNullType()
++ {
++ if (!currentValue().isNull() && !currentValue().isUndefined()) {
++ warnNonNull();
++ }
++ }
++
++ bool Reader::startField(const QString &fieldName)
++ {
++ int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel);
++ m_p->objectsStack.last().visitedFields.insert(fieldName);
++ m_p->valuesStack.append(ValueStack { currentValue()[fieldName], fieldName, -1,
++ (oldWarnLevel ? oldWarnLevel + 1 : 0) });
++ return true;
++ }
++
++ bool Reader::startField(const char *fieldName)
++ {
++ QString f = QString::fromUtf8(fieldName); // conversion needed just to set the fieldPath
++ return startField(f);
++ }
++
++ bool Reader::startField(const QByteArray &fieldName)
++ {
++ return startField(fieldName.data());
++ }
++
++ void Reader::endField(const QString &fieldName)
++ {
++ Q_ASSERT(m_p->valuesStack.last().fieldPath == fieldName);
++ m_p->valuesStack.removeLast();
++ }
++
++ void Reader::endField(const char *fieldName)
++ {
++ QString f = QString::fromUtf8(fieldName);
++ endField(f);
++ }
++
++ void Reader::endField(const QByteArray &fieldName)
++ {
++ endField(fieldName.data());
++ }
++
++ bool Reader::startObjectF(const char *type, ObjectOptions options, quintptr)
++ {
++ if (m_p->parseStatus != ParseStatus::Normal)
++ return false;
++ if (currentValue().isUndefined()) {
++ m_p->parseStatus = ParseStatus::Failed;
++ return false;
++ }
++ m_p->objectsStack.append(ObjectStack { type, options, {} });
++ return true;
++ }
++
++ void Reader::endObjectF(const char *type, ObjectOptions, quintptr)
++ {
++ Q_ASSERT(std::strcmp(m_p->objectsStack.last().type, type) == 0);
++ m_p->objectsStack.removeLast();
++ }
++
++ void Reader::warnExtra(const QJsonObject &e)
++ {
++ if (e.constBegin() != e.constEnd())
++ warn(QStringLiteral(u"%1 has extra fields %2")
++ .arg(currentPath(), QString::fromUtf8(QJsonDocument(e).toJson())));
++ }
++
++ void Reader::warnInvalidSize(qint32 size, qint32 expectedSize)
++ {
++ if (size != expectedSize)
++ warn(QStringLiteral(u"%1 expected %1 elements, not %2.")
++ .arg(currentPath(), QString::number(expectedSize), QString::number(size)));
++ }
++
++ void Reader::warnMissing(QStringView s)
++ {
++ warn(QStringLiteral(u"%1 misses value of type %2").arg(currentPath(), s));
++ }
++
++ void Reader::warnNonNull()
++ {
++ QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson();
++ warn(QStringLiteral(u"%1 is supposed to be null, but is %2")
++ .arg(currentPath(), QString::fromUtf8(val.mid(1, val.size() - 2))));
++ }
++
++ void Reader::warn(const QString &msg)
++ {
++ m_p->errorMessages.append(msg);
++ m_p->parseStatus = ParseStatus::Failed;
++ }
++
++ void Reader::handleJson(QJsonValue &v)
++ {
++ v = currentValue();
++ }
++
++ void Reader::handleJson(QJsonObject &v)
++ {
++ if (!currentValue().isObject() && !currentValue().isNull() && !currentValue().isUndefined()) {
++ QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson();
++ warn(QStringLiteral(u"Error: expected an object at %1, not %2")
++ .arg(currentPath(), QString::fromUtf8(val.mid(1, val.size() - 2))));
++ }
++ v = currentValue().toObject();
++ }
++
++ void Reader::handleJson(QJsonArray &v)
++ {
++ if (!currentValue().isArray() && !currentValue().isNull() && !currentValue().isUndefined()) {
++ QByteArray val = QJsonDocument(QJsonArray({ currentValue() })).toJson();
++ warn(QStringLiteral(u"Error: expected an array at %1, not %2")
++ .arg(currentPath(), QString::fromUtf8(val.mid(1, val.size() - 2))));
++ }
++ v = currentValue().toArray();
++ }
++
++ QJsonObject Reader::getExtraFields() const
++ {
++ QJsonObject extraFields;
++ QJsonObject v = currentValue().toObject();
++ auto it = v.constBegin();
++ auto end = v.constEnd();
++ auto &vField = m_p->objectsStack.last().visitedFields;
++ while (it != end) {
++ if (!vField.contains(it.key())) {
++ extraFields.insert(it.key(), it.value());
++ }
++ ++it;
++ }
++ return extraFields;
++ }
++
++ void Reader::startArrayF(qint32 &size)
++ {
++ size = int(currentValue().toArray().size());
++ }
++
++ bool Reader::startElement(qint32 index)
++ {
++ int oldWarnLevel = (m_p->valuesStack.isEmpty() ? 0 : m_p->valuesStack.last().warnLevel);
++ m_p->valuesStack.append(ValueStack { currentValue().toArray().at(index), QString(), index,
++ (oldWarnLevel ? oldWarnLevel + 1 : 0) });
++ return true;
++ }
++
++ void Reader::endElement(qint32 index)
++ {
++ Q_ASSERT(m_p->valuesStack.last().indexPath == index);
++ m_p->valuesStack.removeLast();
++ }
++
++ void Reader::endArrayF(qint32 &) { }
++
++ QString Reader::currentPath() const
++ {
++ QStringList res;
++ for (const auto &el : std::as_const(m_p->valuesStack)) {
++ if (el.indexPath != -1)
++ res.append(QString::number(el.indexPath));
++ else
++ res.append(el.fieldPath);
++ }
++ return res.join(u".");
++ }
++
++ bool Reader::startTuple(qint32 size)
++ {
++ qint32 expected = qint32(currentValue().toArray().size());
++ if (size != expected) {
++ warnInvalidSize(size, expected);
++ return false;
++ };
++ return true;
++ }
++
++ void Reader::endTuple(qint32) { }
++
++ void JsonBuilder::handleBasic(const bool &v)
++ {
++ m_values.append(QJsonValue(v));
++ }
++
++ void JsonBuilder::handleBasic(const QByteArray &v)
++ {
++ handleConstString(v);
++ }
++
++ void JsonBuilder::handleConstString(QByteArrayView v)
++ {
++ m_values.append(QJsonValue(QString::fromUtf8(v)));
++ }
++
++ void JsonBuilder::handleBasic(const int &v)
++ {
++ m_values.append(QJsonValue(v));
++ }
++
++ void JsonBuilder::handleBasic(const unsigned int &v)
++ {
++ // note: there is no unsigned representation in QJsonValue.
++ handleBasic(static_cast<double>(v));
++ }
++
++ void JsonBuilder::handleBasic(const double &v)
++ {
++ m_values.append(QJsonValue(v));
++ }
++
++ void JsonBuilder::handleNullType()
++ {
++ m_values.append(QJsonValue(QJsonValue::Type::Null));
++ }
++
++ void JsonBuilder::handleMissingOptional()
++ {
++ if (m_fieldLevel.isEmpty() || m_fieldLevel.last() != m_values.size())
++ handleNullType();
++ }
++
++ bool JsonBuilder::startField(const QString &)
++ {
++ m_fieldLevel.append(m_values.size());
++ return true;
++ }
++ bool JsonBuilder::startField(const QByteArray &)
++ {
++ return startField(nullptr);
++ }
++
++ bool JsonBuilder::startField(const char *)
++ {
++ m_fieldLevel.append(m_values.size());
++ return true;
++ }
++
++ void JsonBuilder::endField(const QString &v)
++ {
++ Q_ASSERT(!m_fieldLevel.isEmpty());
++ if (m_fieldLevel.last() < m_values.size()) {
++ Q_ASSERT(m_values.size() > 1);
++ if (QJsonObject *o = std::get_if<QJsonObject>(&m_values[m_values.size() - 2])) {
++ o->insert(v, popLastValue());
++ } else {
++ Q_ASSERT(false);
++ }
++ }
++ Q_ASSERT(!m_fieldLevel.isEmpty() && m_fieldLevel.last() == m_values.size());
++ m_fieldLevel.removeLast();
++ }
++
++ void JsonBuilder::endField(const char *v)
++ {
++ endField(QString::fromUtf8(v));
++ }
++
++ void JsonBuilder::endField(const QByteArray &v)
++ {
++ endField(v.data());
++ }
++
++ bool JsonBuilder::startObjectF(const char *, ObjectOptions, quintptr)
++ {
++ m_values.append(QJsonObject());
++ return true;
++ }
++
++ void JsonBuilder::endObjectF(const char *, ObjectOptions, quintptr) { }
++
++ bool JsonBuilder::startArrayF(qint32 &)
++ {
++ m_values.append(QJsonArray());
++ m_arrayLevel.append(m_values.size());
++ return true;
++ }
++
++ bool JsonBuilder::startElement(qint32)
++ {
++ return true;
++ }
++
++ void JsonBuilder::endElement(qint32)
++ {
++ Q_ASSERT(m_values.size() > 1);
++ if (QJsonArray *a = std::get_if<QJsonArray>(&m_values[m_values.size() - 2])) {
++ a->append(popLastValue());
++ } else {
++ Q_ASSERT(false);
++ }
++ }
++
++ void JsonBuilder::endArrayF(qint32 &)
++ {
++ Q_ASSERT(!m_arrayLevel.isEmpty() && m_arrayLevel.last() == m_values.size());
++ m_arrayLevel.removeLast();
++ }
++
++ void JsonBuilder::handleJson(QJsonValue &v)
++ {
++ m_values.append(v);
++ }
++
++ void JsonBuilder::handleJson(QJsonObject &v)
++ {
++ m_values.append(v);
++ }
++
++ void JsonBuilder::handleJson(QJsonArray &v)
++ {
++ m_values.append(v);
++ }
++
++ QJsonValue JsonBuilder::popLastValue()
++ {
++ if (m_values.isEmpty())
++ return QJsonValue(QJsonValue::Type::Undefined);
++ QJsonValue res = std::visit([](auto &v) { return QJsonValue(v); }, m_values.last());
++ m_values.removeLast();
++ return res;
++ }
++
++ bool JsonBuilder::startTuple(qint32 size)
++ {
++ return startArrayF(size);
++ }
++
++ void JsonBuilder::endTuple(qint32 size)
++ {
++ endArrayF(size);
++ }
++
++ } // namespace QTypedJson
++
++ QT_END_NAMESPACE