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


/**
 @file
*/

#include <e32std.h>
#include <e32debug.h>

#include "randomimpl.h"
#include "pluginentry.h"
#include "pluginconfig.h"

#include "randsvr.h"
#include "randcliserv.h"
#include "randsvrimpl.h"

_LIT(KRandomServerImg,"z:\\sys\\bin\\randsvr.exe");		// DLL/EXE name
_LIT(KRandomServerConnect, "Randsvr connect");
_LIT(KRandomServerGet, "Randsvr get");

const TUid KServerUid3={0x100066dc};


using namespace SoftwareCrypto;


CRandomImpl* CRandomImpl::NewL(TUid aImplementationUid)
	{
	CRandomImpl* self = new(ELeave)CRandomImpl(aImplementationUid);
	return self;
	}

CRandomImpl* CRandomImpl::NewLC(TUid aImplementationUid)
	{
	CRandomImpl* self = NewL(aImplementationUid);
	CleanupStack::PushL(self);
	return self;
	}

void CRandomImpl::GenerateRandomBytesL(TDes8& aDest)
	{
	TRandomImpl::Random(aDest);
	}

CRandomImpl::CRandomImpl(TUid aImplementationUid) : iImplementationUid(aImplementationUid)
	{
	}

void TRandomImpl::Random(TDes8& aDestination)
	{
	RRandomSessionImpl rs;
	TRAPD(ret,rs.ConnectL());
	if (ret != KErrNone)
		{
		User::Panic(KRandomServerConnect, ret);
		}
	TInt err=rs.GetRandom(aDestination);
	if (err != KErrNone)
		{
		User::Panic(KRandomServerGet, err);
		}
	rs.Close();
	}

void CRandomImpl::GetCharacteristicsL(const TCharacteristics*& aPluginCharacteristics)
	{
	TInt randomNum = sizeof(KRandomCharacteristics)/sizeof(TRandomCharacteristics*);
	for (TInt i = 0; i < randomNum; i++)
		{
		if (KRandomCharacteristics[i]->cmn.iImplementationUID == ImplementationUid().iUid)
			{
			aPluginCharacteristics = KRandomCharacteristics[i];
			break;
			}
		}
	}
	
TAny* CRandomImpl::GetExtension(TUid /*aExtensionId*/)
	{
	return NULL;
	}
	
CExtendedCharacteristics* CRandomImpl::CreateExtendedCharacteristicsL()
	{
	// All Symbian software plug-ins have unlimited concurrency, cannot be reserved
	// for exclusive use and are not CERTIFIED to be standards compliant.
	return CExtendedCharacteristics::NewL(KMaxTInt, EFalse);
	}

const CExtendedCharacteristics* CRandomImpl::GetExtendedCharacteristicsL()
	{
	return CRandomImpl::CreateExtendedCharacteristicsL();
	}

TUid CRandomImpl::ImplementationUid() const
	{
	return iImplementationUid;
	}

CRandomImpl::~CRandomImpl()
	{
	}

void CRandomImpl::Close()
	{
	delete this;
	}

// All crypto plugins must implement this, to reset
// hardware if required. Do nothing in this version
void CRandomImpl::Reset()
	{
	}

RRandomSessionImpl::RRandomSessionImpl(void)
	{
	}

static TInt StartServer()
// Borrowed from AndrewT's server startup code.
// Start the server process/thread which lives in an EPOCEXE object
//
	{
	
	const TUidType serverUid(KNullUid,KNullUid,KServerUid3);

	//
	// EPOC and EKA2 is easy, we just create a new server process. Simultaneous
	// launching of two such processes should be detected when the second one
	// attempts to create the server object, failing with KErrAlreadyExists.
	//
	RProcess server;
	TInt r=server.Create(KRandomServerImg, KNullDesC, serverUid);

	if (r!=KErrNone)
		return r;
	TRequestStatus stat;
	server.Rendezvous(stat);
	if (stat!=KRequestPending)
		server.Kill(0);		// abort startup
	else
		server.Resume();	// logon OK - start the server
	User::WaitForRequest(stat);		// wait for start or death
	// we can't use the 'exit reason' if the server panicked as this
	// is the panic 'reason' and may be '0' which cannot be distinguished
	// from KErrNone
	r=(server.ExitType()==EExitPanic) ? KErrGeneral : stat.Int();
	server.Close();
	return r;

	}

void RRandomSessionImpl::ConnectL(void)
	{
	TInt retry=2;
	for (;;)
		{
		// Magic number 1 below is the number of asynchronous message slots
		TInt r = CreateSession(KRandomServerName,TVersion(0,0,0), 1);
		if (r == KErrNone)
			   User::Leave(r);   // Connected okay
		if (r != KErrNotFound && r != KErrServerTerminated)
			   User::Leave(r);   // Something else happened
		if (--retry == 0)
			User::Leave(r);      // Give up after a while
		r = StartServer();       // Try starting again
		if (r != KErrNone && r != KErrAlreadyExists)
			User::Leave(r);
		}
	}

TInt RRandomSessionImpl::GetRandom(TDes8& aDestination)
	{
	TInt desclength = aDestination.Length();
	for ( TInt i = 0; i < desclength; i += KRandomBlockSize)
		{
		TInt getlen = Min(KRandomBlockSize, desclength - i);
		TPtr8 buffer(&aDestination[i], KRandomBlockSize, KRandomBlockSize);
		TInt err = SendReceive(CRandomSession::KRandomRequest, TIpcArgs(&buffer, getlen));
		if (err != KErrNone)
			{
			return err;
			}
		}
	return KErrNone;
	}
