networksecurity/tls/protocol/recordprotocolevents.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 15 Sep 2010 13:53:10 +0300
branchRCL_3
changeset 67 bb2423252ea3
parent 0 af10295192d8
child 75 c1029e558ef5
permissions -rw-r--r--
Revision: 201036 Kit: 201036

// Copyright (c) 2003-2009 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:
// Record protocol source file. 
// Describes the implementation of the Record protocol classes: Record parser 
// (CRecordParser) and the Record composer (CRecordComposer).
//
//

/**
 @file
*/

#include <es_sock.h>
#include "recordprotocolevents.h"
#include "AlertProtocolEvents.h"
#include "handshakereceiveevents.h"
#include "changecipherevents.h"
#include "tlshandshake.h"

void CRecordParser::ReConstructL( CStateMachine* aStateMachine, const TPtr8& /*aHeldData*/, CSendAlert& aSendAlert )
/**
 * This method sets the iStateMachine and iNext (CAsynchEvent*) data members 
 * inherited from the CAsynchEvent class, in addition to some of its own data members. 
 * It also creates a list of the expected Record protocol content types.
 */
    {
	LOG(Log::Printf(_L("CRecordParser::ReConstructL(CStateMachine*, const TPtr8&, CSendAlert&)"));)

	iStateMachine = aStateMachine;

    if (!iDataIn)
        {
        iDataIn = HBufC8::NewL( KTlsRecordPrealocate );
        }

    ReConstructL();

	CTlsEvent* alertItem = new(ELeave)CRecvAlert( *iStateMachine, *this, aSendAlert );
	//coverity[leave_without_push]
  	LOG(Log::Printf(_L("CRecvAlert %x - %x"), alertItem, (TUint)alertItem + sizeof( CRecvAlert ));)
	iExpRecordTypes.AddLast( *alertItem );

	iHandshakeParser = new(ELeave)CHandshakeParser( *iStateMachine, *this);
  	LOG(Log::Printf(_L("CHandshakeParser %x - %x"), iHandshakeParser, (TUint)iHandshakeParser + sizeof( CHandshakeParser ));)
	iExpRecordTypes.AddLast( *iHandshakeParser );
    }


void CRecordParser::ReConstructL()
/**
 * This method resets the state of the object as if it had just been created.
 */
    {
	LOG(Log::Printf(_L("CRecordParser::ReConstructL()"));)

	DestroyList();

	iHeldData.Zero();

	iReadState = iHeldData.Length() ? ETlsFragmentRecord : ETlsReceiveRecordHeader; 
   
	// Add TLS record protocol content types to the list of expected types
	// It is important that the Handshake parser object is last in the list
	if (iStateMachine->History() == ETlsHandshakeStart)
    	{
		// Change Cipher Spec protocol applies only during handshake negotiation
		__ASSERT_DEBUG( iTlsProvider, TlsPanic(ETlsPanicTlsProviderNotReady));
		CTlsEvent* ccsItem = new(ELeave)CRecvChangeCipherSpec( *iStateMachine, *this );
		//coverity[leave_without_push]
		LOG(Log::Printf(_L("RecvChangeCipherSpec %x - %x"), ccsItem, (TUint)ccsItem + sizeof( CRecvChangeCipherSpec ));)
		iExpRecordTypes.AddLast( *ccsItem );
	    }
	
	iNext = NULL;
    }


CRecordParser::~CRecordParser()
{
	LOG(Log::Printf(_L("CRecordParser::~CRecordParser()"));)

	DestroyList();
	delete iDecryptedData;
   delete iDataIn;
   Reset();
}

void CRecordParser::DestroyList()
{
	LOG(Log::Printf(_L("CRecordParser::DestroyList() of expected record protocol Content types"));)

	CTlsEvent* listItem;
    
   TSglQueIter<CTlsEvent> expListIter( iExpRecordTypes );
    expListIter.SetToFirst(); 
    while ( (listItem = expListIter++) != NULL )
        {
        iExpRecordTypes.Remove(*listItem);
        delete listItem;
        };
}

