25 #include <QStringList>
69 m_procReqLineExp (
"[ \r\n][ \r\n]*"),
70 m_nPort ( SSDP_PORT ),
71 m_nSearchPort ( SSDP_SEARCHPORT ),
73 m_pNotifyTask ( NULL ),
74 m_bAnnouncementsEnabled(
false ),
75 m_bTermRequested (
false ),
76 m_lock ( QMutex::NonRecursive )
78 LOG(VB_UPNP, LOG_NOTICE,
"Starting up SSDP Thread..." );
97 QHostAddress ip4addr( QHostAddress::Any );
108 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Starting soon" );
117 LOG(VB_UPNP, LOG_NOTICE,
"Shutting Down SSDP Thread..." );
130 for (
int nIdx = 0; nIdx < (
int)NumberOfSockets; nIdx++ )
138 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Terminated." );
156 LOG(VB_UPNP, LOG_INFO,
157 "SSDP::EnableNotifications() - creating new task");
164 LOG(VB_UPNP, LOG_INFO,
165 "SSDP::EnableNotifications() - sending NTS_byebye");
176 LOG(VB_UPNP, LOG_INFO,
"SSDP::EnableNotifications() - sending NTS_alive");
182 LOG(VB_UPNP, LOG_INFO,
183 "SSDP::EnableNotifications() - Task added to UPnP queue");
208 timeout_secs = std::max(std::min(timeout_secs, 5U), 1U);
209 QString rRequest = QString(
"M-SEARCH * HTTP/1.1\r\n"
210 "HOST: 239.255.255.250:1900\r\n"
211 "MAN: \"ssdp:discover\"\r\n"
215 .arg(timeout_secs).arg(sST);
217 LOG(VB_UPNP, LOG_DEBUG, QString(
"\n\n%1\n").arg(rRequest));
219 QByteArray sRequest = rRequest.toUtf8();
228 QHostAddress address;
229 address.setAddress( SSDP_GROUP );
231 int nSize = sRequest.size();
234 sRequest.size(), address, SSDP_PORT ) != nSize)
235 LOG(VB_GENERAL, LOG_INFO,
236 "SSDP::PerformSearch - did not write entire buffer.");
241 sRequest.size(), address, SSDP_PORT ) != nSize)
242 LOG(VB_GENERAL, LOG_INFO,
243 "SSDP::PerformSearch - did not write entire buffer.");
255 struct timeval timeout;
257 LOG(VB_UPNP, LOG_INFO,
"SSDP::Run - SSDP Thread Started." );
267 FD_ZERO( &read_set );
269 for (
uint nIdx = 0; nIdx < NumberOfSockets; nIdx++ )
273 FD_SET(
m_Sockets[ nIdx ]->socket(), &read_set );
274 nMaxSocket = max(
m_Sockets[ nIdx ]->socket(), nMaxSocket );
277 if (
m_Sockets[ nIdx ]->bytesAvailable() > 0)
279 LOG(VB_GENERAL, LOG_DEBUG,
280 QString(
"Found Extra data before select: %1")
292 count = select(nMaxSocket + 1, &read_set, NULL, NULL, &timeout);
293 for (
int nIdx = 0; count && nIdx < (
int)NumberOfSockets; nIdx++ )
296 FD_ISSET(
m_Sockets[nIdx]->socket(), &read_set))
299 LOG(VB_GENERAL, LOG_DEBUG, QString(
"FD_ISSET( %1 )").arg(nIdx));
321 buffer.resize(nBytes);
326 long ret = pSocket->
readBlock( buffer.data() + nRead, nBytes - nRead );
329 LOG(VB_GENERAL, LOG_ERR, QString(
"Socket readBlock error %1")
330 .arg(pSocket->
error()));
339 LOG(VB_SOCKET, LOG_WARNING,
340 QString(
"%1 bytes reported available, "
341 "but only %2 bytes read.")
342 .arg(nBytes).arg(nRead));
344 buffer.resize(nBytes);
348 while (nRead < nBytes);
350 if (buffer.isEmpty())
354 quint16 peerPort = pSocket->
peerPort ();
357 QString str = QString(buffer.constData());
358 QStringList lines = str.split(
"\r\n", QString::SkipEmptyParts);
359 QString sRequestLine = lines.size() ? lines[0] :
"";
367 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessData - requestLine: %1")
378 for ( QStringList::Iterator it = lines.begin();
379 it != lines.end(); ++it )
382 QString sName = sLine.section(
':', 0, 0 ).trimmed();
383 QString sValue = sLine.section(
':', 1 );
385 sValue.truncate( sValue.length() );
387 if ((sName.length() != 0) && (sValue.length() !=0))
388 headers.insert( sName.toLower(), sValue.trimmed() );
392 pSocket->SetDestAddress( peerAddress, peerPort );
424 LOG(VB_UPNP, LOG_ERR,
425 "SSPD::ProcessData - Unknown request Type.");
437 QStringList tokens = sLine.split(
m_procReqLineExp, QString::SkipEmptyParts);
446 if ( sLine.startsWith( QString(
"HTTP/") ))
450 if (tokens.count() > 0)
465 const QString &sKey,
const QString &sDefault )
467 QStringMap::const_iterator it = headers.find( sKey.toLower() );
469 if ( it == headers.end())
480 QHostAddress peerAddress,
488 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessSearchrequest : [%1] MX=%2")
496 if ( pRequest->m_sMethod !=
"*" )
return false;
497 if ( pRequest->m_sProtocol !=
"HTTP" )
return false;
498 if ( pRequest->m_nMajor != 1 )
return false;
500 if ( sMAN !=
"\"ssdp:discover\"" )
return false;
501 if ( sST.length() == 0 )
return false;
502 if ( sMX.length() == 0 )
return false;
503 if ((nMX = sMX.toInt()) == 0 )
return false;
504 if ( nMX < 0 )
return false;
510 nMX = (nMX > 120) ? 120 : nMX;
512 int nNewMX = (
int)(0 + ((
unsigned short)
random() % nMX)) * 1000;
518 if ((sST ==
"ssdp:all") || (sST ==
"upnp:rootdevice"))
521 peerAddress, peerPort, sST,
544 if (sUDN.length() > 0)
577 LOG(VB_UPNP, LOG_DEBUG,
578 QString(
"SSDP::ProcessSearchResponse ...\n"
583 .arg(sDescURL).arg(sST).arg(sUSN).arg(sCache));
585 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
590 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
593 int nSecs = sCache.mid( nPos+1 ).toInt();
612 LOG(VB_UPNP, LOG_DEBUG,
613 QString(
"SSDP::ProcessNotify ...\n"
619 .arg(sDescURL).arg(sNTS).arg(sNT).arg(sUSN).arg(sCache));
621 if (sNTS.contains(
"ssdp:alive"))
623 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
628 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
631 int nSecs = sCache.mid( nPos+1 ).toInt();
639 if ( sNTS.contains(
"ssdp:byebye" ) )
663 m_nServicePort(nServicePort)
698 return QStringList(
"/" );
732 QString sUserAgent = pRequest->
GetHeaderValue(
"User-Agent",
"" );
734 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceDesc - " +
735 QString(
"Host=%1 Port=%2 UserAgent=%3" )
739 QTextStream stream( &(pRequest->
m_response) );
760 LOG(VB_UPNP, LOG_DEBUG,
761 QString(
"SSDPExtension::GetFile( %1 ) - Exists")
767 =
"no-cache=\"Ext\", max-age = 5000";
771 LOG(VB_UPNP, LOG_ERR,
772 QString(
"SSDPExtension::GetFile( %1 ) - Not Found")
784 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceList");
787 QTextStream os(&sXML, QIODevice::WriteOnly);
789 uint nDevCount, nEntryCount;
794 NameValue(
"DeviceCount", (
int)nDevCount));
798 NameValue(
"CacheEntriesFound", (
int)nEntryCount));