// Copyright (c) 1995-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:
// f32\sfile\sf_plugin.cpp
// 
//

/**
 @file
 @internalTechnology
*/


#include "sf_std.h"
#include "sf_plugin_priv.h"


EXPORT_C CFsPluginFactory::CFsPluginFactory()
	{}

EXPORT_C CFsPluginFactory::~CFsPluginFactory()
	{}

/**

Uninstalls the plugin factory.

This is called just before the plugin factory object is destroyed, and allows
any clean up to be carried out.

The default implementation does nothing except return KErrNone.
Implementations should return an error code on error detection.

@return KErrNone if successful, otherwise one of the other system wide error
        codes.
*/
EXPORT_C TInt CFsPluginFactory::Remove()
	{
	return(KErrNone);
	}

/**

Sets the plugin factory's resource library.
This library represents the loaded plugin factory.
This is called internally by InstallPluginFactory().

@param aLib The resource library to be set.
*/
EXPORT_C void CFsPluginFactory::SetLibrary(RLibrary aLib)
	{
	iLibrary=aLib;
	}

/**
Gets the plugin factory's resource library.

@return The plugin factory's resource library.
*/
EXPORT_C RLibrary CFsPluginFactory::Library() const
	{
	return(iLibrary);
	}

/**

*/
TBool CFsPluginFactory::IsDriveSupported(TInt aDrive)
	{
	//If this is version 1 of the plugins, then if KPluginAutoAttach was specified at mount
	//then it just returned ETrue! This behaviour is preserved here:
	if(!(iSupportedDrives & KPluginVersionTwo) && (aDrive == KPluginAutoAttach))
		{
		return(ETrue);
		}

	//If we're version 2 plugin (or version1 && !KPluginAutoAttach) then check against what's been set in iSupportedDrives
	return((iSupportedDrives & (1 << aDrive)) ? (TBool)ETrue : (TBool)EFalse);
	}


EXPORT_C CFsPlugin::CFsPlugin()
	: iReadOnly(0)
	{
    Mem::FillZ(iRegisteredIntercepts, sizeof(iRegisteredIntercepts));
	}

EXPORT_C CFsPlugin::~CFsPlugin()
	{
	}

/**
Delivers the request to the end of plugin thread's queue. 
In certain circumstances, where the request requires priority handling
it adds it to the front of the queue.

@param	aRequest: The request to be delivered
@return KErrNone
*/
EXPORT_C TInt CFsPlugin::Deliver(TFsPluginRequest& aRequest)
	{
	__ASSERT_ALWAYS(iThreadP != NULL, User::Panic(_L("CFsPlugin::Dispatch"),999));

	TInt function = aRequest.Function();

	if(function == EFsPluginOpen)
		{
		// Don't dispatch open requests to the plugin thread
		return KPluginMessageForward;
		}

	if(function == EFsPluginDoRequest ||
	   function == EFsPluginDoControl ||
	   function == EFsPluginDoCancel)
		{
		iThreadP->DeliverFront(aRequest.Request());
		}
	else
		{
		iThreadP->DeliverBack(aRequest.Request());
		}

	return KErrNone;
	}

/**
Initialises the plugin but setting all registered intercepts to zero.
Derived classes might wish to implement their own InitialiseL to add intercepts
*/
EXPORT_C void CFsPlugin::InitialiseL()
	{
	}

/**
Creates a new pluginconn object
Leaves with KErrNotSupported

@return NULL
*/
EXPORT_C CFsPluginConn* CFsPlugin::NewPluginConnL()
	{
	User::Leave(KErrNotSupported);
	return NULL;
	}

/**
Registers a particular function with plugin to be intercepted

@param	aMessage:		the message to be intercepted
@param  aInterceptAtts:	If it is post or pre intercept
@return KErrNone on successful completion 
		KErrNotSupported if message is invalid
*/
EXPORT_C TInt CFsPlugin::RegisterIntercept(TInt aMessage, TInterceptAtts aInterceptAtts)
	{
	if(aMessage >= EMaxClientOperations)
		{
		return KErrNotSupported;
		}

	const TUint32 index = aMessage >> 2; //-- index in the intercepts array

    if(index >= KIntcArrSize)
        {
        __ASSERT_DEBUG(0,Fault(EArrayIndexOutOfRange));
        return KErrNotSupported;
        }

	const TUint8 msk  = (TUint8)(aInterceptAtts << ((aMessage & 0x03) << 1));
	iRegisteredIntercepts[index] |= msk;

	return KErrNone;
	}

