/*
* 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: 
*
*/


#include "t_unifiedcertstoreadd.h"
#include "t_certstoredefs.h"
#include "t_input.h"
#include "t_certstoreout.h"
#include <mctwritablecertstore.h>

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

CAddCertificate::~CAddCertificate()
	{
	delete iCertificate;
	delete iCertificateURL;
	delete iCertificateContent;
	delete iCertificateLabel;
	}

void CAddCertificate::PerformAction(TRequestStatus& aStatus)
	{
	switch (iState)
		{
		case EAdding:
			{
 			if (iNotificationSubscribed)
 				{
 				if (!iNotifier)
 					{
 					iNotifier = CCertStoreChangeNotifier::NewL(iNotifierFlag);
 					iNotifier->StartNotification();
 					}
 				iState = ECheckNotification;
 				}
 			else
 				{
 				iState = EFinished;
 				}
			MCTWritableCertStore& store = UnifiedCertStore().WritableCertStore(iStoreIndex);
			
			TKeyIdentifier* issuerKeyId = &iIssuerKeyId;
			TKeyIdentifier* subjectKeyId = &iSubjectKeyId;
			
			ASSERT(iCertificateLabel);
			
			// Use the Add() with Deletable param if Deletable flag present in test data
			if (iDeletableFlagPresent)
				{
				store.Add(*iCertificateLabel, iCertificateFormat, iOwnerType,
						  subjectKeyId, issuerKeyId, *iCertificateContent, 
						  iDeletable, aStatus);
				}
			// otherwise, use the original Add()
			else 
				{
				store.Add(*iCertificateLabel, iCertificateFormat, iOwnerType,
						  subjectKeyId, issuerKeyId, *iCertificateContent, aStatus);
				}
			}
			break;
 		case ECheckNotification:
 			{
 			TInt ret=KErrNone;
 			if (!iNotifierFlag)
 				{
 				ret=KErrGeneral;
 				}
 			iState = EFinished;
 			TRequestStatus* status = &aStatus;
 			User::RequestComplete(status, ret);
 			
 			break;
 			}
		case EFinished:
			{
			if (aStatus == iExpectedResult)
				{
				iResult = ETrue;
				}
			else
				{
				iResult = EFalse;
				}

            if (aStatus != KErrNoMemory)
                {
                iFinished = ETrue;
                }
            
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, aStatus.Int());
			}
			break;

		default:
			User::Invariant();
			break;
		}
	}

void CAddCertificate::PerformCancel()
	{
	switch (iState)
		{
	case ECheckNotification:
	case EFinished:	
		{
		MCTWritableCertStore& store = UnifiedCertStore().WritableCertStore(iStoreIndex);
		store.CancelAdd();
		break;
		}
	default:
		break;
		}
	}

void CAddCertificate::AfterOOMFailure()
	{
	}

void CAddCertificate::Reset()
	{
	iState = EAdding;
	}

void CAddCertificate::DoReportAction()
	{
	iOut.writeString(_L("Adding certificate..."));
	iOut.writeNewLine();
	iOut.writeString(_L("\tLabel = "));
	iOut.writeString(*iCertificateLabel);
	iOut.writeNewLine();
	iOut.writeString(_L("\tOwner type = "));
	WriteOwnerType();
	WriteFormat();
	iOut.writeString(_L("\tSubjectKeyId:  "));
	iOut.writeOctetString(iSubjectKeyId);
	iOut.writeNewLine();
	iOut.writeString(_L("\tDeletable = "));
	iDeletable ? iOut.writeString(KTrue) : iOut.writeString(KFalse);
	iOut.writeNewLine();
	iOut.writeNewLine();
	}

