// Copyright (c) 2008-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_shim.cpp
// 
//

#include "cl_std.h"
#include "sf_std.h"

/*******************************************************
*						RFsPlugin					   *
*******************************************************/

EXPORT_C RFsPlugin::RFsPlugin(TFsPluginRequest& aRequest, TBool aDirectToDrive)
  : iSessionHelper(&aRequest, aDirectToDrive)
	{
	SetReturnedHandle(KNullHandle);
	}

EXPORT_C RFsPlugin::~RFsPlugin()
	{
	Close();
	}

EXPORT_C TInt RFsPlugin::Connect()
/**
Connects a file server plugin to the file server.

To end the file server session, use Close().

@return KErrNone, if successful, otherwise one of the other system-wide error codes.
*/
	{
	return KErrNone;
	}

EXPORT_C void RFsPlugin::Close()
/**
Closes a file server plugin session.
*/
	{
	SetReturnedHandle(KNullHandle);
	}

EXPORT_C TInt RFsPlugin::Delete(const TDesC& aName)
/**
Deletes a single file.

@see RFs::Delete
*/
	{
	return(RFs::Delete(aName));
	}

EXPORT_C TInt RFsPlugin::Rename(const TDesC& aOldName,const TDesC& aNewName)
/**
Renames a single file or directory.

@see RFs::Rename
*/
	{
	return(RFs::Rename(aOldName, aNewName));
	}

EXPORT_C TInt RFsPlugin::Replace(const TDesC& aOldName,const TDesC& aNewName)
/**
Replaces a single file with another.

@see RFs::Replace
*/
	{
	return(RFs::Replace(aOldName, aNewName));
	}

EXPORT_C TInt RFsPlugin::Entry(const TDesC& aName,TEntry& aEntry) const
/**
Gets the entry details for a file or directory.

@see RFs::Entry
*/
	{
	return(RFs::Entry(aName, aEntry));
	}

EXPORT_C TInt RFsPlugin::SetEntry(const TDesC& aName,const TTime& aTime,TUint aSetAttMask,TUint aClearAttMask)
/**
Sets both the attributes and the last modified date and time for a file or directory.

@see RFs::SetEntry
*/
	{
	return(RFs::SetEntry(aName,aTime,aSetAttMask,aClearAttMask));
	}

EXPORT_C TInt RFsPlugin::ReadFileSection(const TDesC& aName,TInt64 aPos,TDes8& aDes,TInt aLength) const
/**
Reads data from a file without opening it.

The contents of the	file can be accessed regardless of the file's lock state.

@see RFs::ReadFileSection
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return(RFs::ReadFileSection(aName,I64LOW(aPos),aDes,aLength));
#else
	return(RFs::ReadFileSection(aName,aPos,aDes,aLength));
#endif
	}

TInt RFsPlugin::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	return iSessionHelper.SendReceive(aFunction, aArgs);
	}

TInt RFs::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	if(Handle())
		return RSessionBase::SendReceive(aFunction, aArgs);
		
	return ((RFsPlugin*) this)->SendReceive(aFunction, aArgs);
	}


/*******************************************************
*						RFilePlugin					   *
*******************************************************/

EXPORT_C RFilePlugin::RFilePlugin(TFsPluginRequest& aRequest, TBool aDirectToDrive)
  : iSessionHelper(&aRequest, aDirectToDrive)
	{
	SetHandle(KErrBadHandle);
	SetSubSessionHandle(KErrBadHandle);
	}

EXPORT_C RFilePlugin::~RFilePlugin()
	{
	Close();
	}

EXPORT_C TInt RFilePlugin::Open(const TDesC& aName,TUint aMode)
/**
Opens an existing file for reading or writing.

If the file does not already exist, an error is returned.

@see RFile::Open
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());
	return(CreateSubSession(fs,EFsFileOpen,TIpcArgs(&aName,aMode)));
	}

EXPORT_C void RFilePlugin::Close()
/**
Closes the file.

@see RFile::Close
*/
	{
	CloseSubSession(EFsFileSubClose);
	SetSubSessionHandle(KErrBadHandle);
	}