/**
Unregisters a particular function with plugin 

@param	aMessage:		the message which should be unregistered
@param  aInterceptAtts:	If it is post or pre intercept
@return KErrNone on successful completion 
		KErrNotSupported if message is invalid
*/
EXPORT_C TInt CFsPlugin::UnregisterIntercept(TInt aMessage, TInterceptAtts aInterceptAtts)
	{
	if(aMessage >= EMaxClientOperations)
		{
		return KErrNotSupported;
		}

	const TUint32 index = aMessage >> 2; //-- index in the intercepts array
    if(index >= KIntcArrSize)
        {
        __ASSERT_DEBUG(0,Fault(EArrayIndexOutOfRange));
        return KErrNotSupported;
        }

	const TUint8 msk = (TUint8)(aInterceptAtts << ((aMessage & 0x03) << 1));
	iRegisteredIntercepts[index] &= ~msk;

	return KErrNone;
	}

/**
    @return ETrue if the message aMessage is registered with any TInterceptAtts type
*/
TBool CFsPlugin::IsRegistered(TInt aMessage)
	{
	if(IsRegistered(aMessage,(TInterceptAtts)EPreIntercept) ||
		IsRegistered(aMessage,(TInterceptAtts)EPrePostIntercept) ||
		IsRegistered(aMessage, (TInterceptAtts)EPostIntercept))
		{
		return (TBool)ETrue;
		}
	return (TBool)EFalse;
	}

/**
    @return ETrue if the message aMessage is registered with the given aInterceptAtts attrubutes
*/
TBool CFsPlugin::IsRegistered(TInt aMessage, TInterceptAtts aInterceptAtts)
	{
	if(aMessage >= EMaxClientOperations)
		{
		return EFalse;
		}

	const TUint32 index = aMessage >> 2; //-- index in the intercepts array
    if(index >= KIntcArrSize)
        {
        __ASSERT_DEBUG(0,Fault(EArrayIndexOutOfRange));
        return EFalse;
        }

	const TUint8 msk = (TUint8)(aInterceptAtts << ((aMessage & 0x03) << 1));

	return((iRegisteredIntercepts[index] & msk) == msk);
	}

/**
   Return ETrue if the calling thread is the plugin thread
*/
TBool CFsPlugin::IsPluginThread(CFsRequest& aRequest)
	{
	if(aRequest.iOwnerPlugin == this)
		return ETrue;

	if(aRequest.iClientThreadId == iThreadId)
		return ETrue;

	// Allow specific requests from the client connection...
	if(aRequest.IsPluginSpecific())
		return EFalse;

	// Check the client connections
	return FsPluginManager::IsPluginConnThread(aRequest.iClientThreadId, this);
	}

TBool CFsPlugin::IsMounted(TInt aDrive)
	{
	CFsPluginFactory* pF = FsPluginManager::GetPluginFactory(this->Name());	
	TInt supportedDrives = pF->SupportedDrives();

	//Version1 plugins could not mount on Z Drive as KPluginAutoAttach==0x19==25==EDriveZ
	//Drive Z is only supported for version two of the plugins.
	//Prevent version 1 plugins here.
	if (!(supportedDrives & KPluginVersionTwo) && (aDrive == EDriveZ))
		return EFalse;
	
	//Some requests have aDrive as -1, so for those requests
	// so long as the plugin was registered we shall say it's mounted.
	if(aDrive > EDriveZ || aDrive < EDriveA)
		return ETrue;
	
	//Otherwise Check iMountedOn
	if(iMountedOn&(1<<aDrive))
		{
		return ETrue;
		}

	return EFalse;
	}

// NOTE: The following API classification might need changing

