diff -r 826cea16efd9 -r 13a33d82ad98 dvrengine/CommonRecordingEngine/src/CCRRTSPCommand.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvrengine/CommonRecordingEngine/src/CCRRTSPCommand.cpp Wed Sep 01 12:20:37 2010 +0100 @@ -0,0 +1,761 @@ +/* +* 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 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: RTSP command parser and producer* +*/ + + + + +// INCLUDE FILES +#include "CCRRtspCommand.h" +#include "CCRSock.h" +#include +#include +#include "videoserviceutilsLogger.h" + +// CONSTANTS +const TReal KRealZero( 0.0 ); +const TReal KRealMinusOne( -1.0 ); +// Length of a digest hash before converting to hex. +const TInt KCRRawHashLength( 16 ); +// Length of a digest hash when represented in hex +const TInt KCRHashLength( 32 ); + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CCRRtspCommand* CCRRtspCommand::NewL() + { + CCRRtspCommand* self = new( ELeave ) CCRRtspCommand(); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::CCRRtspCommand +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +// +CCRRtspCommand::CCRRtspCommand() + : iCommand( ERTSPCommandNOCOMMAND ) + { + // None + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::ConstructL() + { + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::~CCRRtspCommand +// Destructor. +// ----------------------------------------------------------------------------- +// +CCRRtspCommand::~CCRRtspCommand() + { + LOG( "CCRRtspCommand::~CCRRtspCommand" ); + + // iRtspText is deleted in base class destructor + delete iAuthHeader; + delete iMD5Calculator; + delete iUserAgent; + delete iWapProfile; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::TryParseL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::TryParseL( const TDesC8 &aString ) + { + // try to find out if end of the command has been received + // "RTSP/1.0 XXX\r\n\r\n" at least.. + const TInt KDVRMinCommandLen( 14 ); + TInt replyEndOffSet( ( aString.Length() < KDVRMinCommandLen )? + KDVRMinCommandLen: aString.Find( KCR2NewLines() ) ); + if ( replyEndOffSet == KErrNotFound ) + { + // need to have more, do nothing yet + LOG( "CCRRtspCommand::TryParseL() out because response not complete" ); + User::Leave( KErrUnderflow ); + } + + // copy the stuff into local variable: + delete iRtspText; iRtspText = NULL; + iRtspText = aString.AllocL(); + iCommand = ERTSPCommandNOCOMMAND; + + // try each command in order: + if ( iRtspText->Find( KCROPTIONS() ) == 0 ) + { + // it was OPTIONS command + LOG( "CCRRtspCommand::TryParseL() -> OPTIONS" ); + iCommand = ERTSPCommandOPTIONS; + } + else if ( iRtspText->Find( KCRDESCRIBE() ) == 0 ) + { + LOG( "CCRRtspCommand::TryParseL() -> DESCRIBE" ); + iCommand = ERTSPCommandDESCRIBE; + } + else if ( iRtspText->Find( KCRTEARDOWN() ) == 0 ) + { + LOG( "CCRRtspCommand::TryParseL() -> TEARDOWN" ); + iCommand = ERTSPCommandTEARDOWN; + } + else if ( iRtspText->Find( KCRPAUSE() ) == 0 ) + { + LOG( "CCRRtspCommand::TryParseL() -> PAUSE" ); + iCommand = ERTSPCommandPAUSE; + } + else if ( iRtspText->Find( KCRSETUP() ) == 0 ) + { + LOG( "CCRRtspCommand::TryParseL() -> SETUP" ); + iCommand = ERTSPCommandSETUP; + } + else if ( iRtspText->Find( KCRPLAY() ) == 0 ) + { + LOG( "CCRRtspCommand::TryParseL() -> PLAY" ); + iCommand = ERTSPCommandPLAY; + } + else + { + User::Leave( KErrNotSupported ); + } + + // then find CSeq + FindCSeqL(); + // then find session id + FindSessionIdL(); + // then find possible content. for commands it is usually not there + FindContentL(); + // find URL + FindURLL(); + // find possible transport method + // IMPORTANT: should be done before parsing client port + FindTransport(); + // find possible client port + FindClientPorts(); + // find possible range-header + ParseRange(); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::FindURLL +// in rtsp the URL is between first and second whitespace. +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::FindURLL( void ) + { +#ifdef _DEBUG + _LIT( KPanicStr, "RTSPCommon" ); + __ASSERT_DEBUG( iRtspText, + User::Panic( KPanicStr, KErrBadHandle ) ); +#endif + + iURL.Set( NULL, 0 ); + TInt spaceOffset( iRtspText->Locate( ' ' ) ); + if ( spaceOffset < 0 ) + { + User::Leave( KErrNotSupported ); + } + + TPtrC8 beginningFromUrl( iRtspText->Right( iRtspText->Length() - + ( spaceOffset + 1 ) ) ) ; + spaceOffset = beginningFromUrl.Locate( ' ' ); + if ( spaceOffset < 0 ) + { + User::Leave( KErrNotSupported ); + } + + iURL.Set( beginningFromUrl.Left( spaceOffset ) ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::URL +// +// method that returns URL +// ----------------------------------------------------------------------------- +// +TInt CCRRtspCommand::URL( TPtrC8& aURL ) + { + if ( iURL.Ptr() != NULL ) + { + aURL.Set ( iURL ); + return KErrNone; + } + + return KErrNotFound; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetURL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetURL ( const TDesC8& aURL ) + { + iURL.Set ( aURL ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetAuthentication +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetAuthentication( TBool aAuth ) + { + iAuthenticationNeeded = aAuth; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetUserAgentL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetUserAgentL( const TDesC8& aUserAgent ) + { + delete iUserAgent; iUserAgent = NULL; + iUserAgent = aUserAgent.AllocL(); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetBandwidth +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetBandwidth( TInt aBandwidth ) + { + iBandwidth = aBandwidth; + iBandwidthAvailable = ETrue; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetWapProfileL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetWapProfileL( const TDesC8& aWapProfile ) + { + delete iWapProfile; iWapProfile = NULL; + iWapProfile = aWapProfile.AllocL(); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::SetCommand +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::SetCommand( TCommand aCommand ) + { + iCommand = aCommand; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::Command +// +// ----------------------------------------------------------------------------- +// +CCRRtspCommand::TCommand CCRRtspCommand::Command( void ) const + { + return iCommand; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::ProduceL +// +// ----------------------------------------------------------------------------- +// +TPtrC8& CCRRtspCommand::ProduceL( void ) + { + // First common part for all commands, except actual command + delete iRtspText; iRtspText = NULL; + iRtspText = HBufC8::NewL( KMaxName ); + iRtspText->Des().Zero(); + + switch ( iCommand ) + { + case ERTSPCommandOPTIONS: + AppendL( iRtspText, KCROPTIONS ); + break; + + case ERTSPCommandDESCRIBE: + AppendL( iRtspText, KCRDESCRIBE ); + break; + + case ERTSPCommandTEARDOWN: + AppendL( iRtspText, KCRTEARDOWN ); + break; + + case ERTSPCommandPAUSE: + AppendL( iRtspText, KCRPAUSE ); + break; + + case ERTSPCommandSETUP: + AppendL( iRtspText, KCRSETUP ); + break; + + case ERTSPCommandPLAY : + AppendL( iRtspText, KCRPLAY ); + break; + + default: + User::Leave( KErrNotSupported ); + break; + } + + AppendL( iRtspText, iURL ); + AppendL( iRtspText, KCRSpace ); + AppendL( iRtspText, KCRRTSP10 ); + AppendL( iRtspText, KCRNewLine ); + AppendL( iRtspText, KCRCSeq ); + AppendNumL( iRtspText, iCSeq ); + AppendL( iRtspText, KCRNewLine ); + + if ( iUserAgent ) + { + TPtrC8 useragent( iUserAgent->Des() ); + AppendFormatL( iRtspText, KCRRTSPUserAgentHeader, &useragent ); + } + + // then variable tail depending on command + switch ( iCommand ) + { + case ERTSPCommandOPTIONS: + if ( iSessionId.Ptr() != NULL ) + { + AppendL( iRtspText, KCRSessionStr() ); + AppendL( iRtspText, iSessionId ); // now only session number + AppendL( iRtspText, KCRNewLine ); + } + break; + + case ERTSPCommandDESCRIBE: + { + AppendL( iRtspText, KCRAcceptSDP ); + if ( iWapProfile ) + { + TPtrC8 profile = iWapProfile->Des(); + AppendFormatL( iRtspText, KCRRTSPXWapProfile, &profile ); + } + if ( iBandwidthAvailable ) + { + AppendFormatL( iRtspText, KCRRTSPBandwidth, iBandwidth ); + } + } + break; + + case ERTSPCommandTEARDOWN: + if ( iSessionId.Ptr() != NULL ) + { + AppendL( iRtspText, KCRSessionStr() ); + AppendL( iRtspText, iSessionId ); // now only session number + AppendL( iRtspText, KCRNewLine ); + } + break; + + case ERTSPCommandPAUSE: + if ( iSessionId.Ptr() != NULL ) + { + AppendL( iRtspText, KCRSessionStr() ); + AppendL( iRtspText, iSessionId ); // now only session number + AppendL( iRtspText, KCRNewLine ); + } + break; + + case ERTSPCommandSETUP: + { + // build transport header according to chosen method + switch ( iTransport ) + { + case ERTPOverUDP: + AppendFormatL( iRtspText, KCRTransportHeaderUDP, + iClientPort, iClientPort + 1 ); + break; + case ERTPOverTCP: + AppendFormatL( iRtspText, KCRTransportHeaderTCP, + iClientPort, iClientPort + 1 ); + break; + + case ERTPOverMulticast: + AppendL( iRtspText, KCRTransportHeaderMulticast ); + break; + } + + // Session: 5273458854096827704 + if ( iSessionId.Ptr() != NULL ) + { + AppendL( iRtspText, KCRSessionStr ); + AppendL( iRtspText, iSessionId ); // now only session number + AppendL( iRtspText, KCRNewLine ); + } + if ( iWapProfile ) + { + TPtrC8 profile( iWapProfile->Des() ); + AppendFormatL( iRtspText, KCRRTSPXWapProfile, &profile ); + } + } + break; + + case ERTSPCommandPLAY: + { + if ( !( iLowerRange == KRealZero && iUpperRange == KRealMinusOne ) ) + { + // Range was something else than 0,-1 + TBuf8 buf( KCRRangeHeader ); + TRealFormat format( 10, 3 ); + format.iTriLen = 0; + format.iPoint = '.'; + buf.AppendNum( iLowerRange, format ); + buf.Append( '-' ); + if ( iUpperRange > KRealZero ) + { + buf.AppendNum( iUpperRange, format ); + } + + buf.Append( KCRNewLine ); + AppendL( iRtspText, buf ); + } + if ( iSessionId.Ptr() != NULL ) + { + AppendL( iRtspText, KCRSessionStr() ); + AppendL( iRtspText, iSessionId ); // now only session number + AppendL( iRtspText, KCRNewLine ); + } + } + break; + + default: + User::Leave( KErrNotSupported ); + break; + } + + if ( iAuthenticationNeeded ) + { + TBool useDigest( EFalse ); + + if ( iAuthType && ( iAuthType->FindC( KCRAuthDigest ) != KErrNotFound ) ) + { + useDigest = ETrue; + } + + switch ( iCommand ) + { + case ERTSPCommandOPTIONS : + useDigest ? CalculateDigestResponseL( KCROPTIONSNoSpace ) : + CalculateBasicResponseL( KCROPTIONSNoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + case ERTSPCommandDESCRIBE : + useDigest ? CalculateDigestResponseL( KCRDESCRIBENoSpace ) : + CalculateBasicResponseL( KCRDESCRIBENoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + case ERTSPCommandTEARDOWN : + useDigest ? CalculateDigestResponseL( KCRTEARDOWNNoSpace ) : + CalculateBasicResponseL( KCRTEARDOWNNoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + case ERTSPCommandPAUSE : + useDigest ? CalculateDigestResponseL( KCRPAUSENoSpace ) : + CalculateBasicResponseL( KCRPAUSENoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + case ERTSPCommandSETUP : + useDigest ? CalculateDigestResponseL( KCRSETUPNoSpace ) : + CalculateBasicResponseL( KCRSETUPNoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + case ERTSPCommandPLAY : + useDigest ? CalculateDigestResponseL( KCRPLAYNoSpace ) : + CalculateBasicResponseL( KCRPLAYNoSpace ); + + if ( iAuthHeader ) + { + AppendL( iRtspText, iAuthHeader->Des() ); + } + break; + + default: + User::Leave( KErrNotSupported ); + break; + } + } + + AppendL( iRtspText, KCRNewLine ); + iProductDescriptor.Set( *iRtspText ); + return iProductDescriptor; + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::AppendL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::AppendL( HBufC8*& aBuffer, const TDesC8& aStr ) + { + TPtr8 ptr( aBuffer->Des() ); + if ( ( ptr.Length() + aStr.Length() ) >= ptr.MaxLength() ) + { + const TInt newLength( ptr.Length() + aStr.Length() + KMaxName ); + aBuffer = aBuffer->ReAllocL( newLength ); + ptr.Set( aBuffer->Des() ); + } + + ptr.Append( aStr ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::AppendNumL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::AppendNumL( HBufC8*& aBuffer, const TInt aNum ) + { + TPtr8 ptr( aBuffer->Des() ); + if ( ( ptr.Length() + KMaxInfoName ) >= ptr.MaxLength() ) + { + const TInt newLength( ptr.Length() + KMaxInfoName + KMaxName ); + aBuffer = aBuffer->ReAllocL( newLength ); + ptr.Set( aBuffer->Des() ); + } + + ptr.AppendNum( aNum ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::AppendFormatL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::AppendFormatL( + HBufC8*& aBuffer, + TRefByValue aFmt, ... ) + { + VA_LIST list; + VA_START( list, aFmt ); + HBufC8* buf = HBufC8::NewLC( KMaxDataSize ); + buf->Des().FormatList( aFmt, list ); + VA_END( list ); + + TPtr8 ptr( aBuffer->Des() ); + if ( ( ptr.Length() + buf->Length() ) >= ptr.MaxLength() ) + { + const TInt newLength( ptr.Length() + buf->Length() + KMaxName ); + aBuffer = aBuffer->ReAllocL( newLength ); + ptr.Set( aBuffer->Des() ); + } + + ptr.Append( *buf ); + CleanupStack::PopAndDestroy( buf ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::Hash +// Calculates hash value ( from S60 HttpFilters ) +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::HashL( const TDesC8& aMessage, TDes8& aHash ) + { + LOG( "CCRRtspCommand::HashL() in" ); + // check if md5 calculator is already constructed + if ( !iMD5Calculator ) + { + iMD5Calculator = CMD5::NewL(); + } + // Calculate the 128 bit (16 byte) hash + iMD5Calculator->Reset(); + TPtrC8 hash = iMD5Calculator->Hash( aMessage ); + + // Now print it as a 32 byte hex number + aHash.Zero(); + _LIT8( formatStr, "%02x" ); + TBuf8<2> scratch; + for ( TInt i( 0 ); i < KCRRawHashLength; i++ ) + { + scratch.Zero(); + scratch.Format( formatStr, hash[i] ); + aHash.Append( scratch ); + } + + LOG( "CCRRtspCommand::HashL() out" ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::CalculateBasicResponseL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::CalculateBasicResponseL( const TDesC8& /*aMethod*/ ) + { + LOG( "CCRRtspCommand::CalculateBasicResponseL() in" ); + + if ( !( iUserName && iPassword ) ) + { + LOG( "CCRRtspCommand::CalculateBasicResponseL() out, username or password not set" ); + delete iAuthHeader; + iAuthHeader = NULL; + return; // no can do + } + + HBufC8* plainData = HBufC8::NewL( iUserName->Length() + 1 + // ':' + iPassword->Length() ); + + CleanupStack::PushL( plainData ); + + plainData->Des().Append( *iUserName ); + plainData->Des().Append( ':' ); + plainData->Des().Append( *iPassword ); + + // Max size = ((Bytes + 3 - (Bytes MOD 3)) /3) x 4 + TInt base64MaxSize = ( ( plainData->Length() + 3 - + ( plainData->Length() % 3 ) ) / 3 ) * 4; + + HBufC8* encodedData = HBufC8::NewL( base64MaxSize ); + TPtr8 dataPrt( encodedData->Des() ); + CleanupStack::PushL( encodedData ); + + TImCodecB64 b64enc; + b64enc.Initialise(); + b64enc.Encode( *plainData, dataPrt ); + + delete iAuthHeader; + iAuthHeader = NULL; + iAuthHeader = HBufC8::NewL( KCRAuthorizationBasicHeader().Length() + encodedData->Length() ); + iAuthHeader->Des().Format( KCRAuthorizationBasicHeader, encodedData ); + + CleanupStack::PopAndDestroy( encodedData ); + CleanupStack::PopAndDestroy( plainData ); + + LOG( "CCRRtspCommand::CalculateBasicResponseL() out" ); + } + +// ----------------------------------------------------------------------------- +// CCRRtspCommand::CalculateDigestResponseL +// +// ----------------------------------------------------------------------------- +// +void CCRRtspCommand::CalculateDigestResponseL( const TDesC8& aMethod ) + { + LOG( "CCRRtspCommand::CalculateDigestResponseL() in" ); + + if ( !( iUserName && iPassword && iNonce && iOpaque && iRealm && iUri ) ) + { + LOG( "CCRRtspCommand::CalculateDigestResponseL() out, username or password not set" ); + delete iAuthHeader; iAuthHeader = NULL; + return; // no can do + } + + TBuf8 hash1; + TBuf8 hash2; + TBuf8 finalHash; + + // calculate the hash1 using "username:realm:password" + HBufC8* hashPtr = HBufC8::NewL ( iUserName->Length() + 1 + // ':' + iRealm->Length() + 1 + // ':' + iPassword->Length() ); + hashPtr->Des().Append( *iUserName ); + hashPtr->Des().Append( ':' ); + hashPtr->Des().Append( *iRealm ); + hashPtr->Des().Append( ':' ); + hashPtr->Des().Append( *iPassword ); + + HashL( *hashPtr, hash1 ); + delete hashPtr; hashPtr = NULL; + + // calculate hash2 using "Method:uri" + HBufC8* hashPtr2 = HBufC8::NewL(aMethod.Length() + 1 + iUri->Length() ); + hashPtr2->Des().Append( aMethod ); + hashPtr2->Des().Append( ':' ); + hashPtr2->Des().Append( *iUri ); + + HashL( *hashPtr2, hash2 ); + delete hashPtr2; hashPtr2 = NULL; + + // calculate finalHash to be sent to remote server using + // hash1 + ":" + nonce + ":" + hash2 + HBufC8* hashPtr3 = HBufC8::NewL( hash1.Length() + 1 + // ':' + iNonce->Length() + 1 + // ':' + hash2.Length() ); + hashPtr3->Des().Append( hash1 ); + hashPtr3->Des().Append( ':' ); + hashPtr3->Des().Append( *iNonce ); + hashPtr3->Des().Append( ':' ); + hashPtr3->Des().Append( hash2 ); + + HashL( *hashPtr3, finalHash ); + delete hashPtr3; hashPtr3 = NULL; + + // generate the authentication header + if ( iOpaque->Length() ) + { + delete iAuthHeader; iAuthHeader = NULL; + iAuthHeader = HBufC8::NewL( KCRAuthorizationHeader().Length() + + iUserName->Length() + iRealm->Length() + iNonce->Length() + + iUri->Length() + finalHash.Length() + iOpaque->Length() ); + + iAuthHeader->Des().Format( KCRAuthorizationHeader, iUserName, + iRealm, iNonce, iUri, &finalHash, iOpaque ); + } + else + { + delete iAuthHeader; iAuthHeader = NULL; + iAuthHeader = HBufC8::NewL( KCRAuthorizationHeaderNoOpaque().Length() + + iUserName->Length() + iRealm->Length() + iNonce->Length() + + iUri->Length() + finalHash.Length() ); + + iAuthHeader->Des().Format( KCRAuthorizationHeaderNoOpaque, iUserName, + iRealm, iNonce, iUri, &finalHash ); + } + + LOG( "CCRRtspCommand::CalculateDigestResponseL() out" ); + } + +// End of File