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



#include    <apparc.h>
#include    <s32mem.h>
#include    <utf.h>
#include    <msvids.h>
#include    <escapeutils.h>
#include    <e32math.h>
#include    <badesca.h>
#include    <cmsvmimeheaders.h>
#include    <mmsvattachmentmanager.h>
#include    <mmsvattachmentmanagersync.h>
#include	<fileprotectionresolver.h>
#include    <apmstd.h>
// Features
#include    <featmgr.h>    
#include    <bldvariant.hrh>

#include    "mmsheaders.h"
#include    "mmsconst.h"
#include    "mmsdecode.h"
#include    "mmscodec.h"
#include    "mmsservercommon.h"
#include    "mmssession.h"
#include    "mmsgenutils.h"
#include    "mmsentrywrapper.h"
#include    "mmserrors.h"
#include    "mmsmmboxmessageheaders.h"
#include    "mmsmmboxviewheaders.h"
#include    "mmselementdescriptor.h"

enum TMmsMachineStates
    {
    EMmsIdle,
    EMmsDecodingHeaders,
    EMmsDecodingAttachments,
    EMmsFinished
    };

// These are the stages for chunked encoding.
// The chunked encoding is synchronous, so the stages are different
// from the machine states.    
enum TMmsChunkedDecodingStages
    {
    EMmsNotApplicable, // stage used when not doing chunks
    EMmsHeaders,
    EMmsAttachmentHeaders,
    EMmsAttachmentDataStart, // create attachment file and add data
    EMmsAttachmentDataAppend, // append data to existing attachment
    EMmsDone
    };

static const CMmsDecode::TMmsExtensionLookup KMmsFileExtensionMatchTable[] =
    {
        { _S8( "jpeg" ), _S16( "*.jpg" ) },
        { _S8( "gif" ), _S16( "*.gif" ) },
        { _S8( "text" ), _S16( "*.txt" ) },
        { _S8( "smil" ), _S16( "*.smil" ) },
        { _S8( "wbmp" ), _S16( "*.wbmp" ) },
        { _S8( "png" ), _S16( "*.png" ) },
        { _S8( "amr" ), _S16( "*.amr" ) },
        { _S8( "vcard" ), _S16( "*.vcf" ) },
        { _S8( "calendar" ), _S16( "*.vcs" ) },
        { _S8( "drm.message" ), _S16( "*.dm" ) },
        { _S8( "drm.content" ), _S16( "*.dcf" ) },
        { _S8( "drm.rights+xml" ), _S16( "*.dr" ) },
        { _S8( "drm.rights+wbxml" ), _S16( "*.drc" ) },
        { _S8( "drm.dcf" ), _S16( "*.odf" ) },
        // last entries in the table are empty so that
        // we can find the end of the table
        { _S8( "" ), _S16( "" ) }
    };
    
// application specific headers with known handling
static const CMmsDecode::TMmsExtensionHeaderLookup KMmsExtensionHeaderLookup[] =
    {
        { _S8( "X-Mms-Vodafone-Notif-Text" ), &KMmsAssignedExtendedNotificationText },
        { _S8( "X-Mms-Vodafone-Notif-EOL" ), &KMmsAssignedExtendedNotificationEOL },
        // end of table
        { _S8( "" ), 0 }
    };

_LIT8( KMmsWapMultipartReport, "application/vnd.wap.multipart.report" );
// fake assigned number - last one possible
const TUint8 KMmsAssignedApplicationVndWapMultipartReport = 0x7F;

// a header that is a text string, not assigned number
const TUint8 KMmsTextHeader = 0x7F;

_LIT8( KMmsExtension, "X-" );

#ifndef _NO_MMSS_LOGGING_
_LIT16( KLogYes, "-- Yes" );
_LIT16( KLogNo, "-- No" );
_LIT16( KLogUnknown, "-- Unknown %d" );
#endif

_LIT8( KmmsSmilMimeType,"application/smil");
_LIT8( KMmsSmilSubtype, "smil" );
_LIT8( KMmsAudioSubtype, "amr" );
_LIT8( KMmsAudioSubtype2, "x-amr" ); // old subtype - someone may send this
// main type for audio, don't mark for example "application/amr" as audiomessage
// as audiomessage UI can't handle it. UI only handles "audio/amr"
_LIT8( KMmsAudioType, "audio" );

_LIT8( KMessageClassPersonal, "Personal" );
_LIT8( KMessageClassAdvertisement, "Advertisement" );
_LIT8( KMessageClassInformational, "Informational" );
_LIT8( KMessageClassAuto, "Auto" );
         
#ifndef _NO_MMSS_LOGGING_
const TInt KMmsHalfByteShift = 4;
const TUint8 KMms0x0F = 0x0F;
const TInt KMmsDateFormatLength = 30; // length for date string buffer for logging
const TInt KMmsMicroToSeconds = 1000000;
#endif
const TUint8 KMms0x20 = 0x20; // lowest ascii, space
const TInt KMmsMinHeaderLength = 2; // no header can be shorter than this
const TInt KMmsBitsInByte = 8;
const TInt KMmsMaxIntegerLength = 4;
const TInt KMmsMaxLongIntegerLength = 8;
const TUint8 KMms0x80 = 0x80; // 128
const TUint8 KMms0x7F = 0x7F; // 127
const TInt KMms2 = 2; // used to calculate length when converting 8 bit to 16 bit encoding
const TUint8 KMms31 = 31; // uintvar indicator
const TUint8 KMms32 = 32; // low limit for text string encoding
const TUint8 KMms30 = 30; // upper limit for short length encoding
const TInt KMmsUintvarShift = 7; // bit shift for decoding uintvars
const TInt KMms14 = 14; // atta name length for self-generated names
const TInt KMms2k = 2000;
const TInt KMmsTestIllegalValue = 255;
// ==================== 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.
// ---------------------------------------------------------------------------
//
CMmsDecode::CMmsDecode()
    :CMsgActive ( KMmsActiveObjectPriority ),
    iState ( EMmsIdle )
    {
    }

// ---------------------------------------------------------------------------
// Symbian OS default constructor can leave.
// ---------------------------------------------------------------------------
//
void CMmsDecode::ConstructL(RFs& aFs)
    {
    iFs = aFs;
    iMimeHeaders = CMsvMimeHeaders::NewL();
    // This is obsolete. Everything is always logged in debug version
    iLogAllDecoded = ETrue; 
    CActiveScheduler::Add(this);
    }

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

    return self;
    }

    
// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CMmsDecode::~CMmsDecode()
    {
    Cancel();
    delete iMimeHeaders;
    delete iStore; // the store should be committed earlier, not anymore
    delete iRootContentIdBuffer;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsDecode::StartL(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aDecodeBuffer,
    TRequestStatus& aStatus,
    TInt aStartPosition /* = 0 */,
    TInt aLength /* = 0 */ )
    {

    Reset();

    iEntryWrapper = &aEntryWrapper;
    iDecodeBuffer = &aDecodeBuffer;
    iMmsHeaders = &aMmsHeaders;
    iDRMFlags = 0;
    // length will be checked later when we start actual decoding
    iPosition = aStartPosition;
    if ( aLength == 0 )
        {
        iLength = aDecodeBuffer.Size();
        }
    else
        {
        iLength = aLength + aStartPosition;
        }

    iDumpIncoming = iEntryWrapper->GetDumpFlag();

    DumpL();

    Queue(aStatus);
    
    CompleteSelf( iError );

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsDecode::DecodeHeadersL(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aDecodeBuffer,
    TInt aStartPosition /* = 0 */,
    TInt aLength /* = 0 */,
    TInt* aNumberOfAttachments /* = 0 */,
    TInt* aDataStart /* = 0 */ )
    {
    // The initial settings are the same as for asynchronous
    // methods, but decoding just headers is fast enough to
    // be done synchronously

    Reset();

    iEntryWrapper = &aEntryWrapper;
    iDecodeBuffer = &aDecodeBuffer;
    iMmsHeaders = &aMmsHeaders;
    iPosition = aStartPosition;
    iNumberOfAttachments = 0;
    iLastHeader = EFalse;
    
    if ( aLength == 0 )
        {
        iLength = aDecodeBuffer.Size();
        }
    else
        {
        iLength = aLength + aStartPosition;
        }
    if ( aDataStart != 0 )
        {
        // Originally point beyound buffer
        // If data is found, this will be updated
        *aDataStart = iLength;
        }
    if ( aNumberOfAttachments )
        {
        // initialize to no attachments
        *aNumberOfAttachments = 0;
        }

    iMmsHeaders->Reset();

    // Save the time the entry was received into the MMS headers
    // the value in TMsvEntry may change, but this remains
    TTime now;      
    now.UniversalTime();
    iMmsHeaders->SetReceivingTime( now );
        
    iDumpIncoming = iEntryWrapper->GetDumpFlag();
    DumpL();

    if ( iLength < KMmsMinHeaderLength )
        {
        // cannot decode if nothing to decode
        // each header is at least 2 bytes. 
        // If not at least 2 bytes, no can do.
        iState = EMmsIdle;
        iError = KErrCorrupt;
        User::LeaveIfError( iError );
        }

    // each header byte must be followed by at least one byte value.
    // otherwise the message is corrupted
    // when we reach the header that indicates the start of the attachment
    // structure, we expect that no headers follow the attachment part
    // we could handle even headers following the attachments if we
    // wanted to, but if the specification is followed strictly, the 
    // attachments are the last part in the message.

    // each header is at least 2 bytes - header name and value
    while ( ( iPosition <= iLength - KMmsMinHeaderLength ) &&
        ( iError != KErrCorrupt ) &&
        ( !iLastHeader ) )
        {
        DecodeOneHeaderL();
        }
        
    iLength = iDecodeBuffer->Size();
    if ( iPosition >= iLength ||
        ( iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewConf &&
          iMmsHeaders->MultipartType() == 0 ) )
        {
        // Only content type header, but no actual attachments
        iNumberOfAttachments = 0;
        }

    // If the caller is interested, return the number of attachments we found
    if ( aNumberOfAttachments )
        {
        *aNumberOfAttachments = iNumberOfAttachments;
        }

    if ( aDataStart != 0 )
        {
        *aDataStart = iPosition;
        }

    // WE DON'T SAVE THE MESSAGE HERE.
    // THE CALLER CONTROLS THE SAVING OF THE MESSAGE
    // AS WE ONLY DECODE HEADERS, AND DON'T HANDLE
    // ATTACHMENTS HERE
    // THERE MAY BE NO ENTRY AVAILABLE FOR SAVING THE HEADERS.

    // We are done - may be called again...
    iState = EMmsIdle;
    User::LeaveIfError( iError );

    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsDecode::DecodeAttachmentsL(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aDecodeBuffer,
    TInt aNumberOfAttachments,
    TInt& aStartPosition,
    TRequestStatus& aStatus,
    TBool aDoNotUpdateParentEntry /* = EFalse*/,
    TInt aLength /* = 0 */ )
    {

    // The parent entry is updated after specified number of attachments
    // has been handled.
    // Normally all attachments should be handled together.
    // Handling only some attachments is a special case
    
    // We must get an edit store when we are called the first time.
    if ( !iStore )
        {
        iStore = iEntryWrapper->EditStoreL();
        }

    iError = KErrNone; // start with clean slate
    iDataStart = aStartPosition;
    iNextStart = aStartPosition;
    iUpdatedPosition = &aStartPosition;
    iDoNotUpdateParentEntry = aDoNotUpdateParentEntry;

    iEntryWrapper = &aEntryWrapper;
    iMmsHeaders = &aMmsHeaders;
    iDecodeBuffer = &aDecodeBuffer;
    iNumberOfAttachments = aNumberOfAttachments;
    iPosition = aStartPosition;
    if ( aLength == 0 )
        {
        iLength = iDecodeBuffer->Size();
        }
    else
        {
        iLength = aStartPosition + aLength;
        }
    iState = EMmsDecodingAttachments;

    Queue(aStatus);
    
    CompleteSelf( iError );
    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C CMsvMimeHeaders& CMmsDecode::ExtractNextDataPartL(
    CBufFlat& aDecodeBuffer,
    TInt& aStartPosition,
    TInt& aStartOfData,
    TInt& aLengthOfData)
    {
    iMimeHeaders->Reset();
    iDecodeBuffer = &aDecodeBuffer;
    iPosition = aStartPosition;
    iUpdatedPosition = &aStartPosition;
    iLength = iDecodeBuffer->Size();

    TInt headersLength = 0;
    TInt dataLength = 0;
    
    // This function is only used to extract next data part from multipart structure

    headersLength = GetUintvar();
    dataLength = GetUintvar();

    aStartOfData = iPosition + headersLength;
    iDataStart = aStartOfData;
    aLengthOfData = dataLength;
    aStartPosition = iDataStart + dataLength;

    // Extract mime headers
    // iPosition points to the start of headers
    // First is content type without actual header field, just value
    GetAttachmentContentTypeL();

    while ( iPosition < iDataStart )
        {
        DecodeOneContentHeaderL();
        }

    return *iMimeHeaders;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsDecode::CommitStoreL()
    {
    if ( iStore )
        {
        iStore->CommitL();
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
EXPORT_C void CMmsDecode::InitializeChunkedMode(
    MMmsEntryWrapper& aEntryWrapper,
    CMmsHeaders& aMmsHeaders,
    CBufFlat& aDecodeBuffer
    )
    {
    Reset();
    iEntryWrapper = &aEntryWrapper;
    iMmsHeaders = &aMmsHeaders;
    iDecodeBuffer = &aDecodeBuffer;
    iDRMFlags = 0;
    iPosition = 0;
    iLength = 0;
    iRemoveDrm = EFalse;
    iDumpIncoming = iEntryWrapper->GetDumpFlag();
    iDecodingStage = EMmsHeaders;
    
    TRAP_IGNORE(DumpL());
    }
    
// ---------------------------------------------------------
// from class MMmsCodecDataSink
//
// ---------------------------------------------------------
//
TInt CMmsDecode::NextDataPart(
        CBufFlat& aBuffer,
        TInt& aPosition,
        TBool aLastDataChunk )
    {
    if ( iDecodingStage == EMmsNotApplicable )
        {
        aPosition = 0; // if not chunked, nothing handled by data sink -> keep all.
        return KErrNone;
        }
        
    TMsvEntry entry;    
    iEntryWrapper->GetIndexEntry( entry );
    if ( entry.Id() == KMsvNullIndexEntryId )
        {
        // We have no entry, behave as if in non-chunked mode.
        // We are getting a response that will not be saved
        // in a message entry, but may be decoded in its entirety
        // later.
        aPosition = 0; // if not chunked, nothing handled by data sink -> keep all.
        return KErrNone;
        }
        
    iDecodeBuffer = &aBuffer;
    
    iPosition = aPosition;
    // Use iLength to tell how much data must be dumped
    iLength = iDecodeBuffer->Size() - iPosition;
    TRAP_IGNORE(DumpL());
    // Use this to check when end of buffer has been reached
    iLength = iDecodeBuffer->Size();
    
    // We must get an edit store when we are called the first time.
    if ( !iStore && iError == KErrNone )
        {
        TRAP( iError, iStore = iEntryWrapper->EditStoreL() );
        }
    
    if ( iError != KErrNone )
        {
        return iError;
        }
    
    // clear this to make sure it does not point beyond buffer the next time
    iOldData = 0;
    
    TBool moreDataNeeded = EFalse;
    if ( iDecodingStage == EMmsHeaders )
        {
        // Decode headers and tell if there was enough data to do that
        moreDataNeeded = SinkHeaders( aLastDataChunk );
        }
        
    // If we have decoded all headers, we must see if we have enough data left
    // to decode the attachments.    
    
    // If we already ran out of data while decoding the MMS headers
    // we must get more data before even trying the attachments
    
    while ( !moreDataNeeded && !( aLastDataChunk && iPosition == iLength ) &&
        !( iDecodingStage == EMmsDone ) )
        {
        if ( iDecodingStage == EMmsAttachmentHeaders )
            {
            moreDataNeeded = SinkAttachmentHeaders( aLastDataChunk );
            }
            
        if ( iDecodingStage == EMmsAttachmentDataStart )
            {
            SinkAttachmentDataStart();
            }
            
        // If we have a monoblock, we don't know the length - we just write everything we've got    
        if ( iDecodingStage == EMmsAttachmentDataAppend  &&
            ( iAttaDataWritten < iCurrentAttaLength  || iCurrentAttaLength < 0 ) )
            {
            SinkAttachmentData();
            }
            
        if ( iDecodingStage == EMmsAttachmentDataAppend  &&
            ( ( iAttaDataWritten < iCurrentAttaLength ) ||
            ( iCurrentAttaLength < 0 && !aLastDataChunk ) ) )
            {
            moreDataNeeded = ETrue;
            }
            
        if ( ( iDecodingStage == EMmsAttachmentDataAppend &&
            iAttaDataWritten == iCurrentAttaLength ) || aLastDataChunk )
            {
            if ( iCurrentAttaLength > 0 )
                {
                FinishSinkingAttachment( aLastDataChunk );
                }
            }
            
        if ( iError != KErrNone && iError != KMmsErrorRemoveDRM )
            {
            if ( iDecodingStage == EMmsAttachmentDataAppend )
                {
                // We try to clean up.
                // We ignore the error as we have an error already
                TRAP_IGNORE( iEntryWrapper->FinalizeAttachmentL(
                    *iStore, iCurrentAttaLength, iDRMFlags ) );
                }
            // We cannot continue
            iDecodingStage = EMmsDone;
            }
        }

    // clear this in case it was set - it's not valid in chunked mode
    iFakeSubject = 0;
    iTextPlainLength = 0;
    
    if ( aLastDataChunk )
        {
        FinalizeSinkingLastChunk();
        }
        
    // remember how much data was left over
    if ( iPosition > iDecodeBuffer->Size() )
        {
        // this is paranoid, but we make sure that the caller will not panic
        iPosition = iDecodeBuffer->Size();
        }
    iOldData = iDecodeBuffer->Size() - iPosition;
    aPosition = iPosition;
    
    if ( iError != KErrNone )
        {
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("Chunked decode - error %d, deleting store"), iError );
#endif
        delete iStore;
        iStore = NULL;
        }
    
    return iError;
    }
    
// ---------------------------------------------------------
// from class MMmsCodecDataSink
//
// ---------------------------------------------------------
//
void CMmsDecode::RelaseDataSink()
    {
    // store must be released even in case of error
    delete iStore;
    iStore = NULL;
    }

// ---------------------------------------------------------
// from class MMmsCodecDataSink
//
// ---------------------------------------------------------
//
void CMmsDecode::ResetDataSink()
    {
    if ( iDecodingStage == EMmsNotApplicable )
        {
        // not chunked mode - do nothing
        return;
        }
        
    TMsvEntry entry;    
    if ( iEntryWrapper->GetIndexEntry( entry ) == KErrNone && 
         entry.Id() != KMsvNullIndexEntryId )
        {
        if ( !iStore )
            {
            TRAP_IGNORE(iStore = iEntryWrapper->EditStoreL());
            }
        // This should fail only if we run out of memory.
        // In that case the whole message entry will be deleted later,
        // so it does not make sense to return an error here
        // We just do our best and let the rest of the code to continue
        TRAP_IGNORE({
            MMsvAttachmentManager& attaMan = iStore->AttachmentManagerL();
            MMsvAttachmentManagerSync& attaManSync = iStore->AttachmentManagerExtensionsL();
            TInt count = attaMan.AttachmentCount();
            TInt i;
            // delete all old attachments as we are starting from the beginning
            for ( i = count - 1; i >= 0; i-- )
               {
               attaManSync.RemoveAttachmentL( i );
               }
            iStore->CommitL();   
            });
        }
    
    Reset();
    iMmsHeaders->Reset();
    iDRMFlags = 0;
    iPosition = 0;
    iLength = 0;
    iDumpIncoming = iEntryWrapper->GetDumpFlag();
    iDecodingStage = EMmsHeaders;
    if ( entry.Id() == KMsvNullIndexEntryId )
        {
        // no entry - cannot use chunked mode.
        iDecodingStage = EMmsNotApplicable;
        }
    else    
        {
         TRAP_IGNORE(DumpL());
        }
    }

// ---------------------------------------------------------
// From class CMsgActive
//
// ---------------------------------------------------------
//
void CMmsDecode::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;
        iState = EMmsIdle;
        // If we return from DoRunL without becoming active again,
        // RunL completes.
        return;
        }

    SelectNextState();

    if ( iState != EMmsFinished )
        {
        // If appropriate, ChangeStateL makes us active again
        ChangeStateL();
        // If we return from DoRunL without becoming active again,
        // RunL completes.
        }
    else
        {
        iUpdatedPosition = NULL; // no longer needed
        if ( !iDoNotUpdateParentEntry )
            {
            FinishL();
            }
        else
            {
            // store must be released in any case
            CommitStoreL();
            delete iStore;
            iStore = NULL;
            }
        iDoNotUpdateParentEntry = EFalse; // default value
        iState = EMmsIdle;
        iStatus = iError;
        // If we return from DoRunL without becoming active again,
        // RunL completes.
        }

    }

// ---------------------------------------------------------
// From class CMsgActive
//
// ---------------------------------------------------------
//
void CMmsDecode::DoComplete( TInt& /* aStatus */ )
    {
    // We are exiting the loop - we say we are idle now
    iState = EMmsIdle;
    // store must be released in any case - if it is NULL already, nothing happens
    // Can't leave here anymore
    TInt error;
    TRAP( error, CommitStoreL() );
    delete iStore;
    iStore = NULL;
    if ( iError == KErrNone )
        {
        iError = error;
        }
    }

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

    // If appropiate, 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 = EMmsDecodingHeaders;
            break;
        case EMmsDecodingHeaders:
            if ( iNumberOfAttachments > 0 )
                {
                iState = EMmsDecodingAttachments;
                }
            else
                {
                iState = EMmsFinished;
                }
            break;
        case EMmsDecodingAttachments:
            if ( iNumberOfAttachments > 0 )
                {
                iState = EMmsDecodingAttachments;
                }
            else
                {
                iState = EMmsFinished;
                }
            break;
        case EMmsFinished:
            // No more states
            break;
        default:
            // Illegal state
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("Decode - illegal state %d"), iState );
#endif
            break;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::ChangeStateL()
    {
    switch ( iState )
        {
        case EMmsDecodingHeaders:
            DecodeHeadersL();
            break;
        case EMmsDecodingAttachments:
            if ( iNumberOfAttachments > KMmsMaxAttachments )
                {
                // If we have too many attachments, we cannot
                // handle the message. This is a temporary
                // error code that takes care of denying retries
                iError = KMmsErrorEMRUExceeded;
                CompleteSelf( iError );
                return;
                }
            DecodeOneAttachmentL();
            iNumberOfAttachments--;
            if ( iUpdatedPosition )
                {
                // The user wants to know where to continue
                *iUpdatedPosition = iPosition;
                }
            break;
        default:
            iState = EMmsIdle;
            break;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::Reset()
    {
    iError = KErrNone; // start with clean slate
    iMimeHeaders->Reset();
    delete iStore;
    iStore = NULL; // this is a new message, cannot use old store
    iUpdatedPosition = NULL;
    iPosition = 0;
    iDataStart = 0;
    iNextStart = 0;
    iNumberOfAttachments = 0;
    iLastHeader = EFalse;
    iTotalSize = 0;
    iFakeSubject = 0;
    iCharacterSet = KMmsUsAscii; // if no character set, default is US_ASCII
    iTextPlainLength = 0;
    iMultipartType = 0;
    iRootContentId.Set( TPtrC8() );
    delete iRootContentIdBuffer;
    iRootContentIdBuffer = NULL;
    iRootAttachmentId = 0;
    iFirstAttachmentId = 0;
    iFirstTextPlain = 0;
    iUseForSubject = EFalse;
    iAttaNumber = 0;
    iSmilCount = 0;
    iAudioCount = 0;
    iPlainTexts = 0;
    iDoNotUpdateParentEntry = EFalse;
    iDecodingStage = EMmsNotApplicable;
    iOldData = 0;
    iLogAllDecoded = ETrue;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
TBool CMmsDecode::SinkHeaders( TBool aLastDataChunk )
    {
    TBool enoughData = ETrue;
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("Chunked decode - MMS headers"));
#endif
    TInt error = KErrNone;
    // See if we have a whole header
    // We must have enough data to contain both the header and its value.
    // Otherwise we must ask for more data.
    TRAP ( error,
        {
        enoughData = CheckHeaderLength();
        while ( enoughData &&
            ( iError != KErrCorrupt ) &&
            ( !iLastHeader ) )
            {
            DecodeOneHeaderL();
            if ( ( iError != KErrCorrupt ) &&
                ( !iLastHeader ) )
                {
                enoughData = CheckHeaderLength();
                }
            }
        });
        
    if ( iError == KErrNone )
        {
        // we should get an error in decoding only if memory runs out
        // or the message is corrupted
        iError = error;
        }
    // check if we reached the end of the headers and can continue
    // with attachments
    if ( iError == KErrNone )
        {
        if ( iLastHeader )
            {
            // Headers decoded successfully, can continue with attachments
            // The uintvar specifying then number of attachments was decoded
            // at the end of the MMS headers in connection with multipart header.
            // This leaves only if out of disk space or out of memory
            TRAP( iError, SaveMMSHeadersL() );
            iDecodingStage = EMmsAttachmentHeaders;
            }
        else if ( aLastDataChunk && iPosition >= iLength )
            {
            // Headers decoded successfully, but no attachment present
            // This is an empty message
            TRAP( iError, SaveMMSHeadersL() );
            iDecodingStage = EMmsAttachmentHeaders;
            }
        else if ( aLastDataChunk && !enoughData )
            {
            // we have got something that cannot be decoded
            iError = KErrCorrupt;
            iDecodingStage = EMmsDone;
            }
        else 
            {
            // Do nothing - keep LINT happy
            }
        }
    // return ETrue if more data is needed.    
    return !enoughData;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
TBool CMmsDecode::SinkAttachmentHeaders( TBool aLastDataChunk )
    {
    TBool moreDataNeeded = EFalse;
    TInt error = KErrNone;
    
    // We try to get all attachment headers at one go
    // It is easier than decoding the attachment headers one by one
    // As we have headers that tell the total length of attachment
    // headers we can ask for more data if needed.
    // The attachment headers are not normally very long, and if they
    // are, the message is probably corrupted anyway
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Chunked decode - Attachment headers"));
#endif
    iAttaDataWritten = 0; // no data yet    
    iCurrentAttaLength = 0;
    TInt currentPosition = iPosition;
    TInt uintvarStart = iPosition;
    iRemoveDrm = EFalse; // no DRM so far
    if ( iMultipartType && CheckUintvarLength() )
        {
        // restore position to start of Uintvar
        iPosition = uintvarStart;
        TUint attaHeaderLength = GetUintvar();
        // got atta header length. Do we still have more data.
        uintvarStart = iPosition;
        if ( CheckUintvarLength() )
            {
            iPosition = uintvarStart;
            iCurrentAttaLength = GetUintvar();
            // attaHeaderLength is the length of content type header and other
            // headers combined.
            // Do we still have that much data.
            if ( iLength - iPosition >= attaHeaderLength )
                {
                // decode headers
                iPosition = currentPosition;
                TRAP( error, DecodeAttachmentHeadersL() );
                if ( iError == KErrNone )
                    {
                    iError = error;
                    }
                if ( iCurrentAttaLength == 0 )
                    {
                    // if the attachment is empty, we do not create an attachment
                    // we should check here if there is any data left
                    iDecodingStage = EMmsAttachmentHeaders;
                    }
                else if ( iError == KErrNone )
                    {
                    iDecodingStage = EMmsAttachmentDataStart;
                    }
                else
                    {
                    // do nothing - keep LINT happy
                    }
                }
            else     
                {
                // not enough data for decoding headers - give up
                // and ask for more data
                iPosition = currentPosition;
                moreDataNeeded = ETrue;
#ifndef _NO_MMSS_LOGGING_
                TMmsLogger::Log( _L("Chunked decode - need more data for atta headers"));
#endif
                }
            }
        else
            {
            // not enough data for determining attachment data length - give up
            // and ask for more data
            iPosition = currentPosition;
            moreDataNeeded = ETrue;
            }
        }
    else if ( iMultipartType == 0 && !( iLength - iPosition < KMms2k && !aLastDataChunk ) )
        {
        // One chunk, no multipart, very bad, try anyway
        // We can only guess how much of the data is headers.
        TRAP( error, DecodeAttachmentHeadersL() );
        if ( iError == KErrNone )
            {
            iError = error;
            }
        iCurrentAttaLength = -1; // Don't know
        if ( !( aLastDataChunk && iPosition == iLength ) && iError == KErrNone )
            {
            // There is some data
            iDecodingStage = EMmsAttachmentDataStart;
            }
        else
            {
            iCurrentAttaLength = 0; // no data
            }
        }
    else
        {
        // not enough data for determining attachment header length - give up
        // and ask for more data
        iPosition = currentPosition;
        moreDataNeeded = ETrue;
        }

    return moreDataNeeded;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::SinkAttachmentDataStart()
    {
    TInt error = KErrNone;
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Chunked decode - Attachment data start"));
#endif
    // Now the attachment file must be created.
    // We should also have enough data
    
    // First an empty attachment is created. No data is needed for it.
    TRAP ( error, iEntryWrapper->CreateEmptyFileAttachmentL(
            *iStore,
            iParse.NameAndExt(),
            *iMimeHeaders,
            iCurrentAttachment )
        );
        
    if ( iError == KErrNone && error != KErrNone )
        {
        iError = error;
        }
    if ( iError == KErrNone )
        {
        if ( iUseForSubject )
            {
            iFirstTextPlain = iCurrentAttachment;
            iUseForSubject = EFalse;
            }

#ifndef _NO_MMSS_LOGGING_
        TRAP_IGNORE({
            MMsvAttachmentManager& attaMan = iStore->AttachmentManagerL();
            CMsvAttachment* attaInfo = attaMan.GetAttachmentInfoL( iCurrentAttachment );
            TMmsLogger::Log( _L("- Attachment created %S"), &attaInfo->FilePath() );
            delete attaInfo;
            attaInfo = NULL;
            });
#endif
        }
    if ( iError == KErrNone )
        {
        iDecodingStage = EMmsAttachmentDataAppend;
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::SinkAttachmentData()
    {
    TInt error = KErrNone;
    // no problem with signed/unsigned comparison - size cannot be signed
    if ( iPosition < iDecodeBuffer->Size() )
        {
        TPtr8 attaDataPtr = iDecodeBuffer->Ptr( iPosition );
        if ( attaDataPtr.Length() > iCurrentAttaLength - iAttaDataWritten &&
            iCurrentAttaLength > 0 )
            {
            // If we don't know the amount of attachment data, we write the
            // whole buffer
            attaDataPtr.SetLength( iCurrentAttaLength - iAttaDataWritten );
            }
        if ( iError == KMmsErrorRemoveDRM )
            {
            iRemoveDrm = ETrue;
            iError = KErrNone;
            }
        if ( !iRemoveDrm )
            {
            // we have data that can be written to the file
            TRAP( error,
                iError = iEntryWrapper->ProcessAttachmentDataL( attaDataPtr, iDRMFlags )
                );
            if ( iError == KErrNone )
                {
                iError = error;
                }
            if ( iError == KMmsErrorRemoveDRM )
                {
                iRemoveDrm = ETrue;
                iError = KErrNone;
                }
            }
        // if attachment is DRM protected and must be removed, just increase pointers
        iPosition += attaDataPtr.Length();
        iAttaDataWritten += attaDataPtr.Length();
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("Chunked decode - Attachment data %d, cumulative %d"),
        attaDataPtr.Length(), iAttaDataWritten );
#endif
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::FinishSinkingAttachment( TBool aLastDataChunk )
    {
    TInt error = KErrNone;
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Chunked decode - Attachment ready"));
#endif
    // end of attachment - close file
    // This must be called even if the error indicates the DRM file must be removed
    // This call does all cleanup and actually removes the DRM attachment if needed.
    TRAPD( error2,
        error = iEntryWrapper->FinalizeAttachmentL( *iStore, iCurrentAttaLength, iDRMFlags )
        );
    if ( iError == KErrNone )
        {
        iError = error;
        }
    if ( iError == KErrNone )
        {
        iError = error2;
        }
    // If DRM handling changed file size, iCurrentAttaLength gets adjusted
    if ( iError == KMmsErrorRemoveDRM )
        {
        // if error == KMmsErrorRemoveDRM all is well, and we can continue
        // but we don't have an attachment
        iError = KErrNone;
        // DRM already removed - flag can be cleared now
        iRemoveDrm = EFalse;
        }
    else if ( iError == KErrNone )
        {
        iTotalSize += iMimeHeaders->Size() + iCurrentAttaLength;
        if ( ( iMultipartType == KMmsAssignedApplicationVndWapMultipartRelated ) &&
            ( iRootContentId.Length() > 0 ) &&
            ( iRootAttachmentId == 0 ) )
            {
            // check if content-id's match
            if ( iRootContentId.Compare( iMimeHeaders->ContentId() ) == 0 )
                {
                iRootAttachmentId = iCurrentAttachment;
                }
            }
        }
    else
        {
        // do nothing - keep LINT happy
        }
    // no problem with signed/unsigned    
    if ( !( aLastDataChunk && iPosition == iLength ) &&
        ( iAttaNumber < iNumberOfAttachments ) )
        {
        iDecodingStage = EMmsAttachmentHeaders;
        }
    else
        {
        iDecodingStage = EMmsDone;
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::FinalizeSinkingLastChunk()
    {
    TInt error = KErrNone;
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("Chunked decode - last chunk"));
#endif
    // If we have reached the last chunk, all data should be handled by now
    iDecodingStage = EMmsDone;
    
    // Read possible data from plain text file into encode buffer
    // and set iFake subject to point to the buffer
    // Must start from 1 because data would never start from 0
    // and value 0 indicates no fake subject available.

    // Fake subject handling        
    // Our store should still be valid.
    // The fake subject handling and multipart/report handling are "best effort"
    // Even if the operations fail, the code continues as if no error was encountered.
    TInt oldSize = iDecodeBuffer->Size();
    TRAP_IGNORE(
        {
        // if we have no subject but we do have only one text attachment
        // we take text from the beginning as subject
        // 
        if ( iFirstTextPlain && iPlainTexts == 1 && iMmsHeaders->Subject().Length() == 0 )
            {
            MMsvAttachmentManager& attaMan = iStore->AttachmentManagerL();
            RFile textAtta = attaMan.GetAttachmentFileL( iFirstTextPlain );
            CleanupClosePushL( textAtta );
            iDecodeBuffer->ResizeL( KMmsMaxDescription + 1 );
            // We must start from any position > 0
            TPtr8 pointer = iDecodeBuffer->Ptr( 1 );
            // read text into buffer
            error = textAtta.Read( pointer ); 
            if ( error == KErrNone )
                {
                iFakeSubject = 1;
                iTextPlainLength = pointer.Length();
                }
            CleanupStack::PopAndDestroy( &textAtta ); // textAtta file handle closed
            }
            
        // Multipart/report handling
        if ( iMultipartType == KMmsAssignedApplicationVndWapMultipartReport )
            {
            // we only keep the first part from multipart/report
            MMsvAttachmentManager& attaMan = iStore->AttachmentManagerL();
            MMsvAttachmentManagerSync& attaManSync = iStore->AttachmentManagerExtensionsL();
            TInt count = attaMan.AttachmentCount();
            TInt i;
            for ( i = count - 1; i > 0; i-- )
                {
                attaManSync.RemoveAttachmentL( i );
                }
            }
        });
    
    error = KErrNone;
    iLength = iDecodeBuffer->Size();
    TRAP( error, FinishL() );
    TRAP_IGNORE( CommitStoreL() );
    delete iStore;
    iStore = NULL;
    if ( iError == KErrNone )
        {
        iError = error;
        }
    // Restore the original buffer size
    // This should actually never leave because we are just  restoring the original size
    TRAP_IGNORE( iDecodeBuffer->ResizeL( oldSize ));
    iLength = iDecodeBuffer->Size();
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeHeadersL()
    {

    if ( iLength < KMmsMinHeaderLength )
        {
        // cannot decode if nothing to decode
        // each header is at least 2 bytes. 
        // If not at least 2 bytes, no can do.
        iError = KErrCorrupt;
        }

    iMmsHeaders->Reset();
    
    // Save the time the entry was received into the MMS headers
    // the value in TMsvEntry may change, but this remains
    TTime now;      
    now.UniversalTime();
    iMmsHeaders->SetReceivingTime( now );

    // each header byte must be followed by at least one byte value.
    // otherwise the message is corrupted
    // when we reach the header that indicates the start of the attachment
    // structure, we expect that no headers follow the attachment part.
    
    // each header is at least 2 bytes - header name and value
    while ( ( iPosition <= iLength - KMmsMinHeaderLength ) &&
        ( iError != KErrCorrupt ) &&
        ( !iLastHeader ) )
        {
        DecodeOneHeaderL();
        }
      
    SaveMMSHeadersL();    

    TRequestStatus* status = &iStatus;
    *status = KRequestPending;
    SetActive();

    // Now we indicate we already did everything.
    User::RequestComplete( status, iError );

    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::SaveMMSHeadersL()
    {
    
    // We save the headers only in case we are decoding an incoming
    // message. In other cases we deliver the results back to the caller
    // in iMmsHeaders.
    // Only retrieve confirmation is supposed to contain body that
    // needs saving of attachments.

    // We save a message of type KMmsMessageTypeMSendReq as it were an
    // incoming message, as during tests we get our own
    // send requests back as new messages.
    // We also save notifications and delivery reports.
    // However, in normal case MMS server calls a different function
    // (CMmsDecode::DecodeHeadersL) to decode notifications and delivery
    // reports as they do not contain attachments, and delivery reports
    // are not saved on disk, but are logged and deleted.
    // For testing purposes upload request is saved as an incoming message

    if ( ( iMmsHeaders->MessageType() == KMmsMessageTypeMRetrieveConf ||
        iMmsHeaders->MessageType() == KMmsMessageTypeMSendReq ||
        iMmsHeaders->MessageType() == KMmsMessageTypeMNotificationInd ||
        iMmsHeaders->MessageType() == KMmsMessageTypeDeliveryInd ||
        iMmsHeaders->MessageType() == KMmsMessageTypeMBoxUploadReq ) &&
        iError == KErrNone )
        {
        // The entry wrapper must be pointing to our message entry
        if ( !iStore )
            {
            iStore = iEntryWrapper->EditStoreL();
            }
        iError = iEntryWrapper->DiskSpaceBelowCriticalLevelL( iMmsHeaders->Size() );
        if ( iError == KErrNone )
            {
            iMmsHeaders->StoreL( *iStore );
            iStore->CommitL();
            }
        // the store will be left open until all the attachments have been saved
        }

    // Afterwards we must calculate the total message size and
    // store it in the entry.
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeOneHeaderL()
    {

    TInt header;
    header = GetHeaderName();

    if ( header == -1 )
        {
        return; // skip unknown
        }

    // Get the corresponding value
    
    switch ( header )
        {
        case KMmsAssignedFrom:
            DecodeFromHeaderL();
            break;
        case KMmsAssignedTo:
            DecodeToL();
            break;
        case KMmsAssignedCc:
            DecodeCcL();
            break;
        case KMmsAssignedBcc:
            DecodeBccL();
            break;
        case KMmsAssignedContentLocation:
            DecodeContentLocationHeaderL();
            break;
        case KMmsAssignedDate:
            DecodeDateHeaderL();
            break;
        case KMmsAssignedDeliveryReport:
            DecodeDeliveryReportHeader();
            break;
        case KMmsAssignedDeliveryTime:
            DecodeDeliveryTimeL();
            break;
        case KMmsAssignedExpiry:
            DecodeExpiryL();
            break;
        case KMmsAssignedMessageClass:
            DecodeMessageClass();
            break;
        case KMmsAssignedMessageId:
            DecodeMessageIdL();
            break;
        case KMmsAssignedMessageType:
            DecodeMessageType();
            break;
        case KMmsAssignedMmsVersion:
            DecodeMmsVersion();
            break;
        case KMmsAssignedMessageSize:
            DecodeMessageSize();
            break;
        case KMmsAssignedPriority:
            DecodePriority();
            break;
        case KMmsAssignedReadReply:
            DecodeReadReply();
            break;
        case KMmsAssignedReportAllowed:
            DecodeReportAllowed();
            break;
        case KMmsAssignedResponseStatus:
        case KMmsAssignedRetrieveStatus:
            DecodeResponseStatusL( header );
            break;
        case KMmsAssignedResponseText:
        case KMmsAssignedRetrieveText:
        case KMmsAssignedStatusText:
            DecodeResponseTextL( header );
            break;
        case KMmsAssignedSenderVisibility:
            DecodeSenderVisibility();
            break;
        case KMmsAssignedStatus:
            DecodeStatus();
            break;
        case KMmsAssignedSubject:
            DecodeSubjectL();
            break;
        case KMmsAssignedTID:
            DecodeTidL();
            break;
        case KMmsAssignedContentType:
            // Content type is the last header in the PDU
            // Number of attachments and last header indicator will be set 
            DecodeContentTypeL();
            break;
        case KMmsAssignedReadStatus:
            DecodeReadStatus();
            break;
        case KMmsAssignedReplyCharging:
            DecodeReplyCharging();
            break;
        case KMmsAssignedReplyChargingDeadline:
            DecodeReplyChargingDeadlineL();
            break;
        case KMmsAssignedReplyChargingID:
            DecodeReplyChargingIdL();
            break;
        case KMmsAssignedReplyChargingSize:
            DecodeReplyChargingSize();
            break;
        case KMmsAssignedPreviouslySentBy:
            DecodePreviousSenderL();
            break;
        case KMmsAssignedPreviouslySentDate:
            DecodePreviouslySentDateL();
            break;
        case KMmsAssignedMmsStore:
            DecodeStoreHeaderL();
            break;
        case KMmsAssignedMMState:
            DecodeMMBoxStateL();
            break;
        case KMmsAssignedMMFlags:
            GetKeywordL();
            break;
        case KMmsAssignedStoreStatus:
            DecodeMMBoxStoreStatusL();
            break;
        case KMmsAssignedStoreStatusText:
            DecodeMMBoxStoreStatusTextL();
            break;
        case KMmsAssignedStored:
            DecodeStoredInMMBoxHeaderL();
            break;
        case KMmsAssignedAttributes:
            DecodeAttributesHeaderL();
            break;
        case KMmsAssignedTotals:
            DecodeTotalsL();
            break;
        case KMmsAssignedMboxTotals:
            DecodeMboxTotalsL();
            break;
        case KMmsAssignedQuotas:
            DecodeQuotaHeaderL();
            break;
        case KMmsAssignedMboxQuotas:
            DecodeMBoxQuotasL();
            break;
        case KMmsAssignedMessageCount:
            DecodeMessageCountL();
            break;
        case KMmsAssignedStart:
            DecodeStartInMMBoxViewL();
            break;
        case KMmsAssignedDistributionIndicator:
            DecodeDistributionIndicator();
            break;
        case KMmsAssignedElementDescriptor:
            DecodeElementDescriptorL();
            break;
        case KMmsAssignedLimit:
            DecodeMessageLimitL();
            break;
        case KMmsAssignedExtendedNotificationText:
            DecodeExtNotifTextL();
            break;
        case KMmsAssignedExtendedNotificationEOL:
            DecodeExtNotifEolL();
            break;
        case KMmsAssignedContentClass:
            DecodeContentClass();
            break;
        case KMmsAssignedDrmContent:
            DecodeDrmContentHeader();
            break;
        case KMmsAssignedAdaptationAllowed:
            DecodeAdaptationAllowed();
            break;
        case KMmsAssignedApplicId:
            DecodeApplicationIdL();
            break;
        case KMmsAssignedReplyApplicId:
            DecodeReplyApplicationIdL();
            break;
        case KMmsAssignedAuxApplicInfo:
            DecodeApplicationInfoL();
            break;
        case KMmsAssignedRecommendedRetrievalMode:
            DecodeRecommendedRetrievalMode();
            break;
        case KMmsAssignedRecommendedRetrievalModeText:
            DecodeRecommendedRetrievalModeTextL();
            break;
        case KMmsAssignedReplaceId:
        case KMmsAssignedCancelId:
            DecodeCancelReplaceIdL( header );
            break;
        case KMmsAssignedCancelStatus:
            DecodeCancelStatus();
            break;
        default:
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- Unsupported header : 0x%02X"), (header & KMms0x7F) );
#endif
            // This is an error.
            // This is an unknown header, just ignore it.
            SkipFieldValue();
            break;
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsDecode::GetHeaderName()
    {

    TInt header;
    header = -1; // unknown

    TUint8 byte;

    if ( iPosition > iLength - KMmsMinHeaderLength )
        {
        iError = KErrCorrupt;
        return header;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);

    // byte cannot be bigger than 255. No need to test upper limit
    if ( byte >= KMms0x80 )
        {
        // well known header, advance pointer
        iPosition++;
        header = byte;
        return header;
        }

    // we cannot handle headers which are not well known.
    // however, we should be able to skip them and ignore them.
    // If a field name has no encoding, it must be encoded as text.

    // If the header is corrupted, the value may be something
    // illegal. We should have a mechanism of skipping
    // illegal codings, too, but if the header name is not a
    // short integer or a text string, we are so deep in trouble
    // that we cannot decode the whole message anyway.

    // We handle some known text headers. Here we check if possible
    // text header is one we know. If not, it is skipped.

    TPtrC8 byteString = GetByteString();

    // see if this is in our table
    TInt i = 0;
    TBool matchFound = EFalse;
    TPtrC8 matchTablePointer;
    matchTablePointer.Set( KMmsExtensionHeaderLookup[i].extensionHeader );
    while ( matchTablePointer.Length() > 0 && !matchFound )
        {
        if ( matchTablePointer.CompareF( byteString ) == 0 )
            {
            header = *KMmsExtensionHeaderLookup[i].assignedValue;
            matchFound = ETrue;
            }
        i++;
        matchTablePointer.Set( KMmsExtensionHeaderLookup[i].extensionHeader );
        }

#ifndef _NO_MMSS_LOGGING_
    // we do not leave because of logging.
    TInt error = KErrNone;
    TRAP( error,
        {
        // length - terminating zero
        HBufC16* buffer = NULL;
        buffer = HBufC16::NewLC( byteString.Length() );
        buffer->Des().Copy( byteString );
        TPtrC dummy;
        // we cannot log indefinitely long strings.
        // We get this long strings only if the message is corrupted.
        dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
        TMmsLogger::Log( _L("- Text header : %S"), &dummy );
        CleanupStack::PopAndDestroy( buffer );
        });
    if ( error != KErrNone )
        {
        TMmsLogger::Log( _L("- Decode left when reading Text header, error: %d"), error );
        }
#endif

    if ( matchFound )
        {
        return header;
        }

    // If we could interpret text headers, it should be done here
    // For now we just skip the field value without trying to interpret it.
    // As we cannot interpret the header, we must skip the value.
    // If a text header match was found in our table, we can interpret the header
    // and we leave the value intact

    if ( iPosition < iLength && header == -1 )
        {
        SkipFieldValue();
        }
    else
        {
        iError = KErrCorrupt;
        }

    return header;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
/*
// not used any more
void CMmsDecode::SkipTextString()
    {

    TUint8 byte;

    iDecodeBuffer->Read(iPosition, &byte, 1);
    iPosition++;

    // don't read beyond buffer
    while ( byte != 0 && iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        }

    return;
    }
*/        

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::SkipFieldValue()
    {

    TUint length;
    length = GetValueLength();
#ifndef _NO_MMSS_LOGGING_
    // This is an error situation, log always if logging allowed
    TMmsLogger::Log( _L("- Skipped field value: %d bytes"), length);
#endif

#ifndef _NO_MMSS_LOGGING_
    // this may produce a long log - but only if the message is weird
    TUint i;
    TUint8 byte;
    TBuf<1> character;
    for ( i = iPosition; i < iPosition + length && i < iLength; i++ )
        {
        iDecodeBuffer->Read(i, &byte, 1);
        TUint16 word = byte;
        if ( byte >= KMms0x20 && byte < KMms0x7F )
            {
            character.Copy( &word, 1 );
            TMmsLogger::Log( _L("- 0x%02X %S"), byte, &character );
            }
        else
            {
            TMmsLogger::Log( _L("- 0x%02X"), byte );
            }
        }
#endif
    iPosition += length;
// if iPosition == iLength, this was the last header, and we are not necessaily corrupted
// If the PDU contains only headers, and the last one is a text header whose value will be
// skipped, we are all right if iPosition == iLength.
    if ( iPosition > iLength )
        {
        iError = KErrCorrupt;
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::SkipParameterName()
    {

    TUint8 byte;

    if ( iPosition >= iLength )
        {
        return;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);


    // byte cannot be bigger than 255. No need to test upper limit
    if ( byte >= KMms0x80 )
        {
        // well known header, advance pointer and remove high bit
        iPosition++;
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Skipped parameter: 0x%02X"), byte);
#endif
        }
    else
        {
        TPtrC8 byteString = GetByteString();
#ifndef _NO_MMSS_LOGGING_
        // we don't leave because of logging
        TInt error = KErrNone;
        TRAP( error, 
            {
            HBufC16* buffer = NULL;
            buffer = HBufC16::NewLC( byteString.Length() );
            buffer->Des().Copy( byteString );
            TPtrC dummy;
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Skipped parameter : %S"), &dummy ); 
            CleanupStack::PopAndDestroy( buffer );
            });
        if ( error != KErrNone )
            {
            TMmsLogger::Log( _L("- Decode left when logging skipped parameter, error : %d"),
                error );
            }
#endif
        }

    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TUint32 CMmsDecode::GetUintvar()
    {
    
    TUint32 uintvar;
    uintvar = 0;
    TUint8 byte;

    if ( iPosition >= iLength )
        {
        return uintvar;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);
    
    while ( byte & KMms0x80  && iPosition < iLength - 1 )
        {
        uintvar += ( byte & KMms0x7F );
        uintvar <<= KMmsUintvarShift;
        iPosition++;
        iDecodeBuffer->Read(iPosition, &byte, 1);
        }

    // add last byte without shift
    uintvar += byte;
    iPosition++;
    
    // If the file is corrupted, the length may be so big it becomes negative
    // We must make sure the value is less than maximum signed 32-bit integer.
    // 
    if ( uintvar > TUint32( KMaxTInt32 ) )
        {
        uintvar = TUint32( KMaxTInt32 );
        }

    return uintvar;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
HBufC16* CMmsDecode::DecodeFromL()
    {

    TUint8 byte = 0;
    TUint length = 0;

    TUint start;
    start = iPosition;
    length = GetValueLength();

    // Get next token to see, if we have the address present token
    if ( iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        }

    if ( byte > KMms0x7F )
        {
        // an encoded token
        // It must be address present or be insert address
        iPosition++;
        if ( iPosition >= iLength )
            {
            iError = KErrCorrupt;
            return HBufC16::NewL( 0 );
            }

        // These two are the only two valid ones.
        if ( byte != KMmsAddressPresentToken && byte != KMmsInsertAddressToken)
            {
            // this encoding is a mess.
            // try if it resolves as an encoded string without 
            // these tokens
            iPosition = start;
            }
        }

    if ( length > 1 )
        {
        return DecodeAddressL();
        }
    else
        {
        return HBufC16::NewL( 0 );
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
HBufC16* CMmsDecode::DecodeAddressL()
    {
       
    HBufC16* buffer = NULL; // will be allocated by called subroutine

    if ( iPosition >= iLength )
        {
        return HBufC16::NewL( 0 );
        }
        
    // always use GetEncodedTextStringL(), it handles simple text strings, too

    buffer = GetEncodedTextStringL();

    // If our address contains an appended type specification,
    // it must be removed

    TInt position;
    TPtr temp = buffer->Des();
    _LIT ( KDot, "." );
    
    position = buffer->FindF( KMmsPlmnu().Ptr(), KMmsPlmnLength );
    if ( position != KErrNotFound )
        {
        temp.SetLength( position );
        // remove dots from phone number in order to keep
        // plmn number and ipv4 number different.
        position = temp.Find( KDot );
        while ( position >= 0 )
            {
            temp.Delete( position, 1 );
            position = temp.Find( KDot );
            }
        }
    else
        {
        position = buffer->FindF( KMmsIpv4u().Ptr(), KMmsIpv4Length );
        if ( position != KErrNotFound )
            {
            temp.SetLength( position );
            }
        else
            {
            position = buffer->FindF( KMmsIpv6u().Ptr(), KMmsIpv4Length ); 
            if ( position != KErrNotFound )
                {
                temp.SetLength( position );
                }
            }
        }

    return buffer;
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
HBufC16* CMmsDecode::GetSimpleTextStringL( TBool aKeepQuote /*= EFalse*/  )
    {
    // we don't handle quoted strings.
    // they should be present only in application headers, and 
    // we don't handle those

    TUint8 byte;
    HBufC16* buffer = NULL; // we must know length before trying to allocate
    TUint stringLength;
    TChar character;

    if ( iPosition >= iLength )
        {
        return HBufC16::NewL(0);
        }
    iDecodeBuffer->Read( iPosition, &byte, 1 );

    if ( byte == 0 )
        {
        // empty string.
        iPosition++;
        return HBufC16::NewL(0);
        }

    if ( byte == KMmsQuote )
        {
        // skip quote
        iPosition++;
        if ( iPosition >= iLength )
            {
            return HBufC16::NewL(1);
            }
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        }

    TBool foundQuote = EFalse;

    if ( byte == KMmsStringQuote && !aKeepQuote )
        {
        // skip quote
        iPosition++;
        foundQuote = ETrue;
        if ( iPosition >= iLength )
            {
            return HBufC16::NewL(0);
            }
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        }

    stringLength = iPosition;

    while ( byte != 0 && stringLength < iLength )
        {
        iDecodeBuffer->Read( stringLength, &byte, 1 );
        stringLength++;
        }

    // The diffrence of pointers is the actual string length
    // including the terminating zero.
    stringLength -= iPosition;

    if ( iPosition >= iLength )
        {
        return HBufC16::NewL(0);
        }

    iDecodeBuffer->Read( iPosition, &byte, 1 );
    if ( byte == 0 )
        {
        // Empty string. Only terminating zero.
        // Must point past the terminating zero at exit
        iPosition++;
        }

    // Allocate buffer for unicode.
    // Check if this is too long,
    // (allocating just stringLength left or panicked...)
    buffer = HBufC16::NewL( stringLength*KMms2 + KMms2 );

    while ( byte != 0 && iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        if ( byte != 0 )
            {
            character = byte;
            buffer->Des().Append( character );
            }
        iPosition++;
        }

    if ( foundQuote && buffer->Des().Right( 1 ).Compare( KQuote16 ) == 0 )
        {
        // if we removed the leading quote, we remove the trailing quote, too.
        buffer->Des().Delete( buffer->Des().Length() - 1, 1 );
        }
    return buffer;
        
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
HBufC16* CMmsDecode::GetEncodedTextStringL()
    {

    TUint8 byte;
    HBufC16* buffer = NULL; // we must know length before trying to allocate
    TUint stringLength = 0;
    TUint32 charset = 0;
    TUint start;

    if (iPosition >= iLength )
        {
        iError = KErrCorrupt;
        buffer = HBufC16::NewL(0);
        return buffer;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);

    if ( ( byte > KMms31 && byte < KMms0x80 ) || byte == 0 )
        {
        // simple text string. Restart.
        // (includes empty strings)
        // Keep quote because these are strange strings that may contain any
        // number of quotes all over the place
        buffer = GetSimpleTextStringL( ETrue );
        // check if this looks like it needs decoding of MIME stuff (RFC2047)
        if ( buffer->Des().FindF( KEqualsQuestion16 ) == KErrNotFound )
            {
            return buffer;
            }
        else
            {
            // contains the magic equals-question mark combination,
            // try if MIME decoding succeeds
            CleanupStack::PushL( buffer );
            HBufC8* buffer8 = HBufC8::NewL( buffer->Des().Length() );
            buffer8->Des().Copy( buffer->Des() );
            CleanupStack::PushL( buffer8 );
            TPtr pointer = buffer->Des();
            TRAPD( error,
                TMmsGenUtils::DecodeAndConvertMessageHeaderL( buffer8->Des(), pointer, iFs ) );
            if ( error != KErrNone )
                {
                // if did not succeed, keep original as is
                buffer->Des().Copy( buffer8->Des() );
                }
            CleanupStack::PopAndDestroy( buffer8 );
            CleanupStack::Pop( buffer );
            return buffer;
            }
        }

    iPosition++;

    if (iPosition >= iLength )
        {
        iError = KErrCorrupt;
        buffer = HBufC16::NewL(0);
        return buffer;
        }

    if ( byte >= KMms0x80 )
        {
        // the message is corrupted.
        iError = KErrCorrupt;
        buffer = HBufC16::NewL(0);
        return buffer;
        }

    if ( byte == KMms31 )
        {
        stringLength = GetUintvar();
        }
    else 
        {
        stringLength = byte;
        }

    // Get character set
    // Character set must be an integer, short or long

    // check if character set is missing
    charset = GetLongOrShortInteger();
#ifndef _NO_MMSS_LOGGING_
    if ( charset != 0 )
        {
        TMmsLogger::Log( _L("- Encoded string charset %d"), charset );
        }
#endif

    if ( iPosition >= iLength || iError == KErrCorrupt )
        {
        iError = KErrCorrupt;
        buffer = HBufC16::NewL(0);
        return buffer;
        }

    // Get text length whatever character set

    iDecodeBuffer->Read(iPosition, &byte, 1);

    if ( byte == KMmsQuote )
        {
        // skip quote
        iPosition++;
        if (iPosition < iLength)
            {
            iDecodeBuffer->Read(iPosition, &byte, 1);
            }
        }

    start = iPosition;
    stringLength = 0;
    TBool foundQuote = EFalse;

    // Leave quote as is.
    // It has turned out that we do not get quoted strings in the WSP format
    // with leading quote only. We tend to get either both the leading and
    // trailing quote or none at all, so we better not remove the quote.
    // If we start seeing WSP quoted strings, we must start checking that
    // leading and trailing quotes match.

    /*
    if ( byte == KMmsStringQuote )
        {
        // skip quote
        iPosition++;
        start++;
        foundQuote = ETrue;
        }
    */

    while ( byte != 0 && iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        stringLength++;
        }

    // stringLength includes the terminating zero

    // allocate buffer for unicode
    // this should be the maximum we'll ever need
    if ( stringLength == 0 )
        {
        // The length of the string == 0
        // This is an empty string
        // I'm not sure if it is legal or not - but we keep it anyway
        if ( iPosition == start )
            {
            // we did not read anything, the first byte was 0.
            // we must advance out pointer beyond it
            iPosition++;
            }
        buffer = HBufC16::NewL(0);
        return buffer;
        }

    stringLength--; // remove the terminating zero
    buffer = HBufC16::NewL( stringLength * KMms2 );

    if ( stringLength == 0 )
        {
        // buffer has zero length
        // I assume this is an empty string
        // I'm not sure if it is legal or not
        return buffer;
        }

    if ( charset == KMmsUtf8 )
        {
        TPtr8 pointer( iDecodeBuffer->Ptr( start ) );
        pointer.SetLength( stringLength );
        TInt error;
        TPtr16 pointer16 = buffer->Des();
        error = CnvUtfConverter::ConvertToUnicodeFromUtf8( pointer16, pointer );
        // should check error
        if ( error != KErrNone )
            {
            buffer->Des().Copy( pointer ) ; // could not convert, just copy garbage
            }
        }
    else
        {
        // other character sets to be implemented...
        TPtr8 pointer( iDecodeBuffer->Ptr( start ) );
        pointer.SetLength( stringLength );
        buffer->Des().Copy( pointer ) ; // just copy without conversion
        }
 
    if ( foundQuote && buffer->Des().Right( 1 ).Compare( KQuote16 ) == 0 )
        {
        // if we removed the leading quote, we remove the trailing quote, too.
        buffer->Des().Delete( buffer->Des().Length() - 1, 1 );
        }
    return buffer;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TPtrC8 CMmsDecode::GetByteString()
    {
    
    TUint8 byte = 0;
    if ( iPosition < iLength )
        {
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        }
    else
        {
        return TPtrC8();
        }

    TUint start = iPosition;
    TUint length = 0;

    if ( byte == 0 )
        {
        // Empty string, skip over terminating zero.
        // Empty string may or may not be legal - we must
        // be prepared to accept it.
        // The caller must decide if empty value is legal or not.
        iPosition++;
        return TPtrC8();
        }

    TBool foundQuote = EFalse;
    if ( ( byte >= KMms32 ) && ( byte <= KMms0x7F ) )
        {
        if ( byte == KMmsStringQuote )
            {
            // skip quote
            iPosition++;
            start++;
            foundQuote = ETrue;
            }
        else if ( byte == KMmsQuote )
            {
            // We are not supposed to have a string starting with quote here
            // but we must be careful and strip the quote in case one exists.
            // skip quote
            iPosition++;
            start++;
            }

        // don't read beyond buffer
        // byte is between 32 and 127 when we come here.
        while ( byte != 0 && iPosition < iLength )
            {
            iDecodeBuffer->Read(iPosition, &byte, 1);
            iPosition++;
            }
        // now iPosition points beyond terminating zero - if there is one
        
        length = iPosition - start;
        if ( length > 1 )
            {
            // remove terminating zero if there is one
            length--;
            }
        // If we got a end-of-string immediately after string quote,
        // we have nothing left here.
        // Or even worse - we have not even the terminating zero
        if ( length < 1 )
            {
            return TPtrC8();
            }
        TPtr8 pointer( iDecodeBuffer->Ptr( start ) );
        pointer.SetLength( length );
        // Length is at least one, it was just checked.
        if ( foundQuote && ( pointer.Right( 1 )[0] == KMmsStringQuote ) )
            {
            length--; // decrease length to remove end quote
            }
        pointer.SetLength( length );
        return TPtrC8( pointer );
        }
    else
        {
        SkipFieldValue();
        return TPtrC8();
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TPtrC8 CMmsDecode::GetUtf8String()
    {
    TUint8 byte = 0;
    if ( iPosition < iLength )
        {
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        }
    else
        {
        return TPtrC8();
        }

    TUint start = iPosition;
    TUint length = 0;

    if ( byte == 0 )
        {
        // Empty string, skip over terminating zero.
        // Empty string may or may not be legal - we must
        // be prepared to accept it.
        // The caller must decide if empty value is legal or not.
        iPosition++;
        return TPtrC8();
        }

    if ( byte == KMmsQuote )
        {
        // skip quote
        iPosition++;
        start++;
        }

    // don't read beyond buffer
    while ( byte != 0 && iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        }

    // now iPosition points beyond terminating zero
    length = iPosition - start - 1;
    // If we got a end-of-string immediately after string quote,
    // we have nothing left here.
    if ( length < 1 )
        {
        return TPtrC8();
        }
    TPtr8 pointer( iDecodeBuffer->Ptr( start ) );
    pointer.SetLength( length );
    return TPtrC8( pointer );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TUint32 CMmsDecode::GetLongOrShortInteger()
    {

    TUint8 byte;
    TUint32 integer = 0;

    if ( iPosition >= iLength )
        {
        return integer;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);
    iPosition++;
    if ( byte & KMms0x80 )
        {
        integer = byte & KMms0x7F;
        return integer;
        }

    if ( byte > KMms30 )
        {
        // Data is corrupted.
        iError = KErrCorrupt;
        return integer; 
        }

    if ( byte > KMmsMaxIntegerLength )
        {
        // too long.
        iError = KErrTooBig;
        iPosition--;
        // It the caller thinks retry is possible, he must store the
        // original starting position.
        // So far no case has been found where the retry with very long
        // integer would be needed.
        SkipFieldValue();
        return integer;
        }

    TUint i;
    TUint length;
    length = byte;
    for ( i = 0;  i < length && iPosition < iLength; i++)
        {
        integer <<= KMmsBitsInByte;
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        integer += byte;
        }

    return integer;

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt64 CMmsDecode::GetVeryLongInteger()
    {

    TUint8 byte;
    TInt64 veryLongInteger;

    veryLongInteger = 0;

    iDecodeBuffer->Read(iPosition, &byte, 1);
    iPosition++;
    if ( byte & KMms0x80 )
        {
        veryLongInteger = byte & KMms0x7F;
        return veryLongInteger;
        }

    if ( byte > KMms30 )
        {
        // data is corrupted.
        iError = KErrCorrupt;
        return veryLongInteger; 
        }

    if ( byte > KMmsMaxLongIntegerLength )
        {
        // too long.
        iError = KErrTooBig;
        iPosition--;
        // caller may do whatever he wants, or skip whole field
        return veryLongInteger;
        }

    TUint i;
    TUint length;
    length = byte;

    for ( i = 0;  i < length && iPosition < iLength; i++)
        {
        veryLongInteger <<= KMmsBitsInByte;
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        veryLongInteger += TUint( byte );
        }
    
    return veryLongInteger;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TUint8 CMmsDecode::GetWellKnownFieldValueOrSkip()
    {

    TUint8 byte;
    byte = 0;
    iDecodeBuffer->Read(iPosition, &byte, 1);
        
    // Interpret the length of the field

    // one byte encoded value, this field has no more data
    if ( byte >= KMms0x80 )
        {
        iPosition++;
        return byte;
        }

    // if not one byte token, skip it
    SkipFieldValue();
    return byte;  // this is < 128, which is never a legal token

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TUint8 CMmsDecode::GetRelativeOrAbsoluteTime( TInt64& aTime )
    {
    TUint8 byte;
    TInt length;

    // we don't use the length, we just skip it
    length = GetValueLength();
    if ( ( iPosition + length ) > iLength || iPosition >= iLength )
        {
        iPosition = iLength;
        aTime = 0;
        byte = 0;
        iError = KErrCorrupt;
        return byte;
        }
    
    iDecodeBuffer->Read(iPosition, &byte, 1);

    if ( ( byte != KMmsAbsoluteToken ) && ( byte != KMmsRelativeToken ) )
        {
        // illegal encoding, skip.
        iPosition += length;
        byte = 0;
        aTime = 0;
        return byte;
        }

    iPosition++;
    if ( iPosition >= iLength )
        {
        iError = KErrCorrupt;
        byte = 0;
        aTime = 0;
        return byte;
        }

    aTime = GetVeryLongInteger();

    return byte;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TUint CMmsDecode::GetValueLength()
    {
    TUint8 byte;
    TUint length = 0;

    if ( iPosition >= iLength )
        {
        return 0;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);
    
    // Interpret the length of the field

    // one byte encoded value, this field has no more data
    if ( byte >= KMms0x80 )
        {
        length = 1;
        return length;
        }

    // text string
    // ( byte >= 128 returned already, so byte must be <=127 if we are here )
    if ( byte >= KMms32 )
        {
        // find the end
        length = iPosition;

        while ( byte != 0 && length < iLength )
            {
            iDecodeBuffer->Read(length, &byte, 1);
            length++;
            }

        // The difference of pointers is the actual string length
        length -= iPosition;

        return length;
        }
    
    // octet followed by a uintvar
    if ( byte == KMms31 )
        {
        iPosition++;
        // uintvar can be max 5 bytes, max value of
        // result is 32 bits.
        // get uintvar
        if ( iPosition < iLength )
            {
            length = GetUintvar();
            }
        return length;
        }

    // remains: octets 0 - 30
    // octet followed by the indicated number of data octets
    iPosition++;
    length = byte;
    return length;

    }
    
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsDecode::CheckHeaderLength()
    {
    TInt oldPosition = iPosition;
    TBool retVal = ETrue; // optimistic
    TUint8 byte;
    
    TBool checkUintvarData = ETrue;
    
    if ( iPosition < iLength )
        {
        // read first byte for some special checks
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        }
    else
        {
        return EFalse;
        }
    
    if ( iDecodingStage == EMmsAttachmentHeaders )
        {
        if ( ( byte & KMms0x80 ) && ( ( byte & KMms0x7F ) == KWspQValue ) )
            {
            // Quality factor in attachment headers has strange encoding
            checkUintvarData = EFalse;
            }
        }
    
    // check header first - uintvar data included
    retVal = CheckValueLength( ETrue );
    
    // The position of the pointer is at the end of the header name
    // if the whole header name is in the buffer
    
    // Check the length of the header
    if ( retVal )
        {
        retVal = CheckValueLength( checkUintvarData );
        }
        
    if ( iDecodingStage == EMmsHeaders )
        {
        if ( byte == KMmsAssignedContentType && retVal )
            {
            // we need to have an uintvar specifying the number of attachments
            // to follow content type header            
            retVal = CheckUintvarLength();
            }
        }
   
    // always restore the pointer for decoding
    iPosition = oldPosition;
    return retVal;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsDecode::CheckValueLength( TBool aCheckUintvarData )
    {
    TBool retVal = ETrue; // optimistic
    
    TUint8 byte;
    TUint length = 0;
    
    if ( iPosition >= iLength )
        {
        return EFalse;
        }
        
    iDecodeBuffer->Read(iPosition, &byte, 1);
    
    // Interpret the length of the field

    if ( byte >= KMms0x80 )
        {
        // one byte encoded value, this field has no more data
        iPosition++;
        }
    else if ( byte >= KMms32 )
        {
        // text string - find the end
        while ( byte != 0 && iPosition < iLength )
            {
            iDecodeBuffer->Read( iPosition, &byte, 1 );
            iPosition++;
            }
        if ( byte != 0 )
            {
            // we reached the end of the buffer without finding the terminating 0
            // there is not enough data
            retVal = EFalse;
            }
        }
    else if ( byte == KMms31 )
        {
        // octet followed by a uintvar
        iPosition++;
        // get all bytes belonging to uintvar first to see if we have them all.
        TInt uintvarStart = iPosition;
        if ( CheckUintvarLength() )
            {
            // we have an uintvar, do we have all the following bytes
            if ( aCheckUintvarData )
                {
                iPosition = uintvarStart;
                // we are asked to check the data, too
                length = GetUintvar();
                if ( ( iLength - iPosition ) < length )
                    {
                    retVal = EFalse;
                    }
                else
                    {
                    iPosition += length;
                    }
                }
            }
        else
            {
            // we did not even have the uintvar let alone the data following it
            retVal = EFalse;
            }
        }
    else
        {
        // remains: octets 0 - 30
        // octet followed by the indicated number of data octets
        iPosition++;
        length = byte;
        if ( ( iLength - iPosition ) < length )
            {
            retVal = EFalse;
            }
        else
            {
            iPosition += length;
            }
        }
        
    return retVal;
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsDecode::CheckUintvarLength()
    {
    TBool retVal = ETrue; // optimistic
    TUint8 byte;
    
    if ( iPosition >= iLength )
        {
        return EFalse;
        }
    
    // get all bytes belonging to uintvar to see if we have them all.
    iDecodeBuffer->Read( iPosition, &byte, 1 );
    while ( byte & KMms0x80  && iPosition < iLength - 1 )
        {
        iPosition++;
        iDecodeBuffer->Read(iPosition, &byte, 1);
        }
    iPosition++;
    if ( byte & KMms0x80 )
        {
        // if byte still has high bit set, we have not found
        // the end of the uintvar -> there is not enough data
        retVal = EFalse;
        }
                
    return retVal;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TPtrC8 CMmsDecode::GetContentTypeL()
    {

    TUint8 byte;
    TPtrC8 pointer = TPtrC8();

    if ( iPosition >= iLength )
        {
        iError = KErrCorrupt;
        return pointer;
        }

    iDecodeBuffer->Read(iPosition, &byte, 1);

    // Test if it is a well-known media type
    if ( byte >= KMms0x80 )
        {
        iPosition++; // skip the content type byte
        byte &= KMms0x7F; // remove the short integer identifier bit
        if ( ( byte == KMmsAssignedTextPlain ) )
            {
            // The beginning of the first text/plain part is used
            // to generate a description of the message, if
            // no real subject is available.
            // Save the starting point of corresponding data.
            if ( iDataStart < iLength )
                {
                iFakeSubject = iDataStart;
                }
            iUseForSubject = ETrue;
            // count the number of plain text attas.
            // We use iFakeSubject only if iPlainTexts == 1
            iPlainTexts++;
            }
        // encoded value
        if ( byte < KNumberContentTypes )
            {
            pointer.Set( KContentTypeTable[byte] );
            }
        else
            {
            pointer.Set( KContentTypeTable[KMmsUnAssigned] );
            }
        return pointer;
        }
    
    // Test if it is a text string without parameters
    // ( byte >= 128 returned already, byte is <= 127 if we get here )
    if ( byte >= KMms32 )
        {
        // text string
        return GetByteString();
        }

    // We have the worst case left:
    // Content-General-Form = Value-length Media-Type

    TUint mediaLength = 0;
    mediaLength = GetValueLength();
    TUint start = iPosition;

    if ( iPosition < iLength )
        {
        pointer.Set( GetContentTypeL() ); // We call ourselves.
        }

    // Now we should get the parameters, but we know nothing of
    // parameters. For now we just throw them away.
    // The handling of parameters of specific media types needs more study
    
    // The only parameter we care about now is charset.
    // its coding should be well-known

    while ( iPosition < start + mediaLength && iPosition < iLength )
        {
        iDecodeBuffer->Read( iPosition, &byte, 1 );
        if ( ( byte & KMms0x80 ) && ( ( byte & KMms0x7F ) == KWspCharset ) )
            {
            iPosition++;
            TUint32 charset;
            charset = GetLongOrShortInteger();
            iMimeHeaders->SetMimeCharset( charset );
            // if this is the part possibly used to generate
            // our fake subject, save the character set.
            if ( iFakeSubject == iDataStart )
                {
                iCharacterSet = charset;
                }
            }
        else if ( ( byte & KMms0x80 ) && ( ( byte & KMms0x7F ) == KWspName ) )
            {
            iPosition++;
            // Get name parameter to be suggestion of filename
            iMimeHeaders->ContentTypeParams().AppendL( KWspNameString );
            iMimeHeaders->ContentTypeParams().AppendL( GetByteString() );
            }
        else if ( ( byte & KMms0x80 ) && ( ( byte & KMms0x7F ) == KWspQValue ) )
            {
            iPosition++;
            // value encoded as Uintvar, must skip differently than others.
            GetUintvar();
            }
        else
            {
            if ( byte >= KMms0x20 && byte <= KMms0x7F )
                {
                // text string, save as content type parameter
                // If we don have a pair of two text strings, the message is illegal
                // The code will probably leave at some point
                iMimeHeaders->ContentTypeParams().AppendL( GetByteString() );
                iMimeHeaders->ContentTypeParams().AppendL( GetByteString() );
                }
            else
                {
                // skip
                SkipParameterName();
                SkipFieldValue();
                }
            }
        }
        
    iPosition = start + mediaLength;

    return pointer;

    }

// ---------------------------------------------------------
// If the content type is multipart, pointer will be positioned
// to the start of the multipart block (The first attachment)
// ---------------------------------------------------------
//
TUint8 CMmsDecode::GetMultipartContentTypeL()
    {

    TUint8 byte;
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
#endif

    iMultipartRootType.Set( TPtrC8() ); // clear contents
    iDecodeBuffer->Read(iPosition, &byte, 1);
    // Test if it is a well-known media type.
    if ( byte >= KMms0x80 )
        {
        byte &= KMms0x7F; // remove the short integer identifier bit
        // Test if it is a multipart type.
        if ( ( byte >= KMmsAssignedApplicationVndWapMultipart &&
            byte <= KMmsAssignedApplicationVndWapMultipartAlternative ) ||
            ( byte == KMmsAssignedApplicationVndWapMultipartRelated ) )
            {
            iPosition++; // skip the content type byte
            // Get number of parts in multipart
            iNumberOfAttachments = GetUintvar();
            }
        else
            {
            byte = 0;
            iNumberOfAttachments = 1;
            }
        // No more data in this header.
        // For example multipart/mixed has no parameters
        return byte;
        }

    // if we get here byte is <= 127 
    if ( byte >= KMms32 )
        {
        // text string, we don't even try to analyze it.
        // Nobody should send a well-known media type as a text string,
        // and if it is not a well-known type, we don't know what
        // to do with it, it will be treated as a monoblock
        byte = 0;
        iNumberOfAttachments = 1;
        return byte;
        }

    // The most complicated case: Content-General Form
    // Content-General-Form = Value-length Media-Type

    // we save the position in case this is monoblock and
    // we must keep the old position to get the content type for
    // the attachment.
    TUint oldPosition = iPosition;

    TUint mediaLength = 0;
    mediaLength = GetValueLength();
    TUint start = iPosition;
    byte = 0;

    if ( iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        }
    else
        {
        iNumberOfAttachments = 0;
        return 0;
        }

    if ( byte >= KMms0x80 )
        {
        byte &= KMms0x7F; // remove the short integer identifier bit
        // Test if it is a multipart type.
        if ( ( byte >= KMmsAssignedApplicationVndWapMultipart &&
            byte <= KMmsAssignedApplicationVndWapMultipartAlternative ) ||
            ( byte == KMmsAssignedApplicationVndWapMultipartRelated ) )
            {
            iPosition++; // skip the content type byte
            }
        else
            {
            // Not a multipart we can handle
            byte = 0;
            iNumberOfAttachments = 1;
            // point back to start of header
            // parameters will belong to the attachment
            iPosition = oldPosition;
            return byte;
            }
        }
    else
        {
        // text string, we don't even try to analyze it.
        // Nobody should send a well-known media type as a text string,
        // and if it is not a well-known type, we don't know what
        // to do with it, it will be treated as a monoblock
        TPtrC8 mediatype = GetByteString();
        if ( mediatype.CompareF( KMmsWapMultipartReport ) == 0 )
            {
            byte = KMmsAssignedApplicationVndWapMultipartReport;
            }
        else
            {
            byte = 0;
            iNumberOfAttachments = 1;
            // point back to start of header
            // parameters will belong to the attachment
            iPosition = oldPosition;
            return byte;
            }
        }

    // If we get here we have a WSP type multipart.
    // We must still extract the parameters of the content type
    // If we have multipart/related, we have start parameter.
    // We must also try to extract Application-Id and Reply-To-Application-ID
    //     in case Java has added them.
    // We don't care about the others now.

    TInt header;
    TPtrC8 contentHeader; // parameter name in case it is a string
   
    while ( iPosition < start + mediaLength && iPosition < iLength )
        {
        header = GetContentHeaderName( contentHeader );
        if ( header == KWspStart )
            {
            // Now we must get the content-id of the root part
            // and save it temporarily. We can get the actual
            // TMsvId for the attachment only by comparing the
            // Content-Id parameters of the attachments to the
            // saved root-id.
            TPtrC8 pointer = GetByteString();
            if ( pointer.Find( KMmsLeftAngle ) == 0 &&
                pointer.Find( KMmsRightAngle ) == pointer.Length() - 1 )
                {
                // remove angle brackets from cid, 2 characters removed
                pointer.Set( pointer.Mid( 1, pointer.Length() - KMms2 ) );
                }
            delete iRootContentIdBuffer;
            iRootContentIdBuffer = NULL;
            iRootContentIdBuffer = HBufC8::NewL( pointer.Length() );
            iRootContentIdBuffer->Des().Copy( pointer );
            iRootContentId.Set( iRootContentIdBuffer->Des() );
            }
        else if ( header == KWspQValue )
            {
            // value encoded as Uintvar, must skip differently than others.
            GetUintvar();
            }
        else if ( header == KWspRelatedType )
            {
            // get content type
            iMultipartRootType.Set( GetContentTypeL() ); 
            }
        else if ( header == KMmsTextHeader )
            {
            // see if this is Java application id
            if ( contentHeader.CompareF( KMmsJavaApplicationId ) == 0 )
                {
                DecodeApplicationIdL();
                }
            else if ( contentHeader.CompareF( KMmsJavaReplyApplicationId ) == 0 )
                {
                DecodeReplyApplicationIdL();
                }
            else
                {
                // something we don't handle
#ifndef _NO_MMSS_LOGGING_
                HBufC16* buffer = NULL;
                buffer = HBufC16::NewLC( contentHeader.Length() );
                buffer->Des().Copy( contentHeader );
                // we cannot log indefinitely long strings.
                // We get this long strings only if the message is corrupted.
                dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
                TMmsLogger::Log( _L("- Text header: %S"), &dummy );
                CleanupStack::PopAndDestroy( buffer );
                buffer = NULL;
#endif
                SkipFieldValue();
                }
            }
        else
            {
            // skip
            // If the caller has messed things up and sent "start"
            // as a text string, we will miss it...
            // GetContentHeaderName always skips the header name.
            // if header < 0, the field has already been skipped
            if ( header >= 0 )
                {
                SkipFieldValue();
                }
            }
        }
        
    iPosition = start + mediaLength;
    iNumberOfAttachments = GetUintvar();

    return byte;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::GetAttachmentContentTypeL()
    {
    TPtrC8 byteString;
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    TPtrC dummy;
#endif

    // get content type
    byteString.Set( GetContentTypeL() );
    TInt semicolonPosition = 0;
    TInt position = 0;
    semicolonPosition = byteString.FindF( KSemicolon8 );
    // check if this content type has been sent to us as a string containing
    // parameters
    if ( semicolonPosition != KErrNotFound && semicolonPosition != 0 )
        {
        TInt endString = ( byteString.Left( semicolonPosition ).FindF( KSpace8 ) );
        if ( endString == KErrNotFound )
            {
            endString = semicolonPosition;
            }
        // First part of string is actual content-type header,
        // trailing blanks removed.
        // If the string was total garbage like "type huh;hei=hui",
        // the result will be wrong, but that cannot be helped if the input
        // is not legal.
        // The following should be OK
        // "application/type ;param1=value1
        TPtrC8 contentType = byteString.Left( endString );
        position = contentType.Find( KMmsSlash8 );
        if ( position <= 0 || position == contentType.Length() )
            {
            // weird string - no subtype, keep as is
            iMimeHeaders->SetContentTypeL( contentType );
            }
        else
            {
            iMimeHeaders->SetContentTypeL( contentType.Left( position ) );
            iMimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) );
            }
        // get the rest of the stuff as parameters
        ExtractContentTypeParametersL( byteString.Mid( semicolonPosition + 1 ) );
        }
    else
        {
        // If first character is semicolon, the content type is garbage anyway,
        // and we keep it "as is"
        position = byteString.Find( KMmsSlash8 );
        if ( position <= 0 || position == byteString.Length() )
            {
            // weird string - no subtype, keep as is
            iMimeHeaders->SetContentTypeL( byteString );
            }
        else
            {
            iMimeHeaders->SetContentTypeL( byteString.Left( position ) );
            iMimeHeaders->SetContentSubTypeL( byteString.Mid( position + 1 ) );
            }
        }
        
// if attachment subtype is "smil" or "amr", increase respective counter.


    if ( iMimeHeaders->ContentSubType().CompareF( KMmsSmilSubtype ) == 0 )
        {
        iSmilCount++;
        }
    else if ( iMimeHeaders->ContentType().CompareF( KMmsAudioType ) == 0 )
        {
        if ( iMimeHeaders->ContentSubType().CompareF( KMmsAudioSubtype ) == 0 )
            {
            iAudioCount++;
            }
        else if ( iMimeHeaders->ContentSubType().CompareF( KMmsAudioSubtype2 ) == 0 )
            {
            // Change the subtype because the x-amr is not supported
            iMimeHeaders->SetContentSubTypeL( KMmsAudioSubtype );
            iAudioCount++;
            }
        else
            {
            // do nothing - keep LINT happy
            }
        }
    else
        {
        // do nothing - keep LINT happy
        }
            
#ifndef _NO_MMSS_LOGGING_
    if ( semicolonPosition > 0 )
        {
        byteString.Set( byteString.Left( semicolonPosition ) );
        }
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Content Type: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
    if ( iMimeHeaders->MimeCharset() != 0 )
        {
        TMmsLogger::Log( _L("- Character set: %d"), iMimeHeaders->MimeCharset() );
        }
    if ( iMimeHeaders->ContentTypeParams().MdcaCount() > 0 )
        {
        TInt i;
        // we increment by 2 because every other value is parameter name
        // and every othe one the value
        for ( i = 0; i < iMimeHeaders->ContentTypeParams().MdcaCount(); i += KMms2 )
            {
            HBufC16* buffer2 = NULL;
            buffer = HBufC16::NewLC(
                iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() );
            buffer->Des().Copy( iMimeHeaders->ContentTypeParams().MdcaPoint( i ) );
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            if ( i + 1 < iMimeHeaders->ContentTypeParams().MdcaCount() )
                {
                buffer2 = HBufC16::NewLC(
                    iMimeHeaders->ContentTypeParams().MdcaPoint( i + 1 ).Length() );
                TPtrC dummy2;
                buffer2->Des().Copy( iMimeHeaders->ContentTypeParams().MdcaPoint( i + 1 ) );
                dummy2.Set( buffer2->Des().Left( KMmsMaxLogStringLength ) );
                TMmsLogger::Log( _L("- %S: %S"), &dummy, &dummy2 );
                CleanupStack::PopAndDestroy( buffer2 );
                }
            else
                {
                TMmsLogger::Log( _L("- %S"), &dummy );
                }
            CleanupStack::PopAndDestroy( buffer );
            }
        }
#endif

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeOneContentHeaderL()
    {

    TInt header;
    HBufC16* buffer = NULL;
    TUint8 byte = 0;
    TPtrC8 contentHeader; // content header in case it is a string
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
#endif
    header = GetContentHeaderName( contentHeader );

    if ( header < 0 )
        {
        return; // skip unknown
        }

    // Get the corresponding value
    
    switch ( header )
        {
        case KWspContentLocation:
            {
            // I hope this is a simple text string. we cannot handle
            // different character sets in this context.

            buffer = GetSimpleTextStringL(); // we might try to convert from utf8 to unicode
            CleanupStack::PushL( buffer );
            iMimeHeaders->SetContentLocationL( buffer->Des() );
            CleanupStack::PopAndDestroy( buffer );
            buffer = NULL;
            
#ifndef _NO_MMSS_LOGGING_
            buffer = HBufC16::NewLC( iMimeHeaders->ContentLocation().Length() );
            buffer->Des().Copy( iMimeHeaders->ContentLocation() );
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Content location: %S"), &dummy );
            CleanupStack::PopAndDestroy( buffer );
#endif
            break;
            }
        case KWspContentId:
            {
                TPtrC8 pointer = GetByteString();
                if ( pointer.Find( KMmsLeftAngle ) == 0 &&
                    pointer.Find( KMmsRightAngle ) == pointer.Length() - 1 )
                    {
                    // remove angle brackets from cid, 2 characters removed
                    pointer.Set( pointer.Mid( 1, pointer.Length() - KMms2 ) );
                    }
                iMimeHeaders->SetContentIdL( pointer );
                if ( iAttaNumber == 1 &&
                    iMultipartType == KMmsAssignedApplicationVndWapMultipartRelated &&
                    iRootContentId.Length() == 0 )
                    {
                    iRootContentId.Set( pointer );
                    }
#ifndef _NO_MMSS_LOGGING_
                buffer = HBufC16::NewLC( iMimeHeaders->ContentId().Length() );
                buffer->Des().Copy( iMimeHeaders->ContentId() );
                // we cannot log indefinitely long strings.
                // We get this long strings only if the message is corrupted.
                dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
                TMmsLogger::Log( _L("- Content Id: %S"), &dummy );
                CleanupStack::PopAndDestroy( buffer );
#endif
            break;
            }
        case KWspContentDisposition:
            {
            TUint length;
            length = GetValueLength();
            TUint currentPosition = iPosition;

            if ( iPosition < iLength )
                {
                iDecodeBuffer->Read(iPosition, &byte, 1);
                }
            if ( byte >= KMms0x80 )
                {
                // well known header
                iPosition++;
                byte &= KMms0x7F;
                if ( byte == KWspAttachment )
                    {
                    iMimeHeaders->SetContentDispositionL( KWspAttachmentString );
                    }
                else if ( byte == KWspInline )
                    {
                    iMimeHeaders->SetContentDispositionL( KWspInlineString );
                    }
                // never mind others - might be form-data or something unknown
                else
                    {
                    // do nothing - keep LINT happy
                    }
                }
            else
                {
                // string - use as is
                TPtrC8 pointer = GetByteString();
                iMimeHeaders->SetContentDispositionL( pointer );
                }
#ifndef _NO_MMSS_LOGGING_
            buffer = HBufC16::NewLC( iMimeHeaders->ContentDisposition().Length() );
            buffer->Des().Copy( iMimeHeaders->ContentDisposition() );
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Content Disposition: %S"), &dummy );
            CleanupStack::PopAndDestroy( buffer );
#endif
            // now try to see if we have filename parameter
            while ( iPosition < ( currentPosition + length ) && iPosition < iLength )
                {
                byte = 0; // clean out old data
                iDecodeBuffer->Read(iPosition, &byte, 1);

                if ( byte >= KMms0x80 )
                    {
                    // well known parameter
                    iPosition++;
                    byte &= KMms0x7F;
                    if ( byte == KWspFileName )
                        {
                        // this might contain some strange encodings (against specs.)
                        // should be checked
                        TPtrC8 pointer = GetByteString();
                        iMimeHeaders->ContentDispositionParams().AppendL( KWspFilenameString );
                        iMimeHeaders->ContentDispositionParams().AppendL( pointer );
#ifndef _NO_MMSS_LOGGING_
                        buffer = HBufC16::NewLC( pointer.Length() );
                        buffer->Des().Copy( pointer );
                        // we cannot log indefinitely long strings.
                        // We get this long strings only if the message is corrupted.
                        dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
                        TMmsLogger::Log( _L("- filename: %S"), &dummy );
                        CleanupStack::PopAndDestroy( buffer );
#endif
                        }
                    else
                        {
                        SkipFieldValue();
                        }
                    }
                else
                    {
                    // if "filename" has been sent as string, we skip it
                    if ( byte >= KMms0x20 && byte <= KMms0x7F )
                        {
                        // text string, save as content type parameter
                        // If we don have a pair of two text strings, the message is illegal
                        // The code will probably leave at some point
                        iMimeHeaders->ContentDispositionParams().AppendL( GetByteString() );
                        iMimeHeaders->ContentDispositionParams().AppendL( GetByteString() );
                        }
                    else
                        {
                        // skip
                        SkipParameterName();
                        SkipFieldValue();
                        }
                    }
                }

            iPosition = currentPosition + length;
            if ( iPosition >= iLength )
                {
                iError = KErrCorrupt;
                }
            break;
            }
        case KMmsTextHeader:
            {
            // contentHeader points to the textual header
#ifndef _NO_MMSS_LOGGING_
            // we don't leave just for logging
            TInt error = KErrNone;
            TRAP( error,
                {
                buffer = NULL;
                buffer = HBufC16::NewLC( contentHeader.Length() );
                buffer->Des().Copy( contentHeader );
                // we cannot log indefinitely long strings.
                // We get this long strings only if the message is corrupted.
                dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
                TMmsLogger::Log( _L("- Textual content header: %S"), &dummy );
                CleanupStack::PopAndDestroy( buffer );
                };);
            if ( error != KErrNone )
                {
                TMmsLogger::Log( _L("- Decode left when logging content header, error : %d"),
                    error ); 
                }
#endif
            if ( contentHeader.FindF( KMmsExtension ) == 0 )
                {
                // extension header, store to mime headers, get corresponding parameter
                // and store to MIME headers
                TPtrC8 pointer = GetByteString();
                iMimeHeaders->XTypeParams().AppendL( contentHeader ); // header name 
                iMimeHeaders->XTypeParams().AppendL( pointer ); // header value
#ifndef _NO_MMSS_LOGGING_
                buffer = HBufC16::NewLC( pointer.Length() );
                buffer->Des().Copy( pointer );
                // we cannot log indefinitely long strings.
                // We get this long strings only if the message is corrupted.
                dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
                TMmsLogger::Log( _L("- extension header value: %S"), &dummy );
                CleanupStack::PopAndDestroy( buffer );
#endif
                }
            else
                {
                SkipFieldValue();
                }
                
            break;
            }
        default:
#ifndef _NO_MMSS_LOGGING_
            TMmsLogger::Log( _L("- Unknown content header: 0x%02X"), header );
#endif
            SkipFieldValue();
            break;
        }

    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TInt CMmsDecode::GetContentHeaderName( TPtrC8& aTextHeader )
    {

    TInt header;
    header = -1; // unknown
    aTextHeader.Set( TPtrC8() ); // empty string
    
    TUint8 byte;

    if ( iPosition < iLength )
        {
        iDecodeBuffer->Read(iPosition, &byte, 1);
        }
    else
        {
        return header;
        }

    // byte cannot be bigger than 255. No need to test upper limit
    if ( byte >= KMms0x80 )
        {
        // well known header, advance pointer and remove high bit
        iPosition++;
        header = (byte & KMms0x7F);
        return header;
        }
    else
        {
        TPtrC8 pointer = GetByteString();
        aTextHeader.Set( pointer );
        header = KMmsTextHeader; // this will cover X-headers
        if ( pointer.CompareF( KWspContentIdString ) == 0 )
            {
            header = KWspContentId;
            }
        }

    // To handle X-headers and Java apllication id parameters,
    // the text string is offered to caller.
    // The header has been set to KMmsTextHeader to indicate that the field value
    // is still available.

    if ( ( header < 0 ) && ( iPosition < iLength ) )
        {
        SkipFieldValue();
        }

    return header;
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
//
void CMmsDecode::DecodeOneAttachmentL()
    {
    
    DecodeAttachmentHeadersL();
    
    // We must be able to handle an empty message without panic
    if ( iCurrentAttaLength == 0 )
        {
        // if attachment length == 0, do not create an attachment
        return;
        }
    
    TMsvAttachmentId attachmentId = 0;
    TInt32 drmFlags = 0;
    TPtrC8 attaData;
    if ( iPosition >= iLength )
        {
        iCurrentAttaLength = 0;
        }
    attaData.Set( iDecodeBuffer->Ptr( iPosition ).Ptr(), iCurrentAttaLength );
   
    TInt size = 0;
    
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Attachment creation starts %S"), &iParse.FullName() );
#endif
    if ( iError == KErrNone )
        {
        iError = iEntryWrapper->CreateFileAttachmentL(
            *iStore,
            iParse.NameAndExt(),
            attaData,
            *iMimeHeaders,
            attachmentId,
            size,
            drmFlags);
        }

#ifndef _NO_MMSS_LOGGING_
    if ( iError == KErrNone )
        {
        TRAP_IGNORE({
            MMsvAttachmentManager& attaMan = iStore->AttachmentManagerL();
            CMsvAttachment* attaInfo = attaMan.GetAttachmentInfoL( attachmentId );
            TMmsLogger::Log( _L("- Attachment created %S"), &attaInfo->FilePath() );
            delete attaInfo;
            attaInfo = NULL;
            });
        }
#endif

    iDRMFlags |= drmFlags;
    
    if ( iError == KMmsErrorRemoveDRM )
        {
        // The attachment was not saved, but we can continue
        iError = KErrNone;
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- DRM attachment removed") );
#endif
        }
    else if ( iError == KErrNone )
        {
        if ( ( iMultipartType == KMmsAssignedApplicationVndWapMultipartRelated ) &&
            ( iRootContentId.Length() > 0 ) &&
            ( iRootAttachmentId == 0 ) )
            {
            // check if content-id's match
            if ( iRootContentId.Compare( iMimeHeaders->ContentId() ) == 0 )
                {
                iRootAttachmentId = attachmentId;
                }
            }
        iTotalSize += size;
        }
    else
        {
        // do nothing - keep LINT happy
        }
 
    if ( iDataStart == iFakeSubject )
        {
        iTextPlainLength = iCurrentAttaLength;
        }

    // if doing dumping, also dump the smil part to a file
    // Note: this modifies iFileName, but our original file was closed already
    
#ifndef _NO_MMSS_LOGGING_
    DumpSmil( iCurrentAttaLength );
#endif
 
    iPosition = iNextStart;

    if ( iMultipartType == KMmsAssignedApplicationVndWapMultipartReport )
        {
        // If we have a multipart/report, we ignore the rest of attachments
        iNumberOfAttachments = 1;
        iPosition = iLength;
        }
     
    CompleteSelf( iError );    
        
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeAttachmentHeadersL()
    {
    TUint32 headersLength = 0;
    iCurrentAttaLength = 0;
    iAttaNumber++; // starts fron zero, is 1 after first increment
    TInt error = KErrNone; // temporary error
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
#endif

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Attachment #%d"), iAttaNumber );
#endif
    // we may have a multipart structure or a single monoblock data body
    // we handle both here.
    // The momoblock structure probably won't work correctly in chunked mode
    // but it is not supposed to be supported anyway.
    
    // Before this funtion is called in the chunked mode we have checked
    // that we have enough data to cover all the headers (the length of the
    // headers is given in the beginning)
    
    if ( iMultipartType != 0 )
        {
        if ( iPosition < iLength )
            {
            headersLength = GetUintvar();
            }
        else
            {
            iError = KErrCorrupt;
            if ( iDecodingStage == EMmsNotApplicable )
                {
                CompleteSelf( iError );
                }
            return;
            }

        if ( iPosition < iLength )
            {
            iCurrentAttaLength = GetUintvar();
            }
        else
            {
            iError = KErrCorrupt;
            if ( iDecodingStage == EMmsNotApplicable )
                {
                CompleteSelf( iError );
                }
            return;
            }
            
        // Data start can be calculated now.
        // Also the start of next attachment.

        iDataStart = iPosition + headersLength;

        if ( iDataStart > iLength && iDecodingStage == EMmsNotApplicable )
            {
            // This is an error only if we are handling all data, not chunks
            iError = KErrCorrupt;
            CompleteSelf( iError );
            return;
            }

        if ( iDataStart + iCurrentAttaLength > iLength && iDecodingStage == EMmsNotApplicable )
            {
            // if we are doing chunked decoding there may not be enough data in our buffer
            iCurrentAttaLength = iLength - iDataStart;
            }
        iNextStart = iDataStart + iCurrentAttaLength;
        }
    else
    // monoblock - this probably won't work in chunked mode
    // but then MMS engine is not supposed to support this kind of structure anyway
    // The data should always be wrapped in multipart structure even if there is 
    // only one part.
    // If the message is so small that everything fits into the buffer at one go,
    // then this will work in chunked mode, too.
        {
        // save old position
        TUint oldPosition = iPosition;
        headersLength = GetValueLength();
        iDataStart = iPosition + headersLength;
        iCurrentAttaLength = iLength - iDataStart;
        iNextStart = iDataStart + iCurrentAttaLength;
        // Header's length is part of content type header.
        // we cannot remove it, or we cannot interpret the
        // content type header correctly.
        iPosition = oldPosition;
        }

#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Headers length: %d"), headersLength );
    TMmsLogger::Log( _L("- Data length: %d"), iCurrentAttaLength );
#endif

    
    // don't touch iMmsHeaders, they may still be needed.
    // Just reset mime headers, attachments don't need anything else.
    iMimeHeaders->Reset();

    // We must be able to handle an empty message without panic
    if ( iCurrentAttaLength == 0 )
        {
        // empty attachment, don't create entry
        iPosition = iNextStart;
        if ( iDecodingStage == EMmsNotApplicable )
            {
            // chunked mode is not an active object
            CompleteSelf( iError );
            }
        return;
        }
    
    // attachment creation cannot be done before we have all the headers
    
    GetAttachmentContentTypeL();
    
    // Now we must analyze the content-headers
    // We only try to find Content-Location to get the filename
    // We should also save the Content-ID:s in case the SMIL part uses them
    // We also need to check if the Content-ID of some part matches
    // the content id given as the start parameter for multipart/related type
    // Some terminals put filename as parameter of content-disposition,
    // so that should be checked, too.

    while ( iPosition < iDataStart )
        {
        DecodeOneContentHeaderL();
        }

    if ( ( iMultipartType == KMmsAssignedApplicationVndWapMultipartRelated ) &&
        ( iRootContentId.Length() == 0 ) && iAttaNumber == 1 )
        {
        // First part must be root if start parameter not defined
        if ( iMimeHeaders->ContentId().Length() == 0 )
            {
            TTime now;
            now.UniversalTime();
            TInt random;
            TInt64 seed = now.Int64();
            // no problem with conversions - just creating a magic number for content id
            random = Math::Rand( seed );
            iTempBuffer.Des().Num( random );
            iMimeHeaders->SetContentIdL( iTempBuffer.Des() );
            iRootContentId.Set( iTempBuffer.Des() );
            }
        }
    
    // Now we have a root content id if the message is multipart/related.
    // we can set up the id for the root part after the attachment has been
    // created
    
    iPosition = iDataStart; // binary data starts here
    
    // Now we must try to generate a filename for the attachment.
    // A name must be generated before the actual attachment is saved
    
    TPtrC temp = TPtrC();
    TPtrC ptr;

    TBool isContentLocationFileName = EFalse;
    TPtrC8 dummy8;

    // Try name parameter of content location first
    if ( !isContentLocationFileName )
        {
        dummy8.Set( iMimeHeaders->GetContentTypeValue( KWspNameString ) );
        isContentLocationFileName =  MakeFilenameL( dummy8, iParse, TPtrC() );
        }

    // Try filename parameter of content-disposition header next
    TInt i;
    // every other string is parameter, every other one is value
    for ( i = 0;
        i < iMimeHeaders->ContentDispositionParams().MdcaCount() && !isContentLocationFileName;
        i += KMms2 )
        {
        // Try to generate a filename from the filename parameter
        if ( iMimeHeaders->ContentDispositionParams().MdcaPoint( i ).CompareF(
            KWspFilenameString ) == 0 )
            {
            dummy8.Set( iMimeHeaders->ContentDispositionParams().MdcaPoint( i + 1 ) );
            isContentLocationFileName = MakeFilenameL( dummy8, iParse, TPtrC() );
            }
        }

    HBufC16* uri = NULL;
    if ( !isContentLocationFileName &&
        iMimeHeaders->ContentLocation().Length() > 0 )
        {
        // Try to generate a filename from the content location
        error = KErrNone;
        TRAP( error, ( uri = EscapeUtils::EscapeDecodeL( iMimeHeaders->ContentLocation() ) ) );
        if ( error == KErrNone )
            {
            temp.Set( uri->Des().Right( KMmsMaxFileName ) );
            }
        else
            {
            temp.Set( iMimeHeaders->ContentLocation().Right( KMmsMaxFileName ) );
            }
        error = iParse.Set( TPtrC(), &temp, NULL );
        if ( error == KErrNone )
            {
            isContentLocationFileName = iEntryWrapper->IsValidFilename( iParse.NameAndExt() );
            }
        }
    CleanupStack::PushL( uri );

    // if content location not specified, use name parameter
    // as content location. FIX for /// originated messages.
    if ( iMimeHeaders->ContentLocation().Length() == 0 )
        {
        dummy8.Set( iMimeHeaders->GetContentTypeValue( KWspNameString ) );
        // This may use some strange charset. Should be decoded.
        // We only use it if it appears to be ascii.
        if ( dummy8.Length() > 0 && IsStringSafe( dummy8 ) )
            {
            HBufC* name = HBufC::NewL( dummy8.Length() );
            CleanupStack::PushL( name );
            name->Des().Copy( dummy8 );
            iMimeHeaders->SetContentLocationL( name->Des() );
#ifndef _NO_MMSS_LOGGING_
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( name->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Content location from name parameter: %S"), &dummy );
#endif
            CleanupStack::PopAndDestroy( name );
            name = NULL;
            }
        }

    TBuf<KMms14> attaName; // max 4294967295 attas.
    _LIT( KRelated, "att");

    if ( !isContentLocationFileName )
        {
        // We don't have content location, we must invent a filename
        // We should try to generate a sensible extension based on content type
        // Now we will have no extension
        attaName.Num( iAttaNumber, EDecimal );
        attaName.Insert( 0, KRelated );

        i = 0;
        TBool matchFound = EFalse;
        TPtrC8 matchTablePointer;
        matchTablePointer.Set( KMmsFileExtensionMatchTable[i].tag );
        while ( matchTablePointer.Length() > 0 && !matchFound )
            {
            if ( iMimeHeaders->ContentSubType().FindF(
                TPtrC8( KMmsFileExtensionMatchTable[i].tag ) ) != KErrNotFound )
                {
                temp.Set( KMmsFileExtensionMatchTable[i].extension );
                matchFound = ETrue;
                }
            i++;
            matchTablePointer.Set( KMmsFileExtensionMatchTable[i].tag );
            }
        ptr.Set( attaName );
        if ( !matchFound )
            {
            // unknown type, no extension
            iParse.Set( TPtrC(), &ptr, NULL );
            }
        else
            {
            iParse.Set( TPtrC(), &ptr, &temp );
            }
        }
        
    // We are through with filename, and URI may go if it ever was allocated
    CleanupStack::PopAndDestroy( uri );
    

    // We have to use iParse.NameAndExt as a guess for filename
    // we cannot call CApaApplication::GenerateFileName without a path
    
    // The next thing to do is to create an empty file ready to accept
    // the attachment data.
    
    
    
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::ExtractContentTypeParametersL( const TPtrC8& aBuffer )
    {
    // we get here the parameter part of content type in the format
    // parameter1=value1;parameter2=value2
    TInt start = 0;
    TInt end = 0;
    TInt tokenEnd = 0;
    while ( start < aBuffer.Length() )
        {
        // remove leading spaces
        while ( start < aBuffer.Length() && aBuffer.Mid( start ).FindF( KSpace8 ) == 0 )
            {
            start++;
            }
        if ( start >= aBuffer.Length() )
            {
            return; // done, nothing left
            }
        // extract token
        end = aBuffer.Mid( start ).FindF( KEquals8 );
        if ( end == KErrNotFound )
            {
            end = aBuffer.Length(); 
            }
        else
            {
            end += start; // remember the original offset
            }
        tokenEnd = end - 1;
        // remove trailing spaces from token
        while ( tokenEnd - start > start && aBuffer.Mid( start, tokenEnd - start + 1 ).FindF( KSpace8 ) == tokenEnd - start )
            {
            tokenEnd--;
            }
        if ( aBuffer.Mid( start, tokenEnd - start + 1 ).Length() == 0 )
            {
            return; // garbage, give up
            }
        else
            {
            iMimeHeaders->ContentTypeParams().AppendL(
                aBuffer.Mid( start, tokenEnd - start + 1 ) );
            }
        // extract value
        start = end + 1;
        if ( start >= aBuffer.Length() )
            {
            iMimeHeaders->ContentTypeParams().AppendL( TPtrC8() );
            return;
            }
        // remove leading spaces
        while ( start < aBuffer.Length() && aBuffer.Mid( start ).FindF( KSpace8 ) == 0 )
            {
            start++;
            }
        if ( start >= aBuffer.Length() )
            {
            // there was no value for the parameter - must add an empty value
            iMimeHeaders->ContentTypeParams().AppendL( TPtrC8() );
            return; // done, nothing left
            }
        // A string or a quoted string left.
        // If quoted string, search for end quote.
        // If not quoted, search for next semicolon (delimiter between parameters)
        if ( aBuffer.Mid( start ).FindF( &KMmsStringQuote, 1 ) == 0 )
            {
            start++; // discard the quotes
            end = aBuffer.Mid( start ).FindF( &KMmsStringQuote, 1 );
            if ( end != KErrNotFound )
                {
                end += start; // remember the original offset
                }
            else
                {
                end = aBuffer.Length();
                }
            tokenEnd = end - 1;
            // skip to next delimiter
            end = aBuffer.Mid( tokenEnd + 1 ).FindF( KSemicolon8 );
            if ( end == KErrNotFound )
                {
                end = aBuffer.Length();
                }
            else
                {
                end += tokenEnd + 1; // remember the original offset
                }
            }
        else
            {
            end = aBuffer.Mid( start ).FindF( KSemicolon8 );
            if ( end == KErrNotFound )
                {
                end = aBuffer.Length();
                }
            else
                {
                end += start; // remember the original offset
                }
            tokenEnd = end - 1;
            // Remove trailing spaces from value.
            // If the value was a quoted string, spaces are part of value,
            // and must not be removed.
            while ( aBuffer.Mid( start, tokenEnd - start + 1 ).FindF( KSpace8 ) ==
                tokenEnd - start )
                {
                tokenEnd--;
                }
            }
        iMimeHeaders->ContentTypeParams().AppendL( aBuffer.Mid( start, tokenEnd - start + 1 ) );
        // keep trying 
        start = end + 1;
        }
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsDecode::MakeFilenameL( TPtrC8& aSource, TParse& aDestination, const TFileName& aPath )
    {
    if ( aSource.Length() == 0 )
        {
        return EFalse;
        }
    TBool retValue = EFalse;
    TInt error = KErrNone;
    HBufC* name = NULL;
    name = HBufC::NewL( aSource.Length() );
    CleanupStack::PushL( name );
    TPtr pointer = name->Des();
    pointer.Copy( aSource );

    if ( pointer.FindF( KEqualsQuestion16 ) != KErrNotFound )
        {
        // Try to resolve MIME encoding
        TRAP( error, TMmsGenUtils::DecodeAndConvertMessageHeaderL( aSource, pointer, iFs ) );
        }
    else
        {
        // not MIME, error is fake, this may be just fine
        error = KErrCorrupt;
        }

    if ( error != KErrNone )
        {
        // Try to resolve plain utf8
        error = CnvUtfConverter::ConvertToUnicodeFromUtf8( pointer, aSource );
        }

    if ( error != KErrNone )
        {
        // use as is
        name->Des().Copy( aSource );
        }

    TPtrC ptr;

    if ( pointer.Length() > 0 )
        {
#ifndef _NO_MMSS_LOGGING_
        ptr.Set( name->Des().Left( KMmsMaxLogStringLength ) );
        TMmsLogger::Log( _L("- decoded name: %S"), &ptr );
#endif
        // We limit the length in case the filename is way too long (max is 100 characters).
        // We take the rightmost part to get the extension.
        // If we drop something from the beginning, it may be path or beginning of an overly
        // long name. As the too long name does not make sense anyway, we are free to drop
        // any part we like. No filenames should be 100 characters long.
        ptr.Set( name->Des().Right( KMmsMaxFileName ) );
        error = aDestination.Set( aPath, &ptr, NULL );
        if ( error == KErrNone )
            {
            // If the string we are trying to set to TParse is not valid,
            // we cannot check for filename
            retValue = iEntryWrapper->IsValidFilename( aDestination.NameAndExt() );
            }
        }
        
    CleanupStack::PopAndDestroy( name );
    return retValue;
    }


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

    // We don't check the critical disk space level here.
    // If we got this far, we cannot balk now,
    // we have to continue to the bitter end.

    // If root attachment id == 0, the root has been deleted.
    // In that case the message structure has been messed up
    // and we can only show separate objects
    
    // use iStore member
    if ( !iStore )
        {
        iStore = iEntryWrapper->EditStoreL();
        }
    
    iMmsHeaders->RestoreL( *iStore );
    
    if ( iRootContentId.Length() > 0 &&
        iRootAttachmentId != 0 )
        {
        iMmsHeaders->SetRootContentIdL( iRootContentId );
        iMmsHeaders->SetMessageRoot( iRootAttachmentId );
        iMmsHeaders->StoreL( *iStore );
        }

    // this also commits all the attachments
    iStore->CommitL();
    
    // If we are in chunked mode we must get the possible fake subject from a file
    // because buffer gets reused.
    
    // now everything should be committed, and store can go
    delete iStore;
    iStore = NULL;

    // calculate the total size of message
    iTotalSize += iMmsHeaders->Size();

    TPtrC details = TPtrC();
    TPtrC description = TPtrC();

    // Neither sender nor subject might be present.
    // We ought to extract a few characters from the beginning of the
    // first text/plain part for the subject, if no separate subject is available

    // if the sender is a phone number add alias.
    // We don't add alias for email addresses here, as the comms
    // database search for email addresses is very slow
    // if there are lots of contacts.

    HBufC* buffer = HBufC::NewL( KMmsMaxDescription );
    CleanupStack::PushL( buffer );
    TPtr pBuffer = buffer->Des();
    HBufC* descriptionBuffer = NULL; // we may or may not need this

    if ( TMmsGenUtils::GenerateDetails( iMmsHeaders->Sender(),
        pBuffer, KMmsMaxDescription, iFs ) == KErrNone )
        {
        details.Set( pBuffer );
        }
    else
        {
        // We come here only if there was a fatal error in GenerateDetails.
        // Even if we don't find the alias, we have something in the string
        details.Set( iMmsHeaders->Sender() );
        }

    // the buffer cannot be reused, because tEntry only contains
    // a pointer to our data, not a copy of it.
    if ( iMmsHeaders->Subject().Length() > 0 )
        {
        description.Set( iMmsHeaders->Subject() );
        }
    else
        {
        // Check if we have found a text/plain part to be used as description.
        // Only use it if there is only one alternative, otherwise we don't
        // know which one to use.
        if ( iFakeSubject > 0 && iPlainTexts == 1 && iFakeSubject < iDecodeBuffer->Size() )
            {
            // we have a pointer to text/plain part
            // we need a new buffer as our old buffer should not be destroyed

            descriptionBuffer = HBufC::NewL( KMmsMaxDescription );
            CleanupStack::PushL( descriptionBuffer );
            pBuffer.Set( descriptionBuffer->Des() );

            if ( iCharacterSet == KMmsIso10646Ucs2 ||
                iCharacterSet == KMmsUTF16 ||
                iCharacterSet == KMmsUTF16BE ||
                iCharacterSet == KMmsUTF16LE)
                {
                // This may be big-endian or little-endian
                TBool nativeEndian = EFalse; // pessimist.
                TInt length = iTextPlainLength;
                if ( length > KMmsMaxDescription * KMms2 )
                    {
                    length = KMmsMaxDescription * KMms2; // this should be enough
                    }
                TInt i;
                TUint start = 0;
                TUint16 word = 0;
                iPosition = iFakeSubject;
                // must be able to read at least 2 bytes
                if ( iPosition < iLength - 1 )
                    {
                    iDecodeBuffer->Read( iPosition, &word, KMmsMinHeaderLength );
                    }

                // Check if we have a BOM (byte order mark).
                if ( word == KMmsByteOrderMark )
                    {
                    nativeEndian = ETrue;
                    iPosition += KMms2;
                    start += KMms2; // skip the endianness byte from the subject
                    }
                else if ( word == KMmsReversedByteOrderMark )
                    {
                    nativeEndian = EFalse;
                    iPosition += KMms2;
                    start += KMms2; // skip the endianness byte from the subject
                    }
                else
                    {
                    CBufFlat* shortBuffer = CBufFlat::NewL( KMms2 );
                    CleanupStack::PushL( shortBuffer );
                    shortBuffer->ResizeL( KMms2 );
                    // no need to put it into cleanupstack... we dont't leave before we delete it
                    TUint8 byte;
                    // put little-endian BOM to buffer
                    byte = 0xFF;
                    shortBuffer->Write( 0, &byte, 1 );
                    byte = 0xFE;
                    shortBuffer->Write( 1, &byte, 1 );
                    // get the result into a word, and see if it came out straight or reversed
                    shortBuffer->Read( 0, &word, KMms2 );
                    CleanupStack::PopAndDestroy( shortBuffer );
                    shortBuffer = NULL;
                    if ( iCharacterSet == KMmsUTF16LE )
                        {
                        // I think we are little endian, better check to be sure
                        if ( word == KMmsByteOrderMark )
                            {
                            // we are little endian, charset is little endian
                            nativeEndian = ETrue;
                            }
                        else
                            {
                            nativeEndian = EFalse;
                            }
                        }
                    else if ( iCharacterSet == KMmsUTF16BE )
                        {
                        if ( word == KMmsByteOrderMark )
                            {
                            // we are little endian, charset is big endian
                            nativeEndian = EFalse;
                            }
                        else
                            {
                            nativeEndian = ETrue;
                            }
                        }
                    // if NO BOM and character set does not define byte ordering,
                    // we don't really know.
                    // Native endian is left as false - may not be the correct guess.
                    else
                        {
                        // do nothing - keep LINT happy
                        }
                    }

                if ( nativeEndian )
                    {
                    for ( i = start; i < length - 1; i += KMms2 )
                        {
                        iDecodeBuffer->Read(iPosition, &word, KMms2 );
                        iPosition += KMms2;
                        pBuffer.Append( word );
                        }
                    }
                else
                    {
                    for ( i = start; i < length - 1; i += KMms2 )
                        {
                        iDecodeBuffer->Read(iPosition, &word, KMms2 );
                        iPosition += KMms2;
                        TUint16 temp;
                        temp = TUint16 ( word & 0xff ) ; //lower byte
                        word >>= KMmsBitsInByte; // higher to lower
                        word = TUint16 ( ( temp << KMmsBitsInByte ) + word ); 
                        pBuffer.Append( word );
                        }
                    }

                }
            else if ( iCharacterSet == KMmsUsAscii )
                {
                TInt length = iTextPlainLength;
                if ( length > KMmsMaxDescription )
                    {
                    length = KMmsMaxDescription; // this should be enough
                    }
                TPtrC8 attaData;
                attaData.Set( iDecodeBuffer->Ptr( iFakeSubject ).Ptr(), length );
                pBuffer.Copy( attaData );
                }
            else if ( iCharacterSet == KMmsUtf8 )
                {
                TInt length = iTextPlainLength;
                if ( length > KMmsMaxDescription )
                    {
                    length = KMmsMaxDescription; // this should be enough
                    }
                // just in case our message is corrupted...
                if ( length > (TInt) ( iDecodeBuffer->Size() - iFakeSubject ) )
                    {
                    length = iDecodeBuffer->Size() - iFakeSubject;
                    }
                TPtrC8 attaData;
                attaData.Set( iDecodeBuffer->Ptr( iFakeSubject ).Ptr(), length );
                if ( length > 0 )
                    {
                    CnvUtfConverter::ConvertToUnicodeFromUtf8( pBuffer, attaData );
                    if ( pBuffer.Length() > 0 && pBuffer.Find( &KMmsByteOrderMark, 1 ) == 0 )
                        {
                        // remove the BOM
                        pBuffer.Delete( 0, 1 );
                        }
                    }
                }
            else
                {
                // we have some unknown character set.
                // we should call some converter
                }
            TInt position;
            for ( position = 0; position < pBuffer.Length(); position++ )
                {
                if ( pBuffer.Mid( position, 1 ) < KSpace16 ||
                    pBuffer.Mid( position, 1 ) == KMmsUnicodeLineSeparator ||
                    pBuffer.Mid( position, 1 ) == KMmsUnicodeParagraphSeparator ||
                    pBuffer.Mid( position, 1 ) == KMmsIdeographicSpace ||
                    ((TChar)pBuffer[position]).IsControl() )
                    {
                    pBuffer.Replace( position, 1, KSpace16 );
                    }
                }
                
            
            pBuffer.TrimAll();
            description.Set( pBuffer );
            }
        }
     
    // don't set the response status text to description if the status is OK.
    // In that case we have received an empty message that should have no description.
    // In OK case the text is mostly "OK" or something equally uninformative    
    if ( description.Length() == 0 && iMmsHeaders->ResponseText().Length() > 0 &&
        iMmsHeaders->ResponseStatus() > KMmsResponseStatusOK )
        {
        description.Set( iMmsHeaders->ResponseText() );
        }

    TUint32 messageType = 0;

    switch ( iMmsHeaders->MessageType() )
        {
        case KMmsMessageTypeMSendReq:
            messageType = KMmsMessageMSendReq;
            break;
        case KMmsMessageTypeMNotificationInd:
            messageType = KMmsMessageMNotificationInd;
            break;
        case KMmsMessageTypeMRetrieveConf:
            messageType = KMmsMessageMRetrieveConf;
            break;
        case KMmsMessageTypeDeliveryInd:
            messageType = KMmsMessageDeliveryInd;
            break;
        default:
            // Unrecognized type - or one we are not interested in.
            // Messages that should not normally be stored on disk
            // have the type "unrecognized"
            // The type in TMsvEntry should be used for showing icons
            // or other special purposes only
            messageType = KMmsMessageUnrecognized;
            break;
        }

    // All data to iMtmData1 is set to an empty base.
    // If caller has more flags to set, they must be set afterwards
    TInt32 mtmData = 0;
    mtmData |= messageType;
    mtmData |= iDRMFlags;
    if ( iMmsHeaders->MessageClass() == EMmsClassAdvertisement )
        {
        mtmData |= KMmsMessageAdvertisement;
        }
    else if ( iMmsHeaders->MessageClass() == EMmsClassInformational )
        {
        mtmData |= KMmsMessageInformational;
        }
    else
        {
        // do nothing - keep LINT happy
        }

    TMsvEntry entry;
    
    iEntryWrapper->GetIndexEntry( entry );
    
    entry.iDetails.Set( details );
    entry.iDescription.Set( description );
    entry.iMtmData1 = mtmData;
    entry.iSize = iTotalSize;
    // Set the date.
    // This can in principle be either the arrival time of the message
    // or the original time the message was received by MMSC
    
    entry.iDate = iMmsHeaders->ReceivingTime();
    
    switch ( iMmsHeaders->MessagePriority() )
        {
        case KMmsPriorityNormal:
            entry.SetPriority( EMsvMediumPriority );
            break;
        case KMmsPriorityLow:
            entry.SetPriority( EMsvLowPriority );
            break;
        case KMmsPriorityHigh:
            entry.SetPriority( EMsvHighPriority );
            break;
        default:            
            // if not defined default is normal
            entry.SetPriority( EMsvMediumPriority );
            break;
        }
        
    // set the bio type to audio message if the message contains nothing but one
    // amr part in addition to possible one smil
    
    if ( iSmilCount <= 1 && iAudioCount == 1 &&
       ( iAttaNumber == iSmilCount + iAudioCount ) )
        {
         if(!CheckDRMContentL())
             {
                TBool audioSupported = EFalse;
                TRAP_IGNORE(
                    {
                    FeatureManager::InitializeLibL();
                    audioSupported = FeatureManager::FeatureSupported( KFeatureIdAudioMessaging );
                    FeatureManager::UnInitializeLib();
                    });
                
                if ( audioSupported )
                    {
                    entry.iBioType = KUidMsgSubTypeMmsAudioMsg.iUid;
                    }
             }
        }

    //Since the changes have been committed to the store , we should get exact attachment count from the store. 
    //The message type check is not required as the only messagtypes decoded are retrieve-conf in global mode and send-req in local mode
    TInt attachmentCount = 0;
    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );

    // Only new attachment structure is supported    
    MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
    attachmentCount = attachMan.AttachmentCount();

    CleanupStack::PopAndDestroy( store );
    if ( attachmentCount > 0 )
        {
        entry.SetAttachment(ETrue);
        }
       
    iEntryWrapper->ChangeIndexEntry( entry );    

    // now that we have saved our index entry, the buffers can go
    if ( descriptionBuffer )
        {
        // if we have allocated a separate buffer for the
        // description, it sits on cleanups stack too.
        CleanupStack::PopAndDestroy( descriptionBuffer );
        }
    CleanupStack::PopAndDestroy( buffer );

    }

// ---------------------------------------------------------
// CMmsDecode::CheckDRMContent
// ---------------------------------------------------------
//
TBool CMmsDecode::CheckDRMContentL()
    {
    CFileProtectionResolver* resolver = CFileProtectionResolver::NewLC( iFs);

    CMsvStore* store = iEntryWrapper->ReadStoreL();
    CleanupStack::PushL( store );
    MMsvAttachmentManager& attaManager = store->AttachmentManagerL();    
    
    TInt attaCount = attaManager.AttachmentCount();
    TInt flCount = 0;
    TInt sdCount = 0;

    for ( TInt i = 0; i < attaCount; i++ )
        {
        CMsvAttachment* info = attaManager.GetAttachmentInfoL( i );
        CleanupStack::PushL( info );
        TDataType dataType( info->MimeType() );
        if(dataType.Des8().Match(KmmsSmilMimeType) != KErrNotFound)
            {
            CleanupStack::PopAndDestroy( info );
            continue;
            }
        RFile file = attaManager.GetAttachmentFileL( info->Id() );
        CleanupClosePushL( file );
        
        TInt status = resolver->ProtectionStatusL( file, dataType );
        if ( status & EFileProtForwardLocked ||
             status & EFileProtClosedContent )
            {
            flCount++;
            }
        else if ( status & EFileProtSuperDistributable )
            {
            sdCount++;
            }

        CleanupStack::PopAndDestroy( 2, info ); // file, info
        }

    CleanupStack::PopAndDestroy( 2, resolver ); //store, resolver
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- CheckDRMContent:Total count :%d,FlCount: %d"),attaCount, flCount );
    TMmsLogger::Log( _L("- CheckDRMContent:SDCount: %d"), sdCount );
#endif
    if(flCount ||sdCount )
    return ETrue;
    else
    return EFalse;
    }
 
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DumpL()
    {
    // no dump if not logging
#ifndef _NO_MMSS_LOGGING_
    TInt error = KErrNone;
    RFile file;
    TUint att;
    _LIT( KRelated, "Rec.mms");
    // if no can do, sorry.
    TRAP( error,
        {
        if ( ( iDumpIncoming ) && iDecodeBuffer )
            {
            if ( iDecodeBuffer->Size() > 0 && iDecodingStage == EMmsNotApplicable )
                {
                // non-chunked mode. All data is in buffer, and can be dumped in one go
                iFileName = KMmsDefaultLogDirectory;
                // Check if directory exists - no directory, no dump
                User::LeaveIfError( iFs.Att( iFileName, att ) );
                iParse.Set( iFileName, &KRelated, NULL );
                iFileName = iParse.FullName();
                User::LeaveIfError( CApaApplication::GenerateFileName(
                    iFs, iFileName ) );
                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 = iDecodeBuffer->Ptr( 0 );
                file.Write( ptr );
                file.Flush();

                // done - close files
                file.Close();
                }
            // chunked mode start - no data yet, initialize file
            else if ( iLength == 0 && iDecodingStage == EMmsHeaders )
                {
                // This is the initialization of the dump file
                iFileName = KMmsDefaultLogDirectory;
                // create an empty file
                // The name of the file will remain in iFilename
                // Later data will be appended to the same file
                User::LeaveIfError( iFs.Att( iFileName, att ) );
                iParse.Set( iFileName, &KRelated, NULL );
                iFileName = iParse.FullName();
                User::LeaveIfError( CApaApplication::GenerateFileName(
                    iFs, iFileName ) );
                User::LeaveIfError( file.Create( iFs, iFileName,
                    EFileWrite | EFileShareExclusive ) );
                // for message id generation
                iParse.Set( iFileName, NULL, NULL );
                file.Close();
                }
            else if ( iDecodingStage != EMmsNotApplicable )
                {
                User::LeaveIfError( file.Open( iFs, iFileName,
                    EFileWrite | EFileShareExclusive ) );
                TInt position = 0;    
                User::LeaveIfError( file.Seek( ESeekEnd, position ) );
                // the data is supposed to be in the encode buffer
                // But only write the amount of data that has been processed.
                // Some of the data may be pushed back to the beginning of the buffer
                // and we do not want to write such data twice.
                // iOldData tells how much data was left over from previous round
                // We always write as much as we have got so that we can analyze
                // a possible error
                TPtr8 ptr = iDecodeBuffer->Ptr( iOldData );
                // If there is any data, write it    
                if ( ptr.Length() > 0 )
                    {
                    file.Write( ptr );
                    file.Flush();
                    }

                // done - close files
                file.Close();
                }
            else
                {
                // do nothing
                }
            }
        }
    );
    if ( error != KErrNone )
        {
        TMmsLogger::Log( _L("- Dump left, error : %d"), error ); 
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DumpSmil( TInt aDataLength )
    {
    // no dump if not logging
#ifndef _NO_MMSS_LOGGING_

    TBool lowMemory = EFalse;
    
    TRAP_IGNORE({
        lowMemory = iEntryWrapper->DiskSpaceBelowCriticalLevelL( aDataLength );
        });
        
    // Only dump smil part if not decoding in chunks.
    // In chunked mode it would be too complicated.
    if ( iDumpIncoming && iMimeHeaders->ContentType().CompareF( KMmsApplicationSmil ) == 0
        && !lowMemory && iDecodingStage == EMmsNotApplicable)
        {
        RFile file;
        iFileName = KMmsDefaultLogDirectory;
        TUint att;
        if ( iFs.Att( iFileName, att ) == KErrNone )
            {
            _LIT( KRelated2, "smil.txt");
            iParse.Set( iFileName, &KRelated2, NULL );
            iFileName = iParse.FullName();
            TInt error = CApaApplication::GenerateFileName( iFs, iFileName );
            if ( error == KErrNone )
                {
                error = file.Create( iFs, iFileName, EFileWrite | EFileShareExclusive );
                // for message id generation
                iParse.Set( iFileName, NULL, NULL );

                if ( error == KErrNone )
                    {
                    // the data is supposed to be in the encode buffer
                    TPtrC8 ptrx;
                    ptrx.Set( iDecodeBuffer->Ptr( iPosition ).Ptr(), aDataLength );
                    file.Write( ptrx );
                    file.Flush();
                    }

                // done - close files
                file.Close();
                }
            }
        }
#endif
    }
    

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::LogYesNo( TRefByValue<const TDesC> aTitle, TInt aValue )
    {
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( aTitle );
    switch ( aValue )
        {
        case ( KMmsYes ):
            TMmsLogger::Log( KLogYes );
            break;
        case ( KMmsNo ):
            TMmsLogger::Log( KLogNo );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, aValue );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::GetKeywordL()
    {
    GetValueLength(); // must be read just to get it out of way
    TUint8 byte;            // for simple reads
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte; // should be add, remove or filter
    HBufC16* buffer = NULL;
    buffer = GetEncodedTextStringL();
    CleanupStack::PushL( buffer ); // keyword
    TPtrC dummy;
    dummy.Set( buffer->Des() );

    iMmsHeaders->MMBoxMessageHeadersL().AppendKeywordItemL( temp, dummy );
#ifndef _NO_MMSS_LOGGING_
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Keyword: %S"), &dummy );
    switch ( temp )
        {
        case KMmsAddToken:
            TMmsLogger::Log( _L("-- Add") );
            break;
        case KMmsRemoveToken:
            TMmsLogger::Log( _L("-- Remove") );
            break;
        case KMmsFilterToken:
            TMmsLogger::Log( _L("-- Filter") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::CompleteSelf( TInt aError )
    {
    // completed own status to get back to the RunL
    iStatus = KRequestPending;
    SetActive();
    TRequestStatus* status = &iStatus;
    User::RequestComplete( status, aError );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
TBool CMmsDecode::IsStringSafe( const TDesC8& 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;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeFromHeaderL()
    {
    HBufC16* buffer = NULL;
    buffer = DecodeFromL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->SetSenderL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- From: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeToL()
    {
    HBufC16* buffer = NULL;
    buffer = DecodeAddressL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->AddTypedAddresseeL( buffer->Des(), EMmsTo );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- To: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeCcL()
    {
    HBufC16* buffer = NULL;
    buffer = DecodeAddressL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->AddTypedAddresseeL( buffer->Des(), EMmsCc );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Cc: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
        
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeBccL()
    {
    HBufC16* buffer = NULL;
    buffer = DecodeAddressL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->AddTypedAddresseeL( buffer->Des(), EMmsBcc );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Bcc: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeContentLocationHeaderL()
    {
    TPtrC8 byteString;
    TUint32 size = 0;
    // Request headers are handled for testing purposes only
    // Only PDUs that we should decode are the confirmation types
    if ( iMmsHeaders->MessageType() != KMmsMessageTypeMBoxDeleteConf &&
        iMmsHeaders->MessageType() != KMmsMessageTypeDeleteConf )
        {
        byteString.Set( GetByteString() );
        // for the MMBox view type PDUs the content locations go into the array
        if ( iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewConf ||
             iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewReq ||
             iMmsHeaders->MessageType() == KMmsMessageTypeMBoxDeleteReq ||
             iMmsHeaders->MessageType() == KMmsMessageTypeDeleteReq )
            {
            iMmsHeaders->MMBoxMessageHeadersL().ContentLocationList().AppendL(
                byteString );
            }
        else if ( iMmsHeaders->ContentLocation().Length() == 0 )
            {
            iMmsHeaders->SetContentLocationL( byteString );
            }
        else
            {
            // This is some message type that allows more than one content location
            // The only one should be M-Mbox-Delete.req.
            if ( iMmsHeaders->MMBoxMessageHeadersL().ContentLocationList().MdcaCount()
                 == 0 )
                {
                // nothing in the list, append the old one, too
                iMmsHeaders->MMBoxMessageHeadersL().ContentLocationList().AppendL(
                    iMmsHeaders->ContentLocation() );
                }
            iMmsHeaders->MMBoxMessageHeadersL().ContentLocationList().AppendL(
                byteString );
            }
        }
    else // KMmsMessageTypeMBoxDeleteConf has different format
        {
        GetValueLength();
        size = GetLongOrShortInteger(); // this is actually index
        byteString.Set( GetByteString() );
        iMmsHeaders->InsertDeleteContentLocationL( size, byteString );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Content Location index: %d"), size );
#endif
        }
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Content Location: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeDateHeaderL()
    {
    TInt64 date;
    TUint8 byte;
    date = GetVeryLongInteger();
    if ( iError == KErrTooBig )
        {
        // we get here only if we have at least one byte to read
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        // skip rest of field if too long
        iPosition += byte;
        }
    if ( iPosition >= iLength )
        {
        iError = KErrCorrupt;
        return;
        }
    iMmsHeaders->SetDate( date );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Date:") );
    LogDateL( date );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeDeliveryReportHeader()
    {
    TUint8 byte;
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetDeliveryReport( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- DeliveryReport:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeDeliveryTimeL()
    {
    TUint8 byte;
    TInt64 date;
    byte = GetRelativeOrAbsoluteTime( date );
    if ( byte == KMmsRelativeToken )
        {
        iMmsHeaders->SetDeliveryTimeInterval( I64LOW( date ) );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Delivery Time, interval %d s"), I64LOW( date ) );
#endif
        }
    if ( byte == KMmsAbsoluteToken )
        {
        // keep as universal time, don't convert to local time
        iMmsHeaders->SetDeliveryDate( date );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Delivery Time, absolute ") );
        LogDateL( date );
#endif
        }
    // If could not interpret field, don't set anything.
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeExpiryL()
    {
    TUint8 byte;
    TInt64 date;
    byte = GetRelativeOrAbsoluteTime( date );
    if ( byte == KMmsRelativeToken )
        {
        iMmsHeaders->SetExpiryInterval( I64LOW( date ) );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Expiry interval: %d s"), I64LOW( date ) );
#endif
        }
    if ( byte == KMmsAbsoluteToken )
        {
        // keep as universal time, don't convert to local time
        iMmsHeaders->SetExpiryDate( date );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Expiry, absolute:") );
        LogDateL( date );
#endif
        }
    // If could not interpret field, don't set anything.
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageClass()
    {
    TUint8 byte = 0;
    TUint temp;

    iDecodeBuffer->Read(iPosition, &byte, 1);
        
    if ( byte >= KMms0x80 )
        {
        iPosition++;
        temp = byte;
        iMmsHeaders->SetMessageClass( temp );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("-- MessageCalss encoded as Class-identifier") );
#endif
        }
    else    
        {
        TPtrC8 byteString = GetByteString();
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("-- MessageCalss encoded as Token-text") );
        // we don't leave because of logging
        TInt error = KErrNone;
        TRAP( error, 
            {
            HBufC16* buffer = NULL;
            buffer = HBufC16::NewLC( byteString.Length() );
            buffer->Des().Copy( byteString );
            TPtrC dummy;
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Message class : %S"), &dummy ); 
            CleanupStack::PopAndDestroy( buffer );
            });
        if ( error != KErrNone )
            {
            TMmsLogger::Log( _L("- Decode left when logging Message class, error : %d"),
                error );
            }
#endif   
         temp = KMmsTestIllegalValue;
         if ( byteString.CompareF( KMessageClassPersonal ) == 0 )
             {
             temp = KMmsMessageClassPersonal;
             }
         else if ( byteString.CompareF( KMessageClassAdvertisement ) == 0 )
             {
             temp = KMmsMessageClassAdvertisement;
             }  
         else if ( byteString.CompareF( KMessageClassInformational ) == 0 )
             {
             temp = KMmsMessageClassInformational;
             }
         else if ( byteString.CompareF( KMessageClassAuto ) == 0 )
             {
             temp = KMmsMessageClassAuto;
             }
             iMmsHeaders->SetMessageClass( temp );
         }
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Message Class:") );
    switch ( temp )
        {
        case KMmsMessageClassPersonal:
            TMmsLogger::Log( _L("-- Personal") );
            break;
        case KMmsMessageClassAdvertisement:
            TMmsLogger::Log( _L("-- Advertisement") );
            break;
        case KMmsMessageClassInformational:
            TMmsLogger::Log( _L("-- Informational") );
            break;
        case KMmsMessageClassAuto:
            TMmsLogger::Log( _L("-- Auto") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageIdL()
    {
    TPtrC8 byteString;
    byteString.Set( GetByteString() );
    iMmsHeaders->SetMessageIdL( byteString );
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Message Id: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageType()
    {
    TUint8 byte;
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetMessageType( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- MessageType:") );
    switch ( temp )
        {
        case KMmsMessageTypeMSendReq:
            TMmsLogger::Log( _L("-- send-req") );
            break;
        case KMmsMessageTypeMSendConf:
            TMmsLogger::Log( _L("-- send-conf") );
            break;
        case KMmsMessageTypeMNotificationInd:
            TMmsLogger::Log( _L("-- notification-ind") );
            break;
        case KMmsMessageTypeMNotifyRespInd:
            TMmsLogger::Log( _L("-- notifyresp-ind") );
            break;
        case KMmsMessageTypeMRetrieveConf:
            TMmsLogger::Log( _L("-- retrieve-conf") );
            break;
        case KMmsMessageTypeAcknowledgeInd:
            TMmsLogger::Log( _L("-- acknowledge-ind") );
            break;
        case KMmsMessageTypeDeliveryInd:
            TMmsLogger::Log( _L("-- delivery-ind") );
            break;
        case KMmsMessageTypeReadRecInd:
            TMmsLogger::Log( _L("-- read-rec-ind") );
            break;
        case KMmsMessageTypeReadOrigInd:
            TMmsLogger::Log( _L("-- read-orig-ind") );
            break;
        case KMmsMessageTypeForwardReq:
            TMmsLogger::Log( _L("-- forward-req") );
            break;
        case KMmsMessageTypeForwardConf:
            TMmsLogger::Log( _L("-- forward-conf") );
            break;
        case KMmsMessageTypeMboxStoreReq:
            TMmsLogger::Log( _L("-- m-mbox-store-req") );
            break;
        case KMmsMessageTypeMboxStoreConf:
            TMmsLogger::Log( _L("-- m-mbox-store-conf") );
            break;
        case KMmsMessageTypeMboxViewReq:
            TMmsLogger::Log( _L("-- m-mbox-view-req") );
            break;
        case KMmsMessageTypeMboxViewConf:
            TMmsLogger::Log( _L("-- m-mbox-view-conf") );
            break;
        case KMmsMessageTypeMBoxUploadReq:
            TMmsLogger::Log( _L("-- m-mbox-upload-req") );
            break;
        case KMmsMessageTypeMBoxUploadConf:
            TMmsLogger::Log( _L("-- m-mbox-upload-conf") );
            break;
        case KMmsMessageTypeMBoxDeleteReq:
            TMmsLogger::Log( _L("-- m-mbox-delete-req") );
            break;
        case KMmsMessageTypeMBoxDeleteConf:
            TMmsLogger::Log( _L("-- m-mbox-delete-conf") );
            break;
        case KMmsMessageTypeMBoxDescr:
            TMmsLogger::Log( _L("-- m-mbox-descr") );
            break;
        case KMmsMessageTypeDeleteReq:
            TMmsLogger::Log( _L("-- m-delete-req") );
            break;
        case KMmsMessageTypeDeleteConf:
            TMmsLogger::Log( _L("-- m-delete-conf") );
            break;
        case KMmsMessageTypeCancelReq:
            TMmsLogger::Log( _L("-- m-cancel-req") );
            break;
        case KMmsMessageTypeCancelConf:
            TMmsLogger::Log( _L("-- m-cancel-conf") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMmsVersion()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    byte &= KMms0x7F;
    // if version major number is greater than 1,
    // we may not be compatible any more.
    // We save the version number, and check later
    // what we should do about it.
    iMmsHeaders->SetMmsVersion( byte );
#ifndef _NO_MMSS_LOGGING_
    // log version number always - even when decode logging is off
    TUint high = ( byte & KMms0x7F ) >> KMmsHalfByteShift;
    TUint low = byte & KMms0x0F;
    TMmsLogger::Log( _L("- MMS version: %d.%d"), high, low );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageSize()
    {
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    iMmsHeaders->SetMessageSize( size );
#ifndef _NO_MMSS_LOGGING_
    if ( iError != KErrTooBig )
        {
        TMmsLogger::Log( _L("- Message Size: %d"), size );
        }
    else
        {
        TMmsLogger::Log( _L("- Message Size bigger than maxint") );
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodePriority()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetMessagePriority( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Priority:") );
    switch ( temp )
        {
        case KMmsPriorityLow:
            TMmsLogger::Log( _L("-- Low") );
            break;
        case KMmsPriorityNormal:
            TMmsLogger::Log( _L("-- Normal") );
            break;
        case KMmsPriorityHigh:
            TMmsLogger::Log( _L("-- High") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReadReply()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetReadReply( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- Read Reply:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReportAllowed()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetReportAllowed( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- Report Allowed:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
#ifndef _NO_MMSS_LOGGING_
void CMmsDecode::DecodeResponseStatusL( TInt aHeader )
#else
void CMmsDecode::DecodeResponseStatusL( TInt /*aHeader*/ )
#endif
    {
    TUint8 byte;
    TUint temp;
    if ( iMmsHeaders->MessageType() != KMmsMessageTypeMBoxDeleteConf )
        {
        byte = GetWellKnownFieldValueOrSkip();
        temp = byte;
        iMmsHeaders->SetResponseStatus( temp );
        }
    else  // KMmsMessageTypeMBoxDeleteConf has different format
        {
        TUint32 size = 0;
        GetValueLength();
        size = GetLongOrShortInteger(); // this is actually index
        byte = GetWellKnownFieldValueOrSkip();
        temp = byte;
        iMmsHeaders->InsertDeleteStatusL( size, temp );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Response Status Index: %d"), size );
#endif
        }
#ifndef _NO_MMSS_LOGGING_
    if ( aHeader == KMmsAssignedResponseStatus )
        {
        TMmsLogger::Log( _L("- Response Status:") );
        }
    else
        {
        TMmsLogger::Log( _L("- Retrieve Status:") );
        }
    switch ( temp )
        {
        case KMmsStatusOk:
            TMmsLogger::Log( _L("-- Ok") );
            break;
        case KMmsErrorUnspecified:
            TMmsLogger::Log( _L("-- ErrUnspecified") );
            break;
        case KMmsErrorServiceDenied:
            TMmsLogger::Log( _L("-- ErrServiceDenied") );
            break;
        case KMmsErrorMessageFormatCorrupt:
            TMmsLogger::Log( _L("-- ErrMessageFormatCorrupt") );
            break;
        case KMmsErrorSendingAddressUnresolved:
            TMmsLogger::Log( _L("-- ErrSendingAddressUnresolved") );
            break;
        case KMmsErrorMessageNotFound:
            TMmsLogger::Log( _L("-- ErrMessageNotFound") );
            break;
        case KMmsErrorNetworkProblem:
            TMmsLogger::Log( _L("-- ErrNetworkProblem") );
            break;
        case KMmsErrorNoContentAccepted:
            TMmsLogger::Log( _L("-- ErrNoContentAccepted") );
            break;
        case KMmsErrorUnsupportedMessage:
            TMmsLogger::Log( _L("-- ErrUnsupportedMessage") );
            break;
        default:
            if ( ( temp & KMmsErrorRangeMask ) == KMmsErrorTransient )
                {
                TMmsLogger::Log( _L("-- Transient error %d"), temp );
                }
            else if ( ( temp & KMmsErrorRangeMask ) == KMmsErrorPermanent )
                {
                TMmsLogger::Log( _L("-- Permanent error %d"), temp );
                }
            else
                {
                TMmsLogger::Log( KLogUnknown, temp );
                }
            break;
         }   
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeResponseTextL( TInt aHeader )
    {
    // The text may be response status text, retrieve status text
    // or status text depending on which PDU we are decoding.
    // Only one text string appears in each case, so we reuse the header
    HBufC16* buffer = NULL;
    if ( iMmsHeaders->MessageType() != KMmsMessageTypeMBoxDeleteConf )
        {
        // both notes go to the same variable, as both can never be present
        buffer = GetEncodedTextStringL();
        CleanupStack::PushL( buffer );
        iMmsHeaders->SetResponseTextL( buffer->Des() );
        }
    else // KMmsMessageTypeMBoxDeleteConf has different format
        {
        GetValueLength();
        TUint32 index = 0;
        index = GetLongOrShortInteger();
        buffer = GetEncodedTextStringL();
        CleanupStack::PushL( buffer );
        iMmsHeaders->InsertDeleteResponseTextL( index, buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Response Text Index: %d"), index );
#endif
        }
#ifndef _NO_MMSS_LOGGING_
    switch ( aHeader )
        {
        case KMmsAssignedResponseText:
            TMmsLogger::Log( _L("- Response text:") );
            break;           
        case KMmsAssignedRetrieveText:
            TMmsLogger::Log( _L("- Retrieve text:") );
            break;           
        case KMmsAssignedStatusText:
            TMmsLogger::Log( _L("- Status text:") );
            break;           
        }
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("%S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeSenderVisibility()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetSenderVisibility( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Sender Visibility:") );
    switch ( temp )
        {
        case KMmsSenderHide:
            TMmsLogger::Log( _L("-- Hide") );
            break;
        case KMmsSenderShow:
            TMmsLogger::Log( _L("-- Show") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeStatus()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetStatus( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Status:") );
    switch ( temp )
        {
        case KMmsMessageStatusExpired:
            TMmsLogger::Log( _L("-- Expired") );
            break;
        case KMmsMessageStatusRetrieved:
            TMmsLogger::Log( _L("-- Retrieved") );
            break;
        case KMmsMessageStatusRejected:
            TMmsLogger::Log( _L("-- Rejected") );
            break;
        case KMmsMessageStatusDeferred:
            TMmsLogger::Log( _L("-- Deferred") );
            break;
        case KMmsMessageStatusUnrecognized:
            TMmsLogger::Log( _L("-- Unrecognized") );
            break;
        case KMmsMessageStatusIndeterminate:
            TMmsLogger::Log( _L("-- Indeterminate") );
            break;
        case KMmsMessageStatusForwarded:
            TMmsLogger::Log( _L("-- Forwarded") );
            break;
        case KMmsMessageStatusUnreachable:
            TMmsLogger::Log( _L("-- Unreachable") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeSubjectL()
    {
    HBufC16* buffer = NULL;
    buffer = GetEncodedTextStringL();
    CleanupStack::PushL( buffer );
    // TMsvEntry.iDescription will be updated separately.
    iMmsHeaders->SetSubjectL( buffer->Des());
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Subject: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeTidL()
    {
    TPtrC8 byteString;
    byteString.Set( GetByteString() );
    iMmsHeaders->SetTidL( byteString );
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    TPtrC dummy;
    dummy.Set( buffer->Des() );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Transaction Id: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeContentTypeL()
    {
    // Now we must analyze the content type in order to
    // handle the body correctly.
    // The body may be a multipart, or a single part.
    // The subroutine will set the number of Attachments.
    // For a monoblock body the number of attachments will be 1.
    iMultipartType = GetMultipartContentTypeL();
    // save the multipart type in case we must later make decisions
    // about the multipart/alternative type
    iMmsHeaders->SetMultipartType( iMultipartType );
    iLastHeader = ETrue; // Content type is always the last header
#ifndef _NO_MMSS_LOGGING_
    TPtrC8 byteString;
    HBufC16* buffer = NULL;
    TPtrC dummy;
    if ( iMultipartType != 0 )
        {
        if ( iMultipartType <= KNumberContentTypes )
            {
            byteString.Set( KContentTypeTable[ iMultipartType ] );
            }
        else if ( iMultipartType == KMmsAssignedApplicationVndWapMultipartReport )
            {
            byteString.Set( KMmsWapMultipartReport );
            }
        else
            {
            byteString.Set( KContentTypeTable[ KNumberContentTypes ] ); 
            }
        buffer = HBufC16::NewLC( byteString.Length() );
        // we cannot log indefinitely long strings.
        // We get this long strings only if the message is corrupted.
        buffer->Des().Copy( byteString );
        // no entries from our table are more than 200 bytes long.
        dummy.Set( buffer->Des() );
        TMmsLogger::Log( _L("- Content Type: %S"), &dummy );
        CleanupStack::PopAndDestroy( buffer );
        if ( iMultipartRootType.Length() > 0 )
            {
            buffer = HBufC16::NewLC( iMultipartRootType.Length() );
            buffer->Des().Copy( iMultipartRootType );
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Root content type: %S"), &dummy );
            CleanupStack::PopAndDestroy( buffer );
            }
        if ( iRootContentId.Length() > 0 )
            {
            buffer = HBufC16::NewLC( iRootContentId.Length() );
            buffer->Des().Copy( iRootContentId );
            // we cannot log indefinitely long strings.
            // We get this long strings only if the message is corrupted.
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("- Root content id: %S"), &dummy );
            CleanupStack::PopAndDestroy( buffer );
            }
        }
    TMmsLogger::Log( _L("- Number of attachments: %d"), iNumberOfAttachments );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReadStatus()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetReadStatus( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- ReadStatus:") );
    switch ( temp )
        {
        case KMmsReadStatusRead:
            TMmsLogger::Log( _L("-- Read") );
            break;
        case KMmsReadStatusDeletedWithoutBeingRead:
            TMmsLogger::Log( _L("-- Deleted unread") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReplyCharging()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetReplyCharging( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- ReplyCharging:") );
    switch ( temp )
        {
        case KMmsReplyChargingRequested:
            TMmsLogger::Log( _L("-- Requested") );
            break;
        case KMmsReplyChargingRequestedTextOnly:
            TMmsLogger::Log( _L("-- Requested text only") );
            break;
        case KMmsReplyChargingAccepted:
            TMmsLogger::Log( _L("-- Accepted") );
            break;
        case KMmsReplyChargingAcceptedTextOnly:
            TMmsLogger::Log( _L("-- Accepted text only") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReplyChargingDeadlineL()
    {
    TUint8 byte;
    TInt64 date;
    byte = GetRelativeOrAbsoluteTime( date );
    if ( byte == KMmsRelativeToken )
        {
        iMmsHeaders->SetReplyChargingInterval( I64LOW( date ) );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Reply charging interval: %d s"), I64LOW( date ) );
#endif
        }
    if ( byte == KMmsAbsoluteToken )
        {
        // keep as universal time, don't convert to local time
        iMmsHeaders->SetReplyChargingDate( date );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- Reply charging date, absolute:") );
        LogDateL( date );
#endif
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReplyChargingIdL()
    {
    TPtrC8 byteString;
    byteString.Set( GetByteString() );
    iMmsHeaders->SetReplyChargingIdL( byteString );
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Reply Charging Id: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeReplyChargingSize()
    {
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    iMmsHeaders->SetReplyChargingSize( size );
#ifndef _NO_MMSS_LOGGING_
    if ( iError != KErrTooBig )
        {
        TMmsLogger::Log( _L("- Reply charging Size: %d"), size );
        }
    else
        {
        TMmsLogger::Log( _L("- Reply charging Size bigger than: %d"),
            TInt64 (0xffffffff) );
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodePreviousSenderL()
    {
    GetValueLength(); // we don't need this
    TUint32 size = 0;
    size = GetLongOrShortInteger(); // This is actually index
    HBufC16* buffer = NULL;
    buffer = DecodeAddressL();
    CleanupStack::PushL( buffer );
    // If message is corrupted, the result may be garbage, but we do our best
    iMmsHeaders->InsertPreviouslySentByL( size, buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Prevously sent by: %S, order # %d"), &dummy, size );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodePreviouslySentDateL()
    {
    GetValueLength(); // we don't need this
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    TInt64 date;
    date = GetVeryLongInteger();
    TUint8 byte;
    if ( iError == KErrTooBig )
        {
        // we get here only if we have at least one byte to read
        iDecodeBuffer->Read(iPosition, &byte, 1);
        iPosition++;
        // skip rest of field if too long
        iPosition += byte;
        // If this is too big, we just ignore it. It is no legal date.
        date = 0;
        iError = KErrNone;
        }
    // We insert the result no matter what
    iMmsHeaders->InsertPreviouslySentDateL( size, date );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Previously sent date: (order # %d)"), size );
    LogDateL( date );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeStoreHeaderL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->MMBoxMessageHeadersL().SetMmsStore( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- Store to MMBox:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMMBoxStateL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    if ( iMmsHeaders->MessageType() != KMmsMessageTypeMboxViewReq &&
        iMmsHeaders->MessageType() != KMmsMessageTypeMboxViewConf )
        {
        iMmsHeaders->MMBoxMessageHeadersL().SetMMState( temp );
        }
    else
        {
        // For the MMBox view PDU types we must put the states into
        // an array as they are used for filtering, and there may be
        // more than one state headers
        iMmsHeaders->MMBoxViewHeadersL().MMStateArray().InsertInOrder( temp );
        }
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- State in MMBox:") );
    switch ( temp )
        {
        case KMmsDraft:
            TMmsLogger::Log( _L("-- Draft") );
            break;
        case KMmsSent:
            TMmsLogger::Log( _L("-- Sent") );
            break;
        case KMmsNew:
            TMmsLogger::Log( _L("-- New") );
            break;
        case KMmsRetrieved:
            TMmsLogger::Log( _L("-- Retrieved") );
            break;
        case KMmsForwarded:
            TMmsLogger::Log( _L("-- Forwarded") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMMBoxStoreStatusL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->MMBoxMessageHeadersL().SetMmsStoreStatus( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- MMBox Store Status:") );
    switch ( temp )
        {
        case KMmsStatusOk:
            TMmsLogger::Log( _L("-- Ok") );
            break;
        default:
            if ( ( temp & KMmsErrorRangeMask ) == KMmsErrorTransient )
                {
                TMmsLogger::Log( _L("-- Transient error %d"), temp );
                }
            else if ( ( temp & KMmsErrorRangeMask ) == KMmsErrorPermanent )
                {
                TMmsLogger::Log( _L("-- Permanent error %d"), temp );
                }
            else
                {
                TMmsLogger::Log( KLogUnknown, temp );
                }
            break;
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMMBoxStoreStatusTextL()
    {
    HBufC16* buffer = NULL;
    buffer = GetEncodedTextStringL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->MMBoxMessageHeadersL().SetMmsStoreStatusTextL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- MMBox Store Status Text: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeStoredInMMBoxHeaderL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->MMBoxMessageHeadersL().SetMmsStored( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- Stored to MMBox:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeAttributesHeaderL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    User::LeaveIfError(iMmsHeaders->MMBoxViewHeadersL().AttributeArray().Append( temp ));
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- MMBox Attribute: %d"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeTotalsL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->MMBoxViewHeadersL().SetMmsTotals( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- MMBox Totals requested:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMboxTotalsL()
    {
    GetValueLength(); // we don't need this
    // get message quota or size quota
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    // actual value of the quota
    // We only get normal integer (not a 64 bit integer) as a 32 bit integer 
    // will give quota 4294967295 bytes or messages (which should be plenty)
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    // save message quota
    if ( byte == KMmsMessageCountToken )
        {
        iMmsHeaders->MMBoxViewHeadersL().SetMMBoxTotalNumber( size );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- MMBox total: %d messages"), size );
#endif
        }
    else if ( byte == KMmsMessageSizeToken )
        {
        iMmsHeaders->MMBoxViewHeadersL().SetMMBoxTotalSize( size );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- MMBox total: %d bytes"), size );
#endif
        }
    // illegal token value is ignored.
    else
        {
#ifndef _NO_MMSS_LOGGING_
        TUint temp;
        temp = byte;
        TMmsLogger::Log( _L("- MMBox total: illegal token %d"), temp );
#endif
        }
    }
          
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeQuotaHeaderL()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->MMBoxViewHeadersL().SetMmsQuotas( temp );
#ifndef _NO_MMSS_LOGGING_
        LogYesNo( _L("- MMBox Quotas requested:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMBoxQuotasL()
    {
    TUint8 byte;
    GetValueLength(); // we don't need this
    // get message quota or size quota
    byte = GetWellKnownFieldValueOrSkip();
    // actual value of the quota
    // We only get normal integer (not a 64 bit integer) as a 32 bit integer 
    // will give quota 4294967295 bytes or messages (which should be plenty)
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    // save message quota
    if ( byte == KMmsMessageCountToken )
        {
        iMmsHeaders->MMBoxViewHeadersL().SetMMBoxQuotaNumber( size );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- MMBox quota: %d messages"), size );
#endif
        }
    else if ( byte == KMmsMessageSizeToken )
        {
        iMmsHeaders->MMBoxViewHeadersL().SetMMBoxQuotaSize( size );
#ifndef _NO_MMSS_LOGGING_
        TMmsLogger::Log( _L("- MMBox quota: %d bytes"), size );
#endif
        }
    // illegal token value is ignored.
    else
        {
#ifndef _NO_MMSS_LOGGING_
        TUint temp;
        temp = byte;
        TMmsLogger::Log( _L("- MMBox quota: illegal token %d"), temp );
#endif
        }
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageCountL()
    {
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    iMmsHeaders->MMBoxViewHeadersL().SetMmsMessageCount( size );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Message count: %d"), size );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeStartInMMBoxViewL()
    {
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    iMmsHeaders->MMBoxViewHeadersL().SetMmsStart( size );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Start of messages: %d"), size );
#endif
    }


// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeDistributionIndicator()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetDistributionIndicator( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- May be distributed further:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeElementDescriptorL()
    {
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
#endif
    TUint32 size = 0;
    // remember where we are
    size = GetValueLength();
    if ( size > iLength - iPosition )
        {
        // If the message is corrupted and indicates incorrect length,
        // make sure we don't read beyond the buffer
        // We don't declare that the message is corrupted because of this.
        // We just read what we can, we don't use this header anyway.
        // If there is data following a corrupted header, it will be lost.
        size = iLength - iPosition;
        }
    TUint endPosition = iPosition + size;
    HBufC16* buffer = NULL;
    buffer = GetSimpleTextStringL(); // I hope this is correct.
    CleanupStack::PushL( buffer );
    iMmsHeaders->ElementDescriptorL().SetContentReferenceL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Content reference: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    buffer = NULL;
    TUint8 byte;
    while ( iPosition < endPosition )
        {
        byte = GetWellKnownFieldValueOrSkip();
        if ( byte != KMmsAssignedTopLevelContentType )
            {
            // Unknown parameter, ignore
            SkipFieldValue();
            }
        else
            {
            // the value of the parameter is content-type of the top-level message content
            TPtrC8 contType = GetContentTypeL();
            iMmsHeaders->ElementDescriptorL().SetContentTypeL( contType );
#ifndef _NO_MMSS_LOGGING_
            buffer = HBufC16::NewL( contType.Length() );
            CleanupStack::PushL( buffer );
            buffer->Des().Copy( contType );
            dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
            TMmsLogger::Log( _L("-- Top level content type : %S"), &dummy );
            CleanupStack::PopAndDestroy( buffer );
            buffer = NULL;
#endif
            }
        }
    iPosition = endPosition;
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeMessageLimitL()
    {
    TUint32 size = 0;
    size = GetLongOrShortInteger();
    iMmsHeaders->MMBoxViewHeadersL().SetMmsLimit( size );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Max number of messages to list: %d"), size );
#endif
    }
   
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeExtNotifTextL()
    {
    TPtrC8 byteString;
    HBufC16* buffer = NULL;
    byteString.Set( GetUtf8String() );
    // There will be at most one unicode character per one utf-8 character
    buffer = HBufC16::NewL( byteString.Length() * KMms2 );
    TPtr16 pointer16 = buffer->Des();
    CleanupStack::PushL( buffer );
    // We convert what we can and forget the rest.
    CnvUtfConverter::ConvertToUnicodeFromUtf8( pointer16, byteString );
    iMmsHeaders->SetExtendedNotificationL( pointer16 );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Extend. notif. txt: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
   
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeExtNotifEolL()
    {
    HBufC16* buffer = NULL;
    buffer = GetSimpleTextStringL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->SetMessageComplete( ( buffer->Des() )[0] );
#ifndef _NO_MMSS_LOGGING_
    TUint temp;
    temp = ( buffer->Des() )[0];
    TMmsLogger::Log( _L("- End of ext. notif: 0x%02X"), temp );
#endif
    CleanupStack::PopAndDestroy( buffer );
    
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeContentClass()
    {
    TUint8 byte;            // for simple reads
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetContentClass( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Content Class:") );
    switch ( temp )
        {
        case KMmsContentClassText:
            TMmsLogger::Log( _L("-- Text") );
            break;
        case KMmsContentClassImageBasic:
            TMmsLogger::Log( _L("-- Image Basic") );
            break;
        case KMmsContentClassImageRich:
            TMmsLogger::Log( _L("-- Image Rich") );
            break;
        case KMmsContentClassVideoBasic:
            TMmsLogger::Log( _L("-- Video Basic") );
            break;
        case KMmsContentClassVideoRich:
            TMmsLogger::Log( _L("-- Video Rich") );
            break;
        case KMmsContentClassMegaPixel:
            TMmsLogger::Log( _L("-- Megapixel") );
            break;
        case KMmsContentClassContentBasic:
            TMmsLogger::Log( _L("-- Content Basic") );
            break;
        case KMmsContentClassContentRich:
            TMmsLogger::Log( _L("-- Content Rich") );
            break;
        default:
            TMmsLogger::Log( _L("-- Unknown %d"), temp );
            break;
        }
#endif
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeDrmContentHeader()
    {
    TUint8 byte;            // for simple reads
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetDrmContent( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- DRM Content:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeAdaptationAllowed()
    {
    TUint8 byte;            // for simple reads
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetAdaptationAllowed( temp );
#ifndef _NO_MMSS_LOGGING_
    LogYesNo( _L("- Adaptation allowed:"), temp );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeApplicationIdL()
    {
    HBufC16* buffer = NULL;
    buffer = GetSimpleTextStringL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->SetApplicIdL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Application-ID: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeReplyApplicationIdL()
    {
    HBufC16* buffer = NULL;
    buffer = GetSimpleTextStringL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->SetReplyApplicIdL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Reply-To-Application-ID: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }

// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeApplicationInfoL()
    {
    TPtrC8 byteString;
    byteString.Set( GetByteString() );
    iMmsHeaders->SetAuxApplicInfoL( byteString );
#ifndef _NO_MMSS_LOGGING_
    TPtrC dummy;
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Aux-Applic-Info: %S"), &dummy );
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeRecommendedRetrievalMode()
    {
    TUint8 byte;
    TUint temp;
    byte = GetWellKnownFieldValueOrSkip();
    temp = byte;
    iMmsHeaders->SetRecommendedRetrievalMode( temp );
#ifndef _NO_MMSS_LOGGING_
    if ( temp == KMmsRecommendedRetrievalModeManual )
        {
        TMmsLogger::Log( _L("- Recommended retrieval mode: Manual") );
        }
    else
        {
        TMmsLogger::Log( _L("- Recommended retrieval mode unknown %d"), temp );
        }
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeRecommendedRetrievalModeTextL()
    {
    HBufC16* buffer = NULL;
    buffer = GetEncodedTextStringL();
    CleanupStack::PushL( buffer );
    iMmsHeaders->SetRecommendedRetrievalModeTextL( buffer->Des() );
#ifndef _NO_MMSS_LOGGING_
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    TMmsLogger::Log( _L("- Response text: %S"), &dummy );
#endif
    CleanupStack::PopAndDestroy( buffer );
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
//
void CMmsDecode::DecodeCancelReplaceIdL( TInt aHeader )
    {
    TPtrC8 byteString;
    byteString.Set( GetByteString() );
    iMmsHeaders->SetReplaceCancelIdL( byteString );
#ifndef _NO_MMSS_LOGGING_
    HBufC16* buffer = NULL;
    buffer = HBufC16::NewLC( byteString.Length() );
    buffer->Des().Copy( byteString );
    // we cannot log indefinitely long strings.
    // We get this long strings only if the message is corrupted.
    TPtrC dummy;
    dummy.Set( buffer->Des().Left( KMmsMaxLogStringLength ) );
    if ( aHeader == KMmsAssignedReplaceId )
        {
        TMmsLogger::Log( _L("- Replace Id: %S"), &dummy );
        }
    if ( aHeader == KMmsAssignedCancelId )
        {
        TMmsLogger::Log( _L("- Cancel Id: %S"), &dummy );
        }
    CleanupStack::PopAndDestroy( buffer );
#endif
    }
    
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::DecodeCancelStatus()
    {
    TUint8 byte;
    byte = GetWellKnownFieldValueOrSkip();
    TUint temp;
    temp = byte;
    iMmsHeaders->SetCancelStatus( temp );
#ifndef _NO_MMSS_LOGGING_
    TMmsLogger::Log( _L("- Cancel Status:") );
    switch ( temp )
        {
        case KMmsCancelRequestSuccessfullyReceived:
            TMmsLogger::Log( _L("-- successful") );
            break;
        case KMmsCancelRequestCorrupted:
            TMmsLogger::Log( _L("-- corrupted") );
            break;
        default:
            TMmsLogger::Log( KLogUnknown, temp );
            break;
        }
#endif
    }
    
#ifndef _NO_MMSS_LOGGING_
// ---------------------------------------------------------
//
// ---------------------------------------------------------
void CMmsDecode::LogDateL( const TInt64& aDate )
    {
    TBuf<KMmsDateFormatLength> dateString;

    TMmsLogger::Log( _L("%d seconds from 1.1.1970 (UTC)"), aDate );
    TTime time = TTime( KMmsYear1970String ) +
        TTimeIntervalMicroSeconds( aDate * KMmsMicroToSeconds ); 
    time.FormatL(dateString,(_L("%*E%*D%X%*N%Y %1 %2 '%3")));
    TMmsLogger::Log( _L("date %S"), &dateString );
    time.FormatL(dateString,(_L("%-B%:0%J%:1%T%:2%S%:3%+B")));
    TMmsLogger::Log( _L("time %S"), &dateString );
    }
#endif


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

//  End of File  