void CAddCertificate::WriteFormat()
	{
	iOut.writeString(_L("\tFormat = "));
	switch (iCertificateFormat)
		{
		case EX509Certificate:
			iOut.writeString(_L("X.509\n"));
			break;
			
		case EWTLSCertificate:
			iOut.writeString(_L("WTLS\n"));
			break;
			
		case EX509CertificateUrl:
			iOut.writeString(_L("X.509 URL\n"));
			break;
			
		case EWTLSCertificateUrl:
			iOut.writeString(_L("WTLS URL\n"));
			break;
			
		default:
			iOut.writeString(_L("Unknown format\n"));
			break;
		}
	}

void CAddCertificate::WriteOwnerType()
	{
	switch (iOwnerType)
		{
		case ECACertificate:
			iOut.writeString(_L("CA\n"));
			break;
			
		case EUserCertificate:
			iOut.writeString(_L("User"));
			break;
			
		case EPeerCertificate:
			iOut.writeString(_L("Peer"));
			break;

		default:
			iOut.writeString(_L("Unknown"));
			break;
		}
	}
	
CAddCertificate::CAddCertificate(RFs& aFs, CConsoleBase& aConsole,
								 Output& aOut)
: CSubscriberAction(aFs, aConsole, aOut), iState(EAdding), 
  iDeletable(ETrue), iDeletableFlagPresent(EFalse)
	{
	}

void CAddCertificate::ConstructL(const TTestActionSpec& aTestActionSpec)
	{
	CSubscriberAction::ConstructL(aTestActionSpec);

	SetCertFormatL(Input::ParseElement(aTestActionSpec.iActionBody, KCertFormatStart));
	SetCertOwnerTypeL(Input::ParseElement(aTestActionSpec.iActionBody, KCertOwnerTypeStart));
	SetCertLabelL(Input::ParseElement(aTestActionSpec.iActionBody, KCertLabelStart));
	SetKeyId(iIssuerKeyId, Input::ParseElement(aTestActionSpec.iActionBody, KIssuerKeyStart));
	SetKeyId(iSubjectKeyId, Input::ParseElement(aTestActionSpec.iActionBody, KSubjectKeyStart));
	SetStoreToUse(Input::ParseElement(aTestActionSpec.iActionBody, KStoreToUseStart));

	TPtrC8 certFileOrURL = Input::ParseElement(aTestActionSpec.iActionBody, KCertFileStart);

	SetCertificateContentL(certFileOrURL);

	if (iCertificateFormat == EX509CertificateUrl ||
		iCertificateFormat == EWTLSCertificateUrl)
		{
		iCertificateURL = certFileOrURL.AllocL();
		}
	else
		{
		ConstructCertL(certFileOrURL);
		}
		
	// check for a possible deletable flag value for the certificate
	TInt err = KErrNone;
	TInt pos = 0;
	const TDesC8& deletableStr = Input::ParseElement(aTestActionSpec.iActionBody, 
														KDeletableStart,
														KDeletableEnd,
														pos,
														err);

	// set the deletable attribute if a value was found for the certificate
	if (err == KErrNone)
		{
		SetDeletable(deletableStr);
		}

	// Setting the expected result
	HBufC* result = HBufC::NewLC(aTestActionSpec.iActionResult.Length());
	TPtr(result->Des()).Copy(aTestActionSpec.iActionResult);
	Input::GetExpectedResultL(Input::ParseElement(*result, KReturnStart, KReturnEnd), iExpectedResult);
	CleanupStack::PopAndDestroy(result);
	}

void CAddCertificate::SetKeyId(TKeyIdentifier& aKeyIdentifier, const TDesC8& aKeyInfo)
	{
	TInt size = aKeyInfo.Length();
	for (TInt i = 0; i < size; i += 2)
		{
		TInt a = (aKeyInfo[i+1] >= 'a') ? (aKeyInfo[i+1] - 'a' + 10) : (aKeyInfo[i+1] - '0');
		TInt b = (aKeyInfo[i] >= 'a') ? (aKeyInfo[i] - 'a' + 10) : (aKeyInfo[i] - '0');
		aKeyIdentifier.Append(a  + b * 16);
		}
	}

