// APSSES.CPP
//
// Copyright (c) 1997-1999 Symbian Ltd.  All rights reserved.
//

//
// AppArc server session
//

#include <e32svr.h>
#include <apacmdln.h>
#include <apaflrec.h>
#include <apsserv.h>
#include "apsses.h"
#include "apsstd.h"
#include "apsclsv.h"
#include <apgaplst.h>
#include <apgicnfl.h>
#include <apmrec.h>
#include <apmstd.h>
#include <apmfndr.h>
#include <datastor.h>
#include <s32mem.h>

const TInt KApaAppListServMaxBuffer=128;

CApaAppListServSession* CApaAppListServSession::NewL(RThread& aClient,CApaAppListServer* aServer)
	{
	CApaAppListServSession* self=new(ELeave) CApaAppListServSession(aClient,aServer,aServer->FsSession());
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(); // self
	return self;
	}

CApaAppListServSession::CApaAppListServSession(RThread& aClient,CApaAppListServer* aServer, RFs& aFs)
	: CSession(aClient),
	iServ(aServer),
	iFs(aFs),
	iMaxBufSize(KApaAppListServMaxBuffer)
	{ __DECLARE_NAME(_S("CApaAppListServSession")); }

void CApaAppListServSession::ConstructL()
	{
	}

CApaAppListServSession::~CApaAppListServSession()
	{
	delete iBuffer;
	}

void CApaAppListServSession::ServiceL(const RMessage& aMessage)
	{
	TRAPD(err,DispatchMessageL(aMessage));
	aMessage.Complete(err);
	}

void CApaAppListServSession::DispatchMessageL(const RMessage &aMessage)
// service a client request; test the opcode and then do appropriate servicing
	{
	switch (aMessage.Function())
        {
	case EAppListServInitFullList:
		InitList(EListAllApps);
		return;
	case EAppListServInitEmbedList:
		InitList(EListEmbeddedApps);
		return;
	case EAppListServGetNextApp:
		GetNextAppL();
		return;
	case EAppListServEmbedCount:
		EmbedCount();
		return;
	case EAppListServAppCount:
		AppCount();
		return;
	case EAppListServGetAppInfo:
		GetAppInfoL();
		return;
	case EAppListServGetAppCapability:
		GetAppCapabilityL();
		return;
	case EAppListServStartApp:
		StartAppL();
		return;
	case EAppListServRecognizeData:
		RecognizeDataL();
		return;
	case EAppListServRecognizeSpecificData:
		RecognizeSpecificDataL();
		return;
	case EAppListServAppForDataType:
		AppForDataTypeL();
		return;
	case EAppListServStartDocument:
		StartDocumentL(EStart);
		return;
	case EAppListServStartDocumentByDataType:
		StartDocumentL(EStartByDataType);
		return;
	case EAppListServStartDocumentByUid:
		StartDocumentL(EStartByUid);
		return;
	case EAppListServCreateDocumentByUid:
		StartDocumentL(ECreateByUid);
		return;
	case EAppListServAppIconByUid:
		IconForAppL();
		return;
	case EAppListServAppForDocument:
		AppForDocumentL();
		return;
	case EAppListServIsProgram:
		IsProgramL();
		return;
	case EAppListServGetConfidence:
		GetConfidence();
		return;
	case EAppListServSetConfidence:
		SetConfidence();
		return;
	case EAppListServGetBufSize:
		GetBufSize();
		return;
	case EAppListServSetBufSize:
		SetBufSize();
		return;
	case EAppListServGetDataTypesPhase1:
		GetDataTypesCountL();
		return;
	case EAppListServGetDataTypesPhase2:
		GetDataTypesL();
		return;
	default:
		PanicClient(EClientBadRequest); // panic the caller if we dont understand the request
		return;
        }
	}

void CApaAppListServSession::AppForDocumentL()
// TInt AppForDocument(const TDesC& aFileName, TUid& aUid, TDataType& aDataType);
	{
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC* filename=HBufC::NewLC(desLen);
	TPtr fileDes=filename->Des();
	TRAPD(err,Message().ReadL(pD,fileDes));
	if (err!=KErrNone)
		PanicClient(EClientBadDescriptor);
	//
#if defined(__PROFILE)
	TProfile profile;
	RDebug::ProfileReset(5,1);
	RDebug::ProfileStart(5);
#endif
	TDataType dataType=RecognizeFileL(*filename);
	TUid uid;
	if (dataType!=TDataType())
		uid=AppForDataTypeL(dataType);
	else
		uid.iUid=0;
	if (uid.iUid==0)
		// not had any luck, use the old scheme
		{
		CApaFileRecognizerType* type=NULL;
		TRAPD(err,type=FileRecognizer()->RecognizeFileL(*filename));
		if (!err&&type)
			uid=type->AppUid();
		}
#if defined(__PROFILE)
	RDebug::ProfileEnd(5);
	RDebug::ProfileResult(&profile,5,1);
	RDebug::Print(_L("AppForDocumentL - %d.%06d seconds"),profile.iTime/1000000,profile.iTime%1000000);
	RDebug::ProfileStart(5);
#endif
	CleanupStack::PopAndDestroy(); // filename
	TPckgBuf<TUid> uidPk(uid);
	Write(Message().Ptr1(),uidPk);
	TPckgBuf<TDataType> dataPk(dataType);
	Write(Message().Ptr2(),dataPk);
	}

