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 QStringList lines = str.split(
"\r\n", Qt::SkipEmptyParts);
390 QString sRequestLine = !lines.empty() ? lines[0] :
"";
392 if (!lines.isEmpty())
399 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessData - requestLine: %1")
410 for (
const auto& sLine : std::as_const(lines))
412 QString sName = sLine.section(
':', 0, 0 ).trimmed();
413 QString sValue = sLine.section(
':', 1 );
415 sValue.truncate( sValue.length() );
417 if ((sName.length() != 0) && (sValue.length() !=0))
418 headers.insert( sName.toLower(), sValue.trimmed() );
422 pSocket->SetDestAddress( peerAddress, peerPort );
454 LOG(VB_UPNP, LOG_ERR,
455 "SSPD::ProcessData - Unknown request Type.");
476 if ( sLine.startsWith( QString(
"HTTP/") ))
479 if (tokens.count() > 0)
493 const QString &sKey,
const QString &sDefault )
495 QStringMap::const_iterator it =
headers.find( sKey.toLower() );
508 const QHostAddress& peerAddress,
509 quint16 peerPort )
const
514 std::chrono::seconds nMX = 0s;
516 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessSearchrequest : [%1] MX=%2")
524 if ( pRequest->m_sMethod !=
"*" )
return false;
525 if ( pRequest->m_sProtocol !=
"HTTP" )
return false;
526 if ( pRequest->m_nMajor != 1 )
return false;
528 if ( sMAN !=
"\"ssdp:discover\"" )
return false;
529 if ( sST.length() == 0 )
return false;
530 if ( sMX.length() == 0 )
return false;
531 nMX = std::chrono::seconds(sMX.toInt());
532 if ( nMX <= 0s )
return false;
540 auto nNewMX = std::chrono::milliseconds(
MythRandom(0, (duration_cast<std::chrono::milliseconds>(nMX)).count()));
546 if ((sST ==
"ssdp:all") || (sST ==
"upnp:rootdevice"))
549 peerAddress, peerPort, sST,
555 pTask->Execute(
nullptr );
572 if (sUDN.length() > 0)
575 peerPort, sST, sUDN );
579 pTask->Execute(
nullptr );
602 LOG(VB_UPNP, LOG_DEBUG,
603 QString(
"SSDP::ProcessSearchResponse ...\n"
608 .arg(sDescURL, sST, sUSN, sCache));
610 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
615 nPos = sCache.indexOf(
"=", nPos);
619 auto nSecs = std::chrono::seconds(sCache.mid( nPos+1 ).toInt());
638 LOG(VB_UPNP, LOG_DEBUG,
639 QString(
"SSDP::ProcessNotify ...\n"
645 .arg(sDescURL, sNTS, sNT, sUSN, sCache));
647 if (sNTS.contains(
"ssdp:alive"))
649 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
654 nPos = sCache.indexOf(
"=", nPos);
658 auto nSecs = std::chrono::seconds(sCache.mid( nPos+1 ).toInt());
666 if ( sNTS.contains(
"ssdp:byebye" ) )
690 m_nServicePort(nServicePort)
717 return QStringList(
"/" );
753 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceDesc - " +
754 QString(
"Host=%1 Port=%2 UserAgent=%3" )
758 QTextStream stream( &(pRequest->
m_response) );
778 LOG(VB_UPNP, LOG_DEBUG,
779 QString(
"SSDPExtension::GetFile( %1 ) - Exists")
785 =
"no-cache=\"Ext\", max-age = 7200";
791 LOG(VB_UPNP, LOG_ERR,
792 QString(
"SSDPExtension::GetFile( %1 ) - Not Found")
804 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceList");
807 QTextStream os(&sXML, QIODevice::WriteOnly);
810 uint nEntryCount = 0;
815 NameValue(
"DeviceCount", (
int)nDevCount));
819 NameValue(
"CacheEntriesFound", (
int)nEntryCount));