EXPORT_C TInt RFilePlugin::Create(const TDesC& aName,TUint aFileMode)
/**
Closes the file.

@see RFile::Create
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());
	return(CreateSubSession(fs,EFsFileCreate,TIpcArgs(&aName,aFileMode)));
	}

EXPORT_C TInt RFilePlugin::Replace(const TDesC& aName,TUint aFileMode)
/**
Closes the file.

@see RFile::Replace
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());
	return(CreateSubSession(fs,EFsFileReplace,TIpcArgs(&aName,aFileMode)));
	}

EXPORT_C TInt RFilePlugin::Temp(const TDesC& aPath,TFileName& aName,TUint aFileMode)
/**
Closes the file.

@see RFile::Temp
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());
	return(CreateSubSession(fs,EFsFileTemp,TIpcArgs(&aPath,aFileMode,&aName)));
	}

EXPORT_C TInt RFilePlugin::AdoptFromClient()
/**
Closes the file.

@see RFile::AdoptFromClient
*/
	{
	TFsPluginRequest* request = iSessionHelper.Request();
	if(request == NULL)
		return KErrBadHandle;

	TInt clientSubSessionHandle;
	TInt err = request->ClientSubSessionHandle(clientSubSessionHandle);
	if (err != KErrNone)
		return err;

	RFs fs;
	fs.SetHandle(Session().Handle());
	err = CreateSubSession(fs,EFsFileDuplicate, TIpcArgs(clientSubSessionHandle, ETrue));
	if (err != KErrNone)
		return err;

	SetSubSessionHandle(SubSessionHandle() ^ KSubSessionMangleBit);

	return err;
	}
    
EXPORT_C TInt RFilePlugin::TransferToClient()
/**
Closes the file.

@see RFile::TransferToClient
*/
	{
	TFsPluginRequest* request = iSessionHelper.Request();
	if(request == NULL)
		return KErrBadHandle;

	// This doesn't behave like a standard duplicate as we're running in the context of the
	// client's session.  Instead, we can simply return our subsession handle to the client.
	TRAPD(err, request->Request()->WriteL(KMsgPtr3, TPckgC<TInt>(SubSessionHandle())));

	// Next we have to free up the close request reserved for our internal subsession
	// otherwise two messages will be reserved for the client...
	RequestAllocator::OpenSubFailed(request->Request()->Session());

	// And now we're done - we don't bother closing, as the client now completely owns the handle
	SetSubSessionHandle(KErrBadHandle);

	return err;
	}

EXPORT_C TInt RFilePlugin::Write(TInt64 aPos, const TDesC8& aDes)
/**
Writes to the file at the specified offset within the file

@see RFile::Write
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::Write(I64LOW(aPos), aDes);
#else
	return RFile64::Write(aPos, aDes);
#endif
	}

EXPORT_C TInt RFilePlugin::Write(TInt64 aPos,const TDesC8& aDes,TInt aLen)
/**
Writes the specified number of bytes to the file at the specified offset within the file.

@see RFile::Write
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::Write(I64LOW(aPos), aDes, aLen);
#else
	return RFile64::Write(aPos, aDes, aLen);
#endif
	}

EXPORT_C TInt RFilePlugin::Read(TInt64 aPos,TDes8& aDes) const
/**
Reads from the file at the specified offset within the file

@see RFile::Read
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::Read(I64LOW(aPos), aDes);
#else
	return RFile64::Read(aPos, aDes);
#endif
	}

EXPORT_C TInt RFilePlugin::Read(TInt64 aPos,TDes8& aDes,TInt aLen) const
/**
Reads the specified number of bytes of binary data from the file at a specified 
offset within the file.

@see RFile::Read
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::Read(I64LOW(aPos), aDes, aLen);
#else
	return RFile64::Read(aPos, aDes, aLen);
#endif
	}

EXPORT_C TInt RFilePlugin::Size(TInt64& aSize) const
/**
Gets the current file size.

@see RFile::Size
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	TInt size = I64LOW(aSize);
	TInt err = RFile::Size(size);
	aSize = size;
	return err;
#else
	return RFile64::Size(aSize);
#endif
	}

EXPORT_C TInt RFilePlugin::SetSize(TInt64 aSize)
/**
Sets the file size.

@see RFile::SetSize
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::SetSize(I64LOW(aSize));
#else
	return RFile64::SetSize(aSize);
#endif
	}

EXPORT_C TInt RFilePlugin::Lock(TInt64 aPos, TInt64 aLength) const
/**
Locks a region within the file as defined by a range of bytes.

@see RFile::Lock
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::Lock(I64LOW(aPos), I64LOW(aLength));
#else
	return RFile64::Lock(aPos, aLength);
#endif
	}

EXPORT_C TInt RFilePlugin::UnLock(TInt64 aPos, TInt64 aLength) const
/**
Unlocks a region within the file as defined by a range of bytes.

@see RFile::UnLock
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	return RFile::UnLock(I64LOW(aPos), I64LOW(aLength));
#else
	return RFile64::UnLock(aPos, aLength);
#endif
	}

EXPORT_C TInt RFilePlugin::Seek(TSeek aMode,TInt64& aPos) const
/**
Sets the the current file position.

@see RFile::Seek
*/
	{
#ifndef SYMBIAN_ENABLE_64_BIT_FILE_SERVER_API
	TInt position = I64LOW(aPos);
	TInt err = RFile::Seek(aMode, position);
	if(err != KErrNone)
		return err;
	aPos = position;
	return KErrNone;
#else
	return RFile64::Seek(aMode, aPos);
#endif
	}

