/*
* Copyright (c) 2008-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: 
*
*/


#include "validateteststep.h"


CValidateCertChainStep::CValidateCertChainStep()
	{
	SetTestStepName(KValidateCertChainStep);
	}

CValidateCertChainStep::~CValidateCertChainStep()
	{
	delete iActiveObject;
	}

TVerdict CValidateCertChainStep::doTestStepPreambleL()
	{
	CPkixCertStepBase::doTestStepPreambleL();
	GetIntFromConfig(ConfigSection(), KNumberOfCerts, iNumberOfCerts);
	return EPass;
	}

void CValidateCertChainStep::PerformTestL()
	{
	CPkixCertStepBase::PerformTestL();
	
	iCertChain->SetSupportedCriticalExtensionsL(iProcessedOids);
	
	TTime time;
	time.UniversalTime();
	
	ValidateL(time, ConfigSection());
	
	CleanupStack::PopAndDestroy(iCertChain);

	}


void CValidateCertChainStep::ValidateL(TTime aValidateTime, TDes& aConfigSection)
	{
	
	CPKIXValidationResult* result = CPKIXValidationResult::NewLC();
	
	iActiveObject = new (ELeave) CValidateCertChainHelper(Logger());
	CleanupStack::PushL(iActiveObject);

	User::LeaveIfError(iActiveObject->PerformTestL(iCertChain, *result, aValidateTime));

	CleanupStack::PopAndDestroy(iActiveObject);
	iActiveObject = NULL;

	TInt expectedError;
	GetIntFromConfig(aConfigSection, KError, expectedError);
		
	if ((result->Error()).iReason != expectedError)
		{
		ERR_PRINTF3(_L("ERROR: Error: %d.  Expected error: %d"), (result->Error()).iReason, expectedError);
		User::Leave(KErrGeneral);
		}
			
	if (expectedError != EValidatedOK)
		{
		//we are expected to error
		TInt certIndex;
		GetIntFromConfig(aConfigSection, KErrorIndex, certIndex);	
		if ((result->Error()).iCert != certIndex)
			{			
			ERR_PRINTF3(_L("ERROR: Error certificate index: %d.  Expected index: %d"), (result->Error()).iCert, certIndex);
			User::Leave(KErrGeneral);			
			}
		else{
			CleanupStack::PopAndDestroy(result);
			return; //we have passed
			}
		}
		
	
	const CArrayFixFlat<TValidationStatus>& warns = result->Warnings();
	RArray<TValidationStatus> resultOldWarnings;
	CleanupClosePushL(resultOldWarnings);	
	TInt i;
	for (i=0; i < warns.Count(); ++i)
		{
		resultOldWarnings.AppendL(warns[i]);
		}

	const RPointerArray<CCertificateValidationWarnings>& certificateWarningsArray = result->ValidationWarnings();	
	
	HBufC* key;

	RArray<TValidationStatus> processedPolicyQualifiers;
	CleanupClosePushL(processedPolicyQualifiers);		
	
	TInt qualifierWarning = 18;

	for (i=0; i < iNumberOfCerts; ++i)
		{
		key = HBufC::NewLC(KCert().Length() + KKeyFormat().Length() + KPolicyIndex().Length()); 
		TPtr ptr(key->Des());
		ptr = KCert();
		ptr.AppendFormat(KKeyFormat(), i);
		ptr.Append(KPolicyIndex());	
		
		TInt policyIndex = 0;
		GetIntFromConfig(aConfigSection, *key, policyIndex);
		
		if (policyIndex > 0)
			{
			TValidationError error = (TValidationError) qualifierWarning;
			processedPolicyQualifiers.Append(TValidationStatus(error, policyIndex));
			}

		CleanupStack::PopAndDestroy(key);
		}
	
	//handle 1st part old warnings first - check any warnings we expect are present (later we check no additionals) 
	CheckWarningsPresentAndRemoveL(processedPolicyQualifiers, resultOldWarnings);
	
	RArray<TInt> configOldWarnings;
	CleanupClosePushL(configOldWarnings);
	RArray<TValidationStatus> processedConfigOldWarnings;
	CleanupClosePushL(processedConfigOldWarnings);		
	
	RArray<TValidationStatus> configNewWarnings;
	CleanupClosePushL(configNewWarnings);
	RArray<TPtrC> configCritOids;
	CleanupClosePushL(configCritOids);	
	
	for (TInt cert_index=0; cert_index < iNumberOfCerts; ++cert_index)
		{
		INFO_PRINTF2(_L("Certificate index: %d"), cert_index);
		key = HBufC::NewLC(KCert().Length() + KKeyFormat().Length() + KOldWarnings().Length()); 
		TPtr ptr(key->Des());
		ptr = KCert();
		ptr.AppendFormat(KKeyFormat(), cert_index);
		ptr.Append(KOldWarnings());
		
		GetIntArrayFromConfigL(aConfigSection, *key, configOldWarnings);
		
		processedConfigOldWarnings.Reset();
		for (TInt i=0; i < configOldWarnings.Count(); ++i)
			{
			TValidationError error = (TValidationError) configOldWarnings[i];
			processedConfigOldWarnings.Append(TValidationStatus(error, cert_index));
			}
		
		TransferToNewWarningsL(configOldWarnings, configNewWarnings, cert_index);
		
		ptr.Zero();	
		ptr = KCert();
		ptr.AppendFormat(KKeyFormat(), cert_index);
		ptr.Append(KCritOid());
			
		GetStringArrayFromConfigL(aConfigSection, *key, configCritOids);
		
		CleanupStack::PopAndDestroy(key);
		
		//we now have all our arguments from the config file
		
		//handle 1st part old warnings first - check any warnings we expect are present (later we check no additionals) 
		CheckWarningsPresentAndRemoveL(processedConfigOldWarnings, resultOldWarnings);
		
		
		CCertificateValidationWarnings* certWarning = certificateWarningsArray[((iNumberOfCerts - cert_index) - 1)];	//not owned	//warnings returned in reverse order
		
		//check cert and certindex match up correctly		
		if (certWarning->CertIndex() != cert_index)
			{
			ERR_PRINTF3(_L("ERROR: Certificate indexes do not match.  Certificate index: %d.  Expected index: %d"), certWarning->CertIndex(), cert_index);
			User::Leave(KErrGeneral);
			}
		
		key = HBufC::NewLC(KCert().Length() + KKeyFormat().Length() + KPolicyIndex().Length()); 
		TPtr pt(key->Des());
		pt = KCert();
		pt.AppendFormat(KKeyFormat(), cert_index);
		pt.Append(KPolicyIndex());	
			
		TInt policyIndex = 0;
		GetIntFromConfig(aConfigSection, *key, policyIndex);
			
		if (policyIndex > 0)
			{
			TValidationError error = (TValidationError) qualifierWarning;
			configNewWarnings.Append(TValidationStatus(error, policyIndex));
			}

		CleanupStack::PopAndDestroy(key);
			
		//check new warnings behave correctly
		CheckWarningsPresentAndRemoveL(certWarning->Warnings(), configNewWarnings);
		
		if (configNewWarnings.Count() != 0)
			{
			for (TInt err=0; err < configNewWarnings.Count(); ++err)
				{
				ERR_PRINTF2(_L("ERROR: Warning(new API) not reported: %d"), (configNewWarnings[err]).iReason);
				}
			User::Leave(KErrGeneral);
			}
		
		
		//check critoids behave correctly
		CheckCriticalExtsPresentAndRemoveL(certWarning->CriticalExtensionsFound(), configCritOids);
		if (configCritOids.Count() != 0)
			{
			for (TInt err=0; err < configCritOids.Count(); ++err)
				{
				ERR_PRINTF2(_L("ERROR: Critical extension not reported: %S"), &configCritOids[err]);
				}			
			User::Leave(KErrGeneral);
			}
			
		configOldWarnings.Reset();
		configNewWarnings.Reset();
		configCritOids.Reset();		
		}
		
	CleanupStack::PopAndDestroy(5, &processedPolicyQualifiers);	
		
	//check if any warnings in old api we weren't expecting	
	if (resultOldWarnings.Count() != 0)
		{	
		for (TInt warn=0; warn < resultOldWarnings.Count(); ++warn)
			{
			ERR_PRINTF3(_L("ERROR: Warning(old API) not reported: %d.  Index: %d"), (resultOldWarnings[warn]).iReason, (resultOldWarnings[warn]).iCert);
			}		
		User::Leave(KErrGeneral);
		}
	
	CleanupStack::PopAndDestroy(2, result);
	}

