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