localconnectivityservice/obexsendservices/obexservicesendutils/src/BTSBPPObjectRequest.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:20:40 +0100
branchRCL_3
changeset 40 52a167391590
parent 0 c3e98f10fcf4
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* Copyright (c) 2002 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:  GetReferencedObjects -operation implementation
*
*/



// INCLUDE FILES
#include "BTServiceUtils.h"
#include "BTSBPPObjectRequest.h"
#include "BTSUDataConverter.h"
#include "BTSUImageConverter.h"
#include "BTSUDebug.h"

#include <f32file.h>    // RFile
#include <s32mem.h>     // RBufWriteStream
#include <s32file.h>    // RFileReadStream

// CONSTANTS
const TInt KBTSBPPFileSizeNotSet         = 0;
const TInt KBTSBPPFileSizeRequested      = 1;
const TInt KBTSBPPFileSizeUnknown        = -1;
const TInt KBTSBPPRestOfTheFileRequested = -1;

// The field lengths of Application Parameters -header in bytes
//
const TInt KBTSBPPFieldTag       = 1;
const TInt KBTSBPPFieldLength    = 1;
const TInt KBTSBPPValue          = 4;
const TInt KBTSBPPAppParamLength = KBTSBPPFieldTag + KBTSBPPFieldLength + KBTSBPPValue;

_LIT8(KBTSBPPRefObjectOper, "x-obex/referencedobject\0");

static const TUint8 KBTSBPPFileSizeTag = 0x04;
static const TUint8 KBTSBPPFileSizeLength = 0x04;

// MODULE DATA STRUCTURES
enum KBTSBPPAppParamTags
    {
    KBTSBPPTagOffset = 1,
    KBTSBPPTagCount,
    KBTSBPPTagJobId, // used only in JobBased-printing
    KBTSBPPTagFileSize
    };

// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CBTSBPPObjectRequest
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CBTSBPPObjectRequest::CBTSBPPObjectRequest( CObexBaseObject* aGetOperation,
    const CDesCArray* aRefObjectList ) : iRequest( aGetOperation ), 
                                         iObjectList( aRefObjectList )
    {
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::ConstructL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ConstructL()"));

    User::LeaveIfError( iFileSession.Connect() );

    // Check the validity of the operation and it's paramters.
    //
    CheckGetRequestL();

    // Execute any tasks needed and create the requested object.
    //
    ExecuteGetRequestL();

    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ConstructL() completed"));
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CBTSBPPObjectRequest* CBTSBPPObjectRequest::NewL( CObexBaseObject* aGetOperation,
                                              const CDesCArray* aRefObjectList )
    {
    CBTSBPPObjectRequest* self = new( ELeave ) CBTSBPPObjectRequest( aGetOperation,
                                                                 aRefObjectList );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();
    return self;
    }

    
// Destructor
CBTSBPPObjectRequest::~CBTSBPPObjectRequest()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::~CBTSBPPObjectRequest()"));
    iFileSession.Close();

    delete iResponse;
    delete iResponseBuffer;

    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::~CBTSBPPObjectRequest() completed"));
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::GetResponse
// -----------------------------------------------------------------------------
//
CObexBufObject* CBTSBPPObjectRequest::GetResponse()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::GetResponse()"));
    return iResponse;
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CheckGetRequestL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::CheckGetRequestL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckGetRequestL()"));

    CheckTypeHeaderL();
    CheckNameHeaderL();
    CheckAppParamsHeaderL();
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CheckTypeHeaderL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::CheckTypeHeaderL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL()"));

    __ASSERT_DEBUG( iRequest, BTSUPanic( EBTSUPanicNullPointer ) );

    const TDesC8& typeHeader = iRequest->Type();

    if ( typeHeader.Length() == 0 )
        {
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL() ERROR, no type header"));
        User::Leave( KErrNotFound );
        }
    else if ( typeHeader.Length() > KBTSUMaxStringLength )
        {
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL() ERROR, type header too long"));
        User::Leave( KErrTooBig );
        }
    else
        {
        FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL() type header '%S'"), &typeHeader ));
        // The only supported operation is GetReferencedObjects
        //
        if ( typeHeader != KBTSBPPRefObjectOper() )
            {
            FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL() ERROR, unsupported operation"));
            User::Leave( KErrNotSupported );
            }
        }

    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckTypeHeaderL() completed"));
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CheckNameHeaderL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::CheckNameHeaderL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckNameHeaderL()"));

    __ASSERT_DEBUG( iRequest, BTSUPanic( EBTSUPanicNullPointer ) );

    const TDesC& nameHeader = iRequest->Name();

    if ( nameHeader.Length() == 0 )
        {
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckNameHeaderL() ERROR, no name header"));
        User::Leave( KErrNotFound );
        }
    else
        {
        FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckNameHeaderL() filename '%S'"), &nameHeader ));

        // Check are we allowed to send this file
        //
        TInt dummy;
        if ( iObjectList->Find( nameHeader, dummy ) != KBTSUEqualStrings )
            {
            FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckNameHeaderL() ERROR, the asked object is not referenced in the document"));
            User::Leave( KErrPermissionDenied );
            }

        iRequestParams.iName = &nameHeader;
        }
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CheckAppParamsHeaderL
//
// This method expects a Tag-Length-Value format. The method is as defensive 
// as possible, for example unknown or illegal values are discarded.
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::CheckAppParamsHeaderL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL()"));

    __ASSERT_DEBUG( iRequest, BTSUPanic( EBTSUPanicNullPointer ) );

    TPtrC8 header( iRequest->AppParam() );    

    if ( header.Length() == 0 )
        {
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() ERROR, no AppParam header"));
        User::Leave( KErrNotFound );
        }
    else if ( header.Length() < KBTSBPPAppParamLength * 2)
        {
        // There has to be at least offset and count -parameters.
        //
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() ERROR, incomplete AppParam header"));
        User::Leave( KErrArgument );
        }
    else
        {
        // Parse the header
        //
        while( header.Length() >= KBTSBPPAppParamLength )
            {
            // Extract tag number and remove it from the header
            //
            TInt tagNumber = TBTSUDataConverter::ConvertByteL(
                header.Left( KBTSBPPFieldTag ) );
            header.Set( header.Mid( KBTSBPPFieldTag ) );

            // Extract value length and remove it from the header
            //
            TInt valueLength = TBTSUDataConverter::ConvertByteL( 
                header.Left( KBTSBPPFieldLength ) );
            header.Set( header.Mid( KBTSBPPFieldLength ) );

            if ( valueLength != KBTSBPPValue )
                {
                FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() WARNING, illegal length %d"), valueLength ));
                valueLength = KBTSBPPValue;
                // This is an interesting choice of what to do, I would argue that for future compatibility you'll probably want to accept the length
                // provided as it might be for a new tag that is not yet published.  (Checking of the size for known tags is handled by the conversion
                // functions anyway).
                // So, in summary I wouldn't change valueLength from what it is in the payload.  But I've left it in for now in case it was added for
                // interoperability reasons with some existing device.
                }

            // Extract value...
            //
            switch ( tagNumber )
                {
                case KBTSBPPTagOffset:
                    {
                    iRequestParams.iOffset = 
                        TBTSUDataConverter::ConvertDataUnsignedL( 
                        header.Left( valueLength ) );

                    FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() offset '%d'"), iRequestParams.iOffset ));
                    break;
                    }
                case KBTSBPPTagCount:
                    {
                    iRequestParams.iCount = 
                        TBTSUDataConverter::ConvertDataSignedL( 
                        header.Left( valueLength ) );

                    FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() count '%d'"), iRequestParams.iCount ));
                    break;
                    }
                case KBTSBPPTagFileSize:
                    {
                    // The value is not interesting, we just need to know whether 
                    // the parameter was received (and thus requested)
                    //
                    iRequestParams.iOffset = KBTSBPPFileSizeRequested;
                    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ParseHeadersL() fileSize parameter received"));
                    break;
                    }

                case KBTSBPPTagJobId:
                default:
                    {
                    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() WARNING, illegal tag"));
                    break;
                    }
                }

            // ...and remove it from the header
            //
            header.Set( header.Mid( valueLength ) );
            }
        }

    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CheckAppParamsHeaderL() completed"));
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::ExecuteGetRequestL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::ExecuteGetRequestL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ExecuteGetRequestL()"));

    // If iCount is 0, an empty body will be sent
    //
    if ( iRequestParams.iCount != 0 )
        {
        ResolveGetRequestL();
        ReadFileToBufferL();      
        }

    CreateResponseObjectL();

    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ExecuteGetRequestL() completed"));
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::ResolveGetRequestL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::ResolveGetRequestL()
    {
    FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::ResolveGetRequestL()"));

    // Get the file size
    //
    RFile file;
    TInt size = 0;
    User::LeaveIfError( file.Open( iFileSession, *iRequestParams.iName, 
                                   EFileShareReadersOnly | EFileStream ) );

    TInt error = file.Size( size );
    
    if ( error )
        {
        FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::ResolveGetRequestL() error %d in getting the size"), error ) );
        size = KBTSBPPFileSizeUnknown;
        }

    file.Close();

    // Store the file size if it was requested
    //
    if ( iRequestParams.iFileSize == KBTSBPPFileSizeRequested )
        {
        iRequestParams.iFileSize = size;
        }

    // Resolve the actual amount of data that needs to be read.
    // 
    TInt dataLeft = size - iRequestParams.iOffset;

    if ( iRequestParams.iCount == KBTSBPPRestOfTheFileRequested )
        {
        if ( size == KBTSBPPFileSizeUnknown )
            {
            // The size of the file is unknown, so read as much 
            // as would fit into the buffer
            //
            iRequestParams.iCount = KBTSUDataBufferMaxSize;
            }        
        else
            {
            // The rest of the file is requested, so correct the count
            //
            iRequestParams.iCount = dataLeft;
            }
        }
    else if ( iRequestParams.iCount > dataLeft )
        {
        // There is less data left that was requested, so correct the count
        //
        iRequestParams.iCount = dataLeft;
        }

    if ( iRequestParams.iCount > KBTSUDataBufferMaxSize )
        {
        // The requested count is too big
        //
        FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::ResolveGetRequestL() iCount too big %d"), iRequestParams.iCount ) );
        User::Leave( KErrTooBig );
        }

    FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::ResolveGetRequestL() completed, requested %d bytes"), iRequestParams.iCount ) );
    }

// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::ReadFileToBufferL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::ReadFileToBufferL()
    {
    __ASSERT_DEBUG( !iResponseBuffer, BTSUPanic( EBTSUPanicExistingObject ) );

    // Create a buffer for the object and reserve space according to the request
    //
    iResponseBuffer = CBufFlat::NewL( KBTSUDataBufferExpandSize );
    iResponseBuffer->SetReserveL( iRequestParams.iCount );
    iResponseBuffer->ResizeL( iRequestParams.iCount );

    // Open the file
    //
    RFile file;
    User::LeaveIfError( file.Open( iFileSession, *iRequestParams.iName, 
                                   EFileShareReadersOnly | EFileStream ) );
    CleanupClosePushL( file );

    // Create a stream for reading from the file
    //
    RFileReadStream readStream( file, iRequestParams.iOffset );

    // Create a stream for writing into the buffer
    //
    RBufWriteStream writeStream( *iResponseBuffer );

    // Read the data from file to the buffer
    //
    readStream.ReadL( writeStream, iRequestParams.iCount );

    FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::ReadFileToBufferL() space reserved '%d'"), iRequestParams.iCount ));
    FTRACE(FPrint(_L("[BTSBPP]\t CBTSBPPObjectRequest::ReadFileToBufferL() data read '%d'"), iResponseBuffer->Size() ));

    CleanupStack::PopAndDestroy(&file);
    }


// -----------------------------------------------------------------------------
// CBTSBPPObjectRequest::CreateResponseObjectL
// -----------------------------------------------------------------------------
//
void CBTSBPPObjectRequest::CreateResponseObjectL()
    {
    __ASSERT_DEBUG( iResponseBuffer || (iRequestParams.iCount == 0), BTSUPanic( EBTSUPanicNoBufferEvenThoughCountNotZero ) );
    __ASSERT_DEBUG( !iResponse, BTSUPanic( EBTSUPanicResponseAlreadyPresent ) );
    
    // Create the OBEX response object using the buffer already created (or no buffer if appropriate)
    //
    iResponse = CObexBufObject::NewL( iResponseBuffer );
    
    // Generate FileSize parameter if requested
    //
    if ( iRequestParams.iFileSize != KBTSBPPFileSizeNotSet )
        {
        FLOG(_L("[BTSBPP]\t CBTSBPPObjectRequest::CreateResponseObjectL() generate file size param"));
        TBuf8<KBTSBPPAppParamLength> params;
        params.SetMax();
        TUint8* ptr = const_cast<TUint8*>(params.Ptr());
        *ptr++ = KBTSBPPFileSizeTag;
        *ptr++ = KBTSBPPFileSizeLength;
        BigEndian::Put32(ptr, *reinterpret_cast<TUint32*>(&iRequestParams.iFileSize)); // reinterpret cast to retain signed nature...
        iResponse->SetAppParamL( params );
        }
    }

//  End of File