--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/mmsengine/genutils/src/mmsattachmenthandler.cpp Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,855 @@
+/*
+* Copyright (c) 2004-2006 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:
+* Helper class to implement attachment handling
+*
+*/
+
+
+
+// INCLUDE FILES
+
+#include <e32std.h>
+
+#include <msventry.h>
+#include <msvstd.h>
+#include <msvapi.h> //Message Server
+#include <centralrepository.h>
+#include <badesca.h>
+#include <cmsvmimeheaders.h>
+#include <mmsvattachmentmanager.h>
+#include <mmsvattachmentmanagersync.h>
+#include <utf.h>
+#include <f32file.h>
+#include <msgtextutils.h>
+
+#include "mmsconst.h"
+#include "mmsattachmenthandler.h"
+#include "mmsgenutils.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+const TInt KMms10kilos = 10240;
+const TInt KMmsMaxBytesPerCharacter = 4;
+const TInt KMmsTextBufferSize = 256;
+const TInt KMmsUnicodeToUtf2MaxIncrease = 2;
+const TInt KMmsLengthOfCRlf = 2;
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// ==================== LOCAL FUNCTIONS ====================
+
+// ================= MEMBER FUNCTIONS =======================
+
+// C++ default constructor can NOT contain any code, that
+// might leave.
+//
+CMmsAttachmentHandler::CMmsAttachmentHandler()
+ {
+ }
+
+// EPOC default constructor can leave.
+void CMmsAttachmentHandler::ConstructL()
+ {
+ }
+
+// Two-phased constructor.
+EXPORT_C CMmsAttachmentHandler* CMmsAttachmentHandler::NewL()
+ {
+ CMmsAttachmentHandler* self = new (ELeave) CMmsAttachmentHandler();
+
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+
+ return self;
+ }
+
+
+// Destructor
+CMmsAttachmentHandler::~CMmsAttachmentHandler()
+ {
+ }
+
+// ---------------------------------------------------------
+// CMmsAttachmentHandler::AttachmentsSizeL
+// ---------------------------------------------------------
+//
+EXPORT_C TInt CMmsAttachmentHandler::AttachmentsSizeL( CMsvStore& aStore )
+ {
+ // Caller controls store
+ TInt size = 0;
+
+ MMsvAttachmentManager& attachMan = aStore.AttachmentManagerL();
+ TInt numAttachments = attachMan.AttachmentCount();
+
+ TInt i;
+
+ for ( i = 0; i < numAttachments; i++ )
+ {
+ CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(i);
+ CleanupStack::PushL( attachmentInfo );
+
+ CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+ CleanupStack::PushL( mimeHeaders );
+
+ mimeHeaders->RestoreL( *attachmentInfo );
+
+ RFile attaFile = attachMan.GetAttachmentFileL( i );
+ CleanupClosePushL( attaFile );
+ TInt fileSize = 0;
+
+ // If we cannot access the file, we are in trouble
+ User::LeaveIfError( attaFile.Size( fileSize ) );
+
+ // This adds up mime header size + actual attachment binary data
+ size += mimeHeaders->Size() + fileSize;
+
+ CleanupStack::PopAndDestroy( &attaFile ); // close attaFile
+ CleanupStack::PopAndDestroy( mimeHeaders );
+ CleanupStack::PopAndDestroy( attachmentInfo );
+ }
+
+ return size;
+ }
+
+// ---------------------------------------------------------
+// CMmsAttachmentHandler::IsValidFilename
+// ---------------------------------------------------------
+//
+EXPORT_C TBool CMmsAttachmentHandler::IsValidFilename( RFs& aFs, const TPtrC& aFileName )
+ {
+ TBool validName = EFalse; //pessimist.
+
+ if ( aFileName.Length() == 0 )
+ {
+ return EFalse;
+ }
+
+ // filename should not start with dot
+ // or contain any control characters
+ TInt i;
+ // First character may not be . or space
+ if ( aFileName[0] == 0x2e || aFileName[0] == 0x20 )
+ {
+ return EFalse;
+ }
+
+ for ( i = 0; i < aFileName.Length(); i++ )
+ {
+ // check for control characters - RFs does not do it.
+ if ( aFileName[i] < 0x20 )
+ {
+ // found a control character - not allowed.
+ return EFalse;
+ }
+ }
+ validName = aFs.IsValidName( aFileName );
+
+ return validName;
+ }
+
+// ---------------------------------------------------------
+// CMmsAttachmentHandler::CreateAttachmentL
+// ---------------------------------------------------------
+//
+EXPORT_C void CMmsAttachmentHandler::CreateAttachmentL(
+ CMsvStore& aStore,
+ RFile& aFile,
+ RFs& aFs,
+ TDriveUnit aMessageDrive,
+ TDesC8& aMimeType,
+ CMsvMimeHeaders& aMimeHeaders,
+ CMsvAttachment* aAttachmentInfo,
+ TMsvAttachmentId& aAttaId)
+ {
+ // The ownership of aAttachmentInfo will be transferred to attachment manager
+ // We must keep it safe until that time
+ CleanupStack::PushL( aAttachmentInfo );
+
+ // Check that sufficient disk space available
+ // for attachment binary file and index entry
+
+ TInt error = KErrNone;
+ TInt fileSize = 0;
+
+ error = aFile.Size( fileSize );
+ User::LeaveIfError( error );
+
+ aAttachmentInfo->SetSize( fileSize );
+ if ( aMimeHeaders.SuggestedFilename().Length() == 0 )
+ {
+ TFileName name;
+ error = aFile.Name( name );
+ if ( error == KErrNone )
+ {
+ aMimeHeaders.SetSuggestedFilenameL( name );
+ }
+ }
+
+ if ( aMimeHeaders.SuggestedFilename().Length() > 0 )
+ {
+ aAttachmentInfo->SetAttachmentNameL( aMimeHeaders.SuggestedFilename() );
+ }
+ if ( aMimeType.Length() > 0 )
+ {
+ aAttachmentInfo->SetMimeTypeL( aMimeType );
+ }
+
+ // Check that sufficient disk space available
+ // for attachment binary file and index entry
+
+ // This does not include mime headers.
+ // The mime headers are covered by KMmsIndexEntryExtra,
+ // however the value may be too small, has to be checked.
+
+ if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
+ &aFs,
+ fileSize + KMmsIndexEntryExtra,
+ aMessageDrive ) )
+ {
+ // we use standard error code here
+ User::Leave( KErrDiskFull );
+ }
+
+ if ( ( aMimeHeaders.ContentType().Length() == 0 ||
+ aMimeHeaders.ContentSubType().Length() == 0 ) && aMimeType.Length() > 0 )
+ {
+ TInt position = aMimeType.Find( KMmsSlash8 );
+ if ( position > 0 )
+ {
+ aMimeHeaders.SetContentTypeL( aMimeType.Left( position ) );
+ }
+ if ( position < aMimeType.Length() - 1 )
+ {
+ aMimeHeaders.SetContentSubTypeL( aMimeType.Mid( position + 1 ) );
+ }
+ }
+
+ MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
+
+ RFile attaFile;
+
+ // ownership of aAttachmentInfo is transferred to attachment manager.
+ attaManSync.CreateAttachmentL( aMimeHeaders.SuggestedFilename(),
+ attaFile, aAttachmentInfo );
+ aAttaId = aAttachmentInfo->Id();
+ CleanupStack::Pop( aAttachmentInfo ); // attachment manager now owns aAttachmentInfo
+
+ // If the previous call was successful, we can now write the data
+ // We need a buffer because we read from one file and write to another
+
+ CleanupClosePushL( attaFile );
+
+ if ( fileSize > 0 )
+ {
+ // Greedy, but we don't try to swallow large files all in one piece
+ // Small files may be handled in one piece
+ HBufC8* buffer = HBufC8::NewL( Min( fileSize, KMms10kilos ) ); // Try to get at least 10 k
+ CleanupStack::PushL( buffer );
+
+ TPtr8 ptr = buffer->Des();
+ ptr.SetLength( 1 ); // initialized to something larger that 0, size is adjusted later
+
+ while( ptr.Length() > 0 && error == KErrNone )
+ {
+ error = aFile.Read( ptr );
+ if ( ptr.Length() > 0 && error == KErrNone)
+ {
+ error = attaFile.Write( ptr );
+ }
+ }
+ if ( error == KErrNone )
+ {
+ error = attaFile.Flush();
+ }
+
+ CleanupStack::PopAndDestroy( buffer );
+ buffer = NULL;
+ }
+
+ // we must alway close
+ CleanupStack::PopAndDestroy( &attaFile ); // close attaFile
+
+ // Now actual datafile is ready.
+ // We still have the atta info, and we must store the mimeheaders
+
+ aMimeHeaders.StoreL( *aAttachmentInfo );
+
+ // Now all should be ready.
+ // Caller must commit store (maybe headers still need to be changed,
+ // or maybe several attachments are added before committing store)
+
+ User::LeaveIfError( error );
+ }
+
+// ---------------------------------------------------------
+// CMmsAttachmentHandler::CreateTextAttachmentL
+// ---------------------------------------------------------
+EXPORT_C void CMmsAttachmentHandler::CreateTextAttachmentL(
+ CMsvStore& aStore,
+ TMsvAttachmentId& aAttachmentId,
+ const TDesC& aText,
+ const TDesC& aFile,
+ RFs& aFs,
+ TDriveUnit aMessageDrive,
+ TBool aConvertParagraphSeparator /*= ETrue*/ )
+ {
+
+ HBufC* convertedText = NULL;
+ TPtrC text;
+
+ if ( aConvertParagraphSeparator )
+ {
+ convertedText = CMsgTextUtils::ConvertParagraphSeparatorsLC( aText );
+ text.Set( convertedText->Des() );
+ }
+ else
+ {
+ text.Set( aText );
+ }
+
+ const TInt KMmsMaxBytesPerCharacter = 4;
+ HBufC8* buffer = HBufC8::NewL( text.Length() * KMmsMaxBytesPerCharacter ); // paranoid.
+ CleanupStack::PushL( buffer );
+ TPtr8 buf8 = buffer->Des();
+
+ CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+ CleanupStack::PushL( mimeHeaders );
+
+ // attaInfo must be on top of stack because the ownership will be transferred
+ // to attacment manager.
+ CMsvAttachment* attaInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
+ CleanupStack::PushL( attaInfo );
+
+ TPtrC8 contentType;
+ contentType.Set( KMmsTextPlain );
+
+ TInt position = contentType.Find( KMmsSlash8 );
+ mimeHeaders->SetContentTypeL( contentType.Left( position ) );
+ mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) );
+ attaInfo->SetMimeTypeL( contentType );
+ attaInfo->SetAttachmentNameL( aFile );
+
+ mimeHeaders->SetMimeCharset( KMmsUtf8 );
+ mimeHeaders->SetSuggestedFilenameL( aFile );
+
+ // if conversion fails, something is really seriously wrong
+ TInt error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, text );
+
+ if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
+ &aFs,
+ buf8.Length() + mimeHeaders->Size() + KMmsIndexEntryExtra,
+ aMessageDrive ) )
+ {
+ // we use standard error code here
+ User::Leave( KErrDiskFull );
+ }
+ else
+ {
+ User::LeaveIfError( error );
+ }
+
+ attaInfo->SetSize( buf8.Length() );
+ mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info
+
+ MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
+
+ RFile attaFile;
+ attaManSync.CreateAttachmentL( aFile, attaFile, attaInfo );
+ CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred.
+ aAttachmentId = attaInfo->Id();
+
+ // Now our file handle is open for writing
+
+ if ( buf8.Length() > 0 )
+ {
+ attaFile.Write( buf8 );
+ error = attaFile.Flush();
+ }
+ attaFile.Close();
+
+ if ( error != KErrNone )
+ {
+ // Something went wrong when we tried to write our data.
+ // We must delete the attachment as it does not contain the
+ // intended data.
+ RemoveAttachmentL( aAttachmentId, aStore );
+ aAttachmentId = 0;
+ }
+
+ CleanupStack::PopAndDestroy( mimeHeaders );
+ CleanupStack::PopAndDestroy( buffer );
+
+ if ( convertedText )
+ {
+ CleanupStack::PopAndDestroy( convertedText );
+ convertedText = NULL;
+ }
+
+ User::LeaveIfError( error );
+
+ }
+
+// ---------------------------------------------------------
+// CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL
+// ---------------------------------------------------------
+EXPORT_C void CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL(
+ CMsvStore& aStore,
+ TMsvAttachmentId& aAttachmentId,
+ RFile& aFile,
+ RFs& aFs,
+ TDriveUnit aMessageDrive )
+ {
+
+ _LIT8 ( KMmsCrLf8, "\x00D\x00A" ); // 8 bit line feed
+ TInt size = 0;
+ TInt error = KErrNone;
+ error = aFile.Size( size );
+
+ User::LeaveIfError( error ); // if can't get file size, we are in trouble
+
+ TFileName* filename = new( ELeave ) TFileName;
+ CleanupStack::PushL( filename );
+
+ // 256 characters for each read
+ HBufC* textBuffer = HBufC::NewL( KMmsTextBufferSize );
+ CleanupStack::PushL( textBuffer );
+ TPtr textPtr = textBuffer->Des();
+
+ HBufC8* buffer = HBufC8::NewL( KMmsTextBufferSize * KMmsMaxBytesPerCharacter ); // paranoid.
+ TInt fileSize = 0; // we don't know how big the file will be after conversion
+ CleanupStack::PushL( buffer );
+ TPtr8 buf8 = buffer->Des();
+
+ CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
+ CleanupStack::PushL( mimeHeaders );
+
+ // attaInfo must be on top of stack because the ownership will be transferred
+ // to attacment manager.
+ CMsvAttachment* attaInfo = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
+ CleanupStack::PushL( attaInfo );
+
+ TPtrC8 contentType;
+ contentType.Set( KMmsTextPlain );
+
+ TInt position = contentType.Find( KMmsSlash8 );
+ mimeHeaders->SetContentTypeL( contentType.Left( position ) );
+ mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) );
+ attaInfo->SetMimeTypeL( contentType );
+
+ filename->Copy( TPtrC() );
+ aFile.Name( *filename ); // if this returns error, filename should be empty - no suggestion.
+ attaInfo->SetAttachmentNameL( *filename );
+ mimeHeaders->SetSuggestedFilenameL( *filename );
+ mimeHeaders->SetMimeCharset( KMmsUtf8 );
+
+ if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL(
+ &aFs,
+ size * KMmsUnicodeToUtf2MaxIncrease + mimeHeaders->Size() + KMmsIndexEntryExtra,
+ aMessageDrive ) )
+ {
+ // we use standard error code here
+ User::Leave( KErrDiskFull );
+ }
+
+ mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info
+
+ MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
+
+ RFile attaFile;
+ attaManSync.CreateAttachmentL( *filename, attaFile, attaInfo );
+ CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred.
+ aAttachmentId = attaInfo->Id();
+
+ // Now our file handle is open for writing
+
+ error = KErrNone;
+ TMmsFileText textFile;
+ textFile.Set( aFile );
+
+ while ( error == KErrNone || error == KErrTooBig )
+ {
+ error = textFile.Read( textPtr );
+ TBool appendCRLF = ETrue;
+ if ( error == KErrTooBig )
+ {
+ appendCRLF = EFalse;
+ error = KErrNone;
+ }
+ if ( error != KErrEof )
+ {
+ // if conversion fails, something is really seriously wrong
+ error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, textPtr );
+ }
+ if ( error == KErrNone )
+ {
+ error = attaFile.Write( buf8 );
+ if ( error == KErrNone )
+ {
+ fileSize += buf8.Length();
+ if ( appendCRLF )
+ {
+ error = attaFile.Write( KMmsCrLf8 );
+ fileSize += KMmsLengthOfCRlf; // add length of carriage return/line feed
+ }
+ }
+ }
+ }
+
+ if ( error == KErrEof )
+ {
+ // end of file has been reached successfully
+ error = KErrNone;
+ }
+
+ if ( error == KErrNone )
+ {
+ error = attaFile.Flush();
+ }
+ attaFile.Close();
+
+ if ( error != KErrNone )
+ {
+ // Something went wrong when we tried to write our data.
+ // We must delete the attachment as it does not contain the
+ // intended data.
+ RemoveAttachmentL( aAttachmentId, aStore );
+ aAttachmentId = 0;
+ }
+ else
+ {
+ // If data writing was successful, the amount of data written
+ // is now stored in fileSize.
+ // Attachment info structure must be updated
+ MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL();
+ attaInfo = attaMan.GetAttachmentInfoL( aAttachmentId );
+ CleanupStack::PushL( attaInfo );
+ attaInfo->SetSize( fileSize );
+ attaManSync.ModifyAttachmentInfoL( attaInfo );
+ // attachment manager now owns the attachment info
+ CleanupStack::Pop( attaInfo ); // attaInfo
+ }
+
+ CleanupStack::PopAndDestroy( mimeHeaders );
+ CleanupStack::PopAndDestroy( buffer );
+ CleanupStack::PopAndDestroy( textBuffer );
+ CleanupStack::PopAndDestroy( filename );
+
+ User::LeaveIfError( error );
+
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+void CMmsAttachmentHandler::RemoveAttachmentL( TMsvAttachmentId aAttaId, CMsvStore& aStore )
+ {
+ MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL();
+ MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
+
+ // can only remove synchronously if index is known.
+ TInt count = attaMan.AttachmentCount();
+
+ TInt i = count - 1;
+ TBool found = EFalse;
+ while ( i >= 0 && !found )
+ {
+ CMsvAttachment* attachmentInfo = attaMan.GetAttachmentInfoL( i );
+ CleanupStack::PushL( attachmentInfo );
+ if ( attachmentInfo->Id() == aAttaId )
+ {
+ found = ETrue;
+ }
+ else
+ {
+ i--;
+ }
+ CleanupStack::PopAndDestroy( attachmentInfo );
+ attachmentInfo = NULL;
+ }
+ if ( i >= 0 && found )
+ {
+ attaManSync.RemoveAttachmentL( i );
+ }
+ }
+
+
+// Helper class that is used instead of TFileText
+// because the TFileText does not behave like we want it to behave
+
+// ---------------------------------------------------------
+// Default constructor.
+// ---------------------------------------------------------
+//
+TMmsFileText::TMmsFileText()
+ {}
+
+// ---------------------------------------------------------
+// Sets the file to be read from
+// ---------------------------------------------------------
+//
+void TMmsFileText::Set( RFile& aFile )
+ {
+ iFile = aFile;
+ iReadBuf.Zero();
+ iNext = ( TText* )iReadBuf.Ptr();
+ iEnd = iNext;
+ TInt pos = 0;
+ iFile.Seek( ESeekStart, pos );
+ iState = EStartOfFile;
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+TInt TMmsFileText::Read( TDes& aDes )
+/**
+Reads single line text record into the specified descriptor.
+
+The read operation begins at the current file position, and ends when
+a line delimiter character is read or the caller's buffer is full or
+the file ends;
+
+If the line is longer than fits into user's buffer, of if the file does
+not end with a terminator, KErrTooBig is returned.
+The purpose is to inform the caller that a terminator should not be added
+to the line when it is written elsewhere.
+
+Next time the reading continues from the current position so that a long
+line may be read in chunks and terminator added when the end of the line
+has been reached.
+
+If Read() is called when the current position is the end of the file (that
+is, after the last line delimiter in the file), KErrEof is returned, and the
+length of the buffer is set to zero.
+
+@param aDes On return, contains the single record read from the file. Any
+ previous contents are overwritten.
+
+@return KErrNone if successful, otherwise one of the other system-wide error
+ codes. KErrTooBig indicates that the line does not end with a
+ terminator. Buffer is too short to hold the whole line or the line
+ is the last line in the file and the file does not end with a
+ terminator character.
+*/
+ {
+ TText* pD = ( TText* )aDes.Ptr();
+ TInt len = aDes.MaxLength();
+ TInt newLen = 0;
+ TInt r = KErrNone;
+ TBool terminate = EFalse;
+ while ( newLen < len )
+ {
+ if ( iNext >= iEnd )
+ {
+ r = FillBuffer();
+ if ( r != KErrNone && r != KErrEof )
+ {
+ return r;
+ }
+ if ( r == KErrEof )
+ {
+ aDes.SetLength( newLen );
+ return ( newLen ? KErrTooBig : KErrEof );
+ }
+ continue;
+ }
+ terminate = newLen;
+ r = CheckForTerminator( terminate );
+ if ( r != KErrNone || terminate)
+ {
+ aDes.SetLength( newLen );
+ return r;
+ }
+ *pD++ = ( *iNext++ );
+ newLen++;
+ }
+ aDes.SetLength( newLen );
+ terminate = newLen;
+ r=CheckForTerminator( terminate );
+ if ( r != KErrNone || terminate )
+ {
+ return r;
+ }
+// don't skip the rest of the line - return the rest the next time.
+ return KErrTooBig;
+ }
+
+// ---------------------------------------------------------
+//
+// ---------------------------------------------------------
+//
+static void SwapWords( TText* aStart, TInt aCount )
+ {
+ TUint8* p = ( TUint8* )aStart;
+ while ( aCount-- > 0 )
+ {
+ TUint8 temp = *p;
+ *p = p[1];
+ p[1] = temp;
+ p += 2;
+ }
+ }
+
+// ---------------------------------------------------------
+// Read the new data from the file
+// ---------------------------------------------------------
+//
+TInt TMmsFileText::FillBuffer()
+ {
+ TInt r = iFile.Read( iReadBuf );
+ if ( r !=KErrNone )
+ {
+ return r;
+ }
+ if ( iReadBuf.Length() == 0 )
+ {
+ return KErrEof;
+ }
+ iNext = ( const TText* )iReadBuf.Ptr();
+ iEnd = iNext + iReadBuf.Length() / sizeof( TText );
+
+ // Use any leading byte order marker to determine endianness.
+ if ( iState == EStartOfFile )
+ {
+ iState = ENormal;
+
+ // Ignore an ordinary byte order marker.
+ if ( *iNext == 0xFEFF )
+ {
+ iNext++;
+ }
+
+ // Set the endianness state to 'reverse' if a reversed byte order marker is found.
+ else if ( *iNext == 0xFFFE )
+ {
+ iNext++;
+ iState = EReverse;
+ }
+
+ if ( iNext == iEnd )
+ {
+ return KErrEof;
+ }
+ }
+
+ if ( iState == EReverse )
+ {
+ SwapWords( ( TText* )iNext, ( iEnd - iNext ) );
+ }
+
+ return KErrNone;
+ }
+
+// ---------------------------------------------------------
+// Return ETrue if the next char is a record terminator: PARAGRAPH SEPARATOR (U+2029), LINE SEPARATOR (U+2028),
+// CR-LF (U+000D, U+000A), or LF (U+000A)
+// If the file ends without terminator, return KErrTooBig
+// KErrTooBig actually only means that the line does not end with a terminator
+// ---------------------------------------------------------
+//
+TInt TMmsFileText::CheckForTerminator( TBool& anAnswer )
+ {
+ TInt r = KErrNone;
+ if ( iNext >= iEnd )
+ {
+ r = FillBuffer();
+ if ( r != KErrNone )
+ {
+ if ( r == KErrEof && anAnswer )
+ {
+ return KErrTooBig; // no terminator
+ }
+ return r;
+ }
+ }
+
+ anAnswer = EFalse;
+ const TText* oldNext = iNext;
+ TInt oldBufferLength = iReadBuf.Length();
+ TText c = ( *iNext );
+ TBool peek = EFalse;
+
+ // Check for unambiguous paragraph or line separator.
+ if ( c == 0x2029 || c == 0x2028 )
+ {
+ iNext++;
+ anAnswer = ETrue;
+ return KErrNone;
+ }
+
+ // Check for CR-LF or LF.
+ if ( c == 0x000D )
+ {
+ iNext++;
+ if ( iNext < iEnd )
+ {
+ c = ( *iNext );
+ }
+ else
+ {
+ peek = ETrue;
+ r = FillBuffer();
+ if ( r != KErrNone && r != KErrEof )
+ {
+ return r;
+ }
+ if ( r == KErrNone )
+ {
+ c = ( *iNext );
+ }
+ }
+ }
+
+ if ( c == 0x000A )
+ {
+ iNext++;
+ anAnswer = ETrue;
+ return KErrNone;
+ }
+
+ iNext = oldNext;
+ if ( !peek )
+ {
+ return KErrNone;
+ }
+
+ TInt pos = ( -1 ) * ( oldBufferLength + iReadBuf.Length() );
+ r = iFile.Seek( ESeekCurrent, pos );
+ if ( r == KErrNone )
+ {
+ r = FillBuffer();
+ }
+ if ( r != KErrNone )
+ {
+ return r;
+ }
+ iNext = oldNext;
+ return KErrNone;
+ }
+
+// ================= OTHER EXPORTED FUNCTIONS ==============
+
+// End of File