/*
* Copyright (c) 2006-2010 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 "CSWICertStoreTool.h"
#include <testconfigfileparser.h>
#include <cctcertinfo.h>
#include <x509cert.h>
#include <utf.h>

_LIT(KSWICertStoreToolName, "SWICertStoreTool");
_LIT8(KNewLine, "\r\n");

_LIT8(KDefaultsSection, "Defaults");

_LIT8(KLogErrorTemplate, "*** %S: Error: %d");
_LIT8(KLogOKTemplate, "    %S: OK");
_LIT8(KLogParameterTemplate, "  %S = %S");

_LIT8(KLogStartMessage, "SWICertStoreTool");
_LIT8(KLogEndMessage, "Done.");
_LIT8(KLogSectionName, "Processing: %S");
_LIT8(KLogIllegalCertLabel, "*** Section name is not a legal cert label - must be between 0 and 64 characters long");
_LIT8(KLogDuplicateCertLabel, "*** Duplicate cert label");
_LIT8(KLogNoFileSpecifiedError, "*** No certificate file spcified (use 'file = <cert filename>'");
_LIT8(KLogIllegalMandatoryValue, "*** Illegal value for mandatory flag (must be 0 or 1)");
_LIT8(KLogIllegalSystemUpgradeValue, "*** Illegal value for system upgrade flag (must be 0 or 1)");
_LIT8(KLogIllegalCapabilityValue, "*** Unrecognised capability name: %S");
_LIT8(KLogIllegalApplicationValue, "*** Unrecognised application (must be 'SWInstall' or 'SWInstallOCSP')");
_LIT8(KLogNoApplications, "*** No applications specified (must one or more of 'SWInstall' and 'SWInstallOCSP')");

_LIT8(KActionOpenInputFile, "Opening input file");
_LIT8(KActionOpenOutputFile, "Opening output file");
_LIT8(KActionParseSection, "Parsing section");
_LIT8(KActionLoadCertData, "Loading certficate file");
_LIT8(KActionParseX509Cert, "Parsing X509 certificate");
_LIT8(KActionWriteCertData, "Writing cert data to store");
_LIT8(KActionWriteInfoStream, "Writing info stream to store");
_LIT8(KActionWriteRootStream, "Writing root stream to store");

_LIT8(KItemFile, "file");
_LIT8(KItemMandatory, "mandatory");
_LIT8(KItemSystemUpgrade, "systemupgrade");
_LIT8(KItemCapability, "capability");
_LIT8(KItemApplication, "application");

_LIT8(KParamInputFile, "input file");
_LIT8(KParamOutputFile, "outut file");
_LIT8(KParamLabel, "label");

_LIT8(KApplicationSWInstall, "SWInstall");
_LIT8(KApplicationSWInstallOCSP, "SWInstallOCSP");

_LIT8(KString0, "0");
_LIT8(KString1, "1");
_LIT8(KFalse, "false");
_LIT8(KTrue, "true");

const TBool KTrueValue = 1;
const TBool KFalseValue = 0;

const TUid KApplicationSWInstallUid = { 0x100042ab };
const TUid KApplicationSWInstallOCSPUid = { 0x1000a8b6 };

const TInt KMaxLineLength = 200;

// Capability names
_LIT8(KCapabilityTCB,  				"TCB");
_LIT8(KCapabilityCommDD,  			"CommDD");
_LIT8(KCapabilityPowerMgmt,  		"PowerMgmt");
_LIT8(KCapabilityMultimediaDD,  	"MultimediaDD");
_LIT8(KCapabilityReadDeviceData,  	"ReadDeviceData");
_LIT8(KCapabilityWriteDeviceData,  	"WriteDeviceData");
_LIT8(KCapabilityDRM,  				"DRM");
_LIT8(KCapabilityTrustedUI,  		"TrustedUI");
_LIT8(KCapabilityProtServ,  		"ProtServ");
_LIT8(KCapabilityDiskAdmin,  		"DiskAdmin");
_LIT8(KCapabilityNetworkControl,  	"NetworkControl");
_LIT8(KCapabilityAllFiles,  		"AllFiles");
_LIT8(KCapabilitySwEvent,  			"SwEvent");
_LIT8(KCapabilityNetworkServices,  	"NetworkServices");
_LIT8(KCapabilityLocalServices,  	"LocalServices");
_LIT8(KCapabilityReadUserData,  	"ReadUserData");
_LIT8(KCapabilityWriteUserData,  	"WriteUserData");
_LIT8(KCapabilityLocation,  		"Location");
_LIT8(KCapabilitySurroundingsDD,  	"SurroundingsDD");
_LIT8(KCapabilityUserEnvironment,  	"UserEnvironment");

#define TRAP_AND_LOG(NAME, ACTION) \
    { \
	TRAPD(err, ACTION); \
	TrapAndLogL(NAME, err); \
	}

 CSWICertStoreTool* CSWICertStoreTool::NewLC(const TDesC& aInputFile,
											 const TDesC& aOutputFile,
											 const TDesC& aLogFile)
	{
	CSWICertStoreTool* self = new (ELeave) CSWICertStoreTool(aInputFile, aOutputFile);
	CleanupStack::PushL(self);
	self->ConstructL(aLogFile);
	return self;
	}

CSWICertStoreTool::CSWICertStoreTool(const TDesC& aInputFile, const TDesC& aOutputFile) :
	iInputFile(aInputFile), iOutputFile(aOutputFile)
	{
	}

void CSWICertStoreTool::ConstructL(const TDesC& aLogFile)
	{
	User::LeaveIfError(iFs.Connect());
	User::LeaveIfError(iLogFile.Replace(iFs, aLogFile, EFileWrite | EFileShareExclusive));
	}

CSWICertStoreTool::~CSWICertStoreTool()
	{
	iLogFile.Close();
	iFs.Close();
	}

void CSWICertStoreTool::LogL(TRefByValue<const TDesC8> aFmt, ...)
	{
	TBuf8<KMaxLineLength> buf;
	VA_LIST args;
	VA_START(args, aFmt);
	buf.AppendFormatList(aFmt, args);
	VA_END(args);

	User::LeaveIfError(iLogFile.Write(buf));
	User::LeaveIfError(iLogFile.Write(KNewLine));
	User::LeaveIfError(iLogFile.Flush());
	}

void CSWICertStoreTool::TrapAndLogL(const TDesC8& aName, TInt err)
	{
	if (err != KErrNone)
		{
		LogL(KLogErrorTemplate, &aName, err); 
		User::Leave(err);										
		}
	else
		{ 
		LogL(KLogOKTemplate, &aName);
		}
	}

void CSWICertStoreTool::LogArgL(const TDesC8& aName, const TDesC& aValue)
	{
	TBuf8<KMaxLineLength> buf;
	buf.Copy(aValue);
	LogL(KLogParameterTemplate, &aName, &buf);
	}

void CSWICertStoreTool::LogParameterL(const TDesC8& aName, const TDesC8& aValue)
	{
	LogL(KLogParameterTemplate, &aName, &aValue);
	}

void CSWICertStoreTool::RunToolL()
	{
	TRAPD(err, DoRunToolL());
	
	iCerts.ResetAndDestroy();
	iCerts.Close();
	delete iStore;
	delete iScript;

	if (err != KErrNone)
		{
		iFs.Delete(iOutputFile); // ignore errors
		User::Leave(err);
		}
	}

void CSWICertStoreTool::DoRunToolL()
	{
	LogL(KLogStartMessage);
	
	LogArgL(KParamInputFile, iInputFile);
	TRAP_AND_LOG(KActionOpenInputFile, OpenInputFileL(iInputFile));
	
	LogArgL(KParamOutputFile, iOutputFile);
	TRAP_AND_LOG(KActionOpenOutputFile, OpenStoreL(iOutputFile));

	const RPointerArray<CTestConfigSection>& sections = iScript->Sections();

	for (TInt i = 0 ; i < sections.Count() ; ++i)
		{
		const CTestConfigSection& section = *sections[i];
		const TDesC8& name = section.SectionName();

		// Skip 'Defaults' section
		if (name == KDefaultsSection)
			{
			continue;
			}

		if (name.Length() == 0 || name.Length() > KMaxCertLabelLength)
			{
			LogL(KLogIllegalCertLabel, &name);
			User::Leave(KErrArgument);
			}
		
		LogL(KLogSectionName, &name);
		TRAP_AND_LOG(KActionParseSection, ParseScriptSectionL(section));
		}

	TStreamId infoStreamId = KNullStreamId;
	TRAP_AND_LOG(KActionWriteInfoStream, infoStreamId = WriteInfoStreamL());
	TRAP_AND_LOG(KActionWriteRootStream, WriteRootStreamL(infoStreamId));

	LogL(KLogEndMessage);
	}

void CSWICertStoreTool::ParseScriptSectionL(const CTestConfigSection& aSection)
	{
	// Get cert label
	const TDesC8& sectionName = aSection.SectionName();
	TCertLabel label;
	label.Copy(sectionName);
	LogParameterL(KParamLabel, aSection.SectionName());
	CheckCertLabelL(label);
	
	// Parse file
	const CTestConfigItem* item = aSection.Item(KItemFile);
	if (item == NULL)
		{
		LogL(KLogNoFileSpecifiedError);
		User::Leave(KErrNotFound);
		}
	const TDesC8& certFile = item->Value();
	LogParameterL(KItemFile, certFile);

	// Load certificate data
	HBufC8* certData = NULL;
	TRAP_AND_LOG(KActionLoadCertData, certData = LoadCertDataL(certFile));
	CleanupStack::PushL(certData);

	// Parse the certificate
	CX509Certificate* cert = NULL;
	TRAP_AND_LOG(KActionParseX509Cert, cert = CX509Certificate::NewL(*certData));
	CleanupStack::PushL(cert);

	// Write cert to store
	TStreamId dataStreamId = KNullStreamId;
	TRAP_AND_LOG(KActionWriteCertData, dataStreamId = WriteCertDataL(*certData));

	// parse additional meta data for the certificate
	TCertMetaInfo certMetaInfo;
	// Parse mandatory flag
	certMetaInfo.iIsMandatory = 0;
	const CTestConfigItem* mandatory = aSection.Item(KItemMandatory);
	if (mandatory != NULL)
		{
		const TDesC8& mandatoryValue = mandatory->Value();
		if (mandatoryValue.CompareF(KTrue) == KErrNone || mandatoryValue.CompareF(KString1) == KErrNone)
			{
			certMetaInfo.iIsMandatory = KTrueValue;
			}
		else if (mandatoryValue.CompareF(KFalse) == KErrNone || mandatoryValue.CompareF(KString0) == KErrNone)
			{
			certMetaInfo.iIsMandatory = KFalseValue;
			}
		else
			{
			LogL(KLogIllegalMandatoryValue, &mandatoryValue);
			User::Leave(KErrArgument);	
			}
		}
	LogParameterL(KItemMandatory, certMetaInfo.iIsMandatory ? KString1 : KString0);	
	
	// Parse system upgrade flag
	certMetaInfo.iIsSystemUpgrade = 0;
	const CTestConfigItem* sysUpgrade = aSection.Item(KItemSystemUpgrade);
	if (sysUpgrade != NULL)
		{
		const TDesC8& sysUpgradeValue = sysUpgrade->Value();
		if (sysUpgradeValue.CompareF(KTrue) == KErrNone || sysUpgradeValue.CompareF(KString1) == KErrNone)
			{
			certMetaInfo.iIsSystemUpgrade = KTrueValue;
			}
		else if (sysUpgradeValue.CompareF(KFalse) == KErrNone || sysUpgradeValue.CompareF(KString0) == KErrNone)
			{
			certMetaInfo.iIsSystemUpgrade = KFalseValue;
			}
		else
			{
			LogL(KLogIllegalSystemUpgradeValue, &sysUpgradeValue);
			User::Leave(KErrArgument);	
			}
		}
	LogParameterL(KItemSystemUpgrade, certMetaInfo.iIsSystemUpgrade ? KString1 : KString0);


	// Parse capabilities
	certMetaInfo.iCapabilities = ParseCapabilitiesL(aSection);

	// Parse applications
	RArray<TUid> applications;
	CleanupClosePushL(applications);
	ParseApplicationsL(aSection, applications);

	CSWICertStoreToolCert* certInfo = CSWICertStoreToolCert::NewLC(certData->Length(),
																   label,
																   iCertificateId++,
																   cert->KeyIdentifierL(),
																   applications,
																   dataStreamId,
																   certMetaInfo);
	User::LeaveIfError(iCerts.Append(certInfo));
	CleanupStack::Pop(certInfo);
	CleanupStack::PopAndDestroy(3, certData); // applications, cert, certData
	}

void CSWICertStoreTool::CheckCertLabelL(const TDesC& aLabel)
	{
	for (TInt i = 0 ; i < iCerts.Count() ; ++i)
		{
		if (iCerts[i]->Label().CompareF(aLabel) == 0)
			{
			LogL(KLogDuplicateCertLabel);
			User::Leave(KErrArgument);
			}
		}
	}

HBufC8* CSWICertStoreTool::LoadCertDataL(const TDesC8& aPath)
	{
	HBufC* aBuf = HBufC::NewLC(aPath.Length());
	TPtr path(aBuf->Des());
	CnvUtfConverter::ConvertToUnicodeFromUtf8(path,aPath);

	RFile file;
	User::LeaveIfError(file.Open(iFs, path, EFileRead | EFileShareExclusive));
	CleanupClosePushL(file);

	TInt size;
	User::LeaveIfError(file.Size(size));

	HBufC8* buf = HBufC8::NewLC(size);
	TPtr8 ptr = buf->Des();
	User::LeaveIfError(file.Read(ptr, size));
	
	CleanupStack::Pop(buf);
	CleanupStack::PopAndDestroy(&file);
	CleanupStack::PopAndDestroy(aBuf);
	return buf;
	}

TStreamId CSWICertStoreTool::WriteCertDataL(const TDesC8& aData)
	{
	RStoreWriteStream stream;
	TStreamId streamId = stream.CreateLC(*iStore);
	stream.WriteL(aData);
	stream.CommitL();
	CleanupStack::PopAndDestroy(&stream);
	return streamId;
	}

TCapabilitySet CSWICertStoreTool::ParseCapabilitiesL(const CTestConfigSection& aSection)
	{
	TCapabilitySet capabilities;
	capabilities.SetEmpty();
	for (TInt index = 0 ; ; ++index)
		{
		const CTestConfigItem* item = aSection.Item(KItemCapability, index);
		if (item == NULL)
			{
			break;
			}
		const TDesC8& capName = item->Value();
		TCapability capability = ECapability_None;
		TRAPD(err, capability = ParseCapabilityNameL(capName));
		if (err != KErrNone)
			{
			LogL(KLogIllegalCapabilityValue, &item->Value());
			User::Leave(KErrArgument);		
			}
		capabilities.AddCapability(capability);
		LogParameterL(KItemCapability, capName);
		}

	return capabilities;
	}

void CSWICertStoreTool::ParseApplicationsL(const CTestConfigSection& aSection, RArray<TUid>& aApplications)
	{
	CleanupClosePushL(aApplications);
	for (TInt index = 0 ; ; ++index)
		{
		const CTestConfigItem* item = aSection.Item(KItemApplication, index);
		if (item == NULL)
			{
			break;
			}
		const TDesC8& appName = item->Value();
		TUid application;
		TRAPD(err, application = ParseApplicationNameL(appName));
		if (err != KErrNone)
			{
			LogL(KLogIllegalApplicationValue, &item->Value());
			User::Leave(KErrArgument);		
			}
		User::LeaveIfError(aApplications.Append(application));
		LogParameterL(KItemApplication, appName);
		}
	
	if (aApplications.Count() == 0)
		{
		LogL(KLogNoApplications);
		User::Leave(KErrArgument);
		}
	CleanupStack::Pop(&aApplications);
	}

void CSWICertStoreTool::OpenInputFileL(const TDesC& aInputFile)
	{
	iScript = CTestConfig::NewLC(iFs, KSWICertStoreToolName, aInputFile);
	CleanupStack::Pop(iScript);
	}

void CSWICertStoreTool::OpenStoreL(const TDesC& aOutputFile)
	{
 	iStore = CPermanentFileStore::ReplaceL(iFs, aOutputFile, EFileRead | EFileWrite | EFileShareExclusive);
	iStore->SetTypeL(KPermanentFileStoreLayoutUid);
	}

TStreamId CSWICertStoreTool::WriteInfoStreamL()
	{
	// Create info stream
	RStoreWriteStream infoStream;
	TStreamId streamId = infoStream.CreateLC(*iStore);

	infoStream.WriteInt32L(iCerts.Count());
	for (TInt i = 0 ; i < iCerts.Count() ; ++i)
		{
		iCerts[i]->ExternalizeL(infoStream);
		}
	
	// Commit info stream
	infoStream.CommitL();
	CleanupStack::PopAndDestroy(&infoStream);

	return streamId;
	}

void CSWICertStoreTool::WriteRootStreamL(TStreamId aInfoStreamId)
	{
	// Create root stream - just contains id of info stream
	ASSERT(aInfoStreamId != KNullStreamId);
	RStoreWriteStream rootStream;
	TStreamId rootStreamId = rootStream.CreateLC(*iStore);
	iStore->SetRootL(rootStreamId);
	rootStream << aInfoStreamId;
	rootStream.CommitL();
	CleanupStack::PopAndDestroy(&rootStream);
	iStore->Commit();
	}

TUid CSWICertStoreTool::ParseApplicationNameL(const TDesC8& aName)
	{
	TUid result = KNullUid;
	if (aName == KApplicationSWInstall)
		{
		result = KApplicationSWInstallUid;
		}
	else if (aName == KApplicationSWInstallOCSP)
		{
		result = KApplicationSWInstallOCSPUid;
		}
	else
		{
		User::Leave(KErrNotFound);
		}
	return result;
	}

TCapability CSWICertStoreTool::ParseCapabilityNameL(const TDesC8& aName)
	{
	TCapability result = ECapability_None;

	if (aName == KCapabilityTCB) 					result = ECapabilityTCB;
	else if (aName == KCapabilityCommDD) 			result = ECapabilityCommDD;
	else if (aName == KCapabilityPowerMgmt) 		result = ECapabilityPowerMgmt;
	else if (aName == KCapabilityMultimediaDD) 		result = ECapabilityMultimediaDD;
	else if (aName == KCapabilityReadDeviceData) 	result = ECapabilityReadDeviceData;
	else if (aName == KCapabilityWriteDeviceData) 	result = ECapabilityWriteDeviceData;
	else if (aName == KCapabilityDRM) 				result = ECapabilityDRM;
	else if (aName == KCapabilityTrustedUI) 		result = ECapabilityTrustedUI;
	else if (aName == KCapabilityProtServ) 			result = ECapabilityProtServ;
	else if (aName == KCapabilityDiskAdmin) 		result = ECapabilityDiskAdmin;
	else if (aName == KCapabilityNetworkControl) 	result = ECapabilityNetworkControl;
	else if (aName == KCapabilityAllFiles) 			result = ECapabilityAllFiles;
	else if (aName == KCapabilitySwEvent) 			result = ECapabilitySwEvent;
	else if (aName == KCapabilityNetworkServices) 	result = ECapabilityNetworkServices;
	else if (aName == KCapabilityLocalServices) 	result = ECapabilityLocalServices;
	else if (aName == KCapabilityReadUserData) 		result = ECapabilityReadUserData;
	else if (aName == KCapabilityWriteUserData) 	result = ECapabilityWriteUserData;
	else if (aName == KCapabilityLocation) 			result = ECapabilityLocation;
	else if (aName == KCapabilitySurroundingsDD) 	result = ECapabilitySurroundingsDD;
	else if (aName == KCapabilityUserEnvironment) 	result = ECapabilityUserEnvironment;

	if (result == ECapability_None)
		{
		User::Leave(KErrArgument);
		}
	
	return result;
	}