/** 
@prototype
@deprecated
@see RFilePlugin::Read
 */
EXPORT_C TInt CFsPlugin::FileRead(TFsPluginRequest& aRequest, TDes8& aDes, TInt64 aPos)
	{
	CFileShare* share;
	CFileCB* file;
	GetFileFromScratch((CFsMessageRequest*) aRequest.Request(), share, file);
	TInt64 fileSize = file->CachedSize64();
	if (aPos > fileSize)
		aPos = fileSize;
	TInt len = aDes.Length();
	if (aPos >= fileSize)
		len = 0;
	if (aPos + len > fileSize)
		// filesize - pos shall of TInt size
		// Hence to suppress warning
		len = (TInt)(fileSize - aPos);
	aDes.SetLength(len);

	return DispatchOperation(aRequest, aDes, aPos, EFsFileRead);
	}

/** 
@prototype
@deprecated
@see RFilePlugin::Write
*/
EXPORT_C TInt CFsPlugin::FileWrite(TFsPluginRequest& aRequest, const TDesC8& aDes, TInt64 aPos)
	{
	return DispatchOperation(aRequest, (TDes8&) aDes, aPos, EFsFileWrite);
	}

/**
@internalTechnology
@prototype
@deprecated

Pushes a msgop, dispatches it and waits for it to complete
*/
TInt CFsPlugin::DispatchOperation(TFsPluginRequest& aRequest, TDes8& aDes, TInt64 aPos, TInt aFunction)
	{
	if (aRequest.Function() != EFsFileRead && aRequest.Function() != EFsFileWrite)
		return KErrNotSupported;
	if (aFunction != EFsFileRead && aFunction != EFsFileWrite)
		return KErrNotSupported;

	CFsMessageRequest& msgRequest = * (CFsMessageRequest*) aRequest.Request();


	TInt len = aDes.Length();
	if (len <= 0)
		return CFsRequest::EReqActionComplete;

	TUint8* ptr = (TUint8*) aDes.Ptr();

	TInt r = msgRequest.PushOperation(
		aPos, len, ptr,
		0,			// aOffset
		Complete,	// callback
		0,			// next state
		aFunction);
	if (r != KErrNone)
		return r;


	CFsPlugin* plugin = this;
	FsPluginManager::NextPlugin(plugin, &msgRequest,(TBool)ETrue);
	msgRequest.iCurrentPlugin = plugin;
	msgRequest.Dispatch();
	iThreadP->OperationLockWait();

	aDes.SetLength(len);
	
	return msgRequest.LastError();	// KErrNone;
	}

TInt CFsPlugin::WaitForRequest()
	{
	iLastError = KErrNone;
	iThreadP->OperationLockWait();
	return iLastError;
	}


/** @prototype */
TInt CFsPlugin::Complete(CFsRequest* aRequest, TInt aError)
	{
	CFsMessageRequest& msgRequest = *(CFsMessageRequest*) aRequest;

	CFsPlugin* plugin = msgRequest.iOwnerPlugin;
	if (plugin)
		{
		plugin->iLastError = aError;
		plugin->iThreadP->OperationLockSignal();
		msgRequest.iOwnerPlugin = NULL;
		return CFsRequest::EReqActionComplete;
		}

	TMsgOperation& currentOperation = msgRequest.CurrentOperation();

	if (currentOperation.iState == 0)	// waiting ?
		{
		currentOperation.iState = 1;
		msgRequest.iCurrentPlugin->iThreadP->OperationLockSignal();
		// DON'T dispatch message again, DON'T complete message
		return CFsRequest::EReqActionOwnedByPlugin;	
		}
	else
		{
		return CFsRequest::EReqActionComplete;
		}
	}

/** @prototype */
TInt CFsPlugin::Complete(CFsRequest* aRequest)
	{
	return CFsPlugin::Complete(aRequest, KErrNone);
	}

