File indexing completed on 2024-04-28 04:44:22

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 "upnpssdpengine.h"
0008 
0009 #include "ssdplogging.h"
0010 
0011 #include "upnpdiscoveryresult.h"
0012 
0013 #include "upnpabstractdevice.h"
0014 #include "upnpabstractservice.h"
0015 
0016 #include "upnpdevicedescription.h"
0017 #include "upnpservicedescription.h"
0018 
0019 #include "ssdplogging.h"
0020 
0021 #include <QHostAddress>
0022 #include <QNetworkInformation>
0023 #include <QNetworkInterface>
0024 #include <QUdpSocket>
0025 
0026 #include <QHash>
0027 #include <QLoggingCategory>
0028 #include <QSet>
0029 #include <QSharedPointer>
0030 #include <QSysInfo>
0031 #include <QUrl>
0032 
0033 #include <sys/socket.h>
0034 #include <sys/types.h>
0035 
0036 class UpnpSsdpEnginePrivate
0037 {
0038 public:
0039     QHash<QString, UpnpDiscoveryResult> mDiscoveryResults;
0040 
0041     QList<QPointer<QUdpSocket>> mSsdpQuerySocket;
0042 
0043     QList<QPointer<QUdpSocket>> mSsdpStandardSocket;
0044 
0045     QString mServerInformation;
0046 
0047     QString mActiveConfiguration;
0048 
0049     QTimer mTimeoutTimer;
0050 
0051     quint16 mPortNumber = 1900;
0052 
0053     bool mCanExportServices = true;
0054 };
0055 
0056 UpnpSsdpEngine::UpnpSsdpEngine(QObject *parent)
0057     : QObject(parent)
0058     , d(std::make_unique<UpnpSsdpEnginePrivate>())
0059 {
0060     if (QNetworkInformation::loadDefaultBackend()) {
0061         qCInfo(orgKdeUpnpLibQtSsdp) << "network connectivity information is available";
0062         connect(QNetworkInformation::instance(), &QNetworkInformation::reachabilityChanged, this, &UpnpSsdpEngine::networkReachabilityChanged);
0063     } else {
0064         qCWarning(orgKdeUpnpLibQtSsdp) << "cannot get network connectivity information";
0065     }
0066 
0067     connect(&d->mTimeoutTimer, &QTimer::timeout, this, &UpnpSsdpEngine::discoveryResultTimeout);
0068     d->mTimeoutTimer.setSingleShot(false);
0069     d->mTimeoutTimer.start(1000);
0070 }
0071 
0072 void UpnpSsdpEngine::initialize()
0073 {
0074     reconfigureNetwork();
0075 }
0076 
0077 UpnpSsdpEngine::~UpnpSsdpEngine() = default;
0078 
0079 bool UpnpSsdpEngine::port() const
0080 {
0081     return d->mPortNumber;
0082 }
0083 
0084 void UpnpSsdpEngine::setPort(quint16 value)
0085 {
0086     if (d->mPortNumber == value) {
0087         return;
0088     }
0089 
0090     d->mPortNumber = value;
0091     Q_EMIT portChanged();
0092 }
0093 
0094 bool UpnpSsdpEngine::canExportServices() const
0095 {
0096     return d->mCanExportServices;
0097 }
0098 
0099 void UpnpSsdpEngine::setCanExportServices(bool value)
0100 {
0101     if (d->mCanExportServices == value) {
0102         return;
0103     }
0104 
0105     d->mCanExportServices = value;
0106     Q_EMIT canExportServicesChanged();
0107 }
0108 
0109 QList<UpnpDiscoveryResult> UpnpSsdpEngine::existingServices() const
0110 {
0111     auto result = QList<UpnpDiscoveryResult>();
0112 
0113     for (const auto &oneService : qAsConst(d->mDiscoveryResults)) {
0114         result.push_back(oneService);
0115     }
0116 
0117     return result;
0118 }
0119 
0120 bool UpnpSsdpEngine::searchUpnp(SEARCH_TYPE searchType, const QString &searchCriteria, int maxDelay)
0121 {
0122     switch (searchType) {
0123     case AllDevices:
0124         return searchAllUpnpDevice(maxDelay);
0125     case RootDevices:
0126         return searchAllRootDevice(maxDelay);
0127     case DeviceByUUID:
0128         return searchByDeviceUUID(searchCriteria, maxDelay);
0129     case DeviceByType:
0130         return searchByDeviceType(searchCriteria, maxDelay);
0131     case ServiceByType:
0132         return searchByServiceType(searchCriteria, maxDelay);
0133     }
0134 
0135     return false;
0136 }
0137 
0138 bool UpnpSsdpEngine::searchAllUpnpDevice(int maxDelay)
0139 {
0140     QByteArray allDiscoveryMessage;
0141 
0142     allDiscoveryMessage += "M-SEARCH * HTTP/1.1\r\n";
0143     allDiscoveryMessage += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0144     allDiscoveryMessage += "MAN: \"ssdp:discover\"\r\n";
0145     allDiscoveryMessage += "MX: " + QByteArray::number(maxDelay) + "\r\n";
0146     allDiscoveryMessage += "ST: ssdp:all\r\n\r\n";
0147 
0148     qint64 result = -1;
0149 
0150     for (auto &oneSocket : d->mSsdpQuerySocket) {
0151         result = oneSocket->writeDatagram(allDiscoveryMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0152         qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::searchAllUpnpDevice" << result << oneSocket->errorString();
0153     }
0154 
0155     return result != -1;
0156 }
0157 
0158 bool UpnpSsdpEngine::searchAllRootDevice(int maxDelay)
0159 {
0160     QByteArray allDiscoveryMessage;
0161 
0162     allDiscoveryMessage += "M-SEARCH * HTTP/1.1\r\n";
0163     allDiscoveryMessage += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0164     allDiscoveryMessage += "MAN: \"ssdp:discover\"\r\n";
0165     allDiscoveryMessage += "MX: " + QByteArray::number(maxDelay) + "\r\n";
0166     allDiscoveryMessage += "ST: upnp:rootdevice\r\n\r\n";
0167 
0168     qint64 result = -1;
0169 
0170     for (auto &oneSocket : d->mSsdpQuerySocket) {
0171         result = oneSocket->writeDatagram(allDiscoveryMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0172     }
0173 
0174     return result != -1;
0175 }
0176 
0177 bool UpnpSsdpEngine::searchByDeviceUUID(const QString &uuid, int maxDelay)
0178 {
0179     QByteArray allDiscoveryMessage;
0180 
0181     allDiscoveryMessage += "M-SEARCH * HTTP/1.1\r\n";
0182     allDiscoveryMessage += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0183     allDiscoveryMessage += "MAN: \"ssdp:discover\"\r\n";
0184     allDiscoveryMessage += "MX: " + QByteArray::number(maxDelay) + "\r\n";
0185     allDiscoveryMessage += "ST: uuid:" + uuid.toLatin1() + "\r\n\r\n";
0186 
0187     qint64 result = -1;
0188 
0189     for (auto &oneSocket : d->mSsdpQuerySocket) {
0190         result = oneSocket->writeDatagram(allDiscoveryMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0191     }
0192 
0193     return result != -1;
0194 }
0195 
0196 bool UpnpSsdpEngine::searchByDeviceType(const QString &upnpDeviceType, int maxDelay)
0197 {
0198     QByteArray allDiscoveryMessage;
0199 
0200     allDiscoveryMessage += "M-SEARCH * HTTP/1.1\r\n";
0201     allDiscoveryMessage += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0202     allDiscoveryMessage += "MAN: \"ssdp:discover\"\r\n";
0203     allDiscoveryMessage += "MX: " + QByteArray::number(maxDelay) + "\r\n";
0204     allDiscoveryMessage += "ST: urn:" + upnpDeviceType.toLatin1() + "\r\n\r\n";
0205 
0206     qint64 result = -1;
0207 
0208     for (auto &oneSocket : d->mSsdpQuerySocket) {
0209         result = oneSocket->writeDatagram(allDiscoveryMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0210     }
0211 
0212     return result != -1;
0213 }
0214 
0215 bool UpnpSsdpEngine::searchByServiceType(const QString &upnpServiceType, int maxDelay)
0216 {
0217     QByteArray allDiscoveryMessage;
0218 
0219     allDiscoveryMessage += "M-SEARCH * HTTP/1.1\r\n";
0220     allDiscoveryMessage += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0221     allDiscoveryMessage += "MAN: \"ssdp:discover\"\r\n";
0222     allDiscoveryMessage += "MX: " + QByteArray::number(maxDelay) + "\r\n";
0223     allDiscoveryMessage += "ST: urn:" + upnpServiceType.toLatin1() + "\r\n\r\n";
0224 
0225     qint64 result = -1;
0226 
0227     for (auto &oneSocket : d->mSsdpQuerySocket) {
0228         result = oneSocket->writeDatagram(allDiscoveryMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0229     }
0230 
0231     return result != -1;
0232 }
0233 
0234 void UpnpSsdpEngine::subscribeDevice(UpnpAbstractDevice *device)
0235 {
0236     connect(this, &UpnpSsdpEngine::newSearchQuery, device, &UpnpAbstractDevice::newSearchQuery);
0237     publishDevice(device);
0238 }
0239 
0240 void UpnpSsdpEngine::publishDevice(UpnpAbstractDevice *device)
0241 {
0242     QByteArray allDiscoveryMessageCommonContent;
0243 
0244     allDiscoveryMessageCommonContent += "NOTIFY * HTTP/1.1\r\n";
0245     allDiscoveryMessageCommonContent += "HOST: 239.255.255.250:" + QByteArray::number(d->mPortNumber) + "\r\n";
0246     allDiscoveryMessageCommonContent += "CACHE-CONTROL: max-age=" + QByteArray::number(device->description().cacheControl()) + "\r\n";
0247     allDiscoveryMessageCommonContent += "NTS: ssdp:alive\r\n";
0248     allDiscoveryMessageCommonContent += "SERVER: " + d->mServerInformation.toLatin1() + " " + device->description().modelName().toLatin1() + " " + device->description().modelNumber().toLatin1() + "\r\n";
0249 
0250     QByteArray rootDeviceMessage(allDiscoveryMessageCommonContent);
0251     rootDeviceMessage += "NT: upnp:rootdevice\r\n";
0252     rootDeviceMessage += "USN: uuid:" + device->description().UDN().toLatin1() + "::upnp:rootdevice\r\n";
0253     rootDeviceMessage += "LOCATION: " + device->description().locationUrl().toString().toLatin1() + "\r\n";
0254     rootDeviceMessage += "\r\n";
0255 
0256     for (auto &oneSocket : d->mSsdpQuerySocket) {
0257         oneSocket->writeDatagram(rootDeviceMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0258     }
0259 
0260     QByteArray uuidDeviceMessage(allDiscoveryMessageCommonContent);
0261     uuidDeviceMessage += "NT: uuid:" + device->description().UDN().toLatin1() + "\r\n";
0262     uuidDeviceMessage += "USN: uuid:" + device->description().UDN().toLatin1() + "\r\n";
0263     uuidDeviceMessage += "LOCATION: " + device->description().locationUrl().toString().toLatin1() + "\r\n";
0264     uuidDeviceMessage += "\r\n";
0265 
0266     for (auto &oneSocket : d->mSsdpQuerySocket) {
0267         oneSocket->writeDatagram(uuidDeviceMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0268     }
0269 
0270     QByteArray deviceMessage(allDiscoveryMessageCommonContent);
0271     deviceMessage += "NT: " + device->description().deviceType().toLatin1() + "\r\n";
0272     deviceMessage += "USN: uuid:" + device->description().UDN().toLatin1() + "::" + device->description().deviceType().toLatin1() + "\r\n";
0273     deviceMessage += "LOCATION: " + device->description().locationUrl().toString().toLatin1() + "\r\n";
0274     deviceMessage += "\r\n";
0275 
0276     for (auto &oneSocket : d->mSsdpQuerySocket) {
0277         oneSocket->writeDatagram(deviceMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0278     }
0279 
0280     const auto &servicesList = device->description().services();
0281     for (const auto &oneService : servicesList) {
0282         QByteArray deviceMessage(allDiscoveryMessageCommonContent);
0283         deviceMessage += "NT: " + oneService.serviceType().toLatin1() + "\r\n";
0284         deviceMessage += "USN: uuid:" + device->description().UDN().toLatin1() + "::" + oneService.serviceType().toLatin1() + "\r\n";
0285         deviceMessage += "LOCATION: " + device->description().locationUrl().toString().toLatin1() + "\r\n";
0286         deviceMessage += "\r\n";
0287 
0288         for (auto &oneSocket : d->mSsdpQuerySocket) {
0289             oneSocket->writeDatagram(deviceMessage, QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber);
0290         }
0291     }
0292 }
0293 
0294 void UpnpSsdpEngine::standardReceivedData()
0295 {
0296     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::standardReceivedData";
0297     auto *receiverSocket = qobject_cast<QUdpSocket *>(sender());
0298 
0299     while (receiverSocket->hasPendingDatagrams()) {
0300         QByteArray datagram;
0301         datagram.resize(static_cast<int>(receiverSocket->pendingDatagramSize()));
0302         QHostAddress sender;
0303         quint16 senderPort;
0304 
0305         receiverSocket->readDatagram(datagram.data(), datagram.size(),
0306             &sender, &senderPort);
0307 
0308         parseSsdpDatagram(datagram);
0309     }
0310 }
0311 
0312 void UpnpSsdpEngine::queryReceivedData()
0313 {
0314     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::queryReceivedData";
0315     auto *receiverSocket = qobject_cast<QUdpSocket *>(sender());
0316 
0317     while (receiverSocket->hasPendingDatagrams()) {
0318         QByteArray datagram;
0319         datagram.resize(static_cast<int>(receiverSocket->pendingDatagramSize()));
0320         QHostAddress sender;
0321         quint16 senderPort;
0322 
0323         receiverSocket->readDatagram(datagram.data(), datagram.size(),
0324             &sender, &senderPort);
0325 
0326         parseSsdpDatagram(datagram);
0327     }
0328 }
0329 
0330 void UpnpSsdpEngine::discoveryResultTimeout()
0331 {
0332     auto now = QDateTime::currentDateTime();
0333 
0334     auto timedOutDiscoveryResults = QList<QString>();
0335 
0336     for (const auto &itDiscovery : qAsConst(d->mDiscoveryResults)) {
0337         if (now > itDiscovery.validityTimestamp()) {
0338             qCDebug(orgKdeUpnpLibQtSsdp()) << "remove service due to timeout" << itDiscovery;
0339             Q_EMIT removedService(itDiscovery);
0340 
0341             timedOutDiscoveryResults.push_back(itDiscovery.usn());
0342         }
0343     }
0344 
0345     for (const auto &removedUsn : timedOutDiscoveryResults) {
0346         auto itDiscovery = d->mDiscoveryResults.find(removedUsn);
0347         if (itDiscovery != d->mDiscoveryResults.end()) {
0348             d->mDiscoveryResults.erase(itDiscovery);
0349         }
0350     }
0351 }
0352 
0353 void UpnpSsdpEngine::networkReachabilityChanged(QNetworkInformation::Reachability newReachability)
0354 {
0355     qCInfo(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::networkReachabilityChanged" << newReachability;
0356 
0357     reconfigureNetwork();
0358 
0359     Q_EMIT networkChanged();
0360 }
0361 
0362 void UpnpSsdpEngine::networkUpdateCompleted()
0363 {
0364     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::networkUpdateCompleted";
0365 }
0366 
0367 void UpnpSsdpEngine::reconfigureNetwork()
0368 {
0369     for (const auto &oneDevice : qAsConst(d->mDiscoveryResults)) {
0370         Q_EMIT removedService(oneDevice);
0371     }
0372     d->mDiscoveryResults.clear();
0373 
0374     const auto &allInterfaces = QNetworkInterface::allInterfaces();
0375     for (const auto &oneInterface : allInterfaces) {
0376         const auto &allAddresses = oneInterface.addressEntries();
0377         for (const auto &oneAddress : allAddresses) {
0378 
0379             if (oneAddress.ip().protocol() != QAbstractSocket::IPv4Protocol) {
0380                 continue;
0381             }
0382 
0383             qCDebug(orgKdeUpnpLibQtSsdp()) << "I have one address:" << oneInterface;
0384 
0385             if (oneInterface.addressEntries().isEmpty()) {
0386                 continue;
0387             }
0388 
0389             qCDebug(orgKdeUpnpLibQtSsdp()) << "try to open sockets";
0390             d->mSsdpQuerySocket.push_back({ new QUdpSocket });
0391             d->mSsdpStandardSocket.push_back({ new QUdpSocket });
0392 
0393             auto &newQuerySocket = d->mSsdpQuerySocket.last();
0394 
0395             connect(newQuerySocket.data(), &QUdpSocket::readyRead, this, &UpnpSsdpEngine::queryReceivedData);
0396 
0397             newQuerySocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 1);
0398             newQuerySocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 4);
0399 
0400             if (oneInterface.addressEntries().isEmpty()) {
0401                 continue;
0402             }
0403 
0404             auto result = newQuerySocket->bind(oneAddress.ip());
0405             qCDebug(orgKdeUpnpLibQtSsdp()) << "bind" << (result ? "true" : "false");
0406             result = newQuerySocket->joinMulticastGroup(QHostAddress(QStringLiteral("239.255.255.250")), oneInterface);
0407             qCDebug(orgKdeUpnpLibQtSsdp()) << "joinMulticastGroup" << (result ? "true" : "false") << newQuerySocket->errorString();
0408         }
0409     }
0410     {
0411         auto &newStandardSocket = d->mSsdpStandardSocket.last();
0412 
0413         connect(newStandardSocket.data(), &QUdpSocket::readyRead, this, &UpnpSsdpEngine::standardReceivedData);
0414 
0415         newStandardSocket->setSocketOption(QAbstractSocket::MulticastLoopbackOption, 1);
0416         newStandardSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 4);
0417 
0418         auto result = newStandardSocket->bind(QHostAddress(QStringLiteral("239.255.255.250")), d->mPortNumber, QAbstractSocket::ShareAddress);
0419         qCDebug(orgKdeUpnpLibQtSsdp()) << "bind" << (result ? "true" : "false");
0420         result = newStandardSocket->joinMulticastGroup(QHostAddress(QStringLiteral("239.255.255.250")));
0421         qCDebug(orgKdeUpnpLibQtSsdp()) << "joinMulticastGroup" << (result ? "true" : "false") << newStandardSocket->errorString();
0422     }
0423 
0424     d->mServerInformation = QSysInfo::kernelType() + QStringLiteral(" ") + QSysInfo::kernelVersion() + QStringLiteral(" UPnP/1.0 ");
0425 }
0426 
0427 void UpnpSsdpEngine::parseSsdpQueryDatagram(const QByteArray &datagram, const QList<QByteArray> &headers)
0428 {
0429     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::parseSsdpQueryDatagram" << headers;
0430     UpnpSearchQuery newSearch;
0431     bool hasAddress = false;
0432     bool hasAnswerDelay = false;
0433     bool hasSearchTarget = false;
0434 
0435     for (const auto &header : headers) {
0436         if (header.startsWith("HOST")) {
0437             QString hostName;
0438             if ((header)[4] == ' ') {
0439                 hostName = QString::fromLatin1(header.mid(7, header.length() - 8));
0440             } else {
0441                 hostName = QString::fromLatin1(header.mid(6, header.length() - 7));
0442             }
0443             auto addressParts = hostName.split(QStringLiteral(":"));
0444             newSearch.mSearchHostAddress.setAddress(addressParts.first());
0445             newSearch.mSearchHostPort = static_cast<uint16_t>(addressParts.last().toInt());
0446             hasAddress = true;
0447         }
0448         if (header.startsWith("MAN")) {
0449             if (!header.contains("\"ssdp:discover\"")) {
0450                 qCDebug(orgKdeUpnpLibQtSsdp()) << "not valid" << datagram;
0451                 return;
0452             }
0453         }
0454         if (header.startsWith("MX")) {
0455             if ((header)[2] == ' ') {
0456                 newSearch.mAnswerDelay = QString::fromLatin1(header.mid(4, header.length() - 5)).toInt();
0457             } else {
0458                 newSearch.mAnswerDelay = QString::fromLatin1(header.mid(3, header.length() - 4)).toInt();
0459             }
0460             hasAnswerDelay = true;
0461         }
0462         if (header.startsWith("ST")) {
0463             if ((header)[2] == ' ') {
0464                 newSearch.mSearchTarget = QString::fromLatin1(header.mid(5, header.length() - 6));
0465             } else {
0466                 newSearch.mSearchTarget = QString::fromLatin1(header.mid(4, header.length() - 5));
0467             }
0468             if (newSearch.mSearchTarget.startsWith(QStringLiteral("ssdp:all"))) {
0469                 newSearch.mSearchTargetType = SearchTargetType::All;
0470             } else if (newSearch.mSearchTarget.startsWith(QStringLiteral("upnp:rootdevice"))) {
0471                 newSearch.mSearchTargetType = SearchTargetType::RootDevice;
0472             } else if (newSearch.mSearchTarget.startsWith(QStringLiteral("uuid:"))) {
0473                 newSearch.mSearchTargetType = SearchTargetType::DeviceUUID;
0474             } else if (newSearch.mSearchTarget.startsWith(QStringLiteral("urn:"))) {
0475                 if (newSearch.mSearchTarget.contains(QStringLiteral("device:"))) {
0476                     newSearch.mSearchTargetType = SearchTargetType::DeviceType;
0477                 } else if (newSearch.mSearchTarget.contains(QStringLiteral("service:"))) {
0478                     newSearch.mSearchTargetType = SearchTargetType::ServiceType;
0479                 }
0480             }
0481             hasSearchTarget = true;
0482         }
0483     }
0484 
0485     if (hasAddress && hasAnswerDelay && hasSearchTarget) {
0486         Q_EMIT newSearchQuery(this, newSearch);
0487     }
0488 }
0489 
0490 void UpnpSsdpEngine::parseSsdpAnnounceDatagram(const QByteArray &datagram, const QList<QByteArray> &headers, SsdpMessageType messageType)
0491 {
0492     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::parseSsdpAnnounceDatagram" << headers;
0493     UpnpDiscoveryResult newDiscovery;
0494     newDiscovery.setNTS(NotificationSubType::Invalid);
0495 
0496     for (const auto &header : headers) {
0497         if (header.startsWith("LOCATION") || header.startsWith("Location")) {
0498             newDiscovery.setLocation(QString::fromLatin1(header.mid(9, header.length() - 10).trimmed()));
0499         }
0500         if (header.startsWith("HOST:") || header.startsWith("Host:")) {
0501             newDiscovery.setLocation(QString::fromLatin1(header.mid(6, header.length() - 7).trimmed()));
0502         }
0503         if (header.startsWith("USN:")) {
0504             newDiscovery.setUSN(QString::fromLatin1(header.mid(4, header.length() - 5).trimmed()));
0505         }
0506         if (messageType == SsdpMessageType::queryAnswer && header.startsWith("ST")) {
0507             newDiscovery.setNT(QString::fromLatin1(header.mid(3, header.length() - 4).trimmed()));
0508         }
0509         if (messageType == SsdpMessageType::announce && header.startsWith("NT:")) {
0510             newDiscovery.setNT(QString::fromLatin1(header.mid(3, header.length() - 4).trimmed()));
0511         }
0512         if (messageType == SsdpMessageType::announce && header.startsWith("NTS:")) {
0513             if (header.endsWith("ssdp:alive\r")) {
0514                 newDiscovery.setNTS(NotificationSubType::Alive);
0515             }
0516             if (header.endsWith("ssdp:byebye\r")) {
0517                 newDiscovery.setNTS(NotificationSubType::ByeBye);
0518             }
0519             if (header.endsWith("ssdp:discover\r")) {
0520                 newDiscovery.setNTS(NotificationSubType::Discover);
0521             }
0522         }
0523         if (header.startsWith("DATE:")) {
0524             newDiscovery.setAnnounceDate(QString::fromLatin1(header.mid(5, header.length() - 6).trimmed()));
0525         }
0526         if (header.startsWith("Cache-Control:") || header.startsWith("CACHE-CONTROL:")) {
0527             const QList<QByteArray> &splittedLine = header.mid(14, header.length() - 15).split('=');
0528             if (splittedLine.size() == 2) {
0529                 newDiscovery.setCacheDuration(splittedLine.last().trimmed().toInt());
0530             }
0531         }
0532     }
0533 
0534     if (newDiscovery.location().isEmpty() || newDiscovery.usn().isEmpty() || newDiscovery.nt().isEmpty() || (messageType == SsdpMessageType::announce && newDiscovery.nts() == NotificationSubType::Invalid)) {
0535         qCDebug(orgKdeUpnpLibQtSsdp()) << "not decoded" << datagram;
0536         return;
0537     }
0538 
0539     if (newDiscovery.nts() == NotificationSubType::Alive || messageType == SsdpMessageType::queryAnswer) {
0540         qCDebug(orgKdeUpnpLibQtSsdp()) << "valid service announce";
0541         auto itDiscovery = d->mDiscoveryResults.find(newDiscovery.usn());
0542 
0543         if (itDiscovery != d->mDiscoveryResults.end()) {
0544             qCDebug(orgKdeUpnpLibQtSsdp()) << "refresh existing service";
0545             *itDiscovery = newDiscovery;
0546         } else {
0547             d->mDiscoveryResults[newDiscovery.usn()] = newDiscovery;
0548             itDiscovery = d->mDiscoveryResults.find(newDiscovery.usn());
0549 
0550             qCDebug(orgKdeUpnpLibQtSsdp()) << "new service" << newDiscovery;
0551 
0552             Q_EMIT newService(newDiscovery);
0553         }
0554 
0555         qCDebug(orgKdeUpnpLibQtSsdp()) << datagram;
0556         qCDebug(orgKdeUpnpLibQtSsdp()) << "AnnounceDate" << newDiscovery.announceDate();
0557         qCDebug(orgKdeUpnpLibQtSsdp()) << "CacheDuration" << newDiscovery.cacheDuration();
0558 
0559         qCDebug(orgKdeUpnpLibQtSsdp()) << "new service";
0560         qCDebug(orgKdeUpnpLibQtSsdp()) << "DeviceId:" << newDiscovery.usn();
0561         qCDebug(orgKdeUpnpLibQtSsdp()) << "DeviceType:" << newDiscovery.nt();
0562         qCDebug(orgKdeUpnpLibQtSsdp()) << "Location:" << newDiscovery.location();
0563         qCDebug(orgKdeUpnpLibQtSsdp()) << "new service";
0564         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "DeviceId:" << searchResult->DeviceId;
0565         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "DeviceType:" << searchResult->DeviceType;
0566         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "ServiceType:" << searchResult->ServiceType;
0567         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "ServiceVer:" << searchResult->ServiceVer;
0568         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "Os:" << searchResult->Os;
0569         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "Date:" << searchResult->Date;
0570         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "Ext:" << searchResult->Ext;
0571         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "ErrCode:" << searchResult->ErrCode;
0572         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "Expires:" << searchResult->Expires;
0573         //        qCInfo(orgKdeUpnpLibQtSsdp()) << "DestAddr:" << QHostAddress(reinterpret_cast<const sockaddr *>(&searchResult->DestAddr));
0574     } else if (newDiscovery.nts() == NotificationSubType::ByeBye) {
0575         qCDebug(orgKdeUpnpLibQtSsdp()) << "ByeBye" << newDiscovery << d->mDiscoveryResults.keys();
0576         auto itDiscovery = d->mDiscoveryResults.find(newDiscovery.usn());
0577         if (itDiscovery != d->mDiscoveryResults.end()) {
0578             qCDebug(orgKdeUpnpLibQtSsdp()) << "removed device found";
0579             Q_EMIT removedService(newDiscovery);
0580 
0581             d->mDiscoveryResults.erase(itDiscovery);
0582         }
0583     }
0584 }
0585 
0586 void UpnpSsdpEngine::parseSsdpDatagram(const QByteArray &datagram)
0587 {
0588     qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::parseSsdpDatagram" << datagram;
0589     const QList<QByteArray> &headers(datagram.split('\n'));
0590 
0591     if (!headers.last().isEmpty()) {
0592         qCDebug(orgKdeUpnpLibQtSsdp()) << "UpnpSsdpEngine::parseSsdpDatagram"
0593                                        << "out";
0594         return;
0595     }
0596 
0597     SsdpMessageType messageType = SsdpMessageType::invalid;
0598 
0599     if (headers[0].startsWith("M-SEARCH * HTTP/1.1")) {
0600         messageType = SsdpMessageType::query;
0601     }
0602     if (headers[0].startsWith("HTTP/1.1 200 OK\r")) {
0603         messageType = SsdpMessageType::queryAnswer;
0604     }
0605     if (headers[0].startsWith("NOTIFY * HTTP/1.1")) {
0606         messageType = SsdpMessageType::announce;
0607     }
0608 
0609     if (messageType == SsdpMessageType::query) {
0610         parseSsdpQueryDatagram(datagram, headers);
0611     } else if (messageType == SsdpMessageType::announce || messageType == SsdpMessageType::queryAnswer) {
0612         parseSsdpAnnounceDatagram(datagram, headers, messageType);
0613     } else {
0614         qCDebug(orgKdeUpnpLibQtSsdp()) << "not decoded" << datagram;
0615         return;
0616     }
0617 }
0618 
0619 #include "moc_upnpssdpengine.cpp"