wvuing/wvuieng/EngSrc/CCAMessageHandler.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:41:52 +0200
changeset 0 094583676ce7
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2005 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:  Handler class for incoming and outgoing messages
*
*/


#include "CCAMessageHandler.h"
#include "MCAChatInterface.h"
#include "MCAMessagesWriteInterface.h"
#include "MCAMessageUtils.h"
#include "MCAImpsImClient.h"
#include "MCASettings.h"
#include "MCAMessageErrorObserver.h"
#include "MCAImpsFactory.h"
#include "PublicEngineDefinitions.h"
#include "impsbuilddefinitions.h"
#include "mcabuffermemoryhandler.h"
#include "camessageutil.h"

#include <apmstd.h> // KMaxDataTypeLength
#include <utf.h>    // CnvUtfConverter

#include "ChatDebugPrint.h"

#include "ImpsCSPAllErrors.h"

// "test character identity and accents, ignore case"
const TInt KCollationLevel = 1;
const TInt KMemorySafeValue = 1024; // One kbyte for safe memory allocation.

//-----------------------------------------------------------------------------
// CCAMessageHandler::CCAMessageHandler
// ( Other items commented in header )
//-----------------------------------------------------------------------------
CCAMessageHandler::CCAMessageHandler( MCAChatInterface& aChatInterface,
                                      MCAMessageUtils& aMessageUtils,
                                      MCAImpsFactory* aIMPSFactory )
        : iMessageUtils( aMessageUtils ),
        iChatInterface( aChatInterface ),
        iImpsFactory( aIMPSFactory )
    {
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::~CCAMessageHandler
// ( Other items commented in header )
//-----------------------------------------------------------------------------
CCAMessageHandler::~CCAMessageHandler()
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::~CCAMessageHandler" );
    delete iIdle;
    if ( iSendBuffer )
        {
        iSendBuffer->SetObserver( NULL );
        }
    iErrorObservers.Close();
    iHoldingMessages.Close();
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::~CCAMessageHandler" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::NewL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
CCAMessageHandler* CCAMessageHandler::NewL(
    MCAChatInterface& aChatInterface,
    MCAMessageUtils& aMessageUtils,
    MCAImpsFactory* aIMPSFactory )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::NewL" );
    CCAMessageHandler* self = CCAMessageHandler::NewLC(
                                  aChatInterface,
                                  aMessageUtils,
                                  aIMPSFactory );
    CleanupStack::Pop( self );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::NewL" );
    return self;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::NewLC
// ( Other items commented in header )
//-----------------------------------------------------------------------------
CCAMessageHandler* CCAMessageHandler::NewLC(
    MCAChatInterface& aChatInterface,
    MCAMessageUtils& aMessageUtils,
    MCAImpsFactory* aIMPSFactory )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::NewLC" );
    CCAMessageHandler* self = new ( ELeave ) CCAMessageHandler(
        aChatInterface,
        aMessageUtils,
        aIMPSFactory );
    CleanupStack::PushL( self );
    self->ConstructL();
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::NewLC" );
    return self;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::ConstructL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::ConstructL()
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::ConstructL" );

    iImpsImClient = iImpsFactory->CreateImClientL();

    iSendBuffer = &iChatInterface.MessageReadInterfaceL( KNullDesC, KNullDesC );
    iSendBuffer->SetObserver( this );
    iIdle = CIdle::NewL( CActive::EPriorityIdle );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::ConstructL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleNewTextMessageL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleNewTextMessageL(  TInt aOpId,
                                                const TDesC& /*aMessageId*/,
                                                const TDesC& aSender,
                                                const TDesC& aGroupId,
                                                const MDesCArray& aRecipients,
                                                const MDesCArray& aScreenNames,
                                                const TDesC& aText,
                                                TImpsCspIdentifier& aCspId )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleNewTextMessageL" );

    // Don't use memory handling if this is local echo message
    MCAMessagesWriteInterface& messageContainer =
        iChatInterface.MessageWriteInterfaceL(
            aCspId.Sap(),
            aCspId.UserId(),
            ( aGroupId == KNullDesC ? aSender : aGroupId ) );

    if ( !( aGroupId != KNullDesC &&
            iLocalEchoInGroup &&
            messageContainer.OwnScreenName().CompareC( aSender, KCollationLevel, NULL ) == 0 ) )
        {
        if ( !iMessageUtils.MemoryHandler().FreeMemoryIfNeededL( KMemorySafeValue + aText.Size() ) )
            {
            NotifyMessageError( KErrNoMemory, NULL );
            User::Leave( KErrNoMemory );
            }
        }

    HBufC* sap = aCspId.Sap().AllocLC();
    HBufC* userId = aCspId.UserId().AllocLC();

    MCAMessageCreator::SMessageData data =
        {
        KMessageDataVersion,
        aOpId,
        *sap,
        *userId,
        aSender,
        ( aGroupId == KNullDesC ? aSender : aGroupId ),
        &aRecipients,
        &aScreenNames,
        aText,
        KNullDesC8,
        KNullDesC8,
        MCAMessage::EMessageReceived
        };
    HandleNewMessageL( data );

    CleanupStack::PopAndDestroy( 2, sap );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleNewTextMessageL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleNewContentMessageL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleNewContentMessageL(
    TInt aOpId,
    const TDesC& /*aMessageId*/,
    const TDesC& aSender,
    const TDesC& aGroupId,
    const MDesCArray& aRecipients,
    const MDesCArray& aScreenNames,
    const TDesC& aContentType,
    const TDesC8& aContent,
    TImpsCspIdentifier& aCspId )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleNewContentMessageL" );

    // Don't use memory handling if this is local echo message
    MCAMessagesWriteInterface& messageContainer =
        iChatInterface.MessageWriteInterfaceL(
            aCspId.Sap(),
            aCspId.UserId(),
            ( aGroupId == KNullDesC ? aSender : aGroupId ) );

    if ( !( aGroupId != KNullDesC &&
            iLocalEchoInGroup &&
            messageContainer.OwnScreenName().CompareC( aSender, KCollationLevel, NULL ) == 0 ) )
        {
        if ( !iMessageUtils.MemoryHandler().FreeMemoryIfNeededL( KMemorySafeValue + aContent.Size() ) )
            {
            NotifyMessageError( KErrNoMemory, NULL );
            User::Leave( KErrNoMemory );
            }
        }

    HBufC* sap = aCspId.Sap().AllocLC();
    HBufC* userId = aCspId.UserId().AllocLC();

    TBuf8< KMaxDataTypeLength > mimeType;
    CnvUtfConverter::ConvertFromUnicodeToUtf8( mimeType, aContentType );

    MCAMessageCreator::SMessageData data =
        {
        KMessageDataVersion,
        aOpId,
        *sap,
        *userId,
        aSender,
        ( aGroupId == KNullDesC ? aSender : aGroupId ),
        &aRecipients,
        &aScreenNames,
        KNullDesC,
        mimeType,
        aContent,
        MCAMessage::EMessageReceived
        };
    HandleNewMessageL( data );

    CleanupStack::PopAndDestroy( 2, sap );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleNewContentMessageL" );
    }


//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleNewMessageL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleNewMessageL(
    MCAMessageCreator::SMessageData& aData )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleNewMessageL" );
    if ( aData.iTargetId == KNullDesC )
        {
        // Invalid message.
        User::Leave( KErrArgument );
        }
    MCAMessagesWriteInterface& messageContainer =
        iChatInterface.MessageWriteInterfaceL( aData.iSapId,
                                               aData.iUserId,
                                               aData.iTargetId );

    MCAMessage* message = iMessageUtils.MessageCreator().CreateMessageL( aData );

    if ( iLocalEchoInGroup
         && ( message->MessageType() == MCAMessage::EMessageGroup )
         && ( messageContainer.OwnScreenName().CompareC( aData.iSender,
                                                         KCollationLevel, NULL ) == 0 ) )
        {
        // Do not show message because it is already shown with local echo.
        CHAT_DP( D_CHAT_LIT( "Group message not shown, because local echo." ) );
        delete message;
        return;
        }

    // set iTargetId to ScreenName if current received message type is EMessageWhisper
    if ( message->MessageType() == MCAMessage::EMessageWhisper || message->MessageType() == MCAMessage::EMessageGroup )
        {
        TInt count = aData.iScreenNames->MdcaCount();
        MCAMessageCreator::SMessageData data2 =
            {
            aData.iVersion,
            aData.iOpId,
            aData.iSapId,
            aData.iUserId,
            aData.iSender,
            ( count > 0 ) ? aData.iScreenNames->MdcaPoint( 0 ) : aData.iTargetId,
            aData.iRecipients,
            aData.iScreenNames,
            aData.iText,
            aData.iContentType,
            aData.iContentData,
            aData.iMessager
            };

        MCAMessage* message2 = iMessageUtils.MessageCreator().CreateMessageL( data2 );

        delete message;
        message = message2;
        }

    // Message can get different process state during creation. For example
    // EContentNotSupported
    if ( message->ContentProcessState() == MCAMessage::EContentNotProcessed )
        {
        message->SetProcessState( MCAMessage::EContentReady );
        }

    // Add message
    CAMessageUtil::AppendMessageWithDateStampL(
        *message,
        messageContainer,
        iMessageUtils.MessageCreator() );

    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleNewMessageL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleSendCompleteL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleSendCompleteL(	TInt aOpId,
                                             TBool aDeliveryReportOrdered,
                                             TImpsCspIdentifier& /*aCspId*/ )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleSendCompleteL" );

    CHAT_DP( D_CHAT_LIT( "Operationcode %d. Delivery ordered %d" ),
             aOpId, aDeliveryReportOrdered );
    HandleMessageSentL( KErrNone, aOpId, ETrue );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleSendCompleteL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleDeliveryReportL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleDeliveryReportL(	const TDesC& /*aMessageId*/,
                                               TInt /*aResult*/,
                                               const TDesC* /*aDescription*/,
                                               TImpsCspIdentifier& /*aCspId*/ )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleDeliveryReportL" );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleDeliveryReportL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleMessageEvent
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleMessageEvent( TMessageEventType aEvent,
                                            TInt /*aIndex*/ )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleMessageEvent" );
    switch ( aEvent )
        {
        case ENewMessage:
            {
            if ( !iIdle->IsActive() )
                {
                iIdle->Start( TCallBack( SendMessage, this ) );
                }
            break;
            }
        case EChatDeleted:
            {
            // send buffer was deleted
            iSendBuffer = NULL;
            break;
            }
        default:
            break;
        }
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleMessageEvent" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::SendMessage
// ( Other items commented in header )
//-----------------------------------------------------------------------------
TInt CCAMessageHandler::SendMessage( TAny *aInstance )
    {
    CCAMessageHandler* handler = static_cast< CCAMessageHandler* >( aInstance );
    handler->iSendLaunchLock = ETrue;
    TInt retVal = handler->DoSendMessage();
    handler->iSendLaunchLock = EFalse;
    return retVal;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::DoSendMessage
// ( Other items commented in header )
//-----------------------------------------------------------------------------
TInt CCAMessageHandler::DoSendMessage()
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::DoSendMessage" );

    if ( !iSendBuffer )
        {
        // send buffer was deleted, we're shutting down
        CHAT_DP( D_CHAT_LIT( "There is no send buffer." ) );
        return EFalse;
        }

    // 1. Check if there is message in holding.
    MCAMessage* holdingMessage = NextHoldingMessage();
    if ( !holdingMessage && !iSendBuffer->UnreadCount() )
        {
        CHAT_DP( D_CHAT_LIT( "There is not ready messages for sending." ) );
        return EFalse;
        }

    // 2. Choose new message from sendbuffer or use holding message if exists.
    MCAMessage& message = ( !holdingMessage ? iSendBuffer->ReadNextUnread()
                            : *holdingMessage );

    // 3. Add message to holding if needed
    TBool appendedToHolding( EFalse );
    TRAPD( error, appendedToHolding = AppendedToHoldingL( message ) );
#ifndef RD_SEND_NOT_SUPPORTED_CONTENT
    if ( error ) // Corrupted message, inform upstairs
        {
        NotifyMessageError(
            error == KErrOverflow ? error : ECorruptedContent, &message );
        // Remove sent message from send buffer, because answer will not come.
        TInt index = iSendBuffer->FindIndex( message );
        if ( index >= 0 )
            {
            iSendBuffer->DeleteMessage( index );
            }
        return ETrue;
        }
#endif //RD_SEND_NOT_SUPPORTED
    if ( appendedToHolding )
        {
        return ETrue;
        }

    // 4. Send message
    TInt opCode( 0 );
    TRAP( error, opCode = SendMessageToServerL( message ) );
    CHAT_DP( D_CHAT_LIT( "Send retval ( %d )" ), error );

    if ( error == KErrServerBusy )
        {
        TRAPD( err, iHoldingMessages.AppendL( &message ) );
        if ( err )
            {
            CActiveScheduler::Current()->Error( err );
            }
        return ETrue;
        }
    // 5. Local echo message
    TRAPD( error2, LocalEchoMessageL( message ) );
    if ( error2 != KErrNone )
        {
        NotifyMessageError( error2, &message );
        }

    // 6. Handle error
    if ( error != KErrNone )
        {
        TRAP( error2, HandleMessageSentFailedL( message, error ) );
        if ( error2 )
            {
            NotifyMessageError( error2, &message );
            }
        // Remove sent message from send buffer, because answer will not come.
        TInt index = iSendBuffer->FindIndex( message );
        if ( index >= 0 )
            {
            iSendBuffer->DeleteMessage( index );
            }
        }
    else // Send succesfull. -> Local echo
        {
        CHAT_DP( D_CHAT_LIT( "Address: %s, UserId: %s, Recipient %s" ),
                 &message.ServerAddress(),
                 &message.UserId(),
                 &message.Recipient() );
        message.SetOperationCode( opCode );
        }

    // 7. Check need of
    TInt unreadCount = iSendBuffer->UnreadCount();
    TInt holdingMessages( iHoldingMessages.Count() );
    CHAT_DP( D_CHAT_LIT( "Still %d messages to go. Send buffer has %d messages. \
                          Holding %d messages" ),
             unreadCount, iSendBuffer->MessageCount(), holdingMessages );

    CHAT_DP_FUNC_DONE( "CCAMessageHandler::DoSendMessage" );
    return Max( unreadCount, holdingMessages );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleMessageSentL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleMessageSentL( TInt aStatus, TInt aOperationCode,
                                            TBool aSuccess )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleMessageSentL" );
    CHAT_DP( D_CHAT_LIT( "OpCode = %d, Success = %d" ), aOperationCode, aSuccess );

    if ( !iSendBuffer )
        {
        // send buffer was deleted, we're shutting down
        CHAT_DP( D_CHAT_LIT( "There is no send buffer." ) );
        return;
        }

    TInt index = User::LeaveIfError( iSendBuffer->FindIndex( aOperationCode ) );

    if ( !aSuccess )
        {
        MCAMessage& message = iSendBuffer->Message( index );

        message.SetContainerInfo( NULL );

        MCAMessage* failedMessage =
            iMessageUtils.MessageCreator().CreateFailedMessageL( &message );
        CleanupDeletePushL( failedMessage );
        MCAMessagesWriteInterface& messageContainer =
            iChatInterface.MessageWriteInterfaceL( message.ServerAddress(),
                                                   message.UserId(),
                                                   message.Recipient() );
        CleanupStack::Pop( failedMessage );
        messageContainer.AppendL( failedMessage );
        NotifyMessageError( aStatus, &message );
        }
    iSendBuffer->DeleteMessage( index );
    CHAT_DP( D_CHAT_LIT( "Send buffer has %d messages." ),
             iSendBuffer->MessageCount() );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleMessageSentL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::SetLocalEchoInGroup
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::SetLocalEchoInGroup( TBool aLocalEchoInGroup )
    {
    iLocalEchoInGroup = aLocalEchoInGroup;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::NotifyMessageError
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::NotifyMessageError( TInt aStatus, MCAMessage* aMessage )
    {
    TInt count( iErrorObservers.Count() );
    for ( TInt a( 0 ); a < count; ++a )
        {
        iErrorObservers[ a ]->HandleMessageError( aStatus, aMessage );
        }
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::RegisterChatObserver
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::RegisterObserver( MCAMessageErrorObserver* aObserver )
    {
    TInt index = iErrorObservers.Find( aObserver );
    if ( index == KErrNotFound )
        {
        iErrorObservers.Append( aObserver );
        }
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::RegisterChatObserver
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::UnregisterObserver( MCAMessageErrorObserver* aObserver )
    {
    TInt index = iErrorObservers.Find( aObserver );
    if ( index >= 0 )
        {
        iErrorObservers.Remove( index );
        }
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleProcessingComplete
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleProcessingComplete(
    MCAContentProcessor& /*aProcessor*/,
    MCAMessage& /*aMessage*/,
    TInt aStatus )
    {
    if ( !iIdle->IsActive() && aStatus == KErrNone && !iSendLaunchLock )
        {
        iIdle->Start( TCallBack( SendMessage, this ) );
        }
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::NextHoldingMessage
// ( Other items commented in header )
//-----------------------------------------------------------------------------
MCAMessage* CCAMessageHandler::NextHoldingMessage()
    {
    TInt holdingMessages = iHoldingMessages.Count();
    MCAMessage* message = NULL;
    TInt index( 0 );
    // Check if holding messages hold one ready message
    for ( ; index < holdingMessages && !message; ++index )
        {
        MCAMessage* msg = iHoldingMessages[ index ];
        if ( ( msg->ContentProcessState() >= MCAMessage::EContentReady ) ||
             ( msg->ContentType() == MCAMessage::EContentText ) )
            {
            CHAT_DP( D_CHAT_LIT( "Ready holding message found. Index( %d )" ),
                     index );
            message = msg;
            iHoldingMessages.Remove( index );
            }
        }
    return message;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::NextHoldingMessage
// ( Other items commented in header )
//-----------------------------------------------------------------------------
TBool CCAMessageHandler::AppendedToHoldingL( MCAMessage& aMessage )
    {
    switch ( aMessage.ContentType() )
        {
        case MCAMessage::EContentPicture: // add scaler
            {
            if ( aMessage.ContentProcessState() < MCAMessage::EContentReady )
                {
                CHAT_DP( D_CHAT_LIT( "Picture message not yet ready for \
                                  sending. Put to holding." ) );
                TInt added = User::LeaveIfError( aMessage.AddContentProcessor(
                                                     iMessageUtils.ImageScaler(), this ) );
                if ( added )
                    {
                    iHoldingMessages.AppendL( &aMessage );
                    return ETrue;
                    }
                }
            break;
            }
        default: // Flowthrough other types
            {
#ifdef RD_SEND_NOT_SUPPORTED_CONTENT
            if ( aMessage.ContentProcessState() < MCAMessage::EContentReady )
                {
                CHAT_DP( D_CHAT_LIT( "Picture message not yet ready for \
                                  sending. Put to holding." ) );
                TInt added = User::LeaveIfError( aMessage.AddContentProcessor(
                                                     iMessageUtils.ImageScaler(), this ) );
                if ( added )
                    {
                    iHoldingMessages.AppendL( &aMessage );
                    return ETrue;
                    }
                }
#endif //RD_SEND_NOT_SUPPORTED_CONTENT            
            break;
            }
        }
    return EFalse;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::SendMessageToServerL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
TInt CCAMessageHandler::SendMessageToServerL( MCAMessage& aMessage )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::SendMessageToServerL" );

    TInt opCode( 0 );
    TBuf< KMaxDataTypeLength > mimeType;
    CnvUtfConverter::ConvertToUnicodeFromUtf8( mimeType, aMessage.MimeType() );

    // granularity of 1 because only one item appended to array
    CDesCArrayFlat* tempArray = NULL;
    const MDesCArray* recipients = NULL;

    const TDesC* sender = NULL;
    const TDesC* groupId = NULL;

    if ( aMessage.ScreenNames() )
        {
        groupId = &aMessage.Recipient();
        if ( aMessage.ScreenNames()->MdcaCount() > 0 )
            {
            recipients = aMessage.ScreenNames();
            }
        }
    else
        {
        tempArray = new ( ELeave ) CDesCArrayFlat( 1 );
        CleanupStack::PushL( tempArray );
        tempArray->AppendL( aMessage.Recipient() );
        }

    if ( aMessage.ContentType() == MCAMessage::EContentText )
        {
        CHAT_DP( D_CHAT_LIT( "Send text message" ) );
        opCode = iImpsImClient->SendTextMessageL(
                     sender,
                     tempArray,
                     groupId,
                     recipients,
                     aMessage.Text(),
                     EFalse );
        }
    else
        {
        CHAT_DP( D_CHAT_LIT( "Send content message" ) );
        TInt size( aMessage.ContentData().Size() );
        TInt maxSize( iImpsImClient->MaxTransactionContentLengthL() );
        if ( size > maxSize )
            {
            // content too big => can't send
            CHAT_DP( D_CHAT_LIT( "content too big: %d/%d" ), size, maxSize );
            User::Leave( KErrOverflow );
            }

        opCode = iImpsImClient->SendContentMessageL(
                     sender,
                     tempArray,
                     groupId,
                     recipients,
                     mimeType,
                     aMessage.ContentData(),
                     EFalse );
        }

    if ( tempArray )
        {
        CleanupStack::PopAndDestroy( tempArray );
        }

    CHAT_DP_FUNC_DONE( "CCAMessageHandler::SendMessageToServerL" );
    return opCode;
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::HandleMessageSentFailedL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::HandleMessageSentFailedL( MCAMessage& aMessage,
                                                  TInt aError )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::HandleMessageSentFailedL" );
    MCAMessage* failMessage =
        iMessageUtils.MessageCreator().CreateFailedMessageL( &aMessage );
    CleanupDeletePushL( failMessage );
    MCAMessagesWriteInterface& messageContainer =
        iChatInterface.MessageWriteInterfaceL( aMessage.ServerAddress(),
                                               aMessage.UserId(),
                                               aMessage.Recipient() );
    CleanupStack::Pop( failMessage );

    // Notify observers for error.
    NotifyMessageError( aError, &aMessage );
    messageContainer.AppendL( failMessage );
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::HandleMessageSentFailedL" );
    }

//-----------------------------------------------------------------------------
// CCAMessageHandler::LocalEchoMessageL
// ( Other items commented in header )
//-----------------------------------------------------------------------------
void CCAMessageHandler::LocalEchoMessageL( MCAMessage& aMessage )
    {
    CHAT_DP_FUNC_ENTER( "CCAMessageHandler::LocalEchoMessageL" );

    if ( aMessage.MessageType() == MCAMessage::EMessageGroup
         && !iLocalEchoInGroup )
        {
        // Local echo is not allowed in groups.
        CHAT_DP( D_CHAT_LIT( " Message not shown because local echo is not \
                    allowed." ) );
        return;
        }

    MCAMessagesWriteInterface& messageContainer =
        iChatInterface.MessageWriteInterfaceL( aMessage.ServerAddress(),
                                               aMessage.UserId(),
                                               aMessage.Recipient() );

    if ( aMessage.MessageType() == MCAMessage::EMessageWhisper )
        {
        // Local echo ones for all recipients
        const MDesCArray* recipients = aMessage.ScreenNames();
        TInt count( recipients->MdcaCount() );
        for ( TInt a( 0 ); a < count; ++a )
            {
            MCAMessageCreator::SMessageData data =
                {
                KMessageDataVersion,
                aMessage.OperationCode(),
                aMessage.ServerAddress(),
                aMessage.UserId(),
                aMessage.Sender(),
                recipients->MdcaPoint( a ),
                aMessage.Recipients(),
                recipients,
                aMessage.Text(),
                aMessage.MimeType(),
                aMessage.ContentData(),
                aMessage.MessagerType()
                };
            MCAMessage* message =
                iMessageUtils.MessageCreator().CreateMessageL( data );
            message->SetProcessState( MCAMessage::EContentReady );

            // Append message
            CAMessageUtil::AppendMessageWithDateStampL(
                *message,
                messageContainer,
                iMessageUtils.MessageCreator() );
            }
        }
    else
        {
        // Append message
        CAMessageUtil::AppendMessageWithDateStampL(
            aMessage,
            messageContainer,
            iMessageUtils.MessageCreator(),
            ETrue );
        }
    CHAT_DP_FUNC_DONE( "CCAMessageHandler::LocalEchoMessageL" );
    }


// end of file