void CAddCertificate::SetCertFormatL(const TDesC8& aFormat)
	{
	if (aFormat == KX509)
		{
		iCertificateFormat = EX509Certificate;
		}
	else if (aFormat == KWTLS)
		{
		iCertificateFormat = EWTLSCertificate;
		}
	else if (aFormat == KX509URL)
		{
		iCertificateFormat = EX509CertificateUrl;
		}
	else if (aFormat == KWTLSURL)
		{
		iCertificateFormat = EWTLSCertificateUrl;
		}
	else if (aFormat == KUnknown)
		{
		iCertificateFormat = EUnknownCertificate;
		}
	else
		{
		iOut.write(_L("Unknown cert format: "));
		iOut.writeString(aFormat);
		iOut.writeNewLine();		   
		User::Leave(KErrArgument);
		}
	}

void CAddCertificate::SetCertOwnerTypeL(const TDesC8& aOwnerType)
	{
	if (aOwnerType == KCACert)
		{
		iOwnerType = ECACertificate;
		}
	else if (aOwnerType == KUserCert)
		{
		iOwnerType = EUserCertificate;
		}
	else if (aOwnerType == KPeerCert)
		{
		iOwnerType = EPeerCertificate;
		}
	else if (aOwnerType == KUnknown)
		{
		// set dummy bogus owner type
		iOwnerType = static_cast<TCertificateOwnerType>(EPeerCertificate + 1);
		}
	else
		{
		iOut.write(_L("Unknown cert owner type: "));
		iOut.writeString(aOwnerType);
		iOut.writeNewLine();		   
		User::Leave(KErrArgument);
		}
	}

void CAddCertificate::SetCertLabelL(const TDesC8& aLabel)
	{
	delete iCertificateLabel;
	iCertificateLabel = NULL;
	iCertificateLabel = HBufC::NewL(aLabel.Length());
	TPtr ptr = iCertificateLabel->Des();
	ptr.Copy(aLabel);
	}

void CAddCertificate::SetStoreToUse(const TDesC8& aStoreToUse)
	{
	TLex8 lex(aStoreToUse);
	lex.Val(iStoreIndex);
	}

void CAddCertificate::SetCertificateContentL(const TDesC8& aFileName)
	{
	TFileName fileName;
	fileName.Copy(aFileName);
	RFs fs; 
	User::LeaveIfError(fs.Connect());
	CleanupClosePushL(fs);
	__ASSERT_DEBUG(!iCertificateContent, User::Panic(_L("CAddCertificate"), 1));
	TRAPD(err, iCertificateContent = Input::ReadFileL(fileName, fs));
	if (err != KErrNone)
		{
		iConsole.Printf(_L("Error reading file : "));
		iConsole.Printf(fileName);
		iConsole.Printf(_L("\n"));
		User::Leave(err);
		}
	CleanupStack::PopAndDestroy();	// fs
	}

void CAddCertificate::SetDeletable(const TDesC8& aDeletable)
	{
	iDeletableFlagPresent = ETrue;
	if (aDeletable.Compare(KTrue)==0)
		{
		iDeletable = ETrue;
		}
	else
		{
		iDeletable = EFalse;
		}
	}

void CAddCertificate::ConstructCertL(const TDesC8& aCert)
	{
	TFileName filename;
	filename.Copy(aCert);
	RFs fs; 
	User::LeaveIfError(fs.Connect());
	CleanupClosePushL(fs);
	HBufC8* certBuf = 0;
	TRAPD(err, certBuf = Input::ReadFileL(filename, fs));
	if (err != KErrNone)
		{
		iConsole.Printf(_L("Error reading file : "));
		iConsole.Printf(filename);
		iConsole.Printf(_L("\n"));
		User::Leave(err);
		}
	CleanupStack::PushL(certBuf);
	switch (iCertificateFormat)
		{
		case EX509Certificate:
			iCertificate = CX509Certificate::NewL(*certBuf);
			break;
			
		case EWTLSCertificate:
			iCertificate = CWTLSCertificate::NewL(*certBuf);
			break;
			
		default:
			// Unknown format - do nothing
			break;
		}
	CleanupStack::PopAndDestroy(2);
	}