void CValidateCertChainStep::TransferToNewWarningsL(RArray<TInt>& aOldWarnings, RArray<TValidationStatus>& aNewWarnings, TInt aCertIndex)
	{
	for (TInt i=0; i < aOldWarnings.Count(); ++i)
		{
		switch (aOldWarnings[i])
			{
			case ECriticalExtendedKeyUsage:
				break;
			case ECriticalPolicyMapping:
				break;
			case ECriticalDeviceId:
				break;
			case ECriticalSid:
				break;
			case ECriticalVid:
				break;
			case ECriticalCapabilities:
				break;
			case ECriticalCertPoliciesWithQualifiers:	//handled by ini parameter due to overloading by warning of iCert value
				break;
			default:
				TValidationError error = (TValidationError) aOldWarnings[i];
				aNewWarnings.AppendL(TValidationStatus(error, aCertIndex));							
			}
		}
	}





void CValidateCertChainStep::CheckWarningsPresentAndRemoveL(const RArray<TValidationStatus>& aWarningsToCheckFor, RArray<TValidationStatus>& aWarningsToCheckIn)
	{
	TBool found;
	for (TInt warnings_index=0; warnings_index < aWarningsToCheckFor.Count(); ++warnings_index)
		{
		found = EFalse;
		for (TInt k=0; k < aWarningsToCheckIn.Count(); ++k)
			{
			if (ValidationStatusEqual(aWarningsToCheckFor[warnings_index], aWarningsToCheckIn[k]))
				{
				found = ETrue;
				aWarningsToCheckIn.Remove(k);
				break;
				}
			}
				
		if (!found)	
			{	
			ERR_PRINTF2(_L("ERROR: Warning not present: %d"), aWarningsToCheckFor[warnings_index].iReason);
			User::Leave(KErrGeneral); 
			}
		}	
	}


