/*
* Copyright (c) 1998-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 "ValidateTest.h"
#include "t_input.h"
#include <ecom.h>

_LIT(KPath, "");
_LIT(KChainStart, "<chain>");
_LIT(KDateIssuedStart, "<dateissued>");
_LIT(KIOStart, "<io>");

CTestAction* CValidateTest::NewL(RFs& aFs, CConsoleBase& aConsole, 
		Output& aOut, const TTestActionSpec& aTestActionSpec)
	{
	CTestAction* self = CValidateTest::NewLC(aFs, aConsole, aOut, aTestActionSpec);
	CleanupStack::Pop(self);
	return self;
	}

CTestAction* CValidateTest::NewLC(RFs& aFs, CConsoleBase& aConsole, 
		Output& aOut, const TTestActionSpec& aTestActionSpec)
	{
	CValidateTest* self = new(ELeave) CValidateTest(aFs, aConsole, aOut);
	CleanupStack::PushL(self);
	self->ConstructL(aTestActionSpec);
	return self;
	}


void CValidateTest::TestValidation(TRequestStatus& aStatus)
	{
	iOriginalRequestStatus = &aStatus;
	aStatus = KRequestPending;

	iState = EInit;
	TRequestStatus* status = &aStatus;
	User::RequestComplete(status, KErrNone);
	}

CValidateTest::CValidateTest(RFs& aFs, 
								 CConsoleBase& aConsole,
								 Output& aOut)
: CTestAction(aConsole, aOut), iFs(aFs)
	{
	}

void CValidateTest::ConstructL(const TTestActionSpec& aTestActionSpec)
	{
	CTestAction::ConstructL(aTestActionSpec);
	iValidationResult = CWTLSValidationResult::NewL();
	HBufC* aBody = HBufC::NewLC(aTestActionSpec.iActionBody.Length());
	aBody->Des().Copy(aTestActionSpec.iActionBody);
	// creates the test chan validation object
	iTestChain = CTestChainValidate::NewL();
	TPtrC chainBuf = Input::ParseElement(*aBody, KChainStart);
	
	iTestChain->AddChainL(chainBuf);
	TPtrC ioDateIssued = Input::ParseElement(*aBody, KDateIssuedStart);
	iTestChain->AddDateIssued(ioDateIssued);
	TPtrC ioBuf = Input::ParseElement(*aBody, KIOStart);
	iTestChain->AddIOL(ioBuf);

	CleanupStack::PopAndDestroy(aBody);
	}

CValidateTest::~CValidateTest()
	{
	delete iTestChain;
	delete iCertUtils;
	delete iValidationResult;
	delete iChain;
	REComSession::FinalClose();
	}

void CValidateTest::DoPerformPrerequisite(TRequestStatus& aStatus)
	{
	TInt err = KErrNone;
	
	switch (iState)
		{
		case EInit:
			iResult = ETrue;
			__ASSERT_DEBUG(!iCertUtils, User::Panic(_L("TWTLSCertTest"), 1));
			iCertUtils = CCertUtils::NewL(iFs);
			TRAP(err, HandleEInitL(aStatus));
			break;

		case EValidationStoreInitStoreManager1:
			TRAP(err, HandleEValidationInitStoreManager1L(aStatus));
			iActionState = EAction;
			break;		
		case EValidationStoreDepopulateStore1:
		case EValidationStorePopulateStoreRoots:
		case EValidationStorePopulateStoreExtras:
		case EValidationStoreValidate:
		case EValidationStoreValidated:
		case EValidationStoreInitStoreManager2:
		case EValidationStoreDepopulateStore2:
		case EValidationStoreEnd:
		case EValidationSuppliedInit:
		case EValidationSuppliedValidate:
		case EValidationSuppliedValidated:
		case EFinished:
			break;	//	Nothing to do, for compiler
		}
	}

void CValidateTest::DoPerformPostrequisite(TRequestStatus& aStatus)
	{
	TInt err = KErrNone;

	switch (iState)
		{
	case EInit:
	case EValidationStoreInitStoreManager1:
	case EValidationStoreDepopulateStore1:
	case EValidationStorePopulateStoreRoots:
	case EValidationStorePopulateStoreExtras:
	case EValidationStoreValidate:
	case EValidationStoreValidated:
	case EValidationStoreInitStoreManager2:
	case EValidationStoreDepopulateStore2:
	case EValidationStoreEnd:
		break;	//	Nothing to do, for compiler
	case EValidationSuppliedInit:
			{
			iConsole.Printf(_L("started with supplied certs...\n"));
			iOut.writeString(_L("started with supplied certs..."));
			iOut.writeNewLine();

			iState = EValidationSuppliedValidate;
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, KErrNone);
			}
			break;

		case EValidationSuppliedValidate:
			{
			HBufC8* encodedCerts = ReadFilesLC(*iTestChain->iServerCerts);

			if(!encodedCerts)
				{
				TRequestStatus* status = &aStatus;
				iFinished = ETrue;
				User::RequestComplete(status, KErrNone);
				}
			else
				{
				TInt certCount = iTestChain->iRootCerts->MdcaCount();
				CArrayPtrFlat<CWTLSCertificate>* roots = 
					new(ELeave) CArrayPtrFlat<CWTLSCertificate>(1);
				TCleanupItem cleanup(CleanupCertArray, roots);
				CleanupStack::PushL(cleanup);
				for (TInt i = 0; i < certCount; i++)
					{
					// build the root certificates array including all the candidates.
					HBufC8* encCert = 
						ReadFileLC(iTestChain->iRootCerts->MdcaPoint(i));
					CWTLSCertificate* cert = CWTLSCertificate::NewLC(encCert->Des());
					roots->AppendL(cert);
					CleanupStack::Pop();	// cert
					CleanupStack::PopAndDestroy();	// encCert
					}

				__ASSERT_DEBUG(!iChain, User::Panic(_L("CValidateTest"), 1));
				iChain = CWTLSCertChain::NewL(iFs, *encodedCerts, *roots);

				CleanupStack::PopAndDestroy(2);	// encodedCerts, roots

				TDateTime dt(2000, EJuly, 0, 0, 0, 0, 0);
				if(iTestChain->iDateIssued == 1)
					{
					dt.SetYear(2002);
					}
				iTime = dt;
				
				iChain->ValidateL(*iValidationResult, iTime, aStatus);
				iState = EValidationSuppliedValidated;
				}
			}
			break;

		case EValidationSuppliedValidated:
			{
			delete iChain;
			iChain = 0;
			TWTLSValidationStatus* expectedStatus = iTestChain->iError;
			const TWTLSValidationStatus& actualStatus = iValidationResult->Error();

			iOut.writeString(_L("Expected Error = "));
			WriteError(expectedStatus->iReason);
			iOut.writeNewLine();

			iOut.writeString(_L("Actual Error = "));
			WriteError(actualStatus.iReason);
			iOut.writeNewLine();
					
			TInt wCount = iTestChain->iWarnings->Count();
			iOut.writeString(_L("Expected Warnings = "));
			iOut.writeNewLine();
			for (TInt i = 0; i < wCount; i++)
				{
				TWTLSValidationStatus warning = iTestChain->iWarnings->At(i);
				WriteError(warning.iReason);
				iOut.writeNewLine();
				}

			iOut.writeString(_L("Actual Warnings = "));
			iOut.writeNewLine();
			const CArrayFixFlat<TWTLSValidationStatus>& warnings = 
				iValidationResult->Warnings();
			wCount = warnings.Count();
			for (TInt j = 0; j < wCount; j++)
				{
				TWTLSValidationStatus warning = warnings.At(j);
				WriteError(warning.iReason);
				iOut.writeNewLine();
				}
			iOut.writeNewLine();

			if(expectedStatus->iReason != actualStatus.iReason)
				{
				iConsole.Printf(_L("FAILED!!!!\n"));
				iOut.writeString(_L("FAILED!!!!"));
				iOut.writeNewLine();
				iResult = EFalse;
				}
		
			// End of validatewith supplied
			if (err != KErrNone)
				{
				iOut.writeString(_L("Failed: leave code = "));
				iOut.writeNum(err);
				iOut.writeNewLine();
				}

			iState = EFinished;
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, KErrNone);
			}
			break;

		case EFinished:
			{
			TTime end;
			end.HomeTime();
			TTimeIntervalMicroSeconds intervalMS = end.MicroSecondsFrom(iStart);
			iConsole.Printf(_L("Time taken = %d milliseconds\n"), (intervalMS.Int64()/1000));
	
			TRequestStatus* status = &aStatus;
			iFinished = ETrue;
			User::RequestComplete(status, KErrNone);
			}
			break;
		}
	}

void CValidateTest::PerformAction(TRequestStatus& aStatus)
	{
	TRequestStatus* status = &aStatus;

	switch (iState)
		{
		case EValidationStoreDepopulateStore1:
			{
			iCertUtils->RemoveCertsL(aStatus);
			iState = EValidationStorePopulateStoreRoots;
			break;
			}

		case EValidationStorePopulateStoreRoots:
			{
			TUid uid = { 1 };
			TRAP_IGNORE(iCertUtils->RemoveApplicationL(uid));
			iCertUtils->AddApplicationL(_L("testwtls"), uid);
			iCertUtils->AddCACertsL(*iTestChain->iRootCerts, 
				*iTestChain->iRootLabels, EWTLSCertificate, 1, KPath, aStatus);
			iState = EValidationStorePopulateStoreExtras;
			break;
			}

		case EValidationStorePopulateStoreExtras:
			{
			iCertUtils->AddCACertsL(*iTestChain->iExtraCerts, 
				*iTestChain->iExtraLabels, EWTLSCertificate, 2, KPath, aStatus);
			iState = EValidationStoreValidate;
			break;
			}

		case EValidationStoreValidate:
			{
			HBufC8* encodedCerts = ReadFilesLC(*iTestChain->iServerCerts);

			TUid testUid = TUid::Uid(1);

			if(!encodedCerts)
				{
				TRequestStatus* status = &aStatus;
				iFinished = ETrue;
				User::RequestComplete(status, KErrNone);
				}
			else
				{
				__ASSERT_DEBUG(!iChain, User::Panic(_L("CValidateTest"), 1));
				iChain = CWTLSCertChain::NewL(iFs, *encodedCerts, testUid);
				CleanupStack::PopAndDestroy();	// encodedCerts

				TDateTime dt(2000, EJuly, 0, 0, 0, 0, 0);
				if(iTestChain->iDateIssued == 1)
					{
					dt.SetYear(2002);
					}
				iTime = dt;

				TRAP_IGNORE(iChain->ValidateL(*iValidationResult, iTime, aStatus));
				iState = EValidationStoreValidated;
				};
			break;
			}
			
		case EValidationStoreValidated:
			{
			TInt count = iChain->Count();
			if (count > 0)
				{
				iOut.writeString(_L("EE certificate = "));
				const CWTLSCertificate& eeCert = iChain->Cert(0);
				HBufC* eeSubject = eeCert.SubjectL();
				CleanupStack::PushL(eeSubject);
				iOut.writeString(*eeSubject);
				CleanupStack::PopAndDestroy();
				iOut.writeNewLine();
				for (TInt i = 1; i < count - 1; i++)
					{
					iOut.writeString(_L("Intermediate certificate = "));
					const CWTLSCertificate& iCert = iChain->Cert(i);
					HBufC* subject = iCert.SubjectL();
					CleanupStack::PushL(subject);
					iOut.writeString(*subject);
					CleanupStack::PopAndDestroy();
					iOut.writeNewLine();	
					}
				iOut.writeString(_L("Root certificate = "));
				const CWTLSCertificate& rCert = iChain->Cert(count-1);
				HBufC* rSubject = rCert.SubjectL();
				CleanupStack::PushL(rSubject);
				iOut.writeString(*rSubject);
				CleanupStack::PopAndDestroy();
				iOut.writeNewLine();	
				}

			TWTLSValidationStatus* expectedStatus = iTestChain->iError;
			const TWTLSValidationStatus& actualStatus = iValidationResult->Error();

			iOut.writeString(_L("Expected Error = "));
			WriteError(expectedStatus->iReason);
			iOut.writeNewLine();

			iOut.writeString(_L("Actual Error = "));
			WriteError(actualStatus.iReason);
			iOut.writeNewLine();
			
			TInt wCount = iTestChain->iWarnings->Count();
			iOut.writeString(_L("Expected Warnings = "));
			iOut.writeNewLine();
			for (TInt i = 0; i < wCount; i++)
				{
				TWTLSValidationStatus warning = iTestChain->iWarnings->At(i);
				WriteError(warning.iReason);
				iOut.writeNewLine();
				}

			iOut.writeString(_L("Actual Warnings = "));
			iOut.writeNewLine();
			const CArrayFixFlat<TWTLSValidationStatus>& warnings = 
				iValidationResult->Warnings();
			wCount = warnings.Count();
			for (TInt j = 0; j < wCount; j++)
				{
				TWTLSValidationStatus warning = warnings.At(j);
				WriteError(warning.iReason);
				iOut.writeNewLine();
				}
			iOut.writeNewLine();
			
			if(expectedStatus->iReason != actualStatus.iReason)
				{
				iConsole.Printf(_L("FAILED!!!!\n"));
				iOut.writeString(_L("FAILED!!!!"));
				iOut.writeNewLine();
				iResult = EFalse;
				}

			// End of DoValidate
			iState = EValidationStoreInitStoreManager2;				
			User::RequestComplete(status, KErrNone);
			delete iChain;
			iChain = 0;
			break;
			}

		case EValidationStoreInitStoreManager2:
			{
			User::RequestComplete(status, KErrNone);
			iState = EValidationStoreDepopulateStore2;
			break;
			}

		case EValidationStoreDepopulateStore2:
			{
			iCertUtils->RemoveCertsL(aStatus);
			TUid uid = { 1 };
			iCertUtils->RemoveApplicationL(uid);
			iState = EValidationStoreEnd;
			break;
			}

		case EValidationStoreEnd:
			{
			iState = EValidationSuppliedInit;
			iActionState = EPostrequisite;
			User::RequestComplete(status, KErrNone);
			break;
			}
		case EInit:
		case EValidationStoreInitStoreManager1:
		case EValidationSuppliedInit:
		case EValidationSuppliedValidate:
		case EValidationSuppliedValidated:
		case EFinished:
			break;	//	Nothing to do, for compiler
		}
	}

HBufC8* CValidateTest::ReadFileLC(const TDesC& aFilename)
	{
	RFile file;
	User::LeaveIfError(file.Open(iFs, aFilename, EFileRead));
	CleanupClosePushL(file);
	TInt size;
	file.Size(size);
	CleanupStack::PopAndDestroy(1);//fileClose

	HBufC8* res = HBufC8::NewLC(size);
	TPtr8 p(res->Des());
	p.SetLength(size);

	RFileReadStream stream;
	User::LeaveIfError(stream.Open(iFs, aFilename, EFileStream));
	CleanupClosePushL(stream);
	stream.ReadL(p, size);
	CleanupStack::PopAndDestroy();//streamClose...bleurgh
	return res;
	}


HBufC8* CValidateTest::ReadFilesLC(CDesCArray& aServerCerts)
	{
	TInt count = aServerCerts.MdcaCount();
	TInt totalSize = 0;
	TInt i;

	for (i = 0; i < count; i++)
		{
		TPtrC filename = aServerCerts.MdcaPoint(i);
		RFile file;
		TRAPD(err, file.Open(iFs, filename, EFileRead));
		if(err != KErrNone)
			{
			HBufC *failedToLoad = filename.AllocLC();
			SetScriptError(EFileNotFound, failedToLoad->Des());
			CleanupStack::PopAndDestroy(2);//fsclose, fileClose	
			return(NULL);
			};
		CleanupClosePushL(file);
		TInt size;
		file.Size(size);
		CleanupStack::PopAndDestroy(1);	//	fileClose
		totalSize += size;
		}

	HBufC8* res = HBufC8::NewLC(totalSize);
	TPtr8 pRes = res->Des();
	for (i = 0; i < count; i++)
		{
		HBufC8* cert = ReadFileLC(aServerCerts.MdcaPoint(i));
		pRes.Append(cert->Des());
		CleanupStack::PopAndDestroy();//cert
		}
	return res;
	}

void CValidateTest::WriteError(TValidationError aError)
	{
	switch(aError)
		{
		//errors
		case EValidatedOK:
			{
			iOut.writeString(_L("Validated OK"));
			break;
			}
		case EChainHasNoRoot:
			{
			iOut.writeString(_L("No trusted root found"));
			break;
			}
		case ESignatureInvalid:
			{
			iOut.writeString(_L("Signature invalid"));
			break;
			}
		case EDateOutOfRange:
			{
			iOut.writeString(_L("Date out of range"));
			break;
			}
		case ENamesDontChain:
			{
			iOut.writeString(_L("Names don't chain"));
			break;
			}
		case ENotCACert:
			{
			iOut.writeString(_L("Cert not authorised to sign other certs"));
			break;
			}

		default:
			{
			iOut.writeString(_L("Unknown"));
			break;
			}
		}
	}

void CValidateTest::CleanupCertArray(TAny* aCertArray)
	{
	CArrayPtrFlat<CWTLSCertificate>* certs = REINTERPRET_CAST(CArrayPtrFlat<CWTLSCertificate>*, aCertArray);
	certs->ResetAndDestroy();
	delete certs;
	}

void CValidateTest::HandleEInitL(TRequestStatus& aStatus)
	{
	iConsole.Printf(_L("Chain validation tests...\n"));
	iStart.HomeTime();

	iConsole.Printf(_L("started with store...\n"));
	iOut.writeString(_L("started with store..."));
	iOut.writeNewLine();

	iState = EValidationStoreInitStoreManager1;
	TRequestStatus* status = &aStatus;
	User::RequestComplete(status, KErrNone);
	}

void CValidateTest::HandleEValidationInitStoreManager1L(TRequestStatus& aStatus)
	{
	TRequestStatus* status = &aStatus;
	User::RequestComplete(status, KErrNone);
	iState = EValidationStoreDepopulateStore1;
	}


void CValidateTest::DoReportAction()
	{
	}

void CValidateTest::DoCheckResult(TInt /*aError*/)
	{
	}
