/*
* Copyright (c) 2004-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: 
* sisregistry - exported client registry session interface implementation
*
*/


/**
 @file 
 @publishedPartner
 @released
*/

#include "log.h"
 
#include<s32mem.h>
#include <hash.h>
#include "sisregistryclientserver.h"
#include "sisregistryfiledescription.h"
#include "sisregistrysession.h"
#include "sisregistrypackage.h"
#include "arrayutils.h"
#include "hashcontainer.h"
#include "dessisdataprovider.h"
#include "siscontroller.h"
#include "cleanuputils.h"

using namespace Swi;

TInt StartSisRegistry()
	{
	const TUidType serverUid(KNullUid, KNullUid, KServerUid3);
	RProcess server;
	TInt err = server.Create(KSisRegistryImg, KNullDesC, serverUid);
	if (err != KErrNone)
		{
		return err;
		}
	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
	err = (server.ExitType() == EExitPanic) ? KErrGeneral : stat.Int();
	server.Close();
	return err;
	}

EXPORT_C TInt RSisRegistrySession::Connect()
//
// Connect to the server, attempting to start it if necessary
//
	{
	TInt retry = 2;
	for (;;) // loop forever, exit relies on internal count and unexpected errors happening
		{
		TInt err = CreateSession(KSisRegistryName, TVersion(1, 0, 0), 2);
		if (err != KErrNotFound && err != KErrServerTerminated)
			{
			return err;
			}
		if (--retry==0)
			{
			return err;
			}
		err = StartSisRegistry();
		if (err != KErrNone && err != KErrAlreadyExists)
			{
			return err;
			}
		}
	}

EXPORT_C void RSisRegistrySession::InstalledUidsL(RArray<TUid>& aUids)
	{
	HBufC8* buffer = SendReceiveBufferLC(EInstalledUids);
	
	// create a stream based on the buffer
	RDesReadStream stream(*buffer);
	CleanupClosePushL(stream);
	// reassemble the array from the stream
	InternalizeArrayL(aUids, stream);

	CleanupStack::PopAndDestroy(2, buffer);
	}

EXPORT_C void RSisRegistrySession::InstalledPackagesL(RPointerArray<CSisRegistryPackage>& aPackages)
	{
	HBufC8* buffer = SendReceiveBufferLC(EInstalledPackages);

	// create a stream based on the buffer
	RDesReadStream stream(*buffer);
	CleanupClosePushL(stream);
	// reassemble the array from the stream
	InternalizePointerArrayL(aPackages, stream);
 
 	CleanupStack::PopAndDestroy(2, buffer);
	}
 
EXPORT_C TBool RSisRegistrySession::IsInstalledL(TUid aUid)
	{
	TPckgC<TUid> uid(aUid);
	TBool installed = EFalse;
	TPckg<TBool> isInstalled(installed);
	User::LeaveIfError(SendReceive(EUidInstalled, TIpcArgs(&uid, &isInstalled)));
	return installed;
	}

EXPORT_C TBool RSisRegistrySession::IsInstalledL(TDesC8& aController)
	{
	TBool installed = EFalse;
	TPckg<TBool> isInstalled(installed);
	CDesDataProvider* desProvider = CDesDataProvider::NewLC(aController);
	Sis::CController* controller = Sis::CController::NewLC(*desProvider, Sis::EAssumeType);
  
 	CMessageDigest* msgDigest = controller->GenerateControllerHashLC(aController);
 	
	TPtrC8 hash = msgDigest->Final();
	CHashContainer* controllerHash = CHashContainer::NewLC(CMessageDigest::ESHA1, hash);
	
	CBufFlat* tempBuffer = CBufFlat::NewL(KDefaultBufferSize);
	CleanupStack::PushL(tempBuffer);
	
	RBufWriteStream stream(*tempBuffer);
	CleanupClosePushL(stream);
	controllerHash->ExternalizeL(stream);
	CleanupStack::PopAndDestroy(&stream);
	
	// Now, create an HBufC8 from the stream buf's length, and copy 
	// the stream buffer into this descriptor
	HBufC8* buffer = HBufC8::NewLC(tempBuffer->Size());
	TPtr8 ptr(buffer->Des());
	tempBuffer->Read(0, ptr, tempBuffer->Size());
	
	User::LeaveIfError(SendReceive(EControllerInstalled, TIpcArgs(&ptr, &isInstalled)));

	CleanupStack::PopAndDestroy(6, desProvider);
	return installed;
	}

EXPORT_C TBool RSisRegistrySession::ModifiableL(const TDesC& aFileName)
	{
	TBool modifiable = EFalse;
	TPckg<TBool> isModifiable(modifiable);
	
	User::LeaveIfError(SendReceive(EModifiable, TIpcArgs(&aFileName, &isModifiable)));
	return modifiable;
	}

EXPORT_C CHashContainer* RSisRegistrySession::HashL(const TDesC& aFileName)
	{
	HBufC8* buffer = SendReceiveBufferLC(EHash, aFileName);

	RDesReadStream stream(*buffer);
	CleanupClosePushL(stream);
	
	// reassemble the hash from the stream
    CHashContainer* hash = CHashContainer::NewLC(stream);
	CleanupStack::Pop(hash);
	
	CleanupStack::PopAndDestroy(2, buffer);
		
	return hash;
	}