/** @prototype */
EXPORT_C TInt CFsPlugin::ClientWrite(TFsPluginRequest& aRequest, const TDesC8& aDes, TInt aOffset)
	{
	CFsMessageRequest& msgRequest = * (CFsMessageRequest*) aRequest.Request();
	TMsgOperation& currentOperation = msgRequest.CurrentOperation();
	
	TInt r = KErrNone;
	if (currentOperation.iClientRequest)
		{
		r = msgRequest.Write(0, aDes, aOffset);
		}
	else
		{
		TInt len = aDes.Length();
		if (len > (currentOperation.iReadWriteArgs.iTotalLength - aOffset))
			return KErrArgument;
		memcpy(((TUint8*) currentOperation.iReadWriteArgs.iData) + aOffset, aDes.Ptr(), len);
		currentOperation.iReadWriteArgs.iOffset = aOffset + len;
		}
	return r;
	}

/** @prototype */
EXPORT_C TInt CFsPlugin::ClientRead(TFsPluginRequest& aRequest, TDes8& aDes, TInt aOffset)
	{
	CFsMessageRequest& msgRequest = * (CFsMessageRequest*) aRequest.Request();
	TMsgOperation& currentOperation = msgRequest.CurrentOperation();
	
	TInt r = KErrNone;
	if (currentOperation.iClientRequest)
		{
		r = msgRequest.Read(0, aDes, aOffset);
		}
	else
		{
		TInt len = aDes.Length();
		if (len > (currentOperation.iReadWriteArgs.iTotalLength - aOffset))
			return KErrArgument;
		aDes.Copy ( (TUint8*) currentOperation.iReadWriteArgs.iData + aOffset, len );
		currentOperation.iReadWriteArgs.iOffset = aOffset + len;
		}
	return r;
	}

/**
Constructs a TFsPluginRequest object
@param	aReuqest	client's request, to be wrapped by TFsPluginRequest object
*/
EXPORT_C TFsPluginRequest::TFsPluginRequest(CFsRequest* aRequest)
 : iFsRequest(aRequest)
	{ }

/**
@return		The function of the request
*/
EXPORT_C TInt TFsPluginRequest::Function() const
	{ return(iFsRequest->Operation()->Function()); }

/**
@return		The drive number of the request
*/
EXPORT_C TInt TFsPluginRequest::DriveNumber() const
	{ return(iFsRequest->DriveNumber()); }

/**
@return		The source of the request (often the filename)
*/
EXPORT_C TParse& TFsPluginRequest::Src() const
	{ return(iFsRequest->Src()); }

/**
@return		The destination of the request (often the filename)
*/
EXPORT_C TParse& TFsPluginRequest::Dest() const
	{ return(iFsRequest->Dest()); }

/**
@return		The drive of the request
*/
EXPORT_C TDrive* TFsPluginRequest::Drive() const
	{ return(iFsRequest->Drive()); }

/**
@return		The substitude drive of the request
*/
EXPORT_C TDrive* TFsPluginRequest::SubstedDrive() const
	{ return(iFsRequest->SubstedDrive()); }

/**
@return		The message of the request
*/
EXPORT_C const RMessage2& TFsPluginRequest::Message() const
	{ return(iFsRequest->Message()); }

/**
@return		The request itself
*/
EXPORT_C CFsRequest* TFsPluginRequest::Request() const
	{
	__ASSERT_DEBUG(iFsRequest != NULL, User::Invariant());
	return iFsRequest; 
	}

/**
@return		The scratch value of the request
*/
EXPORT_C TUint TFsPluginRequest::ScratchValue() const
	{ return iFsRequest->ScratchValue(); }

/**
@return		The scratch value of the request
*/
EXPORT_C TInt64 TFsPluginRequest::ScratchValue64() const
	{ return iFsRequest->ScratchValue64(); }

/**
@return		ETrue if the operation is in Post-Intercept
*/
EXPORT_C TInt TFsPluginRequest::IsPostOperation() const
	{ return(iFsRequest->IsPostOperation()); }


EXPORT_C TInt TFsPluginRequest::Read(TF32ArgType aType, TDes8& aDes, TInt aOffset)
	{ 
	return(iFsRequest->Read(aType, aDes, aOffset));
	}
	