void CApaAppListServSession::IsProgramL()
//  TInt IsProgram(const TDesC& aFileName, TBool& aProgram);
// Shell optimisation
	{
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC* filename=HBufC::NewLC(desLen);
	TPtr fileDes=filename->Des();
	TRAPD(err,Message().ReadL(pD,fileDes));
	if (err!=KErrNone)
		PanicClient(EClientBadDescriptor);
	CApaFileRecognizerType* type=NULL;
	TRAP(err,type=FileRecognizer()->RecognizeFileL(*filename)); // ignore the error
	CleanupStack::PopAndDestroy(); // filename
	TBool isProgram=EFalse;
	if (type)
		isProgram=(type->Type()==CApaFileRecognizerType::EProgram);
	TPckgBuf<TBool> progPk(isProgram);
	Write(Message().Ptr1(),progPk);
	}

void CApaAppListServSession::GetConfidence()
// void GetAcceptedConfidence(TInt& aConfidence);
	{
	TInt confidence=iServ->DataRecognizer()->AcceptedConfidence();
	TPckgBuf<TInt> confPk(confidence);
	Write(Message().Ptr0(),confPk);
	}

void CApaAppListServSession::SetConfidence()
// SetAcceptedConfidence(TInt aConfidence);
	{
	iServ->DataRecognizer()->SetAcceptedConfidence(Message().Int0());
	}

void CApaAppListServSession::GetBufSize()
// GetMaxDataBufSize(TInt& aBufSize);
	{
	TInt buf=iMaxBufSize;
	TPckgBuf<TInt> bufPk(buf);
	Write(Message().Ptr0(),bufPk);
	}

void CApaAppListServSession::SetBufSize()
// SetMaxDataBufSize(TInt aBufSize);
	{
	iMaxBufSize=Message().Int0();
	}

void CApaAppListServSession::GetDataTypesCountL()
	{
	__ASSERT_DEBUG(!iBuffer,User::Invariant());
	CDataTypeArray* dataTypes=new(ELeave) CDataTypeArray(5);
	CleanupStack::PushL(dataTypes);
	iServ->DataRecognizer()->DataTypeL(*dataTypes);
	iBuffer=CBufFlat::NewL(sizeof(TDataType));
	RBufWriteStream writeStream(*iBuffer);
	writeStream << *dataTypes;
	CleanupStack::PopAndDestroy(); // dataTypes
	TPtr8 ptr=iBuffer->Ptr(0);
	TPckgBuf<TInt> countPk(ptr.Size());
	Write(Message().Ptr0(),countPk);
	}

void CApaAppListServSession::GetDataTypesL()
// GetSupportedDataTypes(CDataTypeArray& aDataTypes);
	{
	__ASSERT_DEBUG(iBuffer,User::Invariant());
	Write(Message().Ptr0(),iBuffer->Ptr(0));
	delete iBuffer;
	iBuffer=NULL;
	}

void CApaAppListServSession::IconForAppL()
// from GetAppIcon(TUid aAppUid, TInt aSideInPixels, CApaMaskedBitmap& aAppBitmap);
// BUT!  It's interface is uid, side, icon handle, mask handle for bitmap sharing 
// and avoiding IPC overhead
	{
	const TUid uid={Message().Int0()};
	const TInt side=Message().Int1();
	TApaAppEntry entry;
	CApaAppData* app=NULL;
	CApaMaskedBitmap* icon=NULL;
	if (!FindAppInList(app,entry,uid))
		User::Leave(KErrNotFound);
	else
		icon=app->Icon(side);
	if (!icon)
		User::Leave(KErrNotFound);
	TPckgBuf<TInt> iconPk(icon->Handle());
	Write(Message().Ptr2(),iconPk);
	TPckgBuf<TInt> maskPk(icon->Mask()->Handle());
	Write(Message().Ptr3(),maskPk);
	}

