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