diff -r f742655b05bf -r d38647835c2e voipplugins/sipmxresolver/src/sipmxresolver.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/voipplugins/sipmxresolver/src/sipmxresolver.cpp Wed Sep 01 12:29:57 2010 +0100 @@ -0,0 +1,759 @@ +/* +* Copyright (c) 2007-2008 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: implementation of sipmxresolver +* +*/ + + +// INCLUDE FILES +#include "sipmxresolver.h" +#include "csipclientresolverutils.h" +#include +#include +#include + +//for checking CS call status +#include + +//SIP message content parsing APIs +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//For checking dynamic voip status and MuS availability +#include +#include +#include + +//Multimedia Sharing client API +#include + +//Incoming Call Monitor API +#include + + +// CONSTANTS +_LIT8( KFTagChar, "+" ); // All feature tags start with + +_LIT8( KVSFeatureTag, "+g.3gpp.cs-voice" ); // For checking VS specific tag +_LIT8( KPoCFeatureTag, "+g.poc.talkburst" ); // PoC specific tag (for ruling out) +_LIT8( KContentType, "application/sdp" ); // For content-type header +_LIT8( KMediaTypeApp, "application" ); // For content-type header checks +_LIT8( KMediaSubtypeSdp, "sdp" ); // For content-type header checks +_LIT8( KSendOnly, "sendonly" ); // For attribute checks +_LIT8( KApplicationAttr, "application" ); // For attribute checks +_LIT8( KXApplicationAttr, "X-application" ); // For attribute checks +_LIT8( KNokiaRtvs, "com.nokia.rtvs" ); // For attribute checks +_LIT8( KSIPMethodsInAllowHeader, "INVITE,ACK,CANCEL,OPTIONS,BYE,PRACK,SUBSCRIBE,REFER,NOTIFY,UPDATE"); // SIP Methods allowed by various plugins +_LIT8( KSIPExtensionsSupported, "100rel,timer,sec-agree"); //Extensions supported by various plugins + +/** + * Cleanup function for RPointerArray + * Called in case of a leave in SupportedSdpMediasL + */ +void CleanupSdpMediasArray( TAny* aObj ) + { + if ( aObj ) + { + static_cast*>( aObj )->ResetAndDestroy(); + } + } + + +// ============================ MEMBER FUNCTIONS ============================= +// --------------------------------------------------------------------------- +// CSipMXResolver::CSipMXResolver +// C++ default constructor can NOT contain any code, that +// might leave. +// --------------------------------------------------------------------------- +// +CSipMXResolver::CSipMXResolver() + { + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::ConstructL +// Symbian 2nd phase constructor can leave. +// --------------------------------------------------------------------------- +// +void CSipMXResolver::ConstructL() + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::ConstructL()" ) + + // Open sdp string pool (needed in media field checks) + TRAPD( err, SdpCodecStringPool::OpenL() ); + + switch ( err ) + { + case KErrNone: + { + //close pool at destructor, not opened by others + iCloseStringPool = ETrue; + break; + } + + case KErrAlreadyExists: + { + //already opened, do not try to close at destructor + iCloseStringPool = EFalse; + break; + } + + default: + { + User::Leave( err ); + } + } + + // Check VoIP and Multimedia Sharing availability + FeatureManager::InitializeLibL(); + + TInt dynvoip = 0; + CRepository* repository = CRepository::NewL( KCRUidTelephonySettings ); + repository->Get( KDynamicVoIP, dynvoip ); + delete repository; + + iVoIPEnabled = ( dynvoip && FeatureManager::FeatureSupported( KFeatureIdCommonVoip ) ); + + if ( FeatureManager::FeatureSupported( KFeatureIdMultimediaSharing ) ) + { + iMuSManager = CMusManager::NewL(); + } + + FeatureManager::UnInitializeLib(); + + // initialize media type strings + iAudioType = SdpCodecStringPool::StringPoolL().StringF( + SdpCodecStringConstants::EMediaAudio, + SdpCodecStringPool::StringTableL() ); + + iVideoType = SdpCodecStringPool::StringPoolL().StringF( + SdpCodecStringConstants::EMediaVideo, + SdpCodecStringPool::StringTableL() ); + + SIPMXRLOG( "[SIPMXRESOLVER] <- CSipMXResolver::ConstructL()" ) + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::NewL +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +CSipMXResolver* CSipMXResolver::NewL() + { + CSipMXResolver* self = new( ELeave ) CSipMXResolver; + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self); + return self; + } + + +// Destructor +CSipMXResolver::~CSipMXResolver() + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::~CSipMXResolver()" ) + + delete iMuSManager; + iAudioType.Close(); + iVideoType.Close(); + + if ( iCloseStringPool ) + { + SdpCodecStringPool::Close(); + } + + SIPMXRLOG( "[SIPMXRESOLVER] <- CSipMXResolver::~CSipMXResolver()" ) + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::MatchAcceptContactsL +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::MatchAcceptContactsL( + RStringF aMethod, + const CUri8& /*aRequestUri*/, + const RPointerArray& aHeaders, + const TDesC8& /*aContent*/, + const CSIPContentTypeHeader* /*aContentType*/, + TUid& aClientUid ) + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::MatchAcceptContactsL()" ) + + TBool match = EFalse; + + // check if we have active CS call + // and MM Sharing is enabled (otherwise this can't be MuS) + if ( iMuSManager && IsCSCallActive() ) + { + if ( CheckForACHeaderTagL( aHeaders, KVSFeatureTag ) ) + { + match = ETrue; + aClientUid = ResolveVSUidL( aMethod ); + } + } + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::MatchAcceptContactsL(), ret:%d", match ) + return match; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::MatchEventL +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::MatchEventL( + RStringF /*aMethod*/, + const CUri8& /*aRequestUri*/, + const RPointerArray& /*aHeaders*/, + const TDesC8& /*aContent*/, + const CSIPContentTypeHeader* /*aContentType*/, + TUid& /*aClientUid*/) + { + //SipMXResolver returns always EFalse for MatchEventL calls + SIPMXRLOG( "[SIPMXRESOLVER] <-> CSipMXResolver::MatchEventL()" ) + return EFalse; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::MatchRequestL +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::MatchRequestL( + RStringF aMethod, + const CUri8& aRequestUri, + const RPointerArray& aHeaders, + const TDesC8& aContent, + const CSIPContentTypeHeader* aContentType, + TUid& aClientUid ) + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::MatchRequestL()" ) + + TBool match = EFalse; + + //poc specific requests will not be processed + TBool poc = CheckForACHeaderTagL( aHeaders, KPoCFeatureTag ); + + //OPTIONS and INVITE requests are processed + TBool requestok = ( aMethod == SIPStrings::StringF( SipStrConsts::EOptions ) || + aMethod == SIPStrings::StringF( SipStrConsts::EInvite ) ); + + //application/sdp content type is required + TBool contentok = ( aContent.Length() > 0 && aContentType && + aContentType->MediaType().CompareF( KMediaTypeApp ) == 0 && + aContentType->MediaSubtype().CompareF( KMediaSubtypeSdp ) == 0 ); + + // do further checks only if we have either VoIP and/or MuS active, + // content type must be valid and accept-contact is not poc specific + if ( ( iVoIPEnabled || iMuSManager ) && requestok && contentok && !poc ) + { + CSdpDocument* sdpDocument = CSdpDocument::DecodeLC( aContent ); + + // determine the parameters + TBool hasAudio = CheckForMedia( sdpDocument, iAudioType ); + TBool hasVideo = CheckForMedia( sdpDocument, iVideoType ); + TBool cscall = IsCSCallActive(); + + if ( iMuSManager && hasVideo && hasAudio && cscall ) + { + // both audio and video medias present and cs call on + // and multimedia sharing enabled => + // we need to do some further resolving for client + if ( CheckForVSAttributes( sdpDocument->MediaFields() ) ) + { + // this is VS + match = ETrue; + aClientUid = ResolveVSUidL( aMethod ); + } + else if ( iVoIPEnabled ) + { + // no vs attributes and voip status is enabled + match = ETrue; + ResolveCPPluginUidL( aClientUid, aRequestUri ); + } + else + { + // possible voip match, but voip not activated + match = EFalse; + } + } + else if ( iMuSManager && hasVideo && cscall ) + { + //video media only && cs call ongoing && multimedia sharing enabled + // => Multimedia Sharing + match = ETrue; + aClientUid = ResolveVSUidL( aMethod ); + } + else if ( hasAudio && iVoIPEnabled ) + { + // audio only or audio and video and no cs call + voip supported + // => this is VoIP + match = ETrue; + ResolveCPPluginUidL( aClientUid, aRequestUri ); + } + else + { + // no medias or has video but no CS call or has audio + // but voip status is disabled + // => no match + match = EFalse; + } + + CleanupStack::PopAndDestroy( sdpDocument ); + } + else if ( aMethod == SIPStrings::StringF( SipStrConsts::EInvite ) && + iVoIPEnabled && !poc && aContent.Length() == 0 ) + { + // Pull model: this is a re-Invite without content + ResolveCPPluginUidL( aClientUid, aRequestUri ); + match = ETrue; + } + else + { + //no match + match = EFalse; + } + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::MatchRequestL(), ret:%d", match ) + return match; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::ConnectSupported +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::ConnectSupported() + { + return ETrue; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::ConnectL +// --------------------------------------------------------------------------- +// +void CSipMXResolver::ConnectL( const TUid& aClientUid ) + { + SIPMXRLOGP(\ + "[SIPMXRESOLVER] -> CSipMXResolver::ConnectL(), uid:%d", aClientUid.iUid ) + + // If Uid matches with MuS, forward to MusManager + if ( iMuSManager && + ( aClientUid.iUid == CMusManager::ESipOptions || + aClientUid.iUid == CMusManager::ESipInviteDesired || + aClientUid.iUid == CMusManager::ESipInviteNotDesired ) ) + { + iMuSManager->HandleSipRequestL( + ( CMusManager::TRequestType ) aClientUid.iUid ); + } + else + { + // start through ICM + TInt result = RProperty::Set( KPSUidICMIncomingCall, + KPropertyKeyICMPluginUID, aClientUid.iUid ); + User::LeaveIfError( result ); + } + + SIPMXRLOG( "[SIPMXRESOLVER] <- CSipMXResolver::ConnectL()" ) + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::CancelConnect +// --------------------------------------------------------------------------- +// +void CSipMXResolver::CancelConnect( const TUid& /*aClientUid*/ ) + { + SIPMXRLOG( "[SIPMXRESOLVER] <-> CSipMXResolver::CancelConnect()" ) + // No implementation for VoIP / MuS. + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::SupportedContentTypesL +// --------------------------------------------------------------------------- +// +RPointerArray CSipMXResolver::SupportedContentTypesL() + { + RPointerArray ret; + + CSIPContentTypeHeader* ctype = CSIPContentTypeHeader::DecodeL( KContentType ); + CleanupStack::PushL( ctype ); + ret.AppendL( ctype ); + CleanupStack::Pop( ctype ); + + return ret; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::SupportedSdpMediasL +// --------------------------------------------------------------------------- +// +RPointerArray CSipMXResolver::SupportedSdpMediasL() + { + // Initialise return array + RPointerArray ret; + TCleanupItem tci( CleanupSdpMediasArray, &ret ); + CleanupStack::PushL( tci ); + + // media field descriptors + _LIT8( KMuSField1, "m=video 0 RTP/AVP 96\r\na=application:com.nokia.rtvs\r\na=X-application:com.nokia.rtvs\r\n" ); + _LIT8( KMuSField2, "m=audio 0 RTP/AVP 97\r\n" ); + _LIT8( KVoIPField1, "m=video 0 RTP/AVP 0\r\n" ); + _LIT8( KVoIPField2, "m=application 0 tcp wb\r\n" ); + _LIT8( KVoIPField3, "m=audio 0 RTP/AVP 0\r\n" ); + _LIT8( KVoIPField4, "m=audio 0 RTP/SAVP 0\r\n" ); + + CSdpMediaField* field = NULL; + + // add media fields to array + if ( iMuSManager ) + { + field = CSdpMediaField::DecodeLC( KMuSField1 ); + ret.AppendL( field ); // ownership to array + CleanupStack::Pop( field ); + + field = CSdpMediaField::DecodeLC( KMuSField2 ); + ret.AppendL( field ); + CleanupStack::Pop( field ); + } + + if ( iVoIPEnabled ) + { + field = CSdpMediaField::DecodeLC( KVoIPField1 ); + ret.AppendL( field ); + CleanupStack::Pop( field ); + + field = CSdpMediaField::DecodeLC( KVoIPField2 ); + ret.AppendL( field ); + CleanupStack::Pop( field ); + + field = CSdpMediaField::DecodeLC( KVoIPField3 ); + ret.AppendL( field ); + CleanupStack::Pop( field ); + + field = CSdpMediaField::DecodeLC( KVoIPField4 ); + ret.AppendL( field ); + CleanupStack::Pop( field ); + } + + CleanupStack::Pop(); // tcleanupitem + return ret; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::AddClientSpecificHeadersForOptionsResponseL +// --------------------------------------------------------------------------- +// +void CSipMXResolver::AddClientSpecificHeadersForOptionsResponseL( + RPointerArray& aHeaders ) + { + SIPMXRLOG( "[SIPMXRESOLVER] -> \ + CSipMXResolver::AddClientSpecificHeadersForOptionsResponseL()" ) + //Add Allow Header + RPointerArray allowheaders = + CSIPAllowHeader::DecodeL(KSIPMethodsInAllowHeader); + TInt count = allowheaders.Count(); + for(TInt i = 0; i supportedheaders = + CSIPSupportedHeader::DecodeL(KSIPExtensionsSupported); + count = supportedheaders.Count(); + for(TInt j = 0; j CSipMXResolver::IsCSCallActive()" ) + + TBool retval = EFalse; + + TInt callType = KErrNone; + RProperty::Get( KPSUidCtsyCallInformation, + KCTsyCallType, callType ); + + if ( EPSCTsyCallTypeCSVoice == callType ) + { + TInt callState = KErrNone; + RProperty::Get( KPSUidCtsyCallInformation, + KCTsyCallState, callState ); + + if ( EPSCTsyCallStateConnected == callState || + EPSCTsyCallStateHold == callState ) + { + retval = ETrue; + } + } + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::IsCSCallActive(), ret:%d", retval ) + + return retval; + } + +// ----------------------------------------------------------------------------- +// CSipMXResolver::CheckForMedia +// ----------------------------------------------------------------------------- +// +TBool CSipMXResolver::CheckForMedia( CSdpDocument* aSdpDoc, + const RStringF& aMediaType ) const + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::CheckForMedia()" ) + + TBool present = EFalse; + + if ( aSdpDoc ) + { + RPointerArray& mFields = aSdpDoc->MediaFields(); + const TInt count = mFields.Count(); + + // if there is media fields, check them for type + for ( TInt i = 0; i < count && !present; i++ ) + { + if ( aMediaType == mFields[ i ]->Media() ) + { + present = ETrue; + } + } + } + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::CheckForMedia(), ret:%d", present ) + + return present; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::CheckForACHeaderTagL +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::CheckForACHeaderTagL( + const RPointerArray& aHeaders, + const TDesC8& aTag ) const + { + TBool match = EFalse; + TInt ftagcount = 0; + + for ( TInt i = 0; i < aHeaders.Count(); i++ ) + { + if ( aHeaders[i]->Name() == + SIPStrings::StringF( SipStrConsts::EAcceptContactHeader ) || + aHeaders[i]->Name() == + SIPStrings::StringF( SipStrConsts::EAcceptContactHeaderCompact ) ) + { + RStringF featureTagStr = SIPStrings::Pool().OpenFStringL( aTag ); + CleanupClosePushL( featureTagStr ); + + CSIPAcceptContactHeader* ach = + static_cast(aHeaders[i]); + TInt pcount = ach->ParamCount(); + + for ( TInt p = 0; p < pcount; p++ ) + { + RStringF mparam; + CleanupClosePushL( mparam ); + + if ( KErrNone == ach->Param( p, mparam ) ) + { + //we need to count all feature tags + if ( mparam.DesC().Left(1).Compare( KFTagChar ) == 0 ) + { + ftagcount++; + } + if ( mparam == featureTagStr ) + { + match = ETrue; + //loop is continued after match to count feature tags + } + } + + CleanupStack::PopAndDestroy( 1 ); //mparam + } + + // Use the dynamic string. + CleanupStack::PopAndDestroy( 1 ); // featureTagStr + } + } + + //return ETrue only if there's only one feature tag and it is MuS specific + return ( match && ftagcount == 1 ); + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::CheckForVSAttributesL +// --------------------------------------------------------------------------- +// +TBool CSipMXResolver::CheckForVSAttributes( + RPointerArray& aFields ) const + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::CheckForVSAttributes()" ) + + TBool vsattributefound = EFalse; + TInt sendonlycount = 0; + TInt videofields = 0; + const TInt fieldcount = aFields.Count(); + + for ( TInt i = 0; i < fieldcount; i++ ) + { + CSdpMediaField* mField = aFields[i]; + + //only check audio/video fields + TBool audio = mField->Media() == iAudioType; + TBool video = mField->Media() == iVideoType; + + if ( video ) + { + videofields++; + } + + if ( video || audio ) + { + RPointerArray< CSdpAttributeField > attrList = + mField->AttributeFields(); + + TInt attrCount = attrList.Count(); + for (TInt j = 0; j < attrCount && !vsattributefound; j++ ) + { + CSdpAttributeField* attributeField = attrList[j]; + RStringF attribute = attributeField->Attribute(); + + // for each sendonly attribute, add counter + if ( KErrNotFound != attribute.DesC().FindF( KSendOnly ) ) + { + sendonlycount++; + } + + // check m=video fields for com.nokia.rtvs attribute + if ( video ) + { + if ( ( KErrNotFound != + attribute.DesC().FindF( KApplicationAttr ) || + KErrNotFound != + attribute.DesC().FindF( KXApplicationAttr ) ) && + KErrNotFound != + attributeField->Value().FindF( KNokiaRtvs ) ) + { + //attribute found + vsattributefound = ETrue; + } + } + } + } + } + + // Video Sharing is assumed if nokia vs specific attributes are found + // or if there is at least one m=video line and all media fields have + // sendonly attribute. + TBool retval = ( vsattributefound || + ( videofields > 0 && fieldcount == sendonlycount ) ); + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::CheckForVSAttributes(),ret:%d", retval ) + + return retval; + } + + +// --------------------------------------------------------------------------- +// CSipMXResolver::ResolveVSUid +// --------------------------------------------------------------------------- +// +TUid CSipMXResolver::ResolveVSUidL( const RStringF& aMethod ) + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::ResolveVSUidL()" ) + + TUid muid = KNullUid; + + if ( iMuSManager ) + { + if ( aMethod == SIPStrings::StringF( SipStrConsts::EOptions ) ) + { + muid.iUid = ( TInt32 ) CMusManager::ESipOptions; + } + else + { + MultimediaSharing::TMusAvailabilityStatus capability = + iMuSManager->AvailabilityL(); + + //check if availability value is from non-error-range + if( capability >= KErrNone && + capability < MultimediaSharing::EErrSipRegistration ) + { + muid.iUid = ( TInt32 ) CMusManager::ESipInviteDesired; + } + else + { + muid.iUid = ( TInt32 ) CMusManager::ESipInviteNotDesired; + } + } + } + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::ResolveVSUidL(), ret:%d", muid.iUid ) + + return muid; + } + + +// ----------------------------------------------------------------------------- +// CSipMXResolver::ResolveCPPluginUidL +// ----------------------------------------------------------------------------- +// +void CSipMXResolver::ResolveCPPluginUidL( TUid& aUid, const CUri8& aRequestUri ) + { + SIPMXRLOG( "[SIPMXRESOLVER] -> CSipMXResolver::ResolveCPPluginUidL()" ) + + CSipClientResolverUtils* resolver = CSipClientResolverUtils::NewLC(); + resolver->GetImplementationUidWithUserL( + aRequestUri.Uri().Extract( EUriUserinfo ), aUid ); + CleanupStack::PopAndDestroy( resolver ); + + SIPMXRLOGP( \ + "[SIPMXRESOLVER] <- CSipMXResolver::ResolveCPPluginUidL(),uID:%d", aUid.iUid ) + } + + +// End of File