--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/mmscodec/src/mmsencode.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -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 <e32std.h>
+#include <apparc.h>
+#include <s32mem.h>
+#include <msventry.h>
+#include <utf.h>
+#include <e32math.h>
+#include <msvids.h>
+#include <escapeutils.h>
+#include <badesca.h>
+#include <mmsvattachmentmanager.h>
+#include <mmsvattachmentmanagersync.h>
+#include <cmsvmimeheaders.h>
+#include <imcvcodc.h>
+
+#include <msgtextutils.h>
+#include <msvstore.h>
+#include <charconv.h>
+
+#include <centralrepository.h> // link against centralrepository.lib
+#include "MmsEnginePrivateCRKeys.h"
+
+#include "mmsheaders.h"
+#include "mmsconst.h"
+#include "mmscodec.h"
+#include "mmsencode.h"
+#include "mmsservercommon.h"
+#include "mmssession.h"
+#include "mmsgenutils.h"
+#include "mmserrors.h"
+#include "mmsentrywrapper.h"
+#include "mmsmmboxmessageheaders.h"
+#include "mmsmmboxviewheaders.h"
+#include "mmssendingchain.h"
+
+const TInt KMmsExtra = 1024; // extra memory left for others
+const TInt KMmsShortIntegerLimit127 = 127; // limit for short integer length
+const TInt KMmsOneByteLimit = 0x100;
+const TInt KMmsTwoByteLimit = 0x10000;
+const TInt KMmsThreeByteLimit = 0x1000000;
+const TInt KMms2 = 2;
+const TInt KMms3 = 3;
+const TInt KMms4 = 4;
+const TInt KMms5 = 5;
+const TInt KMms7 = 7;
+const TInt KMms8 = 8;
+const TInt KMms24 = 24; // shift of three half bytes
+const TInt KMms30 = 30; // upper limit for short length encoding
+const TUint8 KMms0x80 = 0x80; // 128
+const TUint8 KMms0x7F = 0x7F; // 127
+const TUint8 KMms0xFF = 0xFF;
+const TInt KMmsMaxCidLength = 18;
+// max filename length according to MMS conformance specs
+const TInt KMmsMaxFileNameLength = 40;
+// if 40 bytes are encoded in base 64, the result is 56 bytes
+// (two bytes of padding are needed to make the number divisible by 3)
+const TInt KMaxEncodingLength = 56;
+// Maximum length of output buffer needed for encoding the filename
+// Max 56 bytes for actual encoding and 12 bytes for character set (utf8)
+// and encoding scheme indicator 68 for final result, but we allocate
+// the maximum Mime header length to be sure everything fits.
+const TInt KMaxNameBufferLength = 75;
+const TInt KMmsEncodingExtraLength = 12;
+const TInt KMmsPreambleLength = 10;
+_LIT8( KMmsQuotedPreamble, "=?utf-8?Q?" );
+_LIT8( KMmsBase64Preamble, "=?utf-8?B?" );
+_LIT8( KMmsEncodingTrailer, "?=" );
+const TInt KMmsIntUnderscore = 0x5F; // underscore
+
+
+enum TMmsMachineStates
+ {
+ EMmsIdle,
+ EMmsEncodingHeaders,
+ EMmsEncodingAttachments,
+ EMmsFinished
+ };
+
+// These are the stages for chunked encoding.
+// The chunked encoding is synchronous, so the stages are different
+// from the machine states.
+enum TMmsChunkedEncodingStages
+ {
+ EMmsHeaders,
+ EMmsAttachmentHeaders,
+ EMmsAttachmentData
+ };
+
+// ==================== LOCAL FUNCTIONS ====================
+
+// ================= MEMBER FUNCTIONS =======================
+
+// ---------------------------------------------------------------------------
+// C++ default constructor can NOT contain any code, that
+// might leave.
+//
+// All member variables are automatically zeroed in the constructor
+// of a class derived from CBase.
+// Priority is set slightly above standard (1 instead of 0) to make sure
+// that message is sent as soon as possible (user input has priority 10).
+// Variables related to chunked encoding are initialized to indicate
+// one complete chunk only.
+// ---------------------------------------------------------------------------
+//
+CMmsEncode::CMmsEncode()
+ :CMsgActive ( KMmsActiveObjectPriority ),
+ iState ( EMmsIdle ),
+ iOverallDataSize ( -1 ),
+ iLastChunk ( ETrue ),
+ iOnlyOneChunk( ETrue )
+ {
+ }
+
+// ---------------------------------------------------------------------------
+// Symbian OS default constructor can leave.
+// ---------------------------------------------------------------------------
+//
+void CMmsEncode::ConstructL( RFs& aFs )
+ {
+ iFs = aFs;
+ CActiveScheduler::Add( this );
+ }
+
+// ---------------------------------------------------------------------------
+// Two-phased constructor.
+// ---------------------------------------------------------------------------
+//
+EXPORT_C CMmsEncode* CMmsEncode::NewL( RFs& aFs )
+ {
+ CMmsEncode* self = new ( ELeave ) CMmsEncode;
+
+ CleanupStack::PushL( self );
+ self->ConstructL( aFs );
+ CleanupStack::Pop( self );
+
+ return self;
+ }
+
+
+// ---------------------------------------------------------------------------
+// Destructor
+// ---------------------------------------------------------------------------
+//
+CMmsEncode::~CMmsEncode()
+ {
+
+ Cancel();
+
+ if ( iFileOpen )
+ {
+ iAttachFile.Close();
+ iFileOpen = EFalse;
+ }
+
+ // Do not delete pointers that were presents from the caller.
+ // The caller still owns that data
+ delete iMimeHeaders; // this is ours too.
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CMmsEncode::StartL(
+ MMmsEntryWrapper& aEntryWrapper,
+ CMmsHeaders& aMmsHeaders,
+ CBufFlat& aEncodeBuffer,
+ TRequestStatus& aStatus )
+ {
+
+ // This is the entry point for encoding all PDUs that contain both headers
+ // and data.
+ // For normal use the only PDUs in this category would be M-send.req and
+ // M-MBox-Upload.req, but for testing purposes the following PDUs may
+ // also be handled using this entry point:
+ // M-retrieve.conf (for testing purposes)
+ // M-Mbox-View.conf (for testing purposes)
+ // M-Mbox-Descr (for testing purposes)
+
+ // Old asynchronous encoding is retained.
+ // Reset sets all chunk-related variables to indicate one chunk which is the last one
+ Reset();
+
+ iEntryWrapper = &aEntryWrapper;
+ iMmsHeaders = &aMmsHeaders;
+ iEncodeBuffer = &aEncodeBuffer;
+
+ if ( iMimeHeaders )
+ {
+ iMimeHeaders->Reset();
+ }
+ else
+ {
+ iMimeHeaders = CMsvMimeHeaders::NewL();
+ }
+
+ // We need to know the message entry ID
+ // The wrapper originally points to the message entry
+
+ TMsvEntry indexEntry;
+ iError = iEntryWrapper->GetIndexEntry( indexEntry );
+ iCurrentMessageId = indexEntry.Id();
+
+ CMsvStore* store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+
+ // Only new attachment structure is supported
+ MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+ iNumberOfAttachments = attachMan.AttachmentCount();
+
+ CleanupStack::PopAndDestroy( store );
+
+ Queue( aStatus );
+
+ iStatus = KRequestPending;
+ SetActive();
+ // Pretend that we called an asynchronous service
+ // in order to get into the state machine loop
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, iError );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CMmsEncode::EncodeHeadersL(
+ CMmsHeaders& aMmsHeaders,
+ CBufFlat& aEncodeBuffer )
+ {
+ // This function is used to encode all PDUs that contain only headers,
+ // not data. Some are responses sent to MMSC, some are requests that
+ // contain headers only.
+ // Nothing is read from storage, everything to be encoded is contained
+ // in the headers.
+ // No active object is invoked, this is a synchronous function
+
+ // Old one-chunk encoding is retained for encoding headers only.
+ // Reset sets all chunk-related variables to indicate one chunk which is the last one
+ Reset(); // also sets iError to KErrNone
+
+ iMmsHeaders = &aMmsHeaders;
+ iEncodeBuffer = &aEncodeBuffer;
+
+ // start from the beginning
+ iPosition = 0;
+ // we discard old contents of the buffer in case we must expand it
+ iEncodeBuffer->Reset();
+
+ // We are a bit greedy here so that we don't need
+ // to change the code if there is a minor change in the specs.
+ iEncodeBuffer->ResizeL( iMmsHeaders->Size() + KMMSBufferExtra );
+
+ // encode message depending on type
+ // these are all types that contain headers only, never data
+ switch ( iMmsHeaders->MessageType() )
+ {
+ case KMmsMessageTypeMNotifyRespInd:
+ EncodeNotifyResponse();
+ break;
+ case KMmsMessageTypeAcknowledgeInd:
+ EncodeAcknowledgeIndication();
+ break;
+ case KMmsMessageTypeMNotificationInd:
+ EncodeMmsNotificationL();
+ break;
+ case KMmsMessageTypeForwardReq:
+ EncodeForwardRequestL();
+ break;
+ case KMmsMessageTypeReadRecInd:
+ EncodeReadReplyL();
+ break;
+ case KMmsMessageTypeMboxStoreReq:
+ EncodeMMBoxStoreRequestL();
+ break;
+ case KMmsMessageTypeMboxViewReq:
+ // This may contain keywords. Reserve some extra space
+ EncodeMMBoxViewRequestL();
+ break;
+ case KMmsMessageTypeMBoxDeleteReq:
+ case KMmsMessageTypeDeleteReq:
+ EncodeDeleteRequestL();
+ break;
+ case KMmsMessageTypeCancelConf:
+ EncodeCancelResponse();
+ break;
+ case KMmsMessageTypeReadOrigInd:
+ // This is for testing purposes.
+ // identical to ReadRecInd except for PDU type
+ // This would be the PDU sent to originator by MMSC
+ EncodeReadReplyL();
+ break;
+ case KMmsMessageTypeMSendConf:
+ // for testing purposes
+ EncodeSendConfirmationL();
+ break;
+ case KMmsMessageTypeDeliveryInd:
+ // for testing purposes
+ EncodeDeliveryReportL();
+ break;
+ case KMmsMessageTypeForwardConf:
+ // for testing purposes
+ EncodeForwardConfirmationL();
+ break;
+ case KMmsMessageTypeMboxStoreConf:
+ // for testing purposes
+ EncodeMMBoxStoreConfirmationL();
+ break;
+ case KMmsMessageTypeMBoxUploadConf:
+ // for testing purposes
+ EncodeMMBoxUploadConfirmationL();
+ break;
+ case KMmsMessageTypeMBoxDeleteConf:
+ case KMmsMessageTypeDeleteConf:
+ // for testing purposes
+ EncodeDeleteConfirmationL();
+ break;
+ case KMmsMessageTypeCancelReq:
+ // for testing purposes
+ EncodeCancelRequest();
+ break;
+#ifdef __WINS__
+ case KMmsMessageTypeMSendReq:
+ EncodeSendRequestHeadersL();
+ break;
+ case KMmsMessageTypeMBoxUploadReq:
+ // This type has attachments and headers
+ // This function encodes headers only
+ EncodeMMBoxUploadRequestL();
+ break;
+ case KMmsMessageTypeMRetrieveConf:
+ // for test purposes
+ EncodeRetrieveConfirmationL();
+ break;
+ case KMmsMessageTypeMboxViewConf:
+ // for test purposes
+ EncodeMMBoxViewConfirmationL();
+ break;
+ case KMmsMessageTypeMBoxDescr:
+ // for test purposes
+ EncodeMMBoxDescriptionL();
+ break;
+#endif
+ default:
+ // Illegal message type.
+ iEncodeBuffer->Reset();
+ iPosition = 0;
+ break;
+ }
+
+ // Remove slack to keep garbage out from the end of the file
+ iEncodeBuffer->ResizeL( iPosition );
+ // Dump the buffer contents into file if requested
+ Dump();
+
+ iOverallDataSize = iEncodeBuffer->Size();
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+EXPORT_C void CMmsEncode::StartChunkedL(
+ MMmsEntryWrapper& aEntryWrapper,
+ CMmsHeaders& aMmsHeaders,
+ CBufFlat& aEncodeBuffer,
+ TRequestStatus& aStatus )
+ {
+
+ // This is the entry point for chunked encoding all PDUs that contain both headers
+ // and data.
+
+ // Reset sets all chunk-related variables to indicate one chunk which is the last one
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("CMmsEncode: Start chunked") );
+#endif
+ Reset(); // also sets iError to KErrNone
+ iDataSupplierStage = EMmsHeaders;
+
+ iEntryWrapper = &aEntryWrapper;
+ iMmsHeaders = &aMmsHeaders;
+ iEncodeBuffer = &aEncodeBuffer;
+
+ if ( iMimeHeaders )
+ {
+ iMimeHeaders->Reset();
+ }
+ else
+ {
+ iMimeHeaders = CMsvMimeHeaders::NewL();
+ }
+
+ // We need to know the message entry ID
+ // The wrapper originally points to the message entry
+
+ TMsvEntry indexEntry;
+ iError = iEntryWrapper->GetIndexEntry( indexEntry );
+ iCurrentMessageId = indexEntry.Id();
+
+ // Encode headers to the buffer
+ // The rest will follow when next data chunk is requested.
+ EncodeHeadersChunkedL();
+
+ aStatus=KRequestPending;
+ TRequestStatus* status = &aStatus;
+ User::RequestComplete( status, iError );
+ }
+
+
+// ---------------------------------------------------------
+// From class MMmsCodecDataSupplier
+//
+// ---------------------------------------------------------
+//
+TInt CMmsEncode::GetNextDataPart( TPtrC8& aDataPart, TBool& aLastDataChunk )
+ {
+ TInt error = KErrNone;
+ if ( !iEncodeBuffer || iEncodeBuffer->Size() == 0 )
+ {
+ // called out of context or no message data available
+ error = KErrNotFound;
+ }
+ else
+ {
+ aDataPart.Set( iEncodeBuffer->BackPtr( iPosition ) );
+ }
+ aLastDataChunk = iLastChunk;
+
+ return error;
+ }
+
+// ---------------------------------------------------------
+// From class MMmsCodecDataSupplier
+//
+// ---------------------------------------------------------
+//
+TInt CMmsEncode::ReleaseData()
+ {
+ if ( iOnlyOneChunk )
+ {
+ // if we have only one chunk we always point to the beginning
+ // the buffer gets cleared when all data has been handled
+ return KErrNone;
+ }
+
+ iPosition = 0;
+
+ // encode next data part into the buffer
+
+ if ( iDataSupplierStage == EMmsAttachmentHeaders && iNumberOfAttachments == 0 )
+ {
+ iLastChunk = ETrue;
+ iDataSupplierStage = EMmsHeaders;
+ }
+ else if ( iDataSupplierStage == EMmsAttachmentHeaders )
+ {
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("- data supplier stage: Attachment headers") );
+#endif
+ iError = iEntryWrapper->SetCurrentEntry( iCurrentMessageId );
+ if ( iError != KErrNone )
+ {
+ return iError;
+ }
+
+ iCurrentFileSize = 0;
+ CMsvStore* store = NULL;
+ TRAP( iError,
+ {
+ store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+ MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+ iCurrentFileSize = EncodeHeadersAndGetFileL( attachMan );
+ CleanupStack::PopAndDestroy( store );
+ });
+ if ( iError != KErrNone )
+ {
+ return iError;
+ }
+
+ // Now we have the attachment headers in our buffer.
+ // If there is room, put data there, too
+
+ if ( iBufferSize - iPosition >= iCurrentFileSize )
+ {
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("- enough room left in buffer for attachment data") );
+#endif
+ EncodeAttachmentData( iAttachFile, iCurrentFileSize );
+ iPosition += iCurrentFileSize;
+ iAttachFile.Close();
+ iFileOpen = EFalse;
+ iCurrentAttachment++;
+ if ( iCurrentAttachment >= iNumberOfAttachments )
+ {
+ iLastChunk = ETrue;
+ iDataSupplierStage = EMmsHeaders;
+ }
+ else
+ {
+ // one attachment finished - move to next one
+ iDataSupplierStage = EMmsAttachmentHeaders;
+ }
+ }
+ else
+ {
+ iDataSupplierStage = EMmsAttachmentData;
+ }
+ }
+ else if ( iDataSupplierStage == EMmsAttachmentData )
+ {
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("- data supplier stage: Attachment data") );
+#endif
+ // Add as much attachment data as possible.
+ // Attachment file is now open, check position
+ TInt filePosition = 0;
+ iError = iAttachFile.Seek( ESeekCurrent, filePosition );
+ if ( iError != KErrNone )
+ {
+ return iError;
+ }
+ TInt dataLeft = iCurrentFileSize - filePosition;
+ TInt roomLeft = iBufferSize - iPosition;
+ if ( roomLeft >= dataLeft )
+ {
+ EncodeAttachmentData( iAttachFile, dataLeft );
+ iPosition += dataLeft;
+ iAttachFile.Close();
+ iFileOpen = EFalse;
+ iCurrentAttachment++;
+ if ( iCurrentAttachment >= iNumberOfAttachments )
+ {
+ iLastChunk = ETrue;
+ iDataSupplierStage = EMmsHeaders;
+ }
+ else
+ {
+ // one attachment finished - move to next one
+ iDataSupplierStage = EMmsAttachmentHeaders;
+ }
+ }
+ else
+ {
+ // we have more data than fits into buffer
+ EncodeAttachmentData( iAttachFile, roomLeft );
+ iPosition += roomLeft;
+ // We don't close the file because we still have data.
+ // We stay in EMmsAttachmentHeaders state
+ }
+ }
+ else
+ {
+ // do nothing, keep LINT happy
+ }
+#ifndef _NO_MMSS_LOGGING_
+ if ( iDataSupplierStage == EMmsHeaders )
+ {
+ TMmsLogger::Log( _L("- last chunk released") );
+ TMmsLogger::Log( _L("- data supplier stage: MMS headers") );
+ }
+#endif
+ // If we have reached the end and have been asked to release data
+ // we have nothing left to do.
+
+ // dump what we got into file - if needed
+ if ( iPosition > 0 )
+ {
+ DumpAppend();
+ }
+ return iError;
+ }
+
+// ---------------------------------------------------------
+// From class MMmsCodecDataSupplier
+//
+// ---------------------------------------------------------
+//
+TInt CMmsEncode::OverallDataSize()
+ {
+ // If we have encoded all data, iOverallDataSize contains the
+ // actual amount of data.
+ // If we are doing chunked encoding, it contains -1.
+ return iOverallDataSize;
+ }
+
+
+// ---------------------------------------------------------
+// From class MMmsCodecDataSupplier
+//
+// ---------------------------------------------------------
+//
+TInt CMmsEncode::ResetSupplier()
+ {
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("CMmsEncode: ResetSupplier") );
+#endif
+ if ( iOnlyOneChunk )
+ {
+ // if we have only one chunk we always point to the beginning
+ // the buffer gets cleared when all data has been handled
+ return KErrNone;
+ }
+
+ // start data from the beginning
+ iError = KErrNone;
+ iDataSupplierStage = EMmsHeaders;
+ TRAP( iError, EncodeHeadersChunkedL() );
+
+ return iError;
+ }
+
+// ---------------------------------------------------------
+// From class CMsgActive
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::DoRunL()
+ {
+
+ // This routine takes the state machine through the states
+ // until an error is encountered or the cycle completes.
+
+ if ( iError != KErrNone )
+ {
+ // We encountered an error, and cannot continue
+ // For the time being we just give up without trying
+ // to do any decent cleanup.
+ iStatus = iError;
+ // If we return from DoRunL without becoming active again,
+ // RunL completes.
+ return;
+ }
+
+ if ( iState != EMmsFinished )
+ {
+ SelectNextState();
+ // If appropriate, ChangeStateL makes us active again
+ ChangeStateL();
+ }
+ else
+ {
+ // We are done, we must become idle again
+ FinishL();
+ iStatus = iError;
+ // If we return from DoRunL without becoming active again,
+ // RunL completes.
+ }
+ // As we are using standard Mentact RunL, we must leave
+ // if we have encountered an error, and do not want to continue
+
+ if ( iError != KErrNone && !IsActive() )
+ {
+ iPosition = 0;
+ FinishL();
+ User::Leave( iError );
+ }
+ }
+
+// ---------------------------------------------------------
+// From class CMsgActive
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::DoComplete( TInt& /* aStatus */ )
+ {
+ // We are exiting the loop - we say we are idle now
+ iState = EMmsIdle;
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::SelectNextState()
+ {
+
+ // If appropriate, the functions called within the switch statement
+ // will make us active again. If all is done, the asynchronous request
+ // will complete
+
+ switch ( iState )
+ {
+ case EMmsIdle:
+ // start the loop
+ iState = EMmsEncodingHeaders;
+ break;
+ case EMmsEncodingHeaders:
+ if ( iNumberOfAttachments > 0 )
+ {
+ iState = EMmsEncodingAttachments;
+ }
+ else
+ {
+ iState = EMmsFinished;
+ }
+ break;
+ case EMmsEncodingAttachments:
+ // if there are more attachments, don't change state
+ if ( iCurrentAttachment >= iNumberOfAttachments )
+ {
+ iState = EMmsFinished;
+ }
+ break;
+ case EMmsFinished:
+ // No more states
+ iState = EMmsIdle;
+ break;
+ default:
+ // Illegal state
+ break;
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::ChangeStateL()
+ {
+ switch ( iState )
+ {
+ case EMmsEncodingHeaders:
+ EncodeHeadersL();
+ break;
+ case EMmsEncodingAttachments:
+ EncodeAttachmentL();
+ iCurrentAttachment++;
+ break;
+ case EMmsFinished:
+ FinishL();
+ break;
+ default:
+ // Should not be here anymore
+ break;
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::Reset()
+ {
+ // close open file in case operation has ended with error
+ // and the attachment file is still open
+ if ( iFileOpen )
+ {
+ iAttachFile.Close();
+ iFileOpen = EFalse;
+ }
+
+ iNumberOfAttachments = 0;
+ iCurrentAttachment = 0;
+ iError = KErrNone;
+ iCurrentMessageId = KMsvNullIndexEntryId;
+ iOverallDataSize = -1;
+ iLastChunk = ETrue;
+ iOnlyOneChunk = ETrue;
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeHeadersL()
+ {
+
+ // start from the beginning
+ iPosition = 0;
+ // we discard old contents of the buffer in case we must expand it
+ iEncodeBuffer->Reset();
+
+ // calculate space needed and resize buffer
+ TInt size = 0;
+ size += iMmsHeaders->Size();
+
+ User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
+ CMsvStore* store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+ // AttachmentsSize function asks actual size from files
+ // in case the info in CMsvAttachment is not up to date.
+ // This function leaves if the attachment file is not found.
+ // A message cannot be sent if it refers to attachment files that no
+ // longer exist.
+ size += iEntryWrapper->AttachmentsSizeL( *store );
+ CleanupStack::PopAndDestroy( store );
+ store = NULL;
+
+ size += KMMSBufferExtra; // this is just an estimate of header size...
+ // extra needed if lots of attachments.
+ size += KMMSAttachmentExtra * iNumberOfAttachments;
+ TMemoryInfoV1Buf memory;
+ UserHal::MemoryInfo( memory );
+ TInt available = memory().iFreeRamInBytes;
+ // check that buffer fits, leave a little memory for others too.
+ if ( size > ( available - KMmsExtra ) )
+ {
+ // message is too big - don't even try
+ iError = KMmsErrorMessageTooBig;
+ }
+
+ if ( iError == KErrNone )
+ {
+ iEncodeBuffer->ResizeL( size );
+ EncodeRequestHeadersL();
+ }
+
+ iStatus = KRequestPending;
+ SetActive();
+
+ // We complete ourselves.
+ TRequestStatus* status = &iStatus;
+ User::RequestComplete( status, iError );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeHeadersChunkedL()
+ {
+ // The first step of chunked encoding
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("CMmsEncode::EncodeHeadersChunkedL:: start") );
+#endif /* _NO_MMSS_LOGGING_ */
+ User::LeaveIfError( iEntryWrapper->SetCurrentEntry( iCurrentMessageId ) );
+
+ /* Korean req: 415-5434
+ * Get the target encoding MIB enum from cenrep and encode MMS text objects using coresponding 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 paramters, 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 paramter 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 paramter 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 paramters, 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 paramter 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 paramter has no value.
+ // Add the "no value" token
+ iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
+ iPosition++;
+ }
+
+ // End of MIME headers
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeAttachmentData( RFile& aAttachFile, TInt aSize )
+ {
+ // read the attachment file
+ // set up a pointer to point to iEncodeBuffer at iPosition
+ TPtr8 pos = iEncodeBuffer->Ptr( iPosition );
+ // Attachemnt may be a file attacment or a linked file.
+ // The file pointer will point to unread part if file is read in chunks
+ iError = aAttachFile.Read( pos, aSize );
+ }
+
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::FinishL()
+ {
+
+ // Remove slack to keep garbage out from the end of the file
+ iEncodeBuffer->ResizeL( iPosition );
+ iOverallDataSize = iEncodeBuffer->Size();
+ iState = EMmsIdle;
+
+ // Dump the buffer contents into file if requested
+ Dump();
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeRequestHeadersL()
+ {
+ // encode message depending on type
+ switch ( iMmsHeaders->MessageType() )
+ {
+ // KMmsMessageTypeMNotifyRespInd and KMmsMessageTypeAcknowledgeInd and all other PDUs
+ // that contain only headers and no data should be handled by
+ // using function
+ // EncodeHeadersL(CMmsHeaders& aMmsHeaders,CBufFlat& aEncodeBuffer)
+ case KMmsMessageTypeMSendReq:
+ EncodeSendRequestHeadersL();
+ break;
+ case KMmsMessageTypeMBoxUploadReq:
+ // This type has attachments and headers
+ EncodeMMBoxUploadRequestL();
+ break;
+ case KMmsMessageTypeMRetrieveConf:
+ // for test purposes
+ EncodeRetrieveConfirmationL();
+ break;
+ case KMmsMessageTypeMboxViewConf:
+ // for test purposes
+ EncodeMMBoxViewConfirmationL();
+ break;
+ case KMmsMessageTypeMBoxDescr:
+ // for test purposes
+ EncodeMMBoxDescriptionL();
+ break;
+ default:
+ iError = KMmsErrorUnknownMessageType;
+ break;
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeSendRequestHeadersL()
+ {
+
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMSendReq,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ // If we have set the sender, encode it
+ // If not, we ask MMSC to add the sender
+ EncodeSenderL( iMmsHeaders->Sender() );
+
+ // All recipient lists. If no recipients in some of the lists,
+ // nothing is encoded, no need to check separately
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+ EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
+ EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );
+
+ // Subject
+ EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );
+
+ // Message class
+ EncodeOptionalByte( KMmsAssignedMessageClass,
+ iMmsHeaders->MessageClass() );
+
+ // Expiration time
+ EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
+ iMmsHeaders->ExpiryInterval(),
+ iMmsHeaders->ExpiryDate() );
+
+ // Delivery time
+ EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
+ iMmsHeaders->DeliveryTimeInterval(),
+ iMmsHeaders->DeliveryDate() );
+
+ // Priority
+ EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
+
+ // Sender visibility
+ EncodeOptionalByte( KMmsAssignedSenderVisibility,
+ iMmsHeaders->SenderVisibility() );
+
+ // Delivery report
+ EncodeOptionalByte( KMmsAssignedDeliveryReport,
+ iMmsHeaders->DeliveryReport() );
+
+ // Read reply
+ EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );
+
+ // MMS encapsulation 1.1 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
+ {
+ // Do we want to specify reply charging
+ if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly )
+ {
+ // Reply charging value
+ EncodeOptionalByte( KMmsAssignedReplyCharging,
+ iMmsHeaders->ReplyCharging() );
+ // Reply charging deadline
+ EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
+ iMmsHeaders->ReplyChargingInterval(),
+ iMmsHeaders->ReplyChargingDate() );
+ // Reply charging size
+ EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
+ }
+ // Reply charging ID
+ EncodeOptionalString( KMmsAssignedReplyChargingID,
+ iMmsHeaders->ReplyChargingId() );
+ }
+
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
+ {
+ // MMBox related headers - optional
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-Store
+ EncodeOptionalByte( KMmsAssignedMmsStore,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() );
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+ }
+ }
+
+ // Add encapsulation version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
+ EncodeApplicationHeadersL();
+ // X-Mms-Content-Class
+ EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
+ // X-Mms-DRM-Content
+ EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
+ // X-Mms-Adaptation-Allowed
+ EncodeOptionalByte( KMmsAssignedAdaptationAllowed, iMmsHeaders->AdaptationAllowed() );
+ }
+
+ // Get Root TMsvId from headers if set
+ TMsvAttachmentId rootId = iMmsHeaders->MessageRoot();
+
+ TInt error = KErrNone;
+ // If we are handling headers only, we may not have iEntryWrapper
+ if ( iEntryWrapper )
+ {
+ CMsvStore* store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+ MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+ TRAP( error,
+ {
+ CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
+ (TMsvAttachmentId) rootId );
+ delete attachmentInfo; // we just wanted to check that it was found
+ }
+ );
+ CleanupStack::PopAndDestroy( store );
+ }
+
+ // If we have a valid root part, we send the message as multipart/related.
+ // Otherwise we send it as multipart mixed.
+ // We don't try to guess what the root is supposed to be, if it
+ // is not specified or does not point to an existing attachment.
+
+ if ( error == KErrNone && rootId != 0 && iEntryWrapper )
+ {
+ EncodeMultipartRelatedHeaderL( rootId );
+ }
+ else
+ {
+ EncodeMultipartMixedHeaderL();
+ }
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeNotifyResponse()
+ {
+
+ // version may be current version or 1.0
+ // Only byte is used. iMmsHeaders controls the value.
+
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMNotifyRespInd, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // insert message status
+ EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() );
+
+ // insert report allowed if present
+ EncodeOptionalByte( KMmsAssignedReportAllowed,
+ iMmsHeaders->ReportAllowed() );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeAcknowledgeIndication()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeAcknowledgeInd,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ // insert report allowed if present
+ EncodeOptionalByte( KMmsAssignedReportAllowed,
+ iMmsHeaders->ReportAllowed() );
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMmsNotificationL()
+ {
+
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMNotificationInd,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ // for notification sender is optional - added only if it is known
+ if ( iMmsHeaders->Sender().Length() > 0 )
+ {
+ EncodeSenderL( iMmsHeaders->Sender() );
+ }
+
+ EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );
+
+ EncodeOptionalByte( KMmsAssignedDeliveryReport,
+ iMmsHeaders->DeliveryReport() );
+
+ EncodeOptionalByte( KMmsAssignedMessageClass,
+ iMmsHeaders->MessageClass() );
+
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 );
+ iPosition++;
+
+ TInt64 size = iMmsHeaders->MessageSize();
+ EncodeLongInteger( size );
+
+ EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
+ iMmsHeaders->ExpiryInterval(),
+ iMmsHeaders->ExpiryDate() );
+
+ // version 1.1 fields - optional
+ // reply-charging
+ // reply-charging deadline
+ // reply-charging size
+ // reply-charging ID
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
+ {
+ // Do we want to specify
+ if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
+ {
+ // Reply charging value
+ EncodeOptionalByte( KMmsAssignedReplyCharging,
+ iMmsHeaders->ReplyCharging() );
+ // Reply charging deadline
+ EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
+ iMmsHeaders->ReplyChargingInterval(),
+ iMmsHeaders->ReplyChargingDate() );
+ // Reply charging size
+ EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
+ }
+ // if this is a reply, we may specify the ID
+ // Reply charging ID
+ EncodeOptionalString( KMmsAssignedReplyChargingID,
+ iMmsHeaders->ReplyChargingId() );
+ }
+
+ // version 1.2 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
+ {
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ EncodeOptionalByte( KMmsAssignedStored,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() );
+ }
+
+ // Distribution indicator
+ EncodeOptionalByte( KMmsAssignedDistributionIndicator,
+ iMmsHeaders->DistributionIndicator() );
+ }
+
+ // Element descriptor
+ // This is somewhat a mystery - not implemented
+
+ // version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
+ EncodeApplicationHeadersL();
+ // X-Mms-Recommended-Retrieval-Mode
+ EncodeOptionalByte( KMmsAssignedRecommendedRetrievalMode,
+ iMmsHeaders->RecommendedRetrievalMode() );
+ // X-Mms-Recommended-Retrieval-Mode-Text
+ EncodeOptionalStringL( KMmsAssignedRecommendedRetrievalModeText,
+ iMmsHeaders->RecommendedRetrievalModeText() );
+ // X-Mms-Content-Class
+ EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
+ // X-Mms-DRM-Content
+ EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
+ // X-Mms-Replace-ID
+ EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() );
+ }
+
+ // We keep this as the last one, though it does not really matter
+ // Mandatory content-location
+ // this must be USASCII or URL encoded
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeSendConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMSendConf,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ EncodeMandatoryByte( KMmsAssignedResponseStatus,
+ iMmsHeaders->ResponseStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedResponseText,
+ iMmsHeaders->ResponseText() );
+
+ // if send confirmation returns error, it might not contain message id
+ EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );
+
+ // MMBox related headers - optional
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ EncodeOptionalByte( KMmsAssignedStoreStatus,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
+ EncodeOptionalStringL( KMmsAssignedStoreStatusText,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
+ if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ==
+ KMmsStatusOk )
+ {
+ // this must be USASCII or URL encoded
+ // If message was stored to MMBox, this is mandatory
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+ }
+ }
+
+#endif
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeRetrieveConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMRetrieveConf,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ // message id
+ EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ // If we have set the sender, encode it
+ if ( iMmsHeaders->Sender().Length() > 0 )
+ {
+ EncodeSenderL( iMmsHeaders->Sender() );
+ }
+
+ // previously sent by array
+ TInt i;
+ TUint oldPosition;
+ TUint length;
+ for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); i++ )
+ {
+ oldPosition = iPosition;
+ length = 0;
+ // Encode once just to find out filed length
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ // now we have added length, do the actual encoding
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
+ oldPosition = iPosition;
+ length = 0;
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
+ }
+
+ // All recipient lists. If no recipients in some of the lists,
+ // nothing is encoded, no need to check separately
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+ EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
+ // no Bcc in retrieve confirmation
+
+ // Subject
+ EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );
+
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+
+ // Message class
+ EncodeOptionalByte( KMmsAssignedMessageClass,
+ iMmsHeaders->MessageClass() );
+
+ // Priority
+ EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
+
+ // Delivery report
+ EncodeOptionalByte( KMmsAssignedDeliveryReport,
+ iMmsHeaders->DeliveryReport() );
+
+ // Read reply
+ EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );
+
+ // MMS encapsulation 1.1 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion1 )
+ {
+ // Do we want to specify reply charging
+ if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
+ {
+ // Reply charging value
+ EncodeOptionalByte( KMmsAssignedReplyCharging,
+ iMmsHeaders->ReplyCharging() );
+ // Reply charging deadline
+ EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
+ iMmsHeaders->ReplyChargingInterval(),
+ iMmsHeaders->ReplyChargingDate() );
+ // Reply charging size
+ EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
+ }
+ // Reply charging ID
+ EncodeOptionalString( KMmsAssignedReplyChargingID,
+ iMmsHeaders->ReplyChargingId() );
+ // status of the operation
+ EncodeOptionalByte( KMmsAssignedRetrieveStatus,
+ iMmsHeaders->ResponseStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedRetrieveText,
+ iMmsHeaders->ResponseText() );
+ }
+
+ // version 1.2 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
+ {
+ // Distribution indicator
+ EncodeOptionalByte( KMmsAssignedDistributionIndicator,
+ iMmsHeaders->DistributionIndicator() );
+ }
+
+ // version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
+ EncodeApplicationHeadersL();
+ // X-Mms-Content-Class
+ EncodeOptionalByte( KMmsAssignedContentClass, iMmsHeaders->ContentClass() );
+ // X-Mms-DRM-Content
+ EncodeOptionalByte( KMmsAssignedDrmContent, iMmsHeaders->DrmContent() );
+ // X-Mms-Replace-ID
+ EncodeOptionalString( KMmsAssignedReplaceId, iMmsHeaders->ReplaceCancelId() );
+ }
+
+ // Start pointer
+ TUint ii = 0;
+ // Get Root TMsvId from headers if set
+ TMsvId rootId = iMmsHeaders->MessageRoot();
+
+ TInt error = KErrNone;
+ if ( iEntryWrapper )
+ {
+ CMsvStore* store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+ MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+ TRAP( error,
+ {
+ CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
+ (TMsvAttachmentId) rootId );
+ delete attachmentInfo; // we just wanted to check that it was found
+ }
+ );
+ CleanupStack::PopAndDestroy( store );
+ }
+
+ // If we have a valid root part, we send the message as multipart/related.
+ // Otherwise we send it as multipart mixed.
+ // We don't try to guess what the root is supposed to be, if it
+ // is not specified or does not point to an existing attachment.
+
+ if ( error == KErrNone && rootId != 0 && iEntryWrapper )
+ {
+ EncodeMultipartRelatedHeaderL( rootId );
+ }
+ else
+ {
+ EncodeMultipartMixedHeaderL();
+ }
+
+
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeDeliveryReportL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeDeliveryInd, TPtrC8(),
+ iMmsHeaders->MmsVersion() );
+
+ EncodeHeaderAndTextString( KMmsAssignedMessageId,
+ iMmsHeaders->MessageId() );
+
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ // insert message status
+ EncodeMandatoryByte( KMmsAssignedStatus, iMmsHeaders->Status() );
+
+ // version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // X-Mms-Status-Text
+ // Status text is stored in the same place as response text and retrieve
+ // text as only one of them can appear in any PDU
+ EncodeOptionalStringL( KMmsAssignedStatusText,
+ iMmsHeaders->ResponseText() );
+ // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
+ EncodeApplicationHeadersL();
+ }
+
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeForwardRequestL()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeForwardReq,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ // If we have set the sender, encode it
+ // If not, we ask MMSC to add the sender
+ EncodeSenderL( iMmsHeaders->Sender() );
+
+ // All recipient lists. If no recipients in some of the lists,
+ // nothing is encoded, no need to check separately
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+ EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
+ EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );
+
+ // Expiration time
+ EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
+ iMmsHeaders->ExpiryInterval(),
+ iMmsHeaders->ExpiryDate() );
+
+ // Delivery time
+ EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
+ iMmsHeaders->DeliveryTimeInterval(),
+ iMmsHeaders->DeliveryDate() );
+
+ // Delivery report allowed
+ EncodeOptionalByte( KMmsAssignedReportAllowed,
+ iMmsHeaders->ReportAllowed() );
+
+ // Delivery report
+ EncodeOptionalByte( KMmsAssignedDeliveryReport,
+ iMmsHeaders->DeliveryReport() );
+
+ // Read reply
+ EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );
+
+ // version 1.2 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion11 )
+ {
+ // MMBox related headers - optional
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-Store
+ EncodeOptionalByte( KMmsAssignedMmsStore,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() );
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStore() == KMmsYes ||
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsStored() == KMmsYes )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+ }
+ }
+
+ // version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // Do we want to specify reply charging
+ if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly )
+ {
+ // Reply charging value
+ EncodeOptionalByte( KMmsAssignedReplyCharging,
+ iMmsHeaders->ReplyCharging() );
+ // Reply charging deadline
+ EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
+ iMmsHeaders->ReplyChargingInterval(),
+ iMmsHeaders->ReplyChargingDate() );
+ // Reply charging size
+ EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
+ }
+ }
+
+ // Mandatory content-location
+ // this must be USASCII or URL encoded
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeForwardConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeForwardConf,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ EncodeMandatoryByte( KMmsAssignedResponseStatus,
+ iMmsHeaders->ResponseStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedResponseText,
+ iMmsHeaders->ResponseText() );
+
+ EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );
+
+ // MMBox related headers - optional
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ EncodeOptionalByte( KMmsAssignedStoreStatus,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
+ EncodeOptionalStringL( KMmsAssignedStoreStatusText,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
+ if ( iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() ==
+ KMmsStatusOk )
+ {
+ // this must be USASCII or URL encoded
+ // If message was stored to MMBox, this is mandatory
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+ }
+ }
+
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeReadReplyL()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( iMmsHeaders->MessageType(), TPtrC8(), iMmsHeaders->MmsVersion() );
+
+ // message id
+ EncodeHeaderAndTextString( KMmsAssignedMessageId,
+ iMmsHeaders->MessageId() );
+
+ // originator of the original message
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+
+ // If we have set the sender, encode it
+ // If not, we ask MMSC to add the sender
+ EncodeSenderL( iMmsHeaders->Sender() );
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ EncodeMandatoryByte( KMmsAssignedReadStatus, iMmsHeaders->ReadStatus() );
+
+ // version 1.3 headers - optional
+ if ( iMmsHeaders->MmsVersion() > KMmsVersion12 )
+ {
+ // X-Mms-Applic-ID, X-Mms-Reply-Applic-ID and X-Mms-Aux-Applic-Info
+ EncodeApplicationHeadersL();
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxStoreRequestL()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMboxStoreReq, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // this must be USASCII or URL encoded
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxStoreConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMboxStoreConf, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // this must be USASCII or URL encoded
+ EncodeOptionalString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ EncodeMandatoryByte( KMmsAssignedStoreStatus,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedStoreStatusText,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
+
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxViewRequestL()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMboxViewReq, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // Insert content location header. There may be more than one
+ EncodeContentLocationArray();
+
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-MMs-MM-Flags. The token should always be "filter"
+ EncodeKeywordArrayL();
+ }
+
+ if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() )
+ {
+ CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL();
+
+ // states used as filter
+ // X-Mms-MM-State may appear multiple times in view request
+ EncodeMMBoxStates( viewHeaders.MMStateArray() );
+
+ // start
+ EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() );
+ // limit
+ if ( viewHeaders.MmsLimit() != KMaxTUint32 )
+ {
+ // 0 means no message information, absence means all remaining
+ // messages.
+ // If header has value KMaxTUint32, it means that info about all
+ // remaining messages is wanted.
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MmsLimit() );
+ }
+
+ EncodeAttributes( viewHeaders.AttributeArray() );
+ // request totals
+ EncodeOptionalByte( KMmsAssignedTotals, viewHeaders.MmsTotals() );
+ // request quotas
+ EncodeOptionalByte( KMmsAssignedQuotas, viewHeaders.MmsQuotas() );
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxViewConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMboxViewConf,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ EncodeMandatoryByte( KMmsAssignedResponseStatus,
+ iMmsHeaders->ResponseStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedResponseText,
+ iMmsHeaders->ResponseText() );
+
+ EncodeContentLocationArray();
+
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-MMs-MM-Flags. The token should always be "filter"
+ EncodeKeywordArrayL();
+ }
+
+ if ( iMmsHeaders->ReadOnlyMMBoxViewHeaders() )
+ {
+ CMmsMMBoxViewHeaders& viewHeaders = iMmsHeaders->MMBoxViewHeadersL();
+
+ // states used as filter
+ // X-Mms-MM-State may appear multiple times in view request
+ EncodeMMBoxStates( viewHeaders.MMStateArray() );
+
+ // start
+ EncodeOptionalInteger( KMmsAssignedStart, viewHeaders.MmsStart() );
+ // limit
+ if ( viewHeaders.MmsLimit() != KMaxTUint32 )
+ {
+ // 0 means no message information, absence means all remaining
+ // messages.
+ // If header has value KMaxTUint32, it means that info about
+ // all remaining messages is wanted.
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedLimit, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MmsLimit() );
+ }
+ EncodeAttributes( viewHeaders.AttributeArray() );
+
+ // totals
+ TUint oldPosition = iPosition;
+ TUint length = 0;
+
+ if ( viewHeaders.MMBoxTotalNumber() != KMaxTUint32 )
+ {
+ oldPosition = iPosition;
+ EncodeInteger( viewHeaders.MMBoxTotalNumber() );
+ length = iPosition - oldPosition;
+ length += 1; // quota token
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MMBoxTotalNumber() );
+ }
+
+ if ( viewHeaders.MMBoxTotalSize() != KMaxTUint32 )
+ {
+ oldPosition = iPosition;
+ EncodeInteger( viewHeaders.MMBoxTotalSize() );
+ length = iPosition - oldPosition;
+ length += 1; // quota token
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxTotals, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MMBoxTotalSize() );
+ }
+
+ // quotas
+ if ( viewHeaders.MMBoxQuotaNumber() != KMaxTUint32 )
+ {
+ oldPosition = iPosition;
+ EncodeInteger( viewHeaders.MMBoxQuotaNumber() );
+ length = iPosition - oldPosition;
+ length += 1; // quota token
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ iEncodeBuffer->Write( iPosition, &KMmsMessageCountToken, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MMBoxQuotaNumber() );
+ }
+
+ if ( viewHeaders.MMBoxQuotaSize() != KMaxTUint32 )
+ {
+ oldPosition = iPosition;
+ EncodeInteger( viewHeaders.MMBoxQuotaSize() );
+ length = iPosition - oldPosition;
+ length += 1; // quota token
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMboxQuotas, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ iEncodeBuffer->Write( iPosition, &KMmsMessageSizeToken, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MMBoxQuotaSize() );
+ }
+
+ // Message count
+ // This should in principle come from the viewHeaders.MmsMessageCount()
+ // But in this context the number of messages will be the number of
+ // attachments available in this entry
+
+ // However, we will use the viewHeaders.MmsMessageCount() to generate
+ // the header so that we can test that the decoding part can handle
+ // possible conflict between message number and the number of parts
+ // in the multipart structure. This header is optional. KMaxTUint32
+ // will mean unspecified.
+
+ if ( viewHeaders.MmsMessageCount() != KMaxTUint32 )
+ {
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageCount, 1 );
+ iPosition++;
+ EncodeInteger( viewHeaders.MmsMessageCount() );
+ }
+ }
+
+ if ( iNumberOfAttachments == 0 )
+ {
+ EncodeMandatoryByte( KMmsAssignedContentType, KMmsAssignedAny );
+ }
+ else
+ {
+ EncodeMultipartMixedHeaderL();
+ }
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxUploadRequestL()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMBoxUploadReq, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+
+ // Get Root TMsvId from headers if set
+ TMsvAttachmentId rootId = iMmsHeaders->MessageRoot();
+
+ // Find the matching id from attachment list
+
+ TInt error = KErrNone;
+ if ( iEntryWrapper )
+ {
+ CMsvStore* store = iEntryWrapper->ReadStoreL();
+ CleanupStack::PushL( store );
+ // Only new attachment structure is supported
+ MMsvAttachmentManager& attachMan = store->AttachmentManagerL();
+ TRAP( error,
+ {
+ CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(
+ (TMsvAttachmentId) rootId );
+ delete attachmentInfo; // we just wanted to check that it was found
+ }
+ );
+ CleanupStack::PopAndDestroy( store );
+ }
+
+ // If we have a valid root part, we send the message as multipart/related.
+ // Otherwise we send it as multipart mixed.
+ // We don't try to guess what the root is supposed to be, if it
+ // is not specified or does not point to an existing attachment.
+
+ // actually the content should be an MMS PDU as in M-MBox-Descr PDU
+ // It is unclear if if should be wrapped in a multipart structure
+ // (containing only one header)
+ // or if the content type should be application/vnd.wap.mms-message
+ if ( error == KErrNone && rootId != 0 && iEntryWrapper )
+ {
+ EncodeMultipartRelatedHeaderL( rootId );
+ }
+ else
+ {
+ EncodeMultipartMixedHeaderL();
+ }
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxUploadConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeMBoxUploadConf, iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // this must be USASCII or URL encoded
+ EncodeOptionalString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ EncodeMandatoryByte( KMmsAssignedStoreStatus,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatus() );
+
+ EncodeOptionalStringL( KMmsAssignedStoreStatusText,
+ iMmsHeaders->MMBoxMessageHeadersL().MmsStoreStatusText() );
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeDeleteRequestL()
+ {
+ // Insert message type, TID and version number
+
+ EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // Insert content location header. There may be more than one
+ EncodeContentLocationArray();
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeDeleteConfirmationL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( iMmsHeaders->MessageType(), iMmsHeaders->Tid(),
+ iMmsHeaders->MmsVersion() );
+
+ // content location, response status and response text headers have
+ // different format from other PDUs
+
+ const RPointerArray<CMmsDeleteResultArray>& resultArray =
+ iMmsHeaders->DeleteResultArray();
+
+ TInt i;
+ TInt length = 0;
+ TInt oldPosition = 0;
+ TUint oldIndex = KMaxTUint32;
+
+ for ( i = 0; i < resultArray.Count(); i++ )
+ {
+ TUint index = resultArray[i]->Order();
+ if ( index != oldIndex )
+ {
+ // We add status and status text only once, there may be more
+ // than one content location with the same status
+ oldIndex = index;
+ if ( resultArray[i]->ResponseStatus() != 0 )
+ {
+ oldPosition = iPosition;
+ length = 0;
+ // Encode once just to find out field length
+ EncodeInteger( index );
+ length += iPosition - oldPosition;
+ length ++; // response status is only one byte
+ iPosition = oldPosition;
+ // now we have calculated length, do the actual encoding
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseStatus, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ EncodeInteger( index );
+ TUint8 value = (TUint8) resultArray[i]->ResponseStatus();
+ iEncodeBuffer->Write( iPosition, &value, 1 );
+ iPosition++;
+ }
+ if ( resultArray[i]->ResponseText().Length() > 0 )
+ {
+ oldPosition = iPosition;
+ length = 0;
+ // Encode once just to find out field length
+ EncodeInteger( index );
+ EncodeTextStringL( resultArray[i]->ResponseText() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ // now we have calculated length, do the actual encoding
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedResponseText, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ EncodeInteger( index );
+ EncodeTextStringL( resultArray[i]->ResponseText() );
+ }
+ }
+ if ( resultArray[i]->ContentLocation().Length() > 0 )
+ {
+ oldPosition = iPosition;
+ length = 0;
+ // Encode once just to find out field length
+ EncodeInteger( index );
+ EncodeTextString( resultArray[i]->ContentLocation() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ // now we have calculated length, do the actual encoding
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedContentLocation, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ EncodeInteger( index );
+ EncodeTextString( resultArray[i]->ContentLocation() );
+ }
+ }
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxDescriptionL()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ EncodeMandatoryByte( KMmsAssignedMessageType, KMmsMessageTypeMBoxDescr );
+
+ // this must be USASCII or URL encoded
+ EncodeOptionalString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+
+ EncodeOptionalString( KMmsAssignedMessageId, iMmsHeaders->MessageId() );
+
+ // MMBox headers
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ // X-Mms-MM-State
+ EncodeOptionalByte( KMmsAssignedMMState,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->MmsMMState() );
+ // X-MMs-MM-Flags
+ // The keyword token should always be "add" or "remove"
+ EncodeKeywordArrayL();
+ }
+
+ // if we have set a date, insert it
+ EncodeDate( iMmsHeaders->Date() );
+
+ // sender is optional
+ if ( iMmsHeaders->Sender().Length() > 0 )
+ {
+ EncodeSenderL( iMmsHeaders->Sender() );
+ }
+
+ // All recipient lists. If no recipients in some of the lists,
+ // nothing is encoded, no need to check separately
+ EncodeRecipientL( iMmsHeaders->ToRecipients(), EMmsTo );
+ EncodeRecipientL( iMmsHeaders->CcRecipients(), EMmsCc );
+ EncodeRecipientL( iMmsHeaders->BccRecipients(), EMmsBcc );
+
+ // Message class
+ EncodeOptionalByte( KMmsAssignedMessageClass,
+ iMmsHeaders->MessageClass() );
+
+ // Subject
+ EncodeOptionalStringL( KMmsAssignedSubject, iMmsHeaders->Subject() );
+
+ // Priority
+ EncodeOptionalByte( KMmsAssignedPriority, iMmsHeaders->MessagePriority() );
+
+ // Delivery time
+ EncodeOptionalIntervalOrDate( KMmsAssignedDeliveryTime,
+ iMmsHeaders->DeliveryTimeInterval(),
+ iMmsHeaders->DeliveryDate() );
+
+ // Expiration time
+ EncodeOptionalIntervalOrDate( KMmsAssignedExpiry,
+ iMmsHeaders->ExpiryInterval(),
+ iMmsHeaders->ExpiryDate() );
+
+ // Delivery report
+ EncodeOptionalByte( KMmsAssignedDeliveryReport,
+ iMmsHeaders->DeliveryReport() );
+
+ // Read reply
+ EncodeOptionalByte( KMmsAssignedReadReply, iMmsHeaders->ReadReply() );
+
+ if ( iMmsHeaders->MessageSize() > 0 )
+ {
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMessageSize, 1 );
+ iPosition++;
+ TInt64 size = iMmsHeaders->MessageSize();
+ EncodeLongInteger( size );
+ }
+
+ // MMBox description appears from 1.2 onwards - no use to check if we are
+ // 1.1 or later.
+ // Do we want to specify reply charging
+ if ( iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequested ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingRequestedTextOnly ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingAccepted ||
+ iMmsHeaders->ReplyCharging() == KMmsReplyChargingAcceptedTextOnly )
+ {
+ // Reply charging value
+ EncodeOptionalByte( KMmsAssignedReplyCharging,
+ iMmsHeaders->ReplyCharging() );
+ // Reply charging deadline
+ EncodeOptionalIntervalOrDate( KMmsAssignedReplyChargingDeadline,
+ iMmsHeaders->ReplyChargingInterval(),
+ iMmsHeaders->ReplyChargingDate() );
+ // Reply charging size
+ EncodeReplyChargingSize( iMmsHeaders->ReplyChargingSize() );
+ }
+ // Reply charging ID
+ EncodeOptionalString( KMmsAssignedReplyChargingID,
+ iMmsHeaders->ReplyChargingId() );
+
+ TInt i;
+ TUint oldPosition;
+ TUint length;
+ for ( i = 0; i < iMmsHeaders->PreviouslySentList().Count(); i++ )
+ {
+ oldPosition = iPosition;
+ length = 0;
+ // Encode once just to find out field length
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentBy, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ // now we have added length, do the actual encoding
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeAddressL( iMmsHeaders->PreviouslySentList()[i]->Sender() );
+ oldPosition = iPosition;
+ length = 0;
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
+ length += iPosition - oldPosition;
+ iPosition = oldPosition;
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedPreviouslySentDate, 1 );
+ iPosition++;
+ EncodeValueLength( length );
+ EncodeInteger( iMmsHeaders->PreviouslySentList()[i]->Order() );
+ EncodeLongInteger( iMmsHeaders->PreviouslySentList()[i]->Date() );
+ }
+
+ if ( iNumberOfAttachments > 0 )
+ {
+ EncodeMultipartMixedHeaderL();
+ }
+
+#endif
+ }
+
+// ---------------------------------------------------------
+// 8-bit version (no conversions)
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeTextString( const TDesC8& aString )
+ {
+ // Only Content-Id needs a quoted string, for example
+ // "<agjrfnjr>
+ // content-id encoding uses a separate function that adds the quote.
+ // EncodeQuotedTextString
+
+ // Check if we need a Quote (This does not mean the quoted string.)
+ TInt length = aString.Length();
+ if ( length > 0 && aString[0] >= KMms0x80 )
+ {
+ iEncodeBuffer->Write( iPosition, &KMmsQuote, 1 );
+ iPosition++;
+ }
+
+ iEncodeBuffer->Write( iPosition, aString, length );
+ iPosition += length;
+
+ iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
+ iPosition++;
+
+ }
+
+// ---------------------------------------------------------
+// 8-bit version (no conversions)
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeQuotedTextString( const TDesC8& aString )
+ {
+ // Before this function is called, the caller must check
+ // that the length of the string is not 0. Otherwise this
+ // makes no sense. You cannot quote a zero-length string
+
+ // We need quoted string for content-id.
+ // So far for nothing else.
+ // (for the filename in MIME headers, too, just in case)
+
+ // We add one quote to the beginning, nothing to the end.
+ // As we start with a quote, we don't need to check for the special
+ // Quote that is needed if a string starts with a character above 128
+ TInt length = aString.Length();
+ iEncodeBuffer->Write( iPosition, &KMmsStringQuote, 1 );
+ iPosition++;
+
+ iEncodeBuffer->Write( iPosition, aString, length );
+ iPosition += length;
+
+ iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
+ iPosition++;
+
+ }
+
+// ---------------------------------------------------------
+// unicode version
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeTextStringL( const TDesC& aString )
+ {
+
+ // What about a RFC2068 quoted strings -
+ // see explanation in EncodeTextString( const TDesC8& aString ) function
+
+ TInt i = 0;
+ TInt length = aString.Length();
+
+ TBool safe = IsStringSafe( aString );
+
+ // If the string can be encoded as ASCII, go ahead
+ if ( safe )
+ {
+ TUint8 character;
+ // No need to check if we need a quote - if we are safe, we have
+ // no characters >= 128.
+
+ for ( i = 0; i < aString.Length(); i++ )
+ {
+ character = TUint8( aString[i] & KMms0xFF );
+ iEncodeBuffer->Write( iPosition, &character, 1 );
+ iPosition++;
+ }
+ iEncodeBuffer->Write( iPosition, &KMmsNull, 1 );
+ iPosition++;
+ }
+ else
+ {
+ // if our length is 0, we are safe, no need to check the
+ // length here anymore
+ // we must convert to utf-8
+
+ //one ucs-2 character should never produce more than 4 bytes when converted to utf-8
+ 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<KMmsMaxCidLength> target;
+
+ // If cid has not been defined, we must generate one
+ if ( cid.Length() == 0 )
+ {
+ TTime now;
+ now.UniversalTime();
+ TInt random;
+ TInt64 seed = now.Int64();
+ random = Math::Rand( seed );
+ // type conversions irrelevant - just creating a magic number for content id
+ target.Des().Num( random );
+ target.Des().Insert(0, KMmsLeftAngle );
+ target.Des().Append( KMmsRightAngle );
+ cid.Set( target.Des() );
+ // exclude the angle brackets
+ iMimeHeaders->SetContentIdL( cid.Mid( 1, cid.Length() - KMms2 ) );
+ // save the cid to be present when we create the attachment headers
+ // If this does not succeed, the message will have no root.
+ MMsvAttachmentManagerSync& attachManSync =
+ store->AttachmentManagerExtensionsL();
+ iMimeHeaders->StoreL( *attachmentInfo );
+ attachManSync.ModifyAttachmentInfoL( attachmentInfo );
+ // attachment manager now own attachemnt info
+ CleanupStack::Pop( attachmentInfo ); cleanupCount--;
+ store->CommitL();
+ attachmentInfo = attachMan.GetAttachmentInfoL( (TMsvAttachmentId) aRootId );
+ CleanupStack::PushL( attachmentInfo ); cleanupCount++;
+ }
+
+ // add start header length &
+ // assigned number for start parameter, and terminating zero for cid
+ headerLength += cid.Length() + KMms2;
+ if ( cid.Find( KMmsLeftAngle ) != 0 )
+ {
+ headerLength += KMms2; // we must add angle bracket
+ }
+
+ // Then the length of the content-type header...
+ TPtrC8 contentTypeString = iMimeHeaders->ContentType();
+
+ HBufC8* tempContentType = HBufC8::NewL( iMimeHeaders->ContentType().Length() +
+ iMimeHeaders->ContentSubType().Length() + 1 );
+ CleanupStack::PushL( tempContentType );
+ tempContentType->Des().Copy( iMimeHeaders->ContentType() );
+ if ( ( iMimeHeaders->ContentType().Find( KMmsSlash8 ) !=
+ ( iMimeHeaders->ContentType().Length() - 1 ) ) &&
+ ( iMimeHeaders->ContentSubType().Find( KMmsSlash8 ) != 0 ) &&
+ ( iMimeHeaders->ContentSubType().Length() != 0 ) )
+ {
+ tempContentType->Des().Append( KMmsSlash8 );
+ }
+ tempContentType->Des().Append( iMimeHeaders->ContentSubType() );
+ attachmentInfo->SetMimeTypeL( tempContentType->Des() );
+ CleanupStack::PopAndDestroy( tempContentType );
+ contentTypeString.Set( attachmentInfo->MimeType() );
+
+ if ( contentTypeString.Length() == 0 )
+ {
+ // We need a content type...
+ // If we don't know, we say "Any"
+ contentTypeString.Set( KMmsAny );
+ }
+ // Check if we have well-known media.
+ TInt8 rootContentType = -1;
+
+ TInt8 i = 0;
+ for ( i = 0; i < KNumberContentTypes && rootContentType < 0; i++ )
+ {
+ if ( contentTypeString.CompareF( TPtrC8( KContentTypeTable[i] ) ) == 0 )
+ {
+ rootContentType = i;
+ }
+ }
+
+ // start parameter assignment
+ headerLength += 1;
+ if ( rootContentType != -1 )
+ {
+ // well known content type
+ headerLength += 1;
+ }
+ else
+ {
+ // string + terminating zero
+ headerLength += contentTypeString.Length() + 1;
+ }
+
+ TPtrC8 appIdPtr;
+ appIdPtr.Set( KMmsJavaApplicationId );
+ // Java application id parameters added to content-type
+ if ( iMmsHeaders->ApplicId().Length() > 0 )
+ {
+ headerLength += appIdPtr.Length() + 1 ;
+ headerLength += iMmsHeaders->ApplicId().Length() + 1;
+ }
+ appIdPtr.Set( KMmsJavaReplyApplicationId );
+ if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ headerLength += appIdPtr.Length() + 1 ;
+ headerLength += iMmsHeaders->ReplyApplicId().Length() + 1;
+ }
+
+ // write general encoding length
+ EncodeValueLength( headerLength );
+
+ // Then the Media Type with parameters
+
+ // We are multipart/related, which is a well-known media
+ // encode as short integer
+ TUint8 byte = KMmsAssignedApplicationVndWapMultipartRelated | KMms0x80;
+ iEncodeBuffer->Write( iPosition, &byte, 1 );
+ iPosition++;
+
+ // Then the start parameter and cid text string
+ byte = KWspStart | KMms0x80;
+
+ if ( cid.Length() > 0 )
+ {
+ HBufC8* tempContentId = HBufC8::NewL( iMimeHeaders->ContentId().Length() + KMms2 );
+ CleanupStack::PushL( tempContentId );
+ if ( cid.Find( KMmsLeftAngle ) != 0 )
+ {
+ tempContentId->Des().Copy( KMmsLeftAngle );
+ tempContentId->Des().Append( cid );
+ tempContentId->Des().Append( KMmsRightAngle );
+ }
+ else
+ {
+ tempContentId->Des().Copy( cid );
+ }
+ EncodeHeaderAndTextString( byte, tempContentId->Des() );
+ CleanupStack::PopAndDestroy( tempContentId );
+ }
+
+
+ // next the content type of the root part
+ byte = KWspRelatedType | KMms0x80;
+ // and the actual type either as a well-known media type
+ // or a text string.
+
+ if ( rootContentType != -1 )
+ {
+ // Well known content type.
+ // EncodeMandatoryByte will set the high bit
+ EncodeMandatoryByte( byte, rootContentType );
+ }
+ else
+ {
+ // string + terminating zero
+ EncodeHeaderAndTextString( byte, contentTypeString );
+ }
+
+ if ( iMmsHeaders->ApplicId().Length() > 0 ||
+ iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ EncodeApplicationIdParametersL();
+ }
+
+ // encode number of parts
+ EncodeUintvar( iNumberOfAttachments );
+
+ // get rid of stuff we put on stack
+ CleanupStack::PopAndDestroy( cleanupCount );
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMultipartMixedHeaderL()
+ {
+
+ // If Java has added application id or reply-to application id,
+ // even multipart/mxed type needs parameters.
+
+ TUint headerLength = 0; // assume no parameters
+
+ if ( iMmsHeaders->ApplicId().Length() > 0 ||
+ iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ headerLength = 1; // one byte for well known media
+ TPtrC8 appIdPtr;
+ appIdPtr.Set( KMmsJavaApplicationId );
+ if ( iMmsHeaders->ApplicId().Length() > 0 )
+ {
+ headerLength += appIdPtr.Length() + 1 ;
+ headerLength += iMmsHeaders->ApplicId().Length() + 1;
+ }
+ appIdPtr.Set( KMmsJavaReplyApplicationId );
+ if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ headerLength += appIdPtr.Length() + 1 ;
+ headerLength += iMmsHeaders->ReplyApplicId().Length() + 1;
+ }
+ }
+
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedContentType, 1 );
+ iPosition++;
+
+ if ( headerLength > 0 )
+ {
+ // write general encoding length
+ EncodeValueLength( headerLength );
+ }
+
+ // encode as short integer
+ TUint8 contentType = KMmsAssignedApplicationVndWapMultipartMixed | KMms0x80;
+ iEncodeBuffer->Write( iPosition, &contentType, 1 );
+ iPosition++;
+
+ if ( iMmsHeaders->ApplicId().Length() > 0 ||
+ iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ EncodeApplicationIdParametersL();
+ }
+
+ // No more parameters for multipart/mixed, actual parts follow
+
+ // encode number of parts
+ EncodeUintvar( iNumberOfAttachments );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeKeywordArrayL()
+ {
+ TInt i = 0;
+ TInt length = 0;
+
+ // caller must check that iMmsHeaders->ReadOnlyMMBoxMessageHeaders() is not NULL
+ CMmsMMBoxMessageHeaders& temp = iMmsHeaders->MMBoxMessageHeadersL();
+ for ( i = 0; i < temp.KeywordArray().Count(); i++ )
+ {
+ length = temp.KeywordArray()[i]->Keyword().Length();
+ if ( length > 0 )
+ {
+ iEncodeBuffer->Write( iPosition, &KMmsAssignedMMFlags, 1 );
+ iPosition++;
+ // do some fake encoding to get the text length
+ TUint oldPosition = iPosition; // we will return here
+ EncodeTextStringL( temp.KeywordArray()[i]->Keyword() );
+ length = iPosition - oldPosition;
+ iPosition = oldPosition; // return to original place
+ length += 1; // token must come before string
+ EncodeValueLength( length );
+ TUint8 token = (TUint8) temp.KeywordArray()[i]->Token();
+ if ( iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewReq ||
+ iMmsHeaders->MessageType() == KMmsMessageTypeMboxViewConf ||
+ iMmsHeaders->MessageType() == KMmsMessageTypeMBoxDescr )
+ {
+ // the token must always be filter for MMbox PDUs
+ token = KMmsFilterToken;
+ }
+ iEncodeBuffer->Write( iPosition, &token, 1 );
+ iPosition++;
+ EncodeTextStringL( temp.KeywordArray()[i]->Keyword() );
+ }
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeOptionalInteger( TUint8 aHeader, TUint aValue )
+ {
+ if ( aValue == 0 )
+ {
+ return; // not set, nothing to encode, header is optional
+ }
+
+ iEncodeBuffer->Write( iPosition, &aHeader, 1 );
+ iPosition++;
+
+ EncodeInteger( aValue );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeAttributes( RArray<TUint>& aAttributeArray )
+ {
+ TInt i;
+
+ for ( i = 0; i < aAttributeArray.Count(); i++ )
+ {
+ EncodeMandatoryByte( KMmsAssignedAttributes, aAttributeArray[i] );
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeMMBoxStates( RArray<TInt>& aStateArray )
+ {
+ TInt i;
+
+ for ( i = 0; i < aStateArray.Count(); i++ )
+ {
+ EncodeMandatoryByte( KMmsAssignedMMState, aStateArray[i] );
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeContentLocationArray()
+ {
+ TBool mustCheck = EFalse;
+ if ( iMmsHeaders->ContentLocation().Length() > 0 )
+ {
+ mustCheck = ETrue;
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ContentLocation() );
+ }
+ // If there is content location array, encode it, too.
+ // A content-location should never occur in both places, but we still check
+
+ TInt i = 0;
+ if ( iMmsHeaders->ReadOnlyMMBoxMessageHeaders() )
+ {
+ for ( i = 0;
+ i < iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList().Count();
+ i++ )
+ {
+ if ( !( mustCheck && iMmsHeaders->ContentLocation().Compare(
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] ) == 0 ) )
+ {
+ // the content location in the list does not appear as
+ // separate header, we must add it
+ EncodeHeaderAndTextString( KMmsAssignedContentLocation,
+ iMmsHeaders->ReadOnlyMMBoxMessageHeaders()->ContentLocationList()[i] );
+ }
+ }
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeStartingHeaders( TInt aMessageType,
+ const TPtrC8& aTID, TInt aVersion )
+ {
+ // EncodeMandatoryByte will always set the high bit.
+
+ // Message type
+ EncodeMandatoryByte( KMmsAssignedMessageType, aMessageType );
+ // TID if present
+ EncodeOptionalString( KMmsAssignedTID, aTID );
+ // MMS encapsulation version
+ EncodeMandatoryByte( KMmsAssignedMmsVersion, aVersion );
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+void CMmsEncode::EncodeApplicationIdParametersL()
+ {
+ if ( iMmsHeaders->ApplicId().Length() > 0 )
+ {
+ EncodeTextString( KMmsJavaApplicationId );
+ EncodeTextStringL( iMmsHeaders->ApplicId() );
+ }
+ if ( iMmsHeaders->ReplyApplicId().Length() > 0 )
+ {
+ EncodeTextString( KMmsJavaReplyApplicationId );
+ EncodeTextStringL( iMmsHeaders->ReplyApplicId() );
+ }
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsEncode::Dump()
+ {
+ // no dump if not logging
+#ifndef _NO_MMSS_LOGGING_
+ TInt error = KErrNone;
+ // if no can do, sorry.
+ TRAP( error,
+ {
+ if ( iEntryWrapper )
+ {
+ if ( ( iEntryWrapper->GetDumpFlag() ) &&
+ iEncodeBuffer &&
+ iEncodeBuffer->Size() > 0 )
+ {
+ iFileName = KMmsDefaultLogDirectory;
+ TUint att;
+ if ( iFs.Att( iFileName, att ) == KErrNone )
+ {
+ _LIT( KRelated, "Sent.mms");
+ iParse.Set( iFileName, &KRelated, NULL );
+ iFileName = iParse.FullName();
+ User::LeaveIfError( CApaApplication::GenerateFileName(
+ iFs, iFileName ) );
+ RFile file;
+ User::LeaveIfError( file.Create( iFs, iFileName,
+ EFileWrite | EFileShareExclusive ) );
+ // for message id generation
+ iParse.Set( iFileName, NULL, NULL );
+
+ // the data is supposed to be in the encode buffer
+ TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition );
+ file.Write( ptr );
+ file.Flush();
+
+ // done - close files
+ file.Close();
+ }
+ }
+ }
+ }
+ );
+ if ( error != KErrNone )
+ {
+ TMmsLogger::Log( _L("Dump left with error %d"), error );
+ }
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsEncode::DumpAppend()
+ {
+
+ // no dump if not logging
+#ifndef _NO_MMSS_LOGGING_
+ TInt error = KErrNone;
+ // if no can do, sorry.
+ TRAP( error,
+ {
+ if ( iEntryWrapper )
+ {
+ if ( ( iEntryWrapper->GetDumpFlag() ) &&
+ iEncodeBuffer &&
+ iEncodeBuffer->Size() > 0 )
+ {
+ iFileName = KMmsDefaultLogDirectory;
+ TUint att;
+ if ( iFs.Att( iFileName, att ) == KErrNone )
+ {
+ RFile file;
+ iFileName = iParse.FullName();
+ User::LeaveIfError( file.Open( iFs, iFileName,
+ EFileWrite | EFileShareExclusive ) );
+ TInt position = 0; // seek to end
+ file.Seek( ESeekEnd, position );
+ // the data is supposed to be in the encode buffer
+ TPtr8 ptr = iEncodeBuffer->BackPtr( iPosition );
+ file.Write( ptr );
+ file.Flush();
+ // done - close files
+ file.Close();
+ }
+ }
+ }
+ }
+ );
+ if ( error != KErrNone )
+ {
+ TMmsLogger::Log( _L("DumpAppend left with error %d"), error );
+ }
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsEncode::EncodeApplicationHeadersL()
+ {
+// Application headers are only supported in WINS for testing purposes
+// Complete support for routing messages to arbitrary applications
+// requires support for selecting which applications are allowed
+// to send messages etc.
+#ifdef __WINS__
+ // The optional string functions check the length
+ // and return without doing anything if the length of the string is 0
+ if ( IsStringSafe( iMmsHeaders->ApplicId() ) )
+ {
+ // We only send this header if it is us-ascii only
+ // There is no encoding defined, so if this is converted
+ // from unicode to some other character set there is
+ // no guarantee that the recipient can decode it.
+ EncodeOptionalStringL( KMmsAssignedApplicId, iMmsHeaders->ApplicId() );
+ }
+
+ if ( IsStringSafe( iMmsHeaders->ReplyApplicId() ) )
+ {
+ EncodeOptionalStringL( KMmsAssignedReplyApplicId, iMmsHeaders->ReplyApplicId() );
+ }
+
+ EncodeOptionalString( KMmsAssignedAuxApplicInfo, iMmsHeaders->AuxApplicInfo() );
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsEncode::EncodeCancelRequest()
+ {
+// implemented for test purposes
+#ifdef __WINS__
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeCancelReq,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ EncodeOptionalString( KMmsAssignedCancelId, iMmsHeaders->ReplaceCancelId() );
+#endif
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsEncode::EncodeCancelResponse()
+ {
+ // Insert message type, TID and version number
+ EncodeStartingHeaders( KMmsMessageTypeCancelConf,
+ iMmsHeaders->Tid(), iMmsHeaders->MmsVersion() );
+
+ EncodeOptionalByte( KMmsAssignedCancelStatus, iMmsHeaders->CancelStatus() );
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+HBufC8* CMmsEncode::EncodeQuotedPrintableWordL( const TPtrC8& aSource )
+ {
+ // We have calculated that it fits
+
+ TInt bufferLength = KMaxNameBufferLength;
+
+ HBufC8* buffer = HBufC8::NewL( bufferLength );
+ CleanupStack::PushL( buffer );
+ TPtr8 ptr = buffer->Des();
+ buffer->Des().Copy( KMmsQuotedPreamble );
+ ptr.SetLength( bufferLength );
+ TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength );
+ encodeBuffer.SetLength( 0 ); // empty the buffer
+
+ TImCodecQP encoder;
+
+ // The function would return the number of characters written to the buffer.
+ // We don't do anything with the result, so we ignore the return value.
+ encoder.Encode( aSource, encodeBuffer );
+
+ ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength );
+
+ buffer->Des().Append( KMmsEncodingTrailer );
+ CleanupStack::Pop( buffer );
+ return buffer;
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+HBufC8* CMmsEncode::EncodeBase64WordL( const TPtrC8& aSource )
+ {
+ // ((length + 2)/3)*4 3 bytes alwaus produces 4 encoded bytes. Allow filler
+ TInt bufferLength = KMmsEncodingExtraLength + ( aSource.Length() + KMms2 ) / KMms3 * KMms4;
+
+ HBufC8* buffer = HBufC8::NewL( bufferLength );
+ CleanupStack::PushL( buffer );
+ TPtr8 ptr = buffer->Des();
+ buffer->Des().Copy( KMmsBase64Preamble );
+ ptr.SetLength( bufferLength );
+
+ TPtr8 encodeBuffer = ptr.MidTPtr( KMmsPreambleLength );
+ encodeBuffer.SetLength( 0 ); // empty the buffer
+
+ TImCodecB64 encoder;
+
+ // It is rather unclear what this function actually returns (no documentation found).
+ // Therefore we just ignore the result.
+ // Our buffer is long enough for the result to always fit.
+ encoder.Encode( aSource, encodeBuffer );
+
+ ptr.SetLength( encodeBuffer.Length() + KMmsPreambleLength );
+
+ buffer->Des().Append( KMmsEncodingTrailer );
+ CleanupStack::Pop( buffer );
+ return buffer;
+ }
+
+
+// ---------------------------------------------------------
+// CMmsEncode::PreProcessAttachmentDataL
+// Open the message store(Edit mode) and process attachments for further encoding in required encoding type.
+// Update the message store accordingly with the new encoding type and data in the corresponding attachments.
+// ---------------------------------------------------------
+void CMmsEncode::PreProcessAttachmentDataL()
+ {
+ TInt error = KErrNone;
+ RFile attachFile;
+ TBool retVal = EFalse;
+ TInt currAttachI;
+
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- start ") );
+#endif /* _NO_MMSS_LOGGING_ */
+ CMsvStore* editStore = iEntryWrapper->EditStoreL();
+ CleanupStack::PushL( editStore );
+
+ MMsvAttachmentManager& attachMan = editStore->AttachmentManagerL();
+ MMsvAttachmentManagerSync& attachManagerSync = editStore->AttachmentManagerExtensionsL();
+ TInt numberOfAttachments = attachMan.AttachmentCount();
+ CMsvAttachment* attachmentInfo = NULL;
+ iTextUtils = CMsgTextUtils::NewL( iFs );
+
+ for ( currAttachI = 0; ( currAttachI < numberOfAttachments ) && ( error == KErrNone ); currAttachI++ )
+ {
+ //gets the ownership from attachment manager
+ attachmentInfo = attachMan.GetAttachmentInfoL( currAttachI );
+ CleanupStack::PushL( attachmentInfo );
+ iMimeHeaders->RestoreL( *attachmentInfo );
+#ifndef _NO_MMSS_LOGGING_
+ TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentDataL- Attchment:%d "), currAttachI );
+#endif /* _NO_MMSS_LOGGING_ */
+
+ if ( iFileOpen )
+ {
+ // close any file in case we have been reset while the file is still open
+ iAttachFile.Close();
+ iFileOpen = EFalse;
+ }
+
+ retVal = CheckAndUpdateAttachmentL(*attachmentInfo, *iMimeHeaders);
+
+ if( retVal )
+ {
+ TRAPD(
+ err,
+ attachManagerSync.ModifyAttachmentInfoL(attachmentInfo);
+ editStore->CommitL();
+ );
+#ifndef _NO_MMSS_LOGGING_
+ if(err != KErrNone)
+ {
+ TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit error: %d"), err );
+ }
+ else
+ {
+ TMmsLogger::Log( _L("CMmsEncode::PreProcessAttachmentData:: store commit success") );
+ }
+#endif /* _NO_MMSS_LOGGING_ */
+ /* attachmentInfo ownership is transfered 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| --->coverted 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