--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/cms/src/CCMSAuthenticatedData.cpp Tue Jan 26 15:20:08 2010 +0200
@@ -0,0 +1,629 @@
+/*
+* Copyright (c) 2002 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:
+*
+*/
+
+
+// INCLUDE FILES
+#include "CCMSAuthenticatedData.h"
+#include "CCMSX509AlgorithmIdentifier.h"
+#include "CCMSOriginatorInfo.h"
+#include "CCMSRecipientInfo.h"
+#include "CCMSKeyTransRecipientInfo.h"
+#include "CCMSKeyAgreeRecipientInfo.h"
+#include "CCMSKEKRecipientInfo.h"
+#include "CCMSAttribute.h"
+#include "CCMSX509Certificate.h"
+#include "CCMSCertificateChoices.h"
+#include "CCMSX509CertificateList.h"
+#include <asn1dec.h>
+#include <asn1enc.h>
+
+// CONSTANTS
+const TInt KDefaultGranularity = 1;
+const TInt KMinNumberOfSubModules = 5;
+const TInt KMaxNumberOfSubModules = 9;
+const TTagType KOriginatorInfoTag = 0;
+const TTagType KDigestAlgorithmTag = 1;
+const TTagType KAuthenticatedAttributesTag = 2;
+const TTagType KUnauthenticatedAttributesTag = 3;
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// Destructor
+CCMSAuthenticatedData::CAuthenticatedDataData::~CAuthenticatedDataData()
+ {
+ delete iOriginatorInfo;
+ if( iRecipientInfos )
+ {
+ iRecipientInfos->ResetAndDestroy();
+ delete iRecipientInfos;
+ }
+ delete iMacAlgorithm;
+ delete iDigestAlgorithm;
+ delete iEncapContentInfo;
+ if( iAuthenticatedAttributes )
+ {
+ iAuthenticatedAttributes->ResetAndDestroy();
+ delete iAuthenticatedAttributes;
+ }
+ delete iMac;
+ if( iUnauthenticatedAttributes )
+ {
+ iUnauthenticatedAttributes->ResetAndDestroy();
+ delete iUnauthenticatedAttributes;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::CCMSAuthenticatedData
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CCMSAuthenticatedData::CCMSAuthenticatedData( )
+ {
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CCMSAuthenticatedData::ConstructL(
+ CArrayPtr< CCMSRecipientInfo >* aRecipientInfos,
+ CCMSX509AlgorithmIdentifier* aMacAlgorithm,
+ CCMSEncapsulatedContentInfo* aEncapContentInfo,
+ const TDesC8& aMac )
+ {
+ iData = new( ELeave ) CAuthenticatedDataData;
+ iData->iMac = aMac.AllocL();
+
+ // all memory allocations have been done, we can take ownerships
+ iData->iRecipientInfos = aRecipientInfos;
+ iData->iMacAlgorithm = aMacAlgorithm;
+ iData->iEncapContentInfo = aEncapContentInfo;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C void CCMSAuthenticatedData::ConstructL( )
+ {
+ // creating empty/default values
+ iData = new( ELeave ) CAuthenticatedDataData;
+ iData->iRecipientInfos =
+ new( ELeave ) CArrayPtrFlat< CCMSRecipientInfo >( KDefaultGranularity );
+ iData->iMacAlgorithm = CCMSX509AlgorithmIdentifier::NewL();
+ iData->iEncapContentInfo = CCMSEncapsulatedContentInfo::NewL();
+ iData->iMac = KNullDesC8().AllocL();
+ }
+
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::NewLC
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CCMSAuthenticatedData*
+CCMSAuthenticatedData::NewLC()
+ {
+ // creating with empty values
+ CCMSAuthenticatedData* self =
+ new( ELeave ) CCMSAuthenticatedData();
+ CleanupStack::PushL( self );
+ self->ConstructL( );
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::NewLC
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+EXPORT_C CCMSAuthenticatedData*
+CCMSAuthenticatedData::NewLC(
+ CArrayPtr< CCMSRecipientInfo >* aRecipientInfos,
+ CCMSX509AlgorithmIdentifier* aMacAlgorithm,
+ CCMSEncapsulatedContentInfo* aEncapContentInfo,
+ const TDesC8& aMac )
+ {
+ CCMSAuthenticatedData* self =
+ new( ELeave ) CCMSAuthenticatedData();
+ CleanupStack::PushL( self );
+ self->ConstructL( aRecipientInfos, aMacAlgorithm, aEncapContentInfo, aMac );
+
+ return self;
+ }
+
+// Destructor
+CCMSAuthenticatedData::~CCMSAuthenticatedData()
+ {
+ delete iData;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::DecodeL
+// Decrypts raw data to this instance
+// -----------------------------------------------------------------------------
+void CCMSAuthenticatedData::DecodeL( const TDesC8& aRawData )
+ {
+ CArrayPtr< TASN1DecGeneric >* items = DecodeSequenceLC(
+ aRawData, KMinNumberOfSubModules, KMaxNumberOfSubModules );
+
+ // now we know that there are at least 5 items
+ TInt itemCount = items->Count();
+ __ASSERT_DEBUG( itemCount >= KMinNumberOfSubModules, User::Invariant() );
+
+ TInt sequenceCounter = 0;
+
+ CAuthenticatedDataData* data = new( ELeave ) CAuthenticatedDataData;
+ CleanupStack::PushL( data );
+
+ // decode version
+ TASN1DecInteger intDecoder;
+ data->iVersion =
+ intDecoder.DecodeDERShortL( *items->At( sequenceCounter++ ) );
+
+ // decode originatorInfo, if exist
+ TASN1DecGeneric* originatorInfoDecoder = items->At( sequenceCounter );
+ if( originatorInfoDecoder->Tag() == KOriginatorInfoTag )
+ {
+ data->iOriginatorInfo = CCMSOriginatorInfo::NewL();
+ data->iOriginatorInfo->DecodeImplicitTagL(
+ originatorInfoDecoder->Encoding(), KOriginatorInfoTag );
+ sequenceCounter++;
+ }
+
+ // decode recipientInfos
+ CArrayPtr< TASN1DecGeneric >* recipientInfos = DecodeSequenceLC(
+ items->At( sequenceCounter++)->Encoding() );
+ TInt recipientInfoCount = recipientInfos->Count();
+ data->iRecipientInfos =
+ new( ELeave ) CArrayPtrFlat< CCMSRecipientInfo >( recipientInfoCount );
+ for( TInt i = 0; i < recipientInfoCount; i++ )
+ {
+ TASN1DecGeneric* recipientInfoDecoder = recipientInfos->At( i );
+ CCMSRecipientInfo* recipientInfo = NULL;
+ switch( recipientInfoDecoder->Tag() )
+ {
+ case EASN1Sequence:
+ {
+ recipientInfo = CCMSKeyTransRecipientInfo::NewLC();
+ recipientInfo->DecodeL( recipientInfoDecoder->Encoding() );
+ break;
+ }
+ case KCMSKeyAgreeRecipientInfoTag:
+ {
+ recipientInfo = CCMSKeyAgreeRecipientInfo::NewLC();
+ recipientInfo->DecodeL( recipientInfoDecoder->Encoding() );
+ break;
+ }
+ case KCMSKEKRecipientInfoTag:
+ {
+ recipientInfo = CCMSKEKRecipientInfo::NewLC();
+ recipientInfo->DecodeL( recipientInfoDecoder->Encoding() );
+ break;
+ }
+ default:
+ {
+ User::Leave( KErrArgument );
+ }
+ }
+ data->iRecipientInfos->AppendL( recipientInfo );
+ CleanupStack::Pop( recipientInfo );
+ }
+ CleanupStack::PopAndDestroy( recipientInfos );
+
+ // decode macAlgorithm
+ data->iMacAlgorithm = CCMSX509AlgorithmIdentifier::NewL();
+ data->iMacAlgorithm->DecodeL( items->At( sequenceCounter++)->Encoding() );
+
+ // decode digestAlgorithm, if exist
+ TASN1DecGeneric* digestAlgorithmDec = items->At( sequenceCounter );
+ if( digestAlgorithmDec->Tag() == KDigestAlgorithmTag )
+ {
+ data->iDigestAlgorithm = CCMSX509AlgorithmIdentifier::NewL();
+ data->iDigestAlgorithm->DecodeL( digestAlgorithmDec->GetContentDER() );
+ sequenceCounter++;
+ }
+
+ // decode encapContentInfo
+ if( sequenceCounter == itemCount )
+ {
+ User::Leave( KErrArgument );
+ }
+ data->iEncapContentInfo = CCMSEncapsulatedContentInfo::NewL();
+ data->iEncapContentInfo->DecodeL(
+ items->At( sequenceCounter++)->Encoding() );
+
+ // decode authenticatedAttributes, if exist
+ if( sequenceCounter == itemCount )
+ {
+ User::Leave( KErrArgument );
+ }
+ TASN1DecGeneric* authAttributesDec = items->At( sequenceCounter );
+ if( authAttributesDec->Tag() == KAuthenticatedAttributesTag )
+ {
+ data->iAuthenticatedAttributes = DecodeAttributesL( authAttributesDec );
+ sequenceCounter++;
+ }
+
+ // decode mac
+ if( sequenceCounter == itemCount )
+ {
+ User::Leave( KErrArgument );
+ }
+ TASN1DecOctetString octetStrDec;
+ data->iMac = octetStrDec.DecodeDERL( *( items->At( sequenceCounter++ ) ) );
+
+ // decode unauthenticatedAttributes, if exist
+ if( itemCount > sequenceCounter )
+ {
+ TASN1DecGeneric* unauthAttributesDec = items->At( sequenceCounter );
+ if( unauthAttributesDec->Tag() != KUnauthenticatedAttributesTag )
+ {
+ User::Leave( KErrArgument );
+ }
+ data->iUnauthenticatedAttributes =
+ DecodeAttributesL( unauthAttributesDec );
+ }
+
+ // all done, change state
+ CleanupStack::Pop( data );
+ CleanupStack::PopAndDestroy( items );
+ delete iData;
+ iData = data;
+ }
+
+
+
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::EncoderLC
+// Returns ASN1 encoder for this instance
+// -----------------------------------------------------------------------------
+CASN1EncBase* CCMSAuthenticatedData::EncoderLC() const
+ {
+ CASN1EncSequence* root = CASN1EncSequence::NewLC();
+
+ // encode version
+ CASN1EncInt* version = CASN1EncInt::NewLC( iData->iVersion );
+ root->AddAndPopChildL( version );
+
+ // encode originatorInfo, if exist
+ if( iData->iOriginatorInfo )
+ {
+ CASN1EncBase* originatorInfo = iData->iOriginatorInfo->EncoderLC();
+ originatorInfo->SetTag( KOriginatorInfoTag );
+ root->AddAndPopChildL( originatorInfo );
+ }
+
+ // encode recipientInfos ::= SET OF RecipientInfo
+ CASN1EncSequence* recipientInfos = CASN1EncSequence::NewLC();
+ TInt recipientInfoCount = iData->iRecipientInfos->Count();
+ TInt i = 0;
+ for( ; i < recipientInfoCount; i++ )
+ {
+ CASN1EncBase* recipientInfo =
+ iData->iRecipientInfos->At( i )->TaggedEncoderLC();
+ recipientInfos->AddAndPopChildL( recipientInfo );
+ }
+ recipientInfos->SetTag( EASN1Set, EUniversal );
+ root->AddAndPopChildL( recipientInfos );
+
+ // encode macAlgorithm
+ CASN1EncBase* macAlgorithm = iData->iMacAlgorithm->EncoderLC();
+ root->AddAndPopChildL( macAlgorithm );
+
+ // encode digestAlgorithm, if exist
+ if( iData->iDigestAlgorithm )
+ {
+ CASN1EncBase* digestAlgorithm = iData->iDigestAlgorithm->EncoderLC();
+ CleanupStack::Pop( digestAlgorithm );
+
+ // CASN1EncExplicitTag takes ownership of the parameter, even
+ // if the method leaves.
+ CASN1EncExplicitTag* taggedDigestAlgorithm =
+ CASN1EncExplicitTag::NewLC( digestAlgorithm, KDigestAlgorithmTag );
+ root->AddAndPopChildL( taggedDigestAlgorithm );
+ }
+
+ // encode encapContentInfo
+ CASN1EncBase* encapContentInfo = iData->iEncapContentInfo->EncoderLC();
+ root->AddAndPopChildL( encapContentInfo );
+
+ // encode authenticatedAttributes, if exist
+ if( iData->iAuthenticatedAttributes )
+ {
+ CASN1EncSequence* authAttributes = CASN1EncSequence::NewLC();
+ TInt authAttributeCount = iData->iAuthenticatedAttributes->Count();
+ for( i = 0; i < authAttributeCount; i++ )
+ {
+ CASN1EncBase* attribute =
+ iData->iAuthenticatedAttributes->At( i )->EncoderLC();
+ authAttributes->AddAndPopChildL( attribute );
+ }
+ authAttributes->SetTag( KAuthenticatedAttributesTag );
+ root->AddAndPopChildL( authAttributes );
+ }
+
+ // encode mac
+ CASN1EncOctetString* mac = CASN1EncOctetString::NewLC( *iData->iMac );
+ root->AddAndPopChildL( mac );
+
+ // encode unauthenticatedAttributes, if exist
+ if( iData->iUnauthenticatedAttributes )
+ {
+ CASN1EncSequence* unauthAttributes = CASN1EncSequence::NewLC();
+ TInt unauthAttributeCount = iData->iUnauthenticatedAttributes->Count();
+ for( i = 0; i < unauthAttributeCount; i++ )
+ {
+ CASN1EncBase* attribute =
+ iData->iUnauthenticatedAttributes->At( i )->EncoderLC();
+ unauthAttributes->AddAndPopChildL( attribute );
+ }
+ unauthAttributes->SetTag( KUnauthenticatedAttributesTag );
+ root->AddAndPopChildL( unauthAttributes );
+ }
+
+ return root;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::Version()
+// Getter for Version
+// -----------------------------------------------------------------------------
+EXPORT_C TInt CCMSAuthenticatedData::Version() const
+ {
+ return iData->iVersion;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::OriginatorInfo()
+// Getter for OriginatorInfo
+// -----------------------------------------------------------------------------
+EXPORT_C const CCMSOriginatorInfo* CCMSAuthenticatedData::OriginatorInfo() const
+ {
+ return iData->iOriginatorInfo;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::RecipientInfos()
+// Getter for recipientInfos
+// -----------------------------------------------------------------------------
+EXPORT_C const CArrayPtr< CCMSRecipientInfo >&
+CCMSAuthenticatedData::RecipientInfos() const
+ {
+ return *( iData->iRecipientInfos );
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::MacAlgorithm()
+// Getter for macAlgorithm
+// -----------------------------------------------------------------------------
+EXPORT_C const CCMSX509AlgorithmIdentifier&
+CCMSAuthenticatedData::MacAlgorithm() const
+ {
+ return *( iData->iMacAlgorithm );
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::DigestAlgorithm()
+// Getter for digestAlgorithm
+// -----------------------------------------------------------------------------
+EXPORT_C const CCMSX509AlgorithmIdentifier*
+CCMSAuthenticatedData::DigestAlgorithm() const
+ {
+ return iData->iDigestAlgorithm;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::EncapContentInfo()
+// Getter for encapContentInfo
+// -----------------------------------------------------------------------------
+EXPORT_C const CCMSEncapsulatedContentInfo&
+CCMSAuthenticatedData::EncapContentInfo() const
+ {
+ return *( iData->iEncapContentInfo );
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::AuthenticatedAttributes()
+// Getter for authenticatedAttributes
+// -----------------------------------------------------------------------------
+EXPORT_C const CArrayPtr< CCMSAttribute >*
+CCMSAuthenticatedData::AuthenticatedAttributes() const
+ {
+ return iData->iAuthenticatedAttributes;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::Mac()
+// Getter for mac
+// -----------------------------------------------------------------------------
+EXPORT_C const TDesC8& CCMSAuthenticatedData::Mac() const
+ {
+ return *( iData->iMac );
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::UnauthenticatedAttributes()
+// Getter for unauthenticatedAttributes
+// -----------------------------------------------------------------------------
+EXPORT_C const CArrayPtr< CCMSAttribute >*
+CCMSAuthenticatedData::UnauthenticatedAttributes() const
+ {
+ return iData->iUnauthenticatedAttributes;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetVersion()
+// Setter for version
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetVersion( TInt aVersion )
+ {
+ iData->iVersion = aVersion;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetOriginatorInfoL()
+// Setter for OriginatorInfo
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetOriginatorInfoL(
+ CCMSOriginatorInfo* aOriginatorInfo )
+ {
+ delete iData->iOriginatorInfo;
+ iData->iOriginatorInfo = aOriginatorInfo;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetRecipientInfosL()
+// Setter for recipientInfos
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetRecipientInfosL(
+ CArrayPtr< CCMSRecipientInfo >* aRecipientInfos )
+ {
+ if( ( !aRecipientInfos ) || ( aRecipientInfos->Count() == 0 ) )
+ {
+ User::Leave( KErrArgument );
+ }
+
+ if( iData->iRecipientInfos )
+ {
+ iData->iRecipientInfos->ResetAndDestroy();
+ delete iData->iRecipientInfos;
+ }
+ iData->iRecipientInfos = aRecipientInfos;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetMacAlgorithmL()
+// Setter for macAlgorithm
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetMacAlgorithmL(
+ CCMSX509AlgorithmIdentifier* aMacAlgorithm )
+ {
+ if( !aMacAlgorithm )
+ {
+ User::Leave( KErrArgument );
+ }
+ delete iData->iMacAlgorithm;
+ iData->iMacAlgorithm = aMacAlgorithm;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetDigestAlgorithmL()
+// Setter for digestAlgorithm
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetDigestAlgorithmL(
+ CCMSX509AlgorithmIdentifier* aDigestAlgorithm )
+ {
+ delete iData->iDigestAlgorithm;
+ iData->iDigestAlgorithm = aDigestAlgorithm;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetEncapContentInfoL()
+// Setter for encapContentInfo
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetEncapContentInfoL(
+ CCMSEncapsulatedContentInfo* aEncapContentInfo )
+ {
+ if( !aEncapContentInfo )
+ {
+ User::Leave( KErrArgument );
+ }
+ delete iData->iEncapContentInfo;
+ iData->iEncapContentInfo = aEncapContentInfo;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetAuthenticatedAttributesL()
+// Setter for authenticatedAttributes
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetAuthenticatedAttributesL(
+ CArrayPtr< CCMSAttribute >* aAuthenticatedAttributes )
+ {
+ if( iData->iAuthenticatedAttributes )
+ {
+ iData->iAuthenticatedAttributes->ResetAndDestroy();
+ delete iData->iAuthenticatedAttributes;
+ }
+ iData->iAuthenticatedAttributes = aAuthenticatedAttributes;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetMacL
+// Setter for mac
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetMacL(
+ const TDesC8& aMac)
+ {
+ HBufC8* mac = aMac.AllocL();
+ delete iData->iMac;
+ iData->iMac = mac;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::SetUnauthenticatedAttributesL()
+// Setter for unauthenticatedAttributes
+// -----------------------------------------------------------------------------
+EXPORT_C void CCMSAuthenticatedData::SetUnauthenticatedAttributesL(
+ CArrayPtr< CCMSAttribute >* aUnauthenticatedAttributes )
+ {
+ if( iData->iUnauthenticatedAttributes )
+ {
+ iData->iUnauthenticatedAttributes->ResetAndDestroy();
+ delete iData->iUnauthenticatedAttributes;
+ }
+ iData->iUnauthenticatedAttributes = aUnauthenticatedAttributes;
+ }
+
+// -----------------------------------------------------------------------------
+// CCMSAuthenticatedData::DecodeAttributesL
+// Decodes an array of attributes
+// -----------------------------------------------------------------------------
+CArrayPtrFlat< CCMSAttribute >* CCMSAuthenticatedData::DecodeAttributesL(
+ TASN1DecGeneric* aAttributesDec ) // generic decoder for the sequence
+ {
+ TASN1DecSequence sequenceDecoder;
+ CArrayPtr< TASN1DecGeneric >* attributes =
+ sequenceDecoder.DecodeDERLC( *aAttributesDec );
+ TInt attributeCount = attributes->Count();
+ if( attributeCount < 1 )
+ {
+ User::Leave( KErrArgument );
+ }
+ CArrayPtrFlat< CCMSAttribute >* retVal =
+ new( ELeave ) CArrayPtrFlat< CCMSAttribute >( attributeCount );
+ CleanupStack::PushL( retVal );
+ for( TInt i = 0; i < attributeCount; i++ )
+ {
+ CCMSAttribute* attribute = CCMSAttribute::NewLC();
+ attribute->DecodeL( attributes->At( i )->Encoding() );
+ retVal->AppendL( attribute );
+ // attribute is left in cleanup stack, as retVal has not been pushed
+ // with ResetAndDestroyPushL
+ }
+ CleanupStack::Pop( attributeCount ); // all attributes
+ CleanupStack::Pop( retVal );
+ CleanupStack::PopAndDestroy( attributes );
+ return retVal;
+ }
+
+// End of File