File indexing completed on 2024-05-05 08:56:17
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"