KD SOAP API Documentation  2.1
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-2022 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  QVariant::Type metaTypeId = QVariant::Invalid;
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 
130 {
131 }
132 
133 static 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 
156 static 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 
179 KDSoapMessageReader::XmlError KDSoapMessageReader::xmlToMessage(const QByteArray &data, KDSoapMessage *pMsg, QString *pMessageNamespace,
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")
186  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
187  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
188  const QXmlStreamNamespaceDeclarations envNsDecls = reader.namespaceDeclarations();
189  if (reader.readNextStartElement()) {
190  if (reader.name() == QLatin1String("Header")
191  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
192  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
193  KDSoapMessageAddressingProperties messageAddressingProperties;
194  while (reader.readNextStartElement()) {
195  if (KDSoapMessageAddressingProperties::isWSAddressingNamespace(reader.namespaceUri().toString())) {
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")
208  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
209  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
210  if (reader.readNextStartElement()) {
211  *pMsg = parseElement(reader, envNsDecls);
212  if (pMessageNamespace) {
213  *pMessageNamespace = pMsg->namespaceUri();
214  }
215  if (pMsg->name() == QLatin1String("Fault")
216  && (reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope()
217  || reader.namespaceUri() == KDSoapNamespaceManager::soapEnvelope200305())) {
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()) {
233  if (reader.error() == QXmlStreamReader::NotWellFormedError) {
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);
243  return reader.error() == QXmlStreamReader::PrematureEndOfDocumentError ? PrematureEndOfDocumentError : ParseError;
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)
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-2022 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 Jun 13 2023 12:18:34 for KD SOAP API Documentation by doxygen 1.9.1