diff -r 6a20128ce557 -r ebfee66fde93 mmsengine/mmscodec/src/mmsencode.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mmsengine/mmscodec/src/mmsencode.cpp Fri Jun 04 10:25:39 2010 +0100 @@ -0,0 +1,5037 @@ +/* +* Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* binary encoding of a multimedia message +* +*/ + + + +//#define CONTENT_DISPOSITION_TEST + +// INCLUDE FILES +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include // link against centralrepository.lib +#include "MmsEnginePrivateCRKeys.h" + +#include "mmsheaders.h" +#include "mmsconst.h" +#include "mmscodec.h" +#include "mmsencode.h" +#include "mmsservercommon.h" +#include "mmssession.h" +#include "mmsgenutils.h" +#include "mmserrors.h" +#include "mmsentrywrapper.h" +#include "mmsmmboxmessageheaders.h" +#include "mmsmmboxviewheaders.h" +#include "mmssendingchain.h" + +const TInt KMmsExtra = 1024; // extra memory left for others +const TInt KMmsShortIntegerLimit127 = 127; // limit for short integer length +const TInt KMmsOneByteLimit = 0x100; +const TInt KMmsTwoByteLimit = 0x10000; +const TInt KMmsThreeByteLimit = 0x1000000; +const TInt KMms2 = 2; +const TInt KMms3 = 3; +const TInt KMms4 = 4; +const TInt KMms5 = 5; +const TInt KMms7 = 7; +const TInt KMms8 = 8; +const TInt KMms24 = 24; // shift of three half bytes +const TInt KMms30 = 30; // upper limit for short length encoding +const TUint8 KMms0x80 = 0x80; // 128 +const TUint8 KMms0x7F = 0x7F; // 127 +const TUint8 KMms0xFF = 0xFF; +const TInt KMmsMaxCidLength = 18; +// max filename length according to MMS conformance specs +const TInt KMmsMaxFileNameLength = 40; +// if 40 bytes are encoded in base 64, the result is 56 bytes +// (two bytes of padding are needed to make the number divisible by 3) +const TInt KMaxEncodingLength = 56; +// Maximum length of output buffer needed for encoding the filename +// Max 56 bytes for actual encoding and 12 bytes for character set (utf8) +// and encoding scheme indicator 68 for final result, but we allocate +// the maximum Mime header length to be sure everything fits. +const TInt KMaxNameBufferLength = 75; +const TInt KMmsEncodingExtraLength = 12; +const TInt KMmsPreambleLength = 10; +_LIT8( KMmsQuotedPreamble, "=?utf-8?Q?" ); +_LIT8( KMmsBase64Preamble, "=?utf-8?B?" ); +_LIT8( KMmsEncodingTrailer, "?=" ); +const TInt KMmsIntUnderscore = 0x5F; // underscore + + +enum TMmsMachineStates + { + EMmsIdle, + EMmsEncodingHeaders, + EMmsEncodingAttachments, + EMmsFinished + }; + +// These are the stages for chunked encoding. +// The chunked encoding is synchronous, so the stages are different +// from the machine states. +enum TMmsChunkedEncodingStages + { + EMmsHeaders, + EMmsAttachmentHeaders, + EMmsAttachmentData + }; + +// ==================== LOCAL FUNCTIONS ==================== + +// ================= MEMBER FUNCTIONS ======================= + +// --------------------------------------------------------------------------- +// C++ default constructor can NOT contain any code, that +// might leave. +// +// All member variables are automatically zeroed in the constructor +// of a class derived from CBase. +// Priority is set slightly above standard (1 instead of 0) to make sure +// that message is sent as soon as possible (user input has priority 10). +// Variables related to chunked encoding are initialized to indicate +// one complete chunk only. +// --------------------------------------------------------------------------- +// +CMmsEncode::CMmsEncode() + :CMsgActive ( KMmsActiveObjectPriority ), + iState ( EMmsIdle ), + iOverallDataSize ( -1 ), + iLastChunk ( ETrue ), + iOnlyOneChunk( ETrue ) + { + } + +// --------------------------------------------------------------------------- +// Symbian OS default constructor can leave. +// --------------------------------------------------------------------------- +// +void CMmsEncode::ConstructL( RFs& aFs ) + { + iFs = aFs; + CActiveScheduler::Add( this ); + } + +// --------------------------------------------------------------------------- +// Two-phased constructor. +// --------------------------------------------------------------------------- +// +EXPORT_C CMmsEncode* CMmsEncode::NewL( RFs& aFs ) + { + CMmsEncode* self = new ( ELeave ) CMmsEncode; + + CleanupStack::PushL( self ); + self->ConstructL( aFs ); + CleanupStack::Pop( self ); + + return self; + } + + +// --------------------------------------------------------------------------- +// Destructor +// --------------------------------------------------------------------------- +// +CMmsEncode::~CMmsEncode() + { + + Cancel(); + + if ( iFileOpen ) + { + iAttachFile.Close(); + iFileOpen = EFalse; + } + + // Do not delete pointers that were presents from the caller. + // The caller still owns that data + delete iMimeHeaders; // this is ours too. + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +EXPORT_C void CMmsEncode::StartL( + MMmsEntryWrapper& aEntryWrapper, + CMmsHeaders& aMmsHeaders, + CBufFlat& aEncodeBuffer, + TRequestStatus& aStatus ) + { + + // This is the entry point for encoding all PDUs that contain both headers + // and data. + // For normal use the only PDUs in this category would be M-send.req and + // M-MBox-Upload.req, but for testing purposes the following PDUs may + // also be handled using this entry point: + // M-retrieve.conf (for testing purposes) + // M-Mbox-View.conf (for testing purposes) + // M-Mbox-Descr (for testing purposes) + + // Old asynchronous encoding is retained. + // Reset sets all chunk-related variables to indicate one chunk which is the last one + Reset(); + + iEntryWrapper = &aEntryWrapper; + iMmsHeaders = &aMmsHeaders; + iEncodeBuffer = &aEncodeBuffer; + + if ( iMimeHeaders ) + { + iMimeHeaders->Reset(); + } + else + { + iMimeHeaders = CMsvMimeHeaders::NewL(); + } + + // We need to know the message entry ID + // The wrapper originally points to the message entry + + TMsvEntry indexEntry; + iError = iEntryWrapper->GetIndexEntry( indexEntry ); + iCurrentMessageId = indexEntry.Id(); + + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + + // Only new attachment structure is supported + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + iNumberOfAttachments = attachMan.AttachmentCount(); + + CleanupStack::PopAndDestroy( store ); + + Queue( aStatus ); + + iStatus = KRequestPending; + SetActive(); + // Pretend that we called an asynchronous service + // in order to get into the state machine loop + TRequestStatus* status = &iStatus; + User::RequestComplete( status, iError ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +EXPORT_C void CMmsEncode::EncodeHeadersL( + CMmsHeaders& aMmsHeaders, + CBufFlat& aEncodeBuffer ) + { + // This function is used to encode all PDUs that contain only headers, + // not data. Some are responses sent to MMSC, some are requests that + // contain headers only. + // Nothing is read from storage, everything to be encoded is contained + // in the headers. + // No active object is invoked, this is a synchronous function + + // Old one-chunk encoding is retained for encoding headers only. + // Reset sets all chunk-related variables to indicate one chunk which is the last one + Reset(); // also sets iError to KErrNone + + iMmsHeaders = &aMmsHeaders; + iEncodeBuffer = &aEncodeBuffer; + + // start from the beginning + iPosition = 0; + // we discard old contents of the buffer in case we must expand it + iEncodeBuffer->Reset(); + + // We are a bit greedy here so that we don't need + // to change the code if there is a minor change in the specs. + iEncodeBuffer->ResizeL( iMmsHeaders->Size() + KMMSBufferExtra ); + + // encode message depending on type + // these are all types that contain headers only, never data + switch ( iMmsHeaders->MessageType() ) + { + case KMmsMessageTypeMNotifyRespInd: + EncodeNotifyResponse(); + break; + case KMmsMessageTypeAcknowledgeInd: + EncodeAcknowledgeIndication(); + break; + case KMmsMessageTypeMNotificationInd: + EncodeMmsNotificationL(); + break; + case KMmsMessageTypeForwardReq: + EncodeForwardRequestL(); + break; + case KMmsMessageTypeReadRecInd: + EncodeReadReplyL(); + break; + case KMmsMessageTypeMboxStoreReq: + EncodeMMBoxStoreRequestL(); + break; + case KMmsMessageTypeMboxViewReq: + // This may contain keywords. Reserve some extra space + EncodeMMBoxViewRequestL(); + break; + case KMmsMessageTypeMBoxDeleteReq: + case KMmsMessageTypeDeleteReq: + EncodeDeleteRequestL(); + break; + case KMmsMessageTypeCancelConf: + EncodeCancelResponse(); + break; + case KMmsMessageTypeReadOrigInd: + // This is for testing purposes. + // identical to ReadRecInd except for PDU type + // This would be the PDU sent to originator by MMSC + EncodeReadReplyL(); + break; + case KMmsMessageTypeMSendConf: + // for testing purposes + EncodeSendConfirmationL(); + break; + case KMmsMessageTypeDeliveryInd: + // for testing purposes + EncodeDeliveryReportL(); + break; + case KMmsMessageTypeForwardConf: + // for testing purposes + EncodeForwardConfirmationL(); + break; + case KMmsMessageTypeMboxStoreConf: + // for testing purposes + EncodeMMBoxStoreConfirmationL(); + break; + case KMmsMessageTypeMBoxUploadConf: + // for testing purposes + EncodeMMBoxUploadConfirmationL(); + break; + case KMmsMessageTypeMBoxDeleteConf: + case KMmsMessageTypeDeleteConf: + // for testing purposes + EncodeDeleteConfirmationL(); + break; + case KMmsMessageTypeCancelReq: + // for testing purposes + EncodeCancelRequest(); + break; +#ifdef __WINS__ + case KMmsMessageTypeMSendReq: + EncodeSendRequestHeadersL(); + break; + case KMmsMessageTypeMBoxUploadReq: + // This type has attachments and headers + // This function encodes headers only + EncodeMMBoxUploadRequestL(); + break; + case KMmsMessageTypeMRetrieveConf: + // for test purposes + EncodeRetrieveConfirmationL(); + break; + case KMmsMessageTypeMboxViewConf: + // for test purposes + EncodeMMBoxViewConfirmationL(); + break; + case KMmsMessageTypeMBoxDescr: + // for test purposes + EncodeMMBoxDescriptionL(); + break; +#endif + default: + // Illegal message type. + iEncodeBuffer->Reset(); + iPosition = 0; + break; + } + + // Remove slack to keep garbage out from the end of the file + iEncodeBuffer->ResizeL( iPosition ); + // Dump the buffer contents into file if requested + Dump(); + + iOverallDataSize = iEncodeBuffer->Size(); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +EXPORT_C void CMmsEncode::StartChunkedL( + MMmsEntryWrapper& aEntryWrapper, + CMmsHeaders& aMmsHeaders, + CBufFlat& aEncodeBuffer, + TRequestStatus& aStatus ) + { + + // This is the entry point for chunked encoding all PDUs that contain both headers + // and data. + + // Reset sets all chunk-related variables to indicate one chunk which is the last one +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode: Start chunked") ); +#endif + Reset(); // also sets iError to KErrNone + iDataSupplierStage = EMmsHeaders; + + iEntryWrapper = &aEntryWrapper; + iMmsHeaders = &aMmsHeaders; + iEncodeBuffer = &aEncodeBuffer; + + if ( iMimeHeaders ) + { + iMimeHeaders->Reset(); + } + else + { + iMimeHeaders = CMsvMimeHeaders::NewL(); + } + + // We need to know the message entry ID + // The wrapper originally points to the message entry + + TMsvEntry indexEntry; + iError = iEntryWrapper->GetIndexEntry( indexEntry ); + iCurrentMessageId = indexEntry.Id(); + + // Encode headers to the buffer + // The rest will follow when next data chunk is requested. + EncodeHeadersChunkedL(); + + aStatus=KRequestPending; + TRequestStatus* status = &aStatus; + User::RequestComplete( status, iError ); + } + + +// --------------------------------------------------------- +// From class MMmsCodecDataSupplier +// +// --------------------------------------------------------- +// +TInt CMmsEncode::GetNextDataPart( TPtrC8& aDataPart, TBool& aLastDataChunk ) + { + TInt error = KErrNone; + if ( !iEncodeBuffer || iEncodeBuffer->Size() == 0 ) + { + // called out of context or no message data available + error = KErrNotFound; + } + else + { + aDataPart.Set( iEncodeBuffer->BackPtr( iPosition ) ); + } + aLastDataChunk = iLastChunk; + + return error; + } + +// --------------------------------------------------------- +// From class MMmsCodecDataSupplier +// +// --------------------------------------------------------- +// +TInt CMmsEncode::ReleaseData() + { + if ( iOnlyOneChunk ) + { + // if we have only one chunk we always point to the beginning + // the buffer gets cleared when all data has been handled + return KErrNone; + } + + iPosition = 0; + + // encode next data part into the buffer + + if ( iDataSupplierStage == EMmsAttachmentHeaders && iNumberOfAttachments == 0 ) + { + iLastChunk = ETrue; + iDataSupplierStage = EMmsHeaders; + } + else if ( iDataSupplierStage == EMmsAttachmentHeaders ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- data supplier stage: Attachment headers") ); +#endif + iError = iEntryWrapper->SetCurrentEntry( iCurrentMessageId ); + if ( iError != KErrNone ) + { + return iError; + } + + iCurrentFileSize = 0; + CMsvStore* store = NULL; + TRAP( iError, + { + store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + iCurrentFileSize = EncodeHeadersAndGetFileL( attachMan ); + CleanupStack::PopAndDestroy( store ); + }); + if ( iError != KErrNone ) + { + return iError; + } + + // Now we have the attachment headers in our buffer. + // If there is room, put data there, too + + if ( iBufferSize - iPosition >= iCurrentFileSize ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- enough room left in buffer for attachment data") ); +#endif + EncodeAttachmentData( iAttachFile, iCurrentFileSize ); + iPosition += iCurrentFileSize; + iAttachFile.Close(); + iFileOpen = EFalse; + iCurrentAttachment++; + if ( iCurrentAttachment >= iNumberOfAttachments ) + { + iLastChunk = ETrue; + iDataSupplierStage = EMmsHeaders; + } + else + { + // one attachment finished - move to next one + iDataSupplierStage = EMmsAttachmentHeaders; + } + } + else + { + iDataSupplierStage = EMmsAttachmentData; + } + } + else if ( iDataSupplierStage == EMmsAttachmentData ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("- data supplier stage: Attachment data") ); +#endif + // Add as much attachment data as possible. + // Attachment file is now open, check position + TInt filePosition = 0; + iError = iAttachFile.Seek( ESeekCurrent, filePosition ); + if ( iError != KErrNone ) + { + return iError; + } + TInt dataLeft = iCurrentFileSize - filePosition; + TInt roomLeft = iBufferSize - iPosition; + if ( roomLeft >= dataLeft ) + { + EncodeAttachmentData( iAttachFile, dataLeft ); + iPosition += dataLeft; + iAttachFile.Close(); + iFileOpen = EFalse; + iCurrentAttachment++; + if ( iCurrentAttachment >= iNumberOfAttachments ) + { + iLastChunk = ETrue; + iDataSupplierStage = EMmsHeaders; + } + else + { + // one attachment finished - move to next one + iDataSupplierStage = EMmsAttachmentHeaders; + } + } + else + { + // we have more data than fits into buffer + EncodeAttachmentData( iAttachFile, roomLeft ); + iPosition += roomLeft; + // We don't close the file because we still have data. + // We stay in EMmsAttachmentHeaders state + } + } + else + { + // do nothing, keep LINT happy + } +#ifndef _NO_MMSS_LOGGING_ + if ( iDataSupplierStage == EMmsHeaders ) + { + TMmsLogger::Log( _L("- last chunk released") ); + TMmsLogger::Log( _L("- data supplier stage: MMS headers") ); + } +#endif + // If we have reached the end and have been asked to release data + // we have nothing left to do. + + // dump what we got into file - if needed + if ( iPosition > 0 ) + { + DumpAppend(); + } + return iError; + } + +// --------------------------------------------------------- +// From class MMmsCodecDataSupplier +// +// --------------------------------------------------------- +// +TInt CMmsEncode::OverallDataSize() + { + // If we have encoded all data, iOverallDataSize contains the + // actual amount of data. + // If we are doing chunked encoding, it contains -1. + return iOverallDataSize; + } + + +// --------------------------------------------------------- +// From class MMmsCodecDataSupplier +// +// --------------------------------------------------------- +// +TInt CMmsEncode::ResetSupplier() + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode: ResetSupplier") ); +#endif + if ( iOnlyOneChunk ) + { + // if we have only one chunk we always point to the beginning + // the buffer gets cleared when all data has been handled + return KErrNone; + } + + // start data from the beginning + iError = KErrNone; + iDataSupplierStage = EMmsHeaders; + TRAP( iError, EncodeHeadersChunkedL() ); + + return iError; + } + +// --------------------------------------------------------- +// From class CMsgActive +// +// --------------------------------------------------------- +// +void CMmsEncode::DoRunL() + { + + // This routine takes the state machine through the states + // until an error is encountered or the cycle completes. + + if ( iError != KErrNone ) + { + // We encountered an error, and cannot continue + // For the time being we just give up without trying + // to do any decent cleanup. + iStatus = iError; + // If we return from DoRunL without becoming active again, + // RunL completes. + return; + } + + if ( iState != EMmsFinished ) + { + SelectNextState(); + // If appropriate, ChangeStateL makes us active again + ChangeStateL(); + } + else + { + // We are done, we must become idle again + FinishL(); + iStatus = iError; + // If we return from DoRunL without becoming active again, + // RunL completes. + } + // As we are using standard Mentact RunL, we must leave + // if we have encountered an error, and do not want to continue + + if ( iError != KErrNone && !IsActive() ) + { + iPosition = 0; + FinishL(); + User::Leave( iError ); + } + } + +// --------------------------------------------------------- +// From class CMsgActive +// +// --------------------------------------------------------- +// +void CMmsEncode::DoComplete( TInt& /* aStatus */ ) + { + // We are exiting the loop - we say we are idle now + iState = EMmsIdle; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::SelectNextState() + { + + // If appropriate, the functions called within the switch statement + // will make us active again. If all is done, the asynchronous request + // will complete + + switch ( iState ) + { + case EMmsIdle: + // start the loop + iState = EMmsEncodingHeaders; + break; + case EMmsEncodingHeaders: + if ( iNumberOfAttachments > 0 ) + { + iState = EMmsEncodingAttachments; + } + else + { + iState = EMmsFinished; + } + break; + case EMmsEncodingAttachments: + // if there are more attachments, don't change state + if ( iCurrentAttachment >= iNumberOfAttachments ) + { + iState = EMmsFinished; + } + break; + case EMmsFinished: + // No more states + iState = EMmsIdle; + break; + default: + // Illegal state + break; + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::ChangeStateL() + { + switch ( iState ) + { + case EMmsEncodingHeaders: + EncodeHeadersL(); + break; + case EMmsEncodingAttachments: + EncodeAttachmentL(); + iCurrentAttachment++; + break; + case EMmsFinished: + FinishL(); + break; + default: + // Should not be here anymore + break; + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::Reset() + { + // close open file in case operation has ended with error + // and the attachment file is still open + if ( iFileOpen ) + { + iAttachFile.Close(); + iFileOpen = EFalse; + } + + iNumberOfAttachments = 0; + iCurrentAttachment = 0; + iError = KErrNone; + iCurrentMessageId = KMsvNullIndexEntryId; + iOverallDataSize = -1; + iLastChunk = ETrue; + iOnlyOneChunk = ETrue; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeHeadersL() + { + + // start from the beginning + iPosition = 0; + // we discard old contents of the buffer in case we must expand it + iEncodeBuffer->Reset(); + + // calculate space needed and resize buffer + TInt size = 0; + size += iMmsHeaders->Size(); + + User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) ); + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + // AttachmentsSize function asks actual size from files + // in case the info in CMsvAttachment is not up to date. + // This function leaves if the attachment file is not found. + // A message cannot be sent if it refers to attachment files that no + // longer exist. + size += iEntryWrapper->AttachmentsSizeL( *store ); + CleanupStack::PopAndDestroy( store ); + store = NULL; + + size += KMMSBufferExtra; // this is just an estimate of header size... + // extra needed if lots of attachments. + size += KMMSAttachmentExtra * iNumberOfAttachments; + TMemoryInfoV1Buf memory; + UserHal::MemoryInfo( memory ); + TInt available = memory().iFreeRamInBytes; + // check that buffer fits, leave a little memory for others too. + if ( size > ( available - KMmsExtra ) ) + { + // message is too big - don't even try + iError = KMmsErrorMessageTooBig; + } + + if ( iError == KErrNone ) + { + iEncodeBuffer->ResizeL( size ); + EncodeRequestHeadersL(); + } + + iStatus = KRequestPending; + SetActive(); + + // We complete ourselves. + TRequestStatus* status = &iStatus; + User::RequestComplete( status, iError ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeHeadersChunkedL() + { + // The first step of chunked encoding +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: start") ); +#endif /* _NO_MMSS_LOGGING_ */ + User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) ); + + /* Korean req: 415-5434 + * Get the target encoding MIB enum from cenrep and encode MMS text objects using corresponding conversion plugins + */ + TInt temp; + iTargetEncodingType = 0; + CRepository* repository = CRepository::NewLC( KUidMmsServerMtm ); + if ( repository->Get( KMmsEncodingType, temp ) == KErrNone ) + { + iTargetEncodingType = temp; + } + CleanupStack::PopAndDestroy( repository ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: target encoding type: %d"), iTargetEncodingType ); +#endif /* _NO_MMSS_LOGGING_ */ + + if( iTargetEncodingType != 0 ) + { + TRAPD( err, PreProcessAttachmentDataL() ); + +#ifndef _NO_MMSS_LOGGING_ + /* if any error, korean specific encoding failed, But this should not stop from sending MMS using + * existing default encoding. Hence just log the error and continue in any case + */ + if( err != KErrNone ) + { + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: PreProcessAttachmentDataL error: %d"), err ); + } +#endif /* _NO_MMSS_LOGGING_ */ + } + + //Open store for read-only purpose + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + + // Only new attachment structure is supported + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + iNumberOfAttachments = attachMan.AttachmentCount(); + + TInt size = 0; + size += iMmsHeaders->Size(); + size += KMMSBufferExtra; // this is just an estimate of header size... + // The buffer must be at least big enough to hold all headers + iBufferSize = Max( KMmsChunkedBufferSize, size ); + + // extra needed if lots of attachments. + // AttachmentsSize function asks actual size from files + // in case the info in CMsvAttachment is not up to date. + // This function leaves if the attachment file is not found. + // A message cannot be sent if it refers to attachment files that no + // longer exist. + size += iEntryWrapper->AttachmentsSizeL( *store ); + size += KMMSAttachmentExtra * iNumberOfAttachments; + CleanupStack::PopAndDestroy( store ); + store = NULL; + + if ( iBufferSize > size ) + { + // Our message is small enough to be sent in one chunk. + // It does not make sense to send the message in small chunks + // it the total is only a few kilobytes + iBufferSize = size; + iOnlyOneChunk = ETrue; + iLastChunk = ETrue; + } + else + { + // we need several chunks + iOnlyOneChunk = EFalse; + iLastChunk = EFalse; + } + + // start from the beginning + iPosition = 0; + iEncodeBuffer->Reset(); + // This leaves if unable to resize. + // We try to keep our buffer small enough so that this does not leave. + iEncodeBuffer->ResizeL( iBufferSize ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: MMS buff Size(Approx): %d"), iBufferSize ); +#endif /* _NO_MMSS_LOGGING_ */ // The message is small enough to be sent as one chunk + + EncodeRequestHeadersL(); + + TInt i = 0; + if ( iOnlyOneChunk ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: only one chunk") ); +#endif /* _NO_MMSS_LOGGING_ */ // The message is small enough to be sent as one chunk + for ( i = 0; ( i < iNumberOfAttachments ) && ( iError == KErrNone ); ++i ) + { + iCurrentAttachment = i; + // encode attachments, too, as we have decided that our message + // is small enough to fit into the buffer as one chunk + // DoEncodeAttachment always encodes iCurrentAttachment + // and updates buffer position as needed. + DoEncodeAttachmentL(); + // If something goes wrong, DoEncodeAttachmentL() sets iError, + // and StartChunkedL will complete the caller with error. + } + iEncodeBuffer->ResizeL( iPosition ); + iOverallDataSize = iEncodeBuffer->Size(); + } + else + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: Multiple chunks") ); +#endif /* _NO_MMSS_LOGGING_ */ // The message is small enough to be sent as one chunk + // chunked sending - but it should be possible to calculate data size + iOverallDataSize = iPosition; // This is the amount taken by headers + // The message is small enough to be sent as one chunk + for ( i = 0; ( i < iNumberOfAttachments ) && ( iError == KErrNone ); ++i ) + { + iCurrentAttachment = i; + // encode attachments, too, as we have decided that our message + // is small enough to fit into the buffer as one chunk + // DoEncodeAttachment always encodes iCurrentAttachment + // and updates buffer position as needed. + iOverallDataSize += DoGetAttachmentEncodingLengthL(); + // If something goes wrong, DoEncodeAttachmentL() sets iError, + // and StartChunkedL will complete the caller with error. + } + iCurrentAttachment = 0; + iDataSupplierStage = EMmsAttachmentHeaders; + if ( iError != KErrNone ) + { + iOverallDataSize = -1; + } + } + + // Dump headers. More data will follow + Dump(); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: end") ); +#endif /* _NO_MMSS_LOGGING_ */ // The message is small enough to be sent as one chunk + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAttachmentL() + { + + DoEncodeAttachmentL(); + + if ( iError != KErrNone ) + { + // If we return without becoming active again, RunL completes + // we cannot send this message, because we cannot access attachments + return; + } + + iStatus = KRequestPending; + SetActive(); + + // Now we indicate we already did it. + TRequestStatus* status = &iStatus; + User::RequestComplete( status, iError ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::DoEncodeAttachmentL() + { + + // Encode one part + // We must calculate length of headers and content type + + User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) ); + + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + + TInt size = 0; + size = EncodeHeadersAndGetFileL( attachMan ); + if ( iError != KErrNone ) + { + CleanupStack::PopAndDestroy( store ); + return; + } + + // The data read function will not leave + EncodeAttachmentData( iAttachFile, size ); + iPosition += size; + iAttachFile.Close(); + + CleanupStack::PopAndDestroy( store ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TInt CMmsEncode::DoGetAttachmentEncodingLengthL() + { + + // calculate length of headers and content type + + User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) ); + + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + + TInt size = 0; + size = GetHeadersAndFileSizeL( attachMan ); + CleanupStack::PopAndDestroy( store ); + return size; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TInt CMmsEncode::EncodeHeadersAndGetFileL( MMsvAttachmentManager& aAttachMan ) + { + + CMsvAttachment* attachmentInfo = NULL; + attachmentInfo = aAttachMan.GetAttachmentInfoL( iCurrentAttachment ); + CleanupStack::PushL( attachmentInfo ); + + iMimeHeaders->RestoreL( *attachmentInfo ); + + // We don't trust the info, we ask the file itself + // Someone outside the messaging system may have changed the file + // without changing the attachment info + TInt size = 0; + if ( iFileOpen ) + { + // close file in case we have been reset while the file is still open + iAttachFile.Close(); + iFileOpen = EFalse; + } + iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment ); + iError = iAttachFile.Size( size ); + iAttachFile.Close(); + + if ( iError != KErrNone ) + { + CleanupStack::PopAndDestroy( attachmentInfo ); + return size; + } + + // calculate the length of the headers + TBool foundName = EFalse; + TUint contentTypeSize = 0; + TInt8 contentType = -1; // indicate not found + TPtrC8 contentTypeString; + + TUint headerSize = 0; + + HBufC8* buf8 = CalculateAttachmentHeaderLengthL( + *attachmentInfo, + headerSize, + foundName, + contentTypeSize, + contentType, + contentTypeString ); + + CleanupStack::PushL( buf8 ); + + // We have calculated the header length, now we can encode: + + TPtrC8 nameString; + if ( !foundName && buf8 ) + { + nameString.Set( buf8->Des() ); + } + + EncodeAttachmentHeadersL( + size, + headerSize, + foundName, + contentTypeSize, + contentType, + contentTypeString, + nameString ); + + // now we have put the name all over, we can destroy the buffer + CleanupStack::PopAndDestroy( buf8 ); + buf8 = NULL; + CleanupStack::PopAndDestroy( attachmentInfo ); + attachmentInfo = NULL; + + // Now just write all the data octets + iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment ); + iFileOpen = ETrue; + + return size; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TInt CMmsEncode::GetHeadersAndFileSizeL( MMsvAttachmentManager& aAttachMan ) + { + + CMsvAttachment* attachmentInfo = NULL; + attachmentInfo = aAttachMan.GetAttachmentInfoL( iCurrentAttachment ); + CleanupStack::PushL( attachmentInfo ); + + iMimeHeaders->RestoreL( *attachmentInfo ); + + // We don't trust the info, we ask the file itself + // Someone outside the messaging system may have changed the file + // without changing the attachment info + TInt size = 0; + if ( iFileOpen ) + { + // close file in case we have been reset while the file is still open + iAttachFile.Close(); + iFileOpen = EFalse; + } + iAttachFile = aAttachMan.GetAttachmentFileL( iCurrentAttachment ); + iError = iAttachFile.Size( size ); + iAttachFile.Close(); + + if ( iError != KErrNone ) + { + CleanupStack::PopAndDestroy( attachmentInfo ); + return 0; + } + + // calculate the length of the headers + TBool foundName = EFalse; + TUint contentTypeSize = 0; + TInt8 contentType = -1; // indicate not found + TPtrC8 contentTypeString; + + TUint headerSize = 0; + + HBufC8* buf8 = CalculateAttachmentHeaderLengthL( + *attachmentInfo, + headerSize, + foundName, + contentTypeSize, + contentType, + contentTypeString ); + + delete buf8; // This is not needed + buf8 = NULL; + + CleanupStack::PopAndDestroy( attachmentInfo ); + attachmentInfo = NULL; + + // We have calculated the header length, now we still need the + // uintvar lengths: + + size += GetUintvarLength( size ); // encoding size for attachment length + size += headerSize; + size += GetUintvarLength( headerSize ); // encoding size for header length + + return size; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +HBufC8* CMmsEncode::CalculateAttachmentHeaderLengthL( + CMsvAttachment& aAttachmentInfo, + TUint& aHeaderLength, + TBool& aFoundName, + TUint& aContentTypeSize, + TInt8& aContentType, + TPtrC8& aContentTypeString ) + { + + TInt numNonSafe = 0; // number of characters in filename that would require encoding + // Not all content types have parameters. + // The only parameter we can handle to some extent + // is the character set parameter for text content types. + // The name parameter will always be added if not already present + + // Check if we have well-known media. + aContentType = -1; // indicate not found + aContentTypeString.Set( aAttachmentInfo.MimeType() ); + if ( aContentTypeString.Length() == 0 ) + { + // take type from mime headers + HBufC8* tempContentType = HBufC8::NewL( + iMimeHeaders->ContentType().Length() + + iMimeHeaders->ContentSubType().Length() + 1 ); + CleanupStack::PushL( tempContentType ); + tempContentType->Des().Copy( iMimeHeaders->ContentType() ); + if ( ( iMimeHeaders->ContentType().Find( KMmsSlash8 ) != + ( iMimeHeaders->ContentType().Length() - 1 ) ) && + ( iMimeHeaders->ContentSubType().Find( KMmsSlash8 ) != 0 ) && + ( iMimeHeaders->ContentSubType().Length() != 0 ) ) + { + tempContentType->Des().Append( KMmsSlash8 ); + } + tempContentType->Des().Append( iMimeHeaders->ContentSubType() ); + aAttachmentInfo.SetMimeTypeL( tempContentType->Des() ); + CleanupStack::PopAndDestroy( tempContentType ); + aContentTypeString.Set( aAttachmentInfo.MimeType() ); + } + + TInt8 i; + for ( i = 0; i < KNumberContentTypes && aContentType < 0; ++i ) + { + if ( aContentTypeString.CompareF( + TPtrC8( KContentTypeTable[i] ) ) == 0 ) + { + aContentType = i; + } + } + if ( aContentTypeString.Length() == 0 ) + { + aContentTypeString.Set( KMmsUnknownType ); + aContentType = -1; + } + + // we always add the name parameter to content type, so we always + // use the general form. If no name is given, we use the original filename + // If the name parameter is defined, we use it. + + aFoundName = EFalse; + iFileName.SetLength( 0 ); + + aHeaderLength = 0; + // add media type length + if ( aContentType >= 0 ) + { + // well-known media + aHeaderLength += 1; + } + else + { + // Extension media + aHeaderLength += aContentTypeString.Length(); + aHeaderLength += 1; // terminating zero + } + + // If we have parameters, we must calculate their length. + if ( iMimeHeaders->MimeCharset() != 0 ) + { + // charset has well-known encoding + aHeaderLength += 1; + if ( iMimeHeaders->MimeCharset() <= KMmsShortIntegerLimit127 ) + { + // short integer + aHeaderLength += 1; + } + else if ( iMimeHeaders->MimeCharset() < KMmsOneByteLimit ) + { + // long integer, short length + 1 byte of value + aHeaderLength += KMms2; + } + else if ( iMimeHeaders->MimeCharset() < KMmsTwoByteLimit ) + { + // long integer, short length + 2 bytes of value + // so far I don't know any IANA numbers that would + // take more than two hex bytes. + aHeaderLength += KMms3; + } + else if ( iMimeHeaders->MimeCharset() < KMmsThreeByteLimit ) + { + // long integer, short length + 3 bytes of value + aHeaderLength += KMms4; + } + else + { + // long integer, short length + 4 bytes of value + // cannot be longer than this in any case + aHeaderLength += KMms5; + } + } + // Then the rest of the parameters, unknown, + // sent as text strings + // If "name" attribute has been defined, we use it. + // if it is not added, we must add it. + // We must ensure that we don't add it twice... + + TInt mimeHeaderCount = iMimeHeaders->ContentTypeParams().MdcaCount(); + + for ( i = 0; i < mimeHeaderCount; i++ ) + { + // If we have corrupted parameters (length of name is zero) + // we skip the name/value pair. + // value may be zero, it will be encoded as just the terminating zero + if ( i%2 == 0 && + iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() == 0 ) + { + i++; + } + else + { + // 8-byte string size + terminating zero + // If parameter has no value, it still must have + // the "no-value" indicator (just the terminating zero.) + if ( iMimeHeaders->ContentTypeParams().MdcaPoint( i ).CompareF( + KWspNameString ) == 0 && i%2 == 0 ) + { + if ( ( i + 1 ) < mimeHeaderCount && + IsStringSafe( iMimeHeaders->ContentTypeParams().MdcaPoint( i + 1 ), + numNonSafe ) ) + { + // We always move the name to the first item. + // It may be this parameter or something else + + // Content type parameters are 8-bit strings. + // We can use the name only if it is safe, otherwise it may contain some + // unknown encoding (maybe utf-8). + iFileName.Copy( ( iMimeHeaders->ContentTypeParams(). + MdcaPoint( i + 1 ) ).Right( KMmsMaxFileName ) ); + i++; + } + else + { + // Skip the next field as it can only be used if it is safe + i++; + } + } + else + { + aHeaderLength += iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() + 1; + } + } + } + if ( ( iMimeHeaders->ContentTypeParams().MdcaCount() % 2 ) == 1 ) + { + // Odd number. Obviously last parameter has no value. + // Add the "no value" token + aHeaderLength++; + } + + // include name parameter to content-type + + // if the name was not among our mime headers, + // we must add it. + TInt nameLength = 0; + + // We prefer the suggested filename as the content type name parameter. + // If version is 1.3 or later, we can encode a non-safe filename. + // If suggested filename is not safe, but we already have a safe filename + // in iFilename, we don't override it if version is 1.2 or lower + if ( iMimeHeaders->SuggestedFilename().Length() > 0 ) + { + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 || + IsStringSafe( iMimeHeaders->SuggestedFilename() ) || + iFileName.Length() == 0 ) + { + iFileName.Copy( iMimeHeaders->SuggestedFilename().Right( KMmsMaxFileName ) ); + } + } + + TParse* parse = NULL; + // If we don't have a filename suggestion so far, we just use the + // actual filename the attachment manager has used to save the file. + if ( iFileName.Length() == 0 ) + { + parse = new( ELeave )TParse; + CleanupStack::PushL( parse ); + // aAttachmentInfo.FilePath() is an actual Symbian filename including path + // so it never can be too long to fit into TParse or TFilename + parse->Set( aAttachmentInfo.FilePath(), NULL, NULL ); + iFileName.Copy( parse->NameAndExt() ); + CleanupStack::PopAndDestroy( parse ); + parse = NULL; + } + + // Last effort: + // If our version is 1.2 or lower and the filename suggestion so far + // is not safe, we try content-location. It is always safe + + if ( iMmsHeaders->MmsVersion() <= KMmsVersion12 && + !IsStringSafe( iFileName ) ) + { + if ( iMimeHeaders->ContentLocation().Length() > 0 ) + { + // content location should be safe. + // However, we must make sure it contains no URI escapes. + // for example space is legal in filename, but not in URI. + HBufC16* decoded = NULL; + TRAPD( error, ( decoded = EscapeUtils::EscapeDecodeL( + iMimeHeaders->ContentLocation() ) ) ); + if ( error == KErrNone ) + { + iFileName.Copy( decoded->Des().Right( KMmsMaxFileName ) ); + delete decoded; + decoded = NULL; + } + else + { + iFileName.Copy( iMimeHeaders->ContentLocation().Right( KMmsMaxFileName ) ); + } + } + } + + // remove leading and trailing spaces just in case... + iFileName.Trim(); + + // We should now make sure that the filename is not too long. + // According to MMS conformance specs, the maximum should be 40 characters + // It is unclear if the characters mean the total length or the length + // after conversion to utf-8, but as a first approximation we try + // to ensure that the total does not exceed 40 characters + + nameLength = iFileName.Length(); + + if ( nameLength > KMmsMaxFileNameLength ) + { + parse = new( ELeave )TParse; + CleanupStack::PushL( parse ); + parse->Set( aAttachmentInfo.FilePath(), NULL, NULL ); + iFileName.Copy( parse->Name().Left( KMmsMaxFileNameLength - parse->Ext().Length() ) ); + iFileName.Append( parse->Ext() ); + CleanupStack::PopAndDestroy( parse ); + parse = NULL; + nameLength = iFileName.Length(); + } + + // If encoding version is <=1.2, make filename safe, + // otherwise use MIME encoding (either base64 or quoted printable) + + HBufC8* buf8 = NULL; + + TPtrC16 originalName; + TBuf16<1> oneCharacter; + originalName.Set( iFileName ); + + TInt j; + + if ( IsStringSafe( iFileName ) ) + { + // filename only contains us-ascii characters - use as is + buf8 = HBufC8::NewL( nameLength ); + CleanupStack::PushL( buf8 ); + buf8->Des().Copy( iFileName ); + } + else + { + if ( iMmsHeaders->MmsVersion() <= KMmsVersion12 ) + { + // if version is <= 1.2, the filename must be us-ascii + // replace all illegal characters by underscore + buf8 = HBufC8::NewL( nameLength ); + CleanupStack::PushL( buf8 ); + for ( j = 0; j < nameLength; ++j ) + { + oneCharacter.Copy( originalName.Mid( j, 1 ) ); + if ( !IsStringSafe( oneCharacter ) ) + { + _LIT( KMmsUnderscore, "_" ); + oneCharacter.Copy( KMmsUnderscore ); + } + buf8->Des().Append( oneCharacter ); + } + } + else + { + // MMS encapsulation version is 1.3 or later + // Using non-ascii characters in filename parameter is allowed + // The filename must be encoded either using base64 or quoted printable + HBufC8* temp = NULL; + temp = CnvUtfConverter::ConvertFromUnicodeToUtf8L( originalName ); + CleanupStack::PushL( temp ); + // Now we have the filename in utf-8. + // We must check if it contains non-ascii, and if it does, + // we must use base64 or quoted-printable encoding. + TInt numNonSafe; + // Actually we know already that we are not safe. We just need + // to get the number of non-ascii characters + IsStringSafe( temp->Des(), numNonSafe ); + // We prefer quoted printable as it looks nicer in case we have mostly ascii, + // and it leaves extension in cleartext anyway + if ( ( temp->Des().Length() + ( KMms2 * numNonSafe ) ) <= KMaxEncodingLength ) + { + // use quoted printable, but first convert spaces to underscores + TInt spacePosition = temp->Des().Find( KSpace8 ); + while ( spacePosition != KErrNotFound ) + { + temp->Des()[ spacePosition ] = KMmsIntUnderscore; + spacePosition = temp->Des().Find( KSpace8 ); + } + buf8 = EncodeQuotedPrintableWordL( temp->Des() ); + } + else + { + // use base64 + // We have not restricted the filename length in utf-8 format, + // so it may be way longer than 40 bytes. + // However, we can calculate the length the buffer needs, so we + // can try. However, we may end up with some soft line break in + // the middle of the parameter which may not be acceptable. + buf8 = EncodeBase64WordL( temp->Des() ); + } + + CleanupStack::PopAndDestroy( temp ); + + CleanupStack::PushL( buf8 ); + // we must change nameLength here + } + } + + // The name is now in buf8, either just copied or encoded + nameLength = buf8->Des().Length(); + + if ( aFoundName == EFalse ) + { + // well-known encoding for the header itself + aHeaderLength++; + aHeaderLength += nameLength; + aHeaderLength += 1; // terminating zero + } + + aContentTypeSize = aHeaderLength; + + // As we have general form (at least name parameter), we need to indicate the length + if ( aContentTypeSize <= KMms30 ) + { + // short length + aHeaderLength += 1; + } + else + { + // length quote + at least one byte of uintvar + aHeaderLength += KMms2; + // Expecting over 128 bytes is paranoid. + if ( aContentTypeSize >= KMms0x7F ) + { + aHeaderLength++; + } + } + + // Now the rest of the mime headers: + + // Content-location + // just a short integer + a null terminated string + + if ( iMimeHeaders->ContentLocation().Length() > 0 ) + { + aHeaderLength += 1; // encoding of header as short integer + + // If we want to make sure we are safe, + // we must call IsStringSafe() function. + + // We just assume we are safe now. + aHeaderLength += iMimeHeaders->ContentLocation().Length(); + aHeaderLength += 1; // terminating zero + } + + // Content-ID + + if ( iMimeHeaders->ContentId().Length() > 0 ) + { + aHeaderLength += 1; // encoding of header as short integer + // add terminating zero and preceding quote. + // (needed according to WSP 1.3) + aHeaderLength += iMimeHeaders->ContentId().Length() + KMms2; + if ( iMimeHeaders->ContentId().Find( KMmsLeftAngle ) != 0 ) + { + aHeaderLength += KMms2; // we must add angle bracket + } + } + + // x-type parameters + // Must be stored as name/value pairs like content-type parameters + // X-parameter[i] = parameter name + // X-parameter[i+1] = parameter value + + for ( i = 0; i < iMimeHeaders->XTypeParams().MdcaCount(); ++i ) + { + // If we have corrupted parameters (length of name is zero) + // we skip the name/value pair. + // value may be zero, it will be encoded as just the terminating zero + if ( i%2 == 0 && + ( iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() == 0 || + iMimeHeaders->XTypeParams().MdcaPoint( i ).CompareF( + KMmsSeparateDeliveryOmaXHeader ) == 0 ) ) + { + // We skip the header and its value if + // a) the header length is 0 + // b) the header is X-Oma-Drm-Separate-Delivery + i++; + } + else + { + aHeaderLength += iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() + 1; + } + } + if ( ( iMimeHeaders->XTypeParams().MdcaCount() % 2 ) == 1 ) + { + // Odd number. Obviously last parameter has no value. + // Add the "no value" token + aHeaderLength++; + } + + // THESE ARE NOT IMPLEMENTED + + // Content-disposition & parameters + // Content-description + // Content-base + // Relative path + // version + +#ifdef CONTENT_DISPOSITION_TEST +// not supported + + // We generate a general header for content disposition + // for tests we use the actual filename. + // In real life the user's filename should not be + // sent out to the world + + // Fixed coding: + // Content Disposition, value length, attachment, filename, + // actual filename + terminating 0 + + // attachment, filename, terminating zero + // content disposition should be text-string, not quoted text string + TInt contentDispositionLength = KMms3 + nameLength; + + TInt valueLengthLength = 1; + if ( contentDispositionLength > KMms30 ) + { + // lengths 0 - 30 are encoded using "short length" + valueLengthLength++; + // + if ( contentDispositionLength > ShortIntegerLimit127 ) + { + valueLengthLength++; + } + // Should never be longer than this. + // I don't think anybody has a filename that is longer than 16383 bytes. + } + + aHeaderLength += valueLengthLength + contentDispositionLength + 1; +#endif + + CleanupStack::Pop( buf8 ); + return buf8; // caller will delete this afterwards + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAttachmentHeadersL( + TUint aSize, + TUint aHeaderSize, + TBool aFoundName, + TUint aContentTypeSize, + TInt8 aContentType, + TPtrC8& aContentTypeString, + TPtrC8& aNameString ) + { + + EncodeUintvar( aHeaderSize ); + + EncodeUintvar( aSize ); + + TUint8 temp = 0; // used to set the high bit for integers + + // if we have character set (or other parameters), + // we must use general encoding + + EncodeValueLength( aContentTypeSize ); + + if ( aContentType >= 0 ) + { + temp = aContentType | KMms0x80; + iEncodeBuffer->Write( iPosition, &temp, 1 ); + iPosition++; + } + else + { + // we assume this is a safe string + EncodeTextString( aContentTypeString ); + } + + // Then content type parameters. Mainly character set, + // but maybe something else, too + + temp = KWspCharset | KMms0x80; // encode as short integer + EncodeOptionalInteger( temp, iMimeHeaders->MimeCharset() ); + + // if we didn't find "Name" parameter among our parameters, we + // put it first. + + // we need the name later anyway... + + if ( !aFoundName ) + { + temp = KWspName | KMms0x80; // encode as short integer + EncodeHeaderAndTextString( temp, aNameString ); + } + + // Then the rest of content type parameters, just text strings. + + TInt i = 0; + + for ( i = 0; i < iMimeHeaders->ContentTypeParams().MdcaCount(); ++i ) + { + // If we have corrupted parameters (length of name is zero) + // we skip the name/value pair. + // value may be zero, it will be encoded as just the terminating zero + if ( i%2 == 0 && + iMimeHeaders->ContentTypeParams().MdcaPoint( i ).Length() == 0 ) + { + i++; + } + else + { + // 8-byte string + terminating zero + // If parameter has no value, it still must have + // the "no-value" indicator (just the terminating zero.) + // The ContentTypeParameters function provides + // an empty string in this case. + if ( iMimeHeaders->ContentTypeParams().MdcaPoint( i ).CompareF( + KWspNameString ) == 0 && i%2 == 0 ) + { + if ( aFoundName ) + { + temp = KWspName | KMms0x80; // encode as short integer + iEncodeBuffer->Write( iPosition, &temp, 1 ); + iPosition++; + } + else + { + // we can use name from mime headers only if it is "safe" + // (contains only us-ascii characters) + // If the name is not safe, the parameter value is skipped + i++; + } + } + else + { + EncodeTextString( + iMimeHeaders->ContentTypeParams().MdcaPoint( i ) ); + } + } + } + if ( ( iMimeHeaders->ContentTypeParams().MdcaCount() % 2 ) == 1 ) + { + // Odd number. Obviously last parameter has no value. + // Add the "no value" token + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + } + + // content-location + temp = KWspContentLocation | KMms0x80; // encode as short integer + EncodeOptionalStringL( temp, iMimeHeaders->ContentLocation() ); + + // content-id + if ( iMimeHeaders->ContentId().Length() > 0 ) + { + HBufC8* tempContentId = HBufC8::NewL( iMimeHeaders->ContentId().Length() + KMms2 ); + CleanupStack::PushL( tempContentId ); + if ( iMimeHeaders->ContentId().Find( KMmsLeftAngle ) != 0 ) + { + tempContentId->Des().Copy( KMmsLeftAngle ); + tempContentId->Des().Append( iMimeHeaders->ContentId() ); + tempContentId->Des().Append( KMmsRightAngle ); + } + else + { + tempContentId->Des().Copy( iMimeHeaders->ContentId() ); + } + temp = KWspContentId | KMms0x80; // encode as short integer + iEncodeBuffer->Write( iPosition, &temp, 1 ); + iPosition++; + EncodeQuotedTextString( tempContentId->Des() ); + CleanupStack::PopAndDestroy( tempContentId ); + } + +#ifdef CONTENT_DISPOSITION_TEST +// not supported + temp = 0x2E | KMms0x80; // content disposition + iEncodeBuffer->Write( iPosition, &temp, 1 ); + iPosition++; + EncodeValueLength( contentDispositionLength ); + temp = 0x81; // attachment + iEncodeBuffer->Write( iPosition, &temp, 1 ); + iPosition++; + temp = 0x06 | KMms0x80; // filename + // filename should be text-string, not quoted + EncodeHeaderAndTextString( temp, buf8->Des() ); +#endif + + // x-type parameters + for ( i = 0; i < iMimeHeaders->XTypeParams().MdcaCount(); ++i ) + { + // If we have corrupted parameters (length of name is zero) + // we skip the name/value pair. + // value may be zero, it will be encoded as just the terminating zero + if ( i%2 == 0 && + ( iMimeHeaders->XTypeParams().MdcaPoint( i ).Length() == 0 || + iMimeHeaders->XTypeParams().MdcaPoint( i ).CompareF( + KMmsSeparateDeliveryOmaXHeader ) == 0 ) ) + { + // We skip the header and its value if + // a) the header length is 0 + // b) the header is X-Oma-Drm-Separate-Delivery + i++; + } + else + { + // 8-byte string + terminating zero + // If parameter has no value, it still must have + // the "no-value" indicator (just the terminating zero.) + // The ContentTypeParameters function provides + // an empty string in this case. + EncodeTextString( iMimeHeaders->XTypeParams().MdcaPoint( i ) ); + } + } + if ( ( iMimeHeaders->XTypeParams().MdcaCount() % 2 ) == 1 ) + { + // Odd number. Obviously last parameter has no value. + // Add the "no value" token + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + } + + // End of MIME headers + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAttachmentData( RFile& aAttachFile, TInt aSize ) + { + // read the attachment file + // set up a pointer to point to iEncodeBuffer at iPosition + TPtr8 pos = iEncodeBuffer->Ptr( iPosition ); + // Attachemnt may be a file attacment or a linked file. + // The file pointer will point to unread part if file is read in chunks + iError = aAttachFile.Read( pos, aSize ); + } + + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::FinishL() + { + + // Remove slack to keep garbage out from the end of the file + iEncodeBuffer->ResizeL( iPosition ); + iOverallDataSize = iEncodeBuffer->Size(); + iState = EMmsIdle; + + // Dump the buffer contents into file if requested + Dump(); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeRequestHeadersL() + { + // encode message depending on type + switch ( iMmsHeaders->MessageType() ) + { + // KMmsMessageTypeMNotifyRespInd and KMmsMessageTypeAcknowledgeInd and all other PDUs + // that contain only headers and no data should be handled by + // using function + // EncodeHeadersL(CMmsHeaders& aMmsHeaders,CBufFlat& aEncodeBuffer) + case KMmsMessageTypeMSendReq: + EncodeSendRequestHeadersL(); + break; + case KMmsMessageTypeMBoxUploadReq: + // This type has attachments and headers + EncodeMMBoxUploadRequestL(); + break; + case KMmsMessageTypeMRetrieveConf: + // for test purposes + EncodeRetrieveConfirmationL(); + break; + case KMmsMessageTypeMboxViewConf: + // for test purposes + EncodeMMBoxViewConfirmationL(); + break; + case KMmsMessageTypeMBoxDescr: + // for test purposes + EncodeMMBoxDescriptionL(); + break; + default: + iError = KMmsErrorUnknownMessageType; + break; + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeSendRequestHeadersL() + { + + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMSendReq, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + // If we have set the sender, encode it + // If not, we ask MMSC to add the sender + EncodeSenderL( iMmsHeaders->Sender() ); + + // All recipient lists. If no recipients in some of the lists, + // nothing is encoded, no need to check separately + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc ); + EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc ); + + // Subject + EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() ); + + // Message class + EncodeOptionalByte( KMmsAssignedMessageClass, + iMmsHeaders->MessageClass() ); + + // Expiration time + EncodeOptionalIntervalOrDate( KMmsAssignedExpiry, + iMmsHeaders->ExpiryInterval(), + iMmsHeaders->ExpiryDate() ); + + // Delivery time + EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime, + iMmsHeaders->DeliveryTimeInterval(), + iMmsHeaders->DeliveryDate() ); + + // Priority + EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() ); + + // Sender visibility + EncodeOptionalByte( KMmsAssignedSenderVisibility, + iMmsHeaders->SenderVisibility() ); + + // Delivery report + EncodeOptionalByte( KMmsAssignedDeliveryReport, + iMmsHeaders->DeliveryReport() ); + + // Read reply + EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() ); + + // MMS encapsulation 1.1 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion1 ) + { + // Do we want to specify reply charging + if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly ) + { + // Reply charging value + EncodeOptionalByte( KMmsAssignedReplyCharging, + iMmsHeaders->ReplyCharging() ); + // Reply charging deadline + EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline, + iMmsHeaders->ReplyChargingInterval(), + iMmsHeaders->ReplyChargingDate() ); + // Reply charging size + EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() ); + } + // Reply charging ID + EncodeOptionalString( KMmsAssignedReplyChargingID, + iMmsHeaders->ReplyChargingId() ); + } + + if ( iMmsHeaders->MmsVersion() > KMmsVersion11 ) + { + // MMBox related headers - optional + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-Store + EncodeOptionalByte( KMmsAssignedMmsStore, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() ); + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + } + } + + // Add encapsulation version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info + EncodeApplicationHeadersL(); + // X-Mms-Content-Class + EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() ); + // X-Mms-DRM-Content + EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() ); + // X-Mms-Adaptation-Allowed + EncodeOptionalByte( KMmsAssignedAdaptationAllowed, iMmsHeaders->AdaptationAllowed() ); + } + + // Get Root TMsvId from headers if set + TMsvAttachmentId rootId = iMmsHeaders->MessageRoot(); + + TInt error = KErrNone; + // If we are handling headers only, we may not have iEntryWrapper + if ( iEntryWrapper ) + { + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + TRAP( error, + { + CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL( + (TMsvAttachmentId) rootId ); + delete attachmentInfo; // we just wanted to check that it was found + } + ); + CleanupStack::PopAndDestroy( store ); + } + + // If we have a valid root part, we send the message as multipart/related. + // Otherwise we send it as multipart mixed. + // We don't try to guess what the root is supposed to be, if it + // is not specified or does not point to an existing attachment. + + if ( error == KErrNone && rootId != 0 && iEntryWrapper ) + { + EncodeMultipartRelatedHeaderL( rootId ); + } + else + { + EncodeMultipartMixedHeaderL(); + } + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeNotifyResponse() + { + + // version may be current version or 1.0 + // Only byte is used. iMmsHeaders controls the value. + + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMNotifyRespInd, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // insert message status + EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() ); + + // insert report allowed if present + EncodeOptionalByte( KMmsAssignedReportAllowed, + iMmsHeaders->ReportAllowed() ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAcknowledgeIndication() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeAcknowledgeInd, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + // insert report allowed if present + EncodeOptionalByte( KMmsAssignedReportAllowed, + iMmsHeaders->ReportAllowed() ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMmsNotificationL() + { + + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMNotificationInd, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + // for notification sender is optional - added only if it is known + if ( iMmsHeaders->Sender().Length() > 0 ) + { + EncodeSenderL( iMmsHeaders->Sender() ); + } + + EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() ); + + EncodeOptionalByte( KMmsAssignedDeliveryReport, + iMmsHeaders->DeliveryReport() ); + + EncodeOptionalByte( KMmsAssignedMessageClass, + iMmsHeaders->MessageClass() ); + + iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 ); + iPosition++; + + TInt64 size = iMmsHeaders->MessageSize(); + EncodeLongInteger( size ); + + EncodeOptionalIntervalOrDate( KMmsAssignedExpiry, + iMmsHeaders->ExpiryInterval(), + iMmsHeaders->ExpiryDate() ); + + // version 1.1 fields - optional + // reply-charging + // reply-charging deadline + // reply-charging size + // reply-charging ID + if ( iMmsHeaders->MmsVersion() > KMmsVersion1 ) + { + // Do we want to specify + if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly ) + { + // Reply charging value + EncodeOptionalByte( KMmsAssignedReplyCharging, + iMmsHeaders->ReplyCharging() ); + // Reply charging deadline + EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline, + iMmsHeaders->ReplyChargingInterval(), + iMmsHeaders->ReplyChargingDate() ); + // Reply charging size + EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() ); + } + // if this is a reply, we may specify the ID + // Reply charging ID + EncodeOptionalString( KMmsAssignedReplyChargingID, + iMmsHeaders->ReplyChargingId() ); + } + + // version 1.2 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion11 ) + { + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + EncodeOptionalByte( KMmsAssignedStored, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() ); + } + + // Distribution indicator + EncodeOptionalByte( KMmsAssignedDistributionIndicator, + iMmsHeaders->DistributionIndicator() ); + } + + // Element descriptor + // This is somewhat a mystery - not implemented + + // version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info + EncodeApplicationHeadersL(); + // X-Mms-Recommended-Retrieval-Mode + EncodeOptionalByte( KMmsAssignedRecommendedRetrievalMode, + iMmsHeaders->RecommendedRetrievalMode() ); + // X-Mms-Recommended-Retrieval-Mode-Text + EncodeOptionalStringL( KMmsAssignedRecommendedRetrievalModeText, + iMmsHeaders->RecommendedRetrievalModeText() ); + // X-Mms-Content-Class + EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() ); + // X-Mms-DRM-Content + EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() ); + // X-Mms-Replace-ID + EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() ); + } + + // We keep this as the last one, though it does not really matter + // Mandatory content-location + // this must be USASCII or URL encoded + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeSendConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMSendConf, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + EncodeMandatoryByte( KMmsAssignedResponseStatus, + iMmsHeaders->ResponseStatus() ); + + EncodeOptionalStringL( KMmsAssignedResponseText, + iMmsHeaders->ResponseText() ); + + // if send confirmation returns error, it might not contain message id + EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() ); + + // MMBox related headers - optional + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + EncodeOptionalByte( KMmsAssignedStoreStatus, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ); + EncodeOptionalStringL( KMmsAssignedStoreStatusText, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() ); + if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() == + KMmsStatusOk ) + { + // this must be USASCII or URL encoded + // If message was stored to MMBox, this is mandatory + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + } + } + +#endif + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeRetrieveConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMRetrieveConf, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + // message id + EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() ); + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + // If we have set the sender, encode it + if ( iMmsHeaders->Sender().Length() > 0 ) + { + EncodeSenderL( iMmsHeaders->Sender() ); + } + + // previously sent by array + TInt i; + TUint oldPosition; + TUint length; + for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); ++i ) + { + oldPosition = iPosition; + length = 0; + // Encode once just to find out filed length + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 ); + iPosition++; + EncodeValueLength( length ); + // now we have added length, do the actual encoding + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() ); + oldPosition = iPosition; + length = 0; + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 ); + iPosition++; + EncodeValueLength( length ); + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() ); + } + + // All recipient lists. If no recipients in some of the lists, + // nothing is encoded, no need to check separately + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc ); + // no Bcc in retrieve confirmation + + // Subject + EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() ); + + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + + // Message class + EncodeOptionalByte( KMmsAssignedMessageClass, + iMmsHeaders->MessageClass() ); + + // Priority + EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() ); + + // Delivery report + EncodeOptionalByte( KMmsAssignedDeliveryReport, + iMmsHeaders->DeliveryReport() ); + + // Read reply + EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() ); + + // MMS encapsulation 1.1 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion1 ) + { + // Do we want to specify reply charging + if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly ) + { + // Reply charging value + EncodeOptionalByte( KMmsAssignedReplyCharging, + iMmsHeaders->ReplyCharging() ); + // Reply charging deadline + EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline, + iMmsHeaders->ReplyChargingInterval(), + iMmsHeaders->ReplyChargingDate() ); + // Reply charging size + EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() ); + } + // Reply charging ID + EncodeOptionalString( KMmsAssignedReplyChargingID, + iMmsHeaders->ReplyChargingId() ); + // status of the operation + EncodeOptionalByte( KMmsAssignedRetrieveStatus, + iMmsHeaders->ResponseStatus() ); + + EncodeOptionalStringL( KMmsAssignedRetrieveText, + iMmsHeaders->ResponseText() ); + } + + // version 1.2 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion11 ) + { + // Distribution indicator + EncodeOptionalByte( KMmsAssignedDistributionIndicator, + iMmsHeaders->DistributionIndicator() ); + } + + // version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info + EncodeApplicationHeadersL(); + // X-Mms-Content-Class + EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() ); + // X-Mms-DRM-Content + EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() ); + // X-Mms-Replace-ID + EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() ); + } + + // Start pointer + TUint ii = 0; + // Get Root TMsvId from headers if set + TMsvId rootId = iMmsHeaders->MessageRoot(); + + TInt error = KErrNone; + if ( iEntryWrapper ) + { + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + TRAP( error, + { + CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL( + (TMsvAttachmentId) rootId ); + delete attachmentInfo; // we just wanted to check that it was found + } + ); + CleanupStack::PopAndDestroy( store ); + } + + // If we have a valid root part, we send the message as multipart/related. + // Otherwise we send it as multipart mixed. + // We don't try to guess what the root is supposed to be, if it + // is not specified or does not point to an existing attachment. + + if ( error == KErrNone && rootId != 0 && iEntryWrapper ) + { + EncodeMultipartRelatedHeaderL( rootId ); + } + else + { + EncodeMultipartMixedHeaderL(); + } + + +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeDeliveryReportL() + { +// implemented for test purposes +#ifdef __WINS__ + + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeDeliveryInd, TPtrC8(), + iMmsHeaders->MmsVersion() ); + + EncodeHeaderAndTextString( KMmsAssignedMessageId, + iMmsHeaders->MessageId() ); + + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + // insert message status + EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() ); + + // version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // X-Mms-Status-Text + // Status text is stored in the same place as response text and retrieve + // text as only one of them can appear in any PDU + EncodeOptionalStringL( KMmsAssignedStatusText, + iMmsHeaders->ResponseText() ); + // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info + EncodeApplicationHeadersL(); + } + +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeForwardRequestL() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeForwardReq, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + // If we have set the sender, encode it + // If not, we ask MMSC to add the sender + EncodeSenderL( iMmsHeaders->Sender() ); + + // All recipient lists. If no recipients in some of the lists, + // nothing is encoded, no need to check separately + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc ); + EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc ); + + // Expiration time + EncodeOptionalIntervalOrDate( KMmsAssignedExpiry, + iMmsHeaders->ExpiryInterval(), + iMmsHeaders->ExpiryDate() ); + + // Delivery time + EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime, + iMmsHeaders->DeliveryTimeInterval(), + iMmsHeaders->DeliveryDate() ); + + // Delivery report allowed + EncodeOptionalByte( KMmsAssignedReportAllowed, + iMmsHeaders->ReportAllowed() ); + + // Delivery report + EncodeOptionalByte( KMmsAssignedDeliveryReport, + iMmsHeaders->DeliveryReport() ); + + // Read reply + EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() ); + + // version 1.2 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion11 ) + { + // MMBox related headers - optional + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-Store + EncodeOptionalByte( KMmsAssignedMmsStore, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() ); + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes || + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() == KMmsYes ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + } + } + + // version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // Do we want to specify reply charging + if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly ) + { + // Reply charging value + EncodeOptionalByte( KMmsAssignedReplyCharging, + iMmsHeaders->ReplyCharging() ); + // Reply charging deadline + EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline, + iMmsHeaders->ReplyChargingInterval(), + iMmsHeaders->ReplyChargingDate() ); + // Reply charging size + EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() ); + } + } + + // Mandatory content-location + // this must be USASCII or URL encoded + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeForwardConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeForwardConf, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + EncodeMandatoryByte( KMmsAssignedResponseStatus, + iMmsHeaders->ResponseStatus() ); + + EncodeOptionalStringL( KMmsAssignedResponseText, + iMmsHeaders->ResponseText() ); + + EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() ); + + // MMBox related headers - optional + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + EncodeOptionalByte( KMmsAssignedStoreStatus, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ); + EncodeOptionalStringL( KMmsAssignedStoreStatusText, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() ); + if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() == + KMmsStatusOk ) + { + // this must be USASCII or URL encoded + // If message was stored to MMBox, this is mandatory + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + } + } + +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeReadReplyL() + { + // Insert message type, TID and version number + EncodeStartingHeaders( iMmsHeaders->MessageType(), TPtrC8(), iMmsHeaders->MmsVersion() ); + + // message id + EncodeHeaderAndTextString( KMmsAssignedMessageId, + iMmsHeaders->MessageId() ); + + // originator of the original message + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + + // If we have set the sender, encode it + // If not, we ask MMSC to add the sender + EncodeSenderL( iMmsHeaders->Sender() ); + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + EncodeMandatoryByte( KMmsAssignedReadStatus, iMmsHeaders->ReadStatus() ); + + // version 1.3 headers - optional + if ( iMmsHeaders->MmsVersion() > KMmsVersion12 ) + { + // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info + EncodeApplicationHeadersL(); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxStoreRequestL() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMboxStoreReq, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // this must be USASCII or URL encoded + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxStoreConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMboxStoreConf, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // this must be USASCII or URL encoded + EncodeOptionalString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + EncodeMandatoryByte( KMmsAssignedStoreStatus, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ); + + EncodeOptionalStringL( KMmsAssignedStoreStatusText, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() ); + +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxViewRequestL() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMboxViewReq, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // Insert content location header. There may be more than one + EncodeContentLocationArray(); + + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-MMs-MM-Flags. The token should always be "filter" + EncodeKeywordArrayL(); + } + + if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() ) + { + CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL(); + + // states used as filter + // X-Mms-MM-State may appear multiple times in view request + EncodeMMBoxStates( viewHeaders.MMStateArray() ); + + // start + EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() ); + // limit + if ( viewHeaders.MmsLimit() != KMaxTUint32 ) + { + // 0 means no message information, absence means all remaining + // messages. + // If header has value KMaxTUint32, it means that info about all + // remaining messages is wanted. + iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MmsLimit() ); + } + + EncodeAttributes( viewHeaders.AttributeArray() ); + // request totals + EncodeOptionalByte( KMmsAssignedTotals, viewHeaders.MmsTotals() ); + // request quotas + EncodeOptionalByte( KMmsAssignedQuotas, viewHeaders.MmsQuotas() ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxViewConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMboxViewConf, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + EncodeMandatoryByte( KMmsAssignedResponseStatus, + iMmsHeaders->ResponseStatus() ); + + EncodeOptionalStringL( KMmsAssignedResponseText, + iMmsHeaders->ResponseText() ); + + EncodeContentLocationArray(); + + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-MMs-MM-Flags. The token should always be "filter" + EncodeKeywordArrayL(); + } + + if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() ) + { + CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL(); + + // states used as filter + // X-Mms-MM-State may appear multiple times in view request + EncodeMMBoxStates( viewHeaders.MMStateArray() ); + + // start + EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() ); + // limit + if ( viewHeaders.MmsLimit() != KMaxTUint32 ) + { + // 0 means no message information, absence means all remaining + // messages. + // If header has value KMaxTUint32, it means that info about + // all remaining messages is wanted. + iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MmsLimit() ); + } + EncodeAttributes( viewHeaders.AttributeArray() ); + + // totals + TUint oldPosition = iPosition; + TUint length = 0; + + if ( viewHeaders.MMBoxTotalNumber() != KMaxTUint32 ) + { + oldPosition = iPosition; + EncodeInteger( viewHeaders.MMBoxTotalNumber() ); + length = iPosition - oldPosition; + length += 1; // quota token + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 ); + iPosition++; + EncodeValueLength( length ); + iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MMBoxTotalNumber() ); + } + + if ( viewHeaders.MMBoxTotalSize() != KMaxTUint32 ) + { + oldPosition = iPosition; + EncodeInteger( viewHeaders.MMBoxTotalSize() ); + length = iPosition - oldPosition; + length += 1; // quota token + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 ); + iPosition++; + EncodeValueLength( length ); + iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MMBoxTotalSize() ); + } + + // quotas + if ( viewHeaders.MMBoxQuotaNumber() != KMaxTUint32 ) + { + oldPosition = iPosition; + EncodeInteger( viewHeaders.MMBoxQuotaNumber() ); + length = iPosition - oldPosition; + length += 1; // quota token + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 ); + iPosition++; + EncodeValueLength( length ); + iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MMBoxQuotaNumber() ); + } + + if ( viewHeaders.MMBoxQuotaSize() != KMaxTUint32 ) + { + oldPosition = iPosition; + EncodeInteger( viewHeaders.MMBoxQuotaSize() ); + length = iPosition - oldPosition; + length += 1; // quota token + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 ); + iPosition++; + EncodeValueLength( length ); + iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MMBoxQuotaSize() ); + } + + // Message count + // This should in principle come from the viewHeaders.MmsMessageCount() + // But in this context the number of messages will be the number of + // attachments available in this entry + + // However, we will use the viewHeaders.MmsMessageCount() to generate + // the header so that we can test that the decoding part can handle + // possible conflict between message number and the number of parts + // in the multipart structure. This header is optional. KMaxTUint32 + // will mean unspecified. + + if ( viewHeaders.MmsMessageCount() != KMaxTUint32 ) + { + iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageCount, 1 ); + iPosition++; + EncodeInteger( viewHeaders.MmsMessageCount() ); + } + } + + if ( iNumberOfAttachments == 0 ) + { + EncodeMandatoryByte( KMmsAssignedContentType, KMmsAssignedAny ); + } + else + { + EncodeMultipartMixedHeaderL(); + } +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxUploadRequestL() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMBoxUploadReq, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + + // Get Root TMsvId from headers if set + TMsvAttachmentId rootId = iMmsHeaders->MessageRoot(); + + // Find the matching id from attachment list + + TInt error = KErrNone; + if ( iEntryWrapper ) + { + CMsvStore* store = iEntryWrapper->ReadStoreL(); + CleanupStack::PushL( store ); + // Only new attachment structure is supported + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + TRAP( error, + { + CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL( + (TMsvAttachmentId) rootId ); + delete attachmentInfo; // we just wanted to check that it was found + } + ); + CleanupStack::PopAndDestroy( store ); + } + + // If we have a valid root part, we send the message as multipart/related. + // Otherwise we send it as multipart mixed. + // We don't try to guess what the root is supposed to be, if it + // is not specified or does not point to an existing attachment. + + // actually the content should be an MMS PDU as in M-MBox-Descr PDU + // It is unclear if if should be wrapped in a multipart structure + // (containing only one header) + // or if the content type should be application/vnd.wap.mms-message + if ( error == KErrNone && rootId != 0 && iEntryWrapper ) + { + EncodeMultipartRelatedHeaderL( rootId ); + } + else + { + EncodeMultipartMixedHeaderL(); + } + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxUploadConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeMBoxUploadConf, iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // this must be USASCII or URL encoded + EncodeOptionalString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + EncodeMandatoryByte( KMmsAssignedStoreStatus, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ); + + EncodeOptionalStringL( KMmsAssignedStoreStatusText, + iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() ); +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeDeleteRequestL() + { + // Insert message type, TID and version number + + EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // Insert content location header. There may be more than one + EncodeContentLocationArray(); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeDeleteConfirmationL() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(), + iMmsHeaders->MmsVersion() ); + + // content location, response status and response text headers have + // different format from other PDUs + + const RPointerArray& resultArray = + iMmsHeaders->DeleteResultArray(); + + TInt i; + TInt length = 0; + TInt oldPosition = 0; + TUint oldIndex = KMaxTUint32; + + for ( i = 0; i < resultArray.Count(); i++ ) + { + TUint index = resultArray[i]->Order(); + if ( index != oldIndex ) + { + // We add status and status text only once, there may be more + // than one content location with the same status + oldIndex = index; + if ( resultArray[i]->ResponseStatus() != 0 ) + { + oldPosition = iPosition; + length = 0; + // Encode once just to find out field length + EncodeInteger( index ); + length += iPosition - oldPosition; + length ++; // response status is only one byte + iPosition = oldPosition; + // now we have calculated length, do the actual encoding + iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseStatus, 1 ); + iPosition++; + EncodeValueLength( length ); + EncodeInteger( index ); + TUint8 value = (TUint8) resultArray[i]->ResponseStatus(); + iEncodeBuffer->Write( iPosition, &value, 1 ); + iPosition++; + } + if ( resultArray[i]->ResponseText().Length() > 0 ) + { + oldPosition = iPosition; + length = 0; + // Encode once just to find out field length + EncodeInteger( index ); + EncodeTextStringL( resultArray[i]->ResponseText() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + // now we have calculated length, do the actual encoding + iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseText, 1 ); + iPosition++; + EncodeValueLength( length ); + EncodeInteger( index ); + EncodeTextStringL( resultArray[i]->ResponseText() ); + } + } + if ( resultArray[i]->ContentLocation().Length() > 0 ) + { + oldPosition = iPosition; + length = 0; + // Encode once just to find out field length + EncodeInteger( index ); + EncodeTextString( resultArray[i]->ContentLocation() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + // now we have calculated length, do the actual encoding + iEncodeBuffer->Write( iPosition, &KMmsAssignedContentLocation, 1 ); + iPosition++; + EncodeValueLength( length ); + EncodeInteger( index ); + EncodeTextString( resultArray[i]->ContentLocation() ); + } + } +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxDescriptionL() + { +// implemented for test purposes +#ifdef __WINS__ + EncodeMandatoryByte( KMmsAssignedMessageType, KMmsMessageTypeMBoxDescr ); + + // this must be USASCII or URL encoded + EncodeOptionalString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + + EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() ); + + // MMBox headers + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + // X-Mms-MM-State + EncodeOptionalByte( KMmsAssignedMMState, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() ); + // X-MMs-MM-Flags + // The keyword token should always be "add" or "remove" + EncodeKeywordArrayL(); + } + + // if we have set a date, insert it + EncodeDate( iMmsHeaders->Date() ); + + // sender is optional + if ( iMmsHeaders->Sender().Length() > 0 ) + { + EncodeSenderL( iMmsHeaders->Sender() ); + } + + // All recipient lists. If no recipients in some of the lists, + // nothing is encoded, no need to check separately + EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo ); + EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc ); + EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc ); + + // Message class + EncodeOptionalByte( KMmsAssignedMessageClass, + iMmsHeaders->MessageClass() ); + + // Subject + EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() ); + + // Priority + EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() ); + + // Delivery time + EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime, + iMmsHeaders->DeliveryTimeInterval(), + iMmsHeaders->DeliveryDate() ); + + // Expiration time + EncodeOptionalIntervalOrDate( KMmsAssignedExpiry, + iMmsHeaders->ExpiryInterval(), + iMmsHeaders->ExpiryDate() ); + + // Delivery report + EncodeOptionalByte( KMmsAssignedDeliveryReport, + iMmsHeaders->DeliveryReport() ); + + // Read reply + EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() ); + + if ( iMmsHeaders->MessageSize() > 0 ) + { + iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 ); + iPosition++; + TInt64 size = iMmsHeaders->MessageSize(); + EncodeLongInteger( size ); + } + + // MMBox description appears from 1.2 onwards - no use to check if we are + // 1.1 or later. + // Do we want to specify reply charging + if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted || + iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly ) + { + // Reply charging value + EncodeOptionalByte( KMmsAssignedReplyCharging, + iMmsHeaders->ReplyCharging() ); + // Reply charging deadline + EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline, + iMmsHeaders->ReplyChargingInterval(), + iMmsHeaders->ReplyChargingDate() ); + // Reply charging size + EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() ); + } + // Reply charging ID + EncodeOptionalString( KMmsAssignedReplyChargingID, + iMmsHeaders->ReplyChargingId() ); + + TInt i; + TUint oldPosition; + TUint length; + for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); ++i ) + { + oldPosition = iPosition; + length = 0; + // Encode once just to find out field length + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 ); + iPosition++; + EncodeValueLength( length ); + // now we have added length, do the actual encoding + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() ); + oldPosition = iPosition; + length = 0; + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() ); + length += iPosition - oldPosition; + iPosition = oldPosition; + iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 ); + iPosition++; + EncodeValueLength( length ); + EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() ); + EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() ); + } + + if ( iNumberOfAttachments > 0 ) + { + EncodeMultipartMixedHeaderL(); + } + +#endif + } + +// --------------------------------------------------------- +// 8-bit version (no conversions) +// --------------------------------------------------------- +// +void CMmsEncode::EncodeTextString( const TDesC8& aString ) + { + // Only Content-Id needs a quoted string, for example + // " + // content-id encoding uses a separate function that adds the quote. + // EncodeQuotedTextString + + // Check if we need a Quote (This does not mean the quoted string.) + TInt length = aString.Length(); + if ( length > 0 && aString[0] >= KMms0x80 ) + { + iEncodeBuffer->Write( iPosition, &KMmsQuote, 1 ); + iPosition++; + } + + iEncodeBuffer->Write( iPosition, aString, length ); + iPosition += length; + + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + + } + +// --------------------------------------------------------- +// 8-bit version (no conversions) +// --------------------------------------------------------- +// +void CMmsEncode::EncodeQuotedTextString( const TDesC8& aString ) + { + // Before this function is called, the caller must check + // that the length of the string is not 0. Otherwise this + // makes no sense. You cannot quote a zero-length string + + // We need quoted string for content-id. + // So far for nothing else. + // (for the filename in MIME headers, too, just in case) + + // We add one quote to the beginning, nothing to the end. + // As we start with a quote, we don't need to check for the special + // Quote that is needed if a string starts with a character above 128 + TInt length = aString.Length(); + iEncodeBuffer->Write( iPosition, &KMmsStringQuote, 1 ); + iPosition++; + + iEncodeBuffer->Write( iPosition, aString, length ); + iPosition += length; + + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + + } + +// --------------------------------------------------------- +// unicode version +// --------------------------------------------------------- +// +void CMmsEncode::EncodeTextStringL( const TDesC& aString ) + { + + // What about a RFC2068 quoted strings - + // see explanation in EncodeTextString( const TDesC8& aString ) function + + TInt i = 0; + TInt length = aString.Length(); + + TBool safe = IsStringSafe( aString ); + + // If the string can be encoded as ASCII, go ahead + if ( safe ) + { + TUint8 character; + // No need to check if we need a quote - if we are safe, we have + // no characters >= 128. + + for ( i = 0; i < aString.Length(); ++i ) + { + character = TUint8( aString[i] & KMms0xFF ); + iEncodeBuffer->Write( iPosition, &character, 1 ); + iPosition++; + } + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + } + else + { + // if our length is 0, we are safe, no need to check the + // length here anymore + // we must convert to utf-8 + + //one ucs-2 character should never produce more than 4 bytes when converted to utf-8 + HBufC8* buffer = HBufC8::NewL( aString.Length() * KMms4 ); // paranoid. + // we don't leave while we need buffer + TPtr8 buf8 = buffer->Des(); + + // if conversion fails, something is really seriously wrong + iError = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, aString ); + + length = buf8.Length(); + // utf8 character set encoding needs one byte (fits into short integer) + length += KMms2; // add character set encoding byte and trailing NULL + + if ( buf8[0] >= KMms0x80 ) // 128 + { + length++; // we will need a quote byte at the start... + } + + EncodeValueLength( length ); + + // add one byte of character set + // this is a short integer, high bit must be set + TUint8 charset = KMmsUtf8 | KMms0x80; + + iEncodeBuffer->Write( iPosition, &charset, 1 ); + iPosition++; + + // Check if we need a quote + if ( buf8[0] >= KMms0x80 ) // 128 + { + iEncodeBuffer->Write( iPosition, &KMmsQuote, 1 ); + iPosition++; + } + + // Then write the string itself + iEncodeBuffer->Write( iPosition, buf8, buf8.Length() ); + iPosition += buf8.Length(); + + // Add terminating NULL + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + + delete buffer; + buffer = NULL; + } + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeDate( const TInt64& aDate) + { + + if ( aDate == 0 ) + { + return; + } + + iEncodeBuffer->Write( iPosition, &KMmsAssignedDate, 1 ); + iPosition++; + + EncodeLongInteger( aDate ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeLongInteger( const TInt64& aLongInteger ) + { + + TUint8 length = 0; // number of bytes we will need + // The long integer will take 8 bytes or less + TUint8 array[KMms8]; + + TInt64 temp = aLongInteger; + TInt i = 0; + TInt64 reminder = 0; + + for ( i = 7; i >= 0; --i ) + { + reminder = temp % 0x100; + temp = temp / 0x100; + array[i] = TInt8 ( I64INT( reminder ) ) ; + } + + length = KMms8; + i = 0; + // The array has 8 elements, so this is safe. + while( ( i < KMms8 ) && ( array[i] == 0 ) ) + { + i++; + length--; + } + + // a zero should be coded as short integer. + // However, if there is a valid reason to code a zero as a long integer, + // we allow it. The caller should know what he is doing. + if ( length == 0 ) + { + length = 1; + } + + // write short length + iEncodeBuffer->Write( iPosition, &length, 1 ); + iPosition++; + + // write as many bytes as were non-zero + // array index will stay withing limits because of the way it was calculated + iEncodeBuffer->Write( iPosition, &(array[KMms8 - length] ), length ); + iPosition+= length; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeInteger( TUint aInteger ) + { + // KMms1 = 1 + // KMms2 = 2 + // KMms3 = 3 + // KMms4 = 4 + // KMms5 = 5 + // etc. + + TUint8 byte = 0; + + if ( aInteger <= KMmsShortIntegerLimit127 ) + { + byte = ( TInt8 ) aInteger; + byte |= KMms0x80; + iEncodeBuffer->Write( iPosition, &byte, 1); + iPosition++; + return; // this was easy + } + + TUint8 length = KMms4; // number of bytes we will need + TUint8 array[KMms4]; + + TUint temp = aInteger; + byte = TInt8( ( temp >> KMms24 ) & KMms0xFF ); + + while ( byte == 0 && length > 0 ) + { + length--; + temp = temp << KMms8; + byte = TInt8( ( temp >> KMms24 ) & KMms0xFF ); + } + + TUint i = 0; + for ( i = 0; i < length; ++i ) + { + array[i] = TInt8( ( temp >> ( KMms8 * ( KMms3 - i ) ) ) & KMms0xFF ); + } + + // write short length + iEncodeBuffer->Write( iPosition, &length, 1 ); + iPosition++; + + // write as many bytes as were non-zero + // aInteger was tested in the beginning - it needs at least one byte + // So length is at least 1 and the array has been properly initialized. + iEncodeBuffer->Write( iPosition, &array[0], length ); + iPosition+= length; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeSenderL( const TPtrC& aSender ) + { + + iEncodeBuffer->Write( iPosition, &KMmsAssignedFrom, 1 ); + iPosition++; + + // We must insert value length. + + if ( aSender.Length() == 0 ) + { + // The MMSC must insert our address + TUint8 length; + length = 1; + iEncodeBuffer->Write( iPosition, &length, 1 ); + iPosition++; + iEncodeBuffer->Write( iPosition, &KMmsInsertAddressToken, 1 ); + iPosition++; + return; + } + + // Now we must insert something + TUint length = 1; // address present token + + if ( aSender.Find( KMiuMau ) != KErrNotFound ) + { + TBool safe = IsStringSafe( aSender ); + if ( safe ) + { + // this was easy, just ASCII, no conversion + length += aSender.Length(); + length ++; // room for terminating zero + } + else // not USASCII, charset must be specified + { + // worst case scenario. + // must be encoded as UTF-8, value length and character set + // must be added + + // one ucs-2 character will not produce more than 4 bytes when converted to utf-8 + HBufC8* buffer = HBufC8::NewL( aSender.Length() * KMms4 ); // paranoid. + // we don't need to push buffer onto cleanup stack, as we don't + // leave while we are using it + TPtr8 buf8 = buffer->Des(); + + // if we get error here, something is badly wrong + iError = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, aSender ); + + length += buf8.Length(); + // utf8 character set encoding needs one byte (short integer) + length += KMms2; // add character set encoding byte and trailing NULL + + if ( buf8[0] >= KMms0x80 ) // 128 + { + length++; // we will need a quote byte at the start... + } + delete buffer; + buffer = NULL; + } + } + else // no miumau found + { + // phone number, must be ASCII + length += aSender.Length(); + length ++; // room for terminating zero + length += KMmsPlmnLength; // type indication + // this should be the length, if we have a phone number (no alias.) + } + + EncodeValueLength( length ); + + iEncodeBuffer->Write( iPosition, &KMmsAddressPresentToken, 1 ); + iPosition++; + + // If the address contains some non-ascii characters, + // it must be converted to utf-8 + + EncodeAddressL( aSender ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAddressL( const TPtrC& aAddress ) + { + // Supports only address types PLMN and email. + // If the address string contains a @ character, + // it is interpreted as an email + + // Internal alias is removed + + TMmsAddressType addressType = EMmsAddressTypeUnknown; + HBufC* realAddress = HBufC::NewL( aAddress.Length() ); + CleanupStack::PushL( realAddress ); + + TInt error = KErrNone; + + TPtr realAddressPointer = realAddress->Des(); + error = TMmsGenUtils::AddressTypeAndRealAddress( + aAddress, + addressType, + realAddressPointer, + aAddress.Length()); + + if ( error != KErrNone || addressType == EMmsAddressTypeUnknown ) + { + // could not resolve. Send unchanged + realAddress->Des().Copy( aAddress ); + if ( aAddress.Find( KMiuMau ) != KErrNotFound ) + { + addressType = EMmsAddressTypeEmail; + } + else + { + addressType = EMmsAddressTypeMobile; + } + } + + if ( addressType == EMmsAddressTypeEmail ) + { + // email address + // If the address contains only ASCII characters, + // it can be sent as text string, if not, it must be sent as utf-8 + + EncodeTextStringL( *realAddress ); + + } + else + { + // must be a phone number + // We expect for now that the format is correct as is + // All legal characters present in a phone number are ASCII + + TInt i = 0; + TUint8 character = 0; + realAddressPointer = realAddress->Des(); + for ( i = 0; i < realAddress->Length(); ++i ) + { + // The array index is safe because i is always < realAddress->Length(). + character = TUint8( realAddressPointer[i] & KMms0xFF ); + iEncodeBuffer->Write( iPosition, &character, 1 ); + iPosition++; + } + iEncodeBuffer->Write( iPosition, KMmsPlmn, KMmsPlmnLength ); + iPosition += KMmsPlmnLength; + iEncodeBuffer->Write( iPosition, &KMmsNull, 1 ); + iPosition++; + } + CleanupStack::PopAndDestroy( realAddress ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeValueLength( TUint aLength ) + { + + TUint8 shortLength = 0; + + if ( aLength <= KMms30 ) + { + // short length + shortLength = TUint8( aLength ) ; + iEncodeBuffer->Write( iPosition, &shortLength, 1 ); + iPosition++; + } + else + { + iEncodeBuffer->Write( iPosition, &KMmsLengthQuote, 1 ); + iPosition++; + EncodeUintvar( aLength ); + } + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeUintvar( TUint aInteger ) + { + + // The value is split into 7 bit chunks, and continue bit set + // when needed. + + // No more than 5 bytes will be produced + TUint8 buffer[KMms5]; + TUint temp = aInteger; + TInt i; + + for ( i = 0; i < KMms5; ++i ) + { + buffer[KMms4 - i] = TUint8( temp & KMms0x7F ); + temp >>= KMms7; + } + + i = 0; + // buffer indexes are safe because the buffer has been defined long enough. + while ( i < KMms4 && buffer[i] == 0 ) + { + i++; + } + + TInt j; + + for ( j = i; j < KMms4; ++j ) + { + // buffer indexes are safe because the buffer has been defined long enough. + buffer[j] |= KMms0x80; // set Continue bit, but never to last + } + + // buffer indexes are safe because the buffer has been defined long enough. + iEncodeBuffer->Write( iPosition, &buffer[i], KMms5 - i ); + iPosition += KMms5 - i; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TInt CMmsEncode::GetUintvarLength( TUint aInteger ) + { + + // The value is split into 7 bit chunks, and continue bit set + // when needed. + + // No more than 5 bytes will be produced + TUint8 buffer[KMms5]; + TUint temp = aInteger; + TInt i; + + for (i = 0; i < KMms5; ++i ) + { + buffer[KMms4 - i] = TUint8( temp & KMms0x7F ); + temp >>= KMms7; + } + + i = 0; + // buffer indexes are safe because the buffer has been defined long enough. + while ( i < KMms4 && buffer[i] == 0 ) + { + i++; + } + + return KMms5 - i; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeRecipientL( const CDesCArray& aRecipientList, + TMmsRecipients aType ) + { + + TInt i; + TInt size = aRecipientList.Count(); + if ( size == 0 ) + { + return; + } + + TUint8 recipientType = KMmsAssignedTo; + + switch ( aType ) + { + case EMmsTo: + recipientType = KMmsAssignedTo; + break; + case EMmsCc: + recipientType = KMmsAssignedCc; + break; + case EMmsBcc: + recipientType = KMmsAssignedBcc; + break; + default: + break; + } + + for ( i = 0; i < size; ++i ) + { + // check for fakes + if ( aRecipientList[i].Length() > 0 ) + { + iEncodeBuffer->Write( iPosition, &recipientType, 1 ); + iPosition++; + + EncodeAddressL( aRecipientList[i] ); + } + + } + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeOptionalStringL( TUint8 aHeader, + const TPtrC16& aString ) + { + + if ( aString.Length() == 0 ) + { + return; + } + + iEncodeBuffer->Write( iPosition, &aHeader, 1 ); + iPosition++; + + EncodeTextStringL( aString ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeOptionalString( TUint8 aHeader, const TPtrC8& aString ) + { + + if ( aString.Length() == 0 ) + { + return; + } + + EncodeHeaderAndTextString( aHeader, aString ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeIntervalOrDate( TInt aInterval, const TInt64& aDate ) + { + + TInt64 temp = 0; + TUint8 token = 0; + + if ( aDate != 0 ) + { + temp = aDate; + token = KMmsAbsoluteToken; + } + else + { + temp = aInterval; + token = KMmsRelativeToken; + } + + // calculate value length + + TUint length = KMms8; + TUint mask = 0xff000000; + + while ( ( I64HIGH( temp ) & mask ) == 0 && length > KMms4) + { + mask >>= KMms8; + length--; + } + + mask = 0xff000000; + + // Test if the whole high half was zero + if ( I64HIGH( temp ) == 0 ) + { + while ( ( I64LOW( temp ) & mask ) == 0 && length > 1) + { + mask >>= KMms8; + length--; + } + } + + // now add short length and absolute/relative token + + length += KMms2; + + EncodeValueLength( length ); + + iEncodeBuffer->Write( iPosition, &token, 1 ); + iPosition++; + + EncodeLongInteger( temp ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeReplyChargingSize( TInt aReplyChargingSize ) + { + if ( aReplyChargingSize == 0 ) + { + return; + } + TInt64 size = aReplyChargingSize; + iEncodeBuffer->Write( iPosition, &KMmsAssignedReplyChargingSize, 1 ); + iPosition++; + + EncodeLongInteger( size ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeOptionalByte( TUint8 aHeader, TInt aValue ) + { + if ( aValue == 0 ) + { + return; // not set, nothing to encode, header is optional + } + EncodeMandatoryByte( aHeader, aValue ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMandatoryByte( TUint8 aHeader, TInt aValue ) + { + // When ecoding mandatory byte even value 0 is allowed. + // It will become 0x80, and be correctly decoded back to 0. + TUint8 value = TUint8 ( aValue ) | KMms0x80 ; + + iEncodeBuffer->Write( iPosition, &aHeader, 1 ); + iPosition++; + + iEncodeBuffer->Write( iPosition, &value, 1 ); + iPosition++; + } + + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeOptionalIntervalOrDate( TUint8 aHeader, + TInt aInterval, + const TInt64& aDate ) + { + if ( aInterval == 0 && aDate == 0 ) + { + return; // not set, nothing to encode, header is optional + } + + iEncodeBuffer->Write( iPosition, &aHeader, 1 ); + iPosition++; + + EncodeIntervalOrDate( aInterval, aDate ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeHeaderAndTextString( TUint8 aHeader, + const TDesC8& aString ) + { + + iEncodeBuffer->Write( iPosition, &aHeader, 1 ); + iPosition++; + + EncodeTextString( aString ); + + } + + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TBool CMmsEncode::IsStringSafe( const TDesC& aString ) + { + + // Very simple check to see if string is "safe" ASCII + // Used for headers, which are short strings + + TInt i; + TBool safe = ETrue; + + for ( i = 0; i < aString.Length() && safe; ++i ) + { + if ( aString[i] < KMmsLowestAscii || aString[i] >= KMmsHighestAscii ) + { + safe = EFalse; + } + } + return safe; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +TBool CMmsEncode::IsStringSafe( const TDesC8& aString, TInt& aNumNonSafe ) + { + + // Very simple check to see if string is "safe" ASCII + // Used for headers, which are short strings + // spaces count as non-safe because their number must be taken into + // account when calculating the length + + const TInt KIntQuestion = 0x3F; + const TInt KIntEquals = 0x3D; + + TInt i; + aNumNonSafe = 0; + TBool safe = ETrue; + + for ( i = 0; i < aString.Length()/* && safe*/; ++i ) + { + if ( aString[i] < KMmsLowestAscii || aString[i] >= KMmsHighestAscii ) + { + safe = EFalse; + aNumNonSafe++; + } + if ( aString[i] == KIntQuestion || aString[i] == KIntEquals ) + { + // These are safe but must be encoded if quoted printable + // encoding is used. The number is needed to check the header length + aNumNonSafe++; + } + if ( aString[i] == KMmsIntUnderscore ) + { + // This must always be encoded as base64 because Symbian + // does not encode underscores when quoted printable is used. + aNumNonSafe = KMaxEncodingLength; + } + + } + return safe; + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMultipartRelatedHeaderL( const TMsvAttachmentId aRootId ) + { + + iEncodeBuffer->Write( iPosition, &KMmsAssignedContentType, 1 ); + iPosition++; + + // We have parameters (start), so we must use Content-general-form + + // We need the content-id for the root part. If it is not defined already, + // we must generate one. + + TUint headerLength = 1; // one byte for well known media + TInt cleanupCount = 0; // we must keep track what we have on store + + User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) ); + + CMsvStore* store = iEntryWrapper->EditStoreL(); + CleanupStack::PushL( store ); cleanupCount++; + MMsvAttachmentManager& attachMan = store->AttachmentManagerL(); + CMsvAttachment* attachmentInfo = NULL; + attachmentInfo = attachMan.GetAttachmentInfoL( (TMsvAttachmentId) aRootId ); + CleanupStack::PushL( attachmentInfo ); cleanupCount++; + + iMimeHeaders->RestoreL( *attachmentInfo ); + + if ( iError != KErrNone ) + { + CleanupStack::PopAndDestroy( cleanupCount ); + return; + } + + TPtrC8 cid = iMimeHeaders->ContentId(); + TBufC8 target; + + // If cid has not been defined, we must generate one + if ( cid.Length() == 0 ) + { + TTime now; + now.UniversalTime(); + TInt random; + TInt64 seed = now.Int64(); + random = Math::Rand( seed ); + // type conversions irrelevant - just creating a magic number for content id + target.Des().Num( random ); + target.Des().Insert(0, KMmsLeftAngle ); + target.Des().Append( KMmsRightAngle ); + cid.Set( target.Des() ); + // exclude the angle brackets + iMimeHeaders->SetContentIdL( cid.Mid( 1, cid.Length() - KMms2 ) ); + // save the cid to be present when we create the attachment headers + // If this does not succeed, the message will have no root. + MMsvAttachmentManagerSync& attachManSync = + store->AttachmentManagerExtensionsL(); + iMimeHeaders->StoreL( *attachmentInfo ); + attachManSync.ModifyAttachmentInfoL( attachmentInfo ); + // attachment manager now own attachemnt info + CleanupStack::Pop( attachmentInfo ); cleanupCount--; + store->CommitL(); + attachmentInfo = attachMan.GetAttachmentInfoL( (TMsvAttachmentId) aRootId ); + CleanupStack::PushL( attachmentInfo ); cleanupCount++; + } + + // add start header length & + // assigned number for start parameter, and terminating zero for cid + headerLength += cid.Length() + KMms2; + if ( cid.Find( KMmsLeftAngle ) != 0 ) + { + headerLength += KMms2; // we must add angle bracket + } + + // Then the length of the content-type header... + TPtrC8 contentTypeString = iMimeHeaders->ContentType(); + + HBufC8* tempContentType = HBufC8::NewL( iMimeHeaders->ContentType().Length() + + iMimeHeaders->ContentSubType().Length() + 1 ); + CleanupStack::PushL( tempContentType ); + tempContentType->Des().Copy( iMimeHeaders->ContentType() ); + if ( ( iMimeHeaders->ContentType().Find( KMmsSlash8 ) != + ( iMimeHeaders->ContentType().Length() - 1 ) ) && + ( iMimeHeaders->ContentSubType().Find( KMmsSlash8 ) != 0 ) && + ( iMimeHeaders->ContentSubType().Length() != 0 ) ) + { + tempContentType->Des().Append( KMmsSlash8 ); + } + tempContentType->Des().Append( iMimeHeaders->ContentSubType() ); + attachmentInfo->SetMimeTypeL( tempContentType->Des() ); + CleanupStack::PopAndDestroy( tempContentType ); + contentTypeString.Set( attachmentInfo->MimeType() ); + + if ( contentTypeString.Length() == 0 ) + { + // We need a content type... + // If we don't know, we say "Any" + contentTypeString.Set( KMmsAny ); + } + // Check if we have well-known media. + TInt8 rootContentType = -1; + + TInt8 i = 0; + for ( i = 0; i < KNumberContentTypes && rootContentType < 0; ++i ) + { + if ( contentTypeString.CompareF( TPtrC8( KContentTypeTable[i] ) ) == 0 ) + { + rootContentType = i; + } + } + + // start parameter assignment + headerLength += 1; + if ( rootContentType != -1 ) + { + // well known content type + headerLength += 1; + } + else + { + // string + terminating zero + headerLength += contentTypeString.Length() + 1; + } + + TPtrC8 appIdPtr; + appIdPtr.Set( KMmsJavaApplicationId ); + // Java application id parameters added to content-type + if ( iMmsHeaders->ApplicId().Length() > 0 ) + { + headerLength += appIdPtr.Length() + 1 ; + headerLength += iMmsHeaders->ApplicId().Length() + 1; + } + appIdPtr.Set( KMmsJavaReplyApplicationId ); + if ( iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + headerLength += appIdPtr.Length() + 1 ; + headerLength += iMmsHeaders->ReplyApplicId().Length() + 1; + } + + // write general encoding length + EncodeValueLength( headerLength ); + + // Then the Media Type with parameters + + // We are multipart/related, which is a well-known media + // encode as short integer + TUint8 byte = KMmsAssignedApplicationVndWapMultipartRelated | KMms0x80; + iEncodeBuffer->Write( iPosition, &byte, 1 ); + iPosition++; + + // Then the start parameter and cid text string + byte = KWspStart | KMms0x80; + + if ( cid.Length() > 0 ) + { + HBufC8* tempContentId = HBufC8::NewL( iMimeHeaders->ContentId().Length() + KMms2 ); + CleanupStack::PushL( tempContentId ); + if ( cid.Find( KMmsLeftAngle ) != 0 ) + { + tempContentId->Des().Copy( KMmsLeftAngle ); + tempContentId->Des().Append( cid ); + tempContentId->Des().Append( KMmsRightAngle ); + } + else + { + tempContentId->Des().Copy( cid ); + } + EncodeHeaderAndTextString( byte, tempContentId->Des() ); + CleanupStack::PopAndDestroy( tempContentId ); + } + + + // next the content type of the root part + byte = KWspRelatedType | KMms0x80; + // and the actual type either as a well-known media type + // or a text string. + + if ( rootContentType != -1 ) + { + // Well known content type. + // EncodeMandatoryByte will set the high bit + EncodeMandatoryByte( byte, rootContentType ); + } + else + { + // string + terminating zero + EncodeHeaderAndTextString( byte, contentTypeString ); + } + + if ( iMmsHeaders->ApplicId().Length() > 0 || + iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + EncodeApplicationIdParametersL(); + } + + // encode number of parts + EncodeUintvar( iNumberOfAttachments ); + + // get rid of stuff we put on stack + CleanupStack::PopAndDestroy( cleanupCount ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMultipartMixedHeaderL() + { + + // If Java has added application id or reply-to application id, + // even multipart/mxed type needs parameters. + + TUint headerLength = 0; // assume no parameters + + if ( iMmsHeaders->ApplicId().Length() > 0 || + iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + headerLength = 1; // one byte for well known media + TPtrC8 appIdPtr; + appIdPtr.Set( KMmsJavaApplicationId ); + if ( iMmsHeaders->ApplicId().Length() > 0 ) + { + headerLength += appIdPtr.Length() + 1 ; + headerLength += iMmsHeaders->ApplicId().Length() + 1; + } + appIdPtr.Set( KMmsJavaReplyApplicationId ); + if ( iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + headerLength += appIdPtr.Length() + 1 ; + headerLength += iMmsHeaders->ReplyApplicId().Length() + 1; + } + } + + iEncodeBuffer->Write( iPosition, &KMmsAssignedContentType, 1 ); + iPosition++; + + if ( headerLength > 0 ) + { + // write general encoding length + EncodeValueLength( headerLength ); + } + + // encode as short integer + TUint8 contentType = KMmsAssignedApplicationVndWapMultipartMixed | KMms0x80; + iEncodeBuffer->Write( iPosition, &contentType, 1 ); + iPosition++; + + if ( iMmsHeaders->ApplicId().Length() > 0 || + iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + EncodeApplicationIdParametersL(); + } + + // No more parameters for multipart/mixed, actual parts follow + + // encode number of parts + EncodeUintvar( iNumberOfAttachments ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeKeywordArrayL() + { + TInt i = 0; + TInt length = 0; + + // caller must check that iMmsHeaders->ReadOnlyMMBoxMessageHeaders() is not NULL + CMmsMMBoxMessageHeaders& temp = iMmsHeaders->MMBoxMessageHeadersL(); + for ( i = 0; i < temp.KeywordArray().Count(); ++i ) + { + length = temp.KeywordArray()[i]->Keyword().Length(); + if ( length > 0 ) + { + iEncodeBuffer->Write( iPosition, &KMmsAssignedMMFlags, 1 ); + iPosition++; + // do some fake encoding to get the text length + TUint oldPosition = iPosition; // we will return here + EncodeTextStringL( temp.KeywordArray()[i]->Keyword() ); + length = iPosition - oldPosition; + iPosition = oldPosition; // return to original place + length += 1; // token must come before string + EncodeValueLength( length ); + TUint8 token = (TUint8) temp.KeywordArray()[i]->Token(); + if ( iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewReq || + iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewConf || + iMmsHeaders->MessageType() == KMmsMessageTypeMBoxDescr ) + { + // the token must always be filter for MMbox PDUs + token = KMmsFilterToken; + } + iEncodeBuffer->Write( iPosition, &token, 1 ); + iPosition++; + EncodeTextStringL( temp.KeywordArray()[i]->Keyword() ); + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeOptionalInteger( TUint8 aHeader, TUint aValue ) + { + if ( aValue == 0 ) + { + return; // not set, nothing to encode, header is optional + } + + iEncodeBuffer->Write( iPosition, &aHeader, 1 ); + iPosition++; + + EncodeInteger( aValue ); + + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeAttributes( RArray& aAttributeArray ) + { + TInt i; + + for ( i = 0; i < aAttributeArray.Count(); ++i ) + { + EncodeMandatoryByte( KMmsAssignedAttributes, aAttributeArray[i] ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeMMBoxStates( RArray& aStateArray ) + { + TInt i; + + for ( i = 0; i < aStateArray.Count(); ++i ) + { + EncodeMandatoryByte( KMmsAssignedMMState, aStateArray[i] ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeContentLocationArray() + { + TBool mustCheck = EFalse; + if ( iMmsHeaders->ContentLocation().Length() > 0 ) + { + mustCheck = ETrue; + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ContentLocation() ); + } + // If there is content location array, encode it, too. + // A content-location should never occur in both places, but we still check + + TInt i = 0; + if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() ) + { + for ( i = 0; + i < iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList().Count(); + i++ ) + { + if ( !( mustCheck && iMmsHeaders->ContentLocation().Compare( + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] ) == 0 ) ) + { + // the content location in the list does not appear as + // separate header, we must add it + EncodeHeaderAndTextString( KMmsAssignedContentLocation, + iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] ); + } + } + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeStartingHeaders( TInt aMessageType, + const TPtrC8& aTID, TInt aVersion ) + { + // EncodeMandatoryByte will always set the high bit. + + // Message type + EncodeMandatoryByte( KMmsAssignedMessageType, aMessageType ); + // TID if present + EncodeOptionalString( KMmsAssignedTID, aTID ); + // MMS encapsulation version + EncodeMandatoryByte( KMmsAssignedMmsVersion, aVersion ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +// +void CMmsEncode::EncodeApplicationIdParametersL() + { + if ( iMmsHeaders->ApplicId().Length() > 0 ) + { + EncodeTextString( KMmsJavaApplicationId ); + EncodeTextStringL( iMmsHeaders->ApplicId() ); + } + if ( iMmsHeaders->ReplyApplicId().Length() > 0 ) + { + EncodeTextString( KMmsJavaReplyApplicationId ); + EncodeTextStringL( iMmsHeaders->ReplyApplicId() ); + } + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +void CMmsEncode::Dump() + { + // no dump if not logging +#ifndef _NO_MMSS_LOGGING_ + TInt error = KErrNone; + // if no can do, sorry. + TRAP( error, + { + if ( iEntryWrapper ) + { + if ( ( iEntryWrapper->GetDumpFlag() ) && + iEncodeBuffer && + iEncodeBuffer->Size() > 0 ) + { + iFileName = KMmsDefaultLogDirectory; + TUint att; + if ( iFs.Att( iFileName, att ) == KErrNone ) + { + _LIT( KRelated, "Sent.mms"); + iParse.Set( iFileName, &KRelated, NULL ); + iFileName = iParse.FullName(); + User::LeaveIfError( CApaApplication::GenerateFileName( + iFs, iFileName ) ); + RFile file; + User::LeaveIfError( file.Create( iFs, iFileName, + EFileWrite | EFileShareExclusive ) ); + // for message id generation + iParse.Set( iFileName, NULL, NULL ); + + // the data is supposed to be in the encode buffer + TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition ); + file.Write( ptr ); + file.Flush(); + + // done - close files + file.Close(); + } + } + } + } + ); + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("Dump left with error %d"), error ); + } +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +void CMmsEncode::DumpAppend() + { + + // no dump if not logging +#ifndef _NO_MMSS_LOGGING_ + TInt error = KErrNone; + // if no can do, sorry. + TRAP( error, + { + if ( iEntryWrapper ) + { + if ( ( iEntryWrapper->GetDumpFlag() ) && + iEncodeBuffer && + iEncodeBuffer->Size() > 0 ) + { + iFileName = KMmsDefaultLogDirectory; + TUint att; + if ( iFs.Att( iFileName, att ) == KErrNone ) + { + RFile file; + iFileName = iParse.FullName(); + User::LeaveIfError( file.Open( iFs, iFileName, + EFileWrite | EFileShareExclusive ) ); + TInt position = 0; // seek to end + file.Seek( ESeekEnd, position ); + // the data is supposed to be in the encode buffer + TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition ); + file.Write( ptr ); + file.Flush(); + // done - close files + file.Close(); + } + } + } + } + ); + if ( error != KErrNone ) + { + TMmsLogger::Log( _L("DumpAppend left with error %d"), error ); + } +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +void CMmsEncode::EncodeApplicationHeadersL() + { +// Application headers are only supported in WINS for testing purposes +// Complete support for routing messages to arbitrary applications +// requires support for selecting which applications are allowed +// to send messages etc. +#ifdef __WINS__ + // The optional string functions check the length + // and return without doing anything if the length of the string is 0 + if ( IsStringSafe( iMmsHeaders->ApplicId() ) ) + { + // We only send this header if it is us-ascii only + // There is no encoding defined, so if this is converted + // from unicode to some other character set there is + // no guarantee that the recipient can decode it. + EncodeOptionalStringL( KMmsAssignedApplicId, iMmsHeaders->ApplicId() ); + } + + if ( IsStringSafe( iMmsHeaders->ReplyApplicId() ) ) + { + EncodeOptionalStringL( KMmsAssignedReplyApplicId, iMmsHeaders->ReplyApplicId() ); + } + + EncodeOptionalString( KMmsAssignedAuxApplicInfo, iMmsHeaders->AuxApplicInfo() ); +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +void CMmsEncode::EncodeCancelRequest() + { +// implemented for test purposes +#ifdef __WINS__ + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeCancelReq, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + EncodeOptionalString( KMmsAssignedCancelId, iMmsHeaders->ReplaceCancelId() ); +#endif + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +void CMmsEncode::EncodeCancelResponse() + { + // Insert message type, TID and version number + EncodeStartingHeaders( KMmsMessageTypeCancelConf, + iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() ); + + EncodeOptionalByte( KMmsAssignedCancelStatus, iMmsHeaders->CancelStatus() ); + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +HBufC8* CMmsEncode::EncodeQuotedPrintableWordL( const TPtrC8& aSource ) + { + // We have calculated that it fits + + TInt bufferLength = KMaxNameBufferLength; + + HBufC8* buffer = HBufC8::NewL( bufferLength ); + CleanupStack::PushL( buffer ); + TPtr8 ptr = buffer->Des(); + buffer->Des().Copy( KMmsQuotedPreamble ); + ptr.SetLength( bufferLength ); + TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength ); + encodeBuffer.SetLength( 0 ); // empty the buffer + + TImCodecQP encoder; + + // The function would return the number of characters written to the buffer. + // We don't do anything with the result, so we ignore the return value. + encoder.Encode( aSource, encodeBuffer ); + + ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength ); + + buffer->Des().Append( KMmsEncodingTrailer ); + CleanupStack::Pop( buffer ); + return buffer; + } + +// --------------------------------------------------------- +// +// --------------------------------------------------------- +HBufC8* CMmsEncode::EncodeBase64WordL( const TPtrC8& aSource ) + { + // ((length + 2)/3)*4 3 bytes alwaus produces 4 encoded bytes. Allow filler + TInt bufferLength = KMmsEncodingExtraLength + ( aSource.Length() + KMms2 ) / KMms3 * KMms4; + + HBufC8* buffer = HBufC8::NewL( bufferLength ); + CleanupStack::PushL( buffer ); + TPtr8 ptr = buffer->Des(); + buffer->Des().Copy( KMmsBase64Preamble ); + ptr.SetLength( bufferLength ); + + TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength ); + encodeBuffer.SetLength( 0 ); // empty the buffer + + TImCodecB64 encoder; + + // It is rather unclear what this function actually returns (no documentation found). + // Therefore we just ignore the result. + // Our buffer is long enough for the result to always fit. + encoder.Encode( aSource, encodeBuffer ); + + ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength ); + + buffer->Des().Append( KMmsEncodingTrailer ); + CleanupStack::Pop( buffer ); + return buffer; + } + + +// --------------------------------------------------------- +// CMmsEncode::PreProcessAttachmentDataL +// Open the message store(Edit mode) and process attachments for further encoding in required encoding type. +// Update the message store accordingly with the new encoding type and data in the corresponding attachments. +// --------------------------------------------------------- +void CMmsEncode::PreProcessAttachmentDataL() + { + TInt error = KErrNone; + RFile attachFile; + TBool retVal = EFalse; + TInt currAttachI; + +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- start ") ); +#endif /* _NO_MMSS_LOGGING_ */ + CMsvStore* editStore = iEntryWrapper->EditStoreL(); + CleanupStack::PushL( editStore ); + + MMsvAttachmentManager& attachMan = editStore->AttachmentManagerL(); + MMsvAttachmentManagerSync& attachManagerSync = editStore->AttachmentManagerExtensionsL(); + TInt numberOfAttachments = attachMan.AttachmentCount(); + CMsvAttachment* attachmentInfo = NULL; + iTextUtils = CMsgTextUtils::NewL( iFs ); + + for ( currAttachI = 0; ( currAttachI < numberOfAttachments ) && ( error == KErrNone ); currAttachI++ ) + { + //gets the ownership from attachment manager + attachmentInfo = attachMan.GetAttachmentInfoL( currAttachI ); + CleanupStack::PushL( attachmentInfo ); + iMimeHeaders->RestoreL( *attachmentInfo ); +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- Attchment:%d "), currAttachI ); +#endif /* _NO_MMSS_LOGGING_ */ + + if ( iFileOpen ) + { + // close any file in case we have been reset while the file is still open + iAttachFile.Close(); + iFileOpen = EFalse; + } + + retVal = CheckAndUpdateAttachmentL(*attachmentInfo, *iMimeHeaders); + + if( retVal ) + { + TRAPD( + err, + attachManagerSync.ModifyAttachmentInfoL(attachmentInfo); + editStore->CommitL(); + ); +#ifndef _NO_MMSS_LOGGING_ + if(err != KErrNone) + { + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit error: %d"), err ); + } + else + { + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit success") ); + } +#endif /* _NO_MMSS_LOGGING_ */ + /* attachmentInfo ownership is transferred to attachment manager + * Hence, JUST pop attachmentInfo, DO NOT Destroy. + */ + CleanupStack::Pop( attachmentInfo ); + } + else + { + CleanupStack::PopAndDestroy( attachmentInfo ); + attachmentInfo = NULL; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: Tgt encoding NOT Reqd.") ); +#endif /* _NO_MMSS_LOGGING_ */ + } + } + CleanupStack::PopAndDestroy( editStore ); + + delete iTextUtils; + iTextUtils = NULL; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- end ") ); +#endif /* _NO_MMSS_LOGGING_ */ + } + +// --------------------------------------------------------- +// CMmsEncode::CheckAndUpdateAttachmentL +// Check and proceed if given attachment can be encoded using target encoding type based on its content type. +// Returns False if target encoding is not supported for this content type. +// --------------------------------------------------------- +TBool CMmsEncode::CheckAndUpdateAttachmentL( CMsvAttachment& aAttachmentInfo, + CMsvMimeHeaders& aMimeHeaders ) + { + //get the content type string... and set to attachment if required + TInt contentType = -1; // indicate not found + TPtrC8 contentTypeString; + TBool retVal = EFalse; +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- start ") ); +#endif /* _NO_MMSS_LOGGING_ */ + + HBufC8* tempContentType = NULL; + contentTypeString.Set( aAttachmentInfo.MimeType() ); + if ( contentTypeString.Length() == 0 ) + { + // take type from mime headers + TInt cotentLength = aMimeHeaders.ContentType().Length(); + TInt subCotentLength = aMimeHeaders.ContentSubType().Length(); + tempContentType = HBufC8::NewL( cotentLength + subCotentLength + 1 ); + CleanupStack::PushL( tempContentType ); + tempContentType->Des().Copy( aMimeHeaders.ContentType() ); + if ( ( aMimeHeaders.ContentType().Find( KMmsSlash8 ) != ( cotentLength - 1 ) ) && + ( aMimeHeaders.ContentSubType().Find( KMmsSlash8 ) != 0 ) && ( subCotentLength != 0 ) ) + { + tempContentType->Des().Append( KMmsSlash8 ); + } + tempContentType->Des().Append( aMimeHeaders.ContentSubType() ); + aAttachmentInfo.SetMimeTypeL( tempContentType->Des() ); + contentTypeString.Set( aAttachmentInfo.MimeType() ); + } + + //map the content type string to content type + TInt8 i; + for ( i = 0; i < KNumberContentTypes && contentType < 0; i++ ) + { + if ( contentTypeString.CompareF( TPtrC8( KContentTypeTable[i] ) ) == 0 ) + { + contentType = i; + } + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- content type:%d" ), contentType ); +#endif /* _NO_MMSS_LOGGING_ */ + //check if this content type need to be encoded using korean specific + if( IsConversionSupportedContentType( contentType ) ) + { + /* Do the attachment data conversion from "src type" to "target type" for the given "attached file". + * Target type encodng MIB enum is obtained from cenrep. + */ + retVal = ProcessAndConvertAttachmentDataL( aMimeHeaders.MimeCharset(), + iTargetEncodingType, + aAttachmentInfo); + if( retVal ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- conv success: Tgt Enc: %d"), iTargetEncodingType ); +#endif /* _NO_MMSS_LOGGING_ */ + //set new charset type in headers and update attachment info + aMimeHeaders.SetMimeCharset( iTargetEncodingType ); + aMimeHeaders.StoreL(aAttachmentInfo); // save the new charset to attachment as well + /* mimetype might get reset to original mimeheader content type. + * ensure the full content type(if created from mime content and subcontent) is set to attachment + */ + if( tempContentType != NULL ) + { + aAttachmentInfo.SetMimeTypeL( tempContentType->Des() ); + CleanupStack::PopAndDestroy( tempContentType ); + } + } + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::CheckAndUpdateAttachmentL- End , retVal: %d"), retVal ); +#endif /* _NO_MMSS_LOGGING_ */ + return retVal; + } + +// --------------------------------------------------------- +// CMmsEncode::ProcessAndConvertAttachmentDataL +// converts of attachment data from source to target encoding type. +// |src charset buffer| --->converted to ---> |unicode buffer| ---> converted to ---> |target charset| +// Returns false if data is already int target format, or plugins are missing, or file operation issues. +// --------------------------------------------------------- +TBool CMmsEncode::ProcessAndConvertAttachmentDataL( TUint aSrcCharSetMIBEnum, + TUint aTargetCharSetMIBEnum, + CMsvAttachment& aAttachmentInfo) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- start ") ); +#endif /* _NO_MMSS_LOGGING_ */ + if( aSrcCharSetMIBEnum == aTargetCharSetMIBEnum ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- src and dest are same ") ); +#endif /* _NO_MMSS_LOGGING_ */ + //if source and target charset MIB enums are same. + return EFalse; // no conversion + } + + //get source and target charset mapping here. if any error, return + TUint srcCharSetId = iTextUtils->MibIdToCharconvIdL( aSrcCharSetMIBEnum ); + TUint targetCharSetId = iTextUtils->MibIdToCharconvIdL( aTargetCharSetMIBEnum ); + if( srcCharSetId == targetCharSetId || KDefaultCharConvCharset == targetCharSetId) + { + /* If target charset plugin is missing, then default is returned. + Do not encode to any target encoding type, send data as is . + */ +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- missing plugin: Tgt Charset %x"), targetCharSetId ); +#endif /* _NO_MMSS_LOGGING_ */ + return EFalse; + } + + RFile attachFile; + TInt error; + + const TDesC& fileName = aAttachmentInfo.FilePath(); + error = attachFile.Open(iFs, fileName, EFileWrite); + if(error != KErrNone) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file: %s, open error: %d"), fileName, error ); +#endif /* _NO_MMSS_LOGGING_ */ + attachFile.Close(); + return EFalse; + } + + TInt maxLength; + error = attachFile.Size(maxLength); + if( error != KErrNone || maxLength == 0 ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file size: %d, error: %d"), maxLength, error ); +#endif /* _NO_MMSS_LOGGING_ */ + attachFile.Close(); + return EFalse; + } + + //read the original file data into srcBuffer + TInt cleanupCount = 0; + HBufC8* srcBuffer = HBufC8::NewLC( maxLength ); + cleanupCount++; + + TPtr8 srcPtr = srcBuffer->Des(); + attachFile.Read( srcPtr, maxLength ); + + //intermediate buffer in the form of unicode. + HBufC16* unicodeBuffer(NULL); + + //Convert, scrBuffer to unicode format if not already in unicode format + if(srcCharSetId != 0) + { + //Convert from respective foreign charset to unicode buffer. + //returns buf16 pointer descriptor + TRAPD(err, unicodeBuffer = iTextUtils->ConvertToUnicodeL(srcPtr, srcCharSetId) ); + if( err != KErrNone ) + { +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: ConvertToUnicodeL error: %d"), err ); +#endif /* _NO_MMSS_LOGGING_ */ + delete unicodeBuffer; + attachFile.Close(); + CleanupStack::PopAndDestroy( cleanupCount, srcBuffer ); // srcBuffer + return EFalse; + } + CleanupStack::PushL( unicodeBuffer ); + cleanupCount++; + } + else + { + TInt unicodeStdHeaderWordLE = 0xFEFF; + TInt unicodeStdHeaderWordBE = 0xFFFE; + //data is already in unicode format. need to extract the data to 16-bit buffer + unicodeBuffer = HBufC16::NewLC( maxLength/2 );//maxlength value is in terms of bytes + cleanupCount++; + + TPtr16 ptr = unicodeBuffer->Des(); + iTextUtils->ConvertPtrToDesC16(srcPtr, ptr); + /* In case if attachment is UTF-16 format, it will have the UTF-16 representation word(2 butes) + * at the start. Find and delete it before passing the data for conversion + */ + if( ptr[0] == unicodeStdHeaderWordLE || ptr[0] == unicodeStdHeaderWordBE ) + { + ptr.Delete(0, 1); + } + } + + //Now convert unicode buffer to respective target charset type. + if( targetCharSetId != 0 ) + { + // reset this file and write new encoded data to it directly + error = attachFile.SetSize(0); +#ifndef _NO_MMSS_LOGGING_ + if( error != KErrNone ) + { + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentData:: file size: %d, error: %d"), maxLength, error ); + //attachFile.Close(); + //return EFalse; + } +#endif /* _NO_MMSS_LOGGING_ */ + + /* convert to target charset id. + * Ideally, this should not leave with plugin-NOT-found, since it it already verified + */ + iTextUtils->ConvertToFileL(*unicodeBuffer, attachFile, targetCharSetId ); + } + else + { + TPtr16 ptr = unicodeBuffer->Des(); + //Reset/erase old file content. + TInt err = attachFile.SetSize(0); + + //write new encoded data to file using write stream + RFileWriteStream writer( attachFile ); + writer.PushL(); + writer.WriteL( ptr ); + //writer.CommitL(); + writer.Pop(); + writer.Close(); + } + //close file + attachFile.Close(); + CleanupStack::PopAndDestroy( cleanupCount, srcBuffer ); //unicodeBuffer, srcBuffer +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::ProcessAndConvertAttachmentDataL- End, retVal: TRUE") ); +#endif /* _NO_MMSS_LOGGING_ */ + return ETrue; + } + + +// --------------------------------------------------------- +// CMmsEncode::IsConversionSupportedContentType +// checks if input content type is supported for target encoding +// --------------------------------------------------------- +TBool CMmsEncode::IsConversionSupportedContentType( TInt aContentType ) + { + /* Currently only "text/plain" content types are supported for korean encoding. + * Add to the switch case, if any new content type can be supported as well. + * + * IMPORTANT: + * Ensure the values added map to corresponding content type entry in the table KContentTypeTable[], + * defined in ../inc/mmscodec.h + */ +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::IsConversionSupportedContentType- start: %d "), aContentType ); +#endif /* _NO_MMSS_LOGGING_ */ + TBool retVal = EFalse; + switch( aContentType ) + { + case 0x03 : // "text/plain" + { + retVal = ETrue; + break; + } + default: + //do nothing + break;// to avoid compile errors/warning + } +#ifndef _NO_MMSS_LOGGING_ + TMmsLogger::Log( _L("CMmsEncode::IsConversionSupportedContentType- end ") ); +#endif /* _NO_MMSS_LOGGING_ */ + return retVal; + } + +// ================= OTHER EXPORTED FUNCTIONS ============== + +// End of File