EXPORT_C TInt TFsPluginRequest::Read(TF32ArgType aType, TDes16& aDes, TInt aOffset)
	{ 
	//The following if packaged correctly will never come here
	//but just in case someone tries something wrong with a wonky wide descriptor 
	switch(aType)
		{
		case (TF32ArgType)EEntryArray:
		case (TF32ArgType)EEntry:
		case (TF32ArgType)EUid:
		case (TF32ArgType)ETime:
			return KErrBadDescriptor;
		default:
			break;
		}
	return(iFsRequest->Read(aType, aDes, aOffset));
	}
	
EXPORT_C TInt TFsPluginRequest::Read(TF32ArgType aType, TInt& aVal)
	{ 
	//
	// Some messages require special handling...
	//
	if(aType == (TF32ArgType)EPosition)
		{
		return KErrArgument;
		}
	
	return iFsRequest->Read(aType, aVal);
	}
	
EXPORT_C TInt TFsPluginRequest::Read(TF32ArgType aType, TUint& aVal)
	{ 
	//
	// Some messages require special handling...
	//
	if(aType == (TF32ArgType)EPosition)
		{
		return KErrArgument;
		}
	
	return iFsRequest->Read(aType, aVal);
	}

EXPORT_C TInt TFsPluginRequest::Read(TF32ArgType aType, TInt64& aVal)
	{
	TInt err = iFsRequest->Read(aType, aVal);
	if(err != KErrNone)
		return err;

	//
	// Some messages require special handling...
	//
	if(aType == (TF32ArgType)EPosition)
		{
		TInt op = Function();
		if(op == EFsFileRead || op == EFsFileWrite)
			{	
			if (aVal == KCurrentPosition64)
				{
				CFileShare* share = (CFileShare*)iFsRequest->ScratchValue();
				if(share == NULL)
					return KErrBadHandle;
			
				aVal = share->iPos;
				}
			}
		}
	
	return KErrNone;
	}
	
EXPORT_C TInt TFsPluginRequest::Write(TF32ArgType aType, const TDesC8& aDes, TInt aOffset)
	{ 
	return(iFsRequest->Write(aType, aDes, aOffset));
	}
	

EXPORT_C TInt TFsPluginRequest::Write(TF32ArgType aType, const TDesC16& aDes, TInt aOffset)
	{ 
	return(iFsRequest->Write(aType, aDes, aOffset));
	}
	
EXPORT_C TInt TFsPluginRequest::FileName(TDes& aName)
	{
	//Special handling required for directories.
	switch(Function())
		{
		case EFsDirOpen:
		case EFsSetEntry:
			{
			aName.Copy(Request()->Src().FullName());
			break;
			}
		case EFsDirReadOne:
		case EFsDirReadPacked:
		case EFsDirSubClose:
			{
			//Get the name from CDirCB::iName
			CDirCB* dir = (CDirCB*) ScratchValue();
			__ASSERT_ALWAYS(dir!= NULL, Fault(EPluginOpError));
			TName name = dir->Name();
			if(name.Size() == 0)
				{
				return KErrNotFound;
				}
			aName.Copy(name);
			break;
			}
		default:
			{
			CFileShare* share;
			TInt err = ShareFromClientHandle(share);
			if(err != KErrNone || share == NULL)
				return(err);
			
			NameFromShare(*share, aName);
			}
		}
	return KErrNone;
	}

EXPORT_C TInt TFsPluginRequest::SetSharePos(TInt64& aPos)
	{
	CFileShare* share;
	TInt err = ShareFromClientHandle(share);
	if(err != KErrNone || share == NULL)
		return(KErrBadHandle);
	
	share->File().Drive().Lock();
	share->iPos = aPos;
	share->File().Drive().UnLock();
	
	return KErrNone;
	}

TInt TFsPluginRequest::ShareFromClientHandle(CFileShare*& aShare)
	{
	aShare = NULL;
	
	TInt handle;
	TInt err = ClientSubSessionHandle(handle);
	if(err != KErrNone)
		return err;

	aShare = GetShareFromHandle(iFsRequest->Session(), handle);
	
	return aShare ? KErrNone : KErrBadHandle;
	}

