mtpfws/mtpfw/dataproviders/dputility/src/cmtprequestchecker.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 06 Jul 2010 15:13:34 +0300
changeset 33 883e91c086aa
parent 18 1b39655331a3
child 47 63cf70d3ecd8
permissions -rw-r--r--
Revision: 201025 Kit: 2010127

// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "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:
//

#include <f32file.h>
#include <mtp/cmtpobjectmetadata.h>
#include <mtp/mmtpconnection.h>
#include <mtp/mmtpdataproviderframework.h>
#include <mtp/mmtpobjectmgr.h>
#include <mtp/mmtpstoragemgr.h>

#include "cmtprequestchecker.h"
#include "cmtpfsexclusionmgr.h"
#include "cmtpfsentrycache.h"

static const TInt KMTPRequestCheckerHandleGranularity = 2;
__FLOG_STMT(_LIT8(KComponent,"MTPRequestChecker");)

/**
Two-phase construction method
@param aFramework	The data provider framework
@param aConnection	The connection object
@return a pointer to the created request checker object
*/   
EXPORT_C CMTPRequestChecker* CMTPRequestChecker::NewL(MMTPDataProviderFramework& aFramework, MMTPConnection& aConnection)
	{
	CMTPRequestChecker* self = new (ELeave) CMTPRequestChecker(aFramework, aConnection);
	CleanupStack::PushL(self);
	self->ConstructL();
	CleanupStack::Pop(self);
	return self;
	}

/**
Destructor
*/	
EXPORT_C CMTPRequestChecker::~CMTPRequestChecker()
	{
	iDpSingletons.Close();
	iHandles.Close();
	iObjectArray.ResetAndDestroy();
	__FLOG_CLOSE;
	}

/**
Verfiy the request.  It check the request header first (session id and transaction code), and check for special values, last
it iterates through the verification elements for checking individul parameters in the request

@param aRequest	The request object to verify
@param aCount	The number of verification elements
@param aElementInfo	The array of verification elements
@return reponse code to return to the initiator
*/	
EXPORT_C TMTPResponseCode CMTPRequestChecker::VerifyRequestL(const TMTPTypeRequest& aRequest, TInt aCount, const TMTPRequestElementInfo* aElementInfo)
	{
   	TMTPResponseCode result = EMTPRespCodeOK;
   	iHandles.Close();
   	iObjectArray.ResetAndDestroy();
    
	result = CheckRequestHeader(aRequest);
	
	for (TInt i = 0; i < aCount && EMTPRespCodeOK == result; i++)
		{
		TUint32 parameter = aRequest.Uint32(aElementInfo[i].iElementIndex);
		if (!IsSpecialValue(parameter, aElementInfo[i]))
			{
			switch (aElementInfo[i].iElementType)
				{
				case EMTPElementTypeSessionID:
					result = VerifySessionId(parameter, aElementInfo[i]);
					break;
				case EMTPElementTypeObjectHandle:
					result = VerifyObjectHandleL(parameter, aElementInfo[i]);
					break;
				case EMTPElementTypeStorageId:
					result = VerifyStorageIdL(parameter, aElementInfo[i]);
					break;
				case EMTPElementTypeFormatCode:
					result = VerifyFormatCode(parameter, aElementInfo[i]);
					break;				
				default:
					User::Invariant();  // Should never run
					break;				
				}			
			}
	    }
		
    return result;     
	
	}

/**
Return the object info for the handle.  This is to remove extra expensive DMBS retrieval operations.

@param aHandle	the handle of the object requested
@return an object info for the handle
*/
EXPORT_C CMTPObjectMetaData* CMTPRequestChecker::GetObjectInfo(TUint32 aHandle) const
	{
	CMTPObjectMetaData* result = NULL;
	TInt count = iHandles.Count();
	for(TInt i = 0; i < count; i++)
		{
		if (iHandles[i] == aHandle)
			{
			result = iObjectArray[i];
			break;
			}
		}
	return result;
	}

/**
Check the request header portion (session Id and transaction code)
@param aRequest	the request object to check
@return repsonse code to return to initiator
*/
TMTPResponseCode CMTPRequestChecker::CheckRequestHeader(const TMTPTypeRequest& aRequest) const
	{
    TMTPResponseCode ret = EMTPRespCodeOK; 
    TUint16 operationCode = aRequest.Uint16(TMTPTypeRequest::ERequestOperationCode);
  	TUint32 sessionId = aRequest.Uint32(TMTPTypeRequest::ERequestSessionID);
	TUint32 transactionCode = aRequest.Uint32(TMTPTypeRequest::ERequestTransactionID);
	
	if (operationCode == EMTPOpCodeCloseSession || operationCode == EMTPOpCodeResetDevice)
		{
		if (sessionId != 0)
			{
			ret = EMTPRespCodeInvalidParameter;
			}
		}
	else
		{		
	    // requests that are valid when there's no opened session.
	    if (sessionId == 0)
	        {
	        switch (operationCode)
	            {            	
	            case EMTPOpCodeGetDeviceInfo:
	            case EMTPOpCodeOpenSession: 
	                {
	                // Transaction id must be 0 when called out side an active session.
	                if (transactionCode != 0)
	                    {
	                    ret = EMTPRespCodeInvalidTransactionID;
	                    }

	                }
	                break;
	            
	            default:
	                ret = EMTPRespCodeSessionNotOpen;
	                break;
	            }
	        }
	    else if (!iConnection.SessionWithMTPIdExists(sessionId))
	        { 
	        ret = EMTPRespCodeSessionNotOpen;
	        }
		}
    return ret;	
	}

