/*
* Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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: 
* Definition of the Swi::Sis::CController
*
*/


/**
 @file siscontroller.cpp
*/

#include "siscontroller.h"
#include "sisinfo.h"
#include "sissupportedlanguages.h"
#include "sissupportedoptions.h"
#include "sisproperties.h"
#include "sislogo.h"
#include "sisinstallblock.h"
#include "sisproperties.h"
#include "sisprerequisites.h"
#include "swi/sistruststatus.h"
#include "certchainconstraints.h"
#include "sisptrprovider.h"
#include "log.h"

using namespace Swi::Sis;


EXPORT_C /*static*/ CController* CController::NewLC(MSisDataProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = new(ELeave) CController();
	CleanupStack::PushL(self);
	self->ConstructL(aDataProvider, aBytesRead, aTypeReadBehaviour);
	return self;
	}

EXPORT_C /*static*/ CController* CController::NewL(MSisDataProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = NewLC(aDataProvider, aBytesRead, aTypeReadBehaviour);
	CleanupStack::Pop(self);
	return self;
	}
	
EXPORT_C /*static*/ CController* CController::NewLC(MSisDataProvider& aDataProvider, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = new(ELeave) CController();
	CleanupStack::PushL(self);
	TInt64 bytesRead=0;
	self->ConstructL(aDataProvider, bytesRead, aTypeReadBehaviour);
	return self;
	}

EXPORT_C /*static*/ CController* CController::NewL(MSisDataProvider& aDataProvider, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = NewLC(aDataProvider, aTypeReadBehaviour);
	CleanupStack::Pop(self);
	return self;
	}

CController::CController() : iTrust(ESisPackageUnsignedOrSelfSigned)
	{
	}
	
EXPORT_C CController* CController::NewLC(TPtrProvider& aDataProvider, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = new(ELeave) CController();
	CleanupStack::PushL(self);
	TInt64 bytesRead(0);
	self->ConstructL(aDataProvider, bytesRead, aTypeReadBehaviour);
	return self;
	}
	
EXPORT_C CController* CController::NewL(TPtrProvider& aDataProvider, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = NewLC(aDataProvider, aTypeReadBehaviour);
	CleanupStack::Pop(self);
	return self;
	}
	
EXPORT_C CController* CController::NewLC(TPtrProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = new(ELeave) CController();
	CleanupStack::PushL(self);
	self->ConstructL(aDataProvider, aBytesRead, aTypeReadBehaviour);
	return self;
	}
	
EXPORT_C CController* CController::NewL(TPtrProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	CController* self = NewLC(aDataProvider, aBytesRead, aTypeReadBehaviour);
	CleanupStack::Pop(self);
	return self;
	}

EXPORT_C CController::~CController()
	{
	delete iInfo;
	delete iInstallBlock;
	delete iLanguages;
	delete iSupportedOptions;
	delete iProperties;
	delete iLogo;
	delete iPrerequisites;
	delete iDataIndex;
	delete iTrustStatus;
	delete iCertChainConstraints;
    
    iCertChainIndices.Close();
    iSignatureCertificateChains.ResetAndDestroy(); 
	}

void CController::ConstructL(MSisDataProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	iDataOffset=aBytesRead;
	
	CField::ConstructL(aDataProvider, EFieldTypeController, aBytesRead, aTypeReadBehaviour);

 	iInfo=CInfo::NewL(aDataProvider, aBytesRead);
 	
	iSupportedOptions=CSupportedOptions::NewL(aDataProvider, aBytesRead);

	iLanguages=CSupportedLanguages::NewL(aDataProvider, aBytesRead);

	iPrerequisites=CPrerequisites::NewL(aDataProvider, aBytesRead);

	iProperties=CProperties::NewL(aDataProvider, aBytesRead);

	TFieldType fieldType;
	CField::ReadEnumL<TFieldType,TUint32>(aDataProvider, fieldType, aBytesRead);
	
	if (fieldType == EFieldTypeLogo)
		{
		// Logo present
		iLogo=CLogo::NewL(aDataProvider, aBytesRead, EAssumeType);
		iInstallBlock=CInstallBlock::NewL(aDataProvider, aBytesRead);
		}
	else if (fieldType == EFieldTypeInstallBlock)
		{
		// No logo present
		iInstallBlock=CInstallBlock::NewL(aDataProvider, aBytesRead, EAssumeType);
		}
	else
		{
		User::Leave(KErrSISUnexpectedFieldType);
		}
	
	ReadMemberCollectionL(aDataProvider, iSignatureCertificateChains, EFieldTypeSignatureCertificateChain, EFieldTypeDataIndex, aBytesRead);
	
	iDataIndex=CDataIndex::NewL(aDataProvider, aBytesRead, EAssumeType);
	
	CField::SkipUnknownFieldsL(aDataProvider, aBytesRead - iDataOffset, aBytesRead, aTypeReadBehaviour);
	CField::EnsureAlignedL(aDataProvider, aBytesRead - iDataOffset, aBytesRead, aTypeReadBehaviour);
	
	iTrustStatus = new (ELeave)Swi::TSisTrustStatus();

	// Create unconstrained cert chain constraints object; this may be replaced.
	iCertChainConstraints = CCertChainConstraints::NewL();
	
	iRemoveWithLastDependent = 0;
	
	#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
	iUserGrantableCapabilities.SetEmpty();
	#endif
	}
	