TInt TFsPluginRequest::ClientSubSessionHandle(TInt& aHandle)
	{
	aHandle = 0;

	// Subsession handle is in Arg[3] for read/write etc, but 
	// when subsession create it's contained in client descriptor
	if(iFsRequest->Operation()->IsOpenSubSess())
		{
		if(!IsPostOperation())
			return KErrNotSupported;

		TPtr8 handleDes((TUint8*)&aHandle,sizeof(TInt));
		TInt err = iFsRequest->Read(KMsgPtr3,handleDes);
		if(err != KErrNone)
			return err;
		}
	else
		{
		aHandle = iFsRequest->Message().Int3(); 
		}

	return KErrNone;
	}

/**
@publishedPartner

Utility function to obtain the file name from a file share object

@param	aFileShare		A pointer to the file share
@param	aName			A reference to the descriptor to contain the file name
*/
void TFsPluginRequest::NameFromShare(CFileShare& aFileShare, TDes& aName)
	{
	CFileCB& theFile = aFileShare.File();
	aName = _L("?:");
	aName[0] = TText('A' + theFile.Drive().DriveNumber());
	aName.Append(theFile.FileName());
	}


/**
Constructor of plugin connection object
*/
EXPORT_C CFsPluginConn::CFsPluginConn()
	{
	}

/**
Destructor of plugin conn. object
*/
EXPORT_C CFsPluginConn::~CFsPluginConn()
	{
	}

/**
Closes the plugin conn. 
*/
EXPORT_C void CFsPluginConn::Close()
	{
	iRequestQue.DoCancelAll(KErrCancel);
	CFsObject::Close();
	}

CFsPluginConnRequest::CFsPluginConnRequest(CFsPluginConn* aPluginConn)
 : iPluginConn(*aPluginConn)
	{	
	}
	
TInt CFsPluginConnRequest::InitControl(CFsRequest* aRequest)
	{
	iMessage = aRequest->Message();
	const RMessage2& m = aRequest->Message();
	iFunction = m.Int0();
	iParam1 = (TDes8*)m.Ptr1();
	iParam2 = (TDes8*)m.Ptr2();
	return KErrNone;
	}

TInt CFsPluginConnRequest::DoControl()
	{	
	return iPluginConn.DoControl(*this);
	}

TInt CFsPluginConnRequest::InitRequest(CFsRequest* aRequest)
	{
	InitControl(aRequest);
	iPluginConn.iRequestQue.DoAddRequest(this);
	return KErrNone;
	}
	
void CFsPluginConnRequest::DoRequest()
	{	
	iPluginConn.DoRequest(*this);
	}
	
TPluginConnRequestQue::TPluginConnRequestQue()
	{
	iHeader.SetOffset(_FOFF(CFsPluginConnRequest,iLink));
	}

TPluginConnRequestQue::~TPluginConnRequestQue()
	{
	}

void TPluginConnRequestQue::DoAddRequest(CFsPluginConnRequest* aRequest)
	{
	iHeader.AddLast(*aRequest);
	}

/**
Cancels all the requests of plugin connection

@param		aCompletionCode: the code the request are completed 
*/
EXPORT_C void TPluginConnRequestQue::DoCancelAll(TInt aCompletionCode)
	{
	TDblQueIter<CFsPluginConnRequest> q(iHeader);
	CFsPluginConnRequest* info;
	while((info=q++)!=NULL)
		{
		info->Complete(aCompletionCode);
		}
	__ASSERT_DEBUG(iHeader.IsEmpty(),Fault(EBaseQueCancel));
	}


/**
*/
EXPORT_C TDes8& TRawEntryArray::Buf()
	{ return iBuf; }

/**
*/
EXPORT_C void TRawEntryArray::SetBuf(TDes8& aBuf)
	{
	iCount = KCountNeeded;
	iBuf.Copy(aBuf);
	}

/**
*/
EXPORT_C TInt TRawEntryArray::EntrySize(TInt aIdx)
	{ return Align4(::EntrySize((*this)[aIdx])); }