void CAddCertificate::DoCheckResult(TInt aError)
	{
	
	if (iFinished)
		{
		if (iResult )
			{
			if (iExpectedResult == KErrNone )
				{
				iConsole.Write(_L("\tcertificate added successfully\n"));
				iOut.writeString(_L("\tcertificate added successfully"));
				}
			else	
				{
				iConsole.Write(_L("\tcertificate not added.\n"));
				iOut.writeString(_L("\tcertificate not added."));
				}
			iOut.writeNewLine();
			iOut.writeNewLine();
			}
		else if( !iResult)
			{
			if(iExpectedResult == KErrNone )
				{
				iConsole.Write(_L("\tcertificate not added\n"));
				iOut.writeString(_L("\tcertificate not added"));	
				}
			else
				{
				iConsole.Write(_L("\tcertificate should not be added\n"));
				iOut.writeString(_L("\tcertificate should not be added"));
				}
			iOut.writeNewLine();
			iOut.writeString(_L("\t"));
			iOut.writeError(aError);
			if (aError == KErrBadName)
				{
				iOut.writeString(_L(" Check that the label is unique"));
				}
			iOut.writeNewLine();
			iOut.writeNewLine();
			}
		}	
	}
//////////////////////////////////////////////////////////
//	Key import, from keystore for adding user certificates
//////////////////////////////////////////////////////////

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

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

CImportKey::~CImportKey()
{
	delete iLabel;
	delete iKeyData;
	if (iKeyInfo)
		{
		iKeyInfo->Release();
		}
	delete iUnifiedKeyStore;
	iFs.Close();
}

CImportKey::CImportKey(RFs& aFs, CConsoleBase& aConsole, Output& aOut)
:	CCertStoreTestAction(aFs, aConsole, aOut),
	iState(EInitialise)
{}


void CImportKey::ConstructL(const TTestActionSpec& aTestActionSpec)
{
	User::LeaveIfError(iFs.Connect());

	CCertStoreTestAction::ConstructL(aTestActionSpec);

	TInt err = KErrNone;
	TInt pos = 0;
	SetKeyDataFileL(Input::ParseElement(aTestActionSpec.iActionBody, KImportDataFile, KImportDataFileEnd, pos, err));
	for (;SetKeyUsage(Input::ParseElement(aTestActionSpec.iActionBody, KKeyUsageStart, KKeyUsageEnd, pos, err));)
		;
	
	SetKeyLabel(Input::ParseElement(aTestActionSpec.iActionBody, KKeyLabelStart, KKeyLabelEnd, pos, err));
	for (;SetKeyAccessType(Input::ParseElement(aTestActionSpec.iActionBody, KKeyAccessTypeStart, KKeyAccessTypeEnd, pos, err));)
		;
	
	SetKeyPassphrase(Input::ParseElement(aTestActionSpec.iActionBody, KKeyPassphraseStart, KKeyPassphraseEnd, pos, err));
		
	pos = 0;
	
	HBufC* result = HBufC::NewLC(aTestActionSpec.iActionResult.Length());
	TPtr(result->Des()).Copy(aTestActionSpec.iActionResult);
	Input::GetExpectedResultL(Input::ParseElement(*result, KReturnStart, KReturnEnd), iExpectedResult);
	CleanupStack::PopAndDestroy(result);
}

