27 #include <QStringList> 30 #include <sys/select.h> 57 return g_pSSDP ? g_pSSDP : (g_pSSDP =
new SSDP());
77 m_procReqLineExp (
"[ \r\n][ \r\n]*"),
81 m_pNotifyTask ( NULL ),
82 m_bAnnouncementsEnabled(
false ),
83 m_bTermRequested (
false ),
84 m_lock ( QMutex::NonRecursive )
86 LOG(VB_UPNP, LOG_NOTICE,
"Starting up SSDP Thread..." );
105 QHostAddress ip4addr( QHostAddress::Any );
116 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Starting soon" );
125 LOG(VB_UPNP, LOG_NOTICE,
"Shutting Down SSDP Thread..." );
146 LOG(VB_UPNP, LOG_INFO,
"SSDP Thread Terminated." );
164 LOG(VB_UPNP, LOG_INFO,
165 "SSDP::EnableNotifications() - creating new task");
172 LOG(VB_UPNP, LOG_INFO,
173 "SSDP::EnableNotifications() - sending NTS_byebye");
184 LOG(VB_UPNP, LOG_INFO,
"SSDP::EnableNotifications() - sending NTS_alive");
190 LOG(VB_UPNP, LOG_INFO,
191 "SSDP::EnableNotifications() - Task added to UPnP queue");
216 timeout_secs = std::max(std::min(timeout_secs, 5U), 1U);
217 QString rRequest = QString(
"M-SEARCH * HTTP/1.1\r\n" 218 "HOST: 239.255.255.250:1900\r\n" 219 "MAN: \"ssdp:discover\"\r\n" 223 .arg(timeout_secs).arg(sST);
225 LOG(VB_UPNP, LOG_DEBUG, QString(
"\n\n%1\n").arg(rRequest));
227 QByteArray sRequest = rRequest.toUtf8();
230 if ( !pSocket->isValid() )
232 pSocket->setProtocol(MSocketDevice::IPv4);
233 pSocket->setSocket(pSocket->createNewSocket(), MSocketDevice::Datagram);
236 QHostAddress address;
239 int nSize = sRequest.size();
241 if ( pSocket->writeBlock( sRequest.data(),
242 sRequest.size(), address,
SSDP_PORT ) != nSize)
243 LOG(VB_GENERAL, LOG_INFO,
244 "SSDP::PerformSearch - did not write entire buffer.");
246 std::this_thread::sleep_for(std::chrono::milliseconds(
random() % 250));
248 if ( pSocket->writeBlock( sRequest.data(),
249 sRequest.size(), address,
SSDP_PORT ) != nSize)
250 LOG(VB_GENERAL, LOG_INFO,
251 "SSDP::PerformSearch - did not write entire buffer.");
263 struct timeval timeout;
265 LOG(VB_UPNP, LOG_INFO,
"SSDP::Run - SSDP Thread Started." );
275 FD_ZERO( &read_set );
281 FD_SET(
m_Sockets[ nIdx ]->socket(), &read_set );
282 nMaxSocket = max(
m_Sockets[ nIdx ]->socket(), nMaxSocket );
285 if (
m_Sockets[ nIdx ]->bytesAvailable() > 0)
287 LOG(VB_GENERAL, LOG_DEBUG,
288 QString(
"Found Extra data before select: %1")
300 count = select(nMaxSocket + 1, &read_set, NULL, NULL, &timeout);
302 for (
int nIdx = 0; count && nIdx < (
int)NumberOfSockets; nIdx++ )
305 bool cond2 = cond1 &&
m_Sockets[nIdx]->socket() >= 0;
306 bool cond3 = cond2 && FD_ISSET(
m_Sockets[nIdx]->socket(), &read_set);
311 LOG(VB_GENERAL, LOG_DEBUG, QString(
"FD_ISSET( %1 )").arg(nIdx));
330 long nBytes = pSocket->bytesAvailable();
340 LOG(VB_UPNP, LOG_WARNING, QString(
"SSDP: Received 0 byte UDP message"));
343 while ((nBytes = pSocket->bytesAvailable()) > 0 || (nBytes == 0 && !didDoRead))
345 buffer.resize(nBytes);
350 long ret = pSocket->readBlock( buffer.data() + nRead, nBytes - nRead );
354 if (errno == EAGAIN || errno == EWOULDBLOCK)
359 buffer.resize(nBytes);
363 std::this_thread::sleep_for(std::chrono::milliseconds(10));
366 LOG(VB_GENERAL, LOG_ERR, QString(
"Socket readBlock error %1")
367 .arg(pSocket->error()));
375 if (0 == ret && nBytes != 0)
377 LOG(VB_SOCKET, LOG_WARNING,
378 QString(
"%1 bytes reported available, " 379 "but only %2 bytes read.")
380 .arg(nBytes).arg(nRead));
382 buffer.resize(nBytes);
386 while (nRead < nBytes);
388 if (buffer.isEmpty())
391 QHostAddress peerAddress = pSocket->peerAddress();
392 quint16 peerPort = pSocket->peerPort ();
395 QString str = QString(buffer.constData());
396 QStringList lines = str.split(
"\r\n", QString::SkipEmptyParts);
397 QString sRequestLine = lines.size() ? lines[0] :
"";
399 if (!lines.isEmpty())
406 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessData - requestLine: %1")
417 for ( QStringList::Iterator it = lines.begin();
418 it != lines.end(); ++it )
421 QString sName = sLine.section(
':', 0, 0 ).trimmed();
422 QString sValue = sLine.section(
':', 1 );
424 sValue.truncate( sValue.length() );
426 if ((sName.length() != 0) && (sValue.length() !=0))
427 headers.insert( sName.toLower(), sValue.trimmed() );
431 pSocket->SetDestAddress( peerAddress, peerPort );
463 LOG(VB_UPNP, LOG_ERR,
464 "SSPD::ProcessData - Unknown request Type.");
476 QStringList tokens = sLine.split(
m_procReqLineExp, QString::SkipEmptyParts);
485 if ( sLine.startsWith( QString(
"HTTP/") ))
489 if (tokens.count() > 0)
504 const QString &sKey,
const QString &sDefault )
506 QStringMap::const_iterator it = headers.find( sKey.toLower() );
508 if ( it == headers.end())
519 QHostAddress peerAddress,
527 LOG(VB_UPNP, LOG_DEBUG, QString(
"SSDP::ProcessSearchrequest : [%1] MX=%2")
535 if ( pRequest->m_sMethod !=
"*" )
return false;
536 if ( pRequest->m_sProtocol !=
"HTTP" )
return false;
537 if ( pRequest->m_nMajor != 1 )
return false;
539 if ( sMAN !=
"\"ssdp:discover\"" )
return false;
540 if ( sST.length() == 0 )
return false;
541 if ( sMX.length() == 0 )
return false;
542 if ((nMX = sMX.toInt()) == 0 )
return false;
543 if ( nMX < 0 )
return false;
549 nMX = (nMX > 120) ? 120 : nMX;
551 int nNewMX = (
int)(0 + ((
unsigned short)
random() % nMX)) * 1000;
557 if ((sST ==
"ssdp:all") || (sST ==
"upnp:rootdevice"))
560 peerAddress, peerPort, sST,
583 if (sUDN.length() > 0)
616 LOG(VB_UPNP, LOG_DEBUG,
617 QString(
"SSDP::ProcessSearchResponse ...\n" 622 .arg(sDescURL).arg(sST).arg(sUSN).arg(sCache));
624 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
629 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
632 int nSecs = sCache.mid( nPos+1 ).toInt();
651 LOG(VB_UPNP, LOG_DEBUG,
652 QString(
"SSDP::ProcessNotify ...\n" 658 .arg(sDescURL).arg(sNTS).arg(sNT).arg(sUSN).arg(sCache));
660 if (sNTS.contains(
"ssdp:alive"))
662 int nPos = sCache.indexOf(
"max-age", 0, Qt::CaseInsensitive);
667 if ((nPos = sCache.indexOf(
"=", nPos)) < 0)
670 int nSecs = sCache.mid( nPos+1 ).toInt();
678 if ( sNTS.contains(
"ssdp:byebye" ) )
738 return QStringList(
"/" );
774 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceDesc - " +
775 QString(
"Host=%1 Port=%2 UserAgent=%3" )
799 LOG(VB_UPNP, LOG_DEBUG,
800 QString(
"SSDPExtension::GetFile( %1 ) - Exists")
806 =
"no-cache=\"Ext\", max-age = 7200";
812 LOG(VB_UPNP, LOG_ERR,
813 QString(
"SSDPExtension::GetFile( %1 ) - Not Found")
825 LOG(VB_UPNP, LOG_DEBUG,
"SSDPExtension::GetDeviceList");
828 QTextStream os(&sXML, QIODevice::WriteOnly);
830 uint nDevCount, nEntryCount;
835 NameValue(
"DeviceCount", (
int)nDevCount));
839 NameValue(
"CacheEntriesFound", (
int)nEntryCount));
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
static SSDPCache * Instance()
virtual void Execute(TaskQueue *)
virtual int GetValue(const QString &sSetting, int Default)=0
void Remove(const QString &sURI, const QString &sUSN)
This is a wrapper around QThread that does several additional things.
virtual QString GetHostAddress()=0
#define SocketIdx_Broadcast
SSDPExtension(int nServicePort, const QString &sSharePath)
static TaskQueue * Instance()
void GetDeviceDesc(HTTPRequest *pRequest)
static QMutex g_pSSDPCreationLock
SSDPRequestType ProcessRequestLine(const QString &sLine)
bool m_bAnnouncementsEnabled
void GetDeviceList(HTTPRequest *pRequest)
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
virtual void Execute(TaskQueue *)
QStringMap m_mapRespHeaders
QString GetRequestHeader(const QString &sKey, QString sDefault)
void EnableNotifications(int nServicePort)
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
void DisableNotifications()
MSocketDevice * m_Sockets[3]
QByteArray GetResponsePage(void)
QMap< QString, QString > QStringMap
#define SocketIdx_Multicast
QString GetHeaderValue(const QStringMap &headers, const QString &sKey, const QString &sDefault)
void PerformSearch(const QString &sST, uint timeout_secs=2)
bool ProcessSearchRequest(const QStringMap &sHeaders, QHostAddress peerAddress, quint16 peerPort)
virtual void run()
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead...
QString FindDeviceUDN(UPnpDevice *pDevice, QString sST)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void RequestTerminate(void)
void FormatActionResponse(Serializer *ser)
UPnpNotifyTask * m_pNotifyTask
ResponseType m_eResponseType
void ProcessData(MSocketDevice *pSocket)
static Configuration * GetConfiguration()
void AddTask(long msec, Task *pTask)
void SetNTS(UPnpNotifyNTS nts)
#define LOG(_MASK_, _LEVEL_, _STRING_)
virtual QStringList GetBasePaths()
QTextStream & OutputXML(QTextStream &os, uint *pnDevCount=NULL, uint *pnEntryCount=NULL) const
Outputs the XML for this device.
bool ProcessSearchResponse(const QStringMap &sHeaders)
void GetFile(HTTPRequest *pRequest, QString sFileName)
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
bool ProcessRequest(HTTPRequest *pRequest)
bool ProcessNotify(const QStringMap &sHeaders)
static long int random(void)
SSDPMethod GetMethod(const QString &sURI)
void Add(const QString &sURI, const QString &sUSN, const QString &sLocation, long sExpiresInSecs)
void GetValidXML(const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent="")
static UPnpDeviceDesc g_UPnpDeviceDesc