void CRecordParser::ChangeCipher()
{
   if ( iStateMachine->History() & ETlsChangeCipherSent )
   {
		__ASSERT_DEBUG( iActiveTlsSession != iTlsProvider->TlsSessionPtr(), TlsPanic(ETlsPanicInvalidTlsSession));
      delete iActiveTlsSession;
   }
   iActiveTlsSession = iTlsProvider->TlsSessionPtr();
   iReadSequenceNum = 0;
}

void CRecordParser::Reset()
{
	iReadSequenceNum = 0;	// reset the sequence number
   if ( iTlsProvider && iTlsProvider->TlsSessionPtr() && iActiveTlsSession != iTlsProvider->TlsSessionPtr() )
   {//this could happen just in case we re-negotiated unsuccessfully but passed CTlsProvider::Create()
      delete iActiveTlsSession;
   }
   iActiveTlsSession = NULL;
}

CTlsEvent* CRecordParser::LookUpEventL( TInt aRecordType )
/**
 * This method is used to determine which record protocol content type (handshake,
 * change cipher spec, alert, application data) will process a received message.
 * It cycles through the list of expected record types to see which one would
 * accept the received record.
 * 
 * @param aRecordType An integer representing the content type.
 * @return CTlsEvent* A pointer to the event that will process the received message.
 */
{
	LOG(Log::Printf(_L("CRecordParser::LookUpEventL()"));)

   TSglQueIter<CTlsEvent> expListIter( iExpRecordTypes );
	expListIter.SetToFirst();
	CTlsEvent* pEvent;

	while ( ((pEvent = expListIter++) != 0) && !pEvent->AcceptRecord( aRecordType ) )
      {
      }
   
	// When processing application data, NULL is returned as this is the only class processing it
	if ( !pEvent )
   {
      if ( aRecordType != ETlsAppDataContentType )
      {
		   User::Leave( KErrSSLAlertUnexpectedMessage );
      }
      else if ( iStateMachine->History() != KTlsApplicationData )
      {//application adta received during handshaking
         if ( iIgnoreRecordAllowed )
         {//in case client's initiated re-negotiation while the server is still sending some app data
   			LOG(Log::Printf(_L("Application data received while re-negotiating => skip the record"));)
            iIgnoreRecord = 1;
         }
         else
         {
     		   User::Leave( KErrSSLAlertUnexpectedMessage );
         }
      }
   }
   else
   {//once i've received any record but application data one no record will be ignored
      iIgnoreRecordAllowed = 0;
      iIgnoreRecord = 0;
   }
	return pEvent;

}

TInt CRecordParser::ParseHeaderL()
/**
 * This method parses the record header to check that the Protocol version is as 
 * expected. It also retrieves and returns the length of the record fragment from 
 * the Record Protocol header.
 */
{
	LOG(Log::Printf(_L("CRecordParser::ParseHeaderL()"));)

	TUint8* dataPtr = (TUint8*) iDataIn->Des().Ptr();
   iTlsRecordType = static_cast<ETlsRecordType>(dataPtr[KTlsRecordTypeOffset]);
	iNext = LookUpEventL( iTlsRecordType );

   const TTLSProtocolVersion& tlsVersion = iTlsProvider->Attributes()->iNegotiatedProtocol;
   if ( tlsVersion.iMajor )
      {
	   if ( dataPtr[KTlsRecordVersionOffset] != tlsVersion.iMajor ||
		   dataPtr[KTlsRecordVersionOffset + 1] != tlsVersion.iMinor )
	      {
		   User::Leave( KErrSSLAlertProtocolVersion );
	      }
      }
   //no protocol's been agreed yet => must be one of these
	else if ( (dataPtr[KTlsRecordVersionOffset] != KSSL3_0.iMajor ||
		   dataPtr[KTlsRecordVersionOffset + 1] != KSSL3_0.iMinor) &&
         (dataPtr[KTlsRecordVersionOffset] != KTLS1_0.iMajor ||
		   dataPtr[KTlsRecordVersionOffset + 1] != KTLS1_0.iMinor) )
	      {
		   User::Leave( KErrSSLAlertProtocolVersion );
	      }

	//extract and return the fragment length from the header
	TBigEndian value;
	return value.GetValue( dataPtr + KTlsRecordLengthOffset, KTlsRecordBodyLength );
}

