00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00012
00013 #include <cmath>
00014 #include <algorithm>
00015 using namespace std;
00016
00017 #include "upnp.h"
00018 #include "upnpcds.h"
00019 #include "upnputil.h"
00020 #include "mythlogging.h"
00021
00022 #define DIDL_LITE_BEGIN "<DIDL-Lite xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:upnp=\"urn:schemas-upnp-org:metadata-1-0/upnp/\" xmlns=\"urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/\">"
00023 #define DIDL_LITE_END "</DIDL-Lite>";
00024
00026
00028
00029 void UPnpCDSExtensionResults::Add( CDSObject *pObject )
00030 {
00031 if (pObject)
00032 m_List.append( pObject );
00033 }
00034
00036
00038
00039 QString UPnpCDSExtensionResults::GetResultXML(FilterMap &filter)
00040 {
00041 QString sXML;
00042
00043 CDSObjects::const_iterator it = m_List.begin();
00044 for (; it != m_List.end(); ++it)
00045 sXML += (*it)->toXml(filter);
00046
00047 return sXML;
00048 }
00049
00051
00053
00054 UPnpCDS::UPnpCDS( UPnpDevice *pDevice, const QString &sSharePath )
00055 : Eventing( "UPnpCDS", "CDS_Event", sSharePath )
00056 {
00057 m_root.m_eType = OT_Container;
00058 m_root.m_sId = "0";
00059 m_root.m_sParentId = "-1";
00060 m_root.m_sTitle = "MythTV";
00061 m_root.m_sClass = "object.container";
00062 m_root.m_bRestricted= true;
00063 m_root.m_bSearchable= true;
00064
00065 AddVariable( new StateVariable< QString >( "TransferIDs" , true ) );
00066 AddVariable( new StateVariable< QString >( "ContainerUpdateIDs", true ) );
00067 AddVariable( new StateVariable< unsigned short >( "SystemUpdateID" , true ) );
00068
00069 SetValue< unsigned short >( "SystemUpdateID", 1 );
00070
00071 QString sUPnpDescPath = UPnp::GetConfiguration()->GetValue( "UPnP/DescXmlPath", sSharePath );
00072
00073 m_sServiceDescFileName = sUPnpDescPath + "CDS_scpd.xml";
00074 m_sControlUrl = "/CDS_Control";
00075
00076
00077
00078
00079 RegisterService( pDevice );
00080 }
00081
00083
00085
00086 UPnpCDS::~UPnpCDS()
00087 {
00088 while (!m_extensions.empty())
00089 {
00090 delete m_extensions.back();
00091 m_extensions.pop_back();
00092 }
00093 }
00094
00096
00098
00099 UPnpCDSMethod UPnpCDS::GetMethod( const QString &sURI )
00100 {
00101 if (sURI == "GetServDesc" ) return CDSM_GetServiceDescription;
00102 if (sURI == "Browse" ) return CDSM_Browse ;
00103 if (sURI == "Search" ) return CDSM_Search ;
00104 if (sURI == "GetSearchCapabilities" ) return CDSM_GetSearchCapabilities;
00105 if (sURI == "GetSortCapabilities" ) return CDSM_GetSortCapabilities ;
00106 if (sURI == "GetSystemUpdateID" ) return CDSM_GetSystemUpdateID ;
00107
00108 return( CDSM_Unknown );
00109 }
00110
00112
00114
00115 UPnpCDSBrowseFlag UPnpCDS::GetBrowseFlag( const QString &sFlag )
00116 {
00117 if (sFlag == "BrowseMetadata" ) return( CDS_BrowseMetadata );
00118 if (sFlag == "BrowseDirectChildren" ) return( CDS_BrowseDirectChildren );
00119
00120 return( CDS_BrowseUnknown );
00121 }
00122
00124
00126
00127 void UPnpCDS::RegisterExtension ( UPnpCDSExtension *pExtension )
00128 {
00129 if (pExtension != NULL )
00130 m_extensions.append( pExtension );
00131 }
00132
00134
00136
00137 void UPnpCDS::UnregisterExtension( UPnpCDSExtension *pExtension )
00138 {
00139 if (pExtension)
00140 {
00141 delete pExtension;
00142 m_extensions.removeAll(pExtension);
00143 }
00144 }
00145
00147
00149
00150 QStringList UPnpCDS::GetBasePaths()
00151 {
00152 return Eventing::GetBasePaths() << m_sControlUrl;
00153 }
00154
00156
00158
00159 bool UPnpCDS::ProcessRequest( HTTPRequest *pRequest )
00160 {
00161 if (pRequest)
00162 {
00163 if (Eventing::ProcessRequest( pRequest ))
00164 return true;
00165
00166 if ( pRequest->m_sBaseUrl != m_sControlUrl )
00167 {
00168 #if 0
00169 LOG(VB_UPNP, LOG_DEBUG,
00170 QString("UPnpCDS::ProcessRequest - BaseUrl (%1) not ours...")
00171 .arg(pRequest->m_sBaseUrl));
00172 #endif
00173 return false;
00174 }
00175
00176 switch( GetMethod( pRequest->m_sMethod ) )
00177 {
00178 case CDSM_GetServiceDescription :
00179 pRequest->FormatFileResponse( m_sServiceDescFileName );
00180 break;
00181 case CDSM_Browse :
00182 HandleBrowse( pRequest );
00183 break;
00184 case CDSM_Search :
00185 HandleSearch( pRequest );
00186 break;
00187 case CDSM_GetSearchCapabilities :
00188 HandleGetSearchCapabilities( pRequest );
00189 break;
00190 case CDSM_GetSortCapabilities :
00191 HandleGetSortCapabilities( pRequest );
00192 break;
00193 case CDSM_GetSystemUpdateID :
00194 HandleGetSystemUpdateID( pRequest );
00195 break;
00196 default:
00197 UPnp::FormatErrorResponse( pRequest, UPnPResult_InvalidAction );
00198 break;
00199 }
00200
00201 return true;
00202 }
00203
00204 return false;
00205 }
00206
00207 static UPnpCDSClientException clientExceptions[] = {
00208
00209 { CDS_ClientWMP,
00210 "User-Agent",
00211 "Windows-Media-Player/" },
00212
00213 { CDS_ClientWMP,
00214 "User-Agent",
00215 "Mozilla/4.0 (compatible; UPnP/1.0; Windows 9x" },
00216
00217 { CDS_ClientXBMC,
00218 "User-Agent",
00219 "Platinum/" },
00220
00221 { CDS_ClientXBox,
00222 "User-Agent",
00223 "Xbox" },
00224
00225 { CDS_ClientSonyDB,
00226 "X-AV-Client-Info",
00227 "cn=\"Sony Corporation\"; mn=\"Blu-ray Disc Player\"" },
00228 };
00229 static uint clientExceptionCount = sizeof(clientExceptions) /
00230 sizeof(clientExceptions[0]);
00231
00232 void UPnpCDS::DetermineClient( HTTPRequest *pRequest,
00233 UPnpCDSRequest *pCDSRequest )
00234 {
00235 pCDSRequest->m_eClient = CDS_ClientDefault;
00236 pCDSRequest->m_nClientVersion = 0;
00237 bool found = false;
00238
00239
00240 for ( uint i = 0; !found && i < clientExceptionCount; i++ )
00241 {
00242 UPnpCDSClientException *except = &clientExceptions[i];
00243
00244 QString sHeaderValue = pRequest->GetHeaderValue(except->sHeaderKey, "");
00245 int idx = sHeaderValue.indexOf(except->sHeaderValue);
00246 if (idx != -1)
00247 {
00248 pCDSRequest->m_eClient = except->nClientType;
00249
00250 idx += except->sHeaderValue.length();
00251
00252
00253
00254 if ( sHeaderValue[idx] == '/')
00255 {
00256 idx++;
00257 }
00258
00259
00260 QString version = sHeaderValue.mid(idx).trimmed();
00261 idx = version.indexOf( '.' );
00262 if (idx != -1)
00263 {
00264 idx = version.indexOf( '.', idx + 1 );
00265 }
00266 if (idx != -1)
00267 {
00268 version = version.left( idx );
00269 }
00270 idx = version.indexOf( ' ' );
00271 if (idx != -1)
00272 {
00273 version = version.left( idx );
00274 }
00275
00276 pCDSRequest->m_nClientVersion = version.toDouble();
00277
00278 LOG(VB_UPNP, LOG_INFO,
00279 QString("DetermineClient %1:%2 Identified as %3 version %4")
00280 .arg(except->sHeaderKey) .arg(sHeaderValue)
00281 .arg(pCDSRequest->m_eClient)
00282 .arg(pCDSRequest->m_nClientVersion));
00283 found = true;
00284 }
00285 }
00286 }
00287
00288
00290
00292
00293 void UPnpCDS::HandleBrowse( HTTPRequest *pRequest )
00294 {
00295 UPnpCDSExtensionResults *pResult = NULL;
00296 UPnpCDSRequest request;
00297
00298 DetermineClient( pRequest, &request );
00299 request.m_sObjectId = pRequest->m_mapParams[ "ObjectID" ];
00300 request.m_sContainerID = pRequest->m_mapParams[ "ContainerID" ];
00301 request.m_sParentId = "0";
00302 request.m_eBrowseFlag =
00303 GetBrowseFlag( pRequest->m_mapParams[ "BrowseFlag" ] );
00304 request.m_sFilter = pRequest->m_mapParams[ "Filter" ];
00305 request.m_nStartingIndex =
00306 pRequest->m_mapParams[ "StartingIndex" ].toLong();
00307 request.m_nRequestedCount =
00308 pRequest->m_mapParams[ "RequestedCount"].toLong();
00309 request.m_sSortCriteria = pRequest->m_mapParams[ "SortCriteria" ];
00310
00311 #if 0
00312 LOG(VB_UPNP, LOG_DEBUG, QString("UPnpCDS::ProcessRequest \n"
00313 ": url = %1 \n"
00314 ": Method = %2 \n"
00315 ": ObjectId = %3 \n"
00316 ": BrowseFlag = %4 \n"
00317 ": Filter = %5 \n"
00318 ": StartingIndex = %6 \n"
00319 ": RequestedCount = %7 \n"
00320 ": SortCriteria = %8 " )
00321 .arg( pRequest->m_sBaseUrl )
00322 .arg( pRequest->m_sMethod )
00323 .arg( request.m_sObjectId )
00324 .arg( request.m_eBrowseFlag )
00325 .arg( request.m_sFilter )
00326 .arg( request.m_nStartingIndex )
00327 .arg( request.m_nRequestedCount)
00328 .arg( request.m_sSortCriteria ));
00329 #endif
00330
00331 UPnPResultCode eErrorCode = UPnPResult_CDS_NoSuchObject;
00332 QString sErrorDesc = "";
00333 short nNumberReturned = 0;
00334 short nTotalMatches = 0;
00335 short nUpdateID = 0;
00336 QString sResultXML;
00337 FilterMap filter = (FilterMap) request.m_sFilter.split(',');
00338
00339 LOG(VB_UPNP, LOG_INFO,
00340 QString("UPnpCDS::HandleBrowse ObjectID=%1, ContainerId=%2")
00341 .arg(request.m_sObjectId) .arg(request.m_sContainerID));
00342
00343 if (request.m_sObjectId == "0")
00344 {
00345
00346
00347
00348
00349 switch( request.m_eBrowseFlag )
00350 {
00351 case CDS_BrowseMetadata:
00352 {
00353
00354
00355
00356
00357 eErrorCode = UPnPResult_Success;
00358 nNumberReturned = 1;
00359 nTotalMatches = 1;
00360 nUpdateID = m_root.m_nUpdateId;
00361
00362 m_root.SetChildCount( m_extensions.count() );
00363
00364 sResultXML = m_root.toXml(filter);
00365
00366 break;
00367 }
00368
00369 case CDS_BrowseDirectChildren:
00370 {
00371
00372
00373
00374
00375 eErrorCode = UPnPResult_Success;
00376 nTotalMatches = m_extensions.count();
00377 nUpdateID = m_root.m_nUpdateId;
00378
00379 if (request.m_nRequestedCount == 0)
00380 request.m_nRequestedCount = nTotalMatches;
00381
00382 short nStart = Max( request.m_nStartingIndex, short( 0 ));
00383 short nCount = Min( nTotalMatches, request.m_nRequestedCount );
00384
00385 UPnpCDSRequest childRequest;
00386
00387 DetermineClient( pRequest, &request );
00388 childRequest.m_sParentId = "0";
00389 childRequest.m_eBrowseFlag = CDS_BrowseMetadata;
00390 childRequest.m_sFilter = "";
00391 childRequest.m_nStartingIndex = 0;
00392 childRequest.m_nRequestedCount = 1;
00393 childRequest.m_sSortCriteria = "";
00394
00395 for (uint i = nStart;
00396 (i < (uint)m_extensions.size()) &&
00397 (nNumberReturned < nCount);
00398 i++)
00399 {
00400 UPnpCDSExtension *pExtension = m_extensions[i];
00401 childRequest.m_sObjectId = pExtension->m_sExtensionId;
00402
00403 pResult = pExtension->Browse( &childRequest );
00404
00405 if (pResult != NULL)
00406 {
00407 if (pResult->m_eErrorCode == UPnPResult_Success)
00408 {
00409 sResultXML += pResult->GetResultXML(filter);
00410 nNumberReturned ++;
00411 }
00412
00413 delete pResult;
00414 }
00415 }
00416
00417 break;
00418 }
00419 default: break;
00420 }
00421 }
00422 else
00423 {
00424
00425
00426
00427
00428 UPnpCDSExtensionList::iterator it = m_extensions.begin();
00429 for (; (it != m_extensions.end()) && !pResult; ++it)
00430 {
00431 LOG(VB_UPNP, LOG_INFO,
00432 QString("UPNP Browse : Searching for : %1 / ObjectID : %2")
00433 .arg((*it)->m_sExtensionId).arg(request.m_sObjectId));
00434
00435 pResult = (*it)->Browse(&request);
00436 }
00437
00438 if (pResult != NULL)
00439 {
00440 eErrorCode = pResult->m_eErrorCode;
00441 sErrorDesc = pResult->m_sErrorDesc;
00442
00443 if (eErrorCode == UPnPResult_Success)
00444 {
00445 nNumberReturned = pResult->m_List.count();
00446 nTotalMatches = pResult->m_nTotalMatches;
00447 nUpdateID = pResult->m_nUpdateID;
00448 sResultXML = pResult->GetResultXML(filter);
00449 }
00450
00451 delete pResult;
00452 }
00453 }
00454
00455
00456
00457
00458
00459 if (eErrorCode == UPnPResult_Success)
00460 {
00461 NameValues list;
00462
00463 QString sResults = DIDL_LITE_BEGIN;
00464 sResults += sResultXML;
00465 sResults += DIDL_LITE_END;
00466
00467 list.push_back(NameValue("Result", sResults));
00468 list.push_back(NameValue("NumberReturned", nNumberReturned));
00469 list.push_back(NameValue("TotalMatches", nTotalMatches));
00470 list.push_back(NameValue("UpdateID", nUpdateID));
00471
00472 pRequest->FormatActionResponse(list);
00473 }
00474 else
00475 UPnp::FormatErrorResponse ( pRequest, eErrorCode, sErrorDesc );
00476
00477 }
00478
00480
00482
00483 void UPnpCDS::HandleSearch( HTTPRequest *pRequest )
00484 {
00485 UPnpCDSExtensionResults *pResult = NULL;
00486 UPnpCDSRequest request;
00487
00488 UPnPResultCode eErrorCode = UPnPResult_InvalidAction;
00489 QString sErrorDesc = "";
00490 short nNumberReturned = 0;
00491 short nTotalMatches = 0;
00492 short nUpdateID = 0;
00493 QString sResultXML;
00494
00495 DetermineClient( pRequest, &request );
00496 request.m_sObjectId = pRequest->m_mapParams[ "ObjectID" ];
00497 request.m_sContainerID = pRequest->m_mapParams[ "ContainerID" ];
00498 request.m_sFilter = pRequest->m_mapParams[ "Filter" ];
00499 request.m_nStartingIndex =
00500 pRequest->m_mapParams[ "StartingIndex" ].toLong();
00501 request.m_nRequestedCount =
00502 pRequest->m_mapParams[ "RequestedCount"].toLong();
00503 request.m_sSortCriteria = pRequest->m_mapParams[ "SortCriteria" ];
00504 request.m_sSearchCriteria = pRequest->m_mapParams[ "SearchCriteria"];
00505
00506 LOG(VB_UPNP, LOG_INFO,
00507 QString("UPnpCDS::HandleSearch ObjectID=%1, ContainerId=%2")
00508 .arg(request.m_sObjectId) .arg(request.m_sContainerID));
00509
00510
00511
00512
00513
00514
00515 QRegExp rMatch( "\\b(or|and)\\b" );
00516 rMatch.setCaseSensitivity(Qt::CaseInsensitive);
00517
00518 request.m_sSearchList = request.m_sSearchCriteria.split(
00519 rMatch, QString::SkipEmptyParts);
00520 request.m_sSearchClass = "object";
00521
00522
00523
00524
00525
00526
00527 for ( QStringList::Iterator it = request.m_sSearchList.begin();
00528 it != request.m_sSearchList.end();
00529 ++it )
00530 {
00531 if ((*it).contains("upnp:class derivedfrom", Qt::CaseInsensitive))
00532 {
00533 QStringList sParts = (*it).split(' ', QString::SkipEmptyParts);
00534
00535 if (sParts.count() > 2)
00536 {
00537 request.m_sSearchClass = sParts[2].trimmed();
00538 request.m_sSearchClass.remove( '"' );
00539
00540 break;
00541 }
00542 }
00543 }
00544
00545
00546
00547
00548 LOG(VB_UPNP, LOG_INFO, QString("UPnpCDS::ProcessRequest \n"
00549 ": url = %1 \n"
00550 ": Method = %2 \n"
00551 ": ObjectId = %3 \n"
00552 ": SearchCriteria = %4 \n"
00553 ": Filter = %5 \n"
00554 ": StartingIndex = %6 \n"
00555 ": RequestedCount = %7 \n"
00556 ": SortCriteria = %8 \n"
00557 ": SearchClass = %9" )
00558 .arg( pRequest->m_sBaseUrl )
00559 .arg( pRequest->m_sMethod )
00560 .arg( request.m_sObjectId )
00561 .arg( request.m_sSearchCriteria)
00562 .arg( request.m_sFilter )
00563 .arg( request.m_nStartingIndex )
00564 .arg( request.m_nRequestedCount)
00565 .arg( request.m_sSortCriteria )
00566 .arg( request.m_sSearchClass ));
00567
00568 #if 0
00569 bool bSearchDone = false;
00570 #endif
00571
00572 UPnpCDSExtensionList::iterator it = m_extensions.begin();
00573 for (; (it != m_extensions.end()) && !pResult; ++it)
00574 pResult = (*it)->Search(&request);
00575
00576 if (pResult != NULL)
00577 {
00578 eErrorCode = pResult->m_eErrorCode;
00579 sErrorDesc = pResult->m_sErrorDesc;
00580
00581 if (eErrorCode == UPnPResult_Success)
00582 {
00583 FilterMap filter = (FilterMap) request.m_sFilter.split(',');
00584 nNumberReturned = pResult->m_List.count();
00585 nTotalMatches = pResult->m_nTotalMatches;
00586 nUpdateID = pResult->m_nUpdateID;
00587 sResultXML = pResult->GetResultXML(filter);
00588 #if 0
00589 bSearchDone = true;
00590 #endif
00591 }
00592
00593 delete pResult;
00594 }
00595
00596 #if 0
00597 nUpdateID = 0;
00598 LOG(VB_UPNP, LOG_DEBUG, sResultXML);
00599 #endif
00600
00601 if (eErrorCode == UPnPResult_Success)
00602 {
00603 NameValues list;
00604 QString sResults = DIDL_LITE_BEGIN;
00605 sResults += sResultXML;
00606 sResults += DIDL_LITE_END;
00607
00608 list.push_back(NameValue("Result", sResults));
00609 list.push_back(NameValue("NumberReturned", nNumberReturned));
00610 list.push_back(NameValue("TotalMatches", nTotalMatches));
00611 list.push_back(NameValue("UpdateID", nUpdateID));
00612
00613 pRequest->FormatActionResponse(list);
00614 }
00615 else
00616 UPnp::FormatErrorResponse( pRequest, eErrorCode, sErrorDesc );
00617 }
00618
00620
00622
00623 void UPnpCDS::HandleGetSearchCapabilities( HTTPRequest *pRequest )
00624 {
00625 NameValues list;
00626
00627 LOG(VB_UPNP, LOG_INFO,
00628 QString("UPnpCDS::ProcessRequest : %1 : %2")
00629 .arg(pRequest->m_sBaseUrl) .arg(pRequest->m_sMethod));
00630
00631
00632
00633 list.push_back(
00634 NameValue("SearchCaps",
00635 "dc:title,dc:creator,dc:date,upnp:class,res@size"));
00636
00637 pRequest->FormatActionResponse(list);
00638 }
00639
00641
00643
00644 void UPnpCDS::HandleGetSortCapabilities( HTTPRequest *pRequest )
00645 {
00646 NameValues list;
00647
00648 LOG(VB_UPNP, LOG_INFO,
00649 QString("UPnpCDS::ProcessRequest : %1 : %2")
00650 .arg(pRequest->m_sBaseUrl) .arg(pRequest->m_sMethod));
00651
00652
00653
00654 list.push_back(
00655 NameValue("SortCaps",
00656 "dc:title,dc:creator,dc:date,upnp:class,res@size"));
00657
00658 pRequest->FormatActionResponse(list);
00659 }
00660
00662
00664
00665 void UPnpCDS::HandleGetSystemUpdateID( HTTPRequest *pRequest )
00666 {
00667 NameValues list;
00668
00669 LOG(VB_UPNP, LOG_INFO,
00670 QString("UPnpCDS::ProcessRequest : %1 : %2")
00671 .arg(pRequest->m_sBaseUrl) .arg(pRequest->m_sMethod));
00672
00673 unsigned short nId = GetValue<unsigned short>("SystemUpdateID");
00674
00675 list.push_back(NameValue("Id", nId));
00676
00677 pRequest->FormatActionResponse(list);
00678 }
00679
00680
00681
00684
00685
00686
00689
00691
00693
00694 bool UPnpCDSExtension::IsBrowseRequestForUs( UPnpCDSRequest *pRequest )
00695 {
00696 if (!pRequest->m_sObjectId.startsWith(m_sExtensionId, Qt::CaseSensitive))
00697 return false;
00698
00699 return true;
00700 }
00701
00703
00705
00706 UPnpCDSExtensionResults *UPnpCDSExtension::Browse( UPnpCDSRequest *pRequest )
00707 {
00708
00709
00710
00711 if (!IsBrowseRequestForUs( pRequest ))
00712 return( NULL );
00713
00714
00715
00716
00717
00718 QStringList idPath = pRequest->m_sObjectId.section('=',0,0)
00719 .split("/", QString::SkipEmptyParts);
00720
00721 QString key = pRequest->m_sObjectId.section('=',1);
00722
00723 if (idPath.isEmpty())
00724 return( NULL );
00725
00726
00727
00728
00729
00730 UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults();
00731
00732 if (pResults != NULL)
00733 {
00734 if (!key.isEmpty())
00735 idPath.last().append(QString("=%1").arg(key));
00736 else
00737 {
00738 if (pRequest->m_sObjectId.contains("item"))
00739 {
00740 idPath.removeLast();
00741 idPath = idPath.last().split(" ", QString::SkipEmptyParts);
00742 idPath = idPath.first().split('?', QString::SkipEmptyParts);
00743
00744 if (idPath[0].startsWith(QString("Id")))
00745 idPath[0] = QString("item=%1")
00746 .arg(idPath[0].right(idPath[0].length() - 2));
00747 }
00748 }
00749
00750 QString sLast = idPath.last();
00751
00752 pRequest->m_sParentId = pRequest->m_sObjectId;
00753
00754 if (sLast == m_sExtensionId)
00755 return ProcessRoot(pRequest, pResults, idPath);
00756
00757 if (sLast == "0")
00758 return ProcessAll(pRequest, pResults, idPath);
00759
00760 if (sLast.startsWith(QString("key") , Qt::CaseSensitive))
00761 return ProcessKey(pRequest, pResults, idPath);
00762
00763 if (sLast.startsWith(QString("item"), Qt::CaseSensitive))
00764 return ProcessItem(pRequest, pResults, idPath);
00765
00766 int nNodeIdx = sLast.toInt();
00767
00768 if ((nNodeIdx > 0) && (nNodeIdx < GetRootCount()))
00769 return ProcessContainer(pRequest, pResults, nNodeIdx, idPath);
00770
00771 pResults->m_eErrorCode = UPnPResult_CDS_NoSuchObject;
00772 pResults->m_sErrorDesc = "";
00773 }
00774
00775 return( pResults );
00776 }
00777
00779
00781
00782 bool UPnpCDSExtension::IsSearchRequestForUs( UPnpCDSRequest *pRequest )
00783 {
00784 if ( !m_sClass.startsWith( pRequest->m_sSearchClass ))
00785 return false;
00786
00787 return true;
00788 }
00789
00791
00793
00794 UPnpCDSExtensionResults *UPnpCDSExtension::Search( UPnpCDSRequest *pRequest )
00795 {
00796
00797
00798
00799 QStringList sEmptyList;
00800 LOG(VB_UPNP, LOG_INFO,
00801 QString("UPnpCDSExtension::Search : m_sClass = %1 : "
00802 "m_sSearchClass = %2")
00803 .arg(m_sClass).arg(pRequest->m_sSearchClass));
00804
00805 if ( !IsSearchRequestForUs( pRequest ))
00806 {
00807 LOG(VB_UPNP, LOG_INFO,
00808 QString("UPnpCDSExtension::Search - Not For Us : "
00809 "m_sClass = %1 : m_sSearchClass = %2")
00810 .arg(m_sClass).arg(pRequest->m_sSearchClass));
00811 return NULL;
00812 }
00813
00814 UPnpCDSExtensionResults *pResults = new UPnpCDSExtensionResults();
00815
00816 CreateItems( pRequest, pResults, 0, "", false );
00817
00818 return pResults;
00819 }
00820
00822
00824
00825 QString UPnpCDSExtension::RemoveToken( const QString &sToken,
00826 const QString &sStr, int num )
00827 {
00828 QString sResult( "" );
00829 int nPos = -1;
00830
00831 for (int nIdx=0; nIdx < num; nIdx++)
00832 {
00833 if ((nPos = sStr.lastIndexOf( sToken, nPos )) == -1)
00834 break;
00835 }
00836
00837 if (nPos > 0)
00838 sResult = sStr.left( nPos );
00839
00840 return sResult;
00841 }
00842
00844
00846
00847 UPnpCDSExtensionResults *
00848 UPnpCDSExtension::ProcessRoot( UPnpCDSRequest *pRequest,
00849 UPnpCDSExtensionResults *pResults,
00850 QStringList & )
00851 {
00852 pResults->m_nTotalMatches = 0;
00853 pResults->m_nUpdateID = 1;
00854
00855 short nRootCount = GetRootCount();
00856
00857 switch( pRequest->m_eBrowseFlag )
00858 {
00859 case CDS_BrowseMetadata:
00860 {
00861
00862
00863
00864
00865 pResults->m_nTotalMatches = 1;
00866 pResults->m_nUpdateID = 1;
00867
00868 CDSObject *pRoot = CreateContainer( m_sExtensionId, m_sName, "0");
00869
00870 pRoot->SetChildCount( nRootCount );
00871
00872 pResults->Add( pRoot );
00873
00874 break;
00875 }
00876
00877 case CDS_BrowseDirectChildren:
00878 {
00879 LOG(VB_UPNP, LOG_DEBUG, "CDS_BrowseDirectChildren");
00880 pResults->m_nUpdateID = 1;
00881 pResults->m_nTotalMatches = nRootCount ;
00882
00883 if ( pRequest->m_nRequestedCount == 0)
00884 pRequest->m_nRequestedCount = nRootCount ;
00885
00886 short nStart = max(pRequest->m_nStartingIndex, short(0));
00887 short nEnd = min(nRootCount,
00888 short(nStart + pRequest->m_nRequestedCount));
00889
00890 if (nStart < nRootCount)
00891 {
00892 for (short nIdx = nStart; nIdx < nEnd; nIdx++)
00893 {
00894 UPnpCDSRootInfo *pInfo = GetRootInfo( nIdx );
00895 if (pInfo != NULL)
00896 {
00897 QString sId = QString("%1/%2")
00898 .arg(pRequest->m_sObjectId)
00899 .arg(nIdx);
00900
00901 CDSObject *pItem =
00902 CreateContainer( sId, QObject::tr( pInfo->title ),
00903 m_sExtensionId );
00904
00905 pItem->SetChildCount( GetDistinctCount( pInfo ) );
00906
00907 pResults->Add( pItem );
00908 }
00909 }
00910 }
00911 }
00912
00913 case CDS_BrowseUnknown:
00914 default:
00915 break;
00916 }
00917
00918 return pResults;
00919 }
00920
00921
00923
00925
00926 UPnpCDSExtensionResults *
00927 UPnpCDSExtension::ProcessAll ( UPnpCDSRequest *pRequest,
00928 UPnpCDSExtensionResults *pResults,
00929 QStringList & )
00930 {
00931 pResults->m_nTotalMatches = 0;
00932 pResults->m_nUpdateID = 1;
00933
00934
00935
00936
00937
00938 switch( pRequest->m_eBrowseFlag )
00939 {
00940 case CDS_BrowseMetadata:
00941 {
00942
00943
00944
00945
00946 UPnpCDSRootInfo *pInfo = GetRootInfo( 0 );
00947
00948 if (pInfo != NULL)
00949 {
00950 pResults->m_nTotalMatches = 1;
00951 pResults->m_nUpdateID = 1;
00952
00953 CDSObject *pItem =
00954 CreateContainer( pRequest->m_sObjectId,
00955 QObject::tr( pInfo->title ),
00956 m_sExtensionId );
00957
00958 pItem->SetChildCount( GetDistinctCount( pInfo ) );
00959
00960 pResults->Add( pItem );
00961 }
00962
00963 break;
00964 }
00965
00966 case CDS_BrowseDirectChildren:
00967 {
00968 CreateItems( pRequest, pResults, 0, "", false );
00969
00970 break;
00971 }
00972
00973 case CDS_BrowseUnknown:
00974 default:
00975 break;
00976 }
00977
00978 return pResults;
00979 }
00980
00982
00984
00985 UPnpCDSExtensionResults *
00986 UPnpCDSExtension::ProcessItem(UPnpCDSRequest *pRequest,
00987 UPnpCDSExtensionResults *pResults,
00988 QStringList &idPath)
00989 {
00990 pResults->m_nTotalMatches = 0;
00991 pResults->m_nUpdateID = 1;
00992
00993
00994
00995
00996 #if 0
00997 LOG(VB_UPNP, LOG_INFO, QString("UPnpCDSExtension::ProcessItem : %1")
00998 .arg(idPath));
00999 #endif
01000 switch( pRequest->m_eBrowseFlag )
01001 {
01002 case CDS_BrowseMetadata:
01003 {
01004
01005
01006
01007
01008 QStringMap mapParams;
01009 QString sParams = idPath.last().section( '?', 1, 1 );
01010 sParams.replace("&", "&");
01011
01012 HTTPRequest::GetParameters( sParams, mapParams );
01013
01014 MSqlQuery query(MSqlQuery::InitCon());
01015
01016 if (query.isConnected())
01017 {
01018 BuildItemQuery( query, mapParams );
01019
01020 if (query.exec() && query.next())
01021 {
01022 pRequest->m_sObjectId =
01023 RemoveToken( "/", pRequest->m_sObjectId, 1 );
01024
01025 AddItem( pRequest, pRequest->m_sObjectId, pResults, false,
01026 query );
01027 pResults->m_nTotalMatches = 1;
01028 }
01029 }
01030 break;
01031 }
01032 case CDS_BrowseDirectChildren:
01033 {
01034
01035 break;
01036 }
01037 }
01038
01039 return pResults;
01040 }
01041
01043
01045
01046 UPnpCDSExtensionResults *
01047 UPnpCDSExtension::ProcessKey( UPnpCDSRequest *pRequest,
01048 UPnpCDSExtensionResults *pResults,
01049 QStringList &idPath )
01050 {
01051 pResults->m_nTotalMatches = 0;
01052 pResults->m_nUpdateID = 1;
01053
01054
01055
01056
01057
01058 QString sKey = idPath.takeLast().section( '=', 1, 1 );
01059 sKey = QUrl::fromPercentEncoding(sKey.toUtf8());
01060
01061 if (!sKey.isEmpty())
01062 {
01063 int nNodeIdx = idPath.takeLast().toInt();
01064
01065 switch( pRequest->m_eBrowseFlag )
01066 {
01067 case CDS_BrowseMetadata:
01068 {
01069 UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx );
01070
01071 if (pInfo == NULL)
01072 return pResults;
01073
01074 pRequest->m_sParentId =
01075 RemoveToken( "/", pRequest->m_sObjectId, 1 );
01076
01077
01078
01079
01080
01081 MSqlQuery query(MSqlQuery::InitCon());
01082
01083 if (query.isConnected())
01084 {
01085 QString sSQL = QString(pInfo->sql) .arg(pInfo->where);
01086
01087
01088
01089
01090
01091
01092 query.prepare ( sSQL );
01093 query.bindValue( ":KEY", sKey );
01094
01095 if (query.exec() && query.next())
01096 {
01097
01098
01099
01100
01101 pResults->m_nTotalMatches = 1;
01102 pResults->m_nUpdateID = 1;
01103
01104 CDSObject *pItem =
01105 CreateContainer( pRequest->m_sObjectId,
01106 query.value(1).toString(),
01107 pRequest->m_sParentId );
01108
01109 pItem->SetChildCount( GetDistinctCount( pInfo ));
01110
01111 pResults->Add( pItem );
01112 }
01113 }
01114 break;
01115 }
01116
01117 case CDS_BrowseDirectChildren:
01118 {
01119 CreateItems( pRequest, pResults, nNodeIdx, sKey, true );
01120
01121 break;
01122 }
01123
01124 case CDS_BrowseUnknown:
01125 default:
01126 break;
01127 }
01128 }
01129
01130 return pResults;
01131 }
01132
01134
01136
01137 UPnpCDSExtensionResults *
01138 UPnpCDSExtension::ProcessContainer( UPnpCDSRequest *pRequest,
01139 UPnpCDSExtensionResults *pResults,
01140 int nNodeIdx,
01141 QStringList & )
01142 {
01143 pResults->m_nUpdateID = 1;
01144 pResults->m_nTotalMatches = 0;
01145
01146 UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx );
01147
01148 if (pInfo == NULL)
01149 return pResults;
01150
01151 switch( pRequest->m_eBrowseFlag )
01152 {
01153 case CDS_BrowseMetadata:
01154 {
01155
01156
01157
01158
01159 pResults->m_nTotalMatches = 1;
01160 pResults->m_nUpdateID = 1;
01161
01162 CDSObject *pItem = CreateContainer( pRequest->m_sObjectId,
01163 QObject::tr( pInfo->title ),
01164 m_sExtensionId );
01165
01166 pItem->SetChildCount( GetDistinctCount( pInfo ));
01167
01168 pResults->Add( pItem );
01169 break;
01170 }
01171
01172 case CDS_BrowseDirectChildren:
01173 {
01174 pResults->m_nTotalMatches = GetDistinctCount( pInfo );
01175 pResults->m_nUpdateID = 1;
01176
01177 if (pRequest->m_nRequestedCount == 0)
01178 pRequest->m_nRequestedCount = SHRT_MAX;
01179
01180 MSqlQuery query(MSqlQuery::InitCon());
01181
01182 if (query.isConnected())
01183 {
01184
01185 QString sSQL = pInfo->sql;
01186
01187 sSQL.remove( "%1" );
01188 sSQL += QString( " LIMIT %2, %3" )
01189 .arg( pRequest->m_nStartingIndex )
01190 .arg( pRequest->m_nRequestedCount );
01191
01192 query.prepare( sSQL );
01193
01194 if (query.exec())
01195 {
01196 while(query.next())
01197 {
01198 QString sKey = query.value(0).toString();
01199 QString sTitle = query.value(1).toString();
01200 long nCount = query.value(2).toInt();
01201
01202 if (sTitle.length() == 0)
01203 sTitle = "(undefined)";
01204
01205 QString sId = QString( "%1/key=%2" )
01206 .arg( pRequest->m_sParentId )
01207 .arg( sKey );
01208
01209 CDSObject *pRoot =
01210 CreateContainer(sId, sTitle, pRequest->m_sParentId);
01211
01212 pRoot->SetChildCount( nCount );
01213
01214 pResults->Add( pRoot );
01215 }
01216 }
01217 }
01218 break;
01219 }
01220
01221 case CDS_BrowseUnknown:
01222 break;
01223 }
01224
01225 return pResults;
01226 }
01227
01229
01231
01232 int UPnpCDSExtension::GetDistinctCount( UPnpCDSRootInfo *pInfo )
01233 {
01234 int nCount = 0;
01235
01236 if ((pInfo == NULL) || (pInfo->column == NULL))
01237 return 0;
01238
01239 MSqlQuery query(MSqlQuery::InitCon());
01240
01241 if (query.isConnected())
01242 {
01243
01244
01245
01246 QString sSQL;
01247
01248 if (strncmp( pInfo->column, "*", 1) == 0)
01249 {
01250 sSQL = QString( "SELECT count( %1 ) FROM %2" )
01251 .arg( pInfo->column )
01252 .arg( GetTableName( pInfo->column ));
01253 }
01254 else
01255 {
01256 sSQL = QString( "SELECT count( DISTINCT %1 ) FROM %2" )
01257 .arg( pInfo->column )
01258 .arg( GetTableName( pInfo->column ) );
01259 }
01260
01261 query.prepare( sSQL );
01262
01263 if (query.exec() && query.next())
01264 {
01265 nCount = query.value(0).toInt();
01266 }
01267 }
01268
01269 return( nCount );
01270 }
01271
01273
01275
01276 int UPnpCDSExtension::GetCount( const QString &sColumn, const QString &sKey )
01277 {
01278 int nCount = 0;
01279
01280 MSqlQuery query(MSqlQuery::InitCon());
01281
01282 if (query.isConnected())
01283 {
01284 QString sSQL = QString("SELECT count( %1 ) FROM %2")
01285 .arg( sColumn ).arg( GetTableName( sColumn ) );
01286
01287 if ( sKey.length() )
01288 sSQL += " WHERE " + sColumn + " = :KEY";
01289
01290 query.prepare( sSQL );
01291 if ( sKey.length() )
01292 query.bindValue( ":KEY", sKey );
01293
01294 if (query.exec() && query.next())
01295 {
01296 nCount = query.value(0).toInt();
01297 }
01298 LOG(VB_UPNP, LOG_DEBUG, "UPnpCDSExtension::GetCount() - " +
01299 sSQL + " = " + QString::number(nCount));
01300 }
01301
01302 return( nCount );
01303 }
01304
01306
01308
01309 void UPnpCDSExtension::CreateItems( UPnpCDSRequest *pRequest,
01310 UPnpCDSExtensionResults *pResults,
01311 int nNodeIdx,
01312 const QString &sKey,
01313 bool bAddRef )
01314 {
01315 pResults->m_nTotalMatches = 0;
01316 pResults->m_nUpdateID = 1;
01317
01318 UPnpCDSRootInfo *pInfo = GetRootInfo( nNodeIdx );
01319
01320 if (pInfo == NULL)
01321 return;
01322
01323 pResults->m_nTotalMatches = GetCount( pInfo->column, sKey );
01324 pResults->m_nUpdateID = 1;
01325
01326 if (pRequest->m_nRequestedCount == 0)
01327 pRequest->m_nRequestedCount = SHRT_MAX;
01328
01329 MSqlQuery query(MSqlQuery::InitCon());
01330
01331 if (query.isConnected())
01332 {
01333 QString sWhere( "" );
01334 QString sOrder( "" );
01335
01336 if ( sKey.length() > 0)
01337 {
01338 sWhere = QString( "WHERE %1=:KEY " )
01339 .arg( pInfo->column );
01340 }
01341
01342 QString orderColumn( pInfo->orderColumn );
01343 if (orderColumn.length() != 0) {
01344 sOrder = QString( "ORDER BY %1 " )
01345 .arg( orderColumn );
01346 }
01347
01348 QString sSQL = QString( "%1 %2 LIMIT %3, %4" )
01349 .arg( GetItemListSQL( pInfo->column ) )
01350 .arg( sWhere + sOrder )
01351 .arg( pRequest->m_nStartingIndex )
01352 .arg( pRequest->m_nRequestedCount );
01353
01354 query.prepare ( sSQL );
01355 if ( sKey.length() )
01356 query.bindValue(":KEY", sKey );
01357
01358 if (query.exec())
01359 {
01360 while(query.next())
01361 AddItem( pRequest, pRequest->m_sObjectId, pResults, bAddRef,
01362 query );
01363 }
01364 }
01365 }
01366
01367