EXPORT_C TInt RFilePlugin::Flush()
/**
Commits data to the storage device and flushes internal buffers without closing 
the file.

@see RFile::Flush
*/
	{
	return RFile::Flush();
	}

EXPORT_C TInt RFilePlugin::Att(TUint& aVal) const
/**
Gets the file's attributes.

@see RFile::Att
*/
	{
	return RFile::Att(aVal);
	}

EXPORT_C TInt RFilePlugin::SetAtt(TUint aSetAttMask,TUint aClearAttMask)
/**
Sets or clears file attributes using two bitmasks.

@see RFile::SetAtt
*/
	{
	return RFile::SetAtt(aSetAttMask, aClearAttMask);
	}

EXPORT_C TInt RFilePlugin::Modified(TTime& aTime) const
/**
Gets local date and time the file was last modified, in universal time.

@see RFile::Modified
*/
	{
	return RFile::Modified(aTime);
	}

EXPORT_C TInt RFilePlugin::SetModified(const TTime& aTime)
/**
Sets the date and time the file was last modified. UTC date and time should be used.

@see RFile::SetModified
*/
	{
	return RFile::SetModified(aTime);
	}

EXPORT_C TInt RFilePlugin::Set(const TTime& aTime,TUint aMask,TUint aVal)
/**
Sets the file’s attributes, and the date and time it was last modified.

@see RFile::Set
*/
	{
	return RFile::Set(aTime, aMask, aVal);
	}

EXPORT_C TInt RFilePlugin::ChangeMode(TFileMode aNewMode)
/**
Switches an open file's access mode between EFileShareExclusive and EFileShareReadersOnly.

@see RFile::ChangeMode
*/
	{
	return RFile::ChangeMode(aNewMode);
	}

EXPORT_C TInt RFilePlugin::Rename(const TDesC& aNewName)
/**
Renames a file.

@see RFile::Rename
*/
	{
	return RFile::Rename(aNewName);
	}

void RFilePlugin::SetHandle(TInt aHandle)
	{
	*(((TInt*) this) + 0) = aHandle;
	}

void RFilePlugin::SetSubSessionHandle(TInt aHandle)
	{
	*(((TInt*) this) + 1) = aHandle;
	}

TInt RFilePlugin::CreateSubSession(const RSessionBase& aSession, TInt aFunction, const TIpcArgs& aArgs)
	{
	TInt reply;
	TInt err = iSessionHelper.CreateSubSession(aSession, aFunction, aArgs, &reply);
	if(err == KErrNone)
		SetSubSessionHandle(reply);
	return(err);
	}

void RFilePlugin::CloseSubSession(TInt aFunction)
	{
	if (SubSessionHandle())
		{
		SendReceive(aFunction,TIpcArgs(TIpcArgs::ENothing,TIpcArgs::ENothing,TIpcArgs::ENothing,SubSessionHandle()));
		}

	SetHandle(KErrBadHandle);
	SetSubSessionHandle(KErrBadHandle);
	}

TInt RFilePlugin::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	return iSessionHelper.SendReceive(aFunction, aArgs, ((RFilePlugin*) this)->SubSessionHandle());
	}

