KD SOAP API Documentation  2.2
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"
12 #include "KDSoapMessageReader_p.h"
13 #include "KDSoapNamespaceManager.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 
23 static 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 
33 static 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 
64 static 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());
69  val.setNamespaceUri(reader.namespaceUri().toString());
70  val.setNamespaceDeclarations(reader.namespaceDeclarations());
71  val.setEnvironmentNamespaceDeclarations(combinedNamespaceDeclarations);
72  // qDebug() << "parsing" << name;
73 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
74  QVariant::Type metaTypeId = QVariant::Invalid;
75 #else
76  QMetaType::Type metaTypeId = QMetaType::UnknownType;
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 
139 {
140 }
141 
142 static 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 
165 static 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 
188 KDSoapMessageReader::XmlError KDSoapMessageReader::xmlToMessage(const QByteArray &data, KDSoapMessage *pMsg, QString *pMessageNamespace,
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")
195  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
196  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
197  const QXmlStreamNamespaceDeclarations envNsDecls = reader.namespaceDeclarations();
198  if (reader.readNextStartElement()) {
199  if (reader.name() == QLatin1String("Header")
200  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
201  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
202  KDSoapMessageAddressingProperties messageAddressingProperties;
203  while (reader.readNextStartElement()) {
204  if (KDSoapMessageAddressingProperties::isWSAddressingNamespace(reader.namespaceUri().toString())) {
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")
217  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
218  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
219  if (reader.readNextStartElement()) {
220  *pMsg = parseElement(reader, envNsDecls);
221  if (pMessageNamespace) {
222  *pMessageNamespace = pMsg->namespaceUri();
223  }
224  if (pMsg->name() == QLatin1String("Fault")
225  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
226  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
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()) {
242  if (reader.error() == QXmlStreamReader::NotWellFormedError) {
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);
252  return reader.error() == QXmlStreamReader::PrematureEndOfDocumentError ? PrematureEndOfDocumentError : ParseError;
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)
static QString xmlSchemaInstance1999()
static QString xmlSchemaInstance2001()
QList< KDSoapValue > & attributes()
Definition: KDSoapValue.h:375
KDSoapValueList & childValues() const
QXmlStreamNamespaceDeclarations namespaceDeclarations() 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
Definition: KDSoapValue.cpp:94
void setValue(const QVariant &value)
void setEnvironmentNamespaceDeclarations(const QXmlStreamNamespaceDeclarations &environmentNamespaceDeclarations)

© 2010-2024 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 by doxygen 1.9.1