/** @file
* Copyright (c) 2005-2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Element Factory.
*
*/
// INCLUDE FILES
#include <sysutil.h>
#include <uri8.h>
#include <xmlengdomparser.h>
#include "upnpelementfactory.h"
#include "upnpcontentdirectoryglobals.h"
#include "upnpstring.h"
#include "upnpprotocolinfo.h"
#include "upnpcommonupnplits.h"
#include "upnpcdutils.h"
#include "upnpprotocolinfolocal.h"
using namespace UpnpDlnaProtocolInfo;
// ============================= LOCAL FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// DestroyRPointerArray
// Used by TCleanupItem to destroy array
// -----------------------------------------------------------------------------
//
void DestroyRArray(TAny* aArray)
{
RArray<RXmlEngDocument>* array = (RArray<RXmlEngDocument>*) aArray;
for(TInt i = 0; i < array->Count(); i++ )
(*array)[i].Close();
(*array).Close();
}
// ============================ MEMBER FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CUpnpElementFactory::~CUpnpElementFactory()
// C++ default destructor. (virtual destructor)
// -----------------------------------------------------------------------------
//
CUpnpElementFactory::~CUpnpElementFactory()
{
//Close XML document
iDocument.Close();
iDOMImpl.Close();
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::NewL()
// Two-phased constructor
// -----------------------------------------------------------------------------
//
CUpnpElementFactory* CUpnpElementFactory::NewL( const TDesC& aObjectsXmlPath )
{
CUpnpElementFactory* self = CUpnpElementFactory::NewLC( aObjectsXmlPath );
CleanupStack::Pop( self );
return self;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::NewLC()
// Two-phased constructor
// -----------------------------------------------------------------------------
//
CUpnpElementFactory* CUpnpElementFactory::NewLC( const TDesC& aObjectsXmlPath )
{
CUpnpElementFactory* self = new (ELeave) CUpnpElementFactory();
CleanupStack::PushL( self );
self->ConstructL( aObjectsXmlPath );
return self;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ConstructL()
// Two-phased constructor
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ConstructL(const TDesC& aObjectsXmlPath)
{
iDOMImpl.OpenL();
RXmlEngDOMParser parser;
User::LeaveIfError( parser.Open(iDOMImpl) );
CleanupClosePushL(parser);
if ( aObjectsXmlPath == KNullDesC() )
{
RFs fs;
User::LeaveIfError(fs.Connect());
CleanupClosePushL(fs);
TFileName path;
User::LeaveIfError(fs.PrivatePath(path));
TParse fp;
fp.Set(KObjectsXmlFileName(),&path, 0);
path = fp.FullName();
iDocument = parser.ParseFileL( path );
CleanupStack::PopAndDestroy(&fs);
}
else
{
iDocument = parser.ParseFileL( aObjectsXmlPath );
}
CleanupStack::PopAndDestroy(&parser);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::CUpnpElementFactory()
// Default constructor
// -----------------------------------------------------------------------------
//
CUpnpElementFactory::CUpnpElementFactory()
{
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateNewObjectL()
// Function to validate a new object given by Control Point, especially
// upnp:class element field eg. object.item.musictrack
// IMPORTANT: If objects's description is invalid due to missing elements
// it tries to repair it by adding those missing elements.
// -----------------------------------------------------------------------------
//
TUpnpErrorCode CUpnpElementFactory::ValidateNewObjectL( const TXmlEngElement& aNewElement, TBool aNew, TBool aLocalSharing)
{
TInt i;
TPtrC8 objectType = KItem();
RXmlEngNodeList<TXmlEngElement> elements;
CleanupClosePushL(elements);
aNewElement.GetChildElements( elements );
// invalid element if the count of the objects is less than 1 (=0)
if ( elements.Count() != 1 )
{
User::Leave( EInvalidArgs );
}
CleanupStack::PopAndDestroy(&elements);
// try to find the new item from the xml
TXmlEngElement object;
UpnpDomInterface::GetElementL( aNewElement, object, KItem() );
if ( object.IsNull() )
{
// if it's not an item, it's a container
UpnpDomInterface::GetElementL( aNewElement, object, KContainer() );
if ( object.IsNull() )
{
// not even a container, leave!
User::Leave ( EInvalidArgs );
}
objectType.Set( KContainer() );
}
// restriced value
TPtrC8 restrVal = UpnpDomInterface::GetAttrValueL( object, KRestricted );
if (
(UpnpCD::Kfalse().Compare( restrVal ) != 0) &&
(UpnpCD::KZero().Compare( restrVal ) != 0) &&
(UpnpCD::Ktrue().Compare( restrVal ) != 0) &&
(UpnpCD::KOne().Compare( restrVal ) != 0)
)
{
User::Leave( EBadMetadata );
}
// for convenience later, take now a reference to the children of the object
RXmlEngNodeList<TXmlEngElement> children;
CleanupClosePushL(children);
object.GetChildElements( children );
// an exception: we can't have <res importUri=".. element in
// a new object (set by control point)
while ( children.HasNext() )
{
TXmlEngElement child = children.Next();
if ( IsNotEmptyImportUriL( child ) && aNew )
{
User::Leave ( EBadMetadata );
}
//---- check res@duration, 7.3.22 MM DIDL-Lite res@duration Format
CheckDurationOfResElementL(child);
CheckSizeOfResElementL(child);
RXmlEngNodeList<TXmlEngElement> forbiddenChildren;
CleanupClosePushL(forbiddenChildren);
child.GetChildElements( forbiddenChildren );
if ( forbiddenChildren.HasNext()
&& child.Name() != KVendorDescriptor ) // ignore <desc></desc>
{
User::Leave ( EBadMetadata );
}
CleanupStack::PopAndDestroy(&forbiddenChildren);
if ( child.Name() == KDate8() )
{
if (!UpnpCdUtils::ValidateDateL(child.Value()))
child.Remove();
}
}
// if survived here, we have the new element of type item or container
// let's analyze its type
// let's inspect each of types
// at first, collect object type descriptions from objects xml
// to a pointer array
RArray<TXmlEngElement> objectDescrs;
CleanupClosePushL( objectDescrs );
// store class descriptor elements in this array
ClassesL( object, objectDescrs, objectType );
// validate each separately
ValidatePropertiesL(object, objectDescrs);
if(aNew)
{
ValidateBigImageL(object);
}
// for each type, check that
// this new object has all the required fields
for (i=0; i<objectDescrs.Count(); i++)
{
// now we have to go through all the fields of the new item
// first, for convenience take a pointer to <ps>(properties) element
RXmlEngNodeList<TXmlEngElement> propElems;
CleanupClosePushL(propElems);
objectDescrs[i].GetChildElements( propElems );
while ( propElems.HasNext() )
{
TXmlEngElement ps = propElems.Next(); // ps stands for properties
if ( ps.Name() == KProperties() )
{
// take the properties
RXmlEngNodeList<TXmlEngElement> properties;
CleanupClosePushL(properties);
ps.GetChildElements( properties );
const TDesC8& propertyType = UpnpDomInterface::GetAttrValueL( ps, KType() );
TBool elementFound;
// check each property
while ( properties.HasNext() )
{
TXmlEngElement el = properties.Next();
// Validate element
if ( propertyType == KElement() )
{
elementFound = EFalse;
// Check if this element is requiered
const TDesC8& required = UpnpDomInterface::GetAttrValueL( el, KRequiredObject() );
TPtrC8 elementName = UpnpDomInterface::GetAttrValueL( el, KName() );
// If it's required then check it's existence
if ( required == UpnpCD::KOne() || elementName == KRes() || elementName == KAlbumArtURI() )
{
if( !KRes().Compare(elementName) || !KAlbumArtURI().Compare(elementName))
{
elementFound = ETrue;
}
// get children once more, because this kind of list does not have any reset function
object.GetChildElements( children );
while ( children.HasNext() )
{
TXmlEngElement child = children.Next();
HBufC8* nameWithNs = NameWithNsLC( child );
if ( *nameWithNs == elementName )
{
// local sharing
if(aNew && *nameWithNs == KRes)
{
TUriParser8 up;
TPtrC8 rv(child.Text());
if(rv.Length())
{
User::LeaveIfError( up.Parse(child.Text()) );
TPtrC8 path( up.Extract(EUriPath) );
TPtrC8 scheme( up.Extract(EUriScheme) );
if(aLocalSharing)
{ // local action
if( scheme == UpnpHTTP::KSchemeFile8())
{
// The path can be: /c:/....
// or /c/... - without colon.
// Both situation are correct but
// in further operations we assume
// there is not any colon next to the drive letter.
// Therefore, remove it if second element of path table equals ':'
if(path[2] == KColon8()[0])
{ // There IS a colon next to the drive letter.
// Here is an example uri:
// file:///c:/...
// As you can see the second colon must be removed
HBufC8* uri = child.Text().AllocLC();
TPtr8 uriPtr(uri->Des());
TPtrC8 tmp(uri->Des());
TInt second = 2;
TInt colonPos = 0;
for(TInt i = 0; i < second; i++)
{
colonPos += tmp.Find(KColon8) + 1;
tmp.Set( uriPtr.Mid(colonPos) );
}
// remove the colon
uriPtr.Replace(colonPos - 1, KColon8().Length(), KNullString8);
// set TXmlEngElement value
child.SetTextL(uriPtr);
// clean up
CleanupStack::PopAndDestroy(uri);
}
// check other restrictions
TUriParser8 up;
User::LeaveIfError( up.Parse(child.Text()) );
TPtrC8 path( up.Extract(EUriPath) );
// sharing from Z: drive is forbidden
// second character is a drive letter
if(path[1] == KForbiddenDrivez()[0] || path[1] == KForbiddenDriveZ()[0])
{
User::Leave(EArgumentValue);
}
// cannot share from private directory
if(!path.Match(KForbiddenPrivatePattern))
{
User::Leave(EArgumentValue);
}
}
}
else
{ // not local action
// "file" schema is forbidden here
if(scheme == UpnpHTTP::KSchemeFile8())
{
User::Leave(EArgumentValue);
}
}
}
}
if ( elementFound )
{
// multiple values for one element!
const TDesC8& multiple = UpnpDomInterface::GetAttrValueL( el, KMultiple() );
// if it's not allowed for this element, leave!
if ( !multiple.Length() )
{
User::Leave( EInvalidArgs );
}
}
elementFound = ETrue;
// mark the element required - if not res
if( KRes().Compare(elementName) && KAlbumArtURI().Compare(elementName))
{
child.AddNewAttributeL(KRequiredAtrName,KTrueValue8);
}
// nested validation
RXmlEngNodeList<TXmlEngElement> nestEls;
CleanupClosePushL(nestEls);
el.GetChildElements(nestEls);
while(nestEls.HasNext())
{
TXmlEngElement nestPs = nestEls.Next();
// take the properties
RXmlEngNodeList<TXmlEngElement> nestProperties;
nestPs.GetChildElements( nestProperties );
const TDesC8& nestPropertyType = UpnpDomInterface::GetAttrValueL( nestPs, KType() );
// Validate attribute
if ( nestPropertyType == KAttribute() )
{
while(nestProperties.HasNext())
{
TXmlEngElement nestEl = nestProperties.Next();
//TBool nestElementFound;
//nestElementFound = EFalse;
const TDesC8& nestRequired = UpnpDomInterface::GetAttrValueL( nestEl, KRequiredObject() );
TPtrC8 nestElementName = UpnpDomInterface::GetAttrValueL( nestEl, KName() );
if ( nestRequired == UpnpCD::KOne() || nestElementName == KDlnaProfileID)
{
TPtrC8 nestCurrentValue = UpnpDomInterface::GetAttrValueL( child, nestElementName );
TPtrC8 nameOfAttr;
TPtrC8 valOfAttr;
nameOfAttr.Set( nestElementName );
// Start of 'dlna:profileID' attribute case
if( nameWithNs->Des() == KAlbumArtURI && nestElementName == KDlnaProfileID) // ---------------- 1 ------------
{
TXmlEngAttr profId = child.AttributeNodeL(KProfileID, KXmlnsDlna);
if(profId.NotNull())
{
if (profId.Value().Compare(KDefaultProfileID))
{
User::Leave( EBadMetadata );
}
// setting real name of attribute -> localName
nestElementName.Set(KProfileID); // descriptor
nameOfAttr.Set( nestElementName ); // related TString
// generating a new value of 'dlna:profileID'
HBufC8* albumArtURIelemValue = UpnpDomInterface::GetElementValueL(child).AllocLC();
TPtr8 albumArtURIelemValuePtr( albumArtURIelemValue->Des() );
albumArtURIelemValuePtr.Trim(); // deletes leading and trailing whitespace characters
child.SetValueL(albumArtURIelemValuePtr); // sets new trimmed value to albumArtURI
CUpnpDlnaProtocolInfo* tempProtocolInfo = NULL;
TInt error = iContentDirectory->GetProtocolInfoL( albumArtURIelemValuePtr, tempProtocolInfo );
TPtrC8 tempPnParam;
if( error >=0 )
{
tempPnParam.Set( tempProtocolInfo->PnParameter() );
nestCurrentValue.Set(tempPnParam); // descriptor
valOfAttr.Set(tempPnParam); // related TString
}
else
{
User::Leave( EBadMetadata );
}
CleanupStack::PopAndDestroy(albumArtURIelemValue);
// if albumArtURI doesn't contain profileID -> it creates one and also related namespace
// if albumArtURI contains profileID -> it modifies profileID's value
child.SetAttributeL( nameOfAttr, valOfAttr, KXmlnsDlna(), KDlnaPrefix() );
delete tempProtocolInfo;
tempProtocolInfo = NULL;
}
} // End of 'dlna:profileID' attribute case
else
{
if ( !nestCurrentValue.Length() )
{
if(nestElementName == KprotocolInfo)
{
if( ! aLocalSharing )
{
valOfAttr.Set( KEmptyProtocolInfoVal8() );
}
else
{
User::Leave( EBadMetadata );
}
} //------- 2 ----- //------- 2 -----
else
{
valOfAttr.Set( KNullDesC8() );
}
child.AddNewAttributeL( nameOfAttr, valOfAttr );
}
else
{
if(nestElementName == KprotocolInfo)
{
HBufC8* protInfoValue = NULL;
TRAPD(err, protInfoValue = ValidateProtocolInfoInResL( nestCurrentValue, aLocalSharing ));
if(err)
{
User::Leave(EBadMetadata);
}
CleanupStack::PushL( protInfoValue );
valOfAttr.Set( *protInfoValue );
child.SetAttributeL( nameOfAttr, valOfAttr );
CleanupStack::PopAndDestroy( protInfoValue );
} //------- 3 ----- //------- 3 -----
}
}
// if not main tag mark the attr is required
if( !IsMainObjectTagL(child) && nestElementName != KDlnaProfileID)
{
HBufC8* attrReq = HBufC8::NewLC(
nestElementName.Length()
+KRequiredAtrSuf().Length() );
TPtr8 attrReqPtr(attrReq->Des());
attrReqPtr = nestElementName;
attrReqPtr.Append(KRequiredAtrSuf);
child.AddNewAttributeL( attrReqPtr, KTrueValue8 );
CleanupStack::PopAndDestroy(attrReq);
}
}
}
}
}
CleanupStack::PopAndDestroy(&nestEls);
}
CleanupStack::PopAndDestroy( nameWithNs );
}
// If not found, add it
if ( !elementFound )
{
User::Leave(EBadMetadata);
}
}
}
// Validate attribute
if ( propertyType == KAttribute() )
{
elementFound = EFalse;
const TDesC8& required = UpnpDomInterface::GetAttrValueL( el, KRequiredObject() );
if ( required == UpnpCD::KOne() )
{
const TDesC8& elementName = UpnpDomInterface::GetAttrValueL( el, KName() );
const TDesC8& currentValue = UpnpDomInterface::GetAttrValueL( object, elementName );
if ( !currentValue.Length() )
{
TXmlEngAttr attr = object.AttributeNodeL( elementName );
if(attr.NotNull())
{
attr.SetValueL(KNullDesC8);
}
else
{
object.AddNewAttributeL( elementName, KNullDesC8 );
}
}
// if not main tag mark the attr is required
if(!IsMainObjectTagL(object))
{
HBufC8* attrReq = HBufC8::NewLC(
elementName.Length()+KRequiredAtrSuf().Length() );
TPtr8 attrReqPtr(attrReq->Des());
attrReqPtr = elementName;
attrReqPtr.Append(KRequiredAtrSuf);
object.AddNewAttributeL( attrReqPtr, KTrueValue8 );
CleanupStack::PopAndDestroy(attrReq);
}
}
}
}
CleanupStack::PopAndDestroy(&properties);
}
}
CleanupStack::PopAndDestroy(&propElems);
}
CleanupStack::PopAndDestroy(); //objectDescrs.Close();
CleanupStack::PopAndDestroy(&children);
// now the object
// * has the all required fields (if some were missing, they are added)
// * had a proper xml structure (_not_ using UpnpDomInterface:: functions that do not care)
// * has for sure a mostly proper object structure
return EUndefined;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidatePropertiesL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidatePropertiesL( TXmlEngElement aObj,
RArray<TXmlEngElement>& aClassList)
{
// validate main object's attributes
ValidateMainAttributesL(aObj, aClassList);
// validate elements
RXmlEngNodeList<TXmlEngElement> elements;
CleanupClosePushL(elements);
aObj.GetChildElements (elements);
// for each element
while(elements.HasNext())
{
TXmlEngElement el = elements.Next();
ValidateElementL(el, aClassList);
}
CleanupStack::PopAndDestroy(&elements);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateBigImageL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateBigImageL( TXmlEngElement aObj)
{
RArray<TXmlEngElement> elms;
CleanupClosePushL(elms);
if( UpnpDomInterface::GetElementListL(aObj, elms, KRes) )
{
for(TInt i = 0; i < elms.Count(); i++)
{
TXmlEngAttr prInfAttr = elms[i].AttributeNodeL(KprotocolInfo());
if(prInfAttr.NotNull())
{
CUpnpProtocolInfoLocal* protInf = CUpnpProtocolInfoLocal::NewL(
UpnpDomInterface::GetAttrValueL(elms[i], KprotocolInfo) );
CleanupStack::PushL(protInf);
/* Only support for DLNA pn-params:
* 1) JPEG_SM
* 2) MP3
* 3) AAC_ISO_320
* 4) AVC_MP4_BL_CIF15_AAC_520
*/
if( protInf->PnParameter() != KDLNA_PN_JPEG_SM &&
protInf->PnParameter() != KDLNA_PN_MP3 &&
protInf->PnParameter() != KDLNA_PN_AAC_ISO_320 &&
protInf->PnParameter() != KDLNA_PN_AVC_MP4_BL_CIF15_AAC_520
)
{
// set 4th parameter to "*"
protInf->SetFourthFieldL(KAsterisk8);
TPtrC8 prInfo = protInf->ProtocolInfoL();
HBufC8* prInfTmp = prInfo.Alloc();
CleanupStack::PushL(prInfTmp);
prInfAttr.SetValueL( *prInfTmp );
CleanupStack::PopAndDestroy(prInfTmp);
}
// clean up
CleanupStack::PopAndDestroy(protInf);
}
}
}
// clean up
CleanupStack::PopAndDestroy(&elms);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateElementL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateElementL( TXmlEngElement aElement,
RArray<TXmlEngElement>& aClassList)
{
if(aElement.Name() == KRes)
{
ValidateResElL(aElement, aClassList);
}
else
{
// get pattern
HBufC8* name = UpnpCdUtils::GetElmNameWithNsL(aElement);
CleanupStack::PushL(name);
TXmlEngElement pattern = GetPatternL(*name, aClassList, KElement);
if(pattern.NotNull())
{
ValidateElWithPatternL(aElement, pattern); // leaves on error
}
else
{ // attr is not supported - remove
aElement.Remove();
}
// clean up
CleanupStack::PopAndDestroy(name);
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateMainAttributesL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateMainAttributesL( TXmlEngElement aObj,
RArray<TXmlEngElement>& aClassList)
{
// list of attributes
RXmlEngNodeList<TXmlEngAttr> attrList;
CleanupClosePushL(attrList);
aObj.GetAttributes(attrList);
// for each attr
while(attrList.HasNext())
{
// get attr
TXmlEngAttr attr = attrList.Next();
// get pattern
TXmlEngElement pattern = GetPatternL(attr.Name(), aClassList, KAttribute);
if(pattern.NotNull())
{
ValidateAttrWithPatternL(attr, pattern); // leaves on error
}
else
{ // attr is not supported - remove
attr.Remove();
}
}
CleanupStack::PopAndDestroy(&attrList);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateElWithPatternL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateResElL( TXmlEngElement aElement,
RArray<TXmlEngElement>& aClassList)
{
// list of attributes
RXmlEngNodeList<TXmlEngAttr> attrList;
CleanupClosePushL(attrList);
aElement.GetAttributes(attrList);
// for each attr
while(attrList.HasNext())
{
// get attr
TXmlEngAttr attr = attrList.Next();
// get pattern
TXmlEngElement pattern = GetPatternForResAttrL(attr.Name(), aClassList);
if(pattern.NotNull())
{
ValidateAttrWithPatternL(attr, pattern); // leaves on error
}
else
{ // attr is not supported - remove
attr.Remove();
}
}
CleanupStack::PopAndDestroy(&attrList);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateElWithPatternL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateElWithPatternL( TXmlEngElement aElement,
TXmlEngElement aPattern )
{
// is required?
if(IsRequiredL(aPattern) )
{ // cannot be empty
if( !aElement.Value().Length() ||
UpnpCdUtils::IsWhiteString( aElement.Value() ) )
{
User::Leave(EBadMetadata);
}
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateAttrWithPatternL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ValidateAttrWithPatternL( TXmlEngAttr aAttr,
TXmlEngElement aPattern )
{
// is required?
if(IsRequiredL(aPattern) )
{ // cannot be empty
if( !aAttr.Value().Length() ||
UpnpCdUtils::IsWhiteString( aAttr.Value() ) )
{
User::Leave(EBadMetadata);
}
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateAttrWithPatternL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
TBool CUpnpElementFactory::IsRequiredL(TXmlEngElement aPattern)
{
TXmlEngAttr reqAttr = aPattern.AttributeNodeL(KRequiredObject());
TBool ret = EFalse;
if( reqAttr.NotNull() &&
reqAttr.Value().Length() &&
reqAttr.Value() == KTrueValue8() )
{
ret = ETrue;
}
return ret;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::GetPatternForElL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
TXmlEngElement CUpnpElementFactory::GetPatternL( const TDesC8& aPropertyName,
RArray<TXmlEngElement>& aClassList,
const TDesC8& aType )
{
TXmlEngElement retEl;
// for each class
for(TInt i = 0; i < aClassList.Count(); i++)
{
TXmlEngElement elPattEl;
UpnpDomInterface::GetDirectoryElementL(aClassList[i], elPattEl, KProperties, KType, aType);
if(elPattEl.NotNull())
{
UpnpDomInterface::GetDirectoryElementL(elPattEl, retEl, KObjectProperty, KObjectName, aPropertyName);
// break if found
if(retEl.NotNull())
{
break;
}
}
}
return retEl;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::GetPatternForResAttrL()
// Function leaves on error.
// -----------------------------------------------------------------------------
//
TXmlEngElement CUpnpElementFactory::GetPatternForResAttrL(const TDesC8& aPropertyName,
RArray<TXmlEngElement>& aClassList )
{
TXmlEngElement retEl;
// for each class
for(TInt i = 0; i < aClassList.Count(); i++)
{
TXmlEngElement resAttrPattEl;
UpnpDomInterface::GetDirectoryElementL(aClassList[i], resAttrPattEl, KProperties, KType, KResAttr);
if(resAttrPattEl.NotNull())
{
UpnpDomInterface::GetDirectoryElementL(resAttrPattEl, retEl, KObjectProperty, KObjectName, aPropertyName);
// break if found
if(retEl.NotNull())
{
break;
}
}
}
return retEl;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::IsMainObjectTagL()
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CUpnpElementFactory::IsMainObjectTagL(TXmlEngElement aElement)
{
TXmlEngElement notNeeded;
return UpnpDomInterface::GetElementL(aElement, notNeeded, KClassTagName);
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ActiveElementL()
// Gets active element from XML document. An active element
// is a first element different from <DIDL-Lite> element.
// -----------------------------------------------------------------------------
//
TXmlEngElement CUpnpElementFactory::ActiveElementL( const RXmlEngDocument& aDocument )
{
if( aDocument.IsNull() || aDocument.DocumentElement().IsNull() )
{
User::Leave( KErrNotFound );
}
if ( aDocument.DocumentElement().Name().CompareF( KDidlLite() ) != 0 )
{
return aDocument.DocumentElement();
}
else
{
TXmlEngElement root = aDocument.DocumentElement();
RXmlEngNodeList<TXmlEngElement> children;
CleanupClosePushL(children);
root.GetChildElements( children );
children.HasNext();
CleanupStack::PopAndDestroy(&children);
return children.Next();
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ExtractActiveElementL()
// Extracts active element from XML document.
// An active element is a first element different from <DIDL-Lite> element.
// IMPORTANT: Caller takes responsibility for returned element.
// -----------------------------------------------------------------------------
//
RXmlEngDocument CUpnpElementFactory::ExtractActiveElementL( const RXmlEngDocument& aDocument )
{
TXmlEngElement active;
RXmlEngDocument ret;
ret.OpenL(iDOMImpl);
CleanupClosePushL(ret);
if( aDocument.IsNull() || aDocument.DocumentElement().IsNull() )
{
User::Leave( KErrNotFound );
}
if ( aDocument.DocumentElement().Name().CompareF( KDidlLite() ) != 0 )
{
active = aDocument.DocumentElement().Unlink().AsElement();
}
else
{
TXmlEngElement root = aDocument.DocumentElement();
RXmlEngNodeList<TXmlEngElement> children;
CleanupClosePushL(children);
root.GetChildElements( children );
children.HasNext();
active = children.Next().Unlink().AsElement();
CleanupStack::PopAndDestroy(&children);
}
ret.SetDocumentElement(active);
CleanupStack::Pop(&ret);
return ret;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ElementsMatchL()
// Checks if two elements are equal and have exactly the same subtrees.
// -----------------------------------------------------------------------------
//
TBool CUpnpElementFactory::ElementsMatchL( TXmlEngElement aFirst, TXmlEngElement aSecond )
{
// this function checks following things:
// 1. element names
// 2. contents of the elements
// 3. attributes of the elements
// 4. child elements of the elements
if ( aFirst.Name().Compare(aSecond.Name()) == 0 )
{
// aFirst names match. next: check namespace
if ( aFirst.Prefix().Compare( aSecond.Prefix()) == 0 )
{
if (!aFirst.Text().Compare(aSecond.Text()))
{
// namespacess match, next: check attributes
RXmlEngNodeList<TXmlEngAttr> elemAttrs;
CleanupClosePushL(elemAttrs);
RXmlEngNodeList<TXmlEngAttr> currAttrs;
CleanupClosePushL(currAttrs);
aFirst.GetAttributes( elemAttrs );
aSecond.GetAttributes( currAttrs );
if ( elemAttrs.Count() == currAttrs.Count() )
{
// first step in comparing attributes ready (counts match!)
// next, check names and values (for each attribute)
while( elemAttrs.HasNext() && currAttrs.HasNext() )
{
TXmlEngAttr elemAttr = elemAttrs.Next();
TXmlEngAttr currAttr = currAttrs.Next();
// compare names and values
if ( elemAttr.Name().Compare( currAttr.Name() ) )
{
CleanupStack::PopAndDestroy(&currAttrs);
CleanupStack::PopAndDestroy(&elemAttrs);
return EFalse;
}
if ( elemAttr.Value().Compare( currAttr.Value() ) )
{
CleanupStack::PopAndDestroy(&currAttrs);
CleanupStack::PopAndDestroy(&elemAttrs);
return EFalse;
}
}
// compare child elements (recursive function call)
RXmlEngNodeList<TXmlEngElement> fChildren;
CleanupClosePushL(fChildren);
RXmlEngNodeList<TXmlEngElement> sChildren;
CleanupClosePushL(sChildren);
aFirst.GetChildElements( fChildren );
aSecond.GetChildElements( sChildren );
if ( fChildren.Count() == sChildren.Count() )
{
while ( fChildren.HasNext() && sChildren.HasNext() )
{
// call this function again for children
TBool matching = ElementsMatchL( fChildren.Next(), sChildren.Next() );
// if some of the elements do not match, return false!
if (matching == EFalse)
{
CleanupStack::PopAndDestroy(&sChildren);
CleanupStack::PopAndDestroy(&fChildren);
CleanupStack::PopAndDestroy(&currAttrs);
CleanupStack::PopAndDestroy(&elemAttrs);
return EFalse;
}
}
// only place to return ETrue
// all the checks are made; if survived here, elements match!
CleanupStack::PopAndDestroy(&sChildren);
CleanupStack::PopAndDestroy(&fChildren);
CleanupStack::PopAndDestroy(&currAttrs);
CleanupStack::PopAndDestroy(&elemAttrs);
return ETrue;
}
CleanupStack::PopAndDestroy(&sChildren);
CleanupStack::PopAndDestroy(&fChildren);
}
CleanupStack::PopAndDestroy(&currAttrs);
CleanupStack::PopAndDestroy(&elemAttrs);
}
}
}
return EFalse;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::CountElementsL()
// Prepares corresponding RArray<TXmlEngElements> and calls
// CountElementsL( const TDesC8& aName, RArray<TXmlEngElement>& aArray )
// -----------------------------------------------------------------------------
//
TInt CUpnpElementFactory::CountElementsL( const TDesC8& aName, RArray<RXmlEngDocument>& aArray )
{
TInt count(0);
for(TInt i = 0; i < aArray.Count(); i++)
{
if ( aArray[i].DocumentElement().NotNull() )
{
if ( aArray[i].DocumentElement().Name() == aName )
{
count++;
}
}
}
return count;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::CountElementsL()
// Counts elements in array that have the same name.
// -----------------------------------------------------------------------------
//
TInt CUpnpElementFactory::CountElementsL( const TDesC8& aName, RArray<TXmlEngElement>& aArray )
{
TInt count(0);
for (TInt v(0); v<aArray.Count(); v++)
{
if ( aArray[v].NotNull() )
{
if ( aArray[v].Name() == aName )
{
count++;
}
}
}
return count;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::NameWithNsLC()
// Constructs descriptor with element's name and namespace prefix separated with colon.
// IMPORTANT: As the function name indicates, it leaves pointer to heap descriptor on
// cleanup stack.
// -----------------------------------------------------------------------------
//
HBufC8* CUpnpElementFactory::NameWithNsLC(const TXmlEngElement& aElement)
{
const TDesC8& localName = aElement.Name();
TPtrC8 prefix = aElement.Prefix();
if ( prefix.Length() > 0)
{
HBufC8* nameWithNs = HBufC8::NewLC(
localName.Length() +
UpnpString::KColon().Length() +
prefix.Length() );
nameWithNs->Des().Append( prefix );
nameWithNs->Des().Append( UpnpString::KColon() );
nameWithNs->Des().Append( localName );
return nameWithNs;
}
else
{
return localName.AllocLC();
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ClassesL()
// Gets classes descriptions for given object.
// -----------------------------------------------------------------------------
//
void CUpnpElementFactory::ClassesL( const TXmlEngElement& aObject, RArray<TXmlEngElement>& aArray, const TDesC8& aObjType )
{
TInt i(0);
// if survived here, we have the new element of type item or container
// let's analyze its type
TXmlEngElement type;
UpnpDomInterface::GetElementL ( aObject, type, KClass() );
if ( type.IsNull() )
{
// no <class> element! leave
User::Leave( EBadMetadata );
}
// check that does the new element have all the required fields for its type
// and also check that it does not have whatever fields (only optional allowed)
RPointerArray<TPtrC8> objectFields;
TPtrC8 content = type.Text();
if( !content.Length() )
{
User::Leave( EBadMetadata );
}
//--- removing white spaces ------------------
HBufC8* tempBuffer = type.Text().AllocLC();
TPtr8 tmpPtr(tempBuffer->Des());
UpnpCdUtils::RemoveWhiteSpacesL(tmpPtr);
CleanupStack::Check(tempBuffer);
type.SetTextL(tmpPtr);
//--------------------------------------------
UpnpString::CutToPiecesL(tmpPtr, TChar('.'), objectFields );
CleanupStack::Check(tempBuffer);
// let's inspect each of types
// at first, collect object type descriptions from objects xml
// to a pointer array
TInt NumberOfObjectFields = objectFields.Count();
for ( i=0; i < NumberOfObjectFields; i++ )
{
// seek for such object type
TXmlEngElement objectDescription;
UpnpDomInterface::GetDirectoryElementL(
iDocument.DocumentElement(),
objectDescription,
KElement,
KType,
*objectFields[i] );
CleanupStack::Check(tempBuffer);
// if such type found
if ( objectDescription.NotNull() )
{
aArray.Append( objectDescription );
}
/* This case is valid for any numeric value of containerID used in CreateObject() action
* We do some checking starting from 3rd string of <upnp:class> element, if the string isn't
* recognizable, instead of leaving, we trims the value of <upnp:class> element, e.g:
* 'object.item.imageItem.abcd' -> is trimmed into -> 'object.item.imageItem'
* DLNA 7.3.120.4 requirement
*/
else if( i >= 2 )
{
for( TInt j = NumberOfObjectFields - 1; j >= i; j-- )
{
TInt pos = tmpPtr.LocateReverse( TChar('.') );
if( pos > KErrNotFound )
{
tmpPtr.Copy( tmpPtr.Left(pos) );
delete objectFields[j];
objectFields.Remove(j);
}
}
type.SetTextL(tmpPtr); // changing xml
break;
}
else
{
objectFields.ResetAndDestroy();
objectFields.Close();
User::Leave( EBadMetadata );
}
}
CleanupStack::Check(tempBuffer);
/* for each type, check that:
* 1. it is referenced to its current parent type (e.g. audioItem -> item)
* 2. this new object has all the required fields
* 3. relations between object types and values of 'upnp:class':
* <item> -> 'object.item' or <container> -> 'object.container'
*/
// we count the number of elements once again, because size of the array might change
NumberOfObjectFields = objectFields.Count();
for ( i=0; i < NumberOfObjectFields; i++ )
{
const TDesC8& field = *objectFields[i];
// 1. first, check the parent relation
// first type must be "object"!
if ( i==0 )
{
/* Leave if:
* 1. first type isn't the 'object' OR
* 2. 'upnp:class' contains only 'object' type [which in matter of fact isn't instantiable]
*/
if ( field != KObject() ||
field == KObject() && NumberOfObjectFields == 1
)
{
// if not "item", leave!
objectFields.ResetAndDestroy();
objectFields.Close();
User::Leave( EBadMetadata );
}
}
// for later types, check the relation really
else
{
// checking relations between object types and values of 'upnp:class'
if( i == 1 && aObjType.Length() > 0 )
{
if( !field.Compare(KItem) && aObjType.Compare(KItem) ||
!field.Compare(KContainer) && aObjType.Compare(KContainer)
)
{
objectFields.ResetAndDestroy();
objectFields.Close();
User::Leave( EBadMetadata );
}
}
//----------------------------------------
const TDesC8& parent = *objectFields[i-1];
// if we've survived to this point, this parent string
// should match with the one in the previous object description xml element
TXmlEngElement iparent;
UpnpDomInterface::GetElementL ( aArray[i], iparent, KIParent() );
CleanupStack::Check(tempBuffer);
TPtrC8 cont = iparent.Text();
// now check the actual relation. leave if strings do not match!
if ( cont.Length() && parent != cont )
{
objectFields.ResetAndDestroy();
objectFields.Close();
User::Leave( EBadMetadata );
}
}
}
CleanupStack::PopAndDestroy(tempBuffer);
objectFields.ResetAndDestroy();
objectFields.Close();
return;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::IsNotEmptyImportUriL()
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CUpnpElementFactory::IsNotEmptyImportUriL( const TXmlEngElement& aElement )
{
TXmlEngAttr importUri;
if ( aElement.Name() == KRes() )
{
importUri = aElement.AttributeNodeL( KImportUri8() );
// remove if empty importUri: DLNA Requirement [7.3.134.5]
TPtrC8 val( importUri.Value() );
if( UpnpCdUtils::IsWhiteString( val ) )
{
importUri.Remove();
}
}
return importUri.NotNull();
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::IsClassElement()
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
TBool CUpnpElementFactory::IsClassElement( const TXmlEngElement& aElement )
{
if ( aElement.Name() == KClass() )
{
return ETrue;
}
else
{
return EFalse;
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::ValidateProtocolInfoInResL
// Allocates string, which is object type for this object.
// -----------------------------------------------------------------------------
HBufC8* CUpnpElementFactory::ValidateProtocolInfoInResL( const TDesC8& aProtocolInfo, TBool aLocalSharing )
{
HBufC8* result = NULL;
// CUpnpDlnaProtocolInfo* protocolInfo = CUpnpDlnaProtocolInfo::NewL( (TDesC8&)aProtocolInfo );
CUpnpProtocolInfoLocal* protocolInfo = CUpnpProtocolInfoLocal::NewL( (TDesC8&)aProtocolInfo );
CleanupStack::PushL( protocolInfo );
_LIT8(KProtocolInfoHttpGet, "http-get");
protocolInfo->SetFirstFieldL( (TDesC8&)KProtocolInfoHttpGet() );
protocolInfo->SetSecondFieldL( (TDesC8&)KAsterisk8() );
TPtrC8 third = protocolInfo->ThirdField();
if(( third.Find( KSlash8()) == KErrNotFound) &&
( third.Compare( KAsterisk8()) != KErrNone))
{
if( aLocalSharing)
{
User::Leave( EBadMetadata );
}
protocolInfo->SetThirdFieldL( (TDesC8&) KAsterisk8());
}
// if( protocolInfo->IsDlnaInformationIncluded() ) // Check it !!!
{
protocolInfo->SetOpParameterL( UpnpDlnaProtocolInfo::B_VAL , ETrue );
protocolInfo->SetOpParameterL( UpnpDlnaProtocolInfo::A_VAL , EFalse );
}
TPtrC8 prInfo = protocolInfo->ProtocolInfoL();
result = prInfo.Alloc();
CleanupStack::PopAndDestroy( protocolInfo );
return result;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::GetContentDirectoryReference
// Function gets pointer to CUpnpContentDirectory and puts it into CUpnpElementFactory object
// -----------------------------------------------------------------------------
void CUpnpElementFactory::GetContentDirectoryReference(CUpnpContentDirectory* aCD)
{
iContentDirectory = aCD;
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::CheckDurationOfResElement
// If res@duration attribute exists, the method checks whether it has valid format
// -----------------------------------------------------------------------------
void CUpnpElementFactory::CheckDurationOfResElementL(const TXmlEngElement& aElement)
{
TXmlEngAttr duration;
// if this is the 'res' element
if( aElement.Name() == KRes() )
{
// and it has res@duration attribute
duration = aElement.AttributeNodeL( KDuration8() );
if( duration.NotNull() )
{
TCurrentAction action = iContentDirectory->ExecutedAction();
TPtrC8 val( duration.Value() );
// if res@duration atrribute value is invalid [has improper format]
if( !UpnpCdUtils::ValidateDurationValue(val) )
{
// remove it from 'res' element if it is CreateObject()
if( action == ECreateObjectAction )
{
duration.Remove();
}
}
}
}
}
// -----------------------------------------------------------------------------
// CUpnpElementFactory::CheckSizeOfResElement
// If res@size attribute exists, the method checks whether it has valid format
// size should be unsigned int.
// -----------------------------------------------------------------------------
void CUpnpElementFactory::CheckSizeOfResElementL(
const TXmlEngElement& aElement )
{
TXmlEngAttr sizeAttr;
// if this is the 'res' element
if ( aElement.Name() == KRes() )
{
// and it has res@size attribute
sizeAttr = aElement.AttributeNodeL( KSize() );
if ( sizeAttr.NotNull() )
{
TCurrentAction action = iContentDirectory->ExecutedAction();
TPtrC8 val( sizeAttr.Value() );
// if res@size atrribute value is invalid [is not an unsigned long]
TUint32 unsignedLong = 0;
TLex8 lexULong(val);
TInt error = lexULong.Val( unsignedLong, EDecimal );
TInt remainder = lexULong.Remainder().Length();
if ( error != KErrNone || remainder )
{
// remove it from 'res' element if it is CreateObject()
if ( action == ECreateObjectAction )
{
sizeAttr.Remove();
}
}
}
}
}
// End of File