void CApaAppListServSession::AppForDataTypeL()
// from AppForDataType(const TDataType& aDataType, TUid& aAppUid);
	{
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC8* data=HBufC8::NewLC(desLen);
	TPtr8 dataDes=data->Des();
	TRAPD(err,Message().ReadL(pD,dataDes));
	if (err!=KErrNone)
		PanicClient(EClientBadDescriptor);
	TDataType dataType(dataDes);
	CleanupStack::PopAndDestroy(); // dataDes
	TUid result=AppForDataTypeL(dataType);
	TPckgBuf<TUid> uidPk(result);
	Write(Message().Ptr1(),uidPk);
	}

TUid CApaAppListServSession::AppForDataTypeL(const TDataType& aDataType)
// look at the user mappings, if they that app isn't there go for the preferred
// default
	{
	TUid uid;
	iServ->TypeStoreManager()->GetAppByDataType(aDataType,uid);
	TApaAppEntry entry;
	CApaAppData* app=NULL;
	if (uid.iUid==0 || !FindAppInList(app,entry,uid))
		uid=AppList()->PreferredDataHandlerL(aDataType);
	if (uid.iUid==0 && aDataType.IsNative())
		return aDataType.Uid();
	return uid;
	}

TDataType CApaAppListServSession::RecognizeFileL(const TDesC& aFileName)
// find, open, then recognize the file
	{
	TInt preferredSize=iServ->DataRecognizer()->PreferredBufSize();
	TInt bufSize=Min(preferredSize,iMaxBufSize);
	HBufC8* buf=HBufC8::NewLC(bufSize);
	TPtr8 des=buf->Des();
	TInt err=iFs.ReadFileSection(aFileName,0,des,bufSize);
	if (err!=KErrNone)
		des.SetLength(0);
	TDataType dataType=iServ->DataRecognizer()->RecognizeL(aFileName, *buf).iDataType;
	CleanupStack::PopAndDestroy(); // buf
	return dataType;
	}

void CApaAppListServSession::StartDocumentL(TAppListDocumentStart aStartType)
// from StartDocument(const TDesC& aFileName, TThreadId& aId, TLaunchType aLaunchType);
// from StartDocument(const TDesC& aFileName, const TDataType& aDataType, TThreadId& aId, TLaunchType aLaunchType);
// from StartDocument(const TDesC& aFileName, TUid aAppUid, TThreadId& aId, TLaunchType aLaunchType);
// from CreateDocument(const TDesC& aFileName, TUid aAppUid, TThreadId& aId, TLaunchType aLaunchType);
// This method needs to open the file, mime type it then launch it.
	{
	// !! TODO launch type
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC* fileName=HBufC::NewLC(desLen);
	TPtr nameBufPtr = fileName->Des();
	TRAPD(err,Message().ReadL(pD,nameBufPtr));
	__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
	TDataType* dataType=new(ELeave) TDataType();
	CleanupStack::PushL(dataType);
	if (aStartType==EStart)
		*dataType=RecognizeFileL(*fileName);
	TUid uid;
	if (aStartType==EStartByDataType)
		{
		const TAny* p1=Message().Ptr1();
		desLen=Message().Client().GetDesLength(p1);
		HBufC8* dataDes=HBufC8::NewLC(desLen);
		TPtr8 dataDesPtr=dataDes->Des();
		TRAPD(err,Message().ReadL(p1,dataDesPtr));
		__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
		*dataType=TDataType(*dataDes);
		CleanupStack::PopAndDestroy(); // dataDes
		}
	if (aStartType==EStart || aStartType==EStartByDataType)
		uid=AppForDataTypeL(*dataType);
	else
		{
		__ASSERT_DEBUG(aStartType==EStartByUid || aStartType==ECreateByUid,User::Invariant());
		uid.iUid=Message().Int1();
		}
	CleanupStack::PopAndDestroy(); // dataType
	TThreadId id=StartDocumentL(*fileName,uid,(aStartType==ECreateByUid) ? EApaCommandCreate : EApaCommandOpen);
	CleanupStack::PopAndDestroy(); // filename
	// write the id to the client
	TPckgBuf<TThreadId> idPk(id);
	if (aStartType==EStart)
		Write(Message().Ptr1(),idPk);
	else
		Write(Message().Ptr2(),idPk);
	}