TInt RFile::CreateSubSession(const RSessionBase& aSession,TInt aFunction,const TIpcArgs& aArgs)
	{
	if(SubSessionHandle() == KErrBadHandle)
		return ((RFilePlugin*) this)->CreateSubSession(aSession, aFunction, aArgs);

	return RSubSessionBase::CreateSubSession(aSession, aFunction, aArgs);
	}

void RFile::CloseSubSession(TInt aFunction)
	{
	if((Session().Handle() ^ CObjectIx::ENoClose) != KErrBadHandle) 
		RSubSessionBase::CloseSubSession(aFunction);
	else
		((RFilePlugin*) this)->CloseSubSession(aFunction);
	}

TInt RFile::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	if((Session().Handle() ^ CObjectIx::ENoClose) != KErrBadHandle) 
		return RSubSessionBase::SendReceive(aFunction, aArgs);
		
	return ((RFilePlugin*) this)->SendReceive(aFunction, aArgs);
	}


/*******************************************************
*						RDirPlugin					   *
*******************************************************/

EXPORT_C RDirPlugin::RDirPlugin(TFsPluginRequest& aRequest, TBool aDirectToDrive)
  : iSessionHelper(&aRequest, aDirectToDrive)
	{
	SetHandle(KErrBadHandle);
	SetSubSessionHandle(KErrBadHandle);
	}

EXPORT_C RDirPlugin::~RDirPlugin()
	{
	Close();
	}

EXPORT_C TInt RDirPlugin::Open(const TDesC& aMatchName,const TUidType& aUidType)
/**
Opens a directory using the specified UID type to filter the
directory entry types that will subsequently be read.

@see RDir::Open
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());

	TPckgC<TUidType> pckgUid(aUidType);
	return(CreateSubSession(fs,EFsDirOpen,TIpcArgs(&aMatchName,KEntryAttAllowUid,&pckgUid)));
	}

EXPORT_C TInt RDirPlugin::Open(const TDesC& aMatchName,TUint anAttMask)
/**
Opens a directory using an attribute bitmask to filter the directory entry
types that will subsequently be read.

@see RDir::Open
*/
	{
	RFs fs;
	fs.SetHandle(Session().Handle());

	TUidType uidType(TUid::Null(),TUid::Null(),TUid::Null());
	TPckgC<TUidType> pckgUid(uidType);
	return(CreateSubSession(fs,EFsDirOpen,TIpcArgs(&aMatchName,anAttMask,&pckgUid)));
	}
    
EXPORT_C void RDirPlugin::Close()
/**
Closes the the directory.

@see RDir::Close
*/
	{
	CloseSubSession(EFsDirSubClose);
	SetSubSessionHandle(KErrBadHandle);
	}

EXPORT_C TInt RDirPlugin::Read(TEntryArray& aArray)
/**
Reads all filtered directory entries into the specified array.

@see RDir::Read
*/
	{
	return RDir::Read(aArray);
	}

EXPORT_C TInt RDirPlugin::Read(TEntry& aEntry)
/**
Reads all filtered directory entries into the specified array.

@see RDir::Read
*/
	{
	return RDir::Read(aEntry);
	}

void RDirPlugin::SetHandle(TInt aHandle)
	{
	*(((TInt*) this) + 0) = aHandle;
	}

void RDirPlugin::SetSubSessionHandle(TInt aHandle)
	{
	*(((TInt*) this) + 1) = aHandle;
	}

TInt RDirPlugin::CreateSubSession(const RSessionBase& aSession, TInt aFunction, const TIpcArgs& aArgs)
	{
	TInt reply;
	TInt err = iSessionHelper.CreateSubSession(aSession, aFunction, aArgs, &reply);
	if(err == KErrNone)
		SetSubSessionHandle(reply);
	return(err);
	}

void RDirPlugin::CloseSubSession(TInt aFunction)
	{
	if (SubSessionHandle())
		{
		SendReceive(aFunction,TIpcArgs(TIpcArgs::ENothing,TIpcArgs::ENothing,TIpcArgs::ENothing,SubSessionHandle()));
		}

	SetHandle(KErrBadHandle);
	SetSubSessionHandle(KErrBadHandle);
	}

TInt RDirPlugin::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	return iSessionHelper.SendReceive(aFunction, aArgs, ((RDirPlugin*) this)->SubSessionHandle());
	}