TBool CImportKey::SetKeyUsage(const TDesC8& aKeyUsage)
	{
	TBool ret = ETrue;
	if (aKeyUsage.Compare(KAllKeyUsages)==0)
		iUsage = EPKCS15UsageAll;
	else if (aKeyUsage.Compare(KAllKeyUsagesButNR)==0)
		iUsage |= (TKeyUsagePKCS15)(EPKCS15UsageSign) |
			(TKeyUsagePKCS15)(EPKCS15UsageSignRecover) |
			(TKeyUsagePKCS15)(EPKCS15UsageDecrypt);
	else if (aKeyUsage.Compare(KDSAUsage)==0)
		iUsage |= (TKeyUsagePKCS15)(EPKCS15UsageSign) |
			(TKeyUsagePKCS15)(EPKCS15UsageSignRecover);
	else if (aKeyUsage.Compare(KDerive)==0)
		iUsage |= EPKCS15UsageDerive;
	else if (aKeyUsage.Compare(KSign)==0)
		iUsage |= EPKCS15UsageSign;
	else if (aKeyUsage.Compare(KSignRecover)==0)
		iUsage |= EPKCS15UsageSignRecover;
	else if (aKeyUsage.Compare(KDecrypt)==0)
		iUsage |= EPKCS15UsageDecrypt;
	else if (aKeyUsage.Compare(KNR)==0)
		iUsage |= EPKCS15UsageNonRepudiation;
	else if (aKeyUsage.Compare(KEncipherAndSign)==0)
		iUsage |= (TKeyUsagePKCS15)(EPKCS15UsageSign) |
			(TKeyUsagePKCS15)(EPKCS15UsageSignRecover) |
			(TKeyUsagePKCS15)(EPKCS15UsageUnwrap);
	else
		ret = EFalse;
	return ret;
	}

void CImportKey::SetKeyLabel(const TDesC8& aKeyLabel)
{
	iLabel = HBufC::NewMax(aKeyLabel.Size());
	if (iLabel)
	{
		TPtr theLabel(iLabel->Des());
		theLabel.FillZ();
		theLabel.Copy(aKeyLabel);
	}
}


void CImportKey::SetKeyPassphrase(const TDesC8& aPassphrase)
	{
	// If the passphrase is empty, then use "clanger" by default.
	_LIT8(KDefaultPassphrase, "clanger");
	TPtrC8 phrase(KDefaultPassphrase());
	if (aPassphrase.Length())
		{
		phrase.Set(aPassphrase);
		}


	RFs fs;
	RFile file;
	fs.Connect();
	
	// Write the passphrase straight to the file.
	TDriveUnit sysDrive (fs.GetSystemDrive());
	TBuf<24> fileName (sysDrive.Name());
	fileName.Append(_L("\\password.txt"));
	
	file.Replace(fs, fileName, EFileWrite);
	file.Write(phrase);
	file.Close();
	fs.Close();
	}

void CImportKey::SetKeyDataFileL(const TDesC8& aDes)
{	
//	Now the filename itself
	TFileName fileName;
	fileName.FillZ();
	fileName.Copy(aDes);
	
	TDriveUnit sysDrive (RFs::GetSystemDrive());
	TBuf<64> buf(sysDrive.Name());
	buf.Append(_L("\\tcertstore\\data\\"));
	buf.Append(fileName);
		
	RFile file;
	TInt r = file.Open(iFs, buf, EFileRead);
	if ( (r==KErrNotFound) || (r==KErrPathNotFound) )
	{//	Not on c:, try z:
		buf[0] = 'z';
		r = file.Open(iFs, buf, EFileRead);
	}

	User::LeaveIfError(r);

	CleanupClosePushL(file);

	TInt fileSize = 0;
	User::LeaveIfError(file.Size(fileSize));

	if (fileSize > 0)
	{
		iKeyData = HBufC8::NewMaxL(fileSize);	
		TPtr8 data(iKeyData->Des());
		data.FillZ();
		User::LeaveIfError(file.Read(data, fileSize));
		CleanupStack::Pop(1);
	}

	file.Close();
}

