Simplify Propfind XML parsing using QDomDocument instead of complex QXmlStreamReader...
authorClaudio Cambra <claudio.cambra@nextcloud.com>
Thu, 20 Apr 2023 07:35:46 +0000 (15:35 +0800)
committerClaudio Cambra <claudio.cambra@nextcloud.com>
Tue, 16 May 2023 10:23:33 +0000 (18:23 +0800)
Signed-off-by: Claudio Cambra <claudio.cambra@nextcloud.com>
src/libsync/CMakeLists.txt
src/libsync/networkjobs.cpp

index a6ccb6d4f5edb0f7ba2028130328688bf0f8343a..ecf6a9125f14c3c94d6670bd86ad680e123db45f 100644 (file)
@@ -171,7 +171,7 @@ IF (NOT APPLE)
     )
 ENDIF(NOT APPLE)
 
-find_package(Qt5 REQUIRED COMPONENTS WebSockets)
+find_package(Qt5 REQUIRED COMPONENTS WebSockets Xml)
 
 add_library(nextcloudsync SHARED ${libsync_SRCS})
 add_library(Nextcloud::sync ALIAS nextcloudsync)
@@ -185,6 +185,7 @@ target_link_libraries(nextcloudsync
   Qt5::Core
   Qt5::Network
   Qt5::WebSockets
+  Qt5::Xml
 )
 
 if (NOT TOKEN_AUTH_ONLY)
index 152e95fa7608e4c9bb1bebc2cb8d3495f5919c52..b40f6653ea15412e4b024823e39d957f28d0f117 100644 (file)
@@ -23,6 +23,7 @@
 #include <QSslCipher>
 #include <QBuffer>
 #include <QXmlStreamReader>
+#include <QtXml/QDomDocument>
 #include <QStringList>
 #include <QStack>
 #include <QTimer>
@@ -608,34 +609,43 @@ bool PropfindJob::finished()
 
     if (http_result_code == 207) {
         // Parse DAV response
-        QXmlStreamReader reader(reply());
-        reader.addExtraNamespaceDeclaration(QXmlStreamNamespaceDeclaration("d", "DAV:"));
+        auto items = QVariantMap();
+        auto domDocument = QDomDocument();
+        auto errorMsg = QString();
+        auto errorLine = -1;
+        auto errorColumn = -1;
+
+        if (!domDocument.setContent(reply(), true, &errorMsg, &errorLine, &errorColumn)) {
+            qCWarning(lcPropfindJob) << "XML parser error: " << errorMsg << errorLine << errorColumn;
+            emit finishedWithError(reply());
+            return true;
+        }
 
-        QVariantMap items;
-        // introduced to nesting is ignored
-        QStack<QString> curElement;
+        const auto rootElement = domDocument.documentElement();
+        const auto propNodes = rootElement.elementsByTagName("prop");
 
-        while (!reader.atEnd()) {
-            const auto type = reader.readNext();
-            if (type == QXmlStreamReader::StartElement) {
-                if (!curElement.isEmpty() && curElement.top() == QLatin1String("prop")) {
-                    items.insert(reader.name().toString(), reader.readElementText(QXmlStreamReader::SkipChildElements));
-                } else {
-                    curElement.push(reader.name().toString());
-                }
+        for (auto i = 0; i < propNodes.count(); ++i) {
+            const auto propNode = propNodes.at(i);
+            const auto propElement = propNode.toElement();
+
+            if (propElement.isNull() || propElement.tagName() != "prop") {
+                continue;
             }
-            if (type == QXmlStreamReader::EndElement) {
-                if (curElement.top() == reader.name()) {
-                    curElement.pop();
+
+            auto propChildNode = propElement.firstChild();
+
+            while (!propChildNode.isNull()) {
+                const auto propChildElement = propChildNode.toElement();
+
+                if (!propChildElement.isNull()) {
+                    items.insert(propChildElement.tagName(), propChildElement.text());
                 }
+
+                propChildNode = propChildNode.nextSibling();
             }
-        }
-        if (reader.hasError()) {
-            qCWarning(lcPropfindJob) << "XML parser error: " << reader.errorString();
-            emit finishedWithError(reply());
-        } else {
-            emit result(items);
-        }
+    }
+
+        emit result(items);
     } else {
         qCWarning(lcPropfindJob) << "*not* successful, http result code is" << http_result_code
                                  << (http_result_code == 302 ? reply()->header(QNetworkRequest::LocationHeader).toString() : QLatin1String(""));