MythTV  master
upnptasksearch.cpp
Go to the documentation of this file.
1 // Program Name: upnptasksearch.cpp
3 // Created : Oct. 24, 2005
4 //
5 // Purpose : UPnp Task to handle Discovery Responses
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 <chrono> // for milliseconds
14 #include <cstdlib>
15 #include <thread> // for sleep_for
16 
17 #include <QDateTime>
18 #include <QFile>
19 #include <QStringList>
20 #include <utility>
21 
22 #include "upnp.h"
23 #include "upnptasksearch.h"
24 #include "mythversion.h"
25 #include "compat.h"
26 #include "mythdate.h"
27 
28 static QPair<QHostAddress, int> kLinkLocal6 =
29  QHostAddress::parseSubnet("fe80::/10");
30 
33 //
34 // UPnpSearchTask Implementation
35 //
38 
40 //
42 
43 UPnpSearchTask::UPnpSearchTask( int nServicePort,
44  QHostAddress peerAddress,
45  int nPeerPort,
46  QString sST,
47  QString sUDN ) :
48  Task("UPnpSearchTask")
49 {
50  m_peerAddress = std::move(peerAddress);
51  m_nPeerPort = nPeerPort;
52  m_sST = std::move(sST);
53  m_sUDN = std::move(sUDN);
54  m_nServicePort= nServicePort;
55  m_nMaxAge = UPnp::GetConfiguration()->GetValue( "UPnP/SSDP/MaxAge" , 3600 );
56 
57 }
58 
60 //
62 
63 void UPnpSearchTask::SendMsg( MSocketDevice *pSocket,
64  const QString& sST,
65  const QString& sUDN )
66 {
67  QString sUSN;
68 
69  if (( sUDN.length() > 0) && ( sUDN != sST ))
70  sUSN = sUDN + "::" + sST;
71  else
72  sUSN = sST;
73 
74  QString sDate = MythDate::current().toString( "d MMM yyyy hh:mm:ss" );
75 
76  QString sData = QString ( "CACHE-CONTROL: max-age=%1\r\n"
77  "DATE: %2\r\n"
78  "EXT:\r\n"
79  "Server: %3\r\n"
80  "ST: %4\r\n"
81  "USN: %5\r\n"
82  "Content-Length: 0\r\n\r\n" )
83  .arg( m_nMaxAge )
84  .arg( sDate )
86  .arg( sST )
87  .arg( sUSN );
88 
89 #if 0
90  LOG(VB_UPNP, LOG_DEBUG, QString("UPnpSearchTask::SendMsg : %1 : %2 ")
91  .arg(sST) .arg(sUSN));
92 
93  LOG(VB_UPNP, LOG_DEBUG,
94  QString("UPnpSearchTask::SendMsg m_peerAddress = %1 Port=%2")
95  .arg(m_peerAddress.toString()) .arg(m_nPeerPort));
96 #endif
97 
98  foreach (auto & addr, m_addressList)
99  {
100  QString ipaddress;
101 
102  // Avoid announcing the localhost address
103  if (addr == QHostAddress::LocalHost ||
104  addr == QHostAddress::LocalHostIPv6 ||
105  addr == QHostAddress::AnyIPv4 ||
106  addr == QHostAddress::AnyIPv6)
107  continue;
108 
109  QHostAddress ip = addr;
110  // Descope the Link Local address. The scope is only valid
111  // on the server sending the announcement, not the clients
112  // that receive it
113  ip.setScopeId(QString());
114 
115  // If this looks like an IPv6 address, then enclose it in []'s
116  if (ip.protocol() == QAbstractSocket::IPv6Protocol)
117  ipaddress = "[" + ip.toString() + "]";
118  else
119  ipaddress = ip.toString();
120 
121  QString sHeader = QString ( "HTTP/1.1 200 OK\r\n"
122  "LOCATION: http://%1:%2/getDeviceDesc\r\n" )
123  .arg( ipaddress )
124  .arg( m_nServicePort);
125 
126 
127  QString sPacket = sHeader + sData;
128  QByteArray scPacket = sPacket.toUtf8();
129 
130  // ------------------------------------------------------------------
131  // Send Packet to UDP Socket (Send same packet twice)
132  // ------------------------------------------------------------------
133 
134  pSocket->writeBlock( scPacket, scPacket.length(), m_peerAddress,
135  m_nPeerPort );
136  std::this_thread::sleep_for( std::chrono::milliseconds( random() % 250 ));
137  pSocket->writeBlock( scPacket, scPacket.length(), m_peerAddress,
138  m_nPeerPort );
139  }
140 }
141 
143 //
145 
146 void UPnpSearchTask::Execute( TaskQueue * /*pQueue*/ )
147 {
148  auto *pSocket = new MSocketDevice( MSocketDevice::Datagram );
149 
150  // ----------------------------------------------------------------------
151  // Refresh IP Address List in case of changes
152  // ----------------------------------------------------------------------
153 
155 
156  // ----------------------------------------------------------------------
157  // Check to see if this is a rootdevice or all request.
158  // ----------------------------------------------------------------------
159 
161 
162  if ((m_sST == "upnp:rootdevice") || (m_sST == "ssdp:all" ))
163  {
164  SendMsg( pSocket, "upnp:rootdevice", device.GetUDN() );
165 
166  if (m_sST == "ssdp:all")
167  ProcessDevice( pSocket, &device );
168  }
169  else
170  {
171  // ------------------------------------------------------------------
172  // Send Device/Service specific response.
173  // ------------------------------------------------------------------
174 
175  SendMsg( pSocket, m_sST, m_sUDN );
176  }
177 
178  delete pSocket;
179  pSocket = nullptr;
180 }
181 
183 //
185 
187  MSocketDevice *pSocket, UPnpDevice *pDevice)
188 {
189  // ----------------------------------------------------------------------
190  // Loop for each device and send the 2 required messages
191  //
192  // -=>TODO: We need to add support to only notify
193  // Version 1 of a service.
194  // ----------------------------------------------------------------------
195 
196  SendMsg( pSocket, pDevice->GetUDN(), "" );
197  SendMsg( pSocket, pDevice->m_sDeviceType, pDevice->GetUDN() );
198 
199  // ------------------------------------------------------------------
200  // Loop for each service in this device and send the 1 required message
201  // ------------------------------------------------------------------
202 
203  for (auto sit = pDevice->m_listServices.cbegin();
204  sit != pDevice->m_listServices.cend(); ++sit)
205  SendMsg(pSocket, (*sit)->m_sServiceType, pDevice->GetUDN());
206 
207  // ----------------------------------------------------------------------
208  // Process any Embedded Devices
209  // ----------------------------------------------------------------------
210 
211  foreach (auto device, pDevice->m_listDevices)
212  ProcessDevice( pSocket, device);
213 }
214 
virtual int GetValue(const QString &sSetting, int Default)=0
QList< QHostAddress > m_addressList
static QPair< QHostAddress, int > kLinkLocal6
void ProcessDevice(MSocketDevice *pSocket, UPnpDevice *pDevice)
void Execute(TaskQueue *pQueue) override
UPnpServiceList m_listServices
Definition: upnpdevice.h:123
QString GetUDN(void) const
Definition: upnpdevice.cpp:760
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
UPnpDevice m_rootDevice
Definition: upnpdevice.h:152
static Configuration * GetConfiguration()
Definition: upnp.cpp:85
void SendMsg(MSocketDevice *pSocket, const QString &sST, const QString &sUDN)
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:124
QString m_sDeviceType
Definition: upnpdevice.h:103
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
UPnpSearchTask(int nServicePort, QHostAddress peerAddress, int nPeerPort, QString sST, QString sUDN)
static QString GetServerVersion(void)
Definition: httpserver.cpp:288
static long int random(void)
Definition: compat.h:149
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:113
Definition: taskqueue.h:53
QHostAddress m_peerAddress
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:112