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 (auto & socket : m_sockets)
131  delete socket;
132 
133  LOG(VB_UPNP, LOG_INFO, "SSDP Thread Terminated." );
134 }
135 
137 {
138  m_bTermRequested = true;
139 }
140 
142 //
144 
145 void SSDP::EnableNotifications( int nServicePort )
146 {
147  if ( m_pNotifyTask == nullptr )
148  {
149  m_nServicePort = nServicePort;
150 
151  LOG(VB_UPNP, LOG_INFO,
152  "SSDP::EnableNotifications() - creating new task");
154 
155  // ------------------------------------------------------------------
156  // First Send out Notification that we are leaving the network.
157  // ------------------------------------------------------------------
158 
159  LOG(VB_UPNP, LOG_INFO,
160  "SSDP::EnableNotifications() - sending NTS_byebye");
162  m_pNotifyTask->Execute( nullptr );
163 
165  }
166 
167  // ------------------------------------------------------------------
168  // Add Announcement Task to the Queue
169  // ------------------------------------------------------------------
170 
171  LOG(VB_UPNP, LOG_INFO, "SSDP::EnableNotifications() - sending NTS_alive");
172 
174 
176 
177  LOG(VB_UPNP, LOG_INFO,
178  "SSDP::EnableNotifications() - Task added to UPnP queue");
179 }
180 
182 //
184 
186 {
187  m_bAnnouncementsEnabled = false;
188 
189  if (m_pNotifyTask != nullptr)
190  {
191  // Send Announcement that we are leaving.
192 
194  m_pNotifyTask->Execute( nullptr );
195  }
196 }
197 
199 //
201 void SSDP::PerformSearch(const QString &sST, uint timeout_secs)
202 {
203  timeout_secs = std::max(std::min(timeout_secs, 5U), 1U);
204  QString rRequest = QString("M-SEARCH * HTTP/1.1\r\n"
205  "HOST: 239.255.255.250:1900\r\n"
206  "MAN: \"ssdp:discover\"\r\n"
207  "MX: %1\r\n"
208  "ST: %2\r\n"
209  "\r\n")
210  .arg(timeout_secs).arg(sST);
211 
212  LOG(VB_UPNP, LOG_DEBUG, QString("\n\n%1\n").arg(rRequest));
213 
214  QByteArray sRequest = rRequest.toUtf8();
215 
216  MSocketDevice *pSocket = m_sockets[ SocketIdx_Search ];
217  if ( !pSocket->isValid() )
218  {
219  pSocket->setProtocol(MSocketDevice::IPv4);
220  pSocket->setSocket(pSocket->createNewSocket(), MSocketDevice::Datagram);
221  }
222 
223  QHostAddress address;
224  address.setAddress( SSDP_GROUP );
225 
226  int nSize = sRequest.size();
227 
228  if ( pSocket->writeBlock( sRequest.data(),
229  sRequest.size(), address, SSDP_PORT ) != nSize)
230  LOG(VB_GENERAL, LOG_INFO,
231  "SSDP::PerformSearch - did not write entire buffer.");
232 
233  std::this_thread::sleep_for(std::chrono::milliseconds(random() % 250));
234 
235  if ( pSocket->writeBlock( sRequest.data(),
236  sRequest.size(), address, SSDP_PORT ) != nSize)
237  LOG(VB_GENERAL, LOG_INFO,
238  "SSDP::PerformSearch - did not write entire buffer.");
239 }
240 
242 //
244 
245 void SSDP::run()
246 {
247  RunProlog();
248 
249  fd_set read_set;
250  struct timeval timeout {};
251 
252  LOG(VB_UPNP, LOG_INFO, "SSDP::Run - SSDP Thread Started." );
253 
254  // ----------------------------------------------------------------------
255  // Listen for new Requests
256  // ----------------------------------------------------------------------
257 
258  while ( ! m_bTermRequested )
259  {
260  int nMaxSocket = 0;
261 
262  FD_ZERO( &read_set ); // NOLINT(readability-isolate-declaration)
263 
264  for (auto & socket : m_sockets)
265  {
266  if (socket != nullptr && socket->socket() >= 0)
267  {
268  FD_SET( socket->socket(), &read_set );
269  nMaxSocket = max( socket->socket(), nMaxSocket );
270 
271 #if 0
272  if (socket->bytesAvailable() > 0)
273  {
274  LOG(VB_GENERAL, LOG_DEBUG,
275  QString("Found Extra data before select: %1")
276  .arg(nIdx));
277  ProcessData( socket );
278  }
279 #endif
280  }
281  }
282 
283  timeout.tv_sec = 1;
284  timeout.tv_usec = 0;
285 
286  int count = select(nMaxSocket + 1, &read_set, nullptr, nullptr, &timeout);
287 
288  for (int nIdx = 0; count && nIdx < kNumberOfSockets; nIdx++ )
289  {
290  bool cond1 = m_sockets[nIdx] != nullptr;
291  bool cond2 = cond1 && m_sockets[nIdx]->socket() >= 0;
292  bool cond3 = cond2 && FD_ISSET(m_sockets[nIdx]->socket(), &read_set);
293 
294  if (cond3)
295  {
296 #if 0
297  LOG(VB_GENERAL, LOG_DEBUG, QString("FD_ISSET( %1 )").arg(nIdx));
298 #endif
299 
300  ProcessData(m_sockets[nIdx]);
301  count--;
302  }
303  }
304  }
305 
306  RunEpilog();
307 }
308 
310 //
312 
313 void SSDP::ProcessData( MSocketDevice *pSocket )
314 {
315  QByteArray buffer;
316  long nBytes = pSocket->bytesAvailable();
317  int retries = 0;
318  // Note: this function MUST do a read even if someone sends a zero byte UDP message
319  // Otherwise the select() will continue to signal data ready, so to prevent using 100%
320  // CPU, we need to call a recv function to make select() block again
321  bool didDoRead = false;
322 
323  // UDP message of zero length? OK, "recv" it and move on
324  if (nBytes == 0)
325  {
326  LOG(VB_UPNP, LOG_WARNING, QString("SSDP: Received 0 byte UDP message"));
327  }
328 
329  while ((nBytes = pSocket->bytesAvailable()) > 0 || (nBytes == 0 && !didDoRead))
330  {
331  buffer.resize(nBytes);
332 
333  long nRead = 0;
334  do
335  {
336  long ret = pSocket->readBlock( buffer.data() + nRead, nBytes - nRead );
337  didDoRead = true;
338  if (ret < 0)
339  {
340  if (errno == EAGAIN
341 #if EAGAIN != EWOULDBLOCK
342  || errno == EWOULDBLOCK
343 #endif
344  )
345  {
346  if (retries == 3)
347  {
348  nBytes = nRead;
349  buffer.resize(nBytes);
350  break;
351  }
352  retries++;
353  std::this_thread::sleep_for(std::chrono::milliseconds(10));
354  continue;
355  }
356  LOG(VB_GENERAL, LOG_ERR, QString("Socket readBlock error %1")
357  .arg(pSocket->error()));
358  buffer.clear();
359  break;
360  }
361  retries = 0;
362 
363  nRead += ret;
364 
365  if (0 == ret && nBytes != 0)
366  {
367  LOG(VB_SOCKET, LOG_WARNING,
368  QString("%1 bytes reported available, "
369  "but only %2 bytes read.")
370  .arg(nBytes).arg(nRead));
371  nBytes = nRead;
372  buffer.resize(nBytes);
373  break;
374  }
375  }
376  while (nRead < nBytes);
377 
378  if (buffer.isEmpty())
379  continue;
380 
381  QHostAddress peerAddress = pSocket->peerAddress();
382  quint16 peerPort = pSocket->peerPort ();
383 
384  // ------------------------------------------------------------------
385  QString str = QString(buffer.constData());
386  QStringList lines = str.split("\r\n", QString::SkipEmptyParts);
387  QString sRequestLine = !lines.empty() ? lines[0] : "";
388 
389  if (!lines.isEmpty())
390  lines.pop_front();
391 
392  // ------------------------------------------------------------------
393  // Parse request Type
394  // ------------------------------------------------------------------
395 
396  LOG(VB_UPNP, LOG_DEBUG, QString("SSDP::ProcessData - requestLine: %1")
397  .arg(sRequestLine));
398 
399  SSDPRequestType eType = ProcessRequestLine( sRequestLine );
400 
401  // ------------------------------------------------------------------
402  // Read Headers into map
403  // ------------------------------------------------------------------
404 
405  QStringMap headers;
406 
407  foreach (auto sLine, lines)
408  {
409  QString sName = sLine.section( ':', 0, 0 ).trimmed();
410  QString sValue = sLine.section( ':', 1 );
411 
412  sValue.truncate( sValue.length() ); //-2
413 
414  if ((sName.length() != 0) && (sValue.length() !=0))
415  headers.insert( sName.toLower(), sValue.trimmed() );
416  }
417 
418 #if 0
419  pSocket->SetDestAddress( peerAddress, peerPort );
420 #endif
421 
422  // --------------------------------------------------------------
423  // See if this is a valid request
424  // --------------------------------------------------------------
425 
426  switch( eType )
427  {
428  case SSDP_MSearch:
429  {
430  // ----------------------------------------------------------
431  // If we haven't enabled notifications yet, then we don't
432  // want to answer search requests.
433  // ----------------------------------------------------------
434 
435  if (m_pNotifyTask != nullptr)
436  ProcessSearchRequest( headers, peerAddress, peerPort );
437 
438  break;
439  }
440 
441  case SSDP_MSearchResp:
442  ProcessSearchResponse( headers);
443  break;
444 
445  case SSDP_Notify:
446  ProcessNotify( headers );
447  break;
448 
449  case SSDP_Unknown:
450  default:
451  LOG(VB_UPNP, LOG_ERR,
452  "SSPD::ProcessData - Unknown request Type.");
453  break;
454  }
455  }
456 }
457 
459 //
461 
463 {
464  QStringList tokens = sLine.split(m_procReqLineExp, QString::SkipEmptyParts);
465 
466  // ----------------------------------------------------------------------
467  // if this is actually a response, then sLine's format will be:
468  // HTTP/m.n <response code> <response text>
469  // otherwise:
470  // <method> <Resource URI> HTTP/m.n
471  // ----------------------------------------------------------------------
472 
473  if ( sLine.startsWith( QString("HTTP/") ))
474  return SSDP_MSearchResp;
475 
476  if (tokens.count() > 0)
477  {
478  if (tokens[0] == "M-SEARCH" ) return SSDP_MSearch;
479  if (tokens[0] == "NOTIFY" ) return SSDP_Notify;
480  }
481 
482  return SSDP_Unknown;
483 }
484 
486 //
488 
489 QString SSDP::GetHeaderValue( const QStringMap &headers,
490  const QString &sKey, const QString &sDefault )
491 {
492  QStringMap::const_iterator it = headers.find( sKey.toLower() );
493 
494  if ( it == headers.end())
495  return( sDefault );
496 
497  return *it;
498 }
499 
501 //
503 
504 bool SSDP::ProcessSearchRequest( const QStringMap &sHeaders,
505  const QHostAddress& peerAddress,
506  quint16 peerPort )
507 {
508  QString sMAN = GetHeaderValue( sHeaders, "MAN", "" );
509  QString sST = GetHeaderValue( sHeaders, "ST" , "" );
510  QString sMX = GetHeaderValue( sHeaders, "MX" , "" );
511  int nMX = 0;
512 
513  LOG(VB_UPNP, LOG_DEBUG, QString("SSDP::ProcessSearchrequest : [%1] MX=%2")
514  .arg(sST).arg(sMX));
515 
516  // ----------------------------------------------------------------------
517  // Validate Header Values...
518  // ----------------------------------------------------------------------
519 
520 #if 0
521  if ( pRequest->m_sMethod != "*" ) return false;
522  if ( pRequest->m_sProtocol != "HTTP" ) return false;
523  if ( pRequest->m_nMajor != 1 ) return false;
524 #endif
525  if ( sMAN != "\"ssdp:discover\"" ) return false;
526  if ( sST.length() == 0 ) return false;
527  if ( sMX.length() == 0 ) return false;
528  if ((nMX = sMX.toInt()) == 0 ) return false;
529  if ( nMX < 0 ) return false;
530 
531  // ----------------------------------------------------------------------
532  // Adjust timeout to be a random interval between 0 and MX (max of 120)
533  // ----------------------------------------------------------------------
534 
535  nMX = (nMX > 120) ? 120 : nMX;
536 
537  int nNewMX = (0 + ((unsigned short)random() % nMX)) * 1000;
538 
539  // ----------------------------------------------------------------------
540  // See what they are looking for...
541  // ----------------------------------------------------------------------
542 
543  if ((sST == "ssdp:all") || (sST == "upnp:rootdevice"))
544  {
545  auto *pTask = new UPnpSearchTask( m_nServicePort,
546  peerAddress, peerPort, sST,
547  UPnp::g_UPnpDeviceDesc.m_rootDevice.GetUDN());
548 
549 #if 0
550  // Excute task now for fastest response, queue for time-delayed response
551  // -=>TODO: To be trully uPnp compliant, this Execute should be removed.
552  pTask->Execute( nullptr );
553 #endif
554 
555  TaskQueue::Instance()->AddTask( nNewMX, pTask );
556 
557  pTask->DecrRef();
558 
559  return true;
560  }
561 
562  // ----------------------------------------------------------------------
563  // Look for a specific device/service
564  // ----------------------------------------------------------------------
565 
566  QString sUDN = UPnp::g_UPnpDeviceDesc.FindDeviceUDN(
567  &(UPnp::g_UPnpDeviceDesc.m_rootDevice), sST );
568 
569  if (sUDN.length() > 0)
570  {
571  auto *pTask = new UPnpSearchTask( m_nServicePort, peerAddress,
572  peerPort, sST, sUDN );
573 
574  // Excute task now for fastest response, queue for time-delayed response
575  // -=>TODO: To be trully uPnp compliant, this Execute should be removed.
576  pTask->Execute( nullptr );
577 
578  TaskQueue::Instance()->AddTask( nNewMX, pTask );
579 
580  pTask->DecrRef();
581 
582  return true;
583  }
584 
585  return false;
586 }
587 
589 //
591 
593 {
594  QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
595  QString sST = GetHeaderValue( headers, "ST" , "" );
596  QString sUSN = GetHeaderValue( headers, "USN" , "" );
597  QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
598 
599  LOG(VB_UPNP, LOG_DEBUG,
600  QString( "SSDP::ProcessSearchResponse ...\n"
601  "DescURL=%1\n"
602  "ST =%2\n"
603  "USN =%3\n"
604  "Cache =%4")
605  .arg(sDescURL).arg(sST).arg(sUSN).arg(sCache));
606 
607  int nPos = sCache.indexOf("max-age", 0, Qt::CaseInsensitive);
608 
609  if (nPos < 0)
610  return false;
611 
612  if ((nPos = sCache.indexOf("=", nPos)) < 0)
613  return false;
614 
615  int nSecs = sCache.mid( nPos+1 ).toInt();
616 
617  SSDPCache::Instance()->Add( sST, sUSN, sDescURL, nSecs );
618 
619  return true;
620 }
621 
623 //
625 
626 bool SSDP::ProcessNotify( const QStringMap &headers )
627 {
628  QString sDescURL = GetHeaderValue( headers, "LOCATION" , "" );
629  QString sNTS = GetHeaderValue( headers, "NTS" , "" );
630  QString sNT = GetHeaderValue( headers, "NT" , "" );
631  QString sUSN = GetHeaderValue( headers, "USN" , "" );
632  QString sCache = GetHeaderValue( headers, "CACHE-CONTROL" , "" );
633 
634  LOG(VB_UPNP, LOG_DEBUG,
635  QString( "SSDP::ProcessNotify ...\n"
636  "DescURL=%1\n"
637  "NTS =%2\n"
638  "NT =%3\n"
639  "USN =%4\n"
640  "Cache =%5" )
641  .arg(sDescURL).arg(sNTS).arg(sNT).arg(sUSN).arg(sCache));
642 
643  if (sNTS.contains( "ssdp:alive"))
644  {
645  int nPos = sCache.indexOf("max-age", 0, Qt::CaseInsensitive);
646 
647  if (nPos < 0)
648  return false;
649 
650  if ((nPos = sCache.indexOf("=", nPos)) < 0)
651  return false;
652 
653  int nSecs = sCache.mid( nPos+1 ).toInt();
654 
655  SSDPCache::Instance()->Add( sNT, sUSN, sDescURL, nSecs );
656 
657  return true;
658  }
659 
660 
661  if ( sNTS.contains( "ssdp:byebye" ) )
662  {
663  SSDPCache::Instance()->Remove( sNT, sUSN );
664 
665  return true;
666  }
667 
668  return false;
669 }
670 
673 //
674 // SSDPExtension Implementation
675 //
678 
680 //
682 
683 SSDPExtension::SSDPExtension( int nServicePort , const QString &sSharePath)
684  : HttpServerExtension( "SSDP" , sSharePath),
685  m_nServicePort(nServicePort)
686 {
688  m_sUPnpDescPath = UPnp::GetConfiguration()->GetValue( "UPnP/DescXmlPath",
689  m_sSharePath );
690 }
691 
693 //
695 
696 SSDPMethod SSDPExtension::GetMethod( const QString &sURI )
697 {
698  if (sURI == "getDeviceDesc" ) return( SSDPM_GetDeviceDesc );
699  if (sURI == "getDeviceList" ) return( SSDPM_GetDeviceList );
700 
701  return( SSDPM_Unknown );
702 }
703 
705 //
707 
709 {
710  // -=>TODO: This is very inefficient... should look into making
711  // it a unique path.
712 
713  return QStringList( "/" );
714 }
715 
717 //
719 
721 {
722  if (pRequest)
723  {
724  if ( pRequest->m_sBaseUrl != "/")
725  return( false );
726 
727  switch( GetMethod( pRequest->m_sMethod ))
728  {
729  case SSDPM_GetDeviceDesc: GetDeviceDesc( pRequest ); return( true );
730  case SSDPM_GetDeviceList: GetDeviceList( pRequest ); return( true );
731 
732  default: break;
733  }
734  }
735 
736  return( false );
737 }
738 
740 //
742 
744 {
745  pRequest->m_eResponseType = ResponseTypeXML;
746 
747  QString sUserAgent = pRequest->GetRequestHeader( "User-Agent", "" );
748 
749  LOG(VB_UPNP, LOG_DEBUG, "SSDPExtension::GetDeviceDesc - " +
750  QString( "Host=%1 Port=%2 UserAgent=%3" )
751  .arg(pRequest->GetHostAddress()) .arg(m_nServicePort)
752  .arg(sUserAgent));
753 
754  QTextStream stream( &(pRequest->m_response) );
755 
758  stream,
759  sUserAgent );
760 }
761 
763 //
765 
766 void SSDPExtension::GetFile( HTTPRequest *pRequest, const QString& sFileName )
767 {
768  pRequest->m_eResponseType = ResponseTypeHTML;
769 
770  pRequest->m_sFileName = m_sUPnpDescPath + sFileName;
771 
772  if (QFile::exists( pRequest->m_sFileName ))
773  {
774  LOG(VB_UPNP, LOG_DEBUG,
775  QString("SSDPExtension::GetFile( %1 ) - Exists")
776  .arg(pRequest->m_sFileName));
777 
778  pRequest->m_eResponseType = ResponseTypeFile;
779  pRequest->m_nResponseStatus = 200;
780  pRequest->m_mapRespHeaders[ "Cache-Control" ]
781  = "no-cache=\"Ext\", max-age = 7200"; // 2 hours
782  }
783  else
784  {
785  pRequest->m_nResponseStatus = 404;
786  pRequest->m_response.write( pRequest->GetResponsePage() );
787  LOG(VB_UPNP, LOG_ERR,
788  QString("SSDPExtension::GetFile( %1 ) - Not Found")
789  .arg(pRequest->m_sFileName));
790  }
791 
792 }
793 
795 //
797 
799 {
800  LOG(VB_UPNP, LOG_DEBUG, "SSDPExtension::GetDeviceList");
801 
802  QString sXML;
803  QTextStream os(&sXML, QIODevice::WriteOnly);
804 
805  uint nDevCount = 0;
806  uint nEntryCount = 0;
807  SSDPCache::Instance()->OutputXML(os, &nDevCount, &nEntryCount);
808 
809  NameValues list;
810  list.push_back(
811  NameValue("DeviceCount", (int)nDevCount));
812  list.push_back(
813  NameValue("DevicesAllocated", SSDPCacheEntries::g_nAllocated));
814  list.push_back(
815  NameValue("CacheEntriesFound", (int)nEntryCount));
816  list.push_back(
817  NameValue("CacheEntriesAllocated", DeviceLocation::g_nAllocated));
818  list.push_back(
819  NameValue("DeviceList", sXML));
820 
821  pRequest->FormatActionResponse(list);
822 
823  pRequest->m_eResponseType = ResponseTypeXML;
824  pRequest->m_nResponseStatus = 200;
825 }
void RunEpilog(void)
Cleans up a thread's resources, call this if you reimplement run().
Definition: mthread.cpp:215
SSDPRequestType
Definition: ssdp.h:37
static SSDPCache * Instance()
Definition: ssdpcache.cpp:236
QMap< QString, QString > QStringMap
Definition: upnputil.h:44
virtual int GetValue(const QString &sSetting, int Default)=0
void Remove(const QString &sURI, const QString &sUSN)
Definition: ssdpcache.cpp:408
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:55
SSDPExtension(int nServicePort, const QString &sSharePath)
Definition: ssdp.cpp:683
void GetFile(HTTPRequest *pRequest, const QString &sFileName)
Definition: ssdp.cpp:766
QString m_sBaseUrl
Definition: httprequest.h:125
static TaskQueue * Instance()
Definition: taskqueue.cpp:58
void GetDeviceDesc(HTTPRequest *pRequest)
Definition: ssdp.cpp:743
QString m_sSharePath
Definition: httpserver.h:78
static QMutex g_pSSDPCreationLock
Definition: ssdp.cpp:47
SSDPRequestType ProcessRequestLine(const QString &sLine)
Definition: ssdp.cpp:462
~SSDP() override
Definition: ssdp.cpp:115
bool m_bAnnouncementsEnabled
Definition: ssdp.h:72
#define SSDP_GROUP
Definition: ssdp.h:26
static void GetDeviceList(HTTPRequest *pRequest)
Definition: ssdp.cpp:798
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:127
QStringMap m_mapRespHeaders
Definition: httprequest.h:151
QString GetRequestHeader(const QString &sKey, QString sDefault)
static constexpr int kNumberOfSockets
Definition: ssdp.h:64
static int g_nAllocated
Definition: ssdpcache.h:62
void EnableNotifications(int nServicePort)
Definition: ssdp.cpp:145
long m_nResponseStatus
Definition: httprequest.h:150
Definition: ssdp.h:57
void Execute(TaskQueue *pQueue) override
void DisableNotifications()
Definition: ssdp.cpp:185
void Execute(TaskQueue *pQueue) override
QByteArray GetResponsePage(void)
#define SocketIdx_Multicast
Definition: ssdp.h:54
static QString GetHeaderValue(const QStringMap &headers, const QString &sKey, const QString &sDefault)
Definition: ssdp.cpp:489
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:245
void PerformSearch(const QString &sST, uint timeout_secs=2)
Definition: ssdp.cpp:201
int m_nServicePort
Definition: ssdp.h:146
QRegExp m_procReqLineExp
Definition: ssdp.h:63
QString FindDeviceUDN(UPnpDevice *pDevice, QString sST)
Definition: upnpdevice.cpp:541
QStringList GetBasePaths() override
Definition: ssdp.cpp:708
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
void RequestTerminate(void)
Definition: ssdp.cpp:136
void FormatActionResponse(Serializer *ser)
UPnpNotifyTask * m_pNotifyTask
Definition: ssdp.h:71
unsigned int uint
Definition: compat.h:140
QString m_sFileName
Definition: httprequest.h:153
void ProcessData(MSocketDevice *pSocket)
Definition: ssdp.cpp:313
static Configuration * GetConfiguration()
Definition: upnp.cpp:85
void AddTask(long msec, Task *pTask)
Definition: taskqueue.cpp:170
void SetNTS(UPnpNotifyNTS nts)
MSocketDevice * m_sockets[kNumberOfSockets]
Definition: ssdp.h:65
static int g_nAllocated
Definition: upnpdevice.h:211
static SSDP * g_pSSDP
Definition: ssdp.h:61
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static SSDP * Instance()
Definition: ssdp.cpp:54
#define SocketIdx_Search
Definition: ssdp.h:53
QString m_sUPnpDescPath
Definition: ssdp.h:145
static bool ProcessSearchResponse(const QStringMap &sHeaders)
Definition: ssdp.cpp:592
bool m_bTermRequested
Definition: ssdp.h:74
int m_nPort
Definition: ssdp.h:67
void RunProlog(void)
Sets up a thread, call this if you reimplement run().
Definition: mthread.cpp:202
void start(QThread::Priority p=QThread::InheritPriority)
Tell MThread to start running the thread in the near future.
Definition: mthread.cpp:294
QBuffer m_response
Definition: httprequest.h:155
int m_nServicePort
Definition: ssdp.h:69
QTextStream & OutputXML(QTextStream &os, uint *pnDevCount=nullptr, uint *pnEntryCount=nullptr) const
Outputs the XML for this device.
Definition: ssdpcache.cpp:542
HttpResponseType m_eResponseType
Definition: httprequest.h:147
bool ProcessRequest(HTTPRequest *pRequest) override
Definition: ssdp.cpp:720
#define SSDP_PORT
Definition: ssdp.h:27
static bool ProcessNotify(const QStringMap &sHeaders)
Definition: ssdp.cpp:626
static long int random(void)
Definition: compat.h:149
static SSDPMethod GetMethod(const QString &sURI)
Definition: ssdp.cpp:696
#define SSDP_SEARCHPORT
Definition: ssdp.h:28
void Add(const QString &sURI, const QString &sUSN, const QString &sLocation, long sExpiresInSecs)
Definition: ssdpcache.cpp:323
int m_nSearchPort
Definition: ssdp.h:68
bool ProcessSearchRequest(const QStringMap &sHeaders, const QHostAddress &peerAddress, quint16 peerPort)
Definition: ssdp.cpp:504
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
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:112