void CRecordParser::CancelAll()
/**
 * Cancel all read functionality.
 * This method should also cancel decompression when it is supported.
 */
{
	LOG(Log::Printf(_L("CRecordParser::CancelAll()"));)

	iSocket.CancelRead();
	// Cancel the asynchronous service request made.
	iSocket.CancelRecv();
	iTlsProvider->CancelRequest();
	if ( iActiveTlsSession )
	    {
		iActiveTlsSession->CancelRequest();
		}

    TRAPD(ret, ReConstructL());
    if ( ret != KErrNone )
        {
        LOG(Log::Printf(_L("ReConstructL() returned the error %d"), ret);)
        }
}

void CRecordParser::DispatchData()
{
	TInt nUserFree = iUserData ? iUserMaxLength - iUserData->Length() : 0; 
	LOG(Log::Printf(_L("CRecordParser::DispatchData() - %x, %d"), iUserData, nUserFree);)

	if ( iHeldData.Length() )
	{
   	LOG(Log::Printf(_L("iHeldData.Length(), %d"), iHeldData.Length());)
		// Client has data left. Assert that its buffer exists.
		__ASSERT_DEBUG( iUserData, TlsPanic(ETlsPanicNoUserData));

		TInt nToCopy = Min( nUserFree, iHeldData.Length() ); // amount of data to copy
		iUserData->Append( iHeldData.Ptr(), nToCopy );
			
		TInt n = iHeldData.Length() - nToCopy;	// Reset the held data ptr to what is left-over
		iHeldData.Set( (TUint8*)iHeldData.Ptr() + nToCopy, n, n );
			
	}	
	else
	{
		iHeldData.Set(iPtrHBuf);

     	LOG(Log::Printf(_L("iHeldData.Length() == 0 %d"), iHeldData.Length());)
      if ( iUserData && nUserFree >= iHeldData.Length() )
		{	// User's data buffer is big enough, copy data directly into it.
			iUserData->Append(iHeldData.Ptr(), iHeldData.Length());
			iHeldData.Zero();
		}
		else if ( iUserData && nUserFree < iHeldData.Length() )
		{	// User's data buffer is not long enough. Copy nUserFree bytes and
			// save the rest for later
			iUserData->Append( iHeldData.Ptr(), nUserFree );
			TInt n = iHeldData.Length() - nUserFree;
			iHeldData.Set( (TUint8*)iHeldData.Ptr() + nUserFree, n, n ); // Left-over data is in iHeldData
		}
	}
#ifdef _DEBUG
   if ( iUserData )
      {
  	   LOG(Log::Printf(_L("CRecordParser::DispatchData() end iUserData->Length() = %d, %d"), iUserData->Length(), iHeldData.Length());)
      }
#endif
}

