diff -r 000000000000 -r f5a58ecadc66 upnp/upnpstack/serviceframework/src/upnpcontenthandlerscontroller.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/upnp/upnpstack/serviceframework/src/upnpcontenthandlerscontroller.cpp Tue Feb 02 01:12:20 2010 +0200 @@ -0,0 +1,645 @@ +/** @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 +#include +#include + +#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* >(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 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& 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& 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 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 object +// Used as method passed to TCleanupItem object. +// ----------------------------------------------------------------------------- +// +LOCAL_C void ReleaseHandlersOnStack(TAny* aStack) + { + reinterpret_cast*>(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= 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(); + } + +// ----------------------------------------------------------------------------- +// 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