TBool CValidateCertChainStep::ValidationStatusEqual(const TValidationStatus& aValidationStatus1, const TValidationStatus& aValidationStatus2)
	{
	if (aValidationStatus1.iReason != aValidationStatus2.iReason)
		{
		return EFalse;
		}
	if (aValidationStatus1.iCert != aValidationStatus2.iCert)
		{
		return EFalse;
		}
	return ETrue;	
	}


//takes away from aOidsToCheckAgainst
void CValidateCertChainStep::CheckCriticalExtsPresentAndRemoveL(const RPointerArray<TDesC>& aOidsToLookFor, RArray<TPtrC>& aOidsToCheckAgainst)
	{
	TBool found;
	for (TInt oid_index=0; oid_index < aOidsToLookFor.Count(); ++oid_index)
		{
		found = EFalse;
		for (TInt k=0; k < aOidsToCheckAgainst.Count(); ++k)
			{
			if ((*aOidsToLookFor[oid_index])==(aOidsToCheckAgainst[k]))
				{
				found = ETrue;
				aOidsToCheckAgainst.Remove(k);
				break;
				}
			}
				
		if (!found)	
			{
			ERR_PRINTF2(_L("ERROR: Critical extension OID not present: %S"), aOidsToLookFor[oid_index]);
			User::Leave(KErrGeneral);	
			}
		}		
	}



//////////////////////////************

CValidateCertChainHelper::CValidateCertChainHelper(CTestExecuteLogger& aLogger)
:  CActive(EPriorityStandard), iLogger(aLogger)
	{
	CActiveScheduler::Add(this);
	}
	
	
CValidateCertChainHelper::~CValidateCertChainHelper()
	{
	Cancel();
	}
	

TInt CValidateCertChainHelper::PerformTestL(CPKIXCertChain* aCertChain, CPKIXValidationResult& aValidResult, TTime aValidateTime)
	{
	iCertChain = aCertChain;
	
	//call async
	aCertChain->ValidateL(aValidResult, aValidateTime, iStatus);
	
	SetActive();
   	CActiveScheduler::Start();
   	
    return iRunError;	
	}
	
	
void CValidateCertChainHelper::DoCancel()
	{
	iCertChain->CancelValidate();
	}
	
	
void CValidateCertChainHelper::RunL()
	{
	iRunError = KErrNone;
	User::LeaveIfError(iStatus.Int());
	
	//processing here

	
	CActiveScheduler::Stop();	
	}
	

TInt CValidateCertChainHelper::RunError(TInt aError)
	{
	iRunError = aError;
	//processing here

	
	CActiveScheduler::Stop();
	return KErrNone;	
	}
