diff -r 000000000000 -r 72b543305e3a mmsengine/mmscodec/src/mmsdecode.cpp --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Features +#include +#include + +#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 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 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 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 +