CAsynchEvent* CRecordParser::ProcessL( TRequestStatus& aStatus )
/**
 * This asynchronous method parses a received SSL/TLS record.
 * The record protocol is responsible for decrypting, message authentication (verifying 
 * data), decompressing and reassembling data. 
 *
 * Note that the record header is transmitted in the clear (i.e., it is not encrypted 
 * or MACed). As such, only the record body is passed through to Security for deciphering
 * and validation. Also, only NULL compression is supported. MAC verification + Decryption 
 * only happen when security services are active.
 * 
 * @param aStatus TRequestStatus object
 * @return CAsynchEvent* A pointer to an asynchronous event (the next event to be processed).
 */
{

	if ( iStateMachine->History() == KTlsApplicationData )
	{
	LOG(Log::Printf(_L("CRecordParser::ProcessL()"));)
	}
	switch ( iReadState )
	{
	case ETlsReceiveRecordHeader:	
		{
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - ETlsReceiveRecordHeader"));)

			// Prepare to receive a new record fragment. Receive the header initially.
			// Parse it and determine the fragment length. Check that the record
			// fragment length does not exceed the allowed maximum for a SSL/TLS record
			if ( iStateMachine->Fragment() )	// For App data rx, this pointer is NULL
			{
				iStateMachine->Fragment()->Des().Zero(); 
			}
			iPtrHBuf.Set( (TUint8*)iDataIn->Des().Ptr(), 0, KTlsRecordHeaderSize );

			iSocket.Recv( iPtrHBuf, 0, aStatus );
			iReadState = ETlsReceiveRecordBody;
			break;
		}
	case ETlsReceiveRecordBody:
		{
			TInt fragLength = ParseHeaderL();	// Returns the length of the fragment
            //parameters swap due to the defect
			LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iPtrHBuf.Ptr(), iPtrHBuf.Length() ));
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - ETlsReceiveRecordBody of length %x"), fragLength);)

    		User::LeaveIfError( fragLength > KTlsRecordMaxBodySize || fragLength < 0 ? KErrSSLAlertRecordOverflow : KErrNone );
         if ( fragLength > iDataIn->Des().MaxLength() )
            {
            delete iDataIn;
            iDataIn = NULL;
            iDataIn = HBufC8::NewL( fragLength );
            }
			
			// Read the record fragment and overwrite the header (already been evaluated)
			iPtrHBuf.Set( (TUint8*)iDataIn->Des().Ptr(), 0, fragLength );	
			iSocket.Recv( iPtrHBuf, 0, aStatus );

			iReadState = ETlsCipherRecord;
			break;
		}
	case ETlsCipherRecord:
		{
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - ETlsCipherRecord"));)

         if ( iIgnoreRecord )
         {//nothing to be done => skip it all
				iReadState = ETlsReceiveRecordHeader;
         }
         else
         {
			   // Decrypt and verify the MACed record fragment, if appropriate
			   // Initialise the buffer for the plain text data.
			   if (iActiveTlsSession) // Security is active
			   {
				   TRecordProtocol type = (TRecordProtocol) iTlsRecordType;
				   User::LeaveIfError( iActiveTlsSession->DecryptAndVerifyL(iPtrHBuf, iDecryptedData, iReadSequenceNum, type ) );
				   // If error is not zero, it should generate an alert.
  				   iPtrHBuf.Set( iDecryptedData->Des() );
				   User::LeaveIfError( iPtrHBuf.Length() > KTlsRecordPlainText ? KErrSSLAlertRecordOverflow : KErrNone );
			   }
	   
			   // Update the sequence number
			   iReadSequenceNum++;	// Increment the sequence number

			   iReadState = ETlsCompressRecord;
         }

         TRequestStatus* p=&aStatus;
			User::RequestComplete( p, KErrNone );

			break;
		}
	case ETlsCompressRecord:
		{
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - ETlsCompressRecord"));)
					
			// De-compresses the SSL/TLS plain text data. Only NULL compression is 
			// currently supported (identity operation). Fall through to the next state. 
			iReadState = ETlsFragmentRecord;
		}
	case ETlsFragmentRecord:
		{
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - ETlsFragmentRecord"));)

			// In application data mode, if the Client's data buffer is big enough,
			// then copy the verified data directly into it as an append. Otherwise,
			// copy as much as is possible (dependent on the free space in the client's
			// buffer.
			// The 'if' statement below solves the case where, in Application data 
			// mode, the clients data buffer (iUserData) is not big enough to hold the 
			// received record fragment. nUserFree is the amount of free space in the 
			// User's data buffer. This is the amount of data that is appended to its
			// buffer whilst the rest is held till the client frees up space.
			
         if ( (iStateMachine->History() == KTlsApplicationData && iTlsRecordType == ETlsAppDataContentType) ||
            (iStateMachine->History() != KTlsApplicationData && iTlsRecordType == ETlsHandshakeContentType))
         {//don't put wrong data into the user buffer
            DispatchData();
         }
	      if ( iHeldData.Length() == 0 )
	      {	//prepare to read new record and clear the statemachine buffer
		      iReadState = ETlsReceiveRecordHeader;
	      }

			TRequestStatus* p=&aStatus;
			User::RequestComplete( p, KErrNone );
        	LOG(Log::Printf(_L("CRecordParser::ProcessL() iNext == %x"), iNext);)
			return iNext;	// record body parser looked up in ParseHeaderL() function 
								// or NULL if in application data mode
		}	// case statement
	default:
		{
			LOG(Log::Printf(_L("CRecordParser::ProcessL() - Default statement, unknown state"));)
			__ASSERT_ALWAYS( EFalse, TlsPanic(ETlsPanicInvalidProcessState));
		}
	}	// switch statement
   
	return this;
}

