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"