MythTV  master
ssdp.cpp
Go to the documentation of this file.
1 // Program Name: ssdp.cpp
3 // Created : Oct. 1, 2005
4 //
5 // Purpose : SSDP Discovery Service Implmenetation
6 //
7 // Copyright (c) 2005 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see COPYING for details
10 //
12 
13 #include <algorithm>
14 #include <cerrno>
15 #include <chrono> // for milliseconds
16 #include <cstdlib>
17 #include <thread> // for sleep_for
18 
19 #include "upnp.h"
20 #include "mythlogging.h"
21 
22 #include "upnptasksearch.h"
23 #include "upnptaskcache.h"
24 
25 #include "mmulticastsocketdevice.h"
26 #include "mbroadcastsocketdevice.h"
27 
28 #include <QRegExp>
29 #include <QStringList>
30 
31 #ifdef ANDROID
32 #include <sys/select.h>
33 #endif
34 
35 using namespace std;
36 
39 //
40 // SSDP Class Implementation
41 //
44 
45 // We're creating this class immediately so it will always be available.
46 
47 static QMutex g_pSSDPCreationLock;
48 SSDP* SSDP::g_pSSDP = nullptr;
49 
51 //
53 
55 {
56  QMutexLocker locker(&g_pSSDPCreationLock);
57  return g_pSSDP ? g_pSSDP : (g_pSSDP = new SSDP());
58 }
59 
61 //
63 
65 {
66  QMutexLocker locker(&g_pSSDPCreationLock);
67  delete g_pSSDP;
68  g_pSSDP = nullptr;
69 }
70 
72 //
74 
76  MThread("SSDP")
77 {
78  LOG(VB_UPNP, LOG_NOTICE, "Starting up SSDP Thread..." );
79 
81 
82  m_nPort = pConfig->GetValue("UPnP/SSDP/Port" , SSDP_PORT );
83  m_nSearchPort = pConfig->GetValue("UPnP/SSDP/SearchPort", SSDP_SEARCHPORT);
84 
90  new MBroadcastSocketDevice("255.255.255.255", m_nPort);
91 
92  m_Sockets[ SocketIdx_Search ]->setBlocking( false );
93  m_Sockets[ SocketIdx_Multicast ]->setBlocking( false );
94  m_Sockets[ SocketIdx_Broadcast ]->setBlocking( false );
95 
96  // Setup SearchSocket
97  QHostAddress ip4addr( QHostAddress::Any );
98 
99  m_Sockets[ SocketIdx_Search ]->bind( ip4addr , m_nSearchPort );
100  m_Sockets[ SocketIdx_Search ]->bind( QHostAddress::Any, m_nSearchPort );
101 
102  // ----------------------------------------------------------------------
103  // Create the SSDP (Upnp Discovery) Thread.
104  // ----------------------------------------------------------------------
105 
106  start();
107 
108  LOG(VB_UPNP, LOG_INFO, "SSDP Thread Starting soon" );
109 }
110 
112 //
114 
116 {
117  LOG(VB_UPNP, LOG_NOTICE, "Shutting Down SSDP Thread..." );
118 
120 
121  m_bTermRequested = true;
122  wait();
123 
124  if (m_pNotifyTask != nullptr)
125  {
127  m_pNotifyTask = nullptr;
128  }
129 
130  for (int nIdx = 0; nIdx < (int)NumberOfSockets; nIdx++ )
131  {
132  if (m_Sockets[ nIdx ] != nullptr )
133  {
134  delete m_Sockets[ nIdx ];
135  }
136  }
137 
138  LOG(VB_UPNP, LOG_INFO, "SSDP Thread Terminated." );
139 }
140 
142 {
143  m_bTermRequested = true;
144 }
145 
147 //
149 
150 void SSDP::EnableNotifications( int nServicePort )
151 {
152  if ( m_pNotifyTask == nullptr )
153  {
154  m_nServicePort = nServicePort;
155 
156  LOG(VB_UPNP, LOG_INFO,
157  "SSDP::EnableNotifications() - creating new task");
159 
160  // ------------------------------------------------------------------
161  // First Send out Notification that we are leaving the network.
162  // ------------------------------------------------------------------
163 
164  LOG(VB_UPNP, LOG_INFO,
165  "SSDP::EnableNotifications() - sending NTS_byebye");
167  m_pNotifyTask->Execute( nullptr );
168 
170  }
171 
172  // ------------------------------------------------------------------
173  // Add Announcement Task to the Queue
174  // ------------------------------------------------------------------
175 
176  LOG(VB_UPNP, LOG_INFO, "SSDP::EnableNotifications() - sending NTS_alive");
177 
179 
181 
182  LOG(VB_UPNP, LOG_INFO,
183  "SSDP::EnableNotifications() - Task added to UPnP queue");
184 }
185 
187 //
189 
191 {
192  m_bAnnouncementsEnabled = false;
193 
194  if (m_pNotifyTask != nullptr)
195  {
196  // Send Announcement that we are leaving.
197 
199  m_pNotifyTask->Execute( nullptr );
200  }
201 }
202 
204 //
206 void SSDP::PerformSearch(const QString &sST, uint timeout_secs)
207 {
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"
212  "MX: %1\r\n"
213  "ST: %2\r\n"
214  "\r\n")
215  .arg(timeout_secs).arg(sST);
216 
217  LOG(VB_UPNP, LOG_DEBUG, QString("\n\n%1\n").arg(rRequest));
218 
219  QByteArray sRequest = rRequest.toUtf8();
220 
221  MSocketDevice *pSocket = m_Sockets[ SocketIdx_Search ];
222  if ( !pSocket->isValid() )
223  {
224  pSocket->setProtocol(MSocketDevice::IPv4);
225  pSocket->setSocket(pSocket->createNewSocket(), MSocketDevice::Datagram);
226  }
227 
228  QHostAddress address;
229  address.setAddress( SSDP_GROUP );
230 
231  int nSize = sRequest.size();
232 
233  if ( pSocket->writeBlock( sRequest.data(),
234  sRequest.size(), address, SSDP_PORT ) != nSize)
235  LOG(VB_GENERAL, LOG_INFO,
236  "SSDP::PerformSearch - did not write entire buffer.");
237 
238  std::this_thread::sleep_for(std::chrono::milliseconds(random() % 250));
239 
240  if ( pSocket->writeBlock( sRequest.data(),
241  sRequest.size(), address, SSDP_PORT ) != nSize)
242  LOG(VB_GENERAL, LOG_INFO,
243  "SSDP::PerformSearch - did not write entire buffer.");
244 }
245 
247 //
249 
250 void SSDP::run()
251 {
252  RunProlog();
253 
254  fd_set read_set;
255  struct timeval timeout;
256 
257  LOG(VB_UPNP, LOG_INFO, "SSDP::Run - SSDP Thread Started." );
258 
259  // ----------------------------------------------------------------------
260  // Listen for new Requests
261  // ----------------------------------------------------------------------
262 
263  while ( ! m_bTermRequested )
264  {
265  int nMaxSocket = 0;
266 
267  FD_ZERO( &read_set );
268 
269  for (size_t nIdx = 0; nIdx < NumberOfSockets; nIdx++ )
270  {
271  if (m_Sockets[nIdx] != nullptr && m_Sockets[nIdx]->socket() >= 0)
272  {
273  FD_SET( m_Sockets[ nIdx ]->socket(), &read_set );
274  nMaxSocket = max( m_Sockets[ nIdx ]->socket(), nMaxSocket );
275 
276 #if 0
277  if (m_Sockets[ nIdx ]->bytesAvailable() > 0)
278  {
279  LOG(VB_GENERAL, LOG_DEBUG,
280  QString("Found Extra data before select: %1")
281  .arg(nIdx));
282  ProcessData( m_Sockets[ nIdx ] );
283  }
284 #endif
285  }
286  }
287 
288  timeout.tv_sec = 1;
289  timeout.tv_usec = 0;
290 
291  int count;
292  count = select(nMaxSocket + 1, &read_set, nullptr, nullptr, &timeout);
293 
294  for (int nIdx = 0; count && nIdx < (int)NumberOfSockets; nIdx++ )
295  {
296  bool cond1 = m_Sockets[nIdx] != nullptr;
297  bool cond2 = cond1 && m_Sockets[nIdx]->socket() >= 0;
298  bool cond3 = cond2 && FD_ISSET(m_Sockets[nIdx]->socket(), &read_set);
299 
300  if (cond3)
301  {
302 #if 0
303  LOG(VB_GENERAL, LOG_DEBUG, QString("FD_ISSET( %1 )").arg(nIdx));
304 #endif
305 
306  ProcessData(m_Sockets[nIdx]);
307  count--;
308  }
309  }
310  }
311 
312  RunEpilog();
313 }
314 
316 //
318 
319 void SSDP::ProcessData( MSocketDevice *pSocket )
320 {
321  QByteArray buffer;
322  long nBytes = pSocket->bytesAvailable();
323  int retries = 0;
324  // Note: this function MUST do a read even if someone sends a zero byte UDP message
325  // Otherwise the select() will continue to signal data ready, so to prevent using 100%
326  // CPU, we need to call a recv function to make select() block again
327  bool didDoRead = false;
328 
329  // UDP message of zero length? OK, "recv" it and move on
330  if (nBytes == 0)
331  {
332  LOG(VB_UPNP, LOG_WARNING, QString("SSDP: Received 0 byte UDP message"));
333  }
334 
335  while ((nBytes = pSocket->bytesAvailable()) > 0 || (nBytes == 0 && !didDoRead))
336  {
337  buffer.resize(nBytes);
338 
339  long nRead = 0;
340  do
341  {
342  long ret = pSocket->readBlock( buffer.data() + nRead, nBytes - nRead );
343  didDoRead = true;
344  if (ret < 0)
345  {
346  if (errno == EAGAIN
347 #if EAGAIN != EWOULDBLOCK
348  || errno == EWOULDBLOCK
349 #endif
350  )
351  {
352  if (retries == 3)
353  {
354  nBytes = nRead;
355  buffer.resize(nBytes);
356  break;
357  }
358  retries++;
359  std::this_thread::sleep_for(std::chrono::milliseconds(10));
360  continue;
361  }
362  LOG(VB_GENERAL, LOG_ERR, QString("Socket readBlock error %1")
363  .arg(pSocket->error()));
364  buffer.clear();
365  break;
366  }
367  retries = 0;
368 
369  nRead += ret;
370 
371  if (0 == ret && nBytes != 0)
372  {
373  LOG(VB_SOCKET, LOG_WARNING,
374  QString("%1 bytes reported available, "
375  "but only %2 bytes read.")
376  .arg(nBytes).arg(nRead));
377  nBytes = nRead;
378  buffer.resize(nBytes);
379  break;
380  }
381  }
382  while (nRead < nBytes);
383 
384  if (buffer.isEmpty())
385  continue;
386 
387  QHostAddress peerAddress = pSocket->peerAddress();
388  quint16 peerPort = pSocket->peerPort ();
389 
390  // ------------------------------------------------------------------
391  QString str = QString(buffer.constData());
392  QStringList lines = str.split("\r\n", QString::SkipEmptyParts);
393  QString sRequestLine = !lines.empty() ? lines[0] : "";
394 
395  if (!lines.isEmpty())
396  lines.pop_front();
397 
398  // ------------------------------------------------------------------
399  // Parse request Type
400  // ------------------------------------------------------------------
401 
402  LOG(VB_UPNP, LOG_DEBUG, QString("SSDP::ProcessData - requestLine: %1")
403  .arg(sRequestLine));
404 
405  SSDPRequestType eType = ProcessRequestLine( sRequestLine );
406 
407  // ------------------------------------------------------------------
408  // Read Headers into map
409  // ------------------------------------------------------------------
410 
411  QStringMap headers;
412 
413  for ( QStringList::Iterator it = lines.begin();
414  it != lines.end(); ++it )
415  {
416  QString sLine = *it;
417  QString sName = sLine.section( ':', 0, 0 ).trimmed();
418  QString sValue = sLine.section( ':', 1 );
419 
420  sValue.truncate( sValue.length() ); //-2
421 
422  if ((sName.length() != 0) && (sValue.length() !=0))
423  headers.insert( sName.toLower(), sValue.trimmed() );
424  }
425 
426 #if 0
427  pSocket->SetDestAddress( peerAddress, peerPort );
428 #endif
429 
430  // --------------------------------------------------------------
431  // See if this is a valid request
432  // --------------------------------------------------------------
433 
434  switch( eType )
435  {
436  case SSDP_MSearch:
437  {
438  // ----------------------------------------------------------
439  // If we haven't enabled notifications yet, then we don't
440  // want to answer search requests.
441  // ----------------------------------------------------------
442 
443  if (m_pNotifyTask != nullptr)
444  ProcessSearchRequest( headers, peerAddress, peerPort );
445 
446  break;
447  }
448 
449  case SSDP_MSearchResp:
450  ProcessSearchResponse( headers);
451  break;
452 
453  case SSDP_Notify:
454  ProcessNotify( headers );
455  break;
456 
457  case SSDP_Unknown:
458  default:
459  LOG(VB_UPNP, LOG_ERR,
460  "SSPD::ProcessData - Unknown request Type.");
461  break;
462  }
463  }
464 }
465 
467 //
469 
471 {
472  QStringList tokens = sLine.split(m_procReqLineExp, QString::SkipEmptyParts);
473 
474  // ----------------------------------------------------------------------
475  // if this is actually a response, then sLine's format will be:
476  // HTTP/m.n <response code> <response text>
477  // otherwise:
478  // <method> <Resource URI> HTTP/m.n
479  // ----------------------------------------------------------------------
480 
481  if ( sLine.startsWith( QString("HTTP/") ))
482  return SSDP_MSearchResp;
483 
484  if (tokens.count() > 0)
485  {
486  if (tokens[0] == "M-SEARCH" ) return SSDP_MSearch;
487  if (tokens[0] == "NOTIFY" ) return SSDP_Notify;
488  }
489 
490  return SSDP_Unknown;
491 }
492 
494 //
496 
497 QString SSDP::GetHeaderValue( const QStringMap &headers,
498  const QString &sKey, const QString &sDefault )
499 {
500  QStringMap::const_iterator it = headers.find( sKey.toLower() );
501 
502  if ( it == headers.end())
503  return( sDefault );
504 
505  return *it;
506 }
507 
509 //
511 
512 bool SSDP::ProcessSearchRequest( const QStringMap &sHeaders,
513  const QHostAddress& peerAddress,
514  quint16 peerPort )
515 {
516  QString sMAN = GetHeaderValue( sHeaders, "MAN", "" );
517  QString sST = GetHeaderValue( sHeaders, "ST" , "" );
518  QString sMX = GetHeaderValue( sHeaders, "MX" , "" );
519  int nMX = 0;
520 
521  LOG(VB_UPNP, LOG_DEBUG, QString("SSDP::ProcessSearchrequest : [%1] MX=%2")
522  .arg(sST).arg(sMX));
523 
524  // ----------------------------------------------------------------------
525  // Validate Header Values...
526  // ----------------------------------------------------------------------
527 
528 #if 0
529  if ( pRequest->m_sMethod != "*" ) return false;
530  if ( pRequest->m_sProtocol != "HTTP" ) return false;
531  if ( pRequest->m_nMajor != 1 ) return false;
532 #endif
533  if ( sMAN != "\"ssdp:discover\"" ) return false;
534  if ( sST.length() == 0 ) return false;
535  if ( sMX.length() == 0 ) return false;
536  if ((nMX = sMX.toInt()) == 0 ) return false;
537  if ( nMX < 0 ) return false;
538 
539  // ----------------------------------------------------------------------
540  // Adjust timeout to be a random interval between 0 and MX (max of 120)
541  // ----------------------------------------------------------------------
542 
543  nMX = (nMX > 120) ? 120 : nMX;
544 
545  int nNewMX = (0 + ((unsigned short)random() % nMX)) * 1000;
546 
547  // ----------------------------------------------------------------------
548  // See what they are looking for...
549  // ----------------------------------------------------------------------
550 
551  if ((sST == "ssdp:all") || (sST == "upnp:rootdevice"))
552  {
554  peerAddress, peerPort, sST,
555  UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
556 
557 #if 0
558  // Excute task now for fastest response, queue for time-delayed response
559  // -=>TODO: To be trully uPnp compliant, this Execute should be removed.
560  pTask->Execute( nullptr );
561 #endif
562 
563  TaskQueue::Instance()->AddTask( nNewMX, pTask );
564 
565  pTask->DecrRef();
566 
567  return true;
568  }
569 
570  // ----------------------------------------------------------------------
571  // Look for a specific device/service
572  // ----------------------------------------------------------------------
573 
574  QString sUDN = UPnp::g_UPnpDeviceDesc.FindDeviceUDN(
575  &(UPnp::g_UPnpDeviceDesc.m_rootDevice), sST );
576 
577  if (sUDN.length() > 0)
578  {
580  peerAddress,
581  peerPort,
582  sST,
583  sUDN );
584 
585  // Excute task now for fastest response, queue for time-delayed response
586  // -=>TODO: To be trully uPnp compliant, this Execute should be removed.
587  pTask->Execute( nullptr );
588 
589  TaskQueue::Instance()->AddTask( nNewMX, pTask );
590 
591  pTask->DecrRef();
592 
593  return true;
594  }
595 
596  return false;
597 }
598 
600 //
602 
604 {
605  QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
606  QString sST = GetHeaderValue( headers, "ST" , "" );
607  QString sUSN = GetHeaderValue( headers, "USN" , "" );
608  QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
609 
610  LOG(VB_UPNP, LOG_DEBUG,
611  QString( "SSDP::ProcessSearchResponse ...\n"
612  "DescURL=%1\n"
613  "ST =%2\n"
614  "USN =%3\n"
615  "Cache =%4")
616  .arg(sDescURL).arg(sST).arg(sUSN).arg(sCache));
617 
618  int nPos = sCache.indexOf("max-age", 0, Qt::CaseInsensitive);
619 
620  if (nPos < 0)
621  return false;
622 
623  if ((nPos = sCache.indexOf("=", nPos)) < 0)
624  return false;
625 
626  int nSecs = sCache.mid( nPos+1 ).toInt();
627 
628  SSDPCache::Instance()->Add( sST, sUSN, sDescURL, nSecs );
629 
630  return true;
631 }
632 
634 //
636 
637 bool SSDP::ProcessNotify( const QStringMap &headers )
638 {
639  QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
640  QString sNTS = GetHeaderValue( headers, "NTS" , "" );
641  QString sNT = GetHeaderValue( headers, "NT" , "" );
642  QString sUSN = GetHeaderValue( headers, "USN" , "" );
643  QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
644 
645  LOG(VB_UPNP, LOG_DEBUG,
646  QString( "SSDP::ProcessNotify ...\n"
647  "DescURL=%1\n"
648  "NTS =%2\n"
649  "NT =%3\n"
650  "USN =%4\n"
651  "Cache =%5" )
652  .arg(sDescURL).arg(sNTS).arg(sNT).arg(sUSN).arg(sCache));
653 
654  if (sNTS.contains( "ssdp:alive"))
655  {
656  int nPos = sCache.indexOf("max-age", 0, Qt::CaseInsensitive);
657 
658  if (nPos < 0)
659  return false;
660 
661  if ((nPos = sCache.indexOf("=", nPos)) < 0)
662  return false;
663 
664  int nSecs = sCache.mid( nPos+1 ).toInt();
665 
666  SSDPCache::Instance()->Add( sNT, sUSN, sDescURL, nSecs );
667 
668  return true;
669  }
670 
671 
672  if ( sNTS.contains( "ssdp:byebye" ) )
673  {
674  SSDPCache::Instance()->Remove( sNT, sUSN );
675 
676  return true;
677  }
678 
679  return false;
680 }
681 
684 //
685 // SSDPExtension Implementation
686 //
689 
691 //
693 
694 SSDPExtension::SSDPExtension( int nServicePort , const QString &sSharePath)
695  : HttpServerExtension( "SSDP" , sSharePath),
696  m_nServicePort(nServicePort)
697 {
699  m_sUPnpDescPath = UPnp::GetConfiguration()->GetValue( "UPnP/DescXmlPath",
700  m_sSharePath );
701 }
702 
704 //
706 
707 SSDPMethod SSDPExtension::GetMethod( const QString &sURI )
708 {
709  if (sURI == "getDeviceDesc" ) return( SSDPM_GetDeviceDesc );
710  if (sURI == "getDeviceList" ) return( SSDPM_GetDeviceList );
711 
712  return( SSDPM_Unknown );
713 }
714 
716 //
718 
720 {
721  // -=>TODO: This is very inefficient... should look into making
722  // it a unique path.
723 
724  return QStringList( "/" );
725 }
726 
728 //
730 
732 {
733  if (pRequest)
734  {
735  if ( pRequest->m_sBaseUrl != "/")
736  return( false );
737 
738  switch( GetMethod( pRequest->m_sMethod ))
739  {
740  case SSDPM_GetDeviceDesc: GetDeviceDesc( pRequest ); return( true );
741  case SSDPM_GetDeviceList: GetDeviceList( pRequest ); return( true );
742 
743  default: break;
744  }
745  }
746 
747  return( false );
748 }
749 
751 //
753 
755 {
756  pRequest->m_eResponseType = ResponseTypeXML;
757 
758  QString sUserAgent = pRequest->GetRequestHeader( "User-Agent", "" );
759 
760  LOG(VB_UPNP, LOG_DEBUG, "SSDPExtension::GetDeviceDesc - " +
761  QString( "Host=%1 Port=%2 UserAgent=%3" )
762  .arg(pRequest->GetHostAddress()) .arg(m_nServicePort)
763  .arg(sUserAgent));
764 
765  QTextStream stream( &(pRequest->m_response) );
766 
769  stream,
770  sUserAgent );
771 }
772 
774 //
776 
777 void SSDPExtension::GetFile( HTTPRequest *pRequest, const QString& sFileName )
778 {
779  pRequest->m_eResponseType = ResponseTypeHTML;
780 
781  pRequest->m_sFileName = m_sUPnpDescPath + sFileName;
782 
783  if (QFile::exists( pRequest->m_sFileName ))
784  {
785  LOG(VB_UPNP, LOG_DEBUG,
786  QString("SSDPExtension::GetFile( %1 ) - Exists")
787  .arg(pRequest->m_sFileName));
788 
789  pRequest->m_eResponseType = ResponseTypeFile;
790  pRequest->m_nResponseStatus = 200;
791  pRequest->m_mapRespHeaders[ "Cache-Control" ]
792  = "no-cache=\"Ext\", max-age = 7200"; // 2 hours
793  }
794  else
795  {
796  pRequest->m_nResponseStatus = 404;
797  pRequest->m_response.write( pRequest->GetResponsePage() );
798  LOG(VB_UPNP, LOG_ERR,
799  QString("SSDPExtension::GetFile( %1 ) - Not Found")
800  .arg(pRequest->m_sFileName));
801  }
802 
803 }
804 
806 //
808 
810 {
811  LOG(VB_UPNP, LOG_DEBUG, "SSDPExtension::GetDeviceList");
812 
813  QString sXML;
814  QTextStream os(&sXML, QIODevice::WriteOnly);
815 
816  uint nDevCount, nEntryCount;
817  SSDPCache::Instance()->OutputXML(os, &nDevCount, &nEntryCount);
818 
819  NameValues list;
820  list.push_back(
821  NameValue("DeviceCount", (int)nDevCount));
822  list.push_back(
823  NameValue("DevicesAllocated", SSDPCacheEntries::g_nAllocated));
824  list.push_back(
825  NameValue("CacheEntriesFound", (int)nEntryCount));
826  list.push_back(
827  NameValue("CacheEntriesAllocated", DeviceLocation::g_nAllocated));
828  list.push_back(
829  NameValue("DeviceList", sXML));
830 
831  pRequest->FormatActionResponse(list);
832 
833  pRequest->m_eResponseType = ResponseTypeXML;
834  pRequest->m_nResponseStatus = 200;
835 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
SSDPRequestType
Definition: ssdp.h:38
void start(QThread::Priority=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
static SSDPCache * Instance()
Definition: ssdpcache.cpp:240
virtual int GetValue(const QString &sSetting, int Default)=0
void Remove(const QString &sURI, const QString &sUSN)
Definition: ssdpcache.cpp:413
This is a wrapper around QThread that does several additional things.
Definition: mthread.h:46
SSDPMethod
Definition: ssdp.h:30
virtual QString GetHostAddress()=0
#define SocketIdx_Broadcast
Definition: ssdp.h:57
SSDPExtension(int nServicePort, const QString &sSharePath)
Definition: ssdp.cpp:694
void GetFile(HTTPRequest *pRequest, const QString &sFileName)
Definition: ssdp.cpp:777
QString m_sBaseUrl
Definition: httprequest.h:123
static TaskQueue * Instance()
Definition: taskqueue.cpp:58
void GetDeviceDesc(HTTPRequest *pRequest)
Definition: ssdp.cpp:754
QString m_sSharePath
Definition: httpserver.h:78
static QMutex g_pSSDPCreationLock
Definition: ssdp.cpp:47
SSDPRequestType ProcessRequestLine(const QString &sLine)
Definition: ssdp.cpp:470
bool m_bAnnouncementsEnabled
Definition: ssdp.h:75
#define SSDP_GROUP
Definition: ssdp.h:26
void GetDeviceList(HTTPRequest *pRequest)
Definition: ssdp.cpp:809
bool wait(unsigned long time=ULONG_MAX)
Wait for the MThread to exit, with a maximum timeout.
Definition: mthread.cpp:311
QString m_sMethod
Definition: httprequest.h:125
QStringMap m_mapRespHeaders
Definition: httprequest.h:149
QString GetRequestHeader(const QString &sKey, QString sDefault)
static int g_nAllocated
Definition: ssdpcache.h:62
void EnableNotifications(int nServicePort)
Definition: ssdp.cpp:150
long m_nResponseStatus
Definition: httprequest.h:148
unsigned int uint
Definition: compat.h:140
Definition: ssdp.h:61
void DisableNotifications()
Definition: ssdp.cpp:190
void Execute(TaskQueue *) override
MSocketDevice * m_Sockets[3]
Definition: ssdp.h:68
QByteArray GetResponsePage(void)
QMap< QString, QString > QStringMap
Definition: upnputil.h:40
#define SocketIdx_Multicast
Definition: ssdp.h:56
QString GetHeaderValue(const QStringMap &headers, const QString &sKey, const QString &sDefault)
Definition: ssdp.cpp:497
void run() override
Runs the Qt event loop unless we have a QRunnable, in which case we run the runnable run instead.
Definition: ssdp.cpp:250
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:206
int m_nServicePort
Definition: ssdp.h:149
QRegExp m_procReqLineExp
Definition: ssdp.h:67
QString FindDeviceUDN(UPnpDevice *pDevice, QString sST)
Definition: upnpdevice.cpp:544
QStringList GetBasePaths() override
Definition: ssdp.cpp:719
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void RequestTerminate(void)
Definition: ssdp.cpp:141
void FormatActionResponse(Serializer *ser)
UPnpNotifyTask * m_pNotifyTask
Definition: ssdp.h:74
QString m_sFileName
Definition: httprequest.h:151
void ProcessData(MSocketDevice *pSocket)
Definition: ssdp.cpp:319
static Configuration * GetConfiguration()
Definition: upnp.cpp:71
void AddTask(long msec, Task *pTask)
Definition: taskqueue.cpp:173
void SetNTS(UPnpNotifyNTS nts)
void Execute(TaskQueue *) override
static int g_nAllocated
Definition: upnpdevice.h:207
static SSDP * g_pSSDP
Definition: ssdp.h:65
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static SSDP * Instance()
Definition: ssdp.cpp:54
#define SocketIdx_Search
Definition: ssdp.h:55
QString m_sUPnpDescPath
Definition: ssdp.h:148
#define NumberOfSockets
Definition: ssdp.h:59
bool ProcessSearchResponse(const QStringMap &sHeaders)
Definition: ssdp.cpp:603
bool m_bTermRequested
Definition: ssdp.h:77
int m_nPort
Definition: ssdp.h:70
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
QBuffer m_response
Definition: httprequest.h:153
int m_nServicePort
Definition: ssdp.h:72
QTextStream & OutputXML(QTextStream &os, uint *pnDevCount=nullptr, uint *pnEntryCount=nullptr) const
Outputs the XML for this device.
Definition: ssdpcache.cpp:551
HttpResponseType m_eResponseType
Definition: httprequest.h:145
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: ssdp.cpp:731
#define SSDP_PORT
Definition: ssdp.h:27
bool ProcessNotify(const QStringMap &sHeaders)
Definition: ssdp.cpp:637
static long int random(void)
Definition: compat.h:149
SSDPMethod GetMethod(const QString &sURI)
Definition: ssdp.cpp:707
#define SSDP_SEARCHPORT
Definition: ssdp.h:28
void Add(const QString &sURI, const QString &sUSN, const QString &sLocation, long sExpiresInSecs)
Definition: ssdpcache.cpp:328
int m_nSearchPort
Definition: ssdp.h:71
bool ProcessSearchRequest(const QStringMap &sHeaders, const QHostAddress &peerAddress, quint16 peerPort)
Definition: ssdp.cpp:512
void GetValidXML(const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent="")
Definition: upnpdevice.cpp:316
SSDP()
Definition: ssdp.cpp:75
static void Shutdown()
Definition: ssdp.cpp:64
~SSDP()
Definition: ssdp.cpp:115
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:107