/**
Check the session id in the request parameter (NOTE the session id is different from the one in the request header),
this usually only applies to the OpenSession request
@param aSessionId	Session id of the request.
@param aElementInfo ElementInfo data array to check against.
@return repsonse code to return to initiator
*/	
TMTPResponseCode CMTPRequestChecker::VerifySessionId(TUint32 aSessionId, const TMTPRequestElementInfo& /*aElementInfo*/) const
	{
    TMTPResponseCode ret = EMTPRespCodeOK; 
    
    if (aSessionId != 0)   
    	{
    	if (iConnection.SessionWithMTPIdExists(aSessionId))
			{
			ret = EMTPRespCodeSessionAlreadyOpen;			
			}
    	}
    else
    	{
    	ret = EMTPRespCodeInvalidParameter;
    	}
		
    return ret;	
	}

/**
Check the object handle in the request parameter, whether the handle is in the object store, read/write, file/dir
@param aHandle	Object handle to be checked.
@param aElementInfo Element info array to be checked against.
@return repsonse code to return to initiator
*/		
TMTPResponseCode CMTPRequestChecker::VerifyObjectHandleL(TUint32 aHandle, const TMTPRequestElementInfo& aElementInfo)
	{
	__FLOG_VA((_L8("VerifyObjectHandleL entry with handle 0x%08X"), aHandle));
    TMTPResponseCode ret = EMTPRespCodeOK; 

	CMTPObjectMetaData* object(CMTPObjectMetaData::NewLC());
	TBool result(iFramework.ObjectMgr().ObjectL(aHandle, *object));
	iObjectArray.AppendL(object);
	CleanupStack::Pop(object);
	iHandles.AppendL(aHandle);
		
	// Obj handle exists
	if (result)
		{
	    TUint storageID = object->Uint(CMTPObjectMetaData::EStorageId);
	    if(!iFramework.StorageMgr().ValidStorageId(storageID))
	    	{
			return EMTPRespCodeInvalidObjectHandle;
	    	}
	    
		CMTPStorageMetaData* storageMetaData = (CMTPStorageMetaData *)& iFramework.StorageMgr().StorageL(storageID);
		if (storageMetaData->Uint(CMTPStorageMetaData::EStorageSystemType) != CMTPStorageMetaData::ESystemTypeDefaultFileSystem)
			{
			return ret;
			}
		
		const TDesC& suid(object->DesC(CMTPObjectMetaData::ESuid));
		TEntry entry;
        TInt err = iFramework.Fs().Entry(suid, entry);
        
        if ( (object->Uint(CMTPObjectMetaData::EFormatCode) == EMTPFormatCodeAssociation) && 
             (object->Uint(CMTPObjectMetaData::EFormatSubCode) != EMTPAssociationTypeGenericFolder ) )
            {
            // Special association type .. not always present on the filesystem.
            return ret;
            }   
        else
            {
            if ( err != KErrNone )
            	{
            	if( (iDpSingletons.CopyingBigFileCache().TargetHandle() == aHandle) &&
            			(iDpSingletons.CopyingBigFileCache().IsOnGoing()))
            		{
            		// The object is being copied, it is not created in fs yet. Use its cache entry for check
            		__FLOG(_L8("VerifyObjectHandleL - The object is being copied, use its cache entry for check"));
            		entry = iDpSingletons.CopyingBigFileCache().FileEntry();
            		err = KErrNone;
            		}
            	else if( (iDpSingletons.MovingBigFileCache().TargetHandle() == aHandle) &&
            						(iDpSingletons.MovingBigFileCache().IsOnGoing()))
            		{
            		// The object is being moved, it is not created in fs yet. Use its cache entry for check
            		__FLOG(_L8("VerifyObjectHandleL - The object is being moved, use its cache entry for check"));
            		entry = iDpSingletons.MovingBigFileCache().FileEntry();
            		err = KErrNone;
            		}            	
            	}
            User::LeaveIfError(err);
            }
		
		if (aElementInfo.iElementAttr & EMTPElementAttrWrite)
        	{
        	if (entry.IsReadOnly())
        		{
        		ret = EMTPRespCodeObjectWriteProtected;
        		}
        	}
        	
        //((EMTPRespCodeOK == ret) && (aElementInfo.iElementAttr & EMTPElementAttrFileOrDir)) is
        // covered implicitly here, EMTPRespCodeOK will be returned. It is a valid case for an object to be either a folder or file 
        // for certain operation's request parameter, for instance the first parameter of copyObject or
        // moveObject can be either a file or a directory.
        
		// Other cases.
        if ((EMTPRespCodeOK == ret) && (aElementInfo.iElementAttr & EMTPElementAttrFile))
	        	{
	        	if (entry.IsDir())
	        		{
	        		ret = EMTPRespCodeInvalidObjectHandle;
	        		}
	        	}

       	if ((EMTPRespCodeOK == ret) && (aElementInfo.iElementAttr & EMTPElementAttrDir))
	       	{
	       	if (!entry.IsDir())
	       		{
	       		ret = EMTPRespCodeInvalidParentObject;
	       		}        	
	       	}
        	       		         
		 }
	else
		{
		__FLOG(_L8("Object does not exist."));
		ret = EMTPRespCodeInvalidObjectHandle;
		}
	__FLOG_VA((_L8("VerifyObjectHandleL exit with repsonse code 0x%04X"), ret)); 
	return ret;	
	}

