00001
00002
00003
00004
00005
00006
00007
00008
00010
00011 #include "upnp.h"
00012 #include "upnpdevice.h"
00013 #include "httpcomms.h"
00014
00015 #include <unistd.h>
00016
00017 #include <qfile.h>
00018
00019 int DeviceLocation::g_nAllocated = 0;
00020
00023
00024
00025
00028
00030
00032
00033 UPnpDeviceDesc::UPnpDeviceDesc()
00034 {
00035 VERBOSE( VB_UPNP, "UPnpDeviceDesc - Constructor" );
00036 }
00037
00039
00041
00042 UPnpDeviceDesc::~UPnpDeviceDesc()
00043 {
00044
00045
00046 }
00047
00049
00051
00052 bool UPnpDeviceDesc::Load( const QString &sFileName )
00053 {
00054
00055
00056
00057
00058 QDomDocument doc ( "upnp" );
00059 QFile file( sFileName );
00060
00061 if ( !file.open( IO_ReadOnly ) )
00062 return false;
00063
00064 QString sErrMsg;
00065 int nErrLine = 0;
00066 int nErrCol = 0;
00067 bool bSuccess = doc.setContent( &file, false, &sErrMsg, &nErrLine, &nErrCol );
00068
00069 file.close();
00070
00071 if (!bSuccess)
00072 {
00073 VERBOSE(VB_IMPORTANT, QString("UPnpDeviceDesc::Load - "
00074 "Error parsing: %1 "
00075 "at line: %2 column: %3")
00076 .arg( sFileName )
00077 .arg( nErrLine )
00078 .arg( nErrCol ));
00079
00080 VERBOSE(VB_IMPORTANT, QString("UPnpDeviceDesc::Load - Error Msg: %1" )
00081 .arg( sErrMsg ));
00082 return false;
00083 }
00084
00085
00086
00087
00088
00089 return Load( doc );
00090 }
00091
00093
00095
00096 bool UPnpDeviceDesc::Load( const QDomDocument &xmlDevDesc )
00097 {
00098
00099
00100
00101
00102 QDomNode oNode = xmlDevDesc.documentElement();
00103
00104 _InternalLoad( oNode.namedItem( "device" ), &m_rootDevice );
00105
00106 return true;
00107 }
00108
00110
00112
00113 void UPnpDeviceDesc::_InternalLoad( QDomNode oNode, UPnpDevice *pCurDevice )
00114 {
00115 for ( oNode = oNode.firstChild(); !oNode.isNull(); oNode = oNode.nextSibling() )
00116 {
00117 QDomElement e = oNode.toElement();
00118
00119 if (!e.isNull())
00120 {
00121 if ( e.tagName() == "deviceType" ) { SetStrValue( e, pCurDevice->m_sDeviceType ); continue; }
00122 if ( e.tagName() == "friendlyName" ) { SetStrValue( e, pCurDevice->m_sFriendlyName ); continue; }
00123 if ( e.tagName() == "manufacturer" ) { SetStrValue( e, pCurDevice->m_sManufacturer ); continue; }
00124 if ( e.tagName() == "manufacturerURL" ) { SetStrValue( e, pCurDevice->m_sManufacturerURL ); continue; }
00125 if ( e.tagName() == "modelDescription" ) { SetStrValue( e, pCurDevice->m_sModelDescription); continue; }
00126 if ( e.tagName() == "modelName" ) { SetStrValue( e, pCurDevice->m_sModelName ); continue; }
00127 if ( e.tagName() == "modelNumber" ) { SetStrValue( e, pCurDevice->m_sModelNumber ); continue; }
00128 if ( e.tagName() == "modelURL" ) { SetStrValue( e, pCurDevice->m_sModelURL ); continue; }
00129 if ( e.tagName() == "serialNumber" ) { SetStrValue( e, pCurDevice->m_sSerialNumber ); continue; }
00130 if ( e.tagName() == "UPC" ) { SetStrValue( e, pCurDevice->m_sUPC ); continue; }
00131 if ( e.tagName() == "presentationURL" ) { SetStrValue( e, pCurDevice->m_sPresentationURL ); continue; }
00132 if ( e.tagName() == "UDN" ) { SetStrValue( e, pCurDevice->m_sUDN ); continue; }
00133
00134 if ( e.tagName() == "iconList" ) { ProcessIconList ( oNode, pCurDevice ); continue; }
00135 if ( e.tagName() == "serviceList" ) { ProcessServiceList( oNode, pCurDevice ); continue; }
00136 if ( e.tagName() == "deviceList" ) { ProcessDeviceList ( oNode, pCurDevice ); continue; }
00137
00138
00139
00140 QString sValue = "";
00141
00142 SetStrValue( e, sValue );
00143
00144 pCurDevice->m_lstExtra.append( new NameValue( e.tagName(), sValue ));
00145 }
00146 }
00147 }
00148
00150
00152
00153 void UPnpDeviceDesc::ProcessIconList( QDomNode oListNode, UPnpDevice *pDevice )
00154 {
00155 for ( QDomNode oNode = oListNode.firstChild(); !oNode.isNull(); oNode = oNode.nextSibling() )
00156 {
00157 QDomElement e = oNode.toElement();
00158
00159 if (!e.isNull())
00160 {
00161 if ( e.tagName() == "icon" )
00162 {
00163 UPnpIcon *pIcon = new UPnpIcon();
00164 pDevice->m_listIcons.append( pIcon );
00165
00166 SetStrValue( e.namedItem( "mimetype" ), pIcon->m_sMimeType );
00167 SetNumValue( e.namedItem( "width" ), pIcon->m_nWidth );
00168 SetNumValue( e.namedItem( "height" ), pIcon->m_nHeight );
00169 SetNumValue( e.namedItem( "depth" ), pIcon->m_nDepth );
00170 SetStrValue( e.namedItem( "url" ), pIcon->m_sURL );
00171 }
00172 }
00173 }
00174 }
00175
00177
00179
00180 void UPnpDeviceDesc::ProcessServiceList( QDomNode oListNode, UPnpDevice *pDevice )
00181 {
00182 for ( QDomNode oNode = oListNode.firstChild(); !oNode.isNull(); oNode = oNode.nextSibling() )
00183 {
00184 QDomElement e = oNode.toElement();
00185
00186 if (!e.isNull())
00187 {
00188 if ( e.tagName() == "service" )
00189 {
00190 UPnpService *pService = new UPnpService();
00191 pDevice->m_listServices.append( pService );
00192
00193 SetStrValue( e.namedItem( "serviceType" ), pService->m_sServiceType );
00194 SetStrValue( e.namedItem( "serviceId" ), pService->m_sServiceId );
00195 SetStrValue( e.namedItem( "SCPDURL" ), pService->m_sSCPDURL );
00196 SetStrValue( e.namedItem( "controlURL" ), pService->m_sControlURL );
00197 SetStrValue( e.namedItem( "eventSubURL" ), pService->m_sEventSubURL );
00198
00199 VERBOSE(VB_UPNP,QString("ProcessServiceList adding service : %1 : %2 :")
00200 .arg(pService->m_sServiceType)
00201 .arg(pService->m_sServiceId ));
00202 }
00203 }
00204 }
00205 }
00206
00208
00210
00211 void UPnpDeviceDesc::ProcessDeviceList( QDomNode oListNode, UPnpDevice *pDevice )
00212 {
00213 for ( QDomNode oNode = oListNode.firstChild(); !oNode.isNull(); oNode = oNode.nextSibling() )
00214 {
00215 QDomElement e = oNode.toElement();
00216
00217 if (!e.isNull())
00218 {
00219 if ( e.tagName() == "device")
00220 {
00221 UPnpDevice *pNewDevice = new UPnpDevice();
00222 pDevice->m_listDevices.append( pNewDevice );
00223
00224 _InternalLoad( e, pNewDevice );
00225 }
00226 }
00227 }
00228 }
00229
00231
00233
00234 void UPnpDeviceDesc::SetStrValue( const QDomNode &n, QString &sValue )
00235 {
00236 if (!n.isNull())
00237 {
00238 QDomText oText = n.firstChild().toText();
00239
00240 if (!oText.isNull())
00241 sValue = oText.nodeValue();
00242 }
00243 }
00244
00246
00248
00249 void UPnpDeviceDesc::SetNumValue( const QDomNode &n, int &nValue )
00250 {
00251 if (!n.isNull())
00252 {
00253 QDomText oText = n.firstChild().toText();
00254
00255 if (!oText.isNull())
00256 nValue = oText.nodeValue().toInt();
00257 }
00258 }
00259
00261
00263
00264 QString UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort )
00265 {
00266 QString sXML;
00267 QTextStream os( sXML, IO_WriteOnly );
00268
00269 GetValidXML( sBaseAddress, nPort, os );
00270
00271 return( sXML );
00272 }
00273
00275
00277
00278 void UPnpDeviceDesc::GetValidXML( const QString &sBaseAddress, int nPort, QTextStream &os, const QString &sUserAgent )
00279 {
00280
00281
00282 os << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
00283 "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
00284 "<specVersion>\n"
00285 "<major>1</major>\n"
00286 "<minor>0</minor>\n"
00287 "</specVersion>\n"
00288 "<URLBase>http://" << sBaseAddress << ":" << nPort << "/</URLBase>\n";
00289
00290 OutputDevice( os, &m_rootDevice, sUserAgent );
00291
00292 os << "</root>\n";
00293 }
00294
00296
00298
00299 void UPnpDeviceDesc::OutputDevice( QTextStream &os,
00300 UPnpDevice *pDevice,
00301 const QString &sUserAgent )
00302 {
00303 if (pDevice == NULL)
00304 return;
00305
00306 QString sFriendlyName = QString( "%1: %2" )
00307 .arg( GetHostName() )
00308 .arg( pDevice->m_sFriendlyName );
00309
00310
00311
00312
00313
00314 if (pDevice == &m_rootDevice)
00315 sFriendlyName = UPnp::g_pConfig->GetValue( "UPnP/FriendlyName", sFriendlyName );
00316
00317 os << "<device>\n";
00318 os << FormatValue( "deviceType" , pDevice->m_sDeviceType );
00319 os << FormatValue( "friendlyName" , sFriendlyName );
00320
00321
00322
00323
00324
00325
00326
00327
00328 bool bIsXbox360 = sUserAgent.startsWith( "Xbox/2.0", false ) || sUserAgent.startsWith( "Mozilla/4.0", false);
00329
00330 os << FormatValue( "manufacturer" , pDevice->m_sManufacturer );
00331 os << FormatValue( "modelURL" , pDevice->m_sModelURL );
00332
00333 if ( bIsXbox360 )
00334 {
00335 os << FormatValue( "modelName" , "Windows Media Connect Compatible (MythTV)");
00336 }
00337 else
00338 {
00339 os << FormatValue( "modelName" , pDevice->m_sModelName );
00340 }
00341
00342 os << FormatValue( "manufacturerURL" , pDevice->m_sManufacturerURL );
00343 os << FormatValue( "modelDescription" , pDevice->m_sModelDescription);
00344 os << FormatValue( "modelNumber" , pDevice->m_sModelNumber );
00345 os << FormatValue( "serialNumber" , pDevice->m_sSerialNumber );
00346 os << FormatValue( "UPC" , pDevice->m_sUPC );
00347 os << FormatValue( "presentationURL" , pDevice->m_sPresentationURL );
00348
00349 for (NameValue *pNV = pDevice->m_lstExtra.first();
00350 pNV != NULL;
00351 pNV = pDevice->m_lstExtra.next() )
00352 {
00353
00354
00355
00356 if (pNV->sName == "dlna:X_DLNADOC")
00357 os << QString( "<dlna:X_DLNADOC xmlns:dlna=\"urn:schemas-dlna-org:device-1-0\">%1</dlna:X_DLNADOC>\n" ).arg( pNV->sValue);
00358 else
00359 os << FormatValue( pNV->sName, pNV->sValue );
00360 }
00361
00362
00363
00364
00365
00366 if (pDevice->m_listIcons.count() > 0)
00367 {
00368 os << "<iconList>\n";
00369
00370 for ( UPnpIcon *pIcon = pDevice->m_listIcons.first();
00371 pIcon != NULL;
00372 pIcon = pDevice->m_listIcons.next() )
00373 {
00374
00375 os << "<icon>\n";
00376 os << FormatValue( "mimetype", pIcon->m_sMimeType );
00377 os << FormatValue( "width" , pIcon->m_nWidth );
00378 os << FormatValue( "height" , pIcon->m_nHeight );
00379 os << FormatValue( "depth" , pIcon->m_nDepth );
00380 os << FormatValue( "url" , pIcon->m_sURL );
00381 os << "</icon>\n";
00382 }
00383 os << "</iconList>\n";
00384 }
00385
00386 os << FormatValue( "UDN" , pDevice->GetUDN() );
00387
00388
00389
00390
00391
00392 if (pDevice->m_listServices.count() > 0)
00393 {
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409 os << "<serviceList>\n";
00410
00411 for ( UPnpService *pService = pDevice->m_listServices.first();
00412 pService != NULL;
00413 pService = pDevice->m_listServices.next() )
00414 {
00415 if ( !bIsXbox360 && pService->m_sServiceType.startsWith( "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar", false ))
00416 continue;
00417
00418 os << "<service>\n";
00419 os << FormatValue( "serviceType", pService->m_sServiceType );
00420 os << FormatValue( "serviceId" , pService->m_sServiceId );
00421 os << FormatValue( "SCPDURL" , pService->m_sSCPDURL );
00422 os << FormatValue( "controlURL" , pService->m_sControlURL );
00423 os << FormatValue( "eventSubURL", pService->m_sEventSubURL );
00424 os << "</service>\n";
00425 }
00426 os << "</serviceList>\n";
00427 }
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451 os << "</device>\n";
00452 }
00453
00455
00457
00458 QString UPnpDeviceDesc::FormatValue( const QString &sName, const QString &sValue )
00459 {
00460 QString sStr;
00461
00462 if (sValue.length() > 0)
00463 sStr = QString( "<%1>%2</%3>\n" ).arg( sName ).arg(sValue).arg( sName );
00464
00465 return( sStr );
00466 }
00467
00469
00470 QString UPnpDeviceDesc::FormatValue( const QString &sName, int nValue )
00471 {
00472 return( QString( "<%1>%2</%1>\n" ).arg( sName ).arg(nValue) );
00473 }
00474
00476
00478
00479 QString UPnpDeviceDesc::FindDeviceUDN( UPnpDevice *pDevice, QString sST )
00480 {
00481 if (sST == pDevice->m_sDeviceType)
00482 return( pDevice->GetUDN() );
00483
00484 if (sST == pDevice->GetUDN())
00485 return( sST );
00486
00487
00488
00489
00490
00491 for ( UPnpService *pService = pDevice->m_listServices.first();
00492 pService != NULL;
00493 pService = pDevice->m_listServices.next() )
00494 {
00495 if (sST == pService->m_sServiceType)
00496 return( pDevice->GetUDN() );
00497 }
00498
00499
00500
00501
00502
00503 for ( UPnpDevice *pEmbeddedDevice = pDevice->m_listDevices.first();
00504 pEmbeddedDevice != NULL;
00505 pEmbeddedDevice = pDevice->m_listDevices.next() )
00506 {
00507 QString sUDN = FindDeviceUDN( pEmbeddedDevice, sST );
00508
00509 if (sUDN.length() > 0)
00510 return( sUDN );
00511 }
00512
00513 return( "" );
00514 }
00515
00517
00519
00520 UPnpDevice *UPnpDeviceDesc::FindDevice( const QString &sURI )
00521 {
00522 return FindDevice( &m_rootDevice, sURI );
00523 }
00524
00526
00528
00529 UPnpDevice *UPnpDeviceDesc::FindDevice( UPnpDevice *pDevice, const QString &sURI )
00530
00531 {
00532 if ( sURI == pDevice->m_sDeviceType )
00533 return pDevice;
00534
00535
00536
00537
00538
00539 for ( UPnpDevice *pEmbeddedDevice = pDevice->m_listDevices.first();
00540 pEmbeddedDevice != NULL;
00541 pEmbeddedDevice = pDevice->m_listDevices.next() )
00542 {
00543 UPnpDevice *pFound = FindDevice( pEmbeddedDevice, sURI );
00544
00545 if (pFound != NULL)
00546 return pFound;
00547 }
00548
00549 return NULL;
00550 }
00551
00553
00555
00556 UPnpDeviceDesc *UPnpDeviceDesc::Retrieve( QString &sURL, bool bInQtThread )
00557 {
00558 UPnpDeviceDesc *pDevice = NULL;
00559
00560 VERBOSE( VB_UPNP, QString( "UPnpDeviceDesc::Retrieve( %1, %2 ) - Requesting Device Description." )
00561 .arg( sURL )
00562 .arg( bInQtThread ));
00563
00564 QString sXml = HttpComms::getHttp( sURL,
00565 10000,
00566 3,
00567 0,
00568 false,
00569 NULL,
00570 bInQtThread );
00571
00572 if (sXml.startsWith( "<?xml" ))
00573 {
00574 QString sErrorMsg;
00575
00576 QDomDocument xml( "upnp" );
00577
00578 if ( xml.setContent( sXml, false, &sErrorMsg ))
00579 {
00580 pDevice = new UPnpDeviceDesc();
00581
00582 pDevice->Load( xml );
00583
00584 pDevice->m_HostUrl = sURL;
00585 pDevice->m_sHostName = pDevice->m_HostUrl.host();
00586 }
00587 else
00588 {
00589 VERBOSE( VB_UPNP, QString( "UPnp::Retrieve - Error parsing device description xml [%1]" )
00590 .arg( sErrorMsg ));
00591 }
00592 }
00593 else
00594 {
00595 VERBOSE( VB_UPNP, QString( "UPnp::Retrieve - Invalid response from %1" )
00596 .arg( sURL ));
00597 }
00598
00599 return pDevice;
00600 }
00601
00603
00605
00606 QString UPnpDeviceDesc::GetHostName()
00607 {
00608 if (m_sHostName.length() == 0)
00609 {
00610
00611
00612 char localHostName[1024];
00613
00614 if (gethostname(localHostName, 1024))
00615 VERBOSE(VB_IMPORTANT, "UPnpDeviceDesc: Error, could not determine host name." + ENO);
00616
00617 return UPnp::g_pConfig->GetValue( "Settings/HostName", QString( localHostName ));
00618 }
00619
00620 return m_sHostName;
00621 }