File indexing completed on 2024-04-21 04:44:13

0001 /*
0002    SPDX-FileCopyrightText: 2015 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
0003 
0004    SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #include "upnpdevicedescriptionparser.h"
0008 
0009 #include "upnplogging.h"
0010 
0011 #include "upnpdevicedescription.h"
0012 #include "upnpservicedescription.h"
0013 
0014 #include "upnpservicedescriptionparser.h"
0015 
0016 #include <QNetworkAccessManager>
0017 #include <QNetworkReply>
0018 #include <QNetworkRequest>
0019 
0020 #include <QDomDocument>
0021 
0022 #include <QLoggingCategory>
0023 
0024 class UpnpDeviceDescriptionParserPrivate
0025 {
0026 public:
0027     UpnpDeviceDescriptionParserPrivate(QNetworkAccessManager *aNetworkAccess, UpnpDeviceDescription &deviceDescription)
0028         : mNetworkAccess(aNetworkAccess)
0029         , mDeviceDescription(deviceDescription)
0030         , mDeviceURL()
0031     {
0032     }
0033 
0034     QNetworkAccessManager *mNetworkAccess;
0035 
0036     UpnpDeviceDescription &mDeviceDescription;
0037 
0038     std::map<QString, std::unique_ptr<UpnpServiceDescriptionParser>> mServiceDescriptionParsers;
0039 
0040     QUrl mDeviceURL;
0041 };
0042 
0043 UpnpDeviceDescriptionParser::UpnpDeviceDescriptionParser(QNetworkAccessManager *aNetworkAccess, UpnpDeviceDescription &deviceDescription, QObject *parent)
0044     : QObject(parent)
0045     , d(std::make_unique<UpnpDeviceDescriptionParserPrivate>(aNetworkAccess, deviceDescription))
0046 {
0047 }
0048 
0049 UpnpDeviceDescriptionParser::~UpnpDeviceDescriptionParser() = default;
0050 
0051 void UpnpDeviceDescriptionParser::downloadDeviceDescription(const QUrl &deviceUrl)
0052 {
0053     d->mDeviceURL = deviceUrl;
0054     d->mNetworkAccess->get(QNetworkRequest(deviceUrl));
0055 }
0056 
0057 void UpnpDeviceDescriptionParser::serviceDescriptionParsed(const QString &upnpServiceId)
0058 {
0059     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceDescriptionParser::serviceDescriptionParsed" << upnpServiceId;
0060 
0061     d->mServiceDescriptionParsers.erase(upnpServiceId);
0062 
0063     if (d->mServiceDescriptionParsers.empty()) {
0064         Q_EMIT descriptionParsed(d->mDeviceDescription.UDN());
0065     }
0066 }
0067 
0068 void UpnpDeviceDescriptionParser::finishedDownload(QNetworkReply *reply)
0069 {
0070     qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceDescriptionParser::finishedDownload";
0071     if (reply->url() == d->mDeviceURL) {
0072         if (reply->isFinished() && reply->error() == QNetworkReply::NoError) {
0073             parseDeviceDescription(reply, reply->url().adjusted(QUrl::RemovePath).toString());
0074         } else if (reply->isFinished()) {
0075             qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceDescriptionParser::finishedDownload"
0076                                            << "error when downloading device description";
0077             Q_EMIT deviceDescriptionInError(d->mDeviceDescription.UDN());
0078         }
0079     } else {
0080         qCDebug(orgKdeUpnpLibQtUpnp()) << "UpnpDeviceDescriptionParser::finishedDownload"
0081                                        << "unexpected reply for another device url";
0082     }
0083 }
0084 
0085 void UpnpDeviceDescriptionParser::parseDeviceDescription(QIODevice *deviceDescriptionContent, const QString &fallBackURLBase)
0086 {
0087     QDomDocument deviceDescriptionDocument;
0088     deviceDescriptionDocument.setContent(deviceDescriptionContent);
0089 
0090     const QDomElement &documentRoot = deviceDescriptionDocument.documentElement();
0091 
0092     QVariantMap deviceDescription;
0093 
0094     QDomNode currentChild = documentRoot.firstChild();
0095     while (!currentChild.isNull()) {
0096         if (currentChild.isElement() && !currentChild.firstChild().isNull() && !currentChild.firstChild().hasChildNodes()) {
0097             deviceDescription[currentChild.nodeName()] = currentChild.toElement().text();
0098         }
0099         currentChild = currentChild.nextSibling();
0100     }
0101 
0102     const QDomElement &deviceRoot = documentRoot.firstChildElement(QStringLiteral("device"));
0103 
0104     currentChild = deviceRoot.firstChild();
0105     while (!currentChild.isNull()) {
0106         if (currentChild.isElement() && !currentChild.firstChild().isNull() && !currentChild.firstChild().hasChildNodes()) {
0107             deviceDescription[currentChild.nodeName()] = currentChild.toElement().text();
0108         }
0109         currentChild = currentChild.nextSibling();
0110     }
0111 
0112     d->mDeviceDescription.setUDN(deviceDescription[QStringLiteral("UDN")].toString());
0113     d->mDeviceDescription.setUPC(deviceDescription[QStringLiteral("UPC")].toString());
0114     d->mDeviceDescription.setDeviceType(deviceDescription[QStringLiteral("deviceType")].toString());
0115     d->mDeviceDescription.setFriendlyName(deviceDescription[QStringLiteral("friendlyName")].toString());
0116     d->mDeviceDescription.setManufacturer(deviceDescription[QStringLiteral("manufacturer")].toString());
0117     d->mDeviceDescription.setManufacturerURL(deviceDescription[QStringLiteral("manufacturerURL")].toUrl());
0118     d->mDeviceDescription.setModelDescription(deviceDescription[QStringLiteral("modelDescription")].toString());
0119     d->mDeviceDescription.setModelName(deviceDescription[QStringLiteral("modelName")].toString());
0120     d->mDeviceDescription.setModelNumber(deviceDescription[QStringLiteral("modelNumber")].toString());
0121     d->mDeviceDescription.setModelURL(deviceDescription[QStringLiteral("modelURL")].toUrl());
0122     d->mDeviceDescription.setSerialNumber(deviceDescription[QStringLiteral("serialNumber")].toString());
0123 
0124     if (deviceDescription[QStringLiteral("URLBase")].isValid() && !deviceDescription[QStringLiteral("URLBase")].toString().isEmpty()) {
0125         d->mDeviceDescription.setURLBase(deviceDescription[QStringLiteral("URLBase")].toString());
0126     } else {
0127         d->mDeviceDescription.setURLBase(fallBackURLBase);
0128     }
0129 
0130     auto serviceList = deviceDescriptionDocument.elementsByTagName(QStringLiteral("service"));
0131     for (int serviceIndex = 0; serviceIndex < serviceList.length(); ++serviceIndex) {
0132         const QDomNode &serviceNode(serviceList.at(serviceIndex));
0133         if (!serviceNode.isNull()) {
0134             auto newService = UpnpServiceDescription {};
0135 
0136             const QDomNode &serviceTypeNode = serviceNode.firstChildElement(QStringLiteral("serviceType"));
0137 #if 0
0138             if (!serviceTypeNode.isNull()) {
0139                 if (serviceTypeNode.toElement().text() == QStringLiteral("urn:schemas-upnp-org:service:AVTransport:1")) {
0140                     newService = new UpnpControlAVTransport;
0141                 } else if (serviceTypeNode.toElement().text() == QStringLiteral("urn:schemas-upnp-org:service:RenderingControl:1")) {
0142                     newService = new UpnpControlSwitchPower;
0143                 } else if (serviceTypeNode.toElement().text() == QStringLiteral("urn:schemas-upnp-org:service:SwitchPower:1")) {
0144                     newService = new UpnpControlSwitchPower;
0145                 } else if (serviceTypeNode.toElement().text() == QStringLiteral("urn:schemas-upnp-org:service:ConnectionManager:1")) {
0146                     newService = new UpnpControlConnectionManager;
0147                 } else if (serviceTypeNode.toElement().text() == QStringLiteral("urn:schemas-upnp-org:service:ContentDirectory:1")) {
0148                     newService = new UpnpControlContentDirectory;
0149                 } else {
0150                     newService = new UpnpControlAbstractService;
0151                 }
0152             } else {
0153                 newService = new UpnpControlAbstractService;
0154             }
0155 #endif
0156 
0157             newService.setBaseURL(d->mDeviceDescription.URLBase());
0158             if (!serviceTypeNode.isNull()) {
0159                 newService.setServiceType(serviceTypeNode.toElement().text());
0160             }
0161 
0162             const QDomNode &serviceIdNode = serviceNode.firstChildElement(QStringLiteral("serviceId"));
0163             if (!serviceIdNode.isNull()) {
0164                 newService.setServiceId(serviceIdNode.toElement().text());
0165             }
0166 
0167             const QDomNode &SCPDURLNode = serviceNode.firstChildElement(QStringLiteral("SCPDURL"));
0168             if (!SCPDURLNode.isNull()) {
0169                 newService.setSCPDURL(QUrl(SCPDURLNode.toElement().text()));
0170             }
0171 
0172             const QDomNode &controlURLNode = serviceNode.firstChildElement(QStringLiteral("controlURL"));
0173             if (!controlURLNode.isNull()) {
0174                 QUrl controlUrl(controlURLNode.toElement().text());
0175                 if (!controlUrl.isValid() || controlUrl.scheme().isEmpty()) {
0176                     controlUrl = QUrl(d->mDeviceDescription.URLBase());
0177                     controlUrl.setPath(controlURLNode.toElement().text());
0178                 }
0179                 newService.setControlURL(controlUrl);
0180             }
0181 
0182             const QDomNode &eventSubURLNode = serviceNode.firstChildElement(QStringLiteral("eventSubURL"));
0183             if (!eventSubURLNode.isNull()) {
0184                 QUrl eventUrl(eventSubURLNode.toElement().text());
0185                 if (!eventUrl.isValid() || eventUrl.scheme().isEmpty()) {
0186                     eventUrl = QUrl(d->mDeviceDescription.URLBase());
0187                     eventUrl.setPath(eventSubURLNode.toElement().text());
0188                 }
0189                 newService.setEventURL(eventUrl);
0190             }
0191 
0192             QUrl serviceUrl(newService.SCPDURL().toString());
0193             if (!serviceUrl.isValid() || serviceUrl.scheme().isEmpty()) {
0194                 serviceUrl.setUrl(d->mDeviceDescription.URLBase());
0195                 serviceUrl.setPath(newService.SCPDURL().toString());
0196             }
0197 
0198             d->mDeviceDescription.addService(std::move(newService));
0199 
0200             auto &serviceJustCreated = d->mDeviceDescription.serviceByIndex(serviceIndex);
0201             d->mServiceDescriptionParsers[serviceJustCreated.serviceId()]
0202                     = std::make_unique<UpnpServiceDescriptionParser>(d->mNetworkAccess, d->mDeviceDescription.serviceByIndex(serviceIndex));
0203 
0204             connect(d->mServiceDescriptionParsers[serviceJustCreated.serviceId()].get(), &UpnpServiceDescriptionParser::descriptionParsed,
0205                 this, &UpnpDeviceDescriptionParser::serviceDescriptionParsed);
0206             connect(d->mNetworkAccess, &QNetworkAccessManager::finished,
0207                 d->mServiceDescriptionParsers[serviceJustCreated.serviceId()].get(), &UpnpServiceDescriptionParser::finishedDownload);
0208 
0209             d->mServiceDescriptionParsers[serviceJustCreated.serviceId()]->downloadServiceDescription(serviceUrl);
0210         }
0211     }
0212 }
0213 
0214 #include "moc_upnpdevicedescriptionparser.cpp"