TInt RDir::SendReceive(TInt aFunction,const TIpcArgs& aArgs) const
	{
	if((Session().Handle() ^ CObjectIx::ENoClose) != KErrBadHandle) 
		return RSubSessionBase::SendReceive(aFunction, aArgs);

	return ((RDirPlugin*) this)->SendReceive(aFunction, aArgs);
	}


/*******************************************************
*				TFsPluginSessionHelper				   *
*******************************************************/

TPluginSessionHelper::TPluginSessionHelper()
	{ memclr(this, sizeof(TPluginSessionHelper)); }

TPluginSessionHelper::TPluginSessionHelper(TFsPluginRequest* aRequest, TBool aDirectToDrive)
  : iPlugin(aRequest->Request()->iCurrentPlugin),
	iSession(aRequest->Request()->Session()),
	iDirectToDrive(aDirectToDrive),
    iRequest(aRequest)
	{
	// need to initialise RLocalMessage with client session
	*((RMessage2*) &iMessage) = aRequest->Message();
	iMessage.InitHandle();	// set handle to KLocalMessageHandle
	memclr(iSpare, sizeof(iSpare));
	}

TInt TPluginSessionHelper::CreateSubSession(const RSessionBase& aSession, TInt aFunction, const TIpcArgs& aArgs, TInt* aReply)
	{
	(void)aSession;

	// Init message
	TIpcArgs args;
	args.iArgs[0] = aArgs.iArgs[0];
	args.iArgs[1] = aArgs.iArgs[1];
	args.iArgs[2] = aArgs.iArgs[2];
	args.iFlags = aArgs.iFlags&((1<<(3*TIpcArgs::KBitsPerType))-1);

	TPckgBuf<TInt> reply;
	args.Set(3,&reply);

	// copy session pointer
	RLocalMessage message = iMessage;
	message.SetFunction(aFunction);
	message.SetArgs(args);

	TInt err = Dispatch(aFunction, args);
	if (err == KErrNone)
		*aReply = reply();

	return err;
	}

TInt TPluginSessionHelper::Dispatch(TInt aFunction, TIpcArgs& aArgs) const
	{
	// copy session pointer
	RLocalMessage message = iMessage;
	message.SetFunction(aFunction);
	message.SetArgs(aArgs);

	// allocate request
	CFsClientMessageRequest* newRequest;
	const TOperation& oP = OperationArray[aFunction & KIpcFunctionMask];
	TInt err = RequestAllocator::GetMessageRequest(oP, message, newRequest);
	if (err != KErrNone)
		return err;

	newRequest->Set(message, oP, iSession);
	
	//This is wrong. drive number is set in TFsXxx::initialise
	//newRequest->SetDrive(&TheDrives[iPlugin->Drive()]);

	newRequest->iCurrentPlugin = iPlugin;
	newRequest->iOwnerPlugin   = iPlugin;
	newRequest->iDirectToDrive = iDirectToDrive;

	newRequest->Dispatch();

	// NOTE : newRequest will be free'd by the File Server before completing the 
	//        request so it's not safe to touch the request from now on...
	
	return(iPlugin->WaitForRequest());
	}

TInt TPluginSessionHelper::SendReceive(TInt aFunction, const TIpcArgs& aArgs, TInt aSubSessionHandle) const
	{
	// Init message
	TIpcArgs args;
	args.iArgs[0] = aArgs.iArgs[0];
	args.iArgs[1] = aArgs.iArgs[1];
	args.iArgs[2] = aArgs.iArgs[2];
	args.iFlags   = aArgs.iFlags&((1<<(3*TIpcArgs::KBitsPerType))-1);
	args.iArgs[3] = aSubSessionHandle;

	return Dispatch(aFunction, args);
	}

TInt TPluginSessionHelper::SendReceive(TInt aFunction, const TIpcArgs& aArgs) const
	{
	// Init message
	TIpcArgs args;
	args.iArgs[0] = aArgs.iArgs[0];
	args.iArgs[1] = aArgs.iArgs[1];
	args.iArgs[2] = aArgs.iArgs[2];
	args.iArgs[3] = aArgs.iArgs[3];
	args.iFlags = aArgs.iFlags&((1<<(3*TIpcArgs::KBitsPerType))-1);

	return Dispatch(aFunction, args);
	}

GLDEF_C void Panic(TClientPanic aPanic)
//
// Panic the current client with a file server client side panic.
//
	{
	User::Panic(_L("FS_PLUGIN_CLIENT panic"),aPanic);
	}

