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 // Not one of the expected element names... add to extra list.
171 QString sValue = "";
172 SetStrValue( e, sValue );
173 NameValue node = NameValue(e.tagName(), sValue);
174 QDomNamedNodeMap attributes = e.attributes();
175 for (int i = 0; i < attributes.size(); i++)
176 {
177 node.AddAttribute(attributes.item(i).nodeName(),
178 attributes.item(i).nodeValue(),
179 true); // TODO Check whether all attributes are in fact requires for the device xml
180 }
181 pCurDevice->m_lstExtra.push_back(node);
182 }
183 }
184}
185
187//
189
190void UPnpDeviceDesc::ProcessIconList( const QDomNode& oListNode, UPnpDevice *pDevice )
191{
192 for ( QDomNode oNode = oListNode.firstChild();
193 !oNode.isNull();
194 oNode = oNode.nextSibling() )
195 {
196 QDomElement e = oNode.toElement();
197
198 if (e.isNull())
199 continue;
200
201 if ( e.tagName() == "icon" )
202 {
203 auto *pIcon = new UPnpIcon();
204 pDevice->m_listIcons.append( pIcon );
205
206 SetStrValue( e.namedItem( "mimetype" ), pIcon->m_sMimeType );
207 SetNumValue( e.namedItem( "width" ), pIcon->m_nWidth );
208 SetNumValue( e.namedItem( "height" ), pIcon->m_nHeight );
209 SetNumValue( e.namedItem( "depth" ), pIcon->m_nDepth );
210 SetStrValue( e.namedItem( "url" ), pIcon->m_sURL );
211 }
212 }
213}
214
216//
218
219void UPnpDeviceDesc::ProcessServiceList( const QDomNode& oListNode, UPnpDevice *pDevice )
220{
221 for ( QDomNode oNode = oListNode.firstChild();
222 !oNode.isNull();
223 oNode = oNode.nextSibling() )
224 {
225 QDomElement e = oNode.toElement();
226
227 if (e.isNull())
228 continue;
229
230 if ( e.tagName() == "service" )
231 {
232 auto *pService = new UPnpService();
233 pDevice->m_listServices.append( pService );
234
235 SetStrValue(e.namedItem( "serviceType" ), pService->m_sServiceType);
236 SetStrValue(e.namedItem( "serviceId" ), pService->m_sServiceId);
237 SetStrValue(e.namedItem( "SCPDURL" ), pService->m_sSCPDURL);
238 SetStrValue(e.namedItem( "controlURL" ), pService->m_sControlURL);
239 SetStrValue(e.namedItem( "eventSubURL" ), pService->m_sEventSubURL);
240
241 LOG(VB_UPNP, LOG_INFO,
242 QString("ProcessServiceList adding service : %1 : %2 :")
243 .arg(pService->m_sServiceType,
244 pService->m_sServiceId));
245 }
246 }
247}
248
250//
252
253void UPnpDeviceDesc::ProcessDeviceList( const QDomNode& oListNode,
254 UPnpDevice *pDevice )
255{
256 for ( QDomNode oNode = oListNode.firstChild();
257 !oNode.isNull();
258 oNode = oNode.nextSibling() )
259 {
260 QDomElement e = oNode.toElement();
261
262 if (e.isNull())
263 continue;
264
265 if ( e.tagName() == "device")
266 {
267 auto *pNewDevice = new UPnpDevice();
268 pDevice->m_listDevices.append( pNewDevice );
269 InternalLoad( e, pNewDevice );
270 }
271 }
272}
273
276void UPnpDeviceDesc::SetStrValue( const QDomNode &n, QString &sValue )
277{
278 if (!n.isNull())
279 {
280 QDomText oText = n.firstChild().toText();
281
282 if (!oText.isNull())
283 sValue = oText.nodeValue();
284 }
285}
286
289void UPnpDeviceDesc::SetNumValue( const QDomNode &n, int &nValue )
290{
291 if (!n.isNull())
292 {
293 QDomText oText = n.firstChild().toText();
294
295 if (!oText.isNull())
296 nValue = oText.nodeValue().toInt();
297 }
298}
299
300void UPnpDeviceDesc::SetBoolValue( const QDomNode &n, bool &nValue )
301{
302 if (!n.isNull())
303 {
304 QDomText oText = n.firstChild().toText();
305
306 if (!oText.isNull())
307 {
308 QString s = oText.nodeValue();
309 nValue = (s == "yes" || s == "true" || (s.toInt() != 0));
310 }
311 }
312}
313
315//
317
318QString UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort )
319{
320 QString sXML;
321#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
322 QTextStream os( &sXML, QIODevice::WriteOnly );
323#else
324 QTextStream os(&sXML, QIODeviceBase::WriteOnly);
325#endif
326
327 GetValidXML( sBaseAddress, nPort, os );
328 os << Qt::flush;
329 return sXML;
330}
331
333//
335
337 const QString &/*sBaseAddress*/, int /*nPort*/,
338 QTextStream &os, const QString &sUserAgent )
339{
340#if 0
341 os.setEncoding( QTextStream::UnicodeUTF8 );
342#endif
343 os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
344 "<root xmlns=\"urn:schemas-upnp-org:device-1-0\" "
345 " xmlns:mythtv=\"mythtv.org\">\n"
346 "<specVersion>\n"
347 "<major>1</major>\n"
348 "<minor>0</minor>\n"
349 "</specVersion>\n";
350
351 OutputDevice( os, &m_rootDevice, sUserAgent );
352
353 os << "</root>\n";
354 os << Qt::flush;
355}
356
358//
360
361void UPnpDeviceDesc::OutputDevice( QTextStream &os,
362 UPnpDevice *pDevice,
363 const QString &sUserAgent )
364{
365 if (pDevice == nullptr)
366 return;
367
368 QString sFriendlyName = QString( "%1: %2" )
369 .arg( GetHostName(),
370 pDevice->m_sFriendlyName );
371
372 // ----------------------------------------------------------------------
373 // Only override the root device
374 // ----------------------------------------------------------------------
375
376 if (pDevice == &m_rootDevice)
377 sFriendlyName = XmlConfiguration().GetValue("UPnP/FriendlyName", sFriendlyName);
378
379 os << "<device>\n";
380 os << FormatValue( "deviceType" , pDevice->m_sDeviceType );
381 os << FormatValue( "friendlyName" , sFriendlyName );
382
383 // ----------------------------------------------------------------------
384 // XBox 360 needs specific values in the Device Description.
385 //
386 // -=>TODO: This should be externalized in a more generic/extension
387 // kind of way.
388 // ----------------------------------------------------------------------
389
390 bool bIsXbox360 =
391 sUserAgent.startsWith(QString("Xbox/2.0"), Qt::CaseInsensitive) ||
392 sUserAgent.startsWith(QString("Mozilla/4.0"), Qt::CaseInsensitive);
393
394 os << FormatValue( "manufacturer" , pDevice->m_sManufacturer );
395 os << FormatValue( "modelURL" , pDevice->m_sModelURL );
396
397 // HACK
398// if ( bIsXbox360 )
399// {
400// os << FormatValue( "modelName" ,
401// "Windows Media Connect Compatible (MythTV)");
402// }
403// else
404// {
405 os << FormatValue( "modelName" , pDevice->m_sModelName );
406// }
407
408 os << FormatValue( "manufacturerURL" , pDevice->m_sManufacturerURL );
409 os << FormatValue( "modelDescription" , pDevice->m_sModelDescription);
410 os << FormatValue( "modelNumber" , pDevice->m_sModelNumber );
411 os << FormatValue( "serialNumber" , pDevice->m_sSerialNumber );
412 os << FormatValue( "UPC" , pDevice->m_sUPC );
413 os << FormatValue( "presentationURL" , pDevice->m_sPresentationURL );
414
415 // MythTV Custom information
416 os << FormatValue( "mythtv:X_secure" ,
417 pDevice->m_securityPin ? "true" : "false");
418 os << FormatValue( "mythtv:X_protocol", pDevice->m_protocolVersion );
419
420 for (const auto & nit : std::as_const(pDevice->m_lstExtra))
421 {
422 os << FormatValue( nit );
423 }
424
425 // ----------------------------------------------------------------------
426 // Output Any Icons.
427 // ----------------------------------------------------------------------
428
429 if (pDevice->m_listIcons.count() > 0)
430 {
431 os << "<iconList>\n";
432
433 for (auto *icon : std::as_const(pDevice->m_listIcons))
434 {
435 os << "<icon>\n";
436 os << FormatValue( "mimetype", icon->m_sMimeType );
437 os << FormatValue( "width" , icon->m_nWidth );
438 os << FormatValue( "height" , icon->m_nHeight );
439 os << FormatValue( "depth" , icon->m_nDepth );
440 os << FormatValue( "url" , icon->m_sURL );
441 os << "</icon>\n";
442 }
443 os << "</iconList>\n";
444 }
445
446 os << FormatValue( "UDN" , pDevice->GetUDN() );
447
448 // ----------------------------------------------------------------------
449 // Output any Services
450 // ----------------------------------------------------------------------
451
452 if (pDevice->m_listServices.count() > 0)
453 {
454 // ------------------------------------------------------------------
455 // -=>TODO: As a temporary fix don't expose the MSRR service unless we
456 // as an XBox360 or Windows MediaPlayer.
457 //
458 // There is a problem with a DSM-520 with firmware 1.04 and
459 // the Denon AVR-4306 receiver.
460 //
461 // If the MSRR Service is exposed, it won't let us browse
462 // any media content.
463 //
464 // Need to find out real fix and remove this code.
465 // ------------------------------------------------------------------
466
467#if 0
468 bool bDSM = sUserAgent.startsWith("INTEL_NMPR/2.1 DLNADOC/1.00", false);
469#endif
470
471 os << "<serviceList>\n";
472
473 for (auto *service : std::as_const(pDevice->m_listServices))
474 {
475 if (!bIsXbox360 && service->m_sServiceType.startsWith(
476 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar",
477 Qt::CaseInsensitive))
478 {
479 continue;
480 }
481
482 os << "<service>\n";
483 os << FormatValue( "serviceType", service->m_sServiceType );
484 os << FormatValue( "serviceId" , service->m_sServiceId );
485 os << FormatValue( "SCPDURL" , service->m_sSCPDURL );
486 os << FormatValue( "controlURL" , service->m_sControlURL );
487 os << FormatValue( "eventSubURL", service->m_sEventSubURL );
488 os << "</service>\n";
489 }
490 os << "</serviceList>\n";
491 }
492
493 // ----------------------------------------------------------------------
494 // Output any Embedded Devices
495 // ----------------------------------------------------------------------
496
497 if (pDevice->m_listDevices.count() > 0)
498 {
499 os << "<deviceList>";
500
501 UPnpDeviceList::iterator it;
502 for ( it = pDevice->m_listDevices.begin();
503 it != pDevice->m_listDevices.end();
504 ++it )
505 {
506 UPnpDevice *pEmbeddedDevice = (*it);
507 OutputDevice( os, pEmbeddedDevice );
508 }
509 os << "</deviceList>";
510 }
511 os << "</device>\n";
512 os << Qt::flush;
513}
514
516//
518
520{
521 QString sStr;
522
523 QString sAttributes;
524 NameValues::iterator it;
525 for (it = node.m_pAttributes->begin(); it != node.m_pAttributes->end(); ++it)
526 {
527 sAttributes += QString(" %1='%2'").arg((*it).m_sName, (*it).m_sValue);
528 }
529 sStr = QString("<%1%2>%3</%1>\n").arg(node.m_sName, sAttributes, node.m_sValue);
530
531 return sStr;
532}
533
535//
537
538QString UPnpDeviceDesc::FormatValue( const QString &sName,
539 const QString &sValue )
540{
541 QString sStr;
542
543 if (sValue.length() > 0)
544 sStr = QString("<%1>%2</%1>\n") .arg(sName, sValue);
545
546 return sStr;
547}
548
550
551QString UPnpDeviceDesc::FormatValue( const QString &sName, int nValue )
552{
553 return( QString("<%1>%2</%1>\n") .arg(sName) .arg(nValue) );
554}
555
557//
559
560QString UPnpDeviceDesc::FindDeviceUDN( UPnpDevice *pDevice, QString sST )
561{
562 // Ignore device version, UPnP is backwards compatible
563 if (sST.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2))
564 return pDevice->GetUDN();
565
566 if (sST.section(':', 0, -2) == pDevice->GetUDN().section(':', 0, -2))
567 return sST;
568
569 // ----------------------------------------------------------------------
570 // Check for matching Service
571 // ----------------------------------------------------------------------
572
573 for (auto sit = pDevice->m_listServices.cbegin();
574 sit != pDevice->m_listServices.cend(); ++sit)
575 {
576 // Ignore the service version, UPnP is backwards compatible
577 if (sST.section(':', 0, -2) == (*sit)->m_sServiceType.section(':', 0, -2))
578 return pDevice->GetUDN();
579 }
580
581 // ----------------------------------------------------------------------
582 // Check Embedded Devices for a Match
583 // ----------------------------------------------------------------------
584
585 for (auto *device : std::as_const(pDevice->m_listDevices))
586 {
587 QString sUDN = FindDeviceUDN( device, sST );
588 if (sUDN.length() > 0)
589 return sUDN;
590 }
591
592 return "";
593}
594
596//
598
600{
601 return FindDevice( &m_rootDevice, sURI );
602}
603
605//
607
609 const QString &sURI )
610{
611 // Ignore device version, UPnP is backwards compatible
612 if ( sURI.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2) )
613 return pDevice;
614
615 // ----------------------------------------------------------------------
616 // Check Embedded Devices for a Match
617 // ----------------------------------------------------------------------
618
619 for (const auto & dev : std::as_const(pDevice->m_listDevices))
620 {
621 UPnpDevice *pFound = FindDevice(dev, sURI);
622
623 if (pFound != nullptr)
624 return pFound;
625 }
626
627 return nullptr;
628}
629
631//
633
635{
636 UPnpDeviceDesc *pDevice = nullptr;
637
638 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpDeviceDesc::Retrieve( %1 )")
639 .arg(sURL));
640
641 QByteArray buffer;
642
643 bool ok = GetMythDownloadManager()->download(sURL, &buffer);
644
645 QString sXml(buffer);
646
647 if (ok && sXml.startsWith( QString("<?xml") ))
648 {
649 QDomDocument xml( "upnp" );
650
651#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
652 QString sErrorMsg;
653 bool success = xml.setContent( sXml, false, &sErrorMsg );
654#else
655 auto parseResult = xml.setContent( sXml );
656 bool success { parseResult };
657 QString sErrorMsg { parseResult.errorMessage };
658#endif
659 if ( success )
660 {
661 pDevice = new UPnpDeviceDesc();
662 pDevice->Load( xml );
663 pDevice->m_hostUrl = QUrl(sURL);
664 pDevice->m_sHostName = pDevice->m_hostUrl.host();
665 }
666 else
667 {
668 LOG(VB_UPNP, LOG_ERR,
669 QString("Error parsing device description xml [%1]")
670 .arg(sErrorMsg));
671 }
672 }
673 else
674 {
675 LOG(VB_UPNP, LOG_ERR, QString("Invalid response '%1'").arg(sXml));
676 }
677
678 return pDevice;
679}
680
682//
684
686{
687 if (m_sHostName.length() == 0)
688 {
689 // Get HostName
690
691 QString localHostName = QHostInfo::localHostName();
692 if (localHostName.isEmpty())
693 LOG(VB_GENERAL, LOG_ERR,
694 "UPnpDeviceDesc: Error, could not determine host name." + ENO);
695
696 return XmlConfiguration().GetValue("Settings/HostName", localHostName);
697 }
698
699 return m_sHostName;
700}
701
704//
705// UPnpDevice Class Implementation
706//
709
711 m_sModelNumber(MYTH_BINARY_VERSION),
712 m_sSerialNumber(GetMythSourceVersion()),
713 m_protocolVersion(MYTH_PROTO_VERSION)
714{
715
716// FIXME: UPnPDevice is created via the static initialisation of UPnPDeviceDesc
717// in upnp.cpp ln 21. This means it's created before the core context is
718// even intialised so we can't use settings, or any 'core' methods to
719// help in populating the Device metadata. That is why the URLs below
720// are relative, not absolute since at this stage we can't determine
721// which IP or port to use!
722
723// NOTE: The icons are defined here and not in devicemaster.xml because they
724// are also used for the MediaRenderer device and other possible future
725// devices too.
726
727 // Large PNG Icon
728 auto *pngIconLrg = new UPnpIcon();
729 pngIconLrg->m_nDepth = 24;
730 pngIconLrg->m_nHeight = 120;
731 pngIconLrg->m_nWidth = 120;
732 pngIconLrg->m_sMimeType = "image/png";
733 pngIconLrg->m_sURL = "/images/icons/upnp_large_icon.png";
734 m_listIcons.append(pngIconLrg);
735
736 // Large JPG Icon
737 auto *jpgIconLrg = new UPnpIcon();
738 jpgIconLrg->m_nDepth = 24;
739 jpgIconLrg->m_nHeight = 120;
740 jpgIconLrg->m_nWidth = 120;
741 jpgIconLrg->m_sMimeType = "image/jpeg";
742 jpgIconLrg->m_sURL = "/images/icons/upnp_large_icon.jpg";
743 m_listIcons.append(jpgIconLrg);
744
745 // Small PNG Icon
746 auto *pngIconSm = new UPnpIcon();
747 pngIconSm->m_nDepth = 24;
748 pngIconSm->m_nHeight = 48;
749 pngIconSm->m_nWidth = 48;
750 pngIconSm->m_sMimeType = "image/png";
751 pngIconSm->m_sURL = "/images/icons/upnp_small_icon.png";
752 m_listIcons.append(pngIconSm);
753
754 // Small JPG Icon
755 auto *jpgIconSm = new UPnpIcon();
756 jpgIconSm->m_nDepth = 24;
757 jpgIconSm->m_nHeight = 48;
758 jpgIconSm->m_nWidth = 48;
759 jpgIconSm->m_sMimeType = "image/jpeg";
760 jpgIconSm->m_sURL = "/images/icons/upnp_small_icon.jpg";
761 m_listIcons.append(jpgIconSm);
762}
763
765{
766 while (!m_listIcons.empty())
767 {
768 delete m_listIcons.back();
769 m_listIcons.pop_back();
770 }
771 while (!m_listServices.empty())
772 {
773 delete m_listServices.back();
774 m_listServices.pop_back();
775 }
776 while (!m_listDevices.empty())
777 {
778 delete m_listDevices.back();
779 m_listDevices.pop_back();
780 }
781}
782
783QString UPnpDevice::GetUDN(void) const
784{
785 if (m_sUDN.isEmpty())
786 m_sUDN = "uuid:" + LookupUDN( m_sDeviceType );
787
788 return m_sUDN;
789}
790
792{
793 map["name"] = m_sFriendlyName;
794 map["modelname"] = m_sModelName;
795 map["modelnumber"] = m_sModelNumber;
796 map["modelurl"] = m_sModelURL;
797 map["modeldescription"] = m_sModelDescription;
798 map["manufacturer"] = m_sManufacturer;
799 map["manufacturerurl"] = m_sManufacturerURL;
800 map["devicetype"] = m_sDeviceType;
801 map["serialnumber"] = m_sSerialNumber;
802 map["UDN"] = m_sUDN;
803 map["UPC"] = m_sUPC;
804 map["protocolversion"] = m_protocolVersion;
805}
806
807UPnpService UPnpDevice::GetService(const QString &urn, bool *found) const
808{
809 UPnpService srv;
810
811 bool done = false;
812
813 for (auto *service : std::as_const(m_listServices))
814 {
815 // Ignore the service version
816 if (service->m_sServiceType.section(':', 0, -2) == urn.section(':', 0, -2))
817 {
818 srv = *service;
819 done = true;
820 break;
821 }
822 }
823
824 if (!done)
825 {
826 UPnpDeviceList::const_iterator dit = m_listDevices.begin();
827 for (; dit != m_listDevices.end() && !done; ++dit)
828 srv = (*dit)->GetService(urn, &done);
829 }
830
831 if (found)
832 *found = done;
833
834 return srv;
835}
836
837QString UPnpDevice::toString(uint padding) const
838{
839 QString ret =
840 QString("UPnP Device\n"
841 "===========\n"
842 "deviceType: %1\n"
843 "friendlyName: %2\n"
844 "manufacturer: %3\n"
845 "manufacturerURL: %4\n"
846 "modelDescription: %5\n"
847 "modelName: %6\n"
848 "modelNumber: %7\n"
849 "modelURL: %8\n")
850 .arg(m_sDeviceType,
857 m_sModelURL) +
858 QString("serialNumber: %1\n"
859 "UPC: %2\n"
860 "presentationURL: %3\n"
861 "UDN: %4\n")
862 .arg(m_sSerialNumber,
863 m_sUPC,
865 m_sUDN);
866
867 if (!m_lstExtra.empty())
868 {
869 ret += "Extra key value pairs\n";
870 for (const auto & extra : std::as_const(m_lstExtra))
871 {
872 ret += extra.m_sName;
873 ret += ":";
874 int int_padding = 18 - (extra.m_sName.length() + 1);
875 for (int i = 0; i < int_padding; i++)
876 ret += " ";
877 ret += QString("%1\n").arg(extra.m_sValue);
878 }
879 }
880
881 if (!m_listIcons.empty())
882 {
883 ret += "Icon List:\n";
884 for (auto *icon : std::as_const(m_listIcons))
885 ret += icon->toString(padding+2) + "\n";
886 }
887
888 if (!m_listServices.empty())
889 {
890 ret += "Service List:\n";
891 for (auto *service : std::as_const(m_listServices))
892 ret += service->toString(padding+2) + "\n";
893 }
894
895 if (!m_listDevices.empty())
896 {
897 ret += "Device List:\n";
898 for (auto *device : std::as_const(m_listDevices))
899 ret += device->toString(padding+2) + "\n";
900 ret += "\n";
901 }
902
903 // remove trailing newline
904 if (ret.endsWith("\n"))
905 ret = ret.left(ret.length()-1);
906
907 // add any padding as necessary
908 if (padding)
909 {
910 QString pad;
911 for (uint i = 0; i < padding; i++)
912 pad += " ";
913 ret = pad + ret.replace("\n", QString("\n%1").arg(pad));
914 }
915
916 return ret;
917}
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:685
void OutputDevice(QTextStream &os, UPnpDevice *pDevice, const QString &sUserAgent="")
Definition: upnpdevice.cpp:361
void ProcessDeviceList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:253
static QString FormatValue(const NameValue &node)
Definition: upnpdevice.cpp:519
void GetValidXML(const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent="")
Definition: upnpdevice.cpp:336
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:289
static UPnpDeviceDesc * Retrieve(QString &sURL)
Definition: upnpdevice.cpp:634
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:560
UPnpDevice * FindDevice(const QString &sURI)
Definition: upnpdevice.cpp:599
UPnpDeviceDesc()=default
static void ProcessServiceList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:219
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:276
static void ProcessIconList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:190
static void SetBoolValue(const QDomNode &n, bool &nValue)
Definition: upnpdevice.cpp:300
NameValues m_lstExtra
Definition: upnpdevice.h:122
QString m_sModelDescription
Definition: upnpdevice.h:113
QString GetUDN(void) const
Definition: upnpdevice.cpp:783
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:791
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:807
UPnpServiceList m_listServices
Definition: upnpdevice.h:129
QString toString(uint padding=0) const
Definition: upnpdevice.cpp:837
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