void CController::ConstructL(TPtrProvider& aDataProvider, TInt64& aBytesRead, TReadTypeBehaviour aTypeReadBehaviour)
	{
	iDataOffset=aBytesRead;
	
	CField::ConstructL(aDataProvider, EFieldTypeController, aBytesRead, aTypeReadBehaviour);

 	iInfo=CInfo::NewL(aDataProvider, aBytesRead);
 	
	iSupportedOptions=CSupportedOptions::NewL(aDataProvider, aBytesRead);

	iLanguages=CSupportedLanguages::NewL(aDataProvider, aBytesRead);

	iPrerequisites=CPrerequisites::NewL(aDataProvider, aBytesRead);

	iProperties=CProperties::NewL(aDataProvider, aBytesRead);

	TFieldType fieldType;
	CField::ReadEnumL<TFieldType,TUint32>(aDataProvider, fieldType, aBytesRead);
	
	if (fieldType == EFieldTypeLogo)
		{
		// Logo present
		iLogo=CLogo::NewL(aDataProvider, aBytesRead, EAssumeType);
		iInstallBlock=CInstallBlock::NewL(aDataProvider, aBytesRead);
		}
	else if (fieldType == EFieldTypeInstallBlock)
		{
		// No logo present
		iInstallBlock=CInstallBlock::NewL(aDataProvider, aBytesRead, EAssumeType);
		}
	else
		{
		User::Leave(KErrSISUnexpectedFieldType);
		}
	
	ReadMemberCollectionL(aDataProvider, iSignatureCertificateChains, EFieldTypeSignatureCertificateChain, EFieldTypeDataIndex, aBytesRead);
	
	iDataIndex=CDataIndex::NewL(aDataProvider, aBytesRead, EAssumeType);
	
	CField::SkipUnknownFieldsL(aDataProvider, aBytesRead - iDataOffset, aBytesRead, aTypeReadBehaviour);
	CField::EnsureAlignedL(aDataProvider, aBytesRead - iDataOffset, aBytesRead, aTypeReadBehaviour);
	
	iTrustStatus = new (ELeave)Swi::TSisTrustStatus();

	// Create unconstrained cert chain constraints object; this may be replaced.
	iCertChainConstraints = CCertChainConstraints::NewL();
	
	iRemoveWithLastDependent = 0;
	
	#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
	iUserGrantableCapabilities.SetEmpty();
	#endif
	}	

EXPORT_C void CController::AddChainIndex(TInt aIndex) const
    {
	iCertChainIndices.Append(aIndex);
    }

EXPORT_C void CController::ClearCertChainIndices() const
    {
	iCertChainIndices.Reset();
    }

EXPORT_C void CController::SetCertChainConstraints(CCertChainConstraints* aConstraints)
 	{
 	// Delete existing cert chain constraints object.
 	delete iCertChainConstraints;
 	iCertChainConstraints=aConstraints;
 	}
 	
EXPORT_C TInt CController::RemoveWithLastDependent() const
	{
	return iRemoveWithLastDependent;
	}
	 	
EXPORT_C void CController::SetRemoveWithLastDependent()
	{
	iRemoveWithLastDependent = 1;
	}


EXPORT_C CMessageDigest* CController::GenerateControllerHashLC(const TDesC8& aController)
	{
	TInt64 infoLength = iInfo->Length() + iInfo->HeaderSize() + iInfo->PaddingSize();
	TInt64 installBLength = iInstallBlock->Length() + iInstallBlock->HeaderSize() + iInstallBlock->PaddingSize();	
	TInt64 supportLLength = iLanguages->Length() + iLanguages->HeaderSize() + iLanguages->PaddingSize();
	TInt64 supportOLength = iSupportedOptions->Length() + iSupportedOptions->HeaderSize() + iSupportedOptions->PaddingSize();
	TInt64 propertiesLength = iProperties->Length() + iProperties->HeaderSize() + iProperties->PaddingSize();
	TInt64 prerequisitesLength = iPrerequisites->Length() + iPrerequisites->HeaderSize() + iPrerequisites->PaddingSize();
	TInt64 logoLength = 0;
	
	if (iLogo)
		{
		logoLength = iLogo->Length() + iLogo->HeaderSize() + iLogo->PaddingSize();
		}

   	TInt64 controllerLength = 0;
	controllerLength = infoLength + installBLength + supportLLength + supportOLength + propertiesLength + prerequisitesLength + logoLength;

	// Length octets are either 4 or 8 bytes but since controllers are loaded into
	// bytes should ALWAYS be sufficient.
	TInt offset = 4;
	
	// Should really modify the length and not the offset skipping the length octets. 
	// This makes the controller hash independent of the signature chains.
	//
	// Unfortunately, this is fixed and changing it potentially breaks compatibility
	// E.g. If the firmware is upgraded without reformatting the system drive then SWI 
	// may think that an application on removable media has not been installed.
	controllerLength += (Length() > 0x80000000 ? 8 : 4);
		
	DEBUG_PRINTF4(_L8("GenerateControllerHashLC aController.Length()=%d, offset=%d, controllerLength=%d"), 
		aController.Length(), offset, controllerLength);
	
	if (offset + controllerLength >= aController.Length()) 
		{
		// Mid will panic if the lengths are outside the buffer.
		User::Leave(KErrCorrupt);
		}
	TPtrC8 ptr;
	ptr.Set(aController.Mid(offset, controllerLength));

	CMessageDigest* digest = CMessageDigestFactory::NewDigestLC(CMessageDigest::ESHA1);
	digest->Update(ptr);

	return digest;
	}
