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  for ( QList<QHostAddress>::Iterator it = m_addressList.begin();
99  it != m_addressList.end();
100  ++it )
101  {
102  QString ipaddress;
103 
104  // Avoid announcing the localhost address
105  if (*it == QHostAddress::LocalHost ||
106  *it == QHostAddress::LocalHostIPv6 ||
107  *it == QHostAddress::AnyIPv4 ||
108  *it == QHostAddress::AnyIPv6)
109  continue;
110 
111  QHostAddress ip = *it;
112  // Descope the Link Local address. The scope is only valid
113  // on the server sending the announcement, not the clients
114  // that receive it
115  ip.setScopeId(QString());
116 
117  // If this looks like an IPv6 address, then enclose it in []'s
118  if (ip.protocol() == QAbstractSocket::IPv6Protocol)
119  ipaddress = "[" + ip.toString() + "]";
120  else
121  ipaddress = ip.toString();
122 
123  QString sHeader = QString ( "HTTP/1.1 200 OK\r\n"
124  "LOCATION: http://%1:%2/getDeviceDesc\r\n" )
125  .arg( ipaddress )
126  .arg( m_nServicePort);
127 
128 
129  QString sPacket = sHeader + sData;
130  QByteArray scPacket = sPacket.toUtf8();
131 
132  // ------------------------------------------------------------------
133  // Send Packet to UDP Socket (Send same packet twice)
134  // ------------------------------------------------------------------
135 
136  pSocket->writeBlock( scPacket, scPacket.length(), m_PeerAddress,
137  m_nPeerPort );
138  std::this_thread::sleep_for( std::chrono::milliseconds( random() % 250 ));
139  pSocket->writeBlock( scPacket, scPacket.length(), m_PeerAddress,
140  m_nPeerPort );
141  }
142 }
143 
145 //
147 
148 void UPnpSearchTask::Execute( TaskQueue * /*pQueue*/ )
149 {
150  MSocketDevice *pSocket = new MSocketDevice( MSocketDevice::Datagram );
151 
152  // ----------------------------------------------------------------------
153  // Refresh IP Address List in case of changes
154  // ----------------------------------------------------------------------
155 
157 
158  // ----------------------------------------------------------------------
159  // Check to see if this is a rootdevice or all request.
160  // ----------------------------------------------------------------------
161 
163 
164  if ((m_sST == "upnp:rootdevice") || (m_sST == "ssdp:all" ))
165  {
166  SendMsg( pSocket, "upnp:rootdevice", device.GetUDN() );
167 
168  if (m_sST == "ssdp:all")
169  ProcessDevice( pSocket, &device );
170  }
171  else
172  {
173  // ------------------------------------------------------------------
174  // Send Device/Service specific response.
175  // ------------------------------------------------------------------
176 
177  SendMsg( pSocket, m_sST, m_sUDN );
178  }
179 
180  delete pSocket;
181  pSocket = nullptr;
182 }
183 
185 //
187 
189  MSocketDevice *pSocket, UPnpDevice *pDevice)
190 {
191  // ----------------------------------------------------------------------
192  // Loop for each device and send the 2 required messages
193  //
194  // -=>TODO: We need to add support to only notify
195  // Version 1 of a service.
196  // ----------------------------------------------------------------------
197 
198  SendMsg( pSocket, pDevice->GetUDN(), "" );
199  SendMsg( pSocket, pDevice->m_sDeviceType, pDevice->GetUDN() );
200 
201  // ------------------------------------------------------------------
202  // Loop for each service in this device and send the 1 required message
203  // ------------------------------------------------------------------
204 
205  UPnpServiceList::const_iterator sit = pDevice->m_listServices.begin();
206  for (; sit != pDevice->m_listServices.end(); ++sit)
207  SendMsg(pSocket, (*sit)->m_sServiceType, pDevice->GetUDN());
208 
209  // ----------------------------------------------------------------------
210  // Process any Embedded Devices
211  // ----------------------------------------------------------------------
212 
213  UPnpDeviceList::const_iterator dit = pDevice->m_listDevices.begin();
214  for (; dit != pDevice->m_listDevices.end(); ++dit)
215  ProcessDevice( pSocket, *dit);
216 }
217 
virtual int GetValue(const QString &sSetting, int Default)=0
QHostAddress m_PeerAddress
QList< QHostAddress > m_addressList
static QPair< QHostAddress, int > kLinkLocal6
void ProcessDevice(MSocketDevice *pSocket, UPnpDevice *pDevice)
UPnpServiceList m_listServices
Definition: upnpdevice.h:119
QString GetUDN(void) const
Definition: upnpdevice.cpp:766
QDateTime current(bool stripped)
Returns current Date and Time in UTC.
Definition: mythdate.cpp:10
UPnpDevice m_rootDevice
Definition: upnpdevice.h:148
static Configuration * GetConfiguration()
Definition: upnp.cpp:71
void Execute(TaskQueue *) override
void SendMsg(MSocketDevice *pSocket, const QString &sST, const QString &sUDN)
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:120
QString m_sDeviceType
Definition: upnpdevice.h:99
#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:292
static long int random(void)
Definition: compat.h:149
static QList< QHostAddress > g_IPAddrList
Definition: upnp.h:108
Definition: taskqueue.h:53
static UPnpDeviceDesc g_UPnpDeviceDesc
Definition: upnp.h:107