mmsengine/genutils/src/mmsattachmenthandler.cpp
changeset 0 72b543305e3a
child 67 fc91263aee62
--- /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