//
void CRecordComposer::ReConstructL( CStateMachine* aStateMachine, TInt aCurrentPos )
/**
 * This method sets the iStateMachine and iNext (CAsynchEvent*) data members 
 * inherited from the CAsynchEvent class, in addition to some of its own data members.
 * It also sets the record's content type. Once set, this will remain, unless it's 
 * interrupted by an alert or handshake negotiation. In this instance, the 
 * ReConstructL() function will be called again.
 */
{
	LOG(Log::Printf(_L("CRecordComposer::ReConstructL()"));)

	iStateMachine = aStateMachine;
	iWriteState = ETlsFragmentRecord;
	iCurrentPos = aCurrentPos;

	TUint8* dataPtr = (TUint8*) iDataOut.Ptr();
	dataPtr[KTlsRecordTypeOffset] = ETlsAppDataContentType; // Default setting
   
	iNext = 0;
}

void CRecordComposer::ChangeCipher()
{
   if ( iStateMachine->History() & ETlsChangeCipherRecv )
   {
		__ASSERT_DEBUG( iActiveTlsSession != iTlsProvider->TlsSessionPtr(), TlsPanic(ETlsPanicInvalidTlsSession));
      delete iActiveTlsSession;
   }
   iActiveTlsSession = iTlsProvider->TlsSessionPtr();
   iWriteSequenceNum = 0;
}

void CRecordComposer::ComposeHeader( TInt aLength )
/**
 * This method composes a Record protocol header. It sets the Protocol version,
 * and the length of the Protocol message (the length is in big endian format). 
 *
 * Note that the content type has previously been set by the message class which 
 * prepared the Record payload. For the first transmitted message (ClientHello),
 * SSL v3.0 is used (to enable TLS to SSL fallback). The record composer's version
 * object is then set to NULL.
 * Subsequent transmitted messages then use the negotiated protocol version.
 */
{
	LOG(Log::Printf(_L("CRecordComposer::ComposeHeader()"));)

	TUint8* dataPtr = (TUint8*) iDataOut.Ptr();
   if ( iTlsVersion )
      {
   	dataPtr[KTlsRecordVersionOffset] = iTlsVersion->iMajor;
	   dataPtr[KTlsRecordVersionOffset + 1] = iTlsVersion->iMinor;
      }
   else
      {
   	dataPtr[KTlsRecordVersionOffset] = KSSL3_0.iMajor;
	   dataPtr[KTlsRecordVersionOffset + 1] = KSSL3_0.iMinor;
      }
      
	TBigEndian value;
	value.SetValue( (TUint8*)iDataOut.Ptr() + KTlsRecordLengthOffset, KTlsRecordBodyLength, aLength );
}

void CRecordComposer::CancelAll()
/**
 * Cancel all write functionality.
 * This method should also cancel compression when it is supported.
 */
{
	LOG(Log::Printf(_L("CRecordComposer::CancelAll()"));)

	iSocket.CancelSend();
	iTlsProvider->CancelRequest();
	
	if (iActiveTlsSession)
		iActiveTlsSession->CancelRequest();
}

