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 QTextStream os( &sXML, QIODevice::WriteOnly );
323
324 GetValidXML( sBaseAddress, nPort, os );
325 os << Qt::flush;
326 return( sXML );
327}
328
330//
332
334 const QString &/*sBaseAddress*/, int /*nPort*/,
335 QTextStream &os, const QString &sUserAgent )
336{
337#if 0
338 os.setEncoding( QTextStream::UnicodeUTF8 );
339#endif
340 os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
341 "<root xmlns=\"urn:schemas-upnp-org:device-1-0\" "
342 " xmlns:mythtv=\"mythtv.org\">\n"
343 "<specVersion>\n"
344 "<major>1</major>\n"
345 "<minor>0</minor>\n"
346 "</specVersion>\n";
347
348 OutputDevice( os, &m_rootDevice, sUserAgent );
349
350 os << "</root>\n";
351 os << Qt::flush;
352}
353
355//
357
358void UPnpDeviceDesc::OutputDevice( QTextStream &os,
359 UPnpDevice *pDevice,
360 const QString &sUserAgent )
361{
362 if (pDevice == nullptr)
363 return;
364
365 QString sFriendlyName = QString( "%1: %2" )
366 .arg( GetHostName(),
367 pDevice->m_sFriendlyName );
368
369 // ----------------------------------------------------------------------
370 // Only override the root device
371 // ----------------------------------------------------------------------
372
373 if (pDevice == &m_rootDevice)
374 sFriendlyName = XmlConfiguration().GetValue("UPnP/FriendlyName", sFriendlyName);
375
376 os << "<device>\n";
377 os << FormatValue( "deviceType" , pDevice->m_sDeviceType );
378 os << FormatValue( "friendlyName" , sFriendlyName );
379
380 // ----------------------------------------------------------------------
381 // XBox 360 needs specific values in the Device Description.
382 //
383 // -=>TODO: This should be externalized in a more generic/extension
384 // kind of way.
385 // ----------------------------------------------------------------------
386
387 bool bIsXbox360 =
388 sUserAgent.startsWith(QString("Xbox/2.0"), Qt::CaseInsensitive) ||
389 sUserAgent.startsWith(QString("Mozilla/4.0"), Qt::CaseInsensitive);
390
391 os << FormatValue( "manufacturer" , pDevice->m_sManufacturer );
392 os << FormatValue( "modelURL" , pDevice->m_sModelURL );
393
394 // HACK
395// if ( bIsXbox360 )
396// {
397// os << FormatValue( "modelName" ,
398// "Windows Media Connect Compatible (MythTV)");
399// }
400// else
401// {
402 os << FormatValue( "modelName" , pDevice->m_sModelName );
403// }
404
405 os << FormatValue( "manufacturerURL" , pDevice->m_sManufacturerURL );
406 os << FormatValue( "modelDescription" , pDevice->m_sModelDescription);
407 os << FormatValue( "modelNumber" , pDevice->m_sModelNumber );
408 os << FormatValue( "serialNumber" , pDevice->m_sSerialNumber );
409 os << FormatValue( "UPC" , pDevice->m_sUPC );
410 os << FormatValue( "presentationURL" , pDevice->m_sPresentationURL );
411
412 // MythTV Custom information
413 os << FormatValue( "mythtv:X_secure" ,
414 pDevice->m_securityPin ? "true" : "false");
415 os << FormatValue( "mythtv:X_protocol", pDevice->m_protocolVersion );
416
417 for (const auto & nit : std::as_const(pDevice->m_lstExtra))
418 {
419 os << FormatValue( nit );
420 }
421
422 // ----------------------------------------------------------------------
423 // Output Any Icons.
424 // ----------------------------------------------------------------------
425
426 if (pDevice->m_listIcons.count() > 0)
427 {
428 os << "<iconList>\n";
429
430 for (auto *icon : std::as_const(pDevice->m_listIcons))
431 {
432 os << "<icon>\n";
433 os << FormatValue( "mimetype", icon->m_sMimeType );
434 os << FormatValue( "width" , icon->m_nWidth );
435 os << FormatValue( "height" , icon->m_nHeight );
436 os << FormatValue( "depth" , icon->m_nDepth );
437 os << FormatValue( "url" , icon->m_sURL );
438 os << "</icon>\n";
439 }
440 os << "</iconList>\n";
441 }
442
443 os << FormatValue( "UDN" , pDevice->GetUDN() );
444
445 // ----------------------------------------------------------------------
446 // Output any Services
447 // ----------------------------------------------------------------------
448
449 if (pDevice->m_listServices.count() > 0)
450 {
451 // ------------------------------------------------------------------
452 // -=>TODO: As a temporary fix don't expose the MSRR service unless we
453 // as an XBox360 or Windows MediaPlayer.
454 //
455 // There is a problem with a DSM-520 with firmware 1.04 and
456 // the Denon AVR-4306 receiver.
457 //
458 // If the MSRR Service is exposed, it won't let us browse
459 // any media content.
460 //
461 // Need to find out real fix and remove this code.
462 // ------------------------------------------------------------------
463
464#if 0
465 bool bDSM = sUserAgent.startsWith("INTEL_NMPR/2.1 DLNADOC/1.00", false);
466#endif
467
468 os << "<serviceList>\n";
469
470 for (auto *service : std::as_const(pDevice->m_listServices))
471 {
472 if (!bIsXbox360 && service->m_sServiceType.startsWith(
473 "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar",
474 Qt::CaseInsensitive))
475 {
476 continue;
477 }
478
479 os << "<service>\n";
480 os << FormatValue( "serviceType", service->m_sServiceType );
481 os << FormatValue( "serviceId" , service->m_sServiceId );
482 os << FormatValue( "SCPDURL" , service->m_sSCPDURL );
483 os << FormatValue( "controlURL" , service->m_sControlURL );
484 os << FormatValue( "eventSubURL", service->m_sEventSubURL );
485 os << "</service>\n";
486 }
487 os << "</serviceList>\n";
488 }
489
490 // ----------------------------------------------------------------------
491 // Output any Embedded Devices
492 // ----------------------------------------------------------------------
493
494 if (pDevice->m_listDevices.count() > 0)
495 {
496 os << "<deviceList>";
497
498 UPnpDeviceList::iterator it;
499 for ( it = pDevice->m_listDevices.begin();
500 it != pDevice->m_listDevices.end();
501 ++it )
502 {
503 UPnpDevice *pEmbeddedDevice = (*it);
504 OutputDevice( os, pEmbeddedDevice );
505 }
506 os << "</deviceList>";
507 }
508 os << "</device>\n";
509 os << Qt::flush;
510}
511
513//
515
517{
518 QString sStr;
519
520 QString sAttributes;
521 NameValues::iterator it;
522 for (it = node.m_pAttributes->begin(); it != node.m_pAttributes->end(); ++it)
523 {
524 sAttributes += QString(" %1='%2'").arg((*it).m_sName, (*it).m_sValue);
525 }
526 sStr = QString("<%1%2>%3</%1>\n").arg(node.m_sName, sAttributes, node.m_sValue);
527
528 return( sStr );
529}
530
532//
534
535QString UPnpDeviceDesc::FormatValue( const QString &sName,
536 const QString &sValue )
537{
538 QString sStr;
539
540 if (sValue.length() > 0)
541 sStr = QString("<%1>%2</%1>\n") .arg(sName, sValue);
542
543 return( sStr );
544}
545
547
548QString UPnpDeviceDesc::FormatValue( const QString &sName, int nValue )
549{
550 return( QString("<%1>%2</%1>\n") .arg(sName) .arg(nValue) );
551}
552
554//
556
557QString UPnpDeviceDesc::FindDeviceUDN( UPnpDevice *pDevice, QString sST )
558{
559 // Ignore device version, UPnP is backwards compatible
560 if (sST.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2))
561 return pDevice->GetUDN();
562
563 if (sST.section(':', 0, -2) == pDevice->GetUDN().section(':', 0, -2))
564 return sST;
565
566 // ----------------------------------------------------------------------
567 // Check for matching Service
568 // ----------------------------------------------------------------------
569
570 for (auto sit = pDevice->m_listServices.cbegin();
571 sit != pDevice->m_listServices.cend(); ++sit)
572 {
573 // Ignore the service version, UPnP is backwards compatible
574 if (sST.section(':', 0, -2) == (*sit)->m_sServiceType.section(':', 0, -2))
575 return pDevice->GetUDN();
576 }
577
578 // ----------------------------------------------------------------------
579 // Check Embedded Devices for a Match
580 // ----------------------------------------------------------------------
581
582 for (auto *device : std::as_const(pDevice->m_listDevices))
583 {
584 QString sUDN = FindDeviceUDN( device, sST );
585 if (sUDN.length() > 0)
586 return sUDN;
587 }
588
589 return "";
590}
591
593//
595
597{
598 return FindDevice( &m_rootDevice, sURI );
599}
600
602//
604
606 const QString &sURI )
607{
608 // Ignore device version, UPnP is backwards compatible
609 if ( sURI.section(':', 0, -2) == pDevice->m_sDeviceType.section(':', 0, -2) )
610 return pDevice;
611
612 // ----------------------------------------------------------------------
613 // Check Embedded Devices for a Match
614 // ----------------------------------------------------------------------
615
616 for (const auto & dev : std::as_const(pDevice->m_listDevices))
617 {
618 UPnpDevice *pFound = FindDevice(dev, sURI);
619
620 if (pFound != nullptr)
621 return pFound;
622 }
623
624 return nullptr;
625}
626
628//
630
632{
633 UPnpDeviceDesc *pDevice = nullptr;
634
635 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpDeviceDesc::Retrieve( %1 )")
636 .arg(sURL));
637
638 QByteArray buffer;
639
640 bool ok = GetMythDownloadManager()->download(sURL, &buffer);
641
642 QString sXml(buffer);
643
644 if (ok && sXml.startsWith( QString("<?xml") ))
645 {
646 QDomDocument xml( "upnp" );
647
648#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
649 QString sErrorMsg;
650 bool success = xml.setContent( sXml, false, &sErrorMsg );
651#else
652 auto parseResult = xml.setContent( sXml );
653 bool success { parseResult };
654 QString sErrorMsg { parseResult.errorMessage };
655#endif
656 if ( success )
657 {
658 pDevice = new UPnpDeviceDesc();
659 pDevice->Load( xml );
660 pDevice->m_hostUrl = sURL;
661 pDevice->m_sHostName = pDevice->m_hostUrl.host();
662 }
663 else
664 {
665 LOG(VB_UPNP, LOG_ERR,
666 QString("Error parsing device description xml [%1]")
667 .arg(sErrorMsg));
668 }
669 }
670 else
671 {
672 LOG(VB_UPNP, LOG_ERR, QString("Invalid response '%1'").arg(sXml));
673 }
674
675 return pDevice;
676}
677
679//
681
683{
684 if (m_sHostName.length() == 0)
685 {
686 // Get HostName
687
688 QString localHostName = QHostInfo::localHostName();
689 if (localHostName.isEmpty())
690 LOG(VB_GENERAL, LOG_ERR,
691 "UPnpDeviceDesc: Error, could not determine host name." + ENO);
692
693 return XmlConfiguration().GetValue("Settings/HostName", localHostName);
694 }
695
696 return m_sHostName;
697}
698
701//
702// UPnpDevice Class Implementation
703//
706
708 m_sModelNumber(MYTH_BINARY_VERSION),
709 m_sSerialNumber(GetMythSourceVersion()),
710 m_protocolVersion(MYTH_PROTO_VERSION)
711{
712
713// FIXME: UPnPDevice is created via the static initialisation of UPnPDeviceDesc
714// in upnp.cpp ln 21. This means it's created before the core context is
715// even intialised so we can't use settings, or any 'core' methods to
716// help in populating the Device metadata. That is why the URLs below
717// are relative, not absolute since at this stage we can't determine
718// which IP or port to use!
719
720// NOTE: The icons are defined here and not in devicemaster.xml because they
721// are also used for the MediaRenderer device and other possible future
722// devices too.
723
724 // Large PNG Icon
725 auto *pngIconLrg = new UPnpIcon();
726 pngIconLrg->m_nDepth = 24;
727 pngIconLrg->m_nHeight = 120;
728 pngIconLrg->m_nWidth = 120;
729 pngIconLrg->m_sMimeType = "image/png";
730 pngIconLrg->m_sURL = "/images/icons/upnp_large_icon.png";
731 m_listIcons.append(pngIconLrg);
732
733 // Large JPG Icon
734 auto *jpgIconLrg = new UPnpIcon();
735 jpgIconLrg->m_nDepth = 24;
736 jpgIconLrg->m_nHeight = 120;
737 jpgIconLrg->m_nWidth = 120;
738 jpgIconLrg->m_sMimeType = "image/jpeg";
739 jpgIconLrg->m_sURL = "/images/icons/upnp_large_icon.jpg";
740 m_listIcons.append(jpgIconLrg);
741
742 // Small PNG Icon
743 auto *pngIconSm = new UPnpIcon();
744 pngIconSm->m_nDepth = 24;
745 pngIconSm->m_nHeight = 48;
746 pngIconSm->m_nWidth = 48;
747 pngIconSm->m_sMimeType = "image/png";
748 pngIconSm->m_sURL = "/images/icons/upnp_small_icon.png";
749 m_listIcons.append(pngIconSm);
750
751 // Small JPG Icon
752 auto *jpgIconSm = new UPnpIcon();
753 jpgIconSm->m_nDepth = 24;
754 jpgIconSm->m_nHeight = 48;
755 jpgIconSm->m_nWidth = 48;
756 jpgIconSm->m_sMimeType = "image/jpeg";
757 jpgIconSm->m_sURL = "/images/icons/upnp_small_icon.jpg";
758 m_listIcons.append(jpgIconSm);
759}
760
762{
763 while (!m_listIcons.empty())
764 {
765 delete m_listIcons.back();
766 m_listIcons.pop_back();
767 }
768 while (!m_listServices.empty())
769 {
770 delete m_listServices.back();
771 m_listServices.pop_back();
772 }
773 while (!m_listDevices.empty())
774 {
775 delete m_listDevices.back();
776 m_listDevices.pop_back();
777 }
778}
779
780QString UPnpDevice::GetUDN(void) const
781{
782 if (m_sUDN.isEmpty())
783 m_sUDN = "uuid:" + LookupUDN( m_sDeviceType );
784
785 return m_sUDN;
786}
787
789{
790 map["name"] = m_sFriendlyName;
791 map["modelname"] = m_sModelName;
792 map["modelnumber"] = m_sModelNumber;
793 map["modelurl"] = m_sModelURL;
794 map["modeldescription"] = m_sModelDescription;
795 map["manufacturer"] = m_sManufacturer;
796 map["manufacturerurl"] = m_sManufacturerURL;
797 map["devicetype"] = m_sDeviceType;
798 map["serialnumber"] = m_sSerialNumber;
799 map["UDN"] = m_sUDN;
800 map["UPC"] = m_sUPC;
801 map["protocolversion"] = m_protocolVersion;
802}
803
804UPnpService UPnpDevice::GetService(const QString &urn, bool *found) const
805{
806 UPnpService srv;
807
808 bool done = false;
809
810 for (auto *service : std::as_const(m_listServices))
811 {
812 // Ignore the service version
813 if (service->m_sServiceType.section(':', 0, -2) == urn.section(':', 0, -2))
814 {
815 srv = *service;
816 done = true;
817 break;
818 }
819 }
820
821 if (!done)
822 {
823 UPnpDeviceList::const_iterator dit = m_listDevices.begin();
824 for (; dit != m_listDevices.end() && !done; ++dit)
825 srv = (*dit)->GetService(urn, &done);
826 }
827
828 if (found)
829 *found = done;
830
831 return srv;
832}
833
834QString UPnpDevice::toString(uint padding) const
835{
836 QString ret =
837 QString("UPnP Device\n"
838 "===========\n"
839 "deviceType: %1\n"
840 "friendlyName: %2\n"
841 "manufacturer: %3\n"
842 "manufacturerURL: %4\n"
843 "modelDescription: %5\n"
844 "modelName: %6\n"
845 "modelNumber: %7\n"
846 "modelURL: %8\n")
847 .arg(m_sDeviceType,
854 m_sModelURL) +
855 QString("serialNumber: %1\n"
856 "UPC: %2\n"
857 "presentationURL: %3\n"
858 "UDN: %4\n")
859 .arg(m_sSerialNumber,
860 m_sUPC,
862 m_sUDN);
863
864 if (!m_lstExtra.empty())
865 {
866 ret += "Extra key value pairs\n";
867 for (const auto & extra : std::as_const(m_lstExtra))
868 {
869 ret += extra.m_sName;
870 ret += ":";
871 int int_padding = 18 - (extra.m_sName.length() + 1);
872 for (int i = 0; i < int_padding; i++)
873 ret += " ";
874 ret += QString("%1\n").arg(extra.m_sValue);
875 }
876 }
877
878 if (!m_listIcons.empty())
879 {
880 ret += "Icon List:\n";
881 for (auto *icon : std::as_const(m_listIcons))
882 ret += icon->toString(padding+2) + "\n";
883 }
884
885 if (!m_listServices.empty())
886 {
887 ret += "Service List:\n";
888 for (auto *service : std::as_const(m_listServices))
889 ret += service->toString(padding+2) + "\n";
890 }
891
892 if (!m_listDevices.empty())
893 {
894 ret += "Device List:\n";
895 for (auto *device : std::as_const(m_listDevices))
896 ret += device->toString(padding+2) + "\n";
897 ret += "\n";
898 }
899
900 // remove trailing newline
901 if (ret.endsWith("\n"))
902 ret = ret.left(ret.length()-1);
903
904 // add any padding as necessary
905 if (padding)
906 {
907 QString pad;
908 for (uint i = 0; i < padding; i++)
909 pad += " ";
910 ret = pad + ret.replace("\n", QString("\n%1").arg(pad));
911 }
912
913 return ret;
914}
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:682
void OutputDevice(QTextStream &os, UPnpDevice *pDevice, const QString &sUserAgent="")
Definition: upnpdevice.cpp:358
void ProcessDeviceList(const QDomNode &oListNode, UPnpDevice *pDevice)
Definition: upnpdevice.cpp:254
static QString FormatValue(const NameValue &node)
Definition: upnpdevice.cpp:516
void GetValidXML(const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent="")
Definition: upnpdevice.cpp:333
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:631
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:557
UPnpDevice * FindDevice(const QString &sURI)
Definition: upnpdevice.cpp:596
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:780
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:788
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:804
UPnpServiceList m_listServices
Definition: upnpdevice.h:129
QString toString(uint padding=0) const
Definition: upnpdevice.cpp:834
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: freesurround.h:24
MythDB * GetMythDB(void)
Definition: mythdb.cpp:51
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