EXPORT_C CSisRegistryPackage* RSisRegistrySession::SidToPackageL(TUid aSid)
	{
	TPckgC<TUid> sid(aSid);

	HBufC8* buffer = SendReceiveBufferLC(ESidToPackage, static_cast<TPtrC8>(sid));
	RDesReadStream stream(*buffer);
	CleanupClosePushL(stream);
	
	// reassemble the array from the stream
    CSisRegistryPackage* package = CSisRegistryPackage::NewLC(stream);
	CleanupStack::Pop(package);
	
	CleanupStack::PopAndDestroy(2, buffer);
	return package;
	}

EXPORT_C void RSisRegistrySession::SidToFileNameL(TUid aSid,TDes& aFileName)
	{
	TPckgC<TUid> sid(aSid);
	
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK		

	HBufC* buffer = HBufC::NewLC(KDefaultBufferSize);
	TPtr ptr(buffer->Des());
	
	User::LeaveIfError(SendReceive(ESidToFileName, TIpcArgs(&sid, &ptr, -1)));
	
	// Get the filename
	aFileName.Copy(*buffer);
	CleanupStack::PopAndDestroy(buffer);
#else
    // -1 is used to indicate no drive check.
	HBufC8* buffer = SendReceiveBufferLC(ESidToFileName, static_cast<TPtrC8>(sid), -1);
	RDesReadStream stream(*buffer);
	stream.PushL();

	// Reassemble the CSisRegistryFileDescription from the stream
	CSisRegistryFileDescription* fileDesc = CSisRegistryFileDescription::NewLC(stream);

	aFileName.Copy(fileDesc->Target());

	CleanupStack::PopAndDestroy(fileDesc);
	CleanupStack::PopAndDestroy(&stream);	
	CleanupStack::PopAndDestroy(buffer);	
 #endif
	}

EXPORT_C void RSisRegistrySession::SidToFileNameL(TUid aSid,TDes& aFileName, TDriveUnit aDrive)
	{
	TPckgC<TUid> sid(aSid);
	
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK		

	HBufC* buffer = HBufC::NewLC(KDefaultBufferSize);
	TPtr ptr(buffer->Des());
	
	User::LeaveIfError(SendReceive(ESidToFileName, TIpcArgs(&sid, &ptr, aDrive)));
	
	// Get the filename
	aFileName.Copy(*buffer);
	CleanupStack::PopAndDestroy(buffer);
#else	
	HBufC8* buffer = SendReceiveBufferLC(ESidToFileName, static_cast<TPtrC8>(sid), aDrive);

	RDesReadStream stream(*buffer);
	stream.PushL();

	// Reassemble the CSisRegistryFileDescription from the stream
	CSisRegistryFileDescription* fileDesc = CSisRegistryFileDescription::NewLC(stream);

	// Get the filename
	aFileName.Copy(fileDesc->Target());

	CleanupStack::PopAndDestroy(fileDesc);
	CleanupStack::PopAndDestroy(&stream);	
	CleanupStack::PopAndDestroy(buffer);	
#endif
	}


EXPORT_C TBool RSisRegistrySession::IsSidPresentL(TUid aSid)
/**
	Queries whether the supplied SID is associated with
	any package on the device.
	
	@param	aSid			The executable's SID.
	@return					Whether that SID is present in any registered
							package.
 */
	{
	TPckgC<TUid> sid(aSid);
	TPckgBuf<TBool> present;
	
	TInt r = SendReceive(EIsSidPresent, TIpcArgs(&sid, &present));
	User::LeaveIfError(r);
	
	return present();
	}
	
EXPORT_C void RSisRegistrySession::RemovablePackagesL(RPointerArray<CSisRegistryPackage>& aPackages)
	{
	HBufC8* buffer = SendReceiveBufferLC(ERemovablePackages);
	
	// create a stream based on the buffer
	RDesReadStream stream(*buffer);
	CleanupClosePushL(stream);
	// reassemble the array from the stream
	InternalizePointerArrayL(aPackages, stream);
	CleanupStack::PopAndDestroy(2, buffer);
	}
	
EXPORT_C void RSisRegistrySession::RecoverL()
	{
	User::LeaveIfError(SendReceive(EInitRecovery));	
	}

HBufC8* RSisRegistrySession::SendReceiveBufferLC(TInt aMessage) 
	{
	HBufC8* output = HBufC8::NewLC(KDefaultBufferSize);
	
	TPtr8 pOutput(output->Des());
	
	TInt result = SendReceive(aMessage, TIpcArgs(&pOutput));
	
	if (result == KErrOverflow)
		{
		TInt sizeNeeded;
		TPckg<TInt> sizeNeededPackage(sizeNeeded);
		sizeNeededPackage.Copy(*output);
		
		// Re-allocate buffer after reclaiming memory
		CleanupStack::PopAndDestroy(output);
		output = HBufC8::NewLC(sizeNeeded);

		TPtr8 pResizedOutput(output->Des());
		
		result=SendReceive(aMessage, TIpcArgs(&pResizedOutput));
		}
	User::LeaveIfError(result);
	return output;
	}
	
