--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/voipplugins/sipconnectionprovider/ipvoicemailengine/src/ipvmbxengine.cpp Wed Sep 01 12:29:57 2010 +0100
@@ -0,0 +1,1258 @@
+/*
+* Copyright (c) 2002-2010 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: Creates SMS message if Event in MailBox has occurred.
+*
+*/
+
+
+
+// Include hierarchy change can't be done. Smuthdr.h uses
+// other files from messaging/sms (smsstd.h and smutset.h). It doesn't seem
+// to find them without old way to declare include folder using SYSTEMINCLUDE.
+//
+#include <smuthdr.h>/*messaging/\sms/\*/
+#include <msvuids.h>
+#include <txtrich.h>
+#include <spsettings.h>
+#include <spproperty.h>
+#include <utf.h>
+#include <mcemanager.h>
+#include <ipvoicemailengine.rsg>
+
+#include "ipvmbxengine.h"
+#include "ipvmbxeventmonitor.h"
+#include "ipvmbxbase.h"
+#include "ipvmbxlogger.h"
+#include "ipvmbxconstants.h"
+#include "ipvmbxpanic.h"
+
+
+const TUid KAppUID = { 0x1020596F };
+const TInt KMaxMessageDigits = 10;
+const TInt KAccountTextLength = 21;
+const TInt KOneChar = 1;
+const TInt KTwoChars = 2;
+const TInt KSmsLength = 160;
+const TInt KTotalLength = 2;
+const TInt KTotalOldLength = 3;
+const TInt KMinIpVoiceMailBoxUriLength = 3;
+const TInt KSpecialNameCharsCount = 10;
+const TInt KMaxMsgDescriptions = 99;
+
+_LIT( KEndLine, "\n" );
+_LIT( KTotal, "%N" );
+_LIT( KTotalNew, "%0N" );
+_LIT( KIpVmbxAppEngineResourceFileDirectory, "\\resource\\" );
+_LIT( KIpVmbxAppEngineResourceFileName, "ipvoicemailengine.rsc" );
+_LIT( KSipString, "sip:");
+_LIT( KOptionalSeparator, "\n\n" );
+_LIT8( KTotalOld, "%1N" );
+_LIT8( KOneMessage, "1" );
+_LIT8( KNoMessages, "0" );
+_LIT8( KMessagesWaiting8, "messages-waiting" );
+_LIT8( KNewMessages8, "yes" );
+_LIT8( KMessageAccount8, "message-account" );
+_LIT8( KVoiceMessage8, "voice-message" );
+_LIT8( KSlash8, "/" );
+_LIT8( KColon8, ":" );
+_LIT8( KCrlf8, "\r\n" );
+_LIT8( KEndLine8, "\n" );
+_LIT8( KHTab8, "\t");
+_LIT8( KSpace8, " ");
+
+const TText KNameDigitLowest = '\x30';
+const TText KNameDigitHighest = '\x39';
+const TText KNameCharUpLowest = '\x41';
+const TText KNameCharUpHighest = '\x5a';
+const TText KNameCharLowLowest = '\x61';
+const TText KNameCharLowHighest = '\x7a';
+// Legal characters in name header
+// 2d, 2e, 21, 25, 2a, 5f, 2b, 60, 27, 7e
+const TText8 KSpecialNameChars[KSpecialNameCharsCount] =
+ {
+ '-',
+ '.',
+ '!',
+ '%',
+ '*',
+ '_',
+ '+',
+ '`',
+ '\'',
+ '~'
+ };
+const TText KSpace = '\x20';
+const TText KValueLowestChar = KSpace;
+const TText KValueHighestChar = '\xfd';
+const TText KCr = '\x0d';
+const TText KLf = '\x0a';
+const TText KHTab = '\x09';
+
+
+// ============================ MEMBER FUNCTIONS ==============================
+
+// ----------------------------------------------------------------------------
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// ----------------------------------------------------------------------------
+//
+CIpVmbxEngine::CIpVmbxEngine( CIpVmbxInterface& aInterface ):
+ iEventData(),
+ iInterface( aInterface )
+ {
+ }
+
+
+// ----------------------------------------------------------------------------
+// Symbian 2nd phase constructor can leave.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::ConstructL()
+ {
+ // One manager = one event observer (restricted)
+ iIpVmbxEventMonitor = new ( ELeave ) TIpVmbxEventMonitor( *this );
+ }
+
+
+// ----------------------------------------------------------------------------
+// Two-phased constructor.
+// ----------------------------------------------------------------------------
+//
+EXPORT_C CIpVmbxEngine* CIpVmbxEngine::NewL( CIpVmbxInterface& aInterface )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::NewL" );
+
+ CIpVmbxEngine* self = new( ELeave ) CIpVmbxEngine( aInterface );
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+
+ return self;
+ }
+
+
+// ----------------------------------------------------------------------------
+// Destructor.
+// ----------------------------------------------------------------------------
+//
+CIpVmbxEngine::~CIpVmbxEngine()
+ {
+ IPVMEPRINT( "CIpVmbxEngine::~CIpVmbxEngine - IN" );
+
+ iVmbxBaseArray.ResetAndDestroy();
+ iVmbxBaseArray.Close();
+
+ delete iMceManager;
+ delete iServiceSettings;
+ delete iIpVmbxEventMonitor;
+
+ IPVMEPRINT( "CIpVmbxEngine::~CIpVmbxEngine - OUT" );
+ }
+
+
+// ----------------------------------------------------------------------------
+// Starts subscription to VoiceMailBox -server
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::SubscribeProfileL(
+ TUint32 aServiceProviderId,
+ CSIPProfile& aSipProfile )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::SubscribeProfileL - IN" );
+
+ if ( !iBasicServicesRunning )
+ {
+ iServiceSettings = CSPSettings::NewL();
+ // one manager recommended for one component
+ iMceManager = CMceManager::NewL( KAppUID, &iEventData );
+ iMceManager->SetEventObserver( iIpVmbxEventMonitor );
+ iBasicServicesRunning = ETrue;
+ }
+
+ HBufC16* voiceMailUri16 = HBufC16::NewLC( KMaxIpVoiceMailBoxUriLength );
+ TPtr16 ptrvoiceMailUri16( voiceMailUri16->Des() );
+ TInt reSubscribe = 0;
+
+ // Fetch MWI address
+ CSPProperty* mwiAddress = CSPProperty::NewLC();
+ User::LeaveIfError( iServiceSettings->FindPropertyL(
+ aServiceProviderId,
+ ESubPropertyVMBXMWIAddress,
+ *mwiAddress ) );
+ User::LeaveIfError( mwiAddress->GetValue( ptrvoiceMailUri16 ) );
+ if ( ptrvoiceMailUri16.Length() < KMinIpVoiceMailBoxUriLength )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::SubscribeProfileL - MWI not found, Leaving..." );
+ User::Leave( KErrNotFound );
+ }
+ IPVMEPRINT( "CIpVmbxEngine::SubscribeProfileL - MWI found" );
+ if ( KErrNotFound == ptrvoiceMailUri16.Find( KSipString ) )
+ {
+ ptrvoiceMailUri16.Insert( 0, KSipString );
+ }
+ CleanupStack::PopAndDestroy( mwiAddress );
+
+ // Fetch also subscribe interval
+ CSPProperty* mwiInterval = CSPProperty::NewLC();
+ User::LeaveIfError( iServiceSettings->FindPropertyL(
+ aServiceProviderId,
+ ESubPropertyVMBXMWISubscribeInterval,
+ *mwiInterval) );
+ User::LeaveIfError( mwiInterval->GetValue( reSubscribe ) );
+ CleanupStack::PopAndDestroy( mwiInterval );
+
+ HBufC8* voiceMailUri8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L(
+ ptrvoiceMailUri16 );
+ CleanupStack::PopAndDestroy( voiceMailUri16 );
+ CleanupStack::PushL( voiceMailUri8 );
+
+ TInt index = iVmbxBaseArray.Count();
+ TBool newProfile = ETrue;
+ if ( index )
+ {
+ // Might be second subscription or pending operation.
+ // Though IPVME and SCP supports currently only one VMBX connection
+ for ( TInt i = 0; i < index; i++ )
+ {
+ CIpVmbxBase* const subscription( iVmbxBaseArray[i] );
+ if ( *voiceMailUri8 == subscription->VmbxUrl() )
+ {
+ // Subscription already exists
+ if ( CIpVmbxBase::EDisabled == subscription->State() )
+ {
+ // Subscription was disabled, enable
+ newProfile = EFalse;
+ subscription->Initialize(
+ aServiceProviderId,
+ aSipProfile );
+
+ subscription->SubscribeL( reSubscribe );
+ }
+ else
+ {
+ User::Leave( KErrAlreadyExists );
+ }
+ }
+ }
+ }
+ if ( newProfile )
+ {
+ // Subscription to new VMBX account
+ IPVMEPRINT( "CIpVmbxEngine::New AppBase" );
+ CIpVmbxBase* app = CIpVmbxBase::NewL(
+ *this,
+ *voiceMailUri8,
+ *iMceManager );
+ CleanupStack::PushL( app );
+ iVmbxBaseArray.AppendL( app );
+ CleanupStack::Pop( app );
+
+ app->Initialize( aServiceProviderId, aSipProfile );
+ app->SubscribeL( reSubscribe );
+ }
+
+ CleanupStack::PopAndDestroy( voiceMailUri8 );
+
+ IPVMEPRINT( "CIpVmbxEngine::SubscribeProfileL - OUT" );
+ }
+
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+TInt CIpVmbxEngine::ProfileSubscribed(
+ TUint32 aServiceProviderId,
+ TBool& aProfileSubscribed )
+ {
+ CIpVmbxBase* subscription( SubscriptionByProvider( aServiceProviderId ) );
+ TInt err = KErrNotFound;
+
+ if ( subscription )
+ {
+ err = KErrNone;
+ if ( CIpVmbxBase::ERegistered == subscription->State() )
+ {
+ aProfileSubscribed = ETrue;
+ }
+ else
+ {
+ aProfileSubscribed = EFalse;
+ }
+ }
+
+ return err;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Resolve base class matching to recipient
+// ---------------------------------------------------------------------------
+//
+CIpVmbxBase* CIpVmbxEngine::SubscriptionByRecipient(
+ const TDesC8& aRecipient8 )
+ {
+ CIpVmbxBase* base( NULL );
+
+ for ( TInt i = 0; i < iVmbxBaseArray.Count(); i++ )
+ {
+ if ( iVmbxBaseArray[i]->VmbxUrl() == aRecipient8 )
+ {
+ base = iVmbxBaseArray[i];
+ }
+ }
+
+ return base;
+ }
+
+
+// ----------------------------------------------------------------------------
+// EventMonitor has received event and the event will be gained and checked
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::EventReceivedL( const TDesC8& aRecipient8 )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::EventReceivedL - IN" );
+
+ CIpVmbxBase* subscription( SubscriptionByRecipient( aRecipient8 ) );
+ if ( !subscription )
+ {
+ User::Leave( KErrNotFound );
+ }
+
+ HBufC8* content8( iEventData.GetContent() );
+ CleanupStack::PushL( content8 );
+
+ HBufC8* totalMessages8 = HBufC8::NewLC( KMaxMessageDigits );
+ HBufC8* newMessages8 = HBufC8::NewLC( KMaxMessageDigits );
+ HBufC8* from8 = HBufC8::NewLC(
+ KMaxIpVoiceMailBoxUriLength + KAccountTextLength );
+ TPtr8 ptrTotalMessages8( totalMessages8->Des() );
+ TPtr8 ptrNewMessages8( newMessages8->Des() );
+ TPtr8 ptrFrom8( from8->Des() );
+ TPtr8 ptrContent8( content8->Des() );
+
+ TBool createSMS = EFalse;
+ // no need to handle errors, returned parameters can still
+ // be interpreted correctly
+ //
+ TRAP_IGNORE(
+ ParseNotifyContentL(
+ createSMS,
+ ptrContent8,
+ ptrTotalMessages8,
+ ptrNewMessages8,
+ ptrFrom8 ) );
+
+ if ( ptrNewMessages8 == KNoMessages )
+ {
+ // protocol test fix, new message should not be created
+ // if new message count is 0
+ IPVMEPRINT( "CIpVmbxEngine::EventReceivedL - No new messages" );
+ createSMS = EFalse;
+ }
+ if ( 0 == ptrFrom8.Length() )
+ {
+ ptrFrom8 = subscription->VmbxUrl();
+ }
+
+ TInt totalMsgs = 0;
+ TInt newMsgs = 0;
+ TLex8 msgsConvert;
+ msgsConvert.Assign( *totalMessages8 );
+ if ( KErrNone == msgsConvert.Val( totalMsgs ) )
+ {
+ msgsConvert.Assign( *newMessages8 );
+ if ( KErrNone != msgsConvert.Val( newMsgs ) )
+ {
+ totalMsgs = 0;
+ }
+ }
+
+ TInt curTotal = 0;
+ TInt curNew = 0;
+ subscription->AccountMessageCount( curTotal, curNew );
+
+ TBool statusChanged = EFalse;
+ if ( totalMsgs != curTotal || newMsgs != curNew )
+ {
+ statusChanged = ETrue;
+ IPVMEPRINT( "CIpVmbxEngine::EventReceivedL - Status changed" );
+ subscription->SetAccountMessageCount( totalMsgs, newMsgs );
+ }
+
+ if ( createSMS && statusChanged )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::EventReceivedL - Create message body" );
+ TBuf8< KSmsLength > messageBody8;
+ CreateMessageBodyL(
+ *content8,
+ ptrTotalMessages8,
+ ptrNewMessages8,
+ ptrFrom8,
+ messageBody8 );
+ CreateSMSMessageL( ptrFrom8, messageBody8 );
+ }
+
+ CleanupStack::PopAndDestroy( from8 );
+ CleanupStack::PopAndDestroy( newMessages8 );
+ CleanupStack::PopAndDestroy( totalMessages8 );
+ CleanupStack::PopAndDestroy( content8 );
+
+ IPVMEPRINT( "CIpVmbxEngine::EventReceivedL - OUT" );
+ }
+
+
+// ---------------------------------------------------------------------------
+//
+// ---------------------------------------------------------------------------
+//
+void CIpVmbxEngine::UnsubscribeProfileL( TUint32 aServiceProviderId )
+ {
+ IPVMEPRINT2( "CIpVmbxEngine::UnsubscribeProfileL: %d - IN",
+ aServiceProviderId );
+
+ CIpVmbxBase* subscription( SubscriptionByProvider( aServiceProviderId ) );
+ if ( !subscription )
+ {
+ User::Leave( KErrNotFound );
+ }
+
+ IPVMEPRINT2( "CIpVmbxEngine::UnsubscribeProfileL: state=%d",
+ subscription->State() );
+
+ switch( subscription->State() )
+ {
+ case CIpVmbxBase::ERegistered:
+ {
+ subscription->TerminateEventL();
+ break;
+ }
+ case CIpVmbxBase::EDisabled:
+ {
+ CleanVmbxBase();
+ break;
+ }
+ case CIpVmbxBase::ESubscribing:
+ {
+ subscription->Cancel();
+ CleanVmbxBase();
+ break;
+ }
+ case CIpVmbxBase::ETerminating:
+ {
+ User::Leave( KErrCancel );
+ break;
+ }
+ default:
+ {
+ IPVMEPRINT( "No implementation" );
+ }
+ }
+
+ }
+
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::CleanVmbxBase()
+ {
+ for ( TInt i = 0; i < iVmbxBaseArray.Count(); i++ )
+ {
+ if ( CIpVmbxBase::ETerminating == iVmbxBaseArray[i]->State() ||
+ CIpVmbxBase::EDisabled == iVmbxBaseArray[i]->State() )
+ {
+ delete iVmbxBaseArray[i];
+ iVmbxBaseArray.Remove( i );
+ }
+ }
+ }
+
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::HandleMessage(
+ TUint32 aServiceProviderId,
+ CIpVmbxEngine::TIpVmbxMessages aMessage )
+ {
+ IPVMEPRINT2( "CIpVmbxEngine::HandleMessage: %d - IN", aMessage );
+
+ CIpVmbxBase* subscription( SubscriptionByProvider( aServiceProviderId ) );
+ // Save current state because some functions modify states
+ CIpVmbxBase::TIpVmbxBaseStates baseState = subscription->State();
+ MIpVmbxObserver::TVmbxMessage message =
+ MIpVmbxObserver::EIncorrectSettings;
+ TBool send = ETrue;
+
+ switch ( aMessage )
+ {
+ case CIpVmbxEngine::EEngineSubscribed:
+ message = MIpVmbxObserver::ESubscribed;
+ break;
+ case CIpVmbxEngine::EEngineTerminated:
+ {
+ if ( CIpVmbxBase::EDisabled != baseState )
+ {
+ CleanVmbxBase();
+ message = MIpVmbxObserver::EUnsubscribed;
+ }
+ else
+ {
+ send = EFalse;
+ }
+ break;
+ }
+ case CIpVmbxEngine::EEngineUndefined:
+ {
+ send = EFalse;
+ break;
+ }
+ case CIpVmbxEngine::EEngineSubscribeRejected:
+ case CIpVmbxEngine::EEngineIncorrectAccount:
+ {
+ subscription->Cancel();
+ CleanVmbxBase();
+ message = MIpVmbxObserver::EIncorrectSettings;
+ break;
+ }
+ case CIpVmbxEngine::EEngineSmsError:
+ {
+ subscription->Cancel();
+ message = MIpVmbxObserver::ESmsError;
+ break;
+ }
+ case CIpVmbxEngine::EEngineSmsOom:
+ message = MIpVmbxObserver::ENoMemory;
+ break;
+ case CIpVmbxEngine::EEngineNetworkLost:
+ {
+ subscription->Cancel();
+ subscription->DeleteEvent();
+ send = EFalse;
+ break;
+ }
+ case CIpVmbxEngine::EEngineNetworkError:
+ {
+ subscription->Cancel();
+ subscription->DeleteEvent();
+ message = MIpVmbxObserver::ENetworkError;
+ break;
+ }
+ case CIpVmbxEngine::EEngineFatalNetworkError:
+ {
+ subscription->Cancel();
+ subscription->DeleteEvent();
+ message = MIpVmbxObserver::EFatalNetworkError;
+ break;
+ }
+ default:
+ IPVMEPRINT( "Unhandled message!" );
+ }
+
+ if ( send )
+ {
+ iInterface.SendMessage( aServiceProviderId, message );
+ }
+
+ IPVMEPRINT( "CIpVmbxEngine::HandleMessage - OUT" );
+ }
+
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+//
+CIpVmbxBase* CIpVmbxEngine::SubscriptionByProvider(
+ TUint32 aServiceProviderId )
+ {
+ CIpVmbxBase* base( NULL );
+ for ( TInt i = 0; i < iVmbxBaseArray.Count(); i++ )
+ {
+ if ( iVmbxBaseArray[i]->ServiceProviderId() == aServiceProviderId )
+ {
+ base = iVmbxBaseArray[i];
+ }
+ }
+ return base;
+ }
+
+
+// ----------------------------------------------------------------------------
+// From class MMsvSessionObserver.
+// Indicates an event has occurred from a Message Server session.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::HandleSessionEventL(
+ TMsvSessionEvent /*aEvent*/,
+ TAny* /*aArg1*/,
+ TAny* /*aArg2*/,
+ TAny* /*aArg3*/ )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::HandleSessionEventL - Dummy implementation" );
+ }
+
+
+// ----------------------------------------------------------------------------
+// Parses critical and important optional parts of content. Already
+// processed data is cut from content.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::ParseNotifyContentL(
+ TBool& aCreateSms,
+ TDes8& aContent8,
+ TDes8& aTotalMessages8,
+ TDes8& aNewMessages8,
+ TDes8& aFrom8 ) const
+ {
+#ifdef _DEBUG
+ HBufC* print = HBufC::NewLC( aContent8.Length() );
+ print->Des().Copy( aContent8 );
+ IPVMEPRINT2( "CIpVmbxEngine::ParseNotifyContentL - aContent8:%S", &print->Des() )
+ CleanupStack::PopAndDestroy( print );
+#endif // _DEBUG
+ aCreateSms = EFalse;
+
+ // check required content
+ TLex8 analyzer8;
+ TPtrC8 posPtr8;
+ TPtrC8 dataPtr8;
+ TPtrC8 messagePtr8;
+
+ TInt atPosWaiting = aContent8.FindF( KMessagesWaiting8 );
+ // check important OPTIONAL fields
+ TInt atPosAccount = aContent8.FindF( KMessageAccount8 );
+ TInt atPosMessage = aContent8.FindF( KVoiceMessage8 );
+
+ if ( 0 == atPosWaiting )
+ {
+ // data field found from correct place
+ messagePtr8.Set( FetchMessagePartL( aContent8 ) );
+ analyzer8.Assign(
+ messagePtr8.Mid( messagePtr8.Find( KColon8 ) + KOneChar ) );
+ dataPtr8.Set( analyzer8.NextToken() );
+ if ( 0 == dataPtr8.CompareF( KNewMessages8 ) )
+ {
+ // message(s) waiting
+ aCreateSms = ETrue;
+ }
+ // cut processed data
+ posPtr8.Set( aContent8.Mid( messagePtr8.Length() ) );
+ analyzer8.Assign( posPtr8 );
+ }
+ else
+ {
+ IPVMEPRINT( "CIpVmbxEngine::ParseNotifyContentL - leave with KErrCorrupt" );
+ // malformed critical part of message
+ User::Leave( KErrCorrupt );
+ }
+
+ if ( KErrNotFound != atPosAccount && KErrNotFound != atPosMessage )
+ {
+ // both optionals found
+ if ( !( atPosAccount < atPosMessage ) )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::ParseNotifyContentL - leave with KErrCorrupt 2" );
+ // incorrect format
+ User::Leave( KErrCorrupt );
+ }
+ }
+
+ if ( KErrNotFound != atPosAccount && aCreateSms )
+ {
+ // get account
+ IPVMEPRINT( "CIpVmbxEngine::ParseNotifyContentL - get account" );
+ messagePtr8.Set( FetchMessagePartL( posPtr8 ) );
+ analyzer8.Assign(
+ messagePtr8.Mid( messagePtr8.Find( KColon8 ) + KOneChar ) );
+ dataPtr8.Set( analyzer8.NextToken() );
+ posPtr8.Set( posPtr8.Mid( messagePtr8.Length() ) );
+ analyzer8.Assign( posPtr8 );
+
+
+ aFrom8.Copy( dataPtr8.Left( aFrom8.MaxLength() ) );
+ }
+
+ if ( KErrNotFound != atPosMessage )
+ {
+ messagePtr8.Set( FetchMessagePartL( posPtr8 ) );
+ analyzer8.Assign(
+ messagePtr8.Mid( messagePtr8.Find( KColon8 ) + KOneChar ) );
+
+ // can hold value required by specification
+ TUint oldMessageCount = 0;
+ TUint newMessageCount = 0;
+ TLex8 value;
+
+ analyzer8.SkipSpace();
+ User::LeaveIfError( analyzer8.Val( newMessageCount ) );
+ analyzer8.SkipSpace();
+ User::LeaveIfError( KSlash8().Locate( analyzer8.Get() ) );
+
+ analyzer8.SkipSpace();
+ User::LeaveIfError( analyzer8.Val( oldMessageCount ) );
+
+ aNewMessages8.Num( newMessageCount );
+ aTotalMessages8.Num( ( TInt64 ) oldMessageCount + ( TInt64 ) newMessageCount );
+
+ posPtr8.Set( posPtr8.Mid( messagePtr8.Length() ) );
+ }
+
+ // Clean string off processed data
+ aContent8.Delete( 0, aContent8.Length() - posPtr8.Length() );
+
+ IPVMEPRINT( "CIpVmbxEngine::ParseNotifyContentL - OUT" );
+ }
+
+
+// ----------------------------------------------------------------------------
+// Creates SMS message and sends it
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::CreateSMSMessageL(
+ const TDesC8& aFrom8,
+ const TDesC8& aMessageBody8 )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::CreateSMSMessageL - IN" );
+
+ // Create the SMS header object...
+ CParaFormatLayer* paraFormatLayer = CParaFormatLayer::NewL();
+ CleanupStack::PushL( paraFormatLayer );
+ CCharFormatLayer* charFormatLayer = CCharFormatLayer::NewL();
+ CleanupStack::PushL( charFormatLayer );
+
+ CRichText* bodyText = CRichText::NewL( paraFormatLayer, charFormatLayer );
+ CleanupStack::PushL( bodyText );
+ TInt position = 0;
+ TBuf< KSmsLength > messageBody;
+ messageBody.Copy( aMessageBody8.Left( messageBody.MaxLength() ) );
+ bodyText->InsertL( position, messageBody );
+
+ CSmsHeader* header = CSmsHeader::NewL( CSmsPDU::ESmsDeliver, *bodyText );
+ CleanupStack::PushL( header );
+ TBuf< KMaxIpVoiceMailBoxUriLength > from;
+ from.Copy( aFrom8 );
+ header->SetFromAddressL( from );
+
+ TMsvEntry entry;
+ entry.SetVisible(ETrue );
+ entry.SetUnread( ETrue );
+ entry.SetNew( ETrue );
+ entry.iServiceId = KMsvRootIndexEntryId;
+ entry.iType = KUidMsvMessageEntry;
+ entry.iMtm = KUidMsgTypeSMS;
+ entry.iDate.UniversalTime();
+ entry.iSize = 0;
+ entry.iDescription.Set( KNullDesC );
+ entry.iDetails.Set( KNullDesC );
+
+ header->Deliver().SetServiceCenterTimeStamp( entry.iDate );
+
+ CSmsSettings* smsSettings = CSmsSettings::NewL();
+ CleanupStack::PushL( smsSettings );
+ smsSettings->SetDelivery( ESmsDeliveryImmediately );
+ smsSettings->SetValidityPeriod( ESmsVPWeek );
+ smsSettings->SetValidityPeriodFormat( TSmsFirstOctet::ESmsVPFInteger );
+ smsSettings->SetReplyQuoted( EFalse );
+ smsSettings->SetRejectDuplicate( ETrue );
+ smsSettings->SetDelivery( ESmsDeliveryImmediately );
+ smsSettings->SetDeliveryReport( ETrue );
+ smsSettings->SetReplyPath( EFalse );
+ smsSettings->SetMessageConversion( ESmsConvPIDNone );
+ smsSettings->SetCanConcatenate( ETrue );
+ smsSettings->SetUseServiceCenterTimeStampForDate( ETrue );
+
+ header->SetSmsSettingsL(*smsSettings );
+
+ CSmsNumber* rcpt = CSmsNumber::NewL();
+ CleanupStack::PushL( rcpt );
+ rcpt->SetAddressL( from.Left( KSmcmSmsNumberMaxNumberLength ) );
+ header->Recipients().AppendL( rcpt );
+ CleanupStack::Pop( rcpt );
+
+ // Update entry description and details...
+ CArrayPtrFlat< CSmsNumber >& recipient = header->Recipients();
+ entry.iDetails.Set( recipient[0]->Address() );
+ entry.iDescription.Set( bodyText->Read(
+ 0, smsSettings->DescriptionLength() ));
+ entry.SetInPreparation( EFalse );
+
+ // Create the entry - set context to the global outbox.
+ TMsvSelectionOrdering ordering = TMsvSelectionOrdering(
+ KMsvNoGrouping,
+ EMsvSortByDescription,
+ ETrue );
+ CMsvSession* session = CMsvSession::OpenSyncL( *this );
+ CleanupStack::PushL( session );
+ CMsvEntry* centry = CMsvEntry::NewL(
+ *session, KMsvRootIndexEntryId, ordering );
+ CleanupStack::PushL( centry );
+ centry->SetEntryL( KMsvGlobalInBoxIndexEntryId );
+ centry->CreateL( entry );
+
+ // Create new store and save header information
+ centry->SetEntryL( entry.Id() );
+ CMsvStore* store = centry->EditStoreL();
+ CleanupStack::PushL(store);
+ header->StoreL( *store );
+ store->StoreBodyTextL( *bodyText );
+ store->CommitL();
+
+ CleanupStack::PopAndDestroy( store );
+ CleanupStack::PopAndDestroy( centry );
+ CleanupStack::PopAndDestroy( session );
+ CleanupStack::PopAndDestroy( smsSettings );
+ CleanupStack::PopAndDestroy( header );
+ CleanupStack::PopAndDestroy( bodyText );
+ CleanupStack::PopAndDestroy( charFormatLayer );
+ CleanupStack::PopAndDestroy( paraFormatLayer );
+
+ IPVMEPRINT( "CIpVmbxEngine::CreateSMSMessageL - OUT" );
+ }
+
+
+// ----------------------------------------------------------------------------
+// Parse optional headers. Parser must take account various optional
+// characters in headers. String lengths are monitored every round to prevent
+// panics when setting data.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::ParseOptionalHeadersL(
+ const TDesC8& aContent8,
+ TDes8& aMessageBody8 ) const
+ {
+ IPVMEPRINT( "CIpVmbxEngine::ParseOptionalHeadersL - IN" );
+
+ if ( aContent8.Left( KCrlf8().Length() ).Compare( KCrlf8 ) ||
+ aContent8.Length() == KTwoChars )
+ {
+ // Checking if optional message does not exist or its too short.
+ // Will also fix parsing of non standard basic Pingtel message.
+ //
+ User::Leave( KErrNotFound );
+ }
+
+ TFileName dll;
+ Dll::FileName( dll );
+ TFileName fileName( TParsePtrC( dll ).Drive() );
+ fileName += KIpVmbxAppEngineResourceFileDirectory;
+ fileName += KIpVmbxAppEngineResourceFileName;
+ CStringResourceReader* resourceReader =
+ CStringResourceReader::NewL( fileName );
+ CleanupStack::PushL( resourceReader );
+
+ if ( aMessageBody8.MaxLength() == aMessageBody8.Length() )
+ {
+ User::Leave( KErrOverflow );
+ }
+ else
+ {
+ // Add another separator before optional messages
+ aMessageBody8.Append( KEndLine8 );
+ }
+
+ TPtrC8 messagePtr8;
+ TPtrC8 tagPtr8;
+ TPtrC8 dataPtr8;
+ TPtrC8 posPtr8( aContent8 );
+ TPtrC8 colon8;
+ TInt messages = 0;
+ HBufC* locTemp( HBufC::NewLC( KSmsLength ) );
+ TPtr locPtr( locTemp->Des() );
+ TPtrC resourcePtr( resourceReader->ReadResourceString( R_VOIP_VM_HEADER_COUNT ) );
+ HBufC* variant( NULL );
+ TLex8 analyzer8;
+ HBufC8* partTemp( HBufC8::NewLC( KSmsLength ) );
+ TPtr8 partPtr( partTemp->Des() );
+ TBool messageEmpty = EFalse; // prevents creation of empty optional message part
+ TBool appendMsgChange = EFalse;
+ // Start sorting through message
+ do
+ {
+ if ( ( KErrNotFound !=
+ posPtr8.Left( KCrlf8().Length() ).FindF( KCrlf8 ) ||
+ 0 == messages ) && !messageEmpty )
+ {
+ // beginning of optional messages or another optional message
+ if ( messages <= KMaxMsgDescriptions )
+ {
+ // add message count for first/next optional message
+ // count is limited to two numbers = 99 (prevents also overflow when
+ // KTotal is replaced)
+ TBuf< KTwoChars > appendNum;
+ appendNum.AppendNum( ++messages );
+ locPtr.Zero();
+ locPtr.Append( KOptionalSeparator );
+ locPtr.Append( resourcePtr );
+ locPtr.Replace( locPtr.Find( KTotal ), KTotal().Length(), appendNum );
+ locPtr.Append( KEndLine );
+ posPtr8.Set( posPtr8.Mid( KCrlf8().Length() ) );
+ messageEmpty = ETrue;
+ appendMsgChange = ETrue;
+ }
+ else
+ {
+ User::Leave( KErrOverflow );
+ }
+ }
+ messagePtr8.Set( FetchMessagePartL( posPtr8 ) );
+ analyzer8.Assign(
+ messagePtr8.Left( messagePtr8.Find( KColon8 ) + KOneChar ) );
+ tagPtr8.Set( analyzer8.NextToken() );
+ if ( KErrNotFound == tagPtr8.Find( KColon8 ) )
+ {
+ // colon required
+ colon8.Set( analyzer8.NextToken() );
+ }
+ analyzer8.Assign(
+ messagePtr8.Mid( messagePtr8.Find( KColon8 ) + KOneChar ) );
+ analyzer8.SkipSpace(); // skipping ws's since one newline is added later
+ if ( analyzer8.Eos() )
+ {
+ // data contained only ws's we must decrease counter by length of CRLF
+ analyzer8.UnGet();
+ analyzer8.UnGet();
+ }
+ dataPtr8.Set(
+ messagePtr8.Mid(
+ messagePtr8.Find( KColon8 ) + KOneChar + analyzer8.Offset(),
+ analyzer8.Remainder().Length() - KCrlf8().Length() ) );
+ posPtr8.Set( posPtr8.Mid( messagePtr8.Length() ) );
+ analyzer8.Assign( posPtr8 );
+
+ variant = TranslateTagL( tagPtr8, *resourceReader );
+
+ if ( variant &&
+ variant->Length() + dataPtr8.Length() + KOneChar <
+ partPtr.MaxLength() - partPtr.Length() )
+ {
+ // Resource translated to users phone variant language.
+ // Optional message headers other than these aren't supported
+ // because language cannot be verified and might differ from phone
+ // variant language.
+ partPtr.Append( *variant );
+ partPtr.Append( dataPtr8 );
+ partPtr.Append( KEndLine8 );
+ messageEmpty = EFalse;
+ }
+ delete variant;
+ variant = NULL;
+
+ if ( !appendMsgChange && !messageEmpty && aMessageBody8.MaxLength() - aMessageBody8.Length() >= partPtr.Length() )
+ {
+ aMessageBody8.Append( partPtr );
+ partPtr.Zero();
+ }
+ else
+ if ( !messageEmpty && aMessageBody8.MaxLength() - aMessageBody8.Length() >= partPtr.Length() + locPtr.Length() )
+ {
+ // Content OK, append to actual actual message
+ aMessageBody8.Append( locPtr );
+ aMessageBody8.Append( partPtr );
+ partPtr.Zero();
+ messageEmpty = EFalse;
+ appendMsgChange = EFalse;
+ }
+ else
+ {
+ // partial ptr was too long for message, reset and try next part from content
+ partPtr.Zero();
+ messageEmpty = ETrue;
+ }
+
+ }while( !analyzer8.Eos() );
+
+ CleanupStack::PopAndDestroy( partTemp );
+ CleanupStack::PopAndDestroy( locTemp );
+ CleanupStack::PopAndDestroy( resourceReader );
+
+ IPVMEPRINT( "CIpVmbxEngine::ParseOptionalHeadersL - OUT" );
+ }
+
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::ReadResourcesL(
+ const TDesC8& aTotalMessages8,
+ const TDesC8& aNewMessages8,
+ TDes8& aMessagesBody8 )
+ {
+ IPVMEPRINT( "CIpVmbxEngine::ReadResourcesL - IN" );
+ __ASSERT_DEBUG( KSmsLength == aMessagesBody8.MaxLength(), Panic( KErrArgument ) );
+
+ TFileName dll;
+ Dll::FileName( dll );
+ TFileName fileName( TParsePtrC( dll ).Drive() );
+ fileName += KIpVmbxAppEngineResourceFileDirectory;
+ fileName += KIpVmbxAppEngineResourceFileName;
+ CStringResourceReader* resourceReader =
+ CStringResourceReader::NewL( fileName );
+ CleanupStack::PushL( resourceReader );
+ TPtrC messageText;
+
+ if( !aTotalMessages8.Length() )
+ {
+ messageText.Set(
+ resourceReader->ReadResourceString( R_VOIP_VM_MSG_SUM_UNKNOWN ) );
+ aMessagesBody8.Copy( messageText );
+ }
+ else if( !aNewMessages8.Compare( KOneMessage ) &&
+ !aTotalMessages8.Compare( KOneMessage ) )
+ {
+ messageText.Set(
+ resourceReader->ReadResourceString( R_VOIP_NEW_VOICE_MESSAGE ) );
+ aMessagesBody8.Copy( messageText );
+ }
+ else if( !aNewMessages8.Compare( KOneMessage ) &&
+ aTotalMessages8.Compare( KOneMessage ) )
+ {
+ TBuf< KSmsLength > tempText;
+ messageText.Set(
+ resourceReader->ReadResourceString(
+ R_VOIP_NEW_AND_OLD_VOICE_MSG ) );
+ tempText.Copy( messageText );
+ TInt atPosFirst = tempText.Find( KTotal );
+ TInt messageLength = tempText.Length();
+ TPtrC messagePart1 = tempText.Mid( 0 , atPosFirst );
+ aMessagesBody8.Copy( messagePart1 );
+ aMessagesBody8.Append( aTotalMessages8.Left( KMaxMessageDigits ) );
+ TPtrC messagePart2 = tempText.Mid(
+ atPosFirst + KTotalLength,
+ messageLength - atPosFirst - KTotalLength );
+ aMessagesBody8.Append( messagePart2 );
+ }
+ else if( aNewMessages8.Compare( KOneMessage ) &&
+ aTotalMessages8.Compare( KOneMessage ) )
+ {
+ messageText.Set(
+ resourceReader->ReadResourceString( R_VOIP_NEW_VOICE_MESSAGES ) );
+
+ // Search new messages location and replace with real value
+ TBuf8< KSmsLength > messagePart1;
+ TInt pos = messageText.Find( KTotalNew );
+ messagePart1.Copy( messageText.Left( pos ) );
+ messagePart1.Append( aNewMessages8.Left( KMaxMessageDigits ) );
+ messagePart1.Append( messageText.Mid( pos + KTotalOldLength ) );
+
+ // Search total messages location and replace with real value
+ TBuf8< KSmsLength > messagePart2;
+ pos = messagePart1.Find( KTotalOld );
+ messagePart2.Copy( messagePart1.Left( pos ) );
+ messagePart2.Append( aTotalMessages8.Left( KMaxMessageDigits ) );
+ messagePart2.Append( messagePart1.Mid( pos + KTotalOldLength ) );
+
+ aMessagesBody8.Append( messagePart2 );
+ }
+
+ aMessagesBody8.Append( KEndLine );
+ aMessagesBody8.Append( KEndLine );
+ aMessagesBody8.Append(
+ resourceReader->ReadResourceString( R_VOIP_VM_MSG_ACCOUNT ) );
+
+ CleanupStack::PopAndDestroy( resourceReader );
+
+ IPVMEPRINT( "CIpVmbxEngine::ReadResourcesL - OUT" );
+ }
+
+// ----------------------------------------------------------------------------
+//
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::CreateMessageBodyL(
+ const TDesC8& aContent8,
+ const TDesC8& aTotal8,
+ const TDesC8& aNew8,
+ TDes8& aFrom8,
+ TDes8& aMessageBody8 )
+ {
+ aFrom8.TrimRight();
+ if ( 0 == aFrom8.Length() )
+ {
+ User::Leave( KErrArgument );
+ }
+
+ ReadResourcesL(
+ aTotal8,
+ aNew8,
+ aMessageBody8 );
+
+ if( aFrom8.Length() &&
+ aMessageBody8.Length() + aFrom8.Length() < KSmsLength )
+ {
+ aMessageBody8.Append( aFrom8 );
+ }
+
+ TRAP_IGNORE( ParseOptionalHeadersL( aContent8, aMessageBody8 ) );
+ }
+
+// ----------------------------------------------------------------------------
+// Creates translated tag from resources
+// ----------------------------------------------------------------------------
+//
+HBufC* CIpVmbxEngine::TranslateTagL(
+ const TDesC8& aTagPtr8,
+ CStringResourceReader& aResourceReader ) const
+ {
+ HBufC* locTemp( NULL );
+ TLex8 analyzer8( aTagPtr8 );
+ TPtrC8 tag( analyzer8.NextToken() );
+ if ( KErrNotFound != tag.Find( KColon8 ) )
+ {
+ // remove possible colon char to make tag matching easier
+ tag.Set( tag.Left( tag.Length() - KOneChar ) );
+ }
+ TIpVmbxParseType::TParseTypes i = TIpVmbxParseType::EDetailTo;
+ TIpVmbxParseType parseType;
+ for ( ;i <= TIpVmbxParseType::EDetailId; i++ )
+ {
+ parseType.Set( i );
+ if ( 0 == tag.MatchF( parseType.Tag() ) )
+ {
+ i = TIpVmbxParseType::EDetailId;
+ locTemp =
+ aResourceReader.ReadResourceString(
+ parseType.ResourceId() ).AllocL();
+ }
+ };
+
+ return locTemp;
+ }
+
+// ----------------------------------------------------------------------------
+// Gets and validates part of message.
+// ----------------------------------------------------------------------------
+//
+TPtrC8 CIpVmbxEngine::FetchMessagePartL( const TDesC8& aContent8 ) const
+ {
+ IPVMEPRINT( "CIpVmbxEngine::FetchMessagePartL - IN" );
+
+ TInt crlf = User::LeaveIfError( aContent8.FindF( KCrlf8 ) ) + KCrlf8().Length();
+ // check for optional CRLF
+ while( crlf < aContent8.Length() &&
+ ( KErrNotFound != KHTab8().Locate( aContent8[crlf] ) ||
+ KErrNotFound != KSpace8().Locate( aContent8[crlf] ) ) )
+ {
+ // This was optional CRLF, try next one
+ crlf += User::LeaveIfError( aContent8.Mid( crlf ).FindF( KCrlf8 ) ) + KTwoChars;
+ }
+ // now we should have correct partial data
+ TPtrC8 part8( aContent8.Left( crlf ) );
+ TLex8 analyzer8( part8 );
+
+ TPtrC8 tagPtr8(
+ part8.Left( User::LeaveIfError( part8.Find( KColon8 ) ) ) );
+ if ( !tagPtr8.Length() )
+ {
+ // At least one character required
+ User::Leave( KErrCorrupt );
+ }
+ TestNamePartL( tagPtr8 );
+
+ TPtrC8 dataPtr8( part8.Mid( tagPtr8.Length() ) );
+ TestValuePartL( dataPtr8 );
+
+ IPVMEPRINT( "CIpVmbxEngine::FetchMessagePartL - OUT" );
+ return aContent8.Left( part8.Length() );
+ }
+
+// ----------------------------------------------------------------------------
+// Validates name part of message.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::TestNamePartL( const TDesC8& aNameHeader8 ) const
+ {
+ // Check legal chars %x41-5A / %x61-7A %x30-39 ( 2d, 2e, 21, 25, 2a, 5f, 2b, 60, 27, 7e)
+ // from name header
+ const TFixedArray< TText8, KSpecialNameCharsCount > legalCharsArray(
+ &KSpecialNameChars[0], KSpecialNameCharsCount );
+ TChar curChar;
+ TLex8 analyzer8( aNameHeader8 );
+ curChar = analyzer8.Get();
+ TBool valid = ETrue;
+ while ( !curChar.Eos() && valid )
+ {
+ valid = EFalse;
+ if ( KNameDigitLowest <= curChar && KNameDigitHighest >= curChar ||
+ KNameCharUpLowest <= curChar && KNameCharUpHighest >= curChar ||
+ KNameCharLowLowest <= curChar && KNameCharLowHighest >= curChar )
+ {
+ valid = ETrue;
+ }
+ for ( TInt i = 0; legalCharsArray.Count() > i && !valid; i++ )
+ {
+ if ( curChar == legalCharsArray.At( i ) )
+ {
+ valid = ETrue;
+ }
+ }
+ if ( valid )
+ {
+ curChar = analyzer8.Get();
+ }
+ }
+ if ( !valid )
+ {
+ valid = ETrue;
+ while ( !curChar.Eos() && valid )
+ {
+ valid = EFalse;
+ if ( KSpace == curChar || KHTab == curChar )
+ {
+ valid = ETrue;
+ }
+ curChar = analyzer8.Get();
+ }
+ }
+ if ( !valid )
+ {
+ User::Leave( KErrCorrupt );
+ }
+ }
+
+// ----------------------------------------------------------------------------
+// Validates value part of message.
+// ----------------------------------------------------------------------------
+//
+void CIpVmbxEngine::TestValuePartL( const TDesC8& aValueHeader8 ) const
+ {
+ TBool valid = ETrue;
+ TChar curChar;
+ TLex8 analyzer8( aValueHeader8 );
+ curChar = analyzer8.Get();
+
+ while ( !curChar.Eos() && valid )
+ {
+ valid = EFalse;
+ if ( KValueLowestChar <= curChar && KValueHighestChar >= curChar )
+ {
+ valid = ETrue;
+ }
+ else
+ if ( KCr == curChar || KLf == curChar || KHTab == curChar )
+ {
+ valid = ETrue;
+ }
+ curChar = analyzer8.Get();
+ }
+ if ( !valid )
+ {
+ User::Leave( KErrCorrupt );
+ }
+ }
+