--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmscodec/src/mmsdecode.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,6651 @@
+/*
+* 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(!CheckDRMContent())
+ {
+ 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::CheckDRMContent()
+ {
+ 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
+