KD SOAP API Documentation 2.2
Loading...
Searching...
No Matches
KDSoapMessageReader.cpp
Go to the documentation of this file.
1/****************************************************************************
2**
3** This file is part of the KD Soap project.
4**
5** SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
6**
7** SPDX-License-Identifier: MIT
8**
9****************************************************************************/
10
11#include "KDDateTime.h"
15
16#include <QDebug>
17#include <QXmlStreamReader>
18
19#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
20#define QStringView QStringRef
21#endif
22
23static QStringView namespaceForPrefix(const QXmlStreamNamespaceDeclarations &decls, const QString &prefix)
24{
25 for (const QXmlStreamNamespaceDeclaration &decl : qAsConst(decls)) {
26 if (decl.prefix() == prefix) {
27 return decl.namespaceUri();
28 }
29 }
30 return QStringView();
31}
32
33static int xmlTypeToMetaType(const QString &xmlType)
34{
35 // Reverse operation from variantToXmlType in KDSoapClientInterface, keep in sync
36 static const struct
37 {
38 const char *xml; // xsd: prefix assumed
39 const int metaTypeId;
40 } s_types[] = {{"string", QVariant::String}, // or QUrl
41 {"base64Binary", QVariant::ByteArray},
42 {"int", QVariant::Int}, // or long, or uint, or longlong
43 {"unsignedInt", QVariant::ULongLong},
44 {"boolean", QVariant::Bool},
45 {"float", QMetaType::Float},
46 {"double", QVariant::Double},
47 {"time", QVariant::Time},
48 {"date", QVariant::Date}};
49 // Speed: could be sorted and then we could use qBinaryFind
50 for (const auto &type : s_types) {
51 if (xmlType == QLatin1String(type.xml)) {
52 return type.metaTypeId;
53 }
54 }
55 if (xmlType == QLatin1String("dateTime")) {
56 return qMetaTypeId<KDDateTime>();
57 }
58 // This will happen with any custom type, don't bother the user
59 // qDebug() << QString::fromLatin1("xmlTypeToMetaType: XML type %1 is not supported in "
60 // "KDSoap, see the documentation").arg(xmlType);
61 return -1;
62}
63
64static KDSoapValue parseElement(QXmlStreamReader &reader, const QXmlStreamNamespaceDeclarations &envNsDecls)
65{
66 const QXmlStreamNamespaceDeclarations combinedNamespaceDeclarations = envNsDecls + reader.namespaceDeclarations();
67 const QString name = reader.name().toString();
68 KDSoapValue val(name, QVariant());
71 val.setEnvironmentNamespaceDeclarations(combinedNamespaceDeclarations);
72 // qDebug() << "parsing" << name;
73#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
75#else
77#endif
78 const auto invalidType = metaTypeId;
79
80 const QXmlStreamAttributes attributes = reader.attributes();
81 for (const QXmlStreamAttribute &attribute : attributes) {
82 const QStringView name = attribute.name();
83 const QStringView ns = attribute.namespaceUri();
84 const QStringView attrValue = attribute.value();
85 // Parse xsi:type and soap-enc:arrayType
86 // and ignore anything else from the xsi or soap-enc namespaces until someone needs it...
88 if (name == QLatin1String("type")) {
89 // The type can be like xsd:float, resolve that
90 const QString type = attrValue.toString();
91 const int pos = type.indexOf(QLatin1Char(':'));
92 const QString dataType = type.mid(pos + 1);
93 val.setType(namespaceForPrefix(combinedNamespaceDeclarations, type.left(pos)).toString(), dataType);
94 metaTypeId = static_cast<decltype(metaTypeId)>(xmlTypeToMetaType(dataType));
95 }
96 continue;
99 continue;
100 }
101 // qDebug() << "Got attribute:" << name << ns << "=" << attrValue;
102 val.childValues().attributes().append(KDSoapValue(name.toString(), attrValue.toString()));
103 }
104 QString text;
105 while (reader.readNext() != QXmlStreamReader::Invalid) {
106 if (reader.isEndElement()) {
107 break;
108 }
109 if (reader.isCharacters()) {
110 text = reader.text().toString();
111 // qDebug() << "text=" << text;
112 } else if (reader.isStartElement()) {
113 const KDSoapValue subVal = parseElement(reader, combinedNamespaceDeclarations); // recurse
114 val.childValues().append(subVal);
115 }
116 }
117
118 if (!text.isEmpty()) {
119 QVariant variant(text);
120 // qDebug() << text << variant << metaTypeId;
121 // With use=encoded, we have type info, we can convert the variant here
122 // Otherwise, for servers, we do it later, once we know the method's parameter types.
123 if (metaTypeId != invalidType) {
124 QVariant copy = variant;
125#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
126 if (!variant.convert(metaTypeId)) {
127#else
128 if (!variant.convert(QMetaType(metaTypeId))) {
129#endif
130 variant = std::move(copy);
131 }
132 }
133 val.setValue(variant);
134 }
135 return val;
136}
137
141
142static bool isInvalidCharRef(const QByteArray &charRef)
143{
144 bool ok = true;
145 int symbol = charRef.indexOf('x');
146 int end = charRef.indexOf(';');
147
148 if (symbol == -1 || end == -1) {
149 return false;
150 }
151
152 uint val = charRef.mid(symbol + 1, end - symbol - 1).toInt(&ok, 16);
153
154 if (!ok) {
155 return false;
156 }
157
158 if (val != 0x9 && val != 0xa && val != 0xd && (val <= 0x20)) {
159 return true;
160 }
161
162 return false;
163}
164
165static QByteArray handleNotWellFormedError(const QByteArray &data, qint64 offset)
166{
167 qint64 i = offset - 1; // offset is the char following the failing one
168 QByteArray dataCleanedUp;
169 QByteArray originalSequence;
170
171 while (i >= 0 && data.at(i) != '&') {
172 if (data.at(i) == '<') { // InvalidXML but not invalid characters related
173 return dataCleanedUp;
174 }
175
176 originalSequence.prepend(data.at(i));
177 i--;
178 }
179
180 if (isInvalidCharRef(originalSequence)) {
181 qWarning() << "found an invalid character sequence to remove:" << QLatin1String(originalSequence.prepend('&').constData());
182 dataCleanedUp = data;
183 dataCleanedUp = dataCleanedUp.replace(originalSequence, "?");
184 }
185 return dataCleanedUp;
186}
187
189 KDSoapHeaders *pRequestHeaders, KDSoap::SoapVersion soapVersion) const
190{
191 Q_ASSERT(pMsg);
192 QXmlStreamReader reader(data);
193 if (reader.readNextStartElement()) {
194 if (reader.name() == QLatin1String("Envelope")
197 const QXmlStreamNamespaceDeclarations envNsDecls = reader.namespaceDeclarations();
198 if (reader.readNextStartElement()) {
199 if (reader.name() == QLatin1String("Header")
202 KDSoapMessageAddressingProperties messageAddressingProperties;
203 while (reader.readNextStartElement()) {
205 KDSoapValue value = parseElement(reader, envNsDecls);
206 messageAddressingProperties.readMessageAddressingProperty(value);
207 } else {
208 KDSoapMessage header;
209 static_cast<KDSoapValue &>(header) = parseElement(reader, envNsDecls);
210 pRequestHeaders->append(header);
211 }
212 }
213 pMsg->setMessageAddressingProperties(messageAddressingProperties);
214 reader.readNextStartElement(); // read <Body>
215 }
216 if (reader.name() == QLatin1String("Body")
219 if (reader.readNextStartElement()) {
220 *pMsg = parseElement(reader, envNsDecls);
221 if (pMessageNamespace) {
222 *pMessageNamespace = pMsg->namespaceUri();
223 }
224 if (pMsg->name() == QLatin1String("Fault")
227 pMsg->setFault(true);
228 }
229 }
230
231 } else {
232 reader.raiseError(QObject::tr("Invalid SOAP Message, Body expected"));
233 }
234 } else {
235 reader.raiseError(QObject::tr("Invalid SOAP Message, empty Envelope"));
236 }
237 } else {
238 reader.raiseError(QObject::tr("Invalid SOAP Message, Envelope expected"));
239 }
240 }
241 if (reader.hasError()) {
243 qWarning() << "Handling a Not well Formed Error";
244 QByteArray dataCleanedUp = handleNotWellFormedError(data, reader.characterOffset());
245 if (!dataCleanedUp.isEmpty()) {
246 return xmlToMessage(dataCleanedUp, pMsg, pMessageNamespace, pRequestHeaders, soapVersion);
247 }
248 }
249 QString faultText = QString::fromLatin1("XML error: [%1:%2] %3")
250 .arg(QString::number(reader.lineNumber()), QString::number(reader.columnNumber()), reader.errorString());
251 pMsg->createFaultMessage(QString::number(reader.error()), faultText, soapVersion);
253 }
254
255 return NoError;
256}
static QStringView namespaceForPrefix(const QXmlStreamNamespaceDeclarations &decls, const QString &prefix)
static QByteArray handleNotWellFormedError(const QByteArray &data, qint64 offset)
static KDSoapValue parseElement(QXmlStreamReader &reader, const QXmlStreamNamespaceDeclarations &envNsDecls)
static int xmlTypeToMetaType(const QString &xmlType)
static bool isInvalidCharRef(const QByteArray &charRef)
static bool isWSAddressingNamespace(const QString &namespaceUri)
XmlError xmlToMessage(const QByteArray &data, KDSoapMessage *pParsedMessage, QString *pMessageNamespace, KDSoapHeaders *pRequestHeaders, KDSoap::SoapVersion soapVersion) const
void setMessageAddressingProperties(const KDSoapMessageAddressingProperties &map)
void createFaultMessage(const QString &faultCode, const QString &faultText, KDSoap::SoapVersion soapVersion)
void setFault(bool fault)
QList< KDSoapValue > & attributes()
KDSoapValueList & childValues() const
void setNamespaceUri(const QString &ns)
QString namespaceUri() const
void setType(const QString &nameSpace, const QString &type)
void setNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &namespaceDeclarations)
QString name() const
void setValue(const QVariant &value)
void setEnvironmentNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &environmentNamespaceDeclarations)
char at(int i) const const
const char * constData() const const
int indexOf(char ch, int from) const const
bool isEmpty() const const
QByteArray mid(int pos, int len) const const
QByteArray & prepend(char ch)
QByteArray & replace(int pos, int len, const char *after)
int toInt(bool *ok, int base) const const
void append(const T &value)
QString tr(const char *sourceText, const char *disambiguation, int n)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString fromLatin1(const char *str, int size)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(int n) const const
QString mid(int position, int n) const const
QString number(int n, int base)
QString toString() const const
QString toString() const const
bool convert(int targetTypeId)
QXmlStreamAttributes attributes() const const
qint64 characterOffset() const const
qint64 columnNumber() const const
QXmlStreamReader::Error error() const const
QString errorString() const const
bool hasError() const const
bool isCharacters() const const
bool isEndElement() const const
bool isStartElement() const const
qint64 lineNumber() const const
QStringRef name() const const
QXmlStreamNamespaceDeclarations namespaceDeclarations() const const
QStringRef namespaceUri() const const
void raiseError(const QString &message)
QXmlStreamReader::TokenType readNext()
bool readNextStartElement()
QStringRef text() const const

© Klarälvdalens Datakonsult AB (KDAB)
"The Qt, C++ and OpenGL Experts"
https://www.kdab.com/
https://www.kdab.com/development-resources/qt-tools/kd-soap/
Generated on Sat Apr 20 2024 00:04:25 for KD SOAP API Documentation by doxygen 1.9.8