CAsynchEvent* CRecordComposer::ProcessL( TRequestStatus& aStatus )
/**
 * This asynchronous method composes a SSL/TLS record.
 * The record protocol is responsible for fragmenting, compressing, message 
 * authentication (adding a MAC) and encrypting data. It then transmits the result.
 *
 * Compression, MAC computation and Encryption only happen when security services are 
 * active. At present, only NULL compression is supported.
 * 
 * @param aStatus TRequestStatus object
 * @return CAsynchEvent* A pointer to an asynchronous event (the next event to be 
 * processed).
 */
{
	LOG(Log::Printf(_L("CRecordComposer::ProcessL()"));)

	// Get a pointer to the data to be transmitted via the Record layer.
	TDesC8* pFragment = iUserData ? iUserData : iStateMachine->Fragment();
	
	switch ( iWriteState )
	{
	case ETlsFragmentRecord:
		{
			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - ETlsFragmentRecord"));)

			// Fragments the SSL/TLS plain text data.
			// The current position within the plain text buffer is initially set to 
			// zero by the state machine (when CRecordComposer::ReconstructL() is called). 
			// The maximum amount of plain text that can be sent is KTlsRecordPlainText  
			// and this data is stored in the iPtrHBuf descriptor.

			iSentPlainTextLength = Min( pFragment->Length() - iCurrentPos, KTlsRecordPlainTextToSend ); 
			iPtrHBuf.Set( (TUint8*)pFragment->Ptr() + iCurrentPos, SentPlainTextLength() );
			iCurrentPos += iSentPlainTextLength; // keep track of fragmented plain text

			iWriteState = ETlsCompressRecord;
			// Fall through to the next case statement (no TRequestStatus to complete).
		}
	case ETlsCompressRecord:	
		{
			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - ETlsCompressRecord"));)

			// Compresses the SSL/TLS plain text data. 
			// NULL compression only so fall through to the next state
			iWriteState = ETlsCipherRecord;
		}
	case ETlsCipherRecord:
		{
			// Apply a MAC and encrypt the compressed SSL/TLS data.
			// MAC and encryption uses the current SSL/TLS connection state and only 
			// happens when the keys have been created and activated. The connection
			// state specifies the compression, encryption and MAC algorithms.
			// Set iPtrEncryptTo to a maximum length of the correct size (plainText + cipherText length)
			iPtrEncryptTo.Set( (TUint8*)iDataOut.Ptr() + KTlsRecordHeaderSize, 0, iPtrHBuf.Length() + KTlsRecordCipherText);

			// Check that security is active
			if (iActiveTlsSession)
			{ 
   			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - ETlsCipherRecord - Encrypt plain text"));)
				TRecordProtocol type = (TRecordProtocol) iDataOut[KTlsRecordTypeOffset];
				User::LeaveIfError( iActiveTlsSession->EncryptL(iPtrHBuf, iEncryptedData, iWriteSequenceNum, type) );
				iPtrEncryptTo.Copy( iEncryptedData->Des() );
				User::LeaveIfError( iPtrEncryptTo.Length() > iDataOut.MaxLength() ? KErrSSLAlertRecordOverflow : KErrNone );
			}
			else
			{ // No security is active. Copy the data into the iPtrEncryptTo buffer
   			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - ETlsCipherRecord - No Encryption plain text"));)
				iPtrEncryptTo.Copy(iPtrHBuf);
			}

			// Get the record length and compose the header. Prepend it to the record data
			iDataOut.SetLength( iPtrEncryptTo.Length() + KTlsRecordHeaderSize );
			ComposeHeader( iPtrEncryptTo.Length() );		
			iWriteState = ETlsTransmitRecord;
		}
	case ETlsTransmitRecord:
		{
			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - ETlsTransmitRecord"));)
            //parameters swap due to the defect
			LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, iDataOut.Ptr(), iDataOut.Length() ));

			// Transmit the Record data (header + data), activate the Write state (if
			// appropriate), increment or reset the Sequence number.
			iSocket.Send( iDataOut, 0, aStatus );
			
			iWriteSequenceNum++;	// Increment the sequence number
			if ( iDataOut[KTlsRecordTypeOffset] == ETlsChangeCipherContentType )
			{
				ChangeCipher();
			}

			iWriteState = ETlsFragmentRecord;
			
			// Check to see if there is any more plain text data to secure and transmit.
			// If yes, process this data. Otherwise, process the next asynchronous event.
			// The content type is reset to Application data as a default
      		
			if ( iCurrentPos >= pFragment->Length() )
			{
				if ( !IsAppData() )
				{	// See CSendAppData::OnCompletion().
					iCurrentPos = 0; // All the plain text has been secured and transmitted
				}

				return iNext;
			}

			break;
		}
	default:
		{
			LOG(Log::Printf(_L("CRecordComposer::ProcessL() - Default statement, unknown state"));)
			break;
		}
	} // switch statement

   return this;
}

CRecordComposer::~CRecordComposer()
/** 
 * Standard Destructor.
 */
{
	LOG(Log::Printf(_L("CRecordComposer::~CRecordComposer()"));)
	delete iEncryptedData;
}