HBufC8* RSisRegistrySession::SendReceiveBufferLC(TInt aMessage, const TDesC& aInputDescriptor) 
	{
	HBufC8* output = HBufC8::NewLC(KDefaultBufferSize);
	
	TPtr8 pOutput(output->Des());
	
	TInt result = SendReceive(aMessage, TIpcArgs(&aInputDescriptor, &pOutput));
	
	if (result == KErrOverflow)
		{
		TInt sizeNeeded = 0;
		TPckg<TInt> sizeNeededPackage(sizeNeeded);
		sizeNeededPackage.Copy(*output);
		
		// Re-allocate buffer
		CleanupStack::PopAndDestroy(output);
		output = HBufC8::NewLC(sizeNeeded);

		TPtr8 pResizedOutput(output->Des());
		
		result=SendReceive(aMessage, TIpcArgs(&aInputDescriptor, &pResizedOutput));
		}
	User::LeaveIfError(result);
	return output;
	}
	
HBufC8* RSisRegistrySession::SendReceiveBufferLC(TInt aMessage, TPtrC8 aInputBuffer) 
	{
	HBufC8* output = HBufC8::NewLC(KDefaultBufferSize);
	
	TPtr8 pOutput(output->Des());
	
	TInt result=SendReceive(aMessage, TIpcArgs(&aInputBuffer, &pOutput));
	
	if (result == KErrOverflow)
		{
		TInt sizeNeeded;
 		TPckg<TInt> sizeNeededPackage(sizeNeeded);
		sizeNeededPackage.Copy(*output);
		
		// Re-allocate buffer
		CleanupStack::PopAndDestroy(output);
		output = HBufC8::NewLC(sizeNeeded);

		TPtr8 pResizedOutput(output->Des());
		
		result=SendReceive(aMessage, TIpcArgs(&aInputBuffer, &pResizedOutput));
		}
	User::LeaveIfError(result);
	return output;
	}
	
EXPORT_C void RSisRegistrySession::RetrieveLogFileL(RPointerArray<CLogEntry>& aLogEntry)
 	{
    CleanupResetAndDestroyPushL(aLogEntry); 	
 	HBufC8* buffer = SendReceiveBufferLC(EloggingFile);
  
 	RDesReadStream stream(*buffer);
 	CleanupClosePushL(stream);
 	TInt err;
 	if(buffer->Length() != 0)
 		{
 		do
	 		{
	 		CLogEntry* log = NULL;
	 		TRAP(err,log = CLogEntry::NewL(stream));
	 		if(err ==KErrEof)
	 			{
				// coverity[memory_leak]
	 			break;
	 			}
	 		else if(err != KErrNone)
				{
				User::Leave(err);	
				}
	 		CleanupStack::PushL(log);
	 		aLogEntry.AppendL(log);
	 		CleanupStack::Pop(log);
	 		}
	 	while(err == KErrNone);
 		}
 	 	
 	CleanupStack::PopAndDestroy(2,buffer);	//buffer,stream
 	CleanupStack::Pop(&aLogEntry);
 	}
 
HBufC8* RSisRegistrySession::SendReceiveBufferLC(TInt aMessage, TPtrC8 aInputBuffer, TInt aThirdArgument) 
	{
	HBufC8* output = HBufC8::NewLC(KDefaultBufferSize);

	TPtr8 pOutput(output->Des());

	TInt result=SendReceive(aMessage, TIpcArgs(&aInputBuffer, &pOutput, aThirdArgument));

	if (result == KErrOverflow)
		{
		TInt sizeNeeded;
		TPckg<TInt> sizeNeededPackage(sizeNeeded);
		sizeNeededPackage.Copy(*output);

		// Re-allocate buffer
		CleanupStack::PopAndDestroy(output);
		output = HBufC8::NewLC(sizeNeeded);

		TPtr8 pResizedOutput(output->Des());

		result=SendReceive(aMessage, TIpcArgs(&aInputBuffer, &pResizedOutput, aThirdArgument));
		}
	User::LeaveIfError(result);
	return output;
	}

#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
EXPORT_C TBool RSisRegistrySession::IsFileRegisteredL(const TDesC& aFileName)
	{
	TBool registered = EFalse;
	TPckg<TBool> registeredPckg(registered);
	
	User::LeaveIfError(SendReceive(EIsFileRegistered, TIpcArgs(&aFileName, &registeredPckg)));
	return registered;	
	}
	
EXPORT_C Usif::TComponentId RSisRegistrySession::GetComponentIdForUidL(const TUid& aUid)
	{
	Usif::TComponentId componentId(0);
	TPckg<Usif::TComponentId> componentIdPckg(componentId);
	
	User::LeaveIfError(SendReceive(EComponentIdForUid, TIpcArgs(aUid.iUid, &componentIdPckg)));
	return componentId;		
	}

#endif //SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK

