mmsengine/mmscodec/src/mmsencode.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Mon, 04 Oct 2010 00:13:15 +0300
changeset 72 6f657153cbc5
parent 44 36f374c67aa8
permissions -rw-r--r--
Revision: 201037 Kit: 201039

/*
* Copyright (c) 2002-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:  
*     binary encoding of a multimedia message
*
*/



//#define CONTENT_DISPOSITION_TEST

// INCLUDE FILES
#include    <e32std.h>
#include    <e32hal.h>
#include    <apparc.h>
#include    <s32mem.h>
#include    <msventry.h>
#include    <utf.h>
#include    <e32math.h>
#include    <msvids.h>
#include    <escapeutils.h>
#include    <badesca.h>
#include    <mmsvattachmentmanager.h>
#include    <mmsvattachmentmanagersync.h>
#include    <cmsvmimeheaders.h>
#include    <imcvcodc.h>

#include    <msgtextutils.h>
#include    <msvstore.h>
#include    <charconv.h>

#include    <centralrepository.h>          // link against centralrepository.lib
#include    "MmsEnginePrivateCRKeys.h"

#include    "mmsheaders.h"
#include    "mmsconst.h"
#include    "mmscodec.h"
#include    "mmsencode.h"
#include    "mmsservercommon.h"
#include    "mmssession.h"
#include    "mmsgenutils.h"
#include    "mmserrors.h"
#include    "mmsentrywrapper.h"
#include    "mmsmmboxmessageheaders.h"
#include    "mmsmmboxviewheaders.h"
#include    "mmssendingchain.h"

const TInt KMmsExtra = 1024; // extra memory left for others
const TInt KMmsShortIntegerLimit127 = 127; // limit for short integer length
const TInt KMmsOneByteLimit = 0x100;
const TInt KMmsTwoByteLimit = 0x10000;
const TInt KMmsThreeByteLimit = 0x1000000;
const TInt KMms2 = 2;
const TInt KMms3 = 3;
const TInt KMms4 = 4;
const TInt KMms5 = 5;
const TInt KMms7 = 7;
const TInt KMms8 = 8;
const TInt KMms24 = 24; // shift of three half bytes
const TInt KMms30 = 30; // upper limit for short length encoding
const TUint8 KMms0x80 = 0x80; // 128
const TUint8 KMms0x7F = 0x7F; // 127
const TUint8 KMms0xFF = 0xFF;
const TInt KMmsMaxCidLength = 18;
// max filename length according to MMS conformance specs
const TInt KMmsMaxFileNameLength = 40;
// if 40 bytes are encoded in base 64, the result is 56 bytes
// (two bytes of padding are needed to make the number divisible by 3)
const TInt KMaxEncodingLength = 56;
// Maximum length of output buffer needed for encoding the filename
// Max 56 bytes for actual encoding and 12 bytes for character set (utf8)
// and encoding scheme indicator 68 for final result, but we allocate
// the maximum Mime header length to be sure everything fits.
const TInt KMaxNameBufferLength = 75;
const TInt KMmsEncodingExtraLength = 12;
const TInt KMmsPreambleLength = 10;
_LIT8( KMmsQuotedPreamble, "=?utf-8?Q?" );
_LIT8( KMmsBase64Preamble, "=?utf-8?B?" );
_LIT8( KMmsEncodingTrailer, "?=" );
const TInt KMmsIntUnderscore = 0x5F; // underscore


enum TMmsMachineStates
    {
    EMmsIdle,
    EMmsEncodingHeaders,
    EMmsEncodingAttachments,
    EMmsFinished
    };
    
// These are the stages for chunked encoding.
// The chunked encoding is synchronous, so the stages are different
// from the machine states.    
enum TMmsChunkedEncodingStages
    {
    EMmsHeaders,
    EMmsAttachmentHeaders,
    EMmsAttachmentData
    };
    
// ==================== LOCAL FUNCTIONS ====================

// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that
// might leave.
//
// All member variables are automatically zeroed in the constructor
// of a class derived from CBase.
// Priority is set slightly above standard (1 instead of 0) to make sure
// that message is sent as soon as possible (user input has priority 10).
// Variables related to chunked encoding are initialized to indicate
// one complete chunk only.
// ---------------------------------------------------------------------------
//
CMmsEncode::CMmsEncode()
    :CMsgActive ( KMmsActiveObjectPriority ),
    iState ( EMmsIdle ),
    iOverallDataSize ( -1 ),
    iLastChunk ( ETrue ),
    iOnlyOneChunk( ETrue )
    {
    }

// ---------------------------------------------------------------------------
// Symbian OS default constructor can leave.
// ---------------------------------------------------------------------------
//
void CMmsEncode::ConstructL( RFs& aFs )
    {
    iFs = aFs;
    CActiveScheduler::Add( this );
    }

// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
EXPORT_C CMmsEncode* CMmsEncode::NewL( RFs& aFs )
    {
    CMmsEncode* self = new ( ELeave ) CMmsEncode;
    
    CleanupStack::PushL( self );
    self->ConstructL( aFs );
    CleanupStack::Pop( self );

    return self;
    }

    
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CMmsEncode::~CMmsEncode()
    {

    Cancel();
    
    if ( iFileOpen )
        {
        iAttachFile.Close();
        iFileOpen = EFalse;
        }
    
    // Do not delete pointers that were presents from the caller.
    // The caller still owns that data
    delete iMimeHeaders; // this is ours too.
   
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsEncode::StartL(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aEncodeBuffer,
    TRequestStatus& aStatus )
    {

    // This is the entry point for encoding all PDUs that contain both headers
    // and data.
    // For normal use the only PDUs in this category would be M-send.req and
    // M-MBox-Upload.req, but for testing purposes the following PDUs may
    // also be handled using this entry point:
    // M-retrieve.conf (for testing purposes)
    // M-Mbox-View.conf (for testing purposes)
    // M-Mbox-Descr (for testing purposes) 

    // Old asynchronous encoding is retained.
    // Reset sets all chunk-related variables to indicate one chunk which is the last one
    Reset();

    iEntryWrapper = &aEntryWrapper;
    iMmsHeaders = &aMmsHeaders;
    iEncodeBuffer = &aEncodeBuffer;

    if ( iMimeHeaders )
        {
        iMimeHeaders->Reset();
        }
    else
        {
        iMimeHeaders = CMsvMimeHeaders::NewL();
        }

    // We need to know the message entry ID
    // The wrapper originally points to the message entry
    
    TMsvEntry indexEntry;
    iError = iEntryWrapper->GetIndexEntry( indexEntry );
    iCurrentMessageId = indexEntry.Id();
    
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );

    // Only new attachment structure is supported    
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    iNumberOfAttachments = attachMan.AttachmentCount();
    
    CleanupStack::PopAndDestroy( store );
    
    Queue( aStatus );
    
    iStatus = KRequestPending;
    SetActive();
    // Pretend that we called an asynchronous service
    // in order to get into the state machine loop
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, iError );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsEncode::EncodeHeadersL(
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aEncodeBuffer )
    {
    // This function is used to encode all PDUs that contain only headers,
    // not data. Some are responses sent to MMSC, some are requests that
    // contain headers only.
    // Nothing is read from storage, everything to be encoded is contained
    // in the headers.
    // No active object is invoked, this is a synchronous function
    
    // Old one-chunk encoding is retained for encoding headers only.
    // Reset sets all chunk-related variables to indicate one chunk which is the last one
    Reset(); // also sets iError to KErrNone

    iMmsHeaders = &aMmsHeaders;
    iEncodeBuffer = &aEncodeBuffer;

    // start from the beginning
    iPosition = 0;
    // we discard old contents of the buffer in case we must expand it
    iEncodeBuffer->Reset();
    
    // We are a bit greedy here so that we don't need
    // to change the code if there is a minor change in the specs.
    iEncodeBuffer->ResizeL( iMmsHeaders->Size() + KMMSBufferExtra );

    // encode message depending on type
    // these are all types that contain headers only, never data
    switch ( iMmsHeaders->MessageType() )
        {
        case KMmsMessageTypeMNotifyRespInd:
            EncodeNotifyResponse();
            break;
        case KMmsMessageTypeAcknowledgeInd:
            EncodeAcknowledgeIndication();
            break;
        case KMmsMessageTypeMNotificationInd:
            EncodeMmsNotificationL();
            break;
        case KMmsMessageTypeForwardReq:
            EncodeForwardRequestL();
            break;
        case KMmsMessageTypeReadRecInd:
            EncodeReadReplyL();
            break;
        case KMmsMessageTypeMboxStoreReq:
            EncodeMMBoxStoreRequestL();
            break;
        case KMmsMessageTypeMboxViewReq:
            // This may contain keywords. Reserve some extra space
            EncodeMMBoxViewRequestL();
            break;
        case KMmsMessageTypeMBoxDeleteReq:
        case KMmsMessageTypeDeleteReq:
            EncodeDeleteRequestL();
            break;
        case KMmsMessageTypeCancelConf:
            EncodeCancelResponse();
            break;
        case KMmsMessageTypeReadOrigInd:
            // This is for testing purposes.
            // identical to ReadRecInd except for PDU type
            // This would be the PDU sent to originator by MMSC
            EncodeReadReplyL();
            break;
        case KMmsMessageTypeMSendConf:
            // for testing purposes
            EncodeSendConfirmationL();
            break;
        case KMmsMessageTypeDeliveryInd:
            // for testing purposes
            EncodeDeliveryReportL();
            break;
        case KMmsMessageTypeForwardConf:
            // for testing purposes
            EncodeForwardConfirmationL();
            break;
        case KMmsMessageTypeMboxStoreConf:
            // for testing purposes
            EncodeMMBoxStoreConfirmationL();
            break;
        case KMmsMessageTypeMBoxUploadConf:
            // for testing purposes
            EncodeMMBoxUploadConfirmationL();
            break;
        case KMmsMessageTypeMBoxDeleteConf:
        case KMmsMessageTypeDeleteConf:
            // for testing purposes
            EncodeDeleteConfirmationL();
            break;
        case KMmsMessageTypeCancelReq:
            // for testing purposes
            EncodeCancelRequest();
            break;
#ifdef __WINS__
        case KMmsMessageTypeMSendReq:
            EncodeSendRequestHeadersL();
            break;
        case KMmsMessageTypeMBoxUploadReq:
            // This type has attachments and headers
            // This function encodes headers only
            EncodeMMBoxUploadRequestL();
            break;
        case KMmsMessageTypeMRetrieveConf:
            // for test purposes
            EncodeRetrieveConfirmationL();
            break;
        case KMmsMessageTypeMboxViewConf:
            // for test purposes
            EncodeMMBoxViewConfirmationL();
            break;
        case KMmsMessageTypeMBoxDescr:
            // for test purposes
            EncodeMMBoxDescriptionL();
            break;
#endif
        default:
            // Illegal message type.
            iEncodeBuffer->Reset();
            iPosition = 0;
            break;
        }

    // Remove slack to keep garbage out from the end of the file
    iEncodeBuffer->ResizeL( iPosition );
    // Dump the buffer contents into file if requested
    Dump();
    
    iOverallDataSize = iEncodeBuffer->Size();

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsEncode::StartChunkedL(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aEncodeBuffer,
    TRequestStatus& aStatus )
    {
    
    // This is the entry point for chunked encoding all PDUs that contain both headers
    // and data.
    
    // Reset sets all chunk-related variables to indicate one chunk which is the last one
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode: Start chunked") );
#endif
    Reset(); // also sets iError to KErrNone
    iDataSupplierStage = EMmsHeaders;
    
    iEntryWrapper = &aEntryWrapper;
    iMmsHeaders = &aMmsHeaders;
    iEncodeBuffer = &aEncodeBuffer;

    if ( iMimeHeaders )
        {
        iMimeHeaders->Reset();
        }
    else
        {
        iMimeHeaders = CMsvMimeHeaders::NewL();
        }
        
    // We need to know the message entry ID
    // The wrapper originally points to the message entry
    
    TMsvEntry indexEntry;
    iError = iEntryWrapper->GetIndexEntry( indexEntry );
    iCurrentMessageId = indexEntry.Id();
    
    // Encode headers to the buffer
    // The rest will follow when next data chunk is requested.
    EncodeHeadersChunkedL();
    
	aStatus=KRequestPending;
    TRequestStatus* status = &aStatus;
    User::RequestComplete( status, iError );
    }
    
    
// ---------------------------------------------------------
// From class MMmsCodecDataSupplier
//
// ---------------------------------------------------------
//
TInt CMmsEncode::GetNextDataPart( TPtrC8& aDataPart, TBool& aLastDataChunk )
    {
    TInt error = KErrNone;
    if ( !iEncodeBuffer || iEncodeBuffer->Size() == 0 )
        {
        // called out of context or no message data available
        error = KErrNotFound;
        }
    else
        {
        aDataPart.Set( iEncodeBuffer->BackPtr( iPosition ) ); 
        }
    aLastDataChunk = iLastChunk;
    
    return error;
    }
    
// ---------------------------------------------------------
// From class MMmsCodecDataSupplier
//
// ---------------------------------------------------------
//
TInt CMmsEncode::ReleaseData()
    {
    if ( iOnlyOneChunk )
        {
        // if we have only one chunk we always point to the beginning
        // the buffer gets cleared when all data has been handled
        return KErrNone;
        }
    
    iPosition = 0;
    
    // encode next data part into the buffer
    
    if ( iDataSupplierStage == EMmsAttachmentHeaders && iNumberOfAttachments == 0 )
        {
        iLastChunk = ETrue;
        iDataSupplierStage = EMmsHeaders;
        }
    else if ( iDataSupplierStage == EMmsAttachmentHeaders )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- data supplier stage: Attachment headers") );
#endif
        iError = iEntryWrapper->SetCurrentEntry( iCurrentMessageId );
        if ( iError != KErrNone )
            {
            return iError;
            }
        
        iCurrentFileSize = 0;
        CMsvStore* store = NULL;
        TRAP( iError, 
            {
            store = iEntryWrapper->ReadStoreL();
            CleanupStack::PushL( store );
            MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
            iCurrentFileSize = EncodeHeadersAndGetFileL( attachMan );
            CleanupStack::PopAndDestroy( store );
            });
        if ( iError != KErrNone )
            {
            return iError;
            }
            
        // Now we have the attachment headers in our buffer.
        // If there is room, put data there, too
        
        if ( iBufferSize - iPosition >= iCurrentFileSize )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- enough room left in buffer for attachment data") );
#endif
            EncodeAttachmentData( iAttachFile, iCurrentFileSize );
            iPosition += iCurrentFileSize;
            iAttachFile.Close();
            iFileOpen = EFalse;
            iCurrentAttachment++;
            if ( iCurrentAttachment >= iNumberOfAttachments )
                {
                iLastChunk = ETrue;
                iDataSupplierStage = EMmsHeaders;
                }
            else
                {
                // one attachment finished - move to next one
                iDataSupplierStage = EMmsAttachmentHeaders;
                }
            }
        else
            {
            iDataSupplierStage = EMmsAttachmentData;
            }
        }
    else if ( iDataSupplierStage == EMmsAttachmentData )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- data supplier stage: Attachment data") );
#endif
        // Add as much attachment data as possible.
        // Attachment file is now open, check position
        TInt filePosition = 0;
        iError = iAttachFile.Seek( ESeekCurrent, filePosition );
        if ( iError != KErrNone )
            {
            return iError;
            }
        TInt dataLeft = iCurrentFileSize - filePosition;
        TInt roomLeft = iBufferSize - iPosition;
        if ( roomLeft >= dataLeft )
            {
            EncodeAttachmentData( iAttachFile, dataLeft );
            iPosition += dataLeft;
            iAttachFile.Close();
            iFileOpen = EFalse;
            iCurrentAttachment++;
            if ( iCurrentAttachment >= iNumberOfAttachments )
                {
                iLastChunk = ETrue;
                iDataSupplierStage = EMmsHeaders;
                }
            else
                {
                // one attachment finished - move to next one
                iDataSupplierStage = EMmsAttachmentHeaders;
                }
            }
        else
            {
            // we have more data than fits into buffer
            EncodeAttachmentData( iAttachFile, roomLeft );
            iPosition += roomLeft;
            // We don't close the file because we still have data.
            // We stay in EMmsAttachmentHeaders state
            }
        }
    else
        {
        // do nothing, keep LINT happy
        }
#ifndef _NO_MMSS_LOGGING_
    if ( iDataSupplierStage == EMmsHeaders )
        {
        TMmsLogger::Log( _L("- last chunk released") );
        TMmsLogger::Log( _L("- data supplier stage: MMS headers") );
        }
#endif
    // If we have reached the end and have been asked to release data
    // we have nothing left to do.    
    
    // dump what we got into file - if needed
    if ( iPosition > 0 )
        {
        DumpAppend();
        }
    return iError;
    }
    
// ---------------------------------------------------------
// From class MMmsCodecDataSupplier
//
// ---------------------------------------------------------
//
TInt CMmsEncode::OverallDataSize()
    {
    // If we have encoded all data, iOverallDataSize contains the 
    // actual amount of data.
    // If we are doing chunked encoding, it contains -1.
    return iOverallDataSize;
    }

    
// ---------------------------------------------------------
// From class MMmsCodecDataSupplier
//
// ---------------------------------------------------------
//
TInt CMmsEncode::ResetSupplier()
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode: ResetSupplier") );
#endif
    if ( iOnlyOneChunk )
        {
        // if we have only one chunk we always point to the beginning
        // the buffer gets cleared when all data has been handled
        return KErrNone;
        }
    
    // start data from the beginning
    iError = KErrNone;
    iDataSupplierStage = EMmsHeaders;
    TRAP( iError, EncodeHeadersChunkedL() );
    
    return iError;
    }

// ---------------------------------------------------------
// From class CMsgActive
//
// ---------------------------------------------------------
//
void CMmsEncode::DoRunL()
    {

    // This routine takes the state machine through the states
    // until an error is encountered or the cycle completes.

    if ( iError != KErrNone )
        {
        // We encountered an error, and cannot continue
        // For the time being we just give up without trying
        // to do any decent cleanup.
        iStatus = iError;
        // If we return from DoRunL without becoming active again,
        // RunL completes.
        return;
        }

    if ( iState != EMmsFinished )
        {
        SelectNextState();
        // If appropriate, ChangeStateL makes us active again
        ChangeStateL();
        }
    else
        {
        // We are done, we must become idle again
        FinishL();
        iStatus = iError;
        // If we return from DoRunL without becoming active again,
        // RunL completes.
        }
    // As we are using standard Mentact RunL, we must leave
    // if we have encountered an error, and do not want to continue

    if ( iError != KErrNone && !IsActive() )
        {
        iPosition = 0;
        FinishL();
        User::Leave( iError );
        }
    }

// ---------------------------------------------------------
// From class CMsgActive
//
// ---------------------------------------------------------
//
void CMmsEncode::DoComplete( TInt& /* aStatus */ )
    {
    // We are exiting the loop - we say we are idle now
    iState = EMmsIdle;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::SelectNextState()
    {

    // If appropriate, the functions called within the switch statement
    // will make us active again. If all is done, the asynchronous request
    // will complete

    switch ( iState )
        {
        case EMmsIdle:
            // start the loop
            iState = EMmsEncodingHeaders;
            break;
        case EMmsEncodingHeaders:
            if ( iNumberOfAttachments > 0 )
                {
                iState = EMmsEncodingAttachments;
                }
            else
                {
                iState = EMmsFinished;
                }
            break;
        case EMmsEncodingAttachments:
            // if there are more attachments, don't change state
            if ( iCurrentAttachment >= iNumberOfAttachments )
                {
                iState = EMmsFinished;
                }
            break;
        case EMmsFinished:
            // No more states
            iState = EMmsIdle;
            break;
        default:
            // Illegal state
            break;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::ChangeStateL()
    {
    switch ( iState )
        {
        case EMmsEncodingHeaders:
            EncodeHeadersL();
            break;
        case EMmsEncodingAttachments:
            EncodeAttachmentL();
            iCurrentAttachment++;
            break;
        case EMmsFinished:
            FinishL();
            break;
        default:
            // Should not be here anymore
            break;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::Reset()
    {
    // close open file in case operation has ended with error
    // and the attachment file is still open
    if ( iFileOpen )
        {
        iAttachFile.Close();
        iFileOpen = EFalse;
        }

    iNumberOfAttachments = 0;
    iCurrentAttachment = 0;
    iError = KErrNone;
    iCurrentMessageId = KMsvNullIndexEntryId;
    iOverallDataSize = -1;
    iLastChunk = ETrue;
    iOnlyOneChunk = ETrue;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeHeadersL()
    {

    // start from the beginning
    iPosition = 0;
    // we discard old contents of the buffer in case we must expand it
    iEncodeBuffer->Reset();
    
    // calculate space needed and resize buffer
    TInt size = 0;
    size += iMmsHeaders->Size();
    
    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );
    // AttachmentsSize function asks actual size from files
    // in case the info in CMsvAttachment is not up to date.
    // This function leaves if the attachment file is not found.
    // A message cannot be sent if it refers to attachment files that no
    // longer exist.
    size += iEntryWrapper->AttachmentsSizeL( *store );
    CleanupStack::PopAndDestroy( store );
    store = NULL;
        
    size += KMMSBufferExtra; // this is just an estimate of header size...
    // extra needed if lots of attachments.
    size += KMMSAttachmentExtra * iNumberOfAttachments;
    TMemoryInfoV1Buf memory;
    UserHal::MemoryInfo( memory );
    TInt available = memory().iFreeRamInBytes;
    // check that buffer fits, leave a little memory for others too.
    if ( size > ( available - KMmsExtra ) )
        {
        // message is too big - don't even try
        iError = KMmsErrorMessageTooBig;
        }

    if ( iError == KErrNone )
        {
        iEncodeBuffer->ResizeL( size );
        EncodeRequestHeadersL();
        }

    iStatus = KRequestPending;
    SetActive();
  
    // We complete ourselves.
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, iError );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeHeadersChunkedL()
    {
    // The first step of chunked encoding
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: start") );
#endif /* _NO_MMSS_LOGGING_ */
    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
    
    /* Korean req: 415-5434
     * Get the target encoding MIB enum from cenrep and encode MMS text objects using corresponding conversion plugins
     */
    TInt temp;
    iTargetEncodingType = 0;
    CRepository* repository = CRepository::NewLC( KUidMmsServerMtm );    
    if ( repository->Get( KMmsEncodingType, temp ) == KErrNone )
        {
        iTargetEncodingType = temp;
        }
    CleanupStack::PopAndDestroy( repository );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: target encoding type: %d"), iTargetEncodingType );
#endif /* _NO_MMSS_LOGGING_ */

    if( iTargetEncodingType != 0 )
        {
        TRAPD( err, PreProcessAttachmentDataL() );

#ifndef _NO_MMSS_LOGGING_
    /* if any error, korean specific encoding failed, But this should not stop from sending MMS using
     * existing default encoding. Hence just log the error and continue in any case
     */
        if( err != KErrNone )
            {
            TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: PreProcessAttachmentDataL error: %d"), err );
            }
#endif /* _NO_MMSS_LOGGING_ */
        }
    
    //Open store for read-only purpose
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );
    
    // Only new attachment structure is supported    
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    iNumberOfAttachments = attachMan.AttachmentCount();
    
    TInt size = 0;
    size += iMmsHeaders->Size();
    size += KMMSBufferExtra; // this is just an estimate of header size...
    // The buffer must be at least big enough to hold all headers
    iBufferSize = Max( KMmsChunkedBufferSize, size );
    
    // extra needed if lots of attachments.
    // AttachmentsSize function asks actual size from files
    // in case the info in CMsvAttachment is not up to date.
    // This function leaves if the attachment file is not found.
    // A message cannot be sent if it refers to attachment files that no
    // longer exist.
    size += iEntryWrapper->AttachmentsSizeL( *store );
    size += KMMSAttachmentExtra * iNumberOfAttachments;
    CleanupStack::PopAndDestroy( store );
    store = NULL;

    if ( iBufferSize > size )
        {
        // Our message is small enough to be sent in one chunk.
        // It does not make sense to send the message in small chunks
        // it the total is only a few kilobytes
        iBufferSize = size;
        iOnlyOneChunk = ETrue;
        iLastChunk = ETrue;
        }
    else
        {
        // we need several chunks
        iOnlyOneChunk = EFalse;
        iLastChunk = EFalse;
        }
        
    // start from the beginning
    iPosition = 0;
    iEncodeBuffer->Reset();
    // This leaves if unable to resize.
    // We try to keep our buffer small enough so that this does not leave.
    iEncodeBuffer->ResizeL( iBufferSize );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: MMS buff Size(Approx): %d"), iBufferSize );
#endif /* _NO_MMSS_LOGGING_ */        // The message is small enough to be sent as one chunk

    EncodeRequestHeadersL();
    
    TInt i = 0;
    if ( iOnlyOneChunk )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: only one chunk") );
#endif /* _NO_MMSS_LOGGING_ */        // The message is small enough to be sent as one chunk
        for ( i = 0; ( i < iNumberOfAttachments ) && ( iError == KErrNone ); ++i )
            {
            iCurrentAttachment = i;
            // encode attachments, too, as we have decided that our message
            // is small enough to fit into the buffer as one chunk
            // DoEncodeAttachment always encodes iCurrentAttachment
            // and updates buffer position as needed.
            DoEncodeAttachmentL();
            // If something goes wrong, DoEncodeAttachmentL() sets iError,
            // and StartChunkedL will complete the caller with error.
            }
        iEncodeBuffer->ResizeL( iPosition );
        iOverallDataSize = iEncodeBuffer->Size();
        }
    else
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: Multiple chunks") );
#endif /* _NO_MMSS_LOGGING_ */        // The message is small enough to be sent as one chunk
        // chunked sending - but it should be possible to calculate data size
        iOverallDataSize = iPosition; // This is the amount taken by headers
        // The message is small enough to be sent as one chunk
        for ( i = 0; ( i < iNumberOfAttachments ) && ( iError == KErrNone ); ++i )
            {
            iCurrentAttachment = i;
            // encode attachments, too, as we have decided that our message
            // is small enough to fit into the buffer as one chunk
            // DoEncodeAttachment always encodes iCurrentAttachment
            // and updates buffer position as needed.
            iOverallDataSize += DoGetAttachmentEncodingLengthL();
            // If something goes wrong, DoEncodeAttachmentL() sets iError,
            // and StartChunkedL will complete the caller with error.
            }
        iCurrentAttachment = 0;
        iDataSupplierStage = EMmsAttachmentHeaders;
        if ( iError != KErrNone )
            {
            iOverallDataSize = -1;
            }
        }
     
    // Dump headers. More data will follow    
    Dump();
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: end") );
#endif /* _NO_MMSS_LOGGING_ */        // The message is small enough to be sent as one chunk
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAttachmentL()
    {

    DoEncodeAttachmentL();
    
    if ( iError != KErrNone )
        {
        // If we return without becoming active again, RunL completes
        // we cannot send this message, because we cannot access attachments
        return;
        }

    iStatus = KRequestPending;
    SetActive();
  
    // Now we indicate we already did it.
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, iError );

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::DoEncodeAttachmentL()
    {
    
    // Encode one part
    // We must calculate length of headers and content type
    
    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
        
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    
    TInt size = 0;
    size = EncodeHeadersAndGetFileL( attachMan );
    if ( iError != KErrNone )
        {
        CleanupStack::PopAndDestroy( store );
        return;
        }
    
    // The data read function will not leave
    EncodeAttachmentData( iAttachFile, size );
    iPosition += size;
    iAttachFile.Close();

    CleanupStack::PopAndDestroy( store );

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsEncode::DoGetAttachmentEncodingLengthL()
    {
    
    // calculate length of headers and content type
    
    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
        
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    
    TInt size = 0;
    size = GetHeadersAndFileSizeL( attachMan );
    CleanupStack::PopAndDestroy( store );
    return size;

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsEncode::EncodeHeadersAndGetFileL( MMsvAttachmentManager& aAttachMan )
    {
    
    CMsvAttachment* attachmentInfo = NULL;
    attachmentInfo = aAttachMan.GetAttachmentInfoL( iCurrentAttachment );
    CleanupStack::PushL( attachmentInfo );
    
    iMimeHeaders->RestoreL( *attachmentInfo );
        
    // We don't trust the info, we ask the file itself
    // Someone outside the messaging system may have changed the file
    // without changing the attachment info
    TInt size = 0;
    if ( iFileOpen )
        {
        // close file in case we have been reset while the file is still open
        iAttachFile.Close();
        iFileOpen = EFalse;
        }
    iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment );
    iError = iAttachFile.Size( size );
    iAttachFile.Close();

    if ( iError != KErrNone )
        {
        CleanupStack::PopAndDestroy( attachmentInfo );
        return size;
        }

    // calculate the length of the headers
    TBool foundName = EFalse;
    TUint contentTypeSize = 0;
    TInt8 contentType = -1; // indicate not found
    TPtrC8 contentTypeString;

    TUint headerSize = 0;
    
    HBufC8* buf8 = CalculateAttachmentHeaderLengthL(
        *attachmentInfo,
        headerSize,    
        foundName,
        contentTypeSize,
        contentType,
        contentTypeString );
     
    CleanupStack::PushL( buf8 );
        
    // We have calculated the header length, now we can encode:

    TPtrC8 nameString;
    if ( !foundName && buf8 )
        {
        nameString.Set( buf8->Des() );
        }
        
    EncodeAttachmentHeadersL(
        size,
        headerSize,
        foundName,
        contentTypeSize,
        contentType,
        contentTypeString,
        nameString );

    // now we have put the name all over, we can destroy the buffer
    CleanupStack::PopAndDestroy( buf8 );
    buf8 = NULL;
    CleanupStack::PopAndDestroy( attachmentInfo );
    attachmentInfo = NULL;
    
    // Now just write all the data octets
    iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment );
    iFileOpen = ETrue;
    
    return size;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsEncode::GetHeadersAndFileSizeL( MMsvAttachmentManager& aAttachMan )
    {
    
    CMsvAttachment* attachmentInfo = NULL;
    attachmentInfo = aAttachMan.GetAttachmentInfoL( iCurrentAttachment );
    CleanupStack::PushL( attachmentInfo );
    
    iMimeHeaders->RestoreL( *attachmentInfo );
        
    // We don't trust the info, we ask the file itself
    // Someone outside the messaging system may have changed the file
    // without changing the attachment info
    TInt size = 0;
    if ( iFileOpen )
        {
        // close file in case we have been reset while the file is still open
        iAttachFile.Close();
        iFileOpen = EFalse;
        }
    iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment );
    iError = iAttachFile.Size( size );
    iAttachFile.Close();

    if ( iError != KErrNone )
        {
        CleanupStack::PopAndDestroy( attachmentInfo );
        return 0;
        }

    // calculate the length of the headers
    TBool foundName = EFalse;
    TUint contentTypeSize = 0;
    TInt8 contentType = -1; // indicate not found
    TPtrC8 contentTypeString;

    TUint headerSize = 0;
    
    HBufC8* buf8 = CalculateAttachmentHeaderLengthL(
        *attachmentInfo,
        headerSize,    
        foundName,
        contentTypeSize,
        contentType,
        contentTypeString );
        
    delete buf8; // This is not needed
    buf8 = NULL;     
     
    CleanupStack::PopAndDestroy( attachmentInfo );
    attachmentInfo = NULL;
    
    // We have calculated the header length, now we still need the
    // uintvar lengths:
    
    size += GetUintvarLength( size ); // encoding size for attachment length
    size += headerSize;
    size += GetUintvarLength( headerSize ); // encoding size for header length
 
    return size;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
HBufC8* CMmsEncode::CalculateAttachmentHeaderLengthL(
    CMsvAttachment& aAttachmentInfo,
    TUint& aHeaderLength,
    TBool& aFoundName,
    TUint& aContentTypeSize,
    TInt8& aContentType,
    TPtrC8& aContentTypeString  )
    {
    
    TInt numNonSafe = 0; // number of characters in filename that would require encoding
    // Not all content types have parameters.
    // The only parameter we can handle to some extent
    // is the character set parameter for text content types.
    // The name parameter will always be added if not already present
    
    // Check if we have well-known media.
    aContentType = -1; // indicate not found
    aContentTypeString.Set( aAttachmentInfo.MimeType() );
    if ( aContentTypeString.Length() == 0 )
        {
        // take type from mime headers
        HBufC8* tempContentType = HBufC8::NewL(
            iMimeHeaders->ContentType().Length() +
            iMimeHeaders->ContentSubType().Length() + 1 );
        CleanupStack::PushL( tempContentType );
        tempContentType->Des().Copy( iMimeHeaders->ContentType() );
        if ( ( iMimeHeaders->ContentType().Find( KMmsSlash8 ) !=
            ( iMimeHeaders->ContentType().Length() - 1 ) ) &&
            ( iMimeHeaders->ContentSubType().Find( KMmsSlash8 ) != 0 ) &&
            ( iMimeHeaders->ContentSubType().Length() != 0 ) )
            {
            tempContentType->Des().Append( KMmsSlash8 );
            }
        tempContentType->Des().Append( iMimeHeaders->ContentSubType() );
        aAttachmentInfo.SetMimeTypeL( tempContentType->Des() );
        CleanupStack::PopAndDestroy( tempContentType );
        aContentTypeString.Set( aAttachmentInfo.MimeType() );
        }
        
    TInt8 i;
    for ( i = 0; i < KNumberContentTypes && aContentType < 0; ++i )
        {
        if ( aContentTypeString.CompareF(
            TPtrC8( KContentTypeTable[i] ) ) == 0 )
            {
            aContentType = i;
            }
        }
    if ( aContentTypeString.Length() == 0 )
        {
        aContentTypeString.Set( KMmsUnknownType );
        aContentType = -1;
        }
        
    // we always add the name parameter to content type, so we always
    // use the general form. If no name is given, we use the original filename
    // If the name parameter is defined, we use it.

    aFoundName = EFalse;
    iFileName.SetLength( 0 );

    aHeaderLength = 0;
    // add media type length
    if ( aContentType >= 0 )
        {
        // well-known media
        aHeaderLength += 1;
        }
    else
        {
        // Extension media
        aHeaderLength += aContentTypeString.Length();
        aHeaderLength += 1; // terminating zero
        }

    // If we have parameters, we must calculate their length.
    if ( iMimeHeaders->MimeCharset() != 0 )
        {
        // charset has well-known encoding
        aHeaderLength += 1;
        if ( iMimeHeaders->MimeCharset() <= KMmsShortIntegerLimit127 )
            {
            // short integer
            aHeaderLength += 1;
            }
        else if ( iMimeHeaders->MimeCharset() < KMmsOneByteLimit )
            {
            // long integer, short length + 1 byte of value
            aHeaderLength += KMms2;
            }
        else if ( iMimeHeaders->MimeCharset() < KMmsTwoByteLimit )
            {
            // long integer, short length + 2 bytes of value
            // so far I don't know any IANA numbers that would
            // take more than two hex bytes.
            aHeaderLength += KMms3;
            }
        else if ( iMimeHeaders->MimeCharset() < KMmsThreeByteLimit )
            {
            // long integer, short length + 3 bytes of value
            aHeaderLength += KMms4;
            }
        else
            {
            // long integer, short length + 4 bytes of value
            // cannot be longer than this in any case
            aHeaderLength += KMms5;
            }
        }
    // Then the rest of the parameters, unknown,
    // sent as text strings
    // If "name" attribute has been defined, we use it.
    // if it is not added, we must add it.
    // We must ensure that we don't add it twice...
    
    TInt mimeHeaderCount = iMimeHeaders->ContentTypeParams().MdcaCount();

    for ( i = 0; i < mimeHeaderCount; i++ )
        {
        // If we have corrupted parameters (length of name is zero)
        // we skip the name/value pair.
        // value may be zero, it will be encoded as just the terminating zero
        if ( i%2 == 0 &&
            iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() == 0 )
            {
            i++;
            }
        else
            {
            // 8-byte string size + terminating zero
            // If parameter has no value, it still must have
            // the "no-value" indicator (just the terminating zero.)
            if ( iMimeHeaders->ContentTypeParams().MdcaPoint( i ).CompareF(
                KWspNameString ) == 0 && i%2 == 0 )
                {
                if ( ( i + 1 ) < mimeHeaderCount && 
                    IsStringSafe( iMimeHeaders->ContentTypeParams().MdcaPoint( i + 1 ),
                    numNonSafe ) )
                    {
                    // We always move the name to the first item.
                    // It may be this parameter or something else
                    
                    // Content type parameters are 8-bit strings.
                    // We can use the name only if it is safe, otherwise it may contain some
                    // unknown encoding (maybe utf-8).
                    iFileName.Copy( ( iMimeHeaders->ContentTypeParams().
                       MdcaPoint( i + 1 ) ).Right( KMmsMaxFileName ) );
                    i++;
                    }
                else
                    {
                    // Skip the next field as it can only be used if it is safe
                    i++;
                    }
                }
            else
                {
                aHeaderLength += iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() + 1;
                }
            }
        }
    if ( ( iMimeHeaders->ContentTypeParams().MdcaCount() % 2 ) == 1 )
        {
        // Odd number. Obviously last parameter has no value.
        // Add the "no value" token
        aHeaderLength++;
        }

    // include name parameter to content-type

    // if the name was not among our mime headers,
    // we must add it.
    TInt nameLength = 0;

    // We prefer the suggested filename as the content type name parameter.
    // If version is 1.3 or later, we can encode a non-safe filename.
    // If suggested filename is not safe, but we already have a safe filename
    // in iFilename, we don't override it if version is 1.2 or lower
    if ( iMimeHeaders->SuggestedFilename().Length() > 0 )
        {
        if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ||
             IsStringSafe( iMimeHeaders->SuggestedFilename() ) ||
             iFileName.Length() == 0 )
            {
            iFileName.Copy( iMimeHeaders->SuggestedFilename().Right( KMmsMaxFileName ) );
            }
        }
        
    TParse* parse = NULL;
    // If we don't have a filename suggestion so far, we just use the
    // actual filename the attachment manager has used to save the file.    
    if ( iFileName.Length() == 0 )
        {
        parse = new( ELeave )TParse;
        CleanupStack::PushL( parse );
        // aAttachmentInfo.FilePath() is an actual Symbian filename including path
        // so it never can be too long to fit into TParse or TFilename 
        parse->Set( aAttachmentInfo.FilePath(), NULL, NULL );
        iFileName.Copy( parse->NameAndExt() );
        CleanupStack::PopAndDestroy( parse );
        parse = NULL;
        }

    // Last effort:
    // If our version is 1.2 or lower and the filename suggestion so far
    // is not safe, we try content-location. It is always safe
        
    if ( iMmsHeaders->MmsVersion() <= KMmsVersion12 &&
        !IsStringSafe( iFileName ) )
        {
        if ( iMimeHeaders->ContentLocation().Length() > 0 )
            {
            // content location should be safe.
            // However, we must make sure it contains no URI escapes.
            // for example space is legal in filename, but not in URI.
            HBufC16* decoded = NULL;
            TRAPD( error, ( decoded = EscapeUtils::EscapeDecodeL(
                iMimeHeaders->ContentLocation() ) ) );
            if ( error == KErrNone )
                {
                iFileName.Copy( decoded->Des().Right( KMmsMaxFileName ) );
                delete decoded;
                decoded = NULL;
                }
            else
                {
                iFileName.Copy( iMimeHeaders->ContentLocation().Right( KMmsMaxFileName ) );
                }
            }
        }
        
    // remove leading and trailing spaces just in case...    
    iFileName.Trim();    
        
    // We should now make sure that the filename is not too long.
    // According to MMS conformance specs, the maximum should be 40 characters
    // It is unclear if the characters mean the total length or the length
    // after conversion to utf-8, but as a first approximation we try
    // to ensure that the total does not exceed 40 characters
    
    nameLength = iFileName.Length();
    
    if ( nameLength > KMmsMaxFileNameLength )
        {
        parse = new( ELeave )TParse;
        CleanupStack::PushL( parse );
        parse->Set( aAttachmentInfo.FilePath(), NULL, NULL );
        iFileName.Copy( parse->Name().Left( KMmsMaxFileNameLength - parse->Ext().Length() ) );
        iFileName.Append( parse->Ext() );
        CleanupStack::PopAndDestroy( parse );
        parse = NULL;
        nameLength = iFileName.Length();
        }

    // If encoding version is <=1.2, make filename safe,
    // otherwise use MIME encoding (either base64 or quoted printable)
    
    HBufC8* buf8 = NULL;

    TPtrC16 originalName;
    TBuf16<1> oneCharacter;
    originalName.Set( iFileName );

    TInt j;
    
    if ( IsStringSafe( iFileName ) )
        {
        // filename only contains us-ascii characters - use as is 
        buf8 = HBufC8::NewL( nameLength );
        CleanupStack::PushL( buf8 );
        buf8->Des().Copy( iFileName );      
        }
    else
        {
        if ( iMmsHeaders->MmsVersion() <= KMmsVersion12 )
            {
            // if version is <= 1.2, the filename must be us-ascii
            // replace all illegal characters by underscore
            buf8 = HBufC8::NewL( nameLength );
            CleanupStack::PushL( buf8 );
            for ( j = 0; j < nameLength; ++j )
                {
                oneCharacter.Copy( originalName.Mid( j, 1 ) );
                if ( !IsStringSafe( oneCharacter ) )
                    {
                    _LIT( KMmsUnderscore, "_" );
                    oneCharacter.Copy( KMmsUnderscore );
                    }
                buf8->Des().Append( oneCharacter );
                }
            }
        else
            {
            // MMS encapsulation version is 1.3 or later
            // Using non-ascii characters in filename parameter is allowed
            // The filename must be encoded either using base64 or quoted printable
            HBufC8* temp = NULL;
			// coverity[size_error][buffer_alloc]
            temp = CnvUtfConverter::ConvertFromUnicodeToUtf8L( originalName );
            CleanupStack::PushL( temp );
            // Now we have the filename in utf-8.
            // We must check if it contains non-ascii, and if it does,
            // we must use base64 or quoted-printable encoding.
            TInt numNonSafe;
            // Actually we know already that we are not safe. We just need
            // to get the number of non-ascii characters
            IsStringSafe( temp->Des(), numNonSafe );
            // We prefer quoted printable as it looks nicer in case we have mostly ascii,
            // and it leaves extension in cleartext anyway
            if ( ( temp->Des().Length() + ( KMms2 * numNonSafe ) ) <= KMaxEncodingLength )
                {
                // use quoted printable, but first convert spaces to underscores
                TInt spacePosition = temp->Des().Find( KSpace8 );
                while ( spacePosition != KErrNotFound )
                    {
                    temp->Des()[ spacePosition ] = KMmsIntUnderscore;
                    spacePosition = temp->Des().Find( KSpace8 );
                    }
                buf8 = EncodeQuotedPrintableWordL( temp->Des() );
                }
            else
                {
                // use base64
                // We have not restricted the filename length in utf-8 format,
                // so it may be way longer than 40 bytes.
                // However, we can calculate the length the buffer needs, so we
                // can try. However, we may end up with some soft line break in 
                // the middle of the parameter which may not be acceptable.
                buf8 = EncodeBase64WordL( temp->Des() );
                }
            
            CleanupStack::PopAndDestroy( temp );
            
            CleanupStack::PushL( buf8 );
            // we must change nameLength here
            }
        }

    // The name is now in buf8, either just copied or encoded
    nameLength = buf8->Des().Length();

    if ( aFoundName == EFalse )
        {
        // well-known encoding for the header itself
        aHeaderLength++;
        aHeaderLength += nameLength;
        aHeaderLength += 1; // terminating zero
        }

    aContentTypeSize = aHeaderLength;

    // As we have general form (at least name parameter), we need to indicate the length
    if ( aContentTypeSize <= KMms30 )
        {
        // short length
        aHeaderLength += 1;
        }
    else
        {
        // length quote + at least one byte of uintvar
        aHeaderLength += KMms2;
        // Expecting over 128 bytes is paranoid.
        if ( aContentTypeSize >= KMms0x7F )
            {
            aHeaderLength++;
            }
        }
        
    // Now the rest of the mime headers:

    // Content-location
    // just a short integer + a null terminated string

    if ( iMimeHeaders->ContentLocation().Length() > 0 )
        {
        aHeaderLength += 1; // encoding of header as short integer

        // If we want to make sure we are safe,
        // we must call IsStringSafe() function.

        // We just assume we are safe now.
        aHeaderLength += iMimeHeaders->ContentLocation().Length();
        aHeaderLength += 1; // terminating zero
        }

   // Content-ID

    if ( iMimeHeaders->ContentId().Length() > 0 )
        {
        aHeaderLength += 1; // encoding of header as short integer
        // add terminating zero and preceding quote.
        // (needed according to WSP 1.3)
        aHeaderLength += iMimeHeaders->ContentId().Length() + KMms2;
        if ( iMimeHeaders->ContentId().Find( KMmsLeftAngle ) != 0 )
            {
            aHeaderLength += KMms2; // we must add angle bracket
            }
        }
        
    // x-type parameters
    // Must be stored as name/value pairs like content-type parameters
    // X-parameter[i] = parameter name
    // X-parameter[i+1] = parameter value
    
    for ( i = 0; i < iMimeHeaders->XTypeParams().MdcaCount(); ++i )
        {
        // If we have corrupted parameters (length of name is zero)
        // we skip the name/value pair.
        // value may be zero, it will be encoded as just the terminating zero
        if ( i%2 == 0 &&
            ( iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() == 0  ||
            iMimeHeaders->XTypeParams().MdcaPoint( i ).CompareF( 
            KMmsSeparateDeliveryOmaXHeader ) == 0 ) )
            {
            // We skip the header and its value if
            // a) the header length is 0
            // b) the header is X-Oma-Drm-Separate-Delivery
            i++;
            }
        else
            {
            aHeaderLength += iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() + 1;
            }
        }
    if ( ( iMimeHeaders->XTypeParams().MdcaCount() % 2 ) == 1 )
        {
        // Odd number. Obviously last parameter has no value.
        // Add the "no value" token
        aHeaderLength++;
        }
        
    // THESE ARE NOT IMPLEMENTED

    // Content-disposition & parameters
    // Content-description
    // Content-base
    // Relative path
    // version

#ifdef CONTENT_DISPOSITION_TEST
// not supported

    // We generate a general header for content disposition
    // for tests we use the actual filename.
    // In real life the user's filename should not be
    // sent out to the world

    // Fixed coding: 
    // Content Disposition, value length, attachment, filename,
    // actual filename + terminating 0

    // attachment, filename, terminating zero
    // content disposition should be text-string, not quoted text string
    TInt contentDispositionLength = KMms3 + nameLength;

    TInt valueLengthLength = 1;
    if ( contentDispositionLength > KMms30 )
        {
        // lengths 0 - 30 are encoded using "short length"
        valueLengthLength++;
        // 
        if ( contentDispositionLength > ShortIntegerLimit127 )
            {
            valueLengthLength++;
            }
        // Should never be longer than this.
        // I don't think anybody has a filename that is longer than 16383 bytes.
        }

    aHeaderLength += valueLengthLength + contentDispositionLength + 1;
#endif

    CleanupStack::Pop( buf8 );
    return buf8; // caller will delete this afterwards
    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAttachmentHeadersL(
    TUint aSize,
    TUint aHeaderSize,
    TBool aFoundName,
    TUint aContentTypeSize,
    TInt8 aContentType,
    TPtrC8& aContentTypeString,
    TPtrC8& aNameString )
    {
    
    EncodeUintvar( aHeaderSize );

    EncodeUintvar( aSize );

    TUint8 temp = 0; // used to set the high bit for integers

    // if we have character set (or other parameters),
    // we must use general encoding

    EncodeValueLength( aContentTypeSize );

    if ( aContentType >= 0 )
        {
        temp = aContentType | KMms0x80;
        iEncodeBuffer->Write( iPosition, &temp, 1 );
        iPosition++;
        }
    else
        {
        // we assume this is a safe string
        EncodeTextString( aContentTypeString );
        }
    
    // Then content type parameters. Mainly character set,
    // but maybe something else, too

    temp = KWspCharset | KMms0x80; // encode as short integer
    EncodeOptionalInteger( temp, iMimeHeaders->MimeCharset() );

    // if we didn't find "Name" parameter among our parameters, we
    // put it first.

    // we need the name later anyway...

    if ( !aFoundName )
        {
        temp = KWspName | KMms0x80; // encode as short integer
        EncodeHeaderAndTextString( temp, aNameString );
        }

    // Then the rest of content type parameters, just text strings.
    
    TInt i = 0;
    
    for ( i = 0; i < iMimeHeaders->ContentTypeParams().MdcaCount(); ++i )
        {
        // If we have corrupted parameters (length of name is zero)
        // we skip the name/value pair.
        // value may be zero, it will be encoded as just the terminating zero
        if ( i%2 == 0 &&
            iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() == 0 )
            {
            i++;
            }
        else
            {
            // 8-byte string + terminating zero
            // If parameter has no value, it still must have
            // the "no-value" indicator (just the terminating zero.)
            // The ContentTypeParameters function provides
            // an empty string in this case.
            if ( iMimeHeaders->ContentTypeParams().MdcaPoint( i ).CompareF(
                KWspNameString ) == 0 && i%2 == 0 ) 
                {
                if ( aFoundName )
                    {
                    temp = KWspName | KMms0x80; // encode as short integer
                    iEncodeBuffer->Write( iPosition, &temp, 1 );
                    iPosition++;
                    }
                else
                    {
                    // we can use name from mime headers only if it is "safe"
                    // (contains only us-ascii characters)
                    // If the name is not safe, the parameter value is skipped
                    i++;
                    }
                }
            else
                {
                EncodeTextString(
                    iMimeHeaders->ContentTypeParams().MdcaPoint( i ) );
                }
            }
        }
    if ( ( iMimeHeaders->ContentTypeParams().MdcaCount() % 2 ) == 1 )
        {
        // Odd number. Obviously last parameter has no value.
        // Add the "no value" token
        iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
        iPosition++;
        }

    // content-location
    temp = KWspContentLocation | KMms0x80; // encode as short integer
    EncodeOptionalStringL( temp, iMimeHeaders->ContentLocation() );

    // content-id
    if ( iMimeHeaders->ContentId().Length() > 0 )
        {
        HBufC8* tempContentId = HBufC8::NewL( iMimeHeaders->ContentId().Length() + KMms2 );
        CleanupStack::PushL( tempContentId );
        if ( iMimeHeaders->ContentId().Find( KMmsLeftAngle ) != 0 )
            {
            tempContentId->Des().Copy( KMmsLeftAngle );
            tempContentId->Des().Append( iMimeHeaders->ContentId() );
            tempContentId->Des().Append( KMmsRightAngle );
            }
        else
            {
            tempContentId->Des().Copy( iMimeHeaders->ContentId() );
            }
        temp = KWspContentId | KMms0x80; // encode as short integer
        iEncodeBuffer->Write( iPosition, &temp, 1 );
        iPosition++;
        EncodeQuotedTextString( tempContentId->Des() );
        CleanupStack::PopAndDestroy( tempContentId );
        }

#ifdef CONTENT_DISPOSITION_TEST
// not supported
     temp = 0x2E | KMms0x80; // content disposition
     iEncodeBuffer->Write( iPosition, &temp, 1 );
     iPosition++;
     EncodeValueLength( contentDispositionLength );
     temp = 0x81; // attachment
     iEncodeBuffer->Write( iPosition, &temp, 1 );
     iPosition++;
     temp = 0x06 | KMms0x80; // filename
     // filename should be text-string, not quoted
     EncodeHeaderAndTextString( temp, buf8->Des() );
#endif

        // x-type parameters
    for ( i = 0; i < iMimeHeaders->XTypeParams().MdcaCount(); ++i )
        {
        // If we have corrupted parameters (length of name is zero)
        // we skip the name/value pair.
        // value may be zero, it will be encoded as just the terminating zero
        if ( i%2 == 0 &&
            ( iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() == 0  ||
            iMimeHeaders->XTypeParams().MdcaPoint( i ).CompareF( 
            KMmsSeparateDeliveryOmaXHeader ) == 0 ) )
            {
            // We skip the header and its value if
            // a) the header length is 0
            // b) the header is X-Oma-Drm-Separate-Delivery
            i++;
            }
        else
            {
            // 8-byte string + terminating zero
            // If parameter has no value, it still must have
            // the "no-value" indicator (just the terminating zero.)
            // The ContentTypeParameters function provides
            // an empty string in this case.
            EncodeTextString( iMimeHeaders->XTypeParams().MdcaPoint( i ) );
            }
        }
    if ( ( iMimeHeaders->XTypeParams().MdcaCount() % 2 ) == 1 )
        {
        // Odd number. Obviously last parameter has no value.
        // Add the "no value" token
        iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
        iPosition++;
        }
        
    // End of MIME headers

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAttachmentData( RFile& aAttachFile, TInt aSize )
    {
    // read the attachment file
    // set up a pointer to point to iEncodeBuffer at iPosition
    TPtr8 pos = iEncodeBuffer->Ptr( iPosition );
    // Attachemnt may be a file attacment or a linked file.
    // The file pointer will point to unread part if file is read in chunks
    iError = aAttachFile.Read( pos, aSize );
    }
    

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::FinishL()
    {

    // Remove slack to keep garbage out from the end of the file
    iEncodeBuffer->ResizeL( iPosition );
    iOverallDataSize = iEncodeBuffer->Size();
    iState = EMmsIdle;
    
    // Dump the buffer contents into file if requested
    Dump();

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeRequestHeadersL()
    {
    // encode message depending on type
    switch ( iMmsHeaders->MessageType() )
        {
        // KMmsMessageTypeMNotifyRespInd and KMmsMessageTypeAcknowledgeInd and all other PDUs
        // that contain only headers and no data should be handled by
        // using function
        // EncodeHeadersL(CMmsHeaders& aMmsHeaders,CBufFlat& aEncodeBuffer)
        case KMmsMessageTypeMSendReq:
            EncodeSendRequestHeadersL();
            break;
        case KMmsMessageTypeMBoxUploadReq:
            // This type has attachments and headers
            EncodeMMBoxUploadRequestL();
            break;
        case KMmsMessageTypeMRetrieveConf:
            // for test purposes
            EncodeRetrieveConfirmationL();
            break;
        case KMmsMessageTypeMboxViewConf:
            // for test purposes
            EncodeMMBoxViewConfirmationL();
            break;
        case KMmsMessageTypeMBoxDescr:
            // for test purposes
            EncodeMMBoxDescriptionL();
            break;
        default:
            iError = KMmsErrorUnknownMessageType;
            break;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeSendRequestHeadersL()
    {
        
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMSendReq,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );

    // If we have set the sender, encode it
    // If not, we ask MMSC to add the sender
    EncodeSenderL( iMmsHeaders->Sender() );

    // All recipient lists. If no recipients in some of the lists,
    // nothing is encoded, no need to check separately
    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
    EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
    EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );

    // Subject
    EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );

    // Message class
    EncodeOptionalByte( KMmsAssignedMessageClass,
        iMmsHeaders->MessageClass() );

    // Expiration time
    EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
        iMmsHeaders->ExpiryInterval(),
        iMmsHeaders->ExpiryDate() );

    // Delivery time
    EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
        iMmsHeaders->DeliveryTimeInterval(),
        iMmsHeaders->DeliveryDate() );

    // Priority
    EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
    
    // Sender visibility
    EncodeOptionalByte( KMmsAssignedSenderVisibility,
        iMmsHeaders->SenderVisibility() );

    // Delivery report
    EncodeOptionalByte( KMmsAssignedDeliveryReport,
        iMmsHeaders->DeliveryReport() );

    // Read reply
    EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );

    // MMS encapsulation 1.1 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
        {
        // Do we want to specify reply charging
        if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
            iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly )
            {
            // Reply charging value
            EncodeOptionalByte( KMmsAssignedReplyCharging,
                iMmsHeaders->ReplyCharging() );
            // Reply charging deadline
            EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
                iMmsHeaders->ReplyChargingInterval(),
                iMmsHeaders->ReplyChargingDate() );
            // Reply charging size
            EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
            }
        // Reply charging ID
        EncodeOptionalString( KMmsAssignedReplyChargingID,
            iMmsHeaders->ReplyChargingId() );
        }

    if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
        {
        // MMBox related headers - optional
        if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
            {
            // X-Mms-MM-Store
            EncodeOptionalByte( KMmsAssignedMmsStore,
                iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() );
            if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes )
                {
                // X-Mms-MM-State
                EncodeOptionalByte( KMmsAssignedMMState,
                    iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
                // X-MMs-MM-Flags
                // The keyword token should always be "add" or "remove"
                EncodeKeywordArrayL();
                }
            }
        }

    // Add encapsulation version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
        EncodeApplicationHeadersL();
        // X-Mms-Content-Class
        EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
        // X-Mms-DRM-Content
        EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
        // X-Mms-Adaptation-Allowed
        EncodeOptionalByte( KMmsAssignedAdaptationAllowed, iMmsHeaders->AdaptationAllowed() );
        }

    // Get Root TMsvId from headers if set
    TMsvAttachmentId rootId = iMmsHeaders->MessageRoot();

    TInt error = KErrNone;
    // If we are handling headers only, we may not have iEntryWrapper
    if ( iEntryWrapper )
        {
        CMsvStore* store = iEntryWrapper->ReadStoreL();
        CleanupStack::PushL( store );
        MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
        TRAP( error,
            {
            CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
                (TMsvAttachmentId) rootId );
            delete attachmentInfo; // we just wanted to check that it was found
            }
            );
        CleanupStack::PopAndDestroy( store );
        }
        
    // If we have a valid root part, we send the message as multipart/related.
    // Otherwise we send it as multipart mixed.
    // We don't try to guess what the root is supposed to be, if it
    // is not specified or does not point to an existing attachment.

    if ( error == KErrNone && rootId != 0 && iEntryWrapper )
        {
        EncodeMultipartRelatedHeaderL( rootId );
        }
    else
        {
        EncodeMultipartMixedHeaderL();
        }
        
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeNotifyResponse()
    {

    // version may be current version or 1.0
    // Only byte is used. iMmsHeaders controls the value.

    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMNotifyRespInd, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // insert message status
    EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() );

    // insert report allowed if present
    EncodeOptionalByte( KMmsAssignedReportAllowed,
        iMmsHeaders->ReportAllowed() );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAcknowledgeIndication()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeAcknowledgeInd,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    // insert report allowed if present
    EncodeOptionalByte( KMmsAssignedReportAllowed,
        iMmsHeaders->ReportAllowed() );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMmsNotificationL()
    {

    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMNotificationInd,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    // for notification sender is optional - added only if it is known
    if ( iMmsHeaders->Sender().Length() > 0 )
        {
        EncodeSenderL( iMmsHeaders->Sender() );
        }

    EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );

    EncodeOptionalByte( KMmsAssignedDeliveryReport,
        iMmsHeaders->DeliveryReport() );

    EncodeOptionalByte( KMmsAssignedMessageClass,
        iMmsHeaders->MessageClass() );

    iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 );
    iPosition++;

    TInt64 size = iMmsHeaders->MessageSize();
    EncodeLongInteger( size );

    EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
        iMmsHeaders->ExpiryInterval(),
        iMmsHeaders->ExpiryDate() );

    // version 1.1 fields - optional
    // reply-charging
    // reply-charging deadline
    // reply-charging size
    // reply-charging ID
    if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
        {
        // Do we want to specify
        if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
            iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
            {
            // Reply charging value
            EncodeOptionalByte( KMmsAssignedReplyCharging,
                iMmsHeaders->ReplyCharging() );
            // Reply charging deadline
            EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
                iMmsHeaders->ReplyChargingInterval(),
                iMmsHeaders->ReplyChargingDate() );
            // Reply charging size
            EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
            }
        // if this is a reply, we may specify the ID
        // Reply charging ID
        EncodeOptionalString( KMmsAssignedReplyChargingID,
            iMmsHeaders->ReplyChargingId() );
        }

    // version 1.2 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
        {
        // MMBox headers
        if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
            {
            EncodeOptionalByte( KMmsAssignedStored,
                iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() );
            }

        // Distribution indicator
        EncodeOptionalByte( KMmsAssignedDistributionIndicator,
            iMmsHeaders->DistributionIndicator() );
        }

    // Element descriptor
    // This is somewhat a mystery - not implemented
    
    // version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
        EncodeApplicationHeadersL();
        // X-Mms-Recommended-Retrieval-Mode
        EncodeOptionalByte( KMmsAssignedRecommendedRetrievalMode,
            iMmsHeaders->RecommendedRetrievalMode() );
        // X-Mms-Recommended-Retrieval-Mode-Text
        EncodeOptionalStringL( KMmsAssignedRecommendedRetrievalModeText,
            iMmsHeaders->RecommendedRetrievalModeText() );
        // X-Mms-Content-Class
        EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
        // X-Mms-DRM-Content
        EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
        // X-Mms-Replace-ID
        EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() );
        }

    // We keep this as the last one, though it does not really matter
    // Mandatory content-location
    // this must be USASCII or URL encoded
    EncodeHeaderAndTextString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeSendConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMSendConf,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    EncodeMandatoryByte( KMmsAssignedResponseStatus,
        iMmsHeaders->ResponseStatus() );

    EncodeOptionalStringL( KMmsAssignedResponseText,
        iMmsHeaders->ResponseText() );

    // if send confirmation returns error, it might not contain message id
    EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );

    // MMBox related headers - optional
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        EncodeOptionalByte( KMmsAssignedStoreStatus,
            iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
        EncodeOptionalStringL( KMmsAssignedStoreStatusText,
            iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
        if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ==
            KMmsStatusOk )
            {
            // this must be USASCII or URL encoded
            // If message was stored to MMBox, this is mandatory
            EncodeHeaderAndTextString( KMmsAssignedContentLocation,
                iMmsHeaders->ContentLocation() );
            }
        }
    
#endif

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeRetrieveConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__

    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMRetrieveConf,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    // message id
    EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );

    // If we have set the sender, encode it
    if ( iMmsHeaders->Sender().Length() > 0 )
        {
        EncodeSenderL( iMmsHeaders->Sender() );
        }

    // previously sent by array
    TInt i;
    TUint oldPosition;
    TUint length;
    for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); ++i )
        {
        oldPosition = iPosition;
        length = 0;
        // Encode once just to find out filed length
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
        length += iPosition - oldPosition;
        iPosition = oldPosition;
        iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 );
        iPosition++;
        EncodeValueLength( length );
        // now we have added length, do the actual encoding
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
        oldPosition = iPosition;
        length = 0;
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
        length += iPosition - oldPosition;
        iPosition = oldPosition;
        iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 );
        iPosition++;
        EncodeValueLength( length );
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
        }

    // All recipient lists. If no recipients in some of the lists,
    // nothing is encoded, no need to check separately
    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
    EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
    // no Bcc in retrieve confirmation

    // Subject
    EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );

    // MMBox headers
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-Mms-MM-State
        EncodeOptionalByte( KMmsAssignedMMState,
            iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
        // X-MMs-MM-Flags
        // The keyword token should always be "add" or "remove"
        EncodeKeywordArrayL();
        }

    // Message class
    EncodeOptionalByte( KMmsAssignedMessageClass,
        iMmsHeaders->MessageClass() );

    // Priority
    EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
    
    // Delivery report
    EncodeOptionalByte( KMmsAssignedDeliveryReport,
        iMmsHeaders->DeliveryReport() );

    // Read reply
    EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );

    // MMS encapsulation 1.1 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
        {
        // Do we want to specify reply charging
        if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
            iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
            {
            // Reply charging value
            EncodeOptionalByte( KMmsAssignedReplyCharging,
                iMmsHeaders->ReplyCharging() );
            // Reply charging deadline
            EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
                iMmsHeaders->ReplyChargingInterval(),
                iMmsHeaders->ReplyChargingDate() );
            // Reply charging size
            EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
            }
        // Reply charging ID
        EncodeOptionalString( KMmsAssignedReplyChargingID,
            iMmsHeaders->ReplyChargingId() );
        // status of the operation
        EncodeOptionalByte( KMmsAssignedRetrieveStatus,
            iMmsHeaders->ResponseStatus() );

        EncodeOptionalStringL( KMmsAssignedRetrieveText,
            iMmsHeaders->ResponseText() );
        }

    // version 1.2 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
        {
        // Distribution indicator
        EncodeOptionalByte( KMmsAssignedDistributionIndicator,
            iMmsHeaders->DistributionIndicator() );
        }

    // version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
        EncodeApplicationHeadersL();
        // X-Mms-Content-Class
        EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
        // X-Mms-DRM-Content
        EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
        // X-Mms-Replace-ID
        EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() );
        }

    // Start pointer
    TUint ii = 0;
    // Get Root TMsvId from headers if set
    TMsvId rootId = iMmsHeaders->MessageRoot();

    TInt error = KErrNone;
    if ( iEntryWrapper )
        {
        CMsvStore* store = iEntryWrapper->ReadStoreL();
        CleanupStack::PushL( store );
        MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
        TRAP( error,
            {
            CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
                (TMsvAttachmentId) rootId );
            delete attachmentInfo; // we just wanted to check that it was found
            }
            );
        CleanupStack::PopAndDestroy( store );
        }
        
    // If we have a valid root part, we send the message as multipart/related.
    // Otherwise we send it as multipart mixed.
    // We don't try to guess what the root is supposed to be, if it
    // is not specified or does not point to an existing attachment.

    if ( error == KErrNone && rootId != 0 && iEntryWrapper )
        {
        EncodeMultipartRelatedHeaderL( rootId );
        }
    else
        {
        EncodeMultipartMixedHeaderL();
        }

    
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeDeliveryReportL()
    {
// implemented for test purposes
#ifdef __WINS__

    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeDeliveryInd, TPtrC8(),
        iMmsHeaders->MmsVersion() );

    EncodeHeaderAndTextString( KMmsAssignedMessageId,
        iMmsHeaders->MessageId() );

    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );

    // insert message status
    EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() );
    
    // version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // X-Mms-Status-Text
        // Status text is stored in the same place as response text and retrieve
        // text as only one of them can appear in any PDU
        EncodeOptionalStringL( KMmsAssignedStatusText,
            iMmsHeaders->ResponseText() );
        // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
        EncodeApplicationHeadersL();
        }
    
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeForwardRequestL()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeForwardReq,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );

    // If we have set the sender, encode it
    // If not, we ask MMSC to add the sender
    EncodeSenderL( iMmsHeaders->Sender() );

    // All recipient lists. If no recipients in some of the lists,
    // nothing is encoded, no need to check separately
    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
    EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
    EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );

    // Expiration time
    EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
        iMmsHeaders->ExpiryInterval(),
        iMmsHeaders->ExpiryDate() );

    // Delivery time
    EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
        iMmsHeaders->DeliveryTimeInterval(),
        iMmsHeaders->DeliveryDate() );

    // Delivery report allowed
    EncodeOptionalByte( KMmsAssignedReportAllowed,
        iMmsHeaders->ReportAllowed() );

    // Delivery report
    EncodeOptionalByte( KMmsAssignedDeliveryReport,
        iMmsHeaders->DeliveryReport() );

    // Read reply
    EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );

    // version 1.2 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
        {
        // MMBox related headers - optional
        if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
            {
            // X-Mms-MM-Store
            EncodeOptionalByte( KMmsAssignedMmsStore,
                iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() );
            if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes ||
                 iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() == KMmsYes )
                {
                // X-Mms-MM-State
                EncodeOptionalByte( KMmsAssignedMMState,
                    iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
                // X-MMs-MM-Flags
                // The keyword token should always be "add" or "remove"
                EncodeKeywordArrayL();
                }
            }
        }
        
    // version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // Do we want to specify reply charging
        if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
            iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly )
            {
            // Reply charging value
            EncodeOptionalByte( KMmsAssignedReplyCharging,
                iMmsHeaders->ReplyCharging() );
            // Reply charging deadline
            EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
                iMmsHeaders->ReplyChargingInterval(),
                iMmsHeaders->ReplyChargingDate() );
            // Reply charging size
            EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
            }
        }

    // Mandatory content-location
    // this must be USASCII or URL encoded
    EncodeHeaderAndTextString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeForwardConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeForwardConf,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    EncodeMandatoryByte( KMmsAssignedResponseStatus,
        iMmsHeaders->ResponseStatus() );

    EncodeOptionalStringL( KMmsAssignedResponseText,
        iMmsHeaders->ResponseText() );

    EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );

    // MMBox related headers - optional
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        EncodeOptionalByte( KMmsAssignedStoreStatus,
            iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
        EncodeOptionalStringL( KMmsAssignedStoreStatusText,
            iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
        if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ==
            KMmsStatusOk )
            {
            // this must be USASCII or URL encoded
            // If message was stored to MMBox, this is mandatory
            EncodeHeaderAndTextString( KMmsAssignedContentLocation,
                iMmsHeaders->ContentLocation() );
            }
        }
    
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeReadReplyL()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( iMmsHeaders->MessageType(), TPtrC8(), iMmsHeaders->MmsVersion() );

    // message id
    EncodeHeaderAndTextString( KMmsAssignedMessageId,
        iMmsHeaders->MessageId() );

    // originator of the original message
    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );

    // If we have set the sender, encode it
    // If not, we ask MMSC to add the sender
    EncodeSenderL( iMmsHeaders->Sender() );

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );

    EncodeMandatoryByte( KMmsAssignedReadStatus, iMmsHeaders->ReadStatus() );
    
    // version 1.3 headers - optional
    if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
        {
        // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
        EncodeApplicationHeadersL();
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxStoreRequestL()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMboxStoreReq, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // this must be USASCII or URL encoded
    EncodeHeaderAndTextString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    // MMBox headers
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-Mms-MM-State
        EncodeOptionalByte( KMmsAssignedMMState,
            iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
        // X-MMs-MM-Flags
        // The keyword token should always be "add" or "remove"
        EncodeKeywordArrayL();
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxStoreConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMboxStoreConf, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // this must be USASCII or URL encoded
    EncodeOptionalString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    EncodeMandatoryByte( KMmsAssignedStoreStatus,
        iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );

    EncodeOptionalStringL( KMmsAssignedStoreStatusText,
        iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );

#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxViewRequestL()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMboxViewReq, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // Insert content location header. There may be more than one
    EncodeContentLocationArray();

    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-MMs-MM-Flags. The token should always be "filter"
        EncodeKeywordArrayL();
        }

    if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() )
        {
        CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL();
        
        // states used as filter
        // X-Mms-MM-State may appear multiple times in view request
        EncodeMMBoxStates( viewHeaders.MMStateArray() ); 

        // start
        EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() );
        // limit
        if ( viewHeaders.MmsLimit() != KMaxTUint32 )
            {
            // 0 means no message information, absence means all remaining
            // messages.
            // If header has value KMaxTUint32, it means that info about all
            // remaining messages is wanted.
            iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MmsLimit() );
            }

        EncodeAttributes( viewHeaders.AttributeArray() );
        // request totals
        EncodeOptionalByte( KMmsAssignedTotals, viewHeaders.MmsTotals() );
        // request quotas
        EncodeOptionalByte( KMmsAssignedQuotas, viewHeaders.MmsQuotas() );
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxViewConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMboxViewConf,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );

    EncodeMandatoryByte( KMmsAssignedResponseStatus,
        iMmsHeaders->ResponseStatus() );

    EncodeOptionalStringL( KMmsAssignedResponseText,
        iMmsHeaders->ResponseText() );

    EncodeContentLocationArray();

    // MMBox headers
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-MMs-MM-Flags. The token should always be "filter"
        EncodeKeywordArrayL();
        }
    
    if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() )
        {
        CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL();

        // states used as filter
        // X-Mms-MM-State may appear multiple times in view request
        EncodeMMBoxStates( viewHeaders.MMStateArray() ); 

        // start
        EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() );
        // limit
        if ( viewHeaders.MmsLimit() != KMaxTUint32 )
            {
            // 0 means no message information, absence means all remaining
            // messages.
            // If header has value KMaxTUint32, it means that info about
            // all remaining messages is wanted.
            iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MmsLimit() );
            }
        EncodeAttributes( viewHeaders.AttributeArray() );

        // totals
        TUint oldPosition = iPosition;
        TUint length = 0;

        if ( viewHeaders.MMBoxTotalNumber() != KMaxTUint32 )
            {
            oldPosition = iPosition;
            EncodeInteger( viewHeaders.MMBoxTotalNumber() );
            length = iPosition - oldPosition;
            length += 1; // quota token
            iPosition = oldPosition;
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 );
            iPosition++;
            EncodeValueLength( length );
            iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MMBoxTotalNumber() );
            }

        if ( viewHeaders.MMBoxTotalSize() != KMaxTUint32 )
            {
            oldPosition = iPosition;
            EncodeInteger( viewHeaders.MMBoxTotalSize() );
            length = iPosition - oldPosition;
            length += 1; // quota token
            iPosition = oldPosition;
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 );
            iPosition++;
            EncodeValueLength( length );
            iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MMBoxTotalSize() );
            }

        // quotas
        if ( viewHeaders.MMBoxQuotaNumber() != KMaxTUint32 )
            {
            oldPosition = iPosition;
            EncodeInteger( viewHeaders.MMBoxQuotaNumber() );
            length = iPosition - oldPosition;
            length += 1; // quota token
            iPosition = oldPosition;
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 );
            iPosition++;
            EncodeValueLength( length );
            iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MMBoxQuotaNumber() );
            }

        if ( viewHeaders.MMBoxQuotaSize() != KMaxTUint32 )
            {
            oldPosition = iPosition;
            EncodeInteger( viewHeaders.MMBoxQuotaSize() );
            length = iPosition - oldPosition;
            length += 1; // quota token
            iPosition = oldPosition;
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 );
            iPosition++;
            EncodeValueLength( length );
            iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MMBoxQuotaSize() );
            }

        // Message count
        // This should in principle come from the viewHeaders.MmsMessageCount()
        // But in this context the number of messages will be the number of
        // attachments available in this entry

        // However, we will use the viewHeaders.MmsMessageCount() to generate
        // the header so that we can test that the decoding part can handle
        // possible conflict between message number and the number of parts
        // in the multipart structure. This header is optional. KMaxTUint32
        // will mean unspecified.

        if ( viewHeaders.MmsMessageCount() != KMaxTUint32 )
            {
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageCount, 1 );
            iPosition++;
            EncodeInteger( viewHeaders.MmsMessageCount() );
            }
        }

    if ( iNumberOfAttachments == 0 )
        {
        EncodeMandatoryByte( KMmsAssignedContentType, KMmsAssignedAny );
        }
    else
        {
        EncodeMultipartMixedHeaderL();
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxUploadRequestL()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMBoxUploadReq, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // MMBox headers
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-Mms-MM-State
        EncodeOptionalByte( KMmsAssignedMMState,
            iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
        // X-MMs-MM-Flags
        // The keyword token should always be "add" or "remove"
        EncodeKeywordArrayL();
        }

    // Get Root TMsvId from headers if set
    TMsvAttachmentId rootId = iMmsHeaders->MessageRoot();

    // Find the matching id from attachment list
    
    TInt error = KErrNone;
    if ( iEntryWrapper )
        {
        CMsvStore* store = iEntryWrapper->ReadStoreL();
        CleanupStack::PushL( store );
        // Only new attachment structure is supported    
        MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
        TRAP( error,
            {
            CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
                (TMsvAttachmentId) rootId );
            delete attachmentInfo; // we just wanted to check that it was found
            }
            );
        CleanupStack::PopAndDestroy( store );
        }
    
    // If we have a valid root part, we send the message as multipart/related.
    // Otherwise we send it as multipart mixed.
    // We don't try to guess what the root is supposed to be, if it
    // is not specified or does not point to an existing attachment.

    // actually the content should be an MMS PDU as in M-MBox-Descr PDU
    // It is unclear if if should be wrapped in a multipart structure 
    // (containing only one header)
    // or if the content type should be application/vnd.wap.mms-message
    if ( error == KErrNone && rootId != 0 && iEntryWrapper )
        {
        EncodeMultipartRelatedHeaderL( rootId );
        }
    else
        {
        EncodeMultipartMixedHeaderL();
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxUploadConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeMBoxUploadConf, iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // this must be USASCII or URL encoded
    EncodeOptionalString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    EncodeMandatoryByte( KMmsAssignedStoreStatus,
        iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );

    EncodeOptionalStringL( KMmsAssignedStoreStatusText,
        iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeDeleteRequestL()
    {
    // Insert message type, TID and version number
    
    EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // Insert content location header. There may be more than one
    EncodeContentLocationArray();
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeDeleteConfirmationL()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(),
        iMmsHeaders->MmsVersion() );

    // content location, response status and response text headers have
    // different format from other PDUs

    const RPointerArray<CMmsDeleteResultArray>& resultArray = 
        iMmsHeaders->DeleteResultArray();

    TInt i;
    TInt length = 0;
    TInt oldPosition = 0;
    TUint oldIndex = KMaxTUint32;

    for ( i = 0; i < resultArray.Count(); i++ )
        {
        TUint index = resultArray[i]->Order();
        if ( index != oldIndex )
            {
            // We add status and status text only once, there may be more
            // than one content location with the same status
            oldIndex = index;
            if ( resultArray[i]->ResponseStatus() != 0 )
                {
                oldPosition = iPosition;
                length = 0;
                // Encode once just to find out field length
                EncodeInteger( index );
                length += iPosition - oldPosition;
                length ++; // response status is only one byte
                iPosition = oldPosition;
                // now we have calculated length, do the actual encoding
                iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseStatus, 1 );
                iPosition++;
                EncodeValueLength( length );
                EncodeInteger( index );
                TUint8 value = (TUint8) resultArray[i]->ResponseStatus();
                iEncodeBuffer->Write( iPosition, &value, 1 );
                iPosition++;
                }
            if ( resultArray[i]->ResponseText().Length() > 0 )
                {
                oldPosition = iPosition;
                length = 0;
                // Encode once just to find out field length
                EncodeInteger( index );
                EncodeTextStringL( resultArray[i]->ResponseText() );
                length += iPosition - oldPosition;
                iPosition = oldPosition;
                // now we have calculated length, do the actual encoding
                iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseText, 1 );
                iPosition++;
                EncodeValueLength( length );
                EncodeInteger( index );
                EncodeTextStringL( resultArray[i]->ResponseText() );
                }
            }
        if ( resultArray[i]->ContentLocation().Length() > 0 )
            {
            oldPosition = iPosition;
            length = 0;
            // Encode once just to find out field length
            EncodeInteger( index );
            EncodeTextString( resultArray[i]->ContentLocation() );
            length += iPosition - oldPosition;
            iPosition = oldPosition;
            // now we have calculated length, do the actual encoding
            iEncodeBuffer->Write( iPosition, &KMmsAssignedContentLocation, 1 );
            iPosition++;
            EncodeValueLength( length );
            EncodeInteger( index );
            EncodeTextString( resultArray[i]->ContentLocation() );
            }
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxDescriptionL()
    {
// implemented for test purposes
#ifdef __WINS__
    EncodeMandatoryByte( KMmsAssignedMessageType, KMmsMessageTypeMBoxDescr );

    // this must be USASCII or URL encoded
    EncodeOptionalString( KMmsAssignedContentLocation,
        iMmsHeaders->ContentLocation() );

    EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );

    // MMBox headers
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        // X-Mms-MM-State
        EncodeOptionalByte( KMmsAssignedMMState,
            iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
        // X-MMs-MM-Flags
        // The keyword token should always be "add" or "remove"
        EncodeKeywordArrayL();
        }

    // if we have set a date, insert it
    EncodeDate( iMmsHeaders->Date() );
    
    // sender is optional
    if ( iMmsHeaders->Sender().Length() > 0 )
        {
        EncodeSenderL( iMmsHeaders->Sender() );
        }

    // All recipient lists. If no recipients in some of the lists,
    // nothing is encoded, no need to check separately
    EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
    EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
    EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );

    // Message class
    EncodeOptionalByte( KMmsAssignedMessageClass,
        iMmsHeaders->MessageClass() );

    // Subject
    EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );

    // Priority
    EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
    
    // Delivery time
    EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
        iMmsHeaders->DeliveryTimeInterval(),
        iMmsHeaders->DeliveryDate() );

    // Expiration time
    EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
        iMmsHeaders->ExpiryInterval(),
        iMmsHeaders->ExpiryDate() );

    // Delivery report
    EncodeOptionalByte( KMmsAssignedDeliveryReport,
        iMmsHeaders->DeliveryReport() );

    // Read reply
    EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );

    if ( iMmsHeaders->MessageSize() > 0 )
        {
        iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 );
        iPosition++;
        TInt64 size = iMmsHeaders->MessageSize();
        EncodeLongInteger( size );
        }

    // MMBox description appears from 1.2 onwards - no use to check if we are
    // 1.1 or later.
    // Do we want to specify reply charging
    if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
        iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly ||
        iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
        iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
        {
        // Reply charging value
        EncodeOptionalByte( KMmsAssignedReplyCharging,
            iMmsHeaders->ReplyCharging() );
        // Reply charging deadline
        EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
            iMmsHeaders->ReplyChargingInterval(),
            iMmsHeaders->ReplyChargingDate() );
        // Reply charging size
        EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
        }
    // Reply charging ID
    EncodeOptionalString( KMmsAssignedReplyChargingID,
        iMmsHeaders->ReplyChargingId() );

    TInt i;
    TUint oldPosition;
    TUint length;
    for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); ++i )
        {
        oldPosition = iPosition;
        length = 0;
        // Encode once just to find out field length
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
        length += iPosition - oldPosition;
        iPosition = oldPosition;
        iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 );
        iPosition++;
        EncodeValueLength( length );
        // now we have added length, do the actual encoding
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
        oldPosition = iPosition;
        length = 0;
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
        length += iPosition - oldPosition;
        iPosition = oldPosition;
        iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 );
        iPosition++;
        EncodeValueLength( length );
        EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
        EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
        }

    if ( iNumberOfAttachments > 0 )
        {
        EncodeMultipartMixedHeaderL();
        }

#endif
    }

// ---------------------------------------------------------
// 8-bit version (no conversions)
// ---------------------------------------------------------
//
void CMmsEncode::EncodeTextString( const TDesC8& aString )
    {
    // Only Content-Id needs a quoted string, for example
    // "<agjrfnjr>
    // content-id encoding uses a separate function that adds the quote.
    // EncodeQuotedTextString

    // Check if we need a Quote (This does not mean the quoted string.)
    TInt length = aString.Length();
    if ( length > 0 && aString[0] >= KMms0x80 )
        {
        iEncodeBuffer->Write( iPosition, &KMmsQuote, 1 );
        iPosition++;
        }

    iEncodeBuffer->Write( iPosition, aString, length );
    iPosition += length;

    iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
    iPosition++;

    }

// ---------------------------------------------------------
// 8-bit version (no conversions)
// ---------------------------------------------------------
//
void CMmsEncode::EncodeQuotedTextString( const TDesC8& aString )
    {
    // Before this function is called, the caller must check
    // that the length of the string is not 0. Otherwise this
    // makes no sense. You cannot quote a zero-length string

    // We need quoted string for content-id.
    // So far for nothing else.
    // (for the filename in MIME headers, too, just in case)

    // We add one quote to the beginning, nothing to the end.
    // As we start with a quote, we don't need to check for the special
    // Quote that is needed if a string starts with a character above 128
    TInt length = aString.Length();
    iEncodeBuffer->Write( iPosition, &KMmsStringQuote, 1 );
    iPosition++;

    iEncodeBuffer->Write( iPosition, aString, length );
    iPosition += length;

    iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
    iPosition++;

    }

// ---------------------------------------------------------
// unicode version
// ---------------------------------------------------------
//
void CMmsEncode::EncodeTextStringL( const TDesC& aString )
    {

    // What about a RFC2068 quoted strings - 
    // see explanation in EncodeTextString( const TDesC8& aString ) function

    TInt i = 0;
    TInt length = aString.Length();

    TBool safe = IsStringSafe( aString );

    // If the string can be encoded as ASCII, go ahead
    if ( safe )
        {
        TUint8 character;
        // No need to check if we need a quote - if we are safe, we have 
        // no characters >= 128.

        for ( i = 0; i < aString.Length(); ++i )
            {
            character = TUint8( aString[i] & KMms0xFF );
            iEncodeBuffer->Write( iPosition, &character, 1 );
            iPosition++;
            }
        iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
        iPosition++;
        }
    else
        {
        // if our length is 0, we are safe, no need to check the 
        // length here anymore
        // we must convert to utf-8
        
        //one ucs-2 character should never produce more than 4 bytes when converted to utf-8
        // coverity[incorrect_multiplication][buffer_alloc]
		HBufC8* buffer = HBufC8::NewL( aString.Length() * KMms4 ); // paranoid.
        // we don't leave while we need buffer
        TPtr8 buf8 = buffer->Des();

        // if conversion fails, something is really seriously wrong
        iError = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, aString );

        length = buf8.Length();
        // utf8 character set encoding needs one byte (fits into short integer)
        length += KMms2; // add character set encoding byte and trailing NULL
        
        if ( buf8[0] >= KMms0x80 ) // 128
            {
            length++; // we will need a quote byte at the start...
            }
        
        EncodeValueLength( length );

        // add one byte of character set
        // this is a short integer, high bit must be set
        TUint8 charset = KMmsUtf8 | KMms0x80;

        iEncodeBuffer->Write( iPosition, &charset, 1 );
        iPosition++;

        // Check if we need a quote
        if ( buf8[0] >= KMms0x80 ) // 128
            {
            iEncodeBuffer->Write( iPosition, &KMmsQuote, 1 );
            iPosition++;
            }
        
        // Then write the string itself
        iEncodeBuffer->Write( iPosition, buf8, buf8.Length() );
        iPosition += buf8.Length();

        // Add terminating NULL
        iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
        iPosition++;

        delete buffer;
        buffer = NULL;
        }
    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeDate( const TInt64& aDate)
    {

    if ( aDate == 0 )
        {
        return;
        }

    iEncodeBuffer->Write( iPosition, &KMmsAssignedDate, 1 );
    iPosition++;

    EncodeLongInteger( aDate );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeLongInteger( const TInt64& aLongInteger )
    {

    TUint8 length = 0; // number of bytes we will need
    // The long integer will take 8 bytes or less
    TUint8 array[KMms8];

    TInt64 temp = aLongInteger;
    TInt i = 0;
    TInt64 reminder = 0;

    for ( i = 7; i >= 0; --i )
        {
        reminder = temp % 0x100;
        temp = temp / 0x100;
        array[i] = TInt8 ( I64INT( reminder ) ) ;
        }

    length = KMms8;
    i = 0;
    // The array has 8 elements, so this is safe.
    while( ( i < KMms8 ) && ( array[i] == 0 ) )
        {
        i++;
        length--;
        }

    // a zero should be coded as short integer.
    // However, if there is a valid reason to code a zero as a long integer,
    // we allow it. The caller should know what he is doing.
    if ( length == 0 )
        {
        length = 1;
        }

    // write short length
    iEncodeBuffer->Write( iPosition, &length, 1 );
    iPosition++;

    // write as many bytes as were non-zero
    // array index will stay withing limits because of the way it was calculated
    iEncodeBuffer->Write( iPosition, &(array[KMms8 - length] ), length );
    iPosition+= length;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeInteger( TUint aInteger )
    {
    // KMms1 = 1
    // KMms2 = 2
    // KMms3 = 3
    // KMms4 = 4
    // KMms5 = 5
    // etc.
    
    TUint8 byte = 0;

    if ( aInteger <= KMmsShortIntegerLimit127 )
        {
        byte = ( TInt8 ) aInteger;
        byte |= KMms0x80;
        iEncodeBuffer->Write( iPosition, &byte, 1);
        iPosition++;
        return; // this was easy
        }

    TUint8 length = KMms4; // number of bytes we will need
    TUint8 array[KMms4];

    TUint temp = aInteger;
    byte = TInt8( ( temp >> KMms24 ) & KMms0xFF );

    while ( byte == 0 && length > 0 )
        {
        length--;
        temp = temp << KMms8;
        byte = TInt8( ( temp >> KMms24 ) & KMms0xFF );
        }

    TUint i = 0;
    for ( i = 0; i < length; ++i )
        {
        array[i] = TInt8( ( temp >> ( KMms8 * ( KMms3 - i ) ) ) & KMms0xFF );
        }

    // write short length
    iEncodeBuffer->Write( iPosition, &length, 1 );
    iPosition++;

    // write as many bytes as were non-zero
    // aInteger was tested in the beginning - it needs at least one byte
    // So length is at least 1 and the array has been properly initialized.
    iEncodeBuffer->Write( iPosition, &array[0], length );
    iPosition+= length;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeSenderL( const TPtrC& aSender )
    {
    
    iEncodeBuffer->Write( iPosition, &KMmsAssignedFrom, 1 );
    iPosition++;

    // We must insert value length.

    if ( aSender.Length() == 0 )
        {
        // The MMSC must insert our address
        TUint8 length;
        length = 1;
        iEncodeBuffer->Write( iPosition, &length, 1 );
        iPosition++;
        iEncodeBuffer->Write( iPosition, &KMmsInsertAddressToken, 1 );
        iPosition++;
        return;
        }
    
    // Now we must insert something
    TUint length = 1; // address present token

    if ( aSender.Find( KMiuMau ) != KErrNotFound )
        {
        TBool safe = IsStringSafe( aSender );
        if ( safe )
            {
            // this was easy, just ASCII, no conversion
            length += aSender.Length();
            length ++; // room for terminating zero
            }
        else // not USASCII, charset must be specified
            {
            // worst case scenario.
            // must be encoded as UTF-8, value length and character set
            // must be added
        
            // one ucs-2 character will not produce more than 4 bytes when converted to utf-8
			// coverity[incorrect_multiplication][buffer_alloc]
            HBufC8* buffer = HBufC8::NewL( aSender.Length() * KMms4 ); // paranoid.
            // we don't need to push buffer onto cleanup stack, as we don't
            // leave while we are using it
            TPtr8 buf8 = buffer->Des();

            // if we get error here, something is badly wrong
            iError = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, aSender );

            length += buf8.Length();
            // utf8 character set encoding needs one byte (short integer)
            length += KMms2; // add character set encoding byte and trailing NULL
        
            if ( buf8[0] >= KMms0x80 ) // 128
                {
                length++; // we will need a quote byte at the start...
                }
            delete buffer;
            buffer = NULL;
            }
        }
    else // no miumau found
        {
        // phone number, must be ASCII
        length += aSender.Length();
        length ++; // room for terminating zero
        length += KMmsPlmnLength; // type indication
        // this should be the length, if we have a phone number (no alias.)
        }

    EncodeValueLength( length );

    iEncodeBuffer->Write( iPosition, &KMmsAddressPresentToken, 1 );
    iPosition++;

    // If the address contains some non-ascii characters,
    // it must be converted to utf-8
    
    EncodeAddressL( aSender );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAddressL( const TPtrC& aAddress )
    {
    // Supports only address types PLMN and email.
    // If the address string contains a @ character,
    // it is interpreted as an email

    // Internal alias is removed

    TMmsAddressType addressType = EMmsAddressTypeUnknown;
    HBufC* realAddress = HBufC::NewL( aAddress.Length() );
    CleanupStack::PushL( realAddress );

    TInt error = KErrNone;

    TPtr realAddressPointer = realAddress->Des();
    error = TMmsGenUtils::AddressTypeAndRealAddress(
        aAddress,
        addressType,
        realAddressPointer,
        aAddress.Length());

    if ( error != KErrNone || addressType == EMmsAddressTypeUnknown )
        {
        // could not resolve. Send unchanged
        realAddress->Des().Copy( aAddress );
        if ( aAddress.Find( KMiuMau ) != KErrNotFound )
            {
            addressType = EMmsAddressTypeEmail;
            }
        else
            {
            addressType = EMmsAddressTypeMobile;
            }
        }
        
    if ( addressType == EMmsAddressTypeEmail )
        {
        // email address
        // If the address contains only ASCII characters,
        // it can be sent as text string, if not, it must be sent as utf-8

        EncodeTextStringL( *realAddress );

        }
    else
        {
        // must be a phone number
        // We expect for now that the format is correct as is
        // All legal characters present in a phone number are ASCII

        TInt i = 0;
        TUint8 character = 0;
        realAddressPointer = realAddress->Des();
        for ( i = 0; i < realAddress->Length(); ++i )
            {
            // The array index is safe because i is always < realAddress->Length().
            character = TUint8( realAddressPointer[i] & KMms0xFF );
            iEncodeBuffer->Write( iPosition, &character, 1 );
            iPosition++;
            }
        iEncodeBuffer->Write( iPosition, KMmsPlmn, KMmsPlmnLength );
        iPosition += KMmsPlmnLength;
        iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
        iPosition++;
        }
    CleanupStack::PopAndDestroy( realAddress );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeValueLength( TUint aLength )
    {

    TUint8 shortLength = 0;

    if ( aLength <= KMms30 )
        {
        // short length
        shortLength = TUint8( aLength ) ;
        iEncodeBuffer->Write( iPosition, &shortLength, 1 );
        iPosition++;
        }
    else
        {
        iEncodeBuffer->Write( iPosition, &KMmsLengthQuote, 1 );
        iPosition++;
        EncodeUintvar( aLength );
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeUintvar( TUint aInteger )
    {

    // The value is split into 7 bit chunks, and continue bit set
    // when needed.

    // No more than 5 bytes will be produced
    TUint8 buffer[KMms5];
    TUint temp = aInteger;
    TInt i;

    for ( i = 0; i < KMms5; ++i )            
        {
        buffer[KMms4 - i] = TUint8( temp & KMms0x7F );
        temp >>= KMms7;
        }

    i = 0;
    // buffer indexes are safe because the buffer has been defined long enough.
    while ( i < KMms4 && buffer[i] == 0 )
        {
        i++;
        }
    
    TInt j;

    for ( j = i; j < KMms4; ++j )
        {
        // buffer indexes are safe because the buffer has been defined long enough.
        buffer[j] |= KMms0x80; // set Continue bit, but never to last
        }

    // buffer indexes are safe because the buffer has been defined long enough.
    iEncodeBuffer->Write( iPosition, &buffer[i], KMms5 - i );
    iPosition += KMms5 - i;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsEncode::GetUintvarLength( TUint aInteger )
    {

    // The value is split into 7 bit chunks, and continue bit set
    // when needed.

    // No more than 5 bytes will be produced
    TUint8 buffer[KMms5];
    TUint temp = aInteger;
    TInt i;

    for (i = 0; i < KMms5; ++i )            
        {
        buffer[KMms4 - i] = TUint8( temp & KMms0x7F );
        temp >>= KMms7;
        }

    i = 0;
    // buffer indexes are safe because the buffer has been defined long enough.
    while ( i < KMms4 && buffer[i] == 0 )
        {
        i++;
        }
    
    return KMms5 - i;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeRecipientL( const CDesCArray& aRecipientList,
    TMmsRecipients aType )
    {

    TInt i;
    TInt size = aRecipientList.Count();
    if ( size == 0 )
        {
        return;
        }

    TUint8 recipientType = KMmsAssignedTo;

    switch ( aType )
        {
        case EMmsTo:
            recipientType = KMmsAssignedTo;
            break;
        case EMmsCc:
            recipientType = KMmsAssignedCc;
            break;
        case EMmsBcc:
            recipientType = KMmsAssignedBcc;
            break;
        default:
            break;
        }

    for ( i = 0; i < size; ++i )
        {
        // check for fakes
        if ( aRecipientList[i].Length() > 0 )
            {
            iEncodeBuffer->Write( iPosition, &recipientType, 1 );
            iPosition++;

            EncodeAddressL( aRecipientList[i] );
            }

        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeOptionalStringL( TUint8 aHeader,
    const TPtrC16& aString )
    {

    if ( aString.Length() == 0 )
        {
        return;
        }

    iEncodeBuffer->Write( iPosition, &aHeader, 1 );
    iPosition++;

    EncodeTextStringL( aString );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeOptionalString( TUint8 aHeader, const TPtrC8& aString )
    {

    if ( aString.Length() == 0 )
        {
        return;
        }

    EncodeHeaderAndTextString( aHeader, aString );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeIntervalOrDate( TInt aInterval, const TInt64& aDate )
    {

    TInt64 temp = 0;
    TUint8 token = 0;

    if ( aDate != 0 )
        {
        temp = aDate;
        token = KMmsAbsoluteToken;
        }
    else
        {
        temp = aInterval;
        token = KMmsRelativeToken;
        }

    // calculate value length

    TUint length = KMms8;
    TUint mask = 0xff000000;

    while ( ( I64HIGH( temp ) & mask ) == 0 && length > KMms4)
        {
        mask >>= KMms8;
        length--;
        }

    mask = 0xff000000;

    // Test if the whole high half was zero
    if ( I64HIGH( temp ) == 0 )
        {
        while ( ( I64LOW( temp ) & mask ) == 0 && length > 1)
            {
            mask >>= KMms8;
            length--;
            }   
        }

    // now add short length and absolute/relative token

    length += KMms2;

    EncodeValueLength( length );

    iEncodeBuffer->Write( iPosition, &token, 1 );
    iPosition++;

    EncodeLongInteger( temp );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeReplyChargingSize( TInt aReplyChargingSize )
    {
    if ( aReplyChargingSize == 0 )
        {
        return;
        }
    TInt64 size = aReplyChargingSize;
    iEncodeBuffer->Write( iPosition, &KMmsAssignedReplyChargingSize, 1 );
    iPosition++;

    EncodeLongInteger( size );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeOptionalByte( TUint8 aHeader, TInt aValue )
    {
    if ( aValue == 0 )
        {
        return; // not set, nothing to encode, header is optional
        }
    EncodeMandatoryByte( aHeader, aValue );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMandatoryByte( TUint8 aHeader, TInt aValue )
    {
    // When ecoding mandatory byte even value 0 is allowed.
    // It will become 0x80, and be correctly decoded back to 0.
    TUint8 value = TUint8 ( aValue ) | KMms0x80 ;

    iEncodeBuffer->Write( iPosition, &aHeader, 1 );
    iPosition++;

    iEncodeBuffer->Write( iPosition, &value, 1 );
    iPosition++;
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeOptionalIntervalOrDate( TUint8 aHeader,
            TInt aInterval,
            const TInt64& aDate )
    {
    if ( aInterval == 0 && aDate == 0 )
        {
        return; // not set, nothing to encode, header is optional
        }

    iEncodeBuffer->Write( iPosition, &aHeader, 1 );
    iPosition++;

    EncodeIntervalOrDate( aInterval, aDate );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeHeaderAndTextString( TUint8 aHeader,
    const TDesC8& aString )
    {

    iEncodeBuffer->Write( iPosition, &aHeader, 1 );
    iPosition++;

    EncodeTextString( aString );

    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsEncode::IsStringSafe( const TDesC& aString )
    {

    // Very simple check to see if string is "safe" ASCII
    // Used for headers, which are short strings

    TInt i;
    TBool safe = ETrue;

    for ( i = 0; i < aString.Length() && safe; ++i )
        {
        if ( aString[i] < KMmsLowestAscii || aString[i] >= KMmsHighestAscii )
            {
            safe = EFalse;
            }
        }
    return safe;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsEncode::IsStringSafe( const TDesC8& aString, TInt& aNumNonSafe  )
    {

    // Very simple check to see if string is "safe" ASCII
    // Used for headers, which are short strings
    // spaces count as non-safe because their number must be taken into
    // account when calculating the length

    const TInt KIntQuestion = 0x3F;
    const TInt KIntEquals = 0x3D;
    
    TInt i;
    aNumNonSafe = 0;
    TBool safe = ETrue;

    for ( i = 0; i < aString.Length()/* && safe*/; ++i )
        {
        if ( aString[i] < KMmsLowestAscii || aString[i] >= KMmsHighestAscii )
            {
            safe = EFalse;
            aNumNonSafe++;
            }
        if ( aString[i] == KIntQuestion || aString[i] == KIntEquals )
            {
            // These are safe but must be encoded if quoted printable
            // encoding is used. The number is needed to check the header length
            aNumNonSafe++;
            }
        if ( aString[i] == KMmsIntUnderscore )
            {
            // This must always be encoded as base64 because Symbian
            // does not encode underscores when quoted printable is used.
            aNumNonSafe = KMaxEncodingLength;
            }
        
        }
    return safe;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMultipartRelatedHeaderL( const TMsvAttachmentId aRootId )
    {

    iEncodeBuffer->Write( iPosition, &KMmsAssignedContentType, 1 );
    iPosition++;

    // We have parameters (start), so we must use Content-general-form

    // We need the content-id for the root part. If it is not defined already,
    // we must generate one.

    TUint headerLength = 1; // one byte for well known media
    TInt cleanupCount = 0; // we must keep track what we have on store

    User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
        
    CMsvStore* store = iEntryWrapper->EditStoreL();
    CleanupStack::PushL( store ); cleanupCount++;
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    CMsvAttachment* attachmentInfo = NULL;
    attachmentInfo = attachMan.GetAttachmentInfoL( (TMsvAttachmentId) aRootId );
    CleanupStack::PushL( attachmentInfo ); cleanupCount++;
    
    iMimeHeaders->RestoreL( *attachmentInfo );

    if ( iError != KErrNone )
        {
        CleanupStack::PopAndDestroy( cleanupCount ); 
        return;
        }

    TPtrC8 cid = iMimeHeaders->ContentId();
    TBufC8<KMmsMaxCidLength> target;

    // If cid has not been defined, we must generate one
    if ( cid.Length() == 0 )
        {
        TTime now;
        now.UniversalTime();
        TInt random;
        TInt64 seed = now.Int64();
        random = Math::Rand( seed );
        // type conversions irrelevant - just creating a magic number for content id
        target.Des().Num( random );
        target.Des().Insert(0, KMmsLeftAngle );
        target.Des().Append( KMmsRightAngle );
        cid.Set( target.Des() );
        // exclude the angle brackets
        iMimeHeaders->SetContentIdL( cid.Mid( 1, cid.Length() - KMms2 ) );
        // save the cid to be present when we create the attachment headers
        // If this does not succeed, the message will have no root.
        MMsvAttachmentManagerSync& attachManSync = 
            store->AttachmentManagerExtensionsL();
        iMimeHeaders->StoreL( *attachmentInfo );
        attachManSync.ModifyAttachmentInfoL( attachmentInfo );
        // attachment manager now own attachemnt info
        CleanupStack::Pop( attachmentInfo ); cleanupCount--;
        store->CommitL();
        attachmentInfo = attachMan.GetAttachmentInfoL( (TMsvAttachmentId) aRootId );
        CleanupStack::PushL( attachmentInfo ); cleanupCount++;
        }

    // add start header length &
    // assigned number for start parameter, and terminating zero for cid
    headerLength += cid.Length() + KMms2;
    if ( cid.Find( KMmsLeftAngle ) != 0 )
        {
        headerLength += KMms2; // we must add angle bracket
        }

    // Then the length of the content-type header...
    TPtrC8 contentTypeString = iMimeHeaders->ContentType();
    
     HBufC8* tempContentType = HBufC8::NewL( iMimeHeaders->ContentType().Length() +
         iMimeHeaders->ContentSubType().Length() + 1 );
     CleanupStack::PushL( tempContentType );
     tempContentType->Des().Copy( iMimeHeaders->ContentType() );
     if ( ( iMimeHeaders->ContentType().Find( KMmsSlash8 ) !=
        ( iMimeHeaders->ContentType().Length() - 1 ) ) &&
        ( iMimeHeaders->ContentSubType().Find( KMmsSlash8 ) != 0 ) &&
        ( iMimeHeaders->ContentSubType().Length() != 0 ) )
        {
        tempContentType->Des().Append( KMmsSlash8 );
        }
    tempContentType->Des().Append( iMimeHeaders->ContentSubType() );
    attachmentInfo->SetMimeTypeL( tempContentType->Des() );
    CleanupStack::PopAndDestroy( tempContentType );
    contentTypeString.Set( attachmentInfo->MimeType() );

    if ( contentTypeString.Length() == 0 )
        {
        // We need a content type...
        // If we don't know, we say "Any"
        contentTypeString.Set( KMmsAny );
        }
    // Check if we have well-known media.
    TInt8 rootContentType = -1;

    TInt8 i = 0;
    for ( i = 0; i < KNumberContentTypes && rootContentType < 0; ++i )
        {
        if ( contentTypeString.CompareF( TPtrC8( KContentTypeTable[i] ) ) == 0 )
            {
            rootContentType = i;
            }
        }
        
    // start parameter assignment
    headerLength += 1;
    if ( rootContentType != -1 )
        {
        // well known content type
        headerLength += 1;
        }
    else
        {
        // string + terminating zero
        headerLength += contentTypeString.Length() + 1;
        }
        
    TPtrC8 appIdPtr;
    appIdPtr.Set( KMmsJavaApplicationId ); 
    // Java application id parameters added to content-type        
    if ( iMmsHeaders->ApplicId().Length() > 0 )
        {
        headerLength += appIdPtr.Length() + 1 ;
        headerLength += iMmsHeaders->ApplicId().Length() + 1;
        }
    appIdPtr.Set( KMmsJavaReplyApplicationId );    
    if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
        {
        headerLength += appIdPtr.Length() + 1 ;
        headerLength += iMmsHeaders->ReplyApplicId().Length() + 1;
        }

    // write general encoding length
    EncodeValueLength( headerLength );

    // Then the Media Type with parameters
        
    // We are multipart/related, which is a well-known media
    // encode as short integer
    TUint8 byte = KMmsAssignedApplicationVndWapMultipartRelated | KMms0x80;
    iEncodeBuffer->Write( iPosition, &byte, 1 );
    iPosition++;

    // Then the start parameter and cid text string
    byte = KWspStart | KMms0x80;
    
    if ( cid.Length() > 0 )
        {
        HBufC8* tempContentId = HBufC8::NewL( iMimeHeaders->ContentId().Length() + KMms2 );
        CleanupStack::PushL( tempContentId );
        if ( cid.Find( KMmsLeftAngle ) != 0 )
            {
            tempContentId->Des().Copy( KMmsLeftAngle );
            tempContentId->Des().Append( cid );
            tempContentId->Des().Append( KMmsRightAngle );
            }
        else
            {
            tempContentId->Des().Copy( cid );
            }
        EncodeHeaderAndTextString( byte, tempContentId->Des() );
        CleanupStack::PopAndDestroy( tempContentId );
        }


    // next the content type of the root part
    byte = KWspRelatedType | KMms0x80;
    // and the actual type either as a well-known media type
    // or a text string.

    if ( rootContentType != -1 )
        {
        // Well known content type.
        // EncodeMandatoryByte will set the high bit
        EncodeMandatoryByte( byte, rootContentType );
        }
    else
        {
        // string + terminating zero
        EncodeHeaderAndTextString( byte, contentTypeString );
        }
       
    if ( iMmsHeaders->ApplicId().Length() > 0 ||
        iMmsHeaders->ReplyApplicId().Length() > 0 )
        {
        EncodeApplicationIdParametersL();
        }
            
    // encode number of parts
    EncodeUintvar( iNumberOfAttachments );
    
    // get rid of stuff we put on stack
    CleanupStack::PopAndDestroy( cleanupCount ); 
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMultipartMixedHeaderL()
    {
    
    // If Java has added application id or reply-to application id, 
    // even multipart/mxed type needs parameters.
    
    TUint headerLength = 0; // assume no parameters
    
    if ( iMmsHeaders->ApplicId().Length() > 0 ||
        iMmsHeaders->ReplyApplicId().Length() > 0 )
        {
        headerLength = 1; // one byte for well known media
        TPtrC8 appIdPtr;
        appIdPtr.Set( KMmsJavaApplicationId ); 
        if ( iMmsHeaders->ApplicId().Length() > 0 )
            {
            headerLength += appIdPtr.Length() + 1 ;
            headerLength += iMmsHeaders->ApplicId().Length() + 1;
            }
        appIdPtr.Set( KMmsJavaReplyApplicationId );    
        if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
            {
            headerLength += appIdPtr.Length() + 1 ;
            headerLength += iMmsHeaders->ReplyApplicId().Length() + 1;
            }
        }

    iEncodeBuffer->Write( iPosition, &KMmsAssignedContentType, 1 );
    iPosition++;
    
    if ( headerLength > 0 )
        {
        // write general encoding length
        EncodeValueLength( headerLength );
        }

    // encode as short integer
    TUint8 contentType = KMmsAssignedApplicationVndWapMultipartMixed | KMms0x80;
    iEncodeBuffer->Write( iPosition, &contentType, 1 );
    iPosition++;

    if ( iMmsHeaders->ApplicId().Length() > 0 ||
        iMmsHeaders->ReplyApplicId().Length() > 0 )
        {
        EncodeApplicationIdParametersL();
        }

    // No more parameters for multipart/mixed, actual parts follow

    // encode number of parts
    EncodeUintvar( iNumberOfAttachments );
    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeKeywordArrayL()
    {
    TInt i = 0;
    TInt length = 0;

    // caller must check that iMmsHeaders->ReadOnlyMMBoxMessageHeaders() is not NULL
    CMmsMMBoxMessageHeaders& temp = iMmsHeaders->MMBoxMessageHeadersL();
    for ( i = 0; i < temp.KeywordArray().Count(); ++i )
        {
        length = temp.KeywordArray()[i]->Keyword().Length();
        if ( length > 0 )
            {
            iEncodeBuffer->Write( iPosition, &KMmsAssignedMMFlags, 1 );
            iPosition++;
            // do some fake encoding to get the text length
            TUint oldPosition = iPosition; // we will return here
            EncodeTextStringL( temp.KeywordArray()[i]->Keyword() );
            length = iPosition - oldPosition;
            iPosition = oldPosition; // return to original place
            length += 1; // token must come before string
            EncodeValueLength( length );
            TUint8 token = (TUint8) temp.KeywordArray()[i]->Token();
            if ( iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewReq ||
                 iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewConf ||
                 iMmsHeaders->MessageType() == KMmsMessageTypeMBoxDescr )
                {
                // the token must always be filter for MMbox PDUs
                token = KMmsFilterToken;
                }
            iEncodeBuffer->Write( iPosition, &token, 1 );
            iPosition++;
            EncodeTextStringL( temp.KeywordArray()[i]->Keyword() );
            }
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeOptionalInteger( TUint8 aHeader, TUint aValue )
    {
    if ( aValue == 0 )
        {
        return; // not set, nothing to encode, header is optional
        }

    iEncodeBuffer->Write( iPosition, &aHeader, 1 );
    iPosition++;

    EncodeInteger( aValue );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeAttributes( RArray<TUint>& aAttributeArray )
    {
    TInt i;

    for ( i = 0; i < aAttributeArray.Count(); ++i )
        {
        EncodeMandatoryByte( KMmsAssignedAttributes, aAttributeArray[i] );
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeMMBoxStates( RArray<TInt>& aStateArray )
    {
    TInt i;

    for ( i = 0; i < aStateArray.Count(); ++i )
        {
        EncodeMandatoryByte( KMmsAssignedMMState, aStateArray[i] );
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeContentLocationArray()
    {
    TBool mustCheck = EFalse;
    if ( iMmsHeaders->ContentLocation().Length() > 0 )
        {
        mustCheck = ETrue;
        EncodeHeaderAndTextString( KMmsAssignedContentLocation,
            iMmsHeaders->ContentLocation() );
        }
    // If there is content location array, encode it, too.
    // A content-location should never occur in both places, but we still check

    TInt i = 0;
    if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
        {
        for ( i = 0;
            i < iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList().Count();
            i++ )
            {
            if ( !( mustCheck && iMmsHeaders->ContentLocation().Compare(
                iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] ) == 0 ) )
                {
                // the content location in the list does not appear as
                // separate header, we must add it
                EncodeHeaderAndTextString( KMmsAssignedContentLocation,
                    iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] );
                }
            }
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeStartingHeaders( TInt aMessageType,
    const TPtrC8& aTID, TInt aVersion )
    {
    // EncodeMandatoryByte will always set the high bit.
    
    // Message type
    EncodeMandatoryByte( KMmsAssignedMessageType, aMessageType );
    // TID if present
    EncodeOptionalString( KMmsAssignedTID, aTID );
    // MMS encapsulation version
    EncodeMandatoryByte( KMmsAssignedMmsVersion, aVersion );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsEncode::EncodeApplicationIdParametersL()
    {
    if ( iMmsHeaders->ApplicId().Length() > 0 )
        {
        EncodeTextString( KMmsJavaApplicationId );
        EncodeTextStringL( iMmsHeaders->ApplicId() );
        }
    if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
        {
        EncodeTextString( KMmsJavaReplyApplicationId );
        EncodeTextStringL( iMmsHeaders->ReplyApplicId() );
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsEncode::Dump()
    {
    // no dump if not logging
#ifndef _NO_MMSS_LOGGING_
    TInt error = KErrNone;
    // if no can do, sorry.
    TRAP( error,
        {
        if ( iEntryWrapper )
            {
            if ( ( iEntryWrapper->GetDumpFlag() ) &&
                iEncodeBuffer &&
                iEncodeBuffer->Size() > 0 )
                {
                iFileName = KMmsDefaultLogDirectory;
                TUint att;
                if ( iFs.Att( iFileName, att ) == KErrNone )
                    {
                    _LIT( KRelated, "Sent.mms");
                    iParse.Set( iFileName, &KRelated, NULL );
                    iFileName = iParse.FullName();
                    User::LeaveIfError( CApaApplication::GenerateFileName(
                        iFs, iFileName ) );
                    RFile file;
                    User::LeaveIfError( file.Create( iFs, iFileName,
                        EFileWrite | EFileShareExclusive ) );
                    // for message id generation
                    iParse.Set( iFileName, NULL, NULL );

                    // the data is supposed to be in the encode buffer
                    TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition );
                    file.Write( ptr );
                    file.Flush();

                    // done - close files
                    file.Close();
                    }
                }
            }
        }
    );
    if ( error != KErrNone )
        {
        TMmsLogger::Log( _L("Dump left with error %d"), error );
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsEncode::DumpAppend()
    {
    
    // no dump if not logging
#ifndef _NO_MMSS_LOGGING_
    TInt error = KErrNone;
    // if no can do, sorry.
    TRAP( error,
        {
        if ( iEntryWrapper )
            {
            if ( ( iEntryWrapper->GetDumpFlag() ) &&
                iEncodeBuffer &&
                iEncodeBuffer->Size() > 0 )
                {
                iFileName = KMmsDefaultLogDirectory;
                TUint att;
                if ( iFs.Att( iFileName, att ) == KErrNone )
                    {
                    RFile file;
                    iFileName = iParse.FullName();
                    User::LeaveIfError( file.Open( iFs, iFileName,
                       EFileWrite | EFileShareExclusive ) );
                    TInt position = 0; // seek to end
                    file.Seek( ESeekEnd, position );
                    // the data is supposed to be in the encode buffer
                    TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition );
                    file.Write( ptr );
                    file.Flush();
                    // done - close files
                    file.Close();
                    }
                }
            }
        }
    );
    if ( error != KErrNone )
        {
        TMmsLogger::Log( _L("DumpAppend left with error %d"), error );
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsEncode::EncodeApplicationHeadersL()
    {
// Application headers are only supported in WINS for testing purposes
// Complete support for routing messages to arbitrary applications
// requires support for selecting which applications are allowed
// to send messages etc.    
#ifdef __WINS__
    // The optional string functions check the length
    // and return without doing anything if the length of the string is 0
    if ( IsStringSafe( iMmsHeaders->ApplicId() ) )
        {
        // We only send this header if it is us-ascii only
        // There is no encoding defined, so if this is converted
        // from unicode to some other character set there is 
        // no guarantee that the recipient can decode it.
        EncodeOptionalStringL( KMmsAssignedApplicId, iMmsHeaders->ApplicId() );
        }
        
    if ( IsStringSafe( iMmsHeaders->ReplyApplicId() ) )
        {
        EncodeOptionalStringL( KMmsAssignedReplyApplicId, iMmsHeaders->ReplyApplicId() );
        }

    EncodeOptionalString( KMmsAssignedAuxApplicInfo, iMmsHeaders->AuxApplicInfo() );
#endif    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsEncode::EncodeCancelRequest()
    {
// implemented for test purposes
#ifdef __WINS__
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeCancelReq,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
        
    EncodeOptionalString( KMmsAssignedCancelId, iMmsHeaders->ReplaceCancelId() );
#endif    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsEncode::EncodeCancelResponse()
    {
    // Insert message type, TID and version number
    EncodeStartingHeaders( KMmsMessageTypeCancelConf,
        iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
        
    EncodeOptionalByte( KMmsAssignedCancelStatus, iMmsHeaders->CancelStatus() );   
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
HBufC8* CMmsEncode::EncodeQuotedPrintableWordL( const TPtrC8& aSource )
    {
    // We have calculated that it fits
    
    TInt bufferLength = KMaxNameBufferLength;
    
    HBufC8* buffer = HBufC8::NewL( bufferLength );
    CleanupStack::PushL( buffer );
    TPtr8 ptr = buffer->Des();
    buffer->Des().Copy( KMmsQuotedPreamble );
    ptr.SetLength( bufferLength );
    TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength );
    encodeBuffer.SetLength( 0 ); // empty the buffer
    
    TImCodecQP encoder;

    // The function would return the number of characters written to the buffer.
    // We don't do anything with the result, so we ignore the return value.
    encoder.Encode( aSource, encodeBuffer );
    
    ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength );
    
    buffer->Des().Append( KMmsEncodingTrailer );
    CleanupStack::Pop( buffer );
    return buffer;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
HBufC8* CMmsEncode::EncodeBase64WordL( const TPtrC8& aSource )
    {
    // ((length + 2)/3)*4  3 bytes alwaus produces 4 encoded bytes. Allow filler
    TInt bufferLength = KMmsEncodingExtraLength + ( aSource.Length() + KMms2 ) / KMms3 * KMms4;
    
    HBufC8* buffer = HBufC8::NewL( bufferLength );
    CleanupStack::PushL( buffer );
    TPtr8 ptr = buffer->Des();
    buffer->Des().Copy( KMmsBase64Preamble );
    ptr.SetLength( bufferLength );

    TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength );
    encodeBuffer.SetLength( 0 ); // empty the buffer

    TImCodecB64 encoder;

    // It is rather unclear what this function actually returns (no documentation found). 
    // Therefore we just ignore the result.
    // Our buffer is long enough for the result to always fit.
    encoder.Encode( aSource, encodeBuffer );
    
    ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength );
    
    buffer->Des().Append( KMmsEncodingTrailer );
    CleanupStack::Pop( buffer );
    return buffer;
    }
    

// ---------------------------------------------------------
// CMmsEncode::PreProcessAttachmentDataL
// Open the message store(Edit mode) and process attachments for further encoding in required encoding type.
// Update the message store accordingly with the new encoding type and data in the corresponding attachments.
// ---------------------------------------------------------
void CMmsEncode::PreProcessAttachmentDataL()
    {
    TInt error = KErrNone;
    RFile attachFile;
    TBool retVal = EFalse;
    TInt currAttachI;
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- start ") );
#endif /* _NO_MMSS_LOGGING_ */
    CMsvStore* editStore = iEntryWrapper->EditStoreL();
    CleanupStack::PushL( editStore );

    MMsvAttachmentManager& attachMan = editStore->AttachmentManagerL();
    MMsvAttachmentManagerSync& attachManagerSync = editStore->AttachmentManagerExtensionsL();
    TInt numberOfAttachments = attachMan.AttachmentCount();
    CMsvAttachment* attachmentInfo = NULL;
    iTextUtils = CMsgTextUtils::NewL( iFs );
    
    for ( currAttachI = 0; ( currAttachI < numberOfAttachments ) && ( error == KErrNone ); currAttachI++ )
        {
        //gets the ownership from attachment manager
        attachmentInfo = attachMan.GetAttachmentInfoL( currAttachI );
        CleanupStack::PushL( attachmentInfo );       
        iMimeHeaders->RestoreL( *attachmentInfo );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- Attchment:%d "), currAttachI );
#endif /* _NO_MMSS_LOGGING_ */
        
        if ( iFileOpen )
            {
            // close any file in case we have been reset while the file is still open
            iAttachFile.Close();
            iFileOpen = EFalse;
            }

        retVal = CheckAndUpdateAttachmentL(*attachmentInfo, *iMimeHeaders);

        if( retVal )
            {
            TRAPD( 
                    err,
                    attachManagerSync.ModifyAttachmentInfoL(attachmentInfo);
                    editStore->CommitL();
                  );
#ifndef _NO_MMSS_LOGGING_
            if(err != KErrNone)
                {
                TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit error: %d"), err );
                }
            else
                {
                TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit success") );                
                }
#endif /* _NO_MMSS_LOGGING_ */
            /*  attachmentInfo ownership is transferred to attachment manager
             *  Hence, JUST pop attachmentInfo, DO NOT Destroy.
             */
            CleanupStack::Pop( attachmentInfo );
            }
        else
            {
            CleanupStack::PopAndDestroy( attachmentInfo );
            attachmentInfo = NULL;
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: Tgt encoding NOT Reqd.") );                
#endif /* _NO_MMSS_LOGGING_ */
            }
        }
    CleanupStack::PopAndDestroy( editStore );
    
    delete iTextUtils;
    iTextUtils = NULL;
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- end ") );
#endif /* _NO_MMSS_LOGGING_ */
    }

// ---------------------------------------------------------
// CMmsEncode::CheckAndUpdateAttachmentL
// Check and proceed if given attachment can be encoded using target encoding type based on its content type.
// Returns False if target encoding is not supported for this content type.
// ---------------------------------------------------------
TBool CMmsEncode::CheckAndUpdateAttachmentL( CMsvAttachment& aAttachmentInfo,
                                             CMsvMimeHeaders& aMimeHeaders )
    {
    //get the content type string... and set to attachment if required
    TInt contentType = -1; // indicate not found
    TPtrC8 contentTypeString;
    TBool retVal = EFalse;
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- start ") );
#endif /* _NO_MMSS_LOGGING_ */

    HBufC8* tempContentType = NULL;
    contentTypeString.Set( aAttachmentInfo.MimeType() );
    if ( contentTypeString.Length() == 0 )
        {
        // take type from mime headers
        TInt cotentLength = aMimeHeaders.ContentType().Length();
        TInt subCotentLength = aMimeHeaders.ContentSubType().Length();
        tempContentType = HBufC8::NewL( cotentLength + subCotentLength + 1 );
        CleanupStack::PushL( tempContentType );
        tempContentType->Des().Copy( aMimeHeaders.ContentType() );
        if ( ( aMimeHeaders.ContentType().Find( KMmsSlash8 ) != ( cotentLength - 1 ) ) &&
             ( aMimeHeaders.ContentSubType().Find( KMmsSlash8 ) != 0 ) && ( subCotentLength != 0 ) )
            {
            tempContentType->Des().Append( KMmsSlash8 );
            }
        tempContentType->Des().Append( aMimeHeaders.ContentSubType() );
        aAttachmentInfo.SetMimeTypeL( tempContentType->Des() );
        contentTypeString.Set( aAttachmentInfo.MimeType() );
        }
        
    //map the content type string to content type
    TInt8 i;
    for ( i = 0; i < KNumberContentTypes && contentType < 0; i++ )
        {
        if ( contentTypeString.CompareF( TPtrC8( KContentTypeTable[i] ) ) == 0 )
            {
            contentType = i;
            }
        }
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- content type:%d" ), contentType );
#endif /* _NO_MMSS_LOGGING_ */
    //check if this content type need to be encoded using korean specific
    if( IsConversionSupportedContentType( contentType ) )
        {
        /* Do the attachment data conversion from "src type" to "target type" for the given "attached file". 
         * Target type encodng MIB enum is obtained from cenrep.
         */
        retVal = ProcessAndConvertAttachmentDataL( aMimeHeaders.MimeCharset(),
                                                   iTargetEncodingType,
                                                   aAttachmentInfo);
        if( retVal )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- conv success: Tgt Enc: %d"), iTargetEncodingType );
#endif /* _NO_MMSS_LOGGING_ */
            //set new charset type in headers and update attachment info
            aMimeHeaders.SetMimeCharset( iTargetEncodingType );
            aMimeHeaders.StoreL(aAttachmentInfo); // save the new charset to attachment as well
            /* mimetype might get reset to original mimeheader content type.
             * ensure the full content type(if created from mime content and subcontent) is set to attachment
             */
            if( tempContentType != NULL )
                {
                aAttachmentInfo.SetMimeTypeL( tempContentType->Des() );
                CleanupStack::PopAndDestroy( tempContentType );                
                }
            }
        }
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- End , retVal: %d"), retVal );
#endif /* _NO_MMSS_LOGGING_ */
    return retVal;
    }
    
// ---------------------------------------------------------
// CMmsEncode::ProcessAndConvertAttachmentDataL
// converts of attachment data from source to target encoding type.
// |src charset buffer| --->converted to ---> |unicode buffer| ---> converted to ---> |target charset|
// Returns false if data is already int target format, or plugins are missing, or file operation issues.
// ---------------------------------------------------------
TBool CMmsEncode::ProcessAndConvertAttachmentDataL( TUint aSrcCharSetMIBEnum,
                                                    TUint aTargetCharSetMIBEnum, 
                                                    CMsvAttachment& aAttachmentInfo)
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- start ") );
#endif /* _NO_MMSS_LOGGING_ */
    if( aSrcCharSetMIBEnum == aTargetCharSetMIBEnum )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- src and dest are same ") );
#endif /* _NO_MMSS_LOGGING_ */
        //if source and target charset MIB enums are same.  
        return EFalse; // no conversion 
        }
        
    //get source and target charset mapping here. if any error, return
    TUint srcCharSetId = iTextUtils->MibIdToCharconvIdL( aSrcCharSetMIBEnum );
    TUint targetCharSetId = iTextUtils->MibIdToCharconvIdL( aTargetCharSetMIBEnum );
    if( srcCharSetId == targetCharSetId || KDefaultCharConvCharset == targetCharSetId)
        {
        /* If target charset plugin is missing, then default is returned. 
           Do not encode to any target encoding type, send data as is .
         */ 
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- missing plugin: Tgt Charset %x"), targetCharSetId );
#endif /* _NO_MMSS_LOGGING_ */
        return EFalse;
        }
    
    RFile attachFile;
    TInt error;

    const TDesC& fileName = aAttachmentInfo.FilePath();
    error = attachFile.Open(iFs, fileName, EFileWrite);
    if(error != KErrNone)
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file: %s, open error: %d"), fileName, error );
#endif /* _NO_MMSS_LOGGING_ */
        attachFile.Close();
        return EFalse;
        }

    TInt maxLength=0;
    error = attachFile.Size(maxLength);
    if( error != KErrNone || maxLength == 0 )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file size: %d, error: %d"), maxLength, error );
#endif /* _NO_MMSS_LOGGING_ */
        attachFile.Close();
        return EFalse;
        }

    //read the original file data into srcBuffer
    TInt cleanupCount = 0;    
    HBufC8* srcBuffer = HBufC8::NewLC( maxLength );
    cleanupCount++;
    
    TPtr8 srcPtr = srcBuffer->Des();
    attachFile.Read( srcPtr, maxLength );
        
    //intermediate buffer in the form of unicode. 
    HBufC16* unicodeBuffer(NULL);

    //Convert, scrBuffer to unicode format if not already in unicode format
    if(srcCharSetId != 0)
        {
        //Convert from respective foreign charset to unicode buffer.
        //returns buf16 pointer descriptor
        TRAPD(err, unicodeBuffer = iTextUtils->ConvertToUnicodeL(srcPtr, srcCharSetId) );
        if( err != KErrNone )
            {
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: ConvertToUnicodeL error: %d"), err );
#endif /* _NO_MMSS_LOGGING_ */
            delete unicodeBuffer;
            attachFile.Close();
            CleanupStack::PopAndDestroy( cleanupCount, srcBuffer ); // srcBuffer
            return EFalse;
            }
        CleanupStack::PushL( unicodeBuffer );
        cleanupCount++;
        }
    else
        {
        TInt unicodeStdHeaderWordLE = 0xFEFF;
        TInt unicodeStdHeaderWordBE = 0xFFFE;
        //data is already in unicode format. need to extract the data to 16-bit buffer
        unicodeBuffer = HBufC16::NewLC( maxLength/2 );//maxlength value is in terms of bytes
        cleanupCount++;
        
        TPtr16 ptr = unicodeBuffer->Des();
        iTextUtils->ConvertPtrToDesC16(srcPtr, ptr);
        /* In case if attachment is UTF-16 format, it will have the UTF-16 representation word(2 butes)
         * at the start. Find and delete it before passing the data for conversion
         */ 
        if( ptr[0] == unicodeStdHeaderWordLE || ptr[0] == unicodeStdHeaderWordBE )
            {
            ptr.Delete(0, 1);
            }
        }

    //Now convert unicode buffer to respective target charset type.
    if( targetCharSetId != 0 )
        {
        // reset this file and write new encoded data to it directly
        error = attachFile.SetSize(0);
#ifndef _NO_MMSS_LOGGING_
        if( error != KErrNone )
            {
            TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file size: %d, error: %d"), maxLength, error );
            //attachFile.Close();
            //return EFalse;
            }
#endif /* _NO_MMSS_LOGGING_ */
        
        /* convert to target charset id.
         * Ideally, this should not leave with plugin-NOT-found, since it it already verified
         */
        iTextUtils->ConvertToFileL(*unicodeBuffer, attachFile, targetCharSetId );
        }
    else
        {
        TPtr16 ptr = unicodeBuffer->Des();
        //Reset/erase old file content.
        TInt err = attachFile.SetSize(0);

        //write new encoded data to file using write stream
        RFileWriteStream writer( attachFile );
        writer.PushL();
        writer.WriteL( ptr );
        //writer.CommitL();
        writer.Pop();
        writer.Close();
        }
    //close file
    attachFile.Close();
    CleanupStack::PopAndDestroy( cleanupCount, srcBuffer ); //unicodeBuffer, srcBuffer
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- End, retVal: TRUE") );
#endif /* _NO_MMSS_LOGGING_ */
    return ETrue;
    }


// ---------------------------------------------------------
// CMmsEncode::IsConversionSupportedContentType
// checks if input content type is supported for target encoding
// ---------------------------------------------------------
TBool CMmsEncode::IsConversionSupportedContentType( TInt aContentType )
    {
    /* Currently only "text/plain" content types are supported for korean encoding.
     * Add to the switch case, if any new content type can be supported as well.
     * 
     * IMPORTANT: 
     * Ensure the values added map to corresponding content type entry in the table KContentTypeTable[],
     * defined in ../inc/mmscodec.h
     */
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::IsConversionSupportedContentType- start: %d "), aContentType );
#endif /* _NO_MMSS_LOGGING_ */
    TBool retVal = EFalse;
    switch( aContentType )
        {
        case 0x03 :           // "text/plain" 
            {
            retVal = ETrue;
            break;
            }
        default:
            //do nothing
            break;// to avoid compile errors/warning
        }
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("CMmsEncode::IsConversionSupportedContentType- end ") );
#endif /* _NO_MMSS_LOGGING_ */
    return retVal;
    }

// ================= OTHER EXPORTED FUNCTIONS ==============

//  End of File