32 #include <QStringList>
35 #include <sys/select.h>
79 LOG(VB_UPNP, LOG_NOTICE,
"Starting up SSDP Thread..." );
100 QHostAddress ip4addr( QHostAddress::Any );
111 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Starting soon" );
120 LOG(VB_UPNP, LOG_NOTICE,
"Shutting Down SSDP Thread..." );
136 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Terminated." );
154 LOG(VB_UPNP, LOG_INFO,
155 "SSDP::EnableNotifications() - creating new task");
162 LOG(VB_UPNP, LOG_INFO,
163 "SSDP::EnableNotifications() - sending NTS_byebye");
174 LOG(VB_UPNP, LOG_INFO,
"SSDP::EnableNotifications() - sending NTS_alive");
180 LOG(VB_UPNP, LOG_INFO,
181 "SSDP::EnableNotifications() - Task added to UPnP queue");
207 QString rRequest = QString(
"M-SEARCH * HTTP/1.1\r\n"
208 "HOST: 239.255.255.250:1900\r\n"
209 "MAN: \"ssdp:discover\"\r\n"
213 .arg(
timeout.count()).arg(sST);
215 LOG(VB_UPNP, LOG_DEBUG, QString(
"\n\n%1\n").arg(rRequest));
217 QByteArray sRequest = rRequest.toUtf8();
220 if ( !pSocket->isValid() )
222 pSocket->setProtocol(MSocketDevice::IPv4);
223 pSocket->setSocket(pSocket->createNewSocket(), MSocketDevice::Datagram);
226 QHostAddress address;
229 int nSize = sRequest.size();
231 if ( pSocket->writeBlock( sRequest.data(),
232 sRequest.size(), address,
SSDP_PORT ) != nSize)
233 LOG(VB_GENERAL, LOG_INFO,
234 "SSDP::PerformSearch - did not write entire buffer.");
236 std::this_thread::sleep_for(std::chrono::milliseconds(
MythRandom(0, 250)));
238 if ( pSocket->writeBlock( sRequest.data(),
239 sRequest.size(), address,
SSDP_PORT ) != nSize)
240 LOG(VB_GENERAL, LOG_INFO,
241 "SSDP::PerformSearch - did not write entire buffer.");
255 LOG(VB_UPNP, LOG_INFO,
"SSDP::Run - SSDP Thread Started." );
265 FD_ZERO( &read_set );
269 if (socket !=
nullptr && socket->socket() >= 0)
271 FD_SET( socket->socket(), &read_set );
272 nMaxSocket = std::max( socket->socket(), nMaxSocket );
275 if (socket->bytesAvailable() > 0)
277 LOG(VB_GENERAL, LOG_DEBUG,
278 QString(
"Found Extra data before select: %1")
289 int count = select(nMaxSocket + 1, &read_set,
nullptr,
nullptr, &
timeout);
294 bool cond2 = cond1 &&
m_sockets[nIdx]->socket() >= 0;
295 bool cond3 = cond2 && FD_ISSET(
m_sockets[nIdx]->socket(), &read_set);
300 LOG(VB_GENERAL, LOG_DEBUG, QString(
"FD_ISSET( %1 )").arg(nIdx));
319 long nBytes = pSocket->bytesAvailable();
324 bool didDoRead =
false;
329 LOG(VB_UPNP, LOG_WARNING, QString(
"SSDP: Received 0 byte UDP message"));
332 while (((nBytes = pSocket->bytesAvailable()) > 0 || (nBytes == 0 && !didDoRead)) && !
m_bTermRequested)
334 buffer.resize(nBytes);
339 long ret = pSocket->readBlock( buffer.data() + nRead, nBytes - nRead );
344 #
if EAGAIN != EWOULDBLOCK
345 || errno == EWOULDBLOCK
352 buffer.resize(nBytes);
356 std::this_thread::sleep_for(10ms);
359 LOG(VB_GENERAL, LOG_ERR, QString(
"Socket readBlock error %1")
360 .arg(pSocket->error()));
368 if (0 == ret && nBytes != 0)
370 LOG(VB_SOCKET, LOG_WARNING,
371 QString(
"%1 bytes reported available, "
372 "but only %2 bytes read.")
373 .arg(nBytes).arg(nRead));
375 buffer.resize(nBytes);
379 while (nRead < nBytes);
381 if (buffer.isEmpty())
384 QHostAddress peerAddress = pSocket->peerAddress();
385 quint16 peerPort = pSocket->peerPort ();
388 QString str = QString(buffer.constData());
389 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
390 QStringList lines = str.split(
"\r\n", QString::SkipEmptyParts);
392 QStringList lines = str.split(
"\r\n", Qt::SkipEmptyParts);
394 QString sRequestLine = !lines.empty() ? lines[0] :
"";
396 if (!lines.isEmpty())
403 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessData - requestLine: %1")
414 for (
const auto& sLine : qAsConst(lines))
416 QString sName = sLine.section(
':', 0, 0 ).trimmed();
417 QString sValue = sLine.section(
':', 1 );
419 sValue.truncate( sValue.length() );
421 if ((sName.length() != 0) && (sValue.length() !=0))
422 headers.insert( sName.toLower(), sValue.trimmed() );
426 pSocket->SetDestAddress( peerAddress, peerPort );
458 LOG(VB_UPNP, LOG_ERR,
459 "SSPD::ProcessData - Unknown request Type.");
471 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
472 QStringList tokens = sLine.split(
m_procReqLineExp, QString::SkipEmptyParts);
484 if ( sLine.startsWith( QString(
"HTTP/") ))
487 if (tokens.count() > 0)
501 const QString &sKey,
const QString &sDefault )
503 QStringMap::const_iterator it = headers.find( sKey.toLower() );
505 if ( it == headers.end())
516 const QHostAddress& peerAddress,
517 quint16 peerPort )
const
522 std::chrono::seconds nMX = 0s;
524 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessSearchrequest : [%1] MX=%2")
532 if ( pRequest->m_sMethod !=
"*" )
return false;
533 if ( pRequest->m_sProtocol !=
"HTTP" )
return false;
534 if ( pRequest->m_nMajor != 1 )
return false;
536 if ( sMAN !=
"\"ssdp:discover\"" )
return false;
537 if ( sST.length() == 0 )
return false;
538 if ( sMX.length() == 0 )
return false;
539 if ((nMX = std::chrono::seconds(sMX.toInt())) == 0s)
return false;
540 if ( nMX < 0s )
return false;
546 nMX = std::clamp(nMX, 0s, 120s);
548 auto nNewMX = std::chrono::milliseconds(
MythRandom(0, (duration_cast<std::chrono::milliseconds>(nMX)).count()));
554 if ((sST ==
"ssdp:all") || (sST ==
"upnp:rootdevice"))
557 peerAddress, peerPort, sST,
563 pTask->Execute(
nullptr );
580 if (sUDN.length() > 0)
583 peerPort, sST, sUDN );
587 pTask->Execute(
nullptr );
610 LOG(VB_UPNP, LOG_DEBUG,
611 QString(
"SSDP::ProcessSearchResponse ...\n"
616 .arg(sDescURL, sST, sUSN, sCache));
618 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
623 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
626 auto nSecs = std::chrono::seconds(sCache.mid( nPos+1 ).toInt());
645 LOG(VB_UPNP, LOG_DEBUG,
646 QString(
"SSDP::ProcessNotify ...\n"
652 .arg(sDescURL, sNTS, sNT, sUSN, sCache));
654 if (sNTS.contains(
"ssdp:alive"))
656 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
661 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
664 auto nSecs = std::chrono::seconds(sCache.mid( nPos+1 ).toInt());
672 if ( sNTS.contains(
"ssdp:byebye" ) )
696 m_nServicePort(nServicePort)
723 return QStringList(
"/" );
759 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceDesc - " +
760 QString(
"Host=%1 Port=%2 UserAgent=%3" )
764 QTextStream stream( &(pRequest->
m_response) );
784 LOG(VB_UPNP, LOG_DEBUG,
785 QString(
"SSDPExtension::GetFile( %1 ) - Exists")
791 =
"no-cache=\"Ext\", max-age = 7200";
797 LOG(VB_UPNP, LOG_ERR,
798 QString(
"SSDPExtension::GetFile( %1 ) - Not Found")
810 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceList");
813 QTextStream os(&sXML, QIODevice::WriteOnly);
816 uint nEntryCount = 0;
821 NameValue(
"DeviceCount", (
int)nDevCount));
825 NameValue(
"CacheEntriesFound", (
int)nEntryCount));