KD SOAP API Documentation 2.1
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-2023 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;
74
75 const QXmlStreamAttributes attributes = reader.attributes();
76 for (const QXmlStreamAttribute &attribute : attributes) {
77 const QStringView name = attribute.name();
78 const QStringView ns = attribute.namespaceUri();
79 const QStringView attrValue = attribute.value();
80 // Parse xsi:type and soap-enc:arrayType
81 // and ignore anything else from the xsi or soap-enc namespaces until someone needs it...
83 if (name == QLatin1String("type")) {
84 // The type can be like xsd:float, resolve that
85 const QString type = attrValue.toString();
86 const int pos = type.indexOf(QLatin1Char(':'));
87 const QString dataType = type.mid(pos + 1);
88 val.setType(namespaceForPrefix(combinedNamespaceDeclarations, type.left(pos)).toString(), dataType);
89 metaTypeId = static_cast<QVariant::Type>(xmlTypeToMetaType(dataType));
90 }
91 continue;
94 continue;
95 }
96 // qDebug() << "Got attribute:" << name << ns << "=" << attrValue;
97 val.childValues().attributes().append(KDSoapValue(name.toString(), attrValue.toString()));
98 }
99 QString text;
100 while (reader.readNext() != QXmlStreamReader::Invalid) {
101 if (reader.isEndElement()) {
102 break;
103 }
104 if (reader.isCharacters()) {
105 text = reader.text().toString();
106 // qDebug() << "text=" << text;
107 } else if (reader.isStartElement()) {
108 const KDSoapValue subVal = parseElement(reader, combinedNamespaceDeclarations); // recurse
109 val.childValues().append(subVal);
110 }
111 }
112
113 if (!text.isEmpty()) {
114 QVariant variant(text);
115 // qDebug() << text << variant << metaTypeId;
116 // With use=encoded, we have type info, we can convert the variant here
117 // Otherwise, for servers, we do it later, once we know the method's parameter types.
118 if (metaTypeId != QVariant::Invalid) {
119 QVariant copy = variant;
120 if (!variant.convert(metaTypeId)) {
121 variant = copy;
122 }
123 }
124 val.setValue(variant);
125 }
126 return val;
127}
128
132
133static bool isInvalidCharRef(const QByteArray &charRef)
134{
135 bool ok = true;
136 int symbol = charRef.indexOf('x');
137 int end = charRef.indexOf(';');
138
139 if (symbol == -1 || end == -1) {
140 return false;
141 }
142
143 uint val = charRef.mid(symbol + 1, end - symbol - 1).toInt(&ok, 16);
144
145 if (!ok) {
146 return false;
147 }
148
149 if (val != 0x9 && val != 0xa && val != 0xd && (val <= 0x20)) {
150 return true;
151 }
152
153 return false;
154}
155
156static QByteArray handleNotWellFormedError(const QByteArray &data, qint64 offset)
157{
158 qint64 i = offset - 1; // offset is the char following the failing one
159 QByteArray dataCleanedUp;
160 QByteArray originalSequence;
161
162 while (i >= 0 && data.at(i) != '&') {
163 if (data.at(i) == '<') { // InvalidXML but not invalid characters related
164 return dataCleanedUp;
165 }
166
167 originalSequence.prepend(data.at(i));
168 i--;
169 }
170
171 if (isInvalidCharRef(originalSequence)) {
172 qWarning() << "found an invalid character sequence to remove:" << QLatin1String(originalSequence.prepend('&').constData());
173 dataCleanedUp = data;
174 dataCleanedUp = dataCleanedUp.replace(originalSequence, "?");
175 }
176 return dataCleanedUp;
177}
178
180 KDSoapHeaders *pRequestHeaders, KDSoap::SoapVersion soapVersion) const
181{
182 Q_ASSERT(pMsg);
183 QXmlStreamReader reader(data);
184 if (reader.readNextStartElement()) {
185 if (reader.name() == QLatin1String("Envelope")
188 const QXmlStreamNamespaceDeclarations envNsDecls = reader.namespaceDeclarations();
189 if (reader.readNextStartElement()) {
190 if (reader.name() == QLatin1String("Header")
193 KDSoapMessageAddressingProperties messageAddressingProperties;
194 while (reader.readNextStartElement()) {
196 KDSoapValue value = parseElement(reader, envNsDecls);
197 messageAddressingProperties.readMessageAddressingProperty(value);
198 } else {
199 KDSoapMessage header;
200 static_cast<KDSoapValue &>(header) = parseElement(reader, envNsDecls);
201 pRequestHeaders->append(header);
202 }
203 }
204 pMsg->setMessageAddressingProperties(messageAddressingProperties);
205 reader.readNextStartElement(); // read <Body>
206 }
207 if (reader.name() == QLatin1String("Body")
210 if (reader.readNextStartElement()) {
211 *pMsg = parseElement(reader, envNsDecls);
212 if (pMessageNamespace) {
213 *pMessageNamespace = pMsg->namespaceUri();
214 }
215 if (pMsg->name() == QLatin1String("Fault")
218 pMsg->setFault(true);
219 }
220 }
221
222 } else {
223 reader.raiseError(QObject::tr("Invalid SOAP Message, Body expected"));
224 }
225 } else {
226 reader.raiseError(QObject::tr("Invalid SOAP Message, empty Envelope"));
227 }
228 } else {
229 reader.raiseError(QObject::tr("Invalid SOAP Message, Envelope expected"));
230 }
231 }
232 if (reader.hasError()) {
234 qWarning() << "Handling a Not well Formed Error";
235 QByteArray dataCleanedUp = handleNotWellFormedError(data, reader.characterOffset());
236 if (!dataCleanedUp.isEmpty()) {
237 return xmlToMessage(dataCleanedUp, pMsg, pMessageNamespace, pRequestHeaders, soapVersion);
238 }
239 }
240 QString faultText = QString::fromLatin1("XML error: [%1:%2] %3")
241 .arg(QString::number(reader.lineNumber()), QString::number(reader.columnNumber()), reader.errorString());
242 pMsg->createFaultMessage(QString::number(reader.error()), faultText, soapVersion);
244 }
245
246 return NoError;
247}
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

© 2010-2023 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 Tue Dec 26 2023 00:00:25 for KD SOAP API Documentation by doxygen 1.9.8