TBool CImportKey::SetKeyAccessType(const TDesC8& aKeyAccessType)
	{
	TBool ret = ETrue;
	if (aKeyAccessType.Compare(KExtractable)==0)
		{
		iAccessType |= CCTKeyInfo::EExtractable;
		}
	else if (aKeyAccessType.Compare(KSensitive)==0)
		{
		iAccessType |= CCTKeyInfo::ESensitive;
		}
	else if (aKeyAccessType.Compare(KAlwaysSensitive)==0)
		{
		iAccessType |= CCTKeyInfo::EAlwaysSensitive;
		}
	else if (aKeyAccessType.Compare(KNeverExtractable)==0)
		{
		iAccessType |= CCTKeyInfo::ENeverExtractable;
		}
	else if (aKeyAccessType.Compare(KLocal)==0)
		{
		iAccessType |= CCTKeyInfo::ELocal;
		}
	else
		ret = EFalse;
	return ret;
	}

void CImportKey::PerformAction(TRequestStatus& aStatus)
{
	TDriveUnit sysDrive (RFs::GetSystemDrive());
	switch (iState)
	{
	case EInitialise:
		{
		if (iKeyInfo != NULL)
			{
			iKeyInfo->Release();
			iKeyInfo = NULL;
			}

		// Delete t_secdlg files - this will then always answer "clanger" for the passphrase
		    
		TInt result;
		TBuf<24> datFile(sysDrive.Name());
		datFile.Append(_L("\\t_secdlg_in.dat"));
		result = iFs.Delete(datFile);
		
		if (result != KErrNone && result != KErrNotFound)
			{
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, result);
			return;
			}
			
		datFile.Copy(sysDrive.Name());
		datFile.Append(_L("\\t_secdlg_out.dat"));
		result = iFs.Delete(datFile);
		
		if (result != KErrNone && result != KErrNotFound)
			{
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, result);
			return;
			}
		
		TRAP(result, iUnifiedKeyStore = CUnifiedKeyStore::NewL(iFs));
		if ( (result==KErrNone) && (iUnifiedKeyStore) )
			{
				iUnifiedKeyStore->Initialize(aStatus);
				iState = EImportKey;
			}
		else
			{
				aStatus = result;
				iState = EFinished;
			}
		}
		break;
	case EImportKey:
		{
			if (KErrNone==aStatus.Int())
			{
			//	Currently uses the first store, change to check the script for a specific store
				iUnifiedKeyStore->ImportKey(0, iKeyData->Des(), iUsage, *iLabel, iAccessType,
											TTime(0), TTime(0), iKeyInfo, aStatus);			
			}
			else
				{
				// Errors get passed to next state
				TRequestStatus* status = &aStatus;
				User::RequestComplete(status, aStatus.Int());
				}

			iState = EFinished;
		}
		
		break;
		
		case EFinished:
		{
			TRequestStatus* status = &aStatus;
			User::RequestComplete(status, aStatus.Int());
			if ( (aStatus == iExpectedResult) || (aStatus==KErrAlreadyExists) )
			{
				iResult = ETrue;
			}
			else
			{
				iResult = EFalse;
			}
			
			iFinished = ETrue;
		}
		break;
	}
}

void CImportKey::PerformCancel()
{//	To do when test harness cancel comes back.  Currently cancel testing
//	is performed in RunL with a set of flags and a separate active object
}

void CImportKey::Reset()
{}

void CImportKey::DoReportAction()
{
	_LIT(KImporting, "Importing key from keystore...");
	iOut.writeString(KImporting);
	TPtr theLabel(iLabel->Des());
	iOut.writeString(theLabel);
	iOut.writeNewLine();
}


void CImportKey::DoCheckResult(TInt aError)
{
	
	if (iFinished)
	{
		if (aError == KErrNone)
		{
			_LIT(KSuccessful, "Key imported successfully\n");
			iConsole.Write(KSuccessful);
			iOut.writeString(KSuccessful);
			iOut.writeNewLine();
			iOut.writeNewLine();
		}
		else
		{
			if ( (aError!=iExpectedResult) && (aError!=KErrAlreadyExists) )
			{
				_LIT(KFailed, "!!!Key import failure!!!\n");
				iConsole.Write(KFailed);
				iOut.writeString(KFailed);
			}
			
			iOut.writeNewLine();
			iOut.writeNewLine();
		}
	}
}