/**
Check the storage id parameter in the request, read/write attributes
@param aStorageId	Storage id to be checked.
@param aElementInfo Element info array to be checked against.
@return repsonse code to return to initiator
*/			
TMTPResponseCode CMTPRequestChecker::VerifyStorageIdL(TUint32 aStorageId, const TMTPRequestElementInfo& aElementInfo) const
	{
	MMTPStorageMgr& mgr(iFramework.StorageMgr());
    TMTPResponseCode ret(EMTPRespCodeOK);
    if (!mgr.ValidStorageId(aStorageId))
        {
        ret = EMTPRespCodeInvalidStorageID;
        }
    else if (!mgr.LogicalStorageId(aStorageId))
        {
        ret = EMTPRespCodeStoreNotAvailable;
        }
    else 
        {
        TInt drive(mgr.DriveNumber(aStorageId));
        // StorageIDs which are not system owned do not correspond to drives.
        if (drive != KErrNotFound)
            {
    		TDriveInfo info;
    		User::LeaveIfError(iFramework.Fs().Drive(info, drive));
    		if (info.iType == EMediaNotPresent)
    		    {
    		    /* 
    		    Race conditions between media ejection and request processing
    		    may result in a previously valid storage ID no longer being 
    		    available.
    		    */
    		    ret = EMTPRespCodeStoreNotAvailable;
    		    }
    		else if ((aElementInfo.iElementAttr & EMTPElementAttrWrite) &&
    		            ((info.iMediaAtt & KMediaAttWriteProtected) ||
    		             (info.iDriveAtt & KDriveAttRom)))
    		    {
        		ret = EMTPRespCodeStoreReadOnly;
    		    }
            }
        }
    return ret; 	
	}
	
/**
Check the format code parameter in the request,
@param aStorageId	aFormatCode to be checked.
@param aElementInfo Element info array to be checked against.
@return repsonse code to return to initiator
*/
TMTPResponseCode CMTPRequestChecker::VerifyFormatCode(TUint32 aFormatCode, const TMTPRequestElementInfo& aElementInfo) const
	{
    TMTPResponseCode ret = EMTPRespCodeInvalidObjectFormatCode; 

	if (aElementInfo.iElementAttr == EMTPElementAttrFormatEnums)
		{
		TUint32* formatArray = (TUint32*)(aElementInfo.iValue1);
		TUint32 i = 0;
		for(i = 0; i < aElementInfo.iValue2; i++)
			{
			if (aFormatCode == formatArray[i])
				{
				ret = EMTPRespCodeOK;
				break;
				}
			}
		}
	else if (aFormatCode >= EMTPFormatCodePTPStart && aFormatCode <= EMTPFormatCodeMTPEnd)
		{
		ret = EMTPRespCodeOK;
		}
		
    return ret;	
	}

/**
Check if the parameter is one of the special values
@param aParameter	The parameter value in the request
@param aElementInfo	The ElementInfo for the parameter
@return ETrue if the parameter is one of the special values, otherwise, EFalse
*/
TBool CMTPRequestChecker::IsSpecialValue(TUint32 aParameter, const TMTPRequestElementInfo& aElementInfo) const
	{
	TBool result = EFalse;
	switch(aElementInfo.iCount)
		{
		case 1:
			result = (aParameter == aElementInfo.iValue1);
			break;
		case 2:
			result = (aParameter == aElementInfo.iValue1 || aParameter == aElementInfo.iValue2);
			break;
		default:
			break;			
		}
	return result;
	}

/**
Standard c++ constructor
*/	
CMTPRequestChecker::CMTPRequestChecker(MMTPDataProviderFramework& aFramework, MMTPConnection& aConnection)
	:iFramework(aFramework), 
	iConnection(aConnection),
	iHandles(KMTPRequestCheckerHandleGranularity),
	iObjectArray(KMTPRequestCheckerHandleGranularity)
	{
	__FLOG_OPEN(KMTPSubsystem, KComponent);
	}

/**
 Second phase constructor
*/
void CMTPRequestChecker::ConstructL()
	{
	iDpSingletons.OpenL(iFramework);
	}