TThreadId CApaAppListServSession::StartDocumentL(const TDesC& aFileName, TUid aUid, TApaCommand aCommand)
// Launch the document of aFileName with the app with Uid aUid
	{
	CApaAppData* app=NULL;
	CApaFileRecognizerType* type=NULL;
	if (aUid.iUid==0)
		{
		// if we can't bind the type from the Mime type stuff then use the old scheme
		TRAPD(err,type=FileRecognizer()->RecognizeFileL(aFileName));
		}
	else
		{
		TApaAppEntry entry;
		if (!FindAppInList(app,entry,aUid))
			User::Leave(KErrNotFound); // we have already checked for defaults
		else
			{
			TRAPD(err,type=FileRecognizer()->RecognizeFileL(entry.iFullName));
			}
		}
	if (!type)
		User::Leave(KErrNotFound);
	else
		{
		if (!aFileName.Length())
			return type->RunL(EApaCommandRun,NULL,NULL);
		else
			return type->RunL(aCommand,&aFileName,NULL);
		}
	return TThreadId(); // to keep the compiler happy
	}

void CApaAppListServSession::DoRecognizeUnpackLC(HBufC*& aName, HBufC8*& aBuffer)
	{
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	aName=HBufC::NewLC(desLen);
	TPtr nameBufPtr = aName->Des();
	TRAPD(err,Message().ReadL(pD,nameBufPtr));
	__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
	const TAny* pD2=Message().Ptr1();
	desLen=Message().Client().GetDesLength(pD2);
	aBuffer=HBufC8::NewLC(desLen);
	TPtr8 bufBufPtr=aBuffer->Des();
	TRAP(err,Message().ReadL(pD2,bufBufPtr));
	__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
	}

void CApaAppListServSession::RecognizeDataL()
// Recognize the data type of an object
	{
	HBufC* name=NULL;
	HBufC8* buffer=NULL;
	DoRecognizeUnpackLC(name,buffer);
	//
	TDataRecognitionResult result=iServ->DataRecognizer()->RecognizeL(*name,*buffer);
	//
	CleanupStack::PopAndDestroy(2); // name & buffer
	// write the result
	TPckgBuf<TDataRecognitionResult> resultPk(result);
	Write(Message().Ptr2(),resultPk);
	}

void CApaAppListServSession::RecognizeSpecificDataL()
// Determine whether an object is of a specific data type
	{
	HBufC* name=NULL;
	HBufC8* buffer=NULL;
	DoRecognizeUnpackLC(name,buffer);
	const TAny* pD=Message().Ptr2();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC8* dataType=HBufC8::NewLC(desLen);
	TPtr8 dataTypePtr = dataType->Des();
	TRAPD(err,Message().ReadL(pD,dataTypePtr));
	__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
	TDataType data(*dataType);
	CleanupStack::PopAndDestroy(); // dataType
	//
	TBool result=iServ->DataRecognizer()->RecognizeL(*name,*buffer,data);
	//
	CleanupStack::PopAndDestroy(2); // name & buffer
	// write the result
	TPckgBuf<TBool> boolPk(result);
	Write(Message().Ptr3(),boolPk);
	}

void CApaAppListServSession::InitList(TAppListType aType)
// carries out initialisation prior to starting to pass a new list across
	{
	CApaAppList* list=AppList();
	iApp = list->FirstApp();
	iListCount = list->UpdateCounter();
	iListType = aType;
	}


void CApaAppListServSession::EmbedCount()
// writes back the number of embedded apps in the list
	{
	TInt count=0;
	CApaAppList* list=AppList();
	CApaAppData* app = list->FirstApp();
	while (app)
		{
		if (IsEmbeddable(app))
			count++;
		app = list->NextApp(app);
		}
	TPckgBuf<TInt> countPk(count);
	Write(Message().Ptr0(),countPk);
	}


void CApaAppListServSession::AppCount()
// writes back the number of apps in the list
	{
	TInt count=AppList()->Count();
	TPckgBuf<TInt> countPk(count);
	Write(Message().Ptr0(),countPk);
	}


void CApaAppListServSession::GetNextAppL()
	{
	CApaAppList* list=AppList();
	// see if the list's changed
	TInt count=list->UpdateCounter();
	if (count!=iListCount)
		User::Leave(KErrCorrupt);
	//
	// skip non-embeddable apps if apropriate
	if (iListType==EListEmbeddedApps)
		{
		while (iApp && !IsEmbeddable(iApp))
			iApp = list->NextApp(iApp);
		}
	//
	// return the app info or an error code
	TApaAppInfo info;
	if (iApp)
		{
		// we've found an embeddable one - get it's details
		TApaAppEntry entry = iApp->AppEntry();
		info.iUid = entry.iUidType[2];
		info.iFullName = entry.iFullName;
		info.iCaption = iApp->Caption();
		//
		// now increment the pointer for next time
		iApp = list->NextApp(iApp);
		//
		// and write back our findings
		TPckgBuf<TApaAppInfo> infoPk(info);
		Write(Message().Ptr0(),infoPk);
		}
	else
		User::Leave(KErrNotFound);
	}

TBool CApaAppListServSession::FindAppInList(CApaAppData*& aApp,TApaAppEntry& aEntry,TUid aAppUid)
// locate app in list, return EFalse if it isn't present
	{
	CApaAppList* list=AppList();
	aApp = list->FirstApp();
	TBool found=EFalse;
	while (aApp)
		{
		aEntry = aApp->AppEntry();
		if (aEntry.iUidType[2]==aAppUid)
			{
			found = ETrue;
			break;
			}
		aApp = list->NextApp(aApp);
		}
	return found;
	}


void CApaAppListServSession::GetAppInfoL()
	{
	// get UID of required app
	const TUid uid={Message().Int1()};
	//
	// locate app in list, write back info or error code
	CApaAppData* app=NULL;
	TApaAppEntry entry;
	if (FindAppInList(app,entry,uid))
		{
		TApaAppInfo info;
		info.iUid = entry.iUidType[2];
		info.iFullName = entry.iFullName;
		info.iCaption = app->Caption();
		TPckgBuf<TApaAppInfo> infoPk(info);
		Write(Message().Ptr0(),infoPk);
		}
	else
		User::Leave(KErrNotFound);
	}


void CApaAppListServSession::GetAppCapabilityL()
	{
	// get UID of required app
	const TUid uid={Message().Int1()};
	//
	// locate app in list, write back capability or error code
	CApaAppData* app=NULL;
	TApaAppEntry entry;
	if (FindAppInList(app,entry,uid))
		{
		// get the length of the target descriptor
		const TAny* target=Message().Ptr0();
		TInt targetLen=Message().Client().GetDesMaxLength(target);
		//
		// create a buf of the correct length and get the capability
		HBufC8* buf=HBufC8::NewLC(targetLen);
		TPtr8 ptr=buf->Des();
		app->Capability(ptr);
		ptr.SetLength(targetLen);
		//
		// write the capability back
		Write(target,*buf);
		CleanupStack::PopAndDestroy(); // buf
		}
	else
		User::Leave(KErrNotFound);
	}


void CApaAppListServSession::StartAppL()
	{
	// read the TPckgBuf<TPtrC> from the client
	const TAny* pD=Message().Ptr0();
	TInt desLen=Message().Client().GetDesLength(pD);
	HBufC* cmdBuf=HBufC::NewLC(desLen);
	TPtr cmdBufPtr = cmdBuf->Des();
	TRAPD(err,Message().ReadL(pD,cmdBufPtr));
	__ASSERT_ALWAYS(err==KErrNone,PanicClient(EClientBadDescriptor));
	//
	// create a commandline from the buffer
	CleanupStack::Pop(); // cmdBuf
	CApaCommandLine* cmdLine=CApaCommandLine::New(cmdBuf); // takes ownership
	CleanupStack::PushL(cmdLine);
	//
	// execute the command line
	CApaFileRecognizerType* type=NULL;
	TRAP(err,type=FileRecognizer()->RecognizeFileL(cmdLine->LibraryName()));
	if (type)
		{
		TPtrC fileName=cmdLine->DocumentName();
		TPtrC8 tailEnd=cmdLine->TailEnd();
		type->RunL(cmdLine->Command(),(fileName.Length()?&fileName:NULL),(tailEnd.Length()?&tailEnd:NULL)); // pass in null for components that are not present
		}
	else
		User::Leave(KErrNotFound);
	CleanupStack::PopAndDestroy(); // cmdLine
	}


TBool CApaAppListServSession::IsEmbeddable(CApaAppData* aApp)
// returns ETrue if aApp is embeddable
	{
	__ASSERT_DEBUG(aApp,PanicServer(EDSvrNoApp));
	//
	TApaAppCapabilityBuf buf;
	aApp->Capability(buf);
	return buf().iEmbeddability;
	}

void CApaAppListServSession::PanicClient(TApaClientPanic aPanic) const
// panic the client
	{
	_LIT(KApaPanicCli,"APSERV-CLI");
	Panic(KApaPanicCli,aPanic);
	}


void CApaAppListServSession::Write(const TAny* aPtr,const TDesC8& aDes,TInt anOffset)
// write to the client thread; if unsuccessful, panic the client
	{
	TRAPD(ret,WriteL(aPtr,aDes,anOffset);)
	__ASSERT_ALWAYS(ret==KErrNone,PanicClient(EClientBadDescriptor));
	}


