MythTV  master
upnpdevice.cpp
Go to the documentation of this file.
1 // Program Name: upnpdevice.cpp
3 // Created : Oct. 24, 2005
4 //
5 // Purpose : UPnp Device Description parser/generator
6 //
7 // Copyright (c) 2005 David Blain <dblain@mythtv.org>
8 //
9 // Licensed under the GPL v2 or later, see LICENSE for details
10 //
12 
13 #include "upnp.h"
14 #include "upnpdevice.h"
15 #include "mythdownloadmanager.h"
16 #include "mythlogging.h"
17 #include "mythversion.h" // for MYTH_BINARY_VERSION
18 #include "mythcorecontext.h"
19 #include "configuration.h"
20 
21 // MythDB
22 #include "mythdb.h"
23 
24 #include <unistd.h> // for gethostname
25 
26 #include <QFile>
27 #include <QTextStream>
28 #include <QHostAddress>
29 #include <QHostInfo>
30 
31 #if QT_VERSION < QT_VERSION_CHECK(5,14,0)
32  #define QT_FLUSH flush
33 #else
34  #define QT_FLUSH Qt::flush
35 #endif
36 
37 int DeviceLocation::g_nAllocated = 0; // Debugging only
38 
41 //
42 // UPnpDeviceDesc Class Implementation
43 //
46 
48 //
50 
51 bool UPnpDeviceDesc::Load( const QString &sFileName )
52 {
53  // ----------------------------------------------------------------------
54  // Open Supplied XML uPnp Description file.
55  // ----------------------------------------------------------------------
56 
57  QDomDocument doc ( "upnp" );
58  QFile file( sFileName );
59 
60  if ( !file.open( QIODevice::ReadOnly ) )
61  return false;
62 
63  QString sErrMsg;
64  int nErrLine = 0;
65  int nErrCol = 0;
66  bool bSuccess = doc.setContent( &file, false,
67  &sErrMsg, &nErrLine, &nErrCol );
68 
69  file.close();
70 
71  if (!bSuccess)
72  {
73  LOG(VB_GENERAL, LOG_ERR,
74  QString("UPnpDeviceDesc::Load - Error parsing: %1 "
75  "at line: %2 column: %3")
76  .arg(sFileName) .arg(nErrLine)
77  .arg(nErrCol));
78  LOG(VB_GENERAL, LOG_ERR,
79  QString("UPnpDeviceDesc::Load - Error Msg: %1" ) .arg(sErrMsg));
80  return false;
81  }
82 
83  // --------------------------------------------------------------
84  // XML Document Loaded... now parse it into the UPnpDevice Hierarchy
85  // --------------------------------------------------------------
86 
87  return Load( doc );
88 }
89 
91 //
93 
94 bool UPnpDeviceDesc::Load( const QDomDocument &xmlDevDesc )
95 {
96  // --------------------------------------------------------------
97  // Parse XML into the UPnpDevice Hierarchy
98  // --------------------------------------------------------------
99 
100  QDomNode oNode = xmlDevDesc.documentElement();
101 
102  InternalLoad( oNode.namedItem( "device" ), &m_rootDevice );
103 
104  return true;
105 }
106 
108 //
110 
111 void UPnpDeviceDesc::InternalLoad( QDomNode oNode, UPnpDevice *pCurDevice )
112 {
113  QString pin = GetMythDB()->GetSetting( "SecurityPin", "");
114  pCurDevice->m_securityPin = !(pin.isEmpty() || pin == "0000");
115 
116  for ( oNode = oNode.firstChild();
117  !oNode.isNull();
118  oNode = oNode.nextSibling() )
119  {
120  QDomElement e = oNode.toElement();
121 
122  if (e.isNull())
123  continue;
124 
125  // TODO: make this table driven (using offset within structure)
126  if ( e.tagName() == "deviceType" )
127  SetStrValue( e, pCurDevice->m_sDeviceType);
128  else if ( e.tagName() == "friendlyName" )
129  SetStrValue( e, pCurDevice->m_sFriendlyName );
130  else if ( e.tagName() == "manufacturer" )
131  SetStrValue( e, pCurDevice->m_sManufacturer );
132  else if ( e.tagName() == "manufacturerURL" )
133  SetStrValue( e, pCurDevice->m_sManufacturerURL );
134  else if ( e.tagName() == "modelDescription" )
135  SetStrValue( e, pCurDevice->m_sModelDescription);
136  else if ( e.tagName() == "modelName" )
137  SetStrValue( e, pCurDevice->m_sModelName );
138  else if ( e.tagName() == "modelNumber" )
139  SetStrValue( e, pCurDevice->m_sModelNumber );
140  else if ( e.tagName() == "modelURL" )
141  SetStrValue( e, pCurDevice->m_sModelURL );
142  else if ( e.tagName() == "serialNumber" )
143  SetStrValue( e, pCurDevice->m_sSerialNumber );
144  else if ( e.tagName() == "UPC" )
145  SetStrValue( e, pCurDevice->m_sUPC );
146  else if ( e.tagName() == "presentationURL" )
147  SetStrValue( e, pCurDevice->m_sPresentationURL );
148  else if ( e.tagName() == "UDN" )
149  SetStrValue( e, pCurDevice->m_sUDN );
150  else if ( e.tagName() == "iconList" )
151  ProcessIconList( oNode, pCurDevice );
152  else if ( e.tagName() == "serviceList" )
153  ProcessServiceList( oNode, pCurDevice );
154  else if ( e.tagName() == "deviceList" )
155  ProcessDeviceList ( oNode, pCurDevice );
156  else if ( e.tagName() == "mythtv:X_secure" )
157  SetBoolValue( e, pCurDevice->m_securityPin );
158  else if ( e.tagName() == "mythtv:X_protocol" )
159  SetStrValue( e, pCurDevice->m_protocolVersion );
160  else
161  {
162  // Not one of the expected element names... add to extra list.
163  QString sValue = "";
164  SetStrValue( e, sValue );
165  NameValue node = NameValue(e.tagName(), sValue);
166  QDomNamedNodeMap attributes = e.attributes();
167  for (int i = 0; i < attributes.size(); i++)
168  {
169  node.AddAttribute(attributes.item(i).nodeName(),
170  attributes.item(i).nodeValue(),
171  true); // TODO Check whether all attributes are in fact requires for the device xml
172  }
173  pCurDevice->m_lstExtra.push_back(node);
174  }
175  }
176 }
177 
179 //
181 
182 void UPnpDeviceDesc::ProcessIconList( const QDomNode& oListNode, UPnpDevice *pDevice )
183 {
184  for ( QDomNode oNode = oListNode.firstChild();
185  !oNode.isNull();
186  oNode = oNode.nextSibling() )
187  {
188  QDomElement e = oNode.toElement();
189 
190  if (e.isNull())
191  continue;
192 
193  if ( e.tagName() == "icon" )
194  {
195  auto *pIcon = new UPnpIcon();
196  pDevice->m_listIcons.append( pIcon );
197 
198  SetStrValue( e.namedItem( "mimetype" ), pIcon->m_sMimeType );
199  SetNumValue( e.namedItem( "width" ), pIcon->m_nWidth );
200  SetNumValue( e.namedItem( "height" ), pIcon->m_nHeight );
201  SetNumValue( e.namedItem( "depth" ), pIcon->m_nDepth );
202  SetStrValue( e.namedItem( "url" ), pIcon->m_sURL );
203  }
204  }
205 }
206 
208 //
210 
211 void UPnpDeviceDesc::ProcessServiceList( const QDomNode& oListNode, UPnpDevice *pDevice )
212 {
213  for ( QDomNode oNode = oListNode.firstChild();
214  !oNode.isNull();
215  oNode = oNode.nextSibling() )
216  {
217  QDomElement e = oNode.toElement();
218 
219  if (e.isNull())
220  continue;
221 
222  if ( e.tagName() == "service" )
223  {
224  auto *pService = new UPnpService();
225  pDevice->m_listServices.append( pService );
226 
227  SetStrValue(e.namedItem( "serviceType" ), pService->m_sServiceType);
228  SetStrValue(e.namedItem( "serviceId" ), pService->m_sServiceId);
229  SetStrValue(e.namedItem( "SCPDURL" ), pService->m_sSCPDURL);
230  SetStrValue(e.namedItem( "controlURL" ), pService->m_sControlURL);
231  SetStrValue(e.namedItem( "eventSubURL" ), pService->m_sEventSubURL);
232 
233  LOG(VB_UPNP, LOG_INFO,
234  QString("ProcessServiceList adding service : %1 : %2 :")
235  .arg(pService->m_sServiceType,
236  pService->m_sServiceId));
237  }
238  }
239 }
240 
242 //
244 
245 void UPnpDeviceDesc::ProcessDeviceList( const QDomNode& oListNode,
246  UPnpDevice *pDevice )
247 {
248  for ( QDomNode oNode = oListNode.firstChild();
249  !oNode.isNull();
250  oNode = oNode.nextSibling() )
251  {
252  QDomElement e = oNode.toElement();
253 
254  if (e.isNull())
255  continue;
256 
257  if ( e.tagName() == "device")
258  {
259  auto *pNewDevice = new UPnpDevice();
260  pDevice->m_listDevices.append( pNewDevice );
261  InternalLoad( e, pNewDevice );
262  }
263  }
264 }
265 
268 void UPnpDeviceDesc::SetStrValue( const QDomNode &n, QString &sValue )
269 {
270  if (!n.isNull())
271  {
272  QDomText oText = n.firstChild().toText();
273 
274  if (!oText.isNull())
275  sValue = oText.nodeValue();
276  }
277 }
278 
281 void UPnpDeviceDesc::SetNumValue( const QDomNode &n, int &nValue )
282 {
283  if (!n.isNull())
284  {
285  QDomText oText = n.firstChild().toText();
286 
287  if (!oText.isNull())
288  nValue = oText.nodeValue().toInt();
289  }
290 }
291 
292 void UPnpDeviceDesc::SetBoolValue( const QDomNode &n, bool &nValue )
293 {
294  if (!n.isNull())
295  {
296  QDomText oText = n.firstChild().toText();
297 
298  if (!oText.isNull())
299  {
300  QString s = oText.nodeValue();
301  nValue = (s == "yes" || s == "true" || (s.toInt() != 0));
302  }
303  }
304 }
305 
307 //
309 
310 QString UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort )
311 {
312  QString sXML;
313  QTextStream os( &sXML, QIODevice::WriteOnly );
314 
315  GetValidXML( sBaseAddress, nPort, os );
316  os << QT_FLUSH;
317  return( sXML );
318 }
319 
321 //
323 
325  const QString &/*sBaseAddress*/, int /*nPort*/,
326  QTextStream &os, const QString &sUserAgent )
327 {
328 #if 0
329  os.setEncoding( QTextStream::UnicodeUTF8 );
330 #endif
331  os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
332  "<root xmlns=\"urn:schemas-upnp-org:device-1-0\" "
333  " xmlns:mythtv=\"mythtv.org\">\n"
334  "<specVersion>\n"
335  "<major>1</major>\n"
336  "<minor>0</minor>\n"
337  "</specVersion>\n";
338 
339  OutputDevice( os, &m_rootDevice, sUserAgent );
340 
341  os << "</root>\n";
342  os << QT_FLUSH;
343 }
344 
346 //
348 
349 void UPnpDeviceDesc::OutputDevice( QTextStream &os,
350  UPnpDevice *pDevice,
351  const QString &sUserAgent )
352 {
353  if (pDevice == nullptr)
354  return;
355 
356  QString sFriendlyName = QString( "%1: %2" )
357  .arg( GetHostName(),
358  pDevice->m_sFriendlyName );
359 
360  // ----------------------------------------------------------------------
361  // Only override the root device
362  // ----------------------------------------------------------------------
363 
364  if (pDevice == &m_rootDevice)
365  sFriendlyName = MythCoreContext::GetConfiguration()->GetValue( "UPnP/FriendlyName",
366  sFriendlyName );
367 
368  os << "<device>\n";
369  os << FormatValue( "deviceType" , pDevice->m_sDeviceType );
370  os << FormatValue( "friendlyName" , sFriendlyName );
371 
372  // ----------------------------------------------------------------------
373  // XBox 360 needs specific values in the Device Description.
374  //
375  // -=>TODO: This should be externalized in a more generic/extension
376  // kind of way.
377  // ----------------------------------------------------------------------
378 
379  bool bIsXbox360 =
380  sUserAgent.startsWith(QString("Xbox/2.0"), Qt::CaseInsensitive) ||
381  sUserAgent.startsWith(QString("Mozilla/4.0"), Qt::CaseInsensitive);
382 
383  os << FormatValue( "manufacturer" , pDevice->m_sManufacturer );
384  os << FormatValue( "modelURL" , pDevice->m_sModelURL );
385 
386  // HACK
387 // if ( bIsXbox360 )
388 // {
389 // os << FormatValue( "modelName" ,
390 // "Windows Media Connect Compatible (MythTV)");
391 // }
392 // else
393 // {
394  os << FormatValue( "modelName" , pDevice->m_sModelName );
395 // }
396 
397  os << FormatValue( "manufacturerURL" , pDevice->m_sManufacturerURL );
398  os << FormatValue( "modelDescription" , pDevice->m_sModelDescription);
399  os << FormatValue( "modelNumber" , pDevice->m_sModelNumber );
400  os << FormatValue( "serialNumber" , pDevice->m_sSerialNumber );
401  os << FormatValue( "UPC" , pDevice->m_sUPC );
402  os << FormatValue( "presentationURL" , pDevice->m_sPresentationURL );
403 
404  // MythTV Custom information
405  os << FormatValue( "mythtv:X_secure" ,
406  pDevice->m_securityPin ? "true" : "false");
407  os << FormatValue( "mythtv:X_protocol", pDevice->m_protocolVersion );
408 
409  for (const auto & nit : qAsConst(pDevice->m_lstExtra))
410  {
411  os << FormatValue( nit );
412  }
413 
414  // ----------------------------------------------------------------------
415  // Output Any Icons.
416  // ----------------------------------------------------------------------
417 
418  if (pDevice->m_listIcons.count() > 0)
419  {
420  os << "<iconList>\n";
421 
422  for (auto *icon : qAsConst(pDevice->m_listIcons))
423  {
424  os << "<icon>\n";
425  os << FormatValue( "mimetype", icon->m_sMimeType );
426  os << FormatValue( "width" , icon->m_nWidth );
427  os << FormatValue( "height" , icon->m_nHeight );
428  os << FormatValue( "depth" , icon->m_nDepth );
429  os << FormatValue( "url" , icon->m_sURL );
430  os << "</icon>\n";
431  }
432  os << "</iconList>\n";
433  }
434 
435  os << FormatValue( "UDN" , pDevice->GetUDN() );
436 
437  // ----------------------------------------------------------------------
438  // Output any Services
439  // ----------------------------------------------------------------------
440 
441  if (pDevice->m_listServices.count() > 0)
442  {
443  // ------------------------------------------------------------------
444  // -=>TODO: As a temporary fix don't expose the MSRR service unless we
445  // as an XBox360 or Windows MediaPlayer.
446  //
447  // There is a problem with a DSM-520 with firmware 1.04 and
448  // the Denon AVR-4306 receiver.
449  //
450  // If the MSRR Service is exposed, it won't let us browse
451  // any media content.
452  //
453  // Need to find out real fix and remove this code.
454  // ------------------------------------------------------------------
455 
456 #if 0
457  bool bDSM = sUserAgent.startsWith("INTEL_NMPR/2.1 DLNADOC/1.00", false);
458 #endif
459 
460  os << "<serviceList>\n";
461 
462  for (auto *service : qAsConst(pDevice->m_listServices))
463  {
464  if (!bIsXbox360 && service->m_sServiceType.startsWith(
465  "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar",
466  Qt::CaseInsensitive))
467  {
468  continue;
469  }
470 
471  os << "<service>\n";
472  os << FormatValue( "serviceType", service->m_sServiceType );
473  os << FormatValue( "serviceId" , service->m_sServiceId );
474  os << FormatValue( "SCPDURL" , service->m_sSCPDURL );
475  os << FormatValue( "controlURL" , service->m_sControlURL );
476  os << FormatValue( "eventSubURL", service->m_sEventSubURL );
477  os << "</service>\n";
478  }
479  os << "</serviceList>\n";
480  }
481 
482  // ----------------------------------------------------------------------
483  // Output any Embedded Devices
484  // ----------------------------------------------------------------------
485 
486  if (pDevice->m_listDevices.count() > 0)
487  {
488  os << "<deviceList>";
489 
490  UPnpDeviceList::iterator it;
491  for ( it = pDevice->m_listDevices.begin();
492  it != pDevice->m_listDevices.end();
493  ++it )
494  {
495  UPnpDevice *pEmbeddedDevice = (*it);
496  OutputDevice( os, pEmbeddedDevice );
497  }
498  os << "</deviceList>";
499  }
500  os << "</device>\n";
501  os << QT_FLUSH;
502 }
503 
505 //
507 
509 {
510  QString sStr;
511 
512  QString sAttributes;
513  NameValues::iterator it;
514  for (it = node.m_pAttributes->begin(); it != node.m_pAttributes->end(); ++it)
515  {
516  sAttributes += QString(" %1='%2'").arg((*it).m_sName, (*it).m_sValue);
517  }
518  sStr = QString("<%1%2>%3</%1>\n").arg(node.m_sName, sAttributes, node.m_sValue);
519 
520  return( sStr );
521 }
522 
524 //
526 
527 QString UPnpDeviceDesc::FormatValue( const QString &sName,
528  const QString &sValue )
529 {
530  QString sStr;
531 
532  if (sValue.length() > 0)
533  sStr = QString("<%1>%2</%1>\n") .arg(sName, sValue);
534 
535  return( sStr );
536 }
537 
539 
540 QString UPnpDeviceDesc::FormatValue( const QString &sName, int nValue )
541 {
542  return( QString("<%1>%2</%1>\n") .arg(sName) .arg(nValue) );
543 }
544 
546 //
548 
549 QString UPnpDeviceDesc::FindDeviceUDN( UPnpDevice *pDevice, QString sST )
550 {
551  // Ignore device version, UPnP is backwards compatible
552  if (sST.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2))
553  return pDevice->GetUDN();
554 
555  if (sST.section(':', 0, -2) == pDevice->GetUDN().section(':', 0, -2))
556  return sST;
557 
558  // ----------------------------------------------------------------------
559  // Check for matching Service
560  // ----------------------------------------------------------------------
561 
562  for (auto sit = pDevice->m_listServices.cbegin();
563  sit != pDevice->m_listServices.cend(); ++sit)
564  {
565  // Ignore the service version, UPnP is backwards compatible
566  if (sST.section(':', 0, -2) == (*sit)->m_sServiceType.section(':', 0, -2))
567  return pDevice->GetUDN();
568  }
569 
570  // ----------------------------------------------------------------------
571  // Check Embedded Devices for a Match
572  // ----------------------------------------------------------------------
573 
574  for (auto *device : qAsConst(pDevice->m_listDevices))
575  {
576  QString sUDN = FindDeviceUDN( device, sST );
577  if (sUDN.length() > 0)
578  return sUDN;
579  }
580 
581  return "";
582 }
583 
585 //
587 
588 UPnpDevice *UPnpDeviceDesc::FindDevice( const QString &sURI )
589 {
590  return FindDevice( &m_rootDevice, sURI );
591 }
592 
594 //
596 
598  const QString &sURI )
599 {
600  // Ignore device version, UPnP is backwards compatible
601  if ( sURI.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2) )
602  return pDevice;
603 
604  // ----------------------------------------------------------------------
605  // Check Embedded Devices for a Match
606  // ----------------------------------------------------------------------
607 
608  for (const auto & dev : qAsConst(pDevice->m_listDevices))
609  {
610  UPnpDevice *pFound = FindDevice(dev, sURI);
611 
612  if (pFound != nullptr)
613  return pFound;
614  }
615 
616  return nullptr;
617 }
618 
620 //
622 
624 {
625  UPnpDeviceDesc *pDevice = nullptr;
626 
627  LOG(VB_UPNP, LOG_DEBUG, QString("UPnpDeviceDesc::Retrieve( %1 )")
628  .arg(sURL));
629 
630  QByteArray buffer;
631 
632  bool ok = GetMythDownloadManager()->download(sURL, &buffer);
633 
634  QString sXml(buffer);
635 
636  if (ok && sXml.startsWith( QString("<?xml") ))
637  {
638  QString sErrorMsg;
639 
640  QDomDocument xml( "upnp" );
641 
642  if ( xml.setContent( sXml, false, &sErrorMsg ))
643  {
644  pDevice = new UPnpDeviceDesc();
645  pDevice->Load( xml );
646  pDevice->m_hostUrl = sURL;
647  pDevice->m_sHostName = pDevice->m_hostUrl.host();
648  }
649  else
650  {
651  LOG(VB_UPNP, LOG_ERR,
652  QString("Error parsing device description xml [%1]")
653  .arg(sErrorMsg));
654  }
655  }
656  else
657  {
658  LOG(VB_UPNP, LOG_ERR, QString("Invalid response '%1'").arg(sXml));
659  }
660 
661  return pDevice;
662 }
663 
665 //
667 
669 {
670  if (m_sHostName.length() == 0)
671  {
672  // Get HostName
673 
674  QString localHostName = QHostInfo::localHostName();
675  if (localHostName.isEmpty())
676  LOG(VB_GENERAL, LOG_ERR,
677  "UPnpDeviceDesc: Error, could not determine host name." + ENO);
678 
679  return MythCoreContext::GetConfiguration()->GetValue("Settings/HostName",
680  localHostName);
681  }
682 
683  return m_sHostName;
684 }
685 
688 //
689 // UPnpDevice Class Implementation
690 //
693 
695  m_sModelNumber(MYTH_BINARY_VERSION),
696  m_sSerialNumber(GetMythSourceVersion()),
697  m_protocolVersion(MYTH_PROTO_VERSION)
698 {
699 
700 // FIXME: UPnPDevice is created via the static initialisation of UPnPDeviceDesc
701 // in upnp.cpp ln 21. This means it's created before the core context is
702 // even intialised so we can't use settings, or any 'core' methods to
703 // help in populating the Device metadata. That is why the URLs below
704 // are relative, not absolute since at this stage we can't determine
705 // which IP or port to use!
706 
707 // NOTE: The icons are defined here and not in devicemaster.xml because they
708 // are also used for the MediaRenderer device and other possible future
709 // devices too.
710 
711  // Large PNG Icon
712  auto *pngIconLrg = new UPnpIcon();
713  pngIconLrg->m_nDepth = 24;
714  pngIconLrg->m_nHeight = 120;
715  pngIconLrg->m_nWidth = 120;
716  pngIconLrg->m_sMimeType = "image/png";
717  pngIconLrg->m_sURL = "/images/icons/upnp_large_icon.png";
718  m_listIcons.append(pngIconLrg);
719 
720  // Large JPG Icon
721  auto *jpgIconLrg = new UPnpIcon();
722  jpgIconLrg->m_nDepth = 24;
723  jpgIconLrg->m_nHeight = 120;
724  jpgIconLrg->m_nWidth = 120;
725  jpgIconLrg->m_sMimeType = "image/jpeg";
726  jpgIconLrg->m_sURL = "/images/icons/upnp_large_icon.jpg";
727  m_listIcons.append(jpgIconLrg);
728 
729  // Small PNG Icon
730  auto *pngIconSm = new UPnpIcon();
731  pngIconSm->m_nDepth = 24;
732  pngIconSm->m_nHeight = 48;
733  pngIconSm->m_nWidth = 48;
734  pngIconSm->m_sMimeType = "image/png";
735  pngIconSm->m_sURL = "/images/icons/upnp_small_icon.png";
736  m_listIcons.append(pngIconSm);
737 
738  // Small JPG Icon
739  auto *jpgIconSm = new UPnpIcon();
740  jpgIconSm->m_nDepth = 24;
741  jpgIconSm->m_nHeight = 48;
742  jpgIconSm->m_nWidth = 48;
743  jpgIconSm->m_sMimeType = "image/jpeg";
744  jpgIconSm->m_sURL = "/images/icons/upnp_small_icon.jpg";
745  m_listIcons.append(jpgIconSm);
746 }
747 
749 {
750  while (!m_listIcons.empty())
751  {
752  delete m_listIcons.back();
753  m_listIcons.pop_back();
754  }
755  while (!m_listServices.empty())
756  {
757  delete m_listServices.back();
758  m_listServices.pop_back();
759  }
760  while (!m_listDevices.empty())
761  {
762  delete m_listDevices.back();
763  m_listDevices.pop_back();
764  }
765 }
766 
767 QString UPnpDevice::GetUDN(void) const
768 {
769  if (m_sUDN.isEmpty())
770  m_sUDN = "uuid:" + LookupUDN( m_sDeviceType );
771 
772  return m_sUDN;
773 }
774 
775 void UPnpDevice::toMap(InfoMap &map) const
776 {
777  map["name"] = m_sFriendlyName;
778  map["modelname"] = m_sModelName;
779  map["modelnumber"] = m_sModelNumber;
780  map["modelurl"] = m_sModelURL;
781  map["modeldescription"] = m_sModelDescription;
782  map["manufacturer"] = m_sManufacturer;
783  map["manufacturerurl"] = m_sManufacturerURL;
784  map["devicetype"] = m_sDeviceType;
785  map["serialnumber"] = m_sSerialNumber;
786  map["UDN"] = m_sUDN;
787  map["UPC"] = m_sUPC;
788  map["protocolversion"] = m_protocolVersion;
789 }
790 
791 UPnpService UPnpDevice::GetService(const QString &urn, bool *found) const
792 {
793  UPnpService srv;
794 
795  bool done = false;
796 
797  for (auto *service : qAsConst(m_listServices))
798  {
799  // Ignore the service version
800  if (service->m_sServiceType.section(':', 0, -2) == urn.section(':', 0, -2))
801  {
802  srv = *service;
803  done = true;
804  break;
805  }
806  }
807 
808  if (!done)
809  {
810  UPnpDeviceList::const_iterator dit = m_listDevices.begin();
811  for (; dit != m_listDevices.end() && !done; ++dit)
812  srv = (*dit)->GetService(urn, &done);
813  }
814 
815  if (found)
816  *found = done;
817 
818  return srv;
819 }
820 
821 QString UPnpDevice::toString(uint padding) const
822 {
823  QString ret =
824  QString("UPnP Device\n"
825  "===========\n"
826  "deviceType: %1\n"
827  "friendlyName: %2\n"
828  "manufacturer: %3\n"
829  "manufacturerURL: %4\n"
830  "modelDescription: %5\n"
831  "modelName: %6\n"
832  "modelNumber: %7\n"
833  "modelURL: %8\n")
834  .arg(m_sDeviceType,
839  m_sModelName,
841  m_sModelURL) +
842  QString("serialNumber: %1\n"
843  "UPC: %2\n"
844  "presentationURL: %3\n"
845  "UDN: %4\n")
846  .arg(m_sSerialNumber,
847  m_sUPC,
849  m_sUDN);
850 
851  if (!m_lstExtra.empty())
852  {
853  ret += "Extra key value pairs\n";
854  for (const auto & extra : qAsConst(m_lstExtra))
855  {
856  ret += extra.m_sName;
857  ret += ":";
858  int int_padding = 18 - (extra.m_sName.length() + 1);
859  for (int i = 0; i < int_padding; i++)
860  ret += " ";
861  ret += QString("%1\n").arg(extra.m_sValue);
862  }
863  }
864 
865  if (!m_listIcons.empty())
866  {
867  ret += "Icon List:\n";
868  for (auto *icon : qAsConst(m_listIcons))
869  ret += icon->toString(padding+2) + "\n";
870  }
871 
872  if (!m_listServices.empty())
873  {
874  ret += "Service List:\n";
875  for (auto *service : qAsConst(m_listServices))
876  ret += service->toString(padding+2) + "\n";
877  }
878 
879  if (!m_listDevices.empty())
880  {
881  ret += "Device List:\n";
882  for (auto *device : qAsConst(m_listDevices))
883  ret += device->toString(padding+2) + "\n";
884  ret += "\n";
885  }
886 
887  // remove trailing newline
888  if (ret.endsWith("\n"))
889  ret = ret.left(ret.length()-1);
890 
891  // add any padding as necessary
892  if (padding)
893  {
894  QString pad;
895  for (uint i = 0; i < padding; i++)
896  pad += " ";
897  ret = pad + ret.replace("\n", QString("\n%1").arg(pad));
898  }
899 
900  return ret;
901 }
UPnpDevice::m_sModelURL
QString m_sModelURL
Definition: upnpdevice.h:112
NameValue
Definition: upnputil.h:52
UPnpDevice::~UPnpDevice
~UPnpDevice()
Definition: upnpdevice.cpp:748
DeviceLocation::g_nAllocated
static int g_nAllocated
Definition: upnpdevice.h:213
UPnpDevice::m_sManufacturer
QString m_sManufacturer
Definition: upnpdevice.h:107
UPnpDevice::m_listIcons
UPnpIconList m_listIcons
Definition: upnpdevice.h:124
ENO
#define ENO
This can be appended to the LOG args with "+".
Definition: mythlogging.h:72
upnpdevice.h
UPnpDeviceDesc::UPnpDeviceDesc
UPnpDeviceDesc()=default
UPnpDeviceDesc::GetValidXML
void GetValidXML(const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent="")
Definition: upnpdevice.cpp:324
UPnpDeviceDesc::GetHostName
QString GetHostName() const
Definition: upnpdevice.cpp:668
UPnpDeviceDesc::Retrieve
static UPnpDeviceDesc * Retrieve(QString &sURL)
Definition: upnpdevice.cpp:623
UPnpDeviceDesc::InternalLoad
void InternalLoad(QDomNode oNode, UPnpDevice *pCurDevice)
Definition: upnpdevice.cpp:111
mythdb.h
NameValue::m_sName
QString m_sName
Definition: upnputil.h:55
UPnpDevice::m_listServices
UPnpServiceList m_listServices
Definition: upnpdevice.h:125
UPnpDevice::GetService
UPnpService GetService(const QString &urn, bool *found=nullptr) const
Definition: upnpdevice.cpp:791
UPnpDevice::m_sUPC
QString m_sUPC
Definition: upnpdevice.h:114
UPnpDevice::toString
QString toString(uint padding=0) const
Definition: upnpdevice.cpp:821
GetMythSourceVersion
const char * GetMythSourceVersion()
Definition: mythversion.cpp:5
UPnpDeviceDesc
Definition: upnpdevice.h:150
UPnpDevice::m_sDeviceType
QString m_sDeviceType
Definition: upnpdevice.h:105
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
UPnpDeviceDesc::FindDeviceUDN
QString FindDeviceUDN(UPnpDevice *pDevice, QString sST)
Definition: upnpdevice.cpp:549
GetMythDB
MythDB * GetMythDB(void)
Definition: mythdb.cpp:50
build_compdb.file
file
Definition: build_compdb.py:55
UPnpDevice::m_sUDN
QString m_sUDN
Definition: upnpdevice.h:116
mythversion.h
InfoMap
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
UPnpDeviceDesc::ProcessServiceList
static void ProcessServiceList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:211
LookupUDN
QString LookupUDN(const QString &sDeviceType)
Definition: upnputil.cpp:47
NameValue::AddAttribute
void AddAttribute(const QString &name, const QString &value, bool required)
Definition: upnputil.h:129
UPnpDeviceDesc::m_rootDevice
UPnpDevice m_rootDevice
Definition: upnpdevice.h:154
UPnpDeviceDesc::SetBoolValue
static void SetBoolValue(const QDomNode &n, bool &nValue)
Definition: upnpdevice.cpp:292
upnp.h
UPnpDevice::toMap
void toMap(InfoMap &map) const
Definition: upnpdevice.cpp:775
UPnpDevice::m_sModelName
QString m_sModelName
Definition: upnpdevice.h:110
mythlogging.h
UPnpDevice::m_securityPin
bool m_securityPin
MythTV specific information.
Definition: upnpdevice.h:121
UPnpDeviceDesc::m_sHostName
QString m_sHostName
Definition: upnpdevice.h:155
UPnpDevice::m_sModelNumber
QString m_sModelNumber
Definition: upnpdevice.h:111
UPnpDevice::m_lstExtra
NameValues m_lstExtra
Definition: upnpdevice.h:118
UPnpIcon
Definition: upnpdevice.h:48
UPnpDevice::m_sSerialNumber
QString m_sSerialNumber
Definition: upnpdevice.h:113
UPnpDevice::m_listDevices
UPnpDeviceList m_listDevices
Definition: upnpdevice.h:126
UPnpDeviceDesc::FormatValue
static QString FormatValue(const NameValue &node)
Definition: upnpdevice.cpp:508
MythDownloadManager::download
bool download(const QString &url, const QString &dest, bool reload=false)
Downloads a URL to a file in blocking mode.
Definition: mythdownloadmanager.cpp:430
UPnpDevice
Definition: upnpdevice.h:101
UPnpDevice::m_sFriendlyName
QString m_sFriendlyName
Definition: upnpdevice.h:106
UPnpDeviceDesc::m_hostUrl
QUrl m_hostUrl
Definition: upnpdevice.h:156
uint
unsigned int uint
Definition: compat.h:144
UPnpDevice::UPnpDevice
UPnpDevice()
Definition: upnpdevice.cpp:694
MYTH_BINARY_VERSION
#define MYTH_BINARY_VERSION
Update this whenever the plug-in ABI changes.
Definition: mythversion.h:15
MYTH_PROTO_VERSION
#define MYTH_PROTO_VERSION
Increment this whenever the MythTV network protocol changes.
Definition: mythversion.h:47
UPnpDeviceDesc::ProcessDeviceList
void ProcessDeviceList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:245
NameValue::m_pAttributes
NameValues * m_pAttributes
Definition: upnputil.h:59
UPnpService
Definition: upnpdevice.h:72
UPnpDeviceDesc::SetNumValue
static void SetNumValue(const QDomNode &n, int &nValue)
Sets nValue param to n.firstChild().toText().nodeValue(), iff neither n.isNull() nor n....
Definition: upnpdevice.cpp:281
mythcorecontext.h
UPnpDeviceDesc::Load
bool Load(const QString &sFileName)
Definition: upnpdevice.cpp:51
QT_FLUSH
#define QT_FLUSH
Definition: upnpdevice.cpp:34
UPnpDevice::m_sModelDescription
QString m_sModelDescription
Definition: upnpdevice.h:109
configuration.h
Configuration::GetValue
virtual int GetValue(const QString &sSetting, int Default)=0
UPnpDevice::m_sManufacturerURL
QString m_sManufacturerURL
Definition: upnpdevice.h:108
UPnpDevice::m_protocolVersion
QString m_protocolVersion
Definition: upnpdevice.h:122
UPnpDeviceDesc::SetStrValue
static void SetStrValue(const QDomNode &n, QString &sValue)
Sets sValue param to n.firstChild().toText().nodeValue(), iff neither n.isNull() nor n....
Definition: upnpdevice.cpp:268
UPnpDeviceDesc::FindDevice
UPnpDevice * FindDevice(const QString &sURI)
Definition: upnpdevice.cpp:588
UPnpDevice::GetUDN
QString GetUDN(void) const
Definition: upnpdevice.cpp:767
UPnpDevice::m_sPresentationURL
QString m_sPresentationURL
Definition: upnpdevice.h:115
UPnpDeviceDesc::ProcessIconList
static void ProcessIconList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:182
mythdownloadmanager.h
NameValue::m_sValue
QString m_sValue
Definition: upnputil.h:56
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145
UPnpDeviceDesc::OutputDevice
void OutputDevice(QTextStream &os, UPnpDevice *pDevice, const QString &sUserAgent="")
Definition: upnpdevice.cpp:349
MythCoreContext::GetConfiguration
static Configuration * GetConfiguration()
Definition: mythcorecontext.cpp:1841