upnp/upnpstack/serviceframework/src/upnpcontenthandlerscontroller.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:12:20 +0200
changeset 0 f5a58ecadc66
permissions -rw-r--r--
Revision: 201003

/** @file
* Copyright (c) 2007 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:  CUpnpContentHandlersController
 *
*/


#include "upnpcontenthandlerscontroller.h"

#include <xml/parser.h>
#include <xml/xmlparsererrors.h>
#include <xml/matchdata.h>

#include "upnpstring.h"
#include "upnpservice.h"
#include "upnpdevice.h"
#include "upnpdeviceimplementation.h"
#include "upnpsilentdeviceimplementation.h"

#include "upnpserviceliterals.h"
#include "upnpdeviceliterals.h"

#include "upnpcontenthandler.h"
#include "upnpignorecontenthandler.h"
#include "upnpsoapcontenthandler.h"
#include "upnppropertysetcontenthandler.h"
#include "upnpservicecontenthandler.h"
#include "upnpdevicecontenthandler.h"

using namespace Xml;

// CONSTANTS
const TUint KUtfLowMax = 0x1F;
const TUint KUtfHighMin = 0x7F;
const TUint KUtfHighMax = 0xA0;

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::~CUpnpContentHandlersController
// Destructor
// -----------------------------------------------------------------------------
//
CUpnpContentHandlersController::~CUpnpContentHandlersController()
    {
    delete iParser;
    ASSERT( NULL == iStack || iStack->IsEmpty() );
    delete iStack;
    
    iContent.Close();
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::NewL
// Two-phased constructor
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpContentHandlersController* CUpnpContentHandlersController::NewL()
    {
    CUpnpContentHandlersController* controller =
        CUpnpContentHandlersController::NewLC();    
    CleanupStack::Pop( controller );
    return controller;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::NewLC
// Two-phased constructor. Leaves the object on the CleanupStack
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpContentHandlersController* CUpnpContentHandlersController::NewLC()
    {
    CUpnpContentHandlersController* controller =
        new (ELeave) CUpnpContentHandlersController();
    CleanupStack::PushL( controller );
    controller->ConstructL();
    return controller;
    }

// -----------------------------------------------------------------------------
// CleanupResetAndDestroy
// Releases array, and object keapt in it.
// Used as method passed to TCleanupItem object.
// -----------------------------------------------------------------------------
//
LOCAL_C void CleanupResetAndDestroy(TAny* aRPointerArray)
    {
    reinterpret_cast<RPointerArray<CUpnpDescriptionProperty>* >(aRPointerArray)
        ->ResetAndDestroy();
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::UpdateActionWithOKResponseL
// For internal use. Update all necessary information from a SOAP-message
// that should be OK answer (e.g. received by Http 200 OK message).
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::UpdateActionWithOKResponseL(
    CUpnpSoapMessage* aMessage, CUpnpAction* aAction )
    {
    iSoapParser.UpdateActionWithOKResponseL( aMessage, aAction );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::UpdateActionWithErrorResponseL
// For internal use. Update all necessary information from a SOAP-message
// that should contain SOAP error (fault) message (eg. received by http
// 500 InternalServerError message.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::UpdateActionWithErrorResponseL(
    CUpnpSoapMessage* aMessage, CUpnpAction* aAction )
    {
    iSoapParser.UpdateActionWithErrorResponseL( aMessage, aAction );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::UpdateActionWithRequestL
// Update action with all necessary information from
// a SOAP request message.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::UpdateActionWithRequestL(
    CUpnpSoapMessage* aMessage, CUpnpAction* aAction )
    {
    iSoapParser.UpdateActionWithRequestL( aMessage, aAction );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::AttachL
// For internal use. Attaches the information contained in a GENA buffer
// to this service. In practice, this means handling of received GENA messages.
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::AttachL( const TDesC8& aGenaNotify,
                                                       CUpnpService& aService )
    {
    RPointerArray<CUpnpDescriptionProperty> parsedValues;
    CleanupStack::PushL( TCleanupItem( CleanupResetAndDestroy, &parsedValues ) );

    TRAPD( parseError, ParseGenaL( aGenaNotify, parsedValues ) );
    if ( KErrNone != parseError )
        {
        User::Leave( KErrGeneral );
        }
    for ( TInt i(0); i < parsedValues.Count(); ++i )
        {
        CUpnpDescriptionProperty* property = parsedValues[i];
        aService.SetStateVariableL( property->Name(), UpnpString::Trim( property->Value()) );
        }
    CleanupStack::PopAndDestroy( &parsedValues );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseSoapL
// Parses a xml document and returns set objects of CUpnpDescriptionProperties
// It is used to parse action messages (soap)
// @pre description property array should be leave safe
// (there will be PopAndDestroy called on it in case of leave)
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::ParseSoapL(
        const TDesC8& aDescription,
        RPointerArray<CUpnpDescriptionProperty>& aParsedValues )
    {
    CUpnpSoapContentHandler* soapCH =
        CUpnpSoapContentHandler::NewL( *this, aParsedValues );
    iCorrectUri.Set( KSoapNamespaceUri );
    ParseXmlL( aDescription, soapCH );
    delete soapCH;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseParseDescriptionPropertiesL
// Parses a xml document and returns set objects of CUpnpDescriptionProperties
// It is used to parse event notification
// @pre description property array should be leave safe
// (there will be PopAndDestroy called on it in case of leave)
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::ParseGenaL(
        const TDesC8& aDescription,
        RPointerArray<CUpnpDescriptionProperty>& aParsedValues )
    {
    CUpnpPropertysetContentHandler* propsetCH =
        CUpnpPropertysetContentHandler::NewL( *this, aParsedValues );
    iCorrectUri.Set( KEventNamespaceUri );
    ParseXmlL( aDescription, propsetCH );
    delete propsetCH;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseServiceL
// Parses a xml document and returns object of CUpnpService class
// It is used for the first time the xml document is parsed
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpService* CUpnpContentHandlersController::ParseServiceL(
        const TDesC8& aDescription, CUpnpDevice* aParentDevice )
    {
    CUpnpServiceContentHandler* serviceCH =
        CUpnpServiceContentHandler::NewL( *this, aParentDevice );
    iCorrectUri.Set( KServiceNamespaceUri );
    ParseXmlL( aDescription, serviceCH );
    CUpnpService* result = serviceCH->ResultService();
    delete serviceCH;
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseServiceL
// Parses an xml document
// It is used in case when CUpnpService object already exists
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::ParseServiceL(
        const TDesC8& aDescription, CUpnpService* aService )
    {
    CUpnpServiceContentHandler* serviceCH =
        CUpnpServiceContentHandler::NewL( *this, *aService );
    iCorrectUri.Set( KServiceNamespaceUri );
    ParseXmlL(aDescription, serviceCH);
    delete serviceCH;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseDeviceL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used for the first time the device xml document is parsed
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDevice* CUpnpContentHandlersController::ParseDeviceL(
    const TDesC8& aDescription )
    {
    return DoParseDeviceL( aDescription, (CUpnpDevice*)NULL );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseDeviceL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used in case when CUpnpDevice object already exists and update of
// device xml is required
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::ParseDeviceL(
    const TDesC8& aDescription, CUpnpDevice* aDevice )
    {
    ASSERT( NULL != aDevice );
    DoParseDeviceL( aDescription, aDevice );
    }

template<class T> T* CUpnpContentHandlersController::DoParseDeviceL(
    const TDesC8& aDescription, T* aDevice )
    {
    CUpnpDeviceContentHandler* deviceCH = CUpnpDeviceContentHandler::NewL(
        *this, aDevice );
    iCorrectUri.Set( KDeviceNamespaceUri );
    ParseXmlL( aDescription, deviceCH );
    T* result(NULL);
    if ( !aDevice )
        {
        deviceCH->GetResultDevice( result );
        }
    delete deviceCH;
    return result;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseDeviceImplL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used for the first time the device xml document is parsed
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpDeviceImplementation* CUpnpContentHandlersController::ParseDeviceImplL(
    const TDesC8& aDescription )
    {
    return DoParseDeviceL( aDescription, (CUpnpDeviceImplementation*)NULL );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseDeviceImplL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used in case when CUpnpDeviceImplementation object already exists and
// update of device xml is required
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::ParseDeviceImplL(
    const TDesC8& aDescription, CUpnpDeviceImplementation* aDeviceImpl )
    {
    ASSERT( NULL != aDeviceImpl );
    DoParseDeviceL( aDescription, aDeviceImpl );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseSilentDeviceImplL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used for the first time the device xml document is parsed
// -----------------------------------------------------------------------------
//
EXPORT_C CUpnpSilentDeviceImplementation* CUpnpContentHandlersController::ParseSilentDeviceImplL(
    const TDesC8& aDescription )
    {
    return DoParseDeviceL( aDescription, (CUpnpSilentDeviceImplementation*)NULL );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseSilentDeviceImplL
// Parses an xml document and returns instance of CUpnpDevice class
// It is used in case when CUpnpSilentDeviceImplementation object already exists and
// update of device xml is required
// -----------------------------------------------------------------------------
//
EXPORT_C void CUpnpContentHandlersController::ParseSilentDeviceImplL(
    const TDesC8& aDescription, CUpnpSilentDeviceImplementation* aDeviceImpl )
    {
    ASSERT( NULL != aDeviceImpl );
    DoParseDeviceL( aDescription, aDeviceImpl );
    }

// -----------------------------------------------------------------------------
// ReleaseHandlersOnStack
// Releases all handlers that are on CStack<CUpnpContentHandler, ETrue> object
// Used as method passed to TCleanupItem object.
// -----------------------------------------------------------------------------
//
LOCAL_C void ReleaseHandlersOnStack(TAny* aStack)
    {
    reinterpret_cast<CStack<CUpnpContentHandler, ETrue>*>(aStack)->Clear();
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ParseXmlL
// Parses a xml document by passed content handler,
// there is a guarantee that aHandlerToUse will be deleted in case of leave
// precondition: iStack is empty
// postcondition: iStack is empty
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::ParseXmlL( const TDesC8& aDescription,
    CUpnpContentHandler* aHandlerToUse )
    {
    ASSERT( iStack->IsEmpty() );
    iCurrentContentHandler = aHandlerToUse;
    iStack->PushL( aHandlerToUse );
    CleanupStack::PushL( TCleanupItem( ReleaseHandlersOnStack, iStack ) );
    iDocStarted = EFalse;
    TRAPD( parseError, Xml::ParseL( *iParser, aDescription ) );
    if ( EXmlSyntax == parseError || EXmlInvalidToken == parseError ) 
        {
        while ( iStack->Count() > 1 )
            {
            delete iStack->Pop();
            }
        aHandlerToUse->ResetState();
        iCurrentContentHandler = aHandlerToUse;   
        iDocStarted = EFalse;
        RBuf8 fixedDes;
        fixedDes.CreateL( aDescription );
        CleanupClosePushL( fixedDes );                
        RemoveForbiddenCharacters( fixedDes );        
        TRAP( parseError, Xml::ParseL( *iParser, fixedDes ) );
        CleanupStack::PopAndDestroy( &fixedDes );
        }
    
    if ( EXmlJunkAfterDocElement != parseError ) 
        {
        User::LeaveIfError( parseError );
        }
    else
        {
        //EXmlJunkAfterDocElement must not be ignored when root isn't complete
        if ( !iDocStarted || iStack->Count() != 1 )
            {
            User::Leave( KErrArgument );     //no valid root element were found
            }
        }
    ASSERT( iStack->Count() == 1 );
    ASSERT( iStack->Head() == iCurrentContentHandler && iCurrentContentHandler == aHandlerToUse );
    iStack->Pop();
    CleanupStack::Pop( iStack );
    ASSERT( iStack->IsEmpty() );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::RemoveForbiddenCharacters
// Removes forbidden characters
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::RemoveForbiddenCharacters( TDes8& aDescription )
    {    
    for( TInt i=0; i<aDescription.Length(); i++ )
        {
        if ( ( aDescription[i] <= KUtfLowMax ) ||
            ( aDescription[i] >= KUtfHighMin && aDescription[i] <= KUtfHighMax ) )
            {
            aDescription.Delete( i, 1 );
            i--;
            }
        }
    }
    
// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::CUpnpContentHandlersController
// Default C++ constructor
// -----------------------------------------------------------------------------
//
CUpnpContentHandlersController::CUpnpContentHandlersController()
    : iSoapParser( *this )
    {
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ConstructL
// 2nd phase constructor, dedicated to be used by inherited classs
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::ConstructL()
    {
    _LIT8( KXmlMimeType, "text/xml" );
    _LIT8( KLibxml2, "libxml2" );
    CMatchData *matchData = CMatchData::NewL();
    CleanupStack::PushL( matchData );
    matchData->SetMimeTypeL( KXmlMimeType );
    matchData->SetVariantL( KLibxml2 );
    iParser = CParser::NewL( *matchData, *this );
    CleanupStack::PopAndDestroy( matchData );
    iStack = new (ELeave) CStack<CUpnpContentHandler,ETrue>();
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnStartDocumentL
// This method is a callback to indicate the start of the document.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnStartDocumentL(
    const RDocumentParameters& /*aDocParam*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    iDocStarted = ETrue;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnEndDocumentL
// This method is a callback to indicate the end of the document.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnEndDocumentL( TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnStartElementL
// This method is a callback to indicate an element has been parsed.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnStartElementL(
    const RTagInfo& aElement, const RAttributeArray& aAttributes,
    TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    ChunksMergingEndL();
    if ( iCurrentContentHandler->InterestedInAllNamespaces()
            || aElement.Uri().DesC().Compare( iCorrectUri ) == 0 )
        {
        iCurrentContentHandler->OnStartElementL( aElement, aAttributes );
        }
    else
        {
        SetCurrentContentHandlerL( CUpnpIgnoreContentHandler::NewL( *this ) );
        }
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnEndElementL
// This method is a callback to indicate the end of the element has been reached.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnEndElementL( const RTagInfo& aElement,
    TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    ChunksMergingEndL();
    iCurrentContentHandler->OnEndElementL( aElement );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnContentL
// This method is a callback that sends the content of the element.
// Not all the content may be returned in one go. The data may be sent in chunks.
// This method just append chunk to chunks previously received. When we get all
// chunks ChunksMergingEndL method pass merged chunks to current content handler.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnContentL( const TDesC8& aBytes,
    TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    const TInt newLength = iContent.Length() + aBytes.Length();
    if ( iContent.MaxLength() < newLength )
        {
        iContent.ReAllocL( newLength );
        }
    iContent.Append( aBytes );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::ChunksMergingEndL
// Method that is called when all chunks that contain current content
// were received, so we can pass merged content to current content handler
// as an argument of OnContentL method
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::ChunksMergingEndL()
    {
    if ( iContent.Length() > 0 )
        {
        TLex8 lex(iContent);
        lex.SkipSpace();
        if ( !lex.Eos() ) 
            {
            iCurrentContentHandler->OnContentL( iContent );
            }
        iContent.Zero();
        }
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnStartPrefixMappingL
// This method is a notification of the beginning of the scope of a prefix-URI Namespace mapping.
// This method is always called before the corresponding OnStartElementL method.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnStartPrefixMappingL(
    const RString& /*aPrefix*/, const RString& /*aUri*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnEndPrefixMappingL
// This method is a notification of the end of the scope of a prefix-URI mapping.
// This method is called after the corresponding DoEndElementL method.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnEndPrefixMappingL(
    const RString& /*aPrefix*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnIgnorableWhiteSpaceL
// This method is a notification of ignorable whitespace in element content.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnIgnorableWhiteSpaceL(
    const TDesC8& /*aBytes*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnSkippedEntityL
// This method is a notification of a skipped entity. If the parser encounters an
// external entity it does not need to expand it - it can return the entity as aName
// for the client to deal with.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnSkippedEntityL(
    const RString& /*aName*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnProcessingInstructionL
// This method is a receive notification of a processing instruction.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnProcessingInstructionL(
    const TDesC8& /*aTarget*/, const TDesC8& /*aData*/, TInt aErrorCode )
    {
    User::LeaveIfError( aErrorCode );
    ChunksMergingEndL();
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::OnError
// This method indicates an error has occurred.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::OnError( TInt /*aErrorCode*/)
    {
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::GetExtendedInterface
// This method obtains the interface matching the specified uid.
// -----------------------------------------------------------------------------
//
TAny* CUpnpContentHandlersController::GetExtendedInterface( const TInt32 /*aUid*/)
    {
    return 0;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::SetCurrentContentHandlerL
// Sets ContentHandler argument as a current content handler, so it will
// receive parsing events. Previous content handler will be push on stack
// that it could be againt current content handler after calling of
// SetPreviousContentHandler.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::SetCurrentContentHandlerL(
    CUpnpContentHandler* aNewContentHandler )
    {
    ASSERT( NULL != aNewContentHandler );
    iStack->PushL( aNewContentHandler );
    iCurrentContentHandler = aNewContentHandler;
    }

// -----------------------------------------------------------------------------
// CUpnpContentHandlersController::SetPreviousContentHandler
// Deletes current content handler, and set previous content handler as current
// contetnt handler.
// -----------------------------------------------------------------------------
//
void CUpnpContentHandlersController::SetPreviousContentHandler()
    {
    ASSERT( iStack->Count() > 1 );
    delete iStack->Pop();
    iCurrentContentHandler = iStack->Head();
    }

//  End of File