mmsengine/genutils/src/mmsattachmenthandler.cpp
changeset 31 ebfee66fde93
parent 0 72b543305e3a
child 67 fc91263aee62
equal deleted inserted replaced
30:6a20128ce557 31:ebfee66fde93
       
     1 /*
       
     2 * Copyright (c) 2004-2006 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:  
       
    15 *      Helper class to implement attachment handling
       
    16 *
       
    17 */
       
    18 
       
    19 
       
    20 
       
    21 // INCLUDE FILES
       
    22 
       
    23 #include    <e32std.h>
       
    24 
       
    25 #include    <msventry.h>
       
    26 #include    <msvstd.h>
       
    27 #include    <msvapi.h> //Message Server
       
    28 #include    <centralrepository.h>
       
    29 #include    <badesca.h>
       
    30 #include    <cmsvmimeheaders.h>
       
    31 #include    <mmsvattachmentmanager.h>
       
    32 #include    <mmsvattachmentmanagersync.h>
       
    33 #include    <utf.h>
       
    34 #include    <f32file.h>
       
    35 #include    <msgtextutils.h>
       
    36 
       
    37 #include    "mmsconst.h"
       
    38 #include    "mmsattachmenthandler.h"
       
    39 #include    "mmsgenutils.h"
       
    40 
       
    41 // EXTERNAL DATA STRUCTURES
       
    42 
       
    43 // EXTERNAL FUNCTION PROTOTYPES  
       
    44 
       
    45 // CONSTANTS
       
    46 
       
    47 // MACROS
       
    48 
       
    49 // LOCAL CONSTANTS AND MACROS
       
    50 const TInt KMms10kilos = 10240;
       
    51 const TInt KMmsMaxBytesPerCharacter = 4;    
       
    52 const TInt KMmsTextBufferSize = 256;
       
    53 const TInt KMmsUnicodeToUtf2MaxIncrease = 2;
       
    54 const TInt KMmsLengthOfCRlf = 2;
       
    55 
       
    56 // MODULE DATA STRUCTURES
       
    57 
       
    58 // LOCAL FUNCTION PROTOTYPES
       
    59 
       
    60 // ==================== LOCAL FUNCTIONS ====================
       
    61 
       
    62 // ================= MEMBER FUNCTIONS =======================
       
    63 
       
    64 // C++ default constructor can NOT contain any code, that
       
    65 // might leave.
       
    66 //
       
    67 CMmsAttachmentHandler::CMmsAttachmentHandler() 
       
    68     {
       
    69     }
       
    70 
       
    71 // EPOC default constructor can leave.
       
    72 void CMmsAttachmentHandler::ConstructL()
       
    73     {
       
    74     }
       
    75 
       
    76 // Two-phased constructor.
       
    77 EXPORT_C CMmsAttachmentHandler* CMmsAttachmentHandler::NewL() 
       
    78     {
       
    79     CMmsAttachmentHandler* self = new (ELeave) CMmsAttachmentHandler();
       
    80     
       
    81     CleanupStack::PushL( self );
       
    82     self->ConstructL();
       
    83     CleanupStack::Pop( self );
       
    84 
       
    85     return self;
       
    86     }
       
    87 
       
    88     
       
    89 // Destructor
       
    90 CMmsAttachmentHandler::~CMmsAttachmentHandler()
       
    91     {
       
    92     }
       
    93 
       
    94 // ---------------------------------------------------------
       
    95 // CMmsAttachmentHandler::AttachmentsSizeL
       
    96 // ---------------------------------------------------------
       
    97 //
       
    98 EXPORT_C TInt CMmsAttachmentHandler::AttachmentsSizeL( CMsvStore& aStore )
       
    99     {
       
   100     // Caller controls store
       
   101     TInt size = 0;
       
   102     
       
   103     MMsvAttachmentManager& attachMan = aStore.AttachmentManagerL();
       
   104     TInt numAttachments = attachMan.AttachmentCount();
       
   105     
       
   106     TInt i;
       
   107     
       
   108     for ( i = 0; i < numAttachments; i++ )
       
   109         {
       
   110         CMsvAttachment* attachmentInfo = attachMan.GetAttachmentInfoL(i);
       
   111         CleanupStack::PushL( attachmentInfo );
       
   112         
       
   113         CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
       
   114         CleanupStack::PushL( mimeHeaders );
       
   115         
       
   116         mimeHeaders->RestoreL( *attachmentInfo );
       
   117         
       
   118         RFile attaFile = attachMan.GetAttachmentFileL( i );
       
   119         CleanupClosePushL( attaFile );
       
   120         TInt fileSize = 0;
       
   121         
       
   122         // If we cannot access the file, we are in trouble
       
   123         User::LeaveIfError( attaFile.Size( fileSize ) ); 
       
   124         
       
   125         // This adds up mime header size + actual attachment binary data
       
   126         size += mimeHeaders->Size() + fileSize;
       
   127         
       
   128         CleanupStack::PopAndDestroy( &attaFile ); // close attaFile
       
   129         CleanupStack::PopAndDestroy( mimeHeaders );
       
   130         CleanupStack::PopAndDestroy( attachmentInfo );
       
   131         }
       
   132     
       
   133     return size;
       
   134     }
       
   135     
       
   136 // ---------------------------------------------------------
       
   137 // CMmsAttachmentHandler::IsValidFilename
       
   138 // ---------------------------------------------------------
       
   139 //
       
   140 EXPORT_C TBool CMmsAttachmentHandler::IsValidFilename( RFs& aFs, const TPtrC& aFileName )
       
   141     {
       
   142     TBool validName = EFalse; //pessimist.
       
   143     
       
   144     if ( aFileName.Length() == 0 )
       
   145         {
       
   146         return EFalse;
       
   147         }
       
   148 
       
   149     // filename should not start with dot
       
   150     // or contain any control characters
       
   151     TInt i;
       
   152     // First character may not be . or space
       
   153     if ( aFileName[0] == 0x2e || aFileName[0] == 0x20 )
       
   154         {
       
   155         return EFalse;
       
   156         }
       
   157 
       
   158     for ( i = 0; i < aFileName.Length(); i++ )
       
   159         {
       
   160         // check for control characters - RFs does not do it.
       
   161         if ( aFileName[i] < 0x20 )
       
   162             {
       
   163             // found a control character - not allowed.
       
   164             return EFalse;
       
   165             }
       
   166         }
       
   167     validName = aFs.IsValidName( aFileName );
       
   168 
       
   169     return validName;
       
   170     }
       
   171     
       
   172 // ---------------------------------------------------------
       
   173 // CMmsAttachmentHandler::CreateAttachmentL
       
   174 // ---------------------------------------------------------
       
   175 //
       
   176 EXPORT_C void CMmsAttachmentHandler::CreateAttachmentL(
       
   177             CMsvStore& aStore,
       
   178             RFile& aFile,
       
   179             RFs& aFs,
       
   180             TDriveUnit aMessageDrive,
       
   181             TDesC8& aMimeType,
       
   182             CMsvMimeHeaders& aMimeHeaders,
       
   183             CMsvAttachment* aAttachmentInfo,
       
   184             TMsvAttachmentId& aAttaId)
       
   185     {
       
   186     // The ownership of aAttachmentInfo will be transferred to attachment manager
       
   187     // We must keep it safe until that time  
       
   188     CleanupStack::PushL( aAttachmentInfo );
       
   189       
       
   190     // Check that sufficient disk space available
       
   191     // for attachment binary file and index entry
       
   192     
       
   193     TInt error = KErrNone;
       
   194     TInt fileSize = 0;
       
   195     
       
   196     error = aFile.Size( fileSize );
       
   197     User::LeaveIfError( error );
       
   198     
       
   199     aAttachmentInfo->SetSize( fileSize );
       
   200     if ( aMimeHeaders.SuggestedFilename().Length() == 0 )
       
   201         {
       
   202         TFileName name;
       
   203         error = aFile.Name( name );
       
   204         if ( error == KErrNone )
       
   205             {
       
   206             aMimeHeaders.SetSuggestedFilenameL( name );
       
   207             }
       
   208         }
       
   209     
       
   210     if ( aMimeHeaders.SuggestedFilename().Length() > 0 )
       
   211         {
       
   212         aAttachmentInfo->SetAttachmentNameL( aMimeHeaders.SuggestedFilename() );
       
   213         }
       
   214     if ( aMimeType.Length() > 0 )
       
   215         {
       
   216         aAttachmentInfo->SetMimeTypeL( aMimeType );
       
   217         }
       
   218     
       
   219     // Check that sufficient disk space available
       
   220     // for attachment binary file and index entry
       
   221     
       
   222     // This does not include mime headers.
       
   223     // The mime headers are covered by KMmsIndexEntryExtra,
       
   224     // however the value may be too small, has to be checked.
       
   225     
       
   226     if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
       
   227         &aFs, 
       
   228         fileSize + KMmsIndexEntryExtra,
       
   229         aMessageDrive ) )
       
   230         {
       
   231         // we use standard error code here
       
   232         User::Leave( KErrDiskFull );
       
   233         }
       
   234         
       
   235     if ( ( aMimeHeaders.ContentType().Length() == 0 ||
       
   236         aMimeHeaders.ContentSubType().Length() == 0  ) && aMimeType.Length() > 0 )
       
   237         {
       
   238         TInt position = aMimeType.Find( KMmsSlash8 );
       
   239         if ( position > 0 )
       
   240             {
       
   241             aMimeHeaders.SetContentTypeL( aMimeType.Left( position ) );
       
   242             }
       
   243         if ( position < aMimeType.Length() - 1 )
       
   244             {
       
   245             aMimeHeaders.SetContentSubTypeL( aMimeType.Mid( position + 1 ) );
       
   246             }
       
   247         }
       
   248     
       
   249     MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
       
   250     
       
   251     RFile attaFile;
       
   252     
       
   253     // ownership of aAttachmentInfo is transferred to attachment manager.
       
   254     attaManSync.CreateAttachmentL( aMimeHeaders.SuggestedFilename(),
       
   255         attaFile, aAttachmentInfo );
       
   256     aAttaId = aAttachmentInfo->Id();
       
   257     CleanupStack::Pop( aAttachmentInfo ); // attachment manager now owns aAttachmentInfo
       
   258        
       
   259     // If the previous call was successful, we can now write the data
       
   260     // We need a buffer because we read from one file and write to another
       
   261     
       
   262     CleanupClosePushL( attaFile );
       
   263     
       
   264     if ( fileSize > 0 )
       
   265         {
       
   266         // Greedy, but we don't try to swallow large files all in one piece
       
   267         // Small files may be handled in one piece
       
   268         HBufC8* buffer = HBufC8::NewL( Min( fileSize, KMms10kilos ) ); // Try to get at least 10 k
       
   269         CleanupStack::PushL( buffer );
       
   270         
       
   271         TPtr8 ptr = buffer->Des();
       
   272         ptr.SetLength( 1 ); // initialized to something larger that 0, size is adjusted later
       
   273         
       
   274         while( ptr.Length() > 0 && error == KErrNone )
       
   275             {
       
   276             error = aFile.Read( ptr );
       
   277             if ( ptr.Length() > 0 && error == KErrNone)
       
   278                 {
       
   279                 error = attaFile.Write( ptr );
       
   280                 }
       
   281             }
       
   282         if ( error == KErrNone )
       
   283             {
       
   284             error = attaFile.Flush();
       
   285             }
       
   286         
       
   287         CleanupStack::PopAndDestroy( buffer );
       
   288         buffer = NULL;
       
   289         }
       
   290         
       
   291     // we must alway close    
       
   292     CleanupStack::PopAndDestroy( &attaFile ); // close attaFile
       
   293     
       
   294     // Now actual datafile is ready.
       
   295     // We still have the atta info, and we must store the mimeheaders
       
   296     
       
   297     aMimeHeaders.StoreL( *aAttachmentInfo );
       
   298     
       
   299     // Now all should be ready. 
       
   300     // Caller must commit store (maybe headers still need to be changed,
       
   301     // or maybe several attachments are added before committing store)
       
   302     
       
   303     User::LeaveIfError( error );
       
   304     }
       
   305     
       
   306 // ---------------------------------------------------------
       
   307 // CMmsAttachmentHandler::CreateTextAttachmentL
       
   308 // ---------------------------------------------------------
       
   309 EXPORT_C void CMmsAttachmentHandler::CreateTextAttachmentL(
       
   310     CMsvStore& aStore,
       
   311     TMsvAttachmentId& aAttachmentId,
       
   312     const TDesC& aText,
       
   313     const TDesC& aFile,
       
   314     RFs& aFs,
       
   315     TDriveUnit aMessageDrive,
       
   316     TBool aConvertParagraphSeparator /*= ETrue*/ )
       
   317     {
       
   318     
       
   319     HBufC* convertedText = NULL;
       
   320     TPtrC text;
       
   321     
       
   322     if ( aConvertParagraphSeparator )
       
   323         {
       
   324         convertedText = CMsgTextUtils::ConvertParagraphSeparatorsLC( aText );
       
   325         text.Set( convertedText->Des() );
       
   326         }
       
   327     else
       
   328         {
       
   329         text.Set( aText );
       
   330         }
       
   331     
       
   332     const TInt KMmsMaxBytesPerCharacter = 4;    
       
   333     HBufC8* buffer = HBufC8::NewL( text.Length() * KMmsMaxBytesPerCharacter ); // paranoid.
       
   334     CleanupStack::PushL( buffer );
       
   335     TPtr8 buf8 = buffer->Des();
       
   336 
       
   337     CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
       
   338     CleanupStack::PushL( mimeHeaders );
       
   339 
       
   340     // attaInfo must be on top of stack because the ownership will be transferred
       
   341     // to attacment manager.    
       
   342     CMsvAttachment* attaInfo = CMsvAttachment::NewL(CMsvAttachment::EMsvFile);
       
   343     CleanupStack::PushL( attaInfo );
       
   344     
       
   345     TPtrC8 contentType;
       
   346     contentType.Set( KMmsTextPlain );
       
   347     
       
   348     TInt position = contentType.Find( KMmsSlash8 );
       
   349     mimeHeaders->SetContentTypeL( contentType.Left( position ) );
       
   350     mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) );
       
   351     attaInfo->SetMimeTypeL( contentType );
       
   352     attaInfo->SetAttachmentNameL( aFile );
       
   353     
       
   354     mimeHeaders->SetMimeCharset( KMmsUtf8 );
       
   355     mimeHeaders->SetSuggestedFilenameL( aFile );
       
   356     
       
   357     // if conversion fails, something is really seriously wrong
       
   358     TInt error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, text );
       
   359   
       
   360     if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
       
   361         &aFs,
       
   362         buf8.Length() + mimeHeaders->Size() + KMmsIndexEntryExtra,
       
   363         aMessageDrive ) )
       
   364         {
       
   365         // we use standard error code here
       
   366         User::Leave( KErrDiskFull );
       
   367         }
       
   368     else
       
   369         {
       
   370         User::LeaveIfError( error );    
       
   371         }
       
   372         
       
   373     attaInfo->SetSize( buf8.Length() );
       
   374     mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info
       
   375 
       
   376     MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
       
   377     
       
   378     RFile attaFile;
       
   379     attaManSync.CreateAttachmentL( aFile, attaFile, attaInfo );
       
   380     CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred.
       
   381     aAttachmentId = attaInfo->Id();
       
   382 
       
   383     // Now our file handle is open for writing
       
   384     
       
   385     if ( buf8.Length() > 0 )
       
   386         {
       
   387         attaFile.Write( buf8 );
       
   388         error = attaFile.Flush();
       
   389         }
       
   390     attaFile.Close();
       
   391     
       
   392     if ( error != KErrNone )
       
   393         {
       
   394         // Something went wrong when we tried to write our data.
       
   395         // We must delete the attachment as it does not contain the
       
   396         // intended data.
       
   397         RemoveAttachmentL( aAttachmentId, aStore );
       
   398         aAttachmentId = 0;
       
   399         }
       
   400 
       
   401     CleanupStack::PopAndDestroy( mimeHeaders );
       
   402     CleanupStack::PopAndDestroy( buffer );
       
   403     
       
   404     if ( convertedText )
       
   405         {
       
   406         CleanupStack::PopAndDestroy( convertedText );
       
   407         convertedText = NULL;
       
   408         }
       
   409         
       
   410     User::LeaveIfError( error );    
       
   411         
       
   412     }
       
   413     
       
   414 // ---------------------------------------------------------
       
   415 // CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL
       
   416 // ---------------------------------------------------------
       
   417 EXPORT_C void CMmsAttachmentHandler::CreateUTF8TextAttachmentFromFileL(
       
   418     CMsvStore& aStore,
       
   419     TMsvAttachmentId& aAttachmentId,
       
   420     RFile& aFile,
       
   421     RFs& aFs,
       
   422     TDriveUnit aMessageDrive )
       
   423     {
       
   424     
       
   425     _LIT8 ( KMmsCrLf8, "\x00D\x00A" ); // 8 bit line feed
       
   426     TInt size = 0;
       
   427     TInt error = KErrNone;
       
   428     error = aFile.Size( size );
       
   429     
       
   430     User::LeaveIfError( error ); // if can't get file size, we are in trouble
       
   431 
       
   432     TFileName* filename = new( ELeave ) TFileName;
       
   433     CleanupStack::PushL( filename );
       
   434     
       
   435     // 256 characters for each read
       
   436     HBufC* textBuffer = HBufC::NewL( KMmsTextBufferSize );
       
   437     CleanupStack::PushL( textBuffer );
       
   438     TPtr textPtr = textBuffer->Des();
       
   439 
       
   440     HBufC8* buffer = HBufC8::NewL( KMmsTextBufferSize * KMmsMaxBytesPerCharacter ); // paranoid.
       
   441     TInt fileSize = 0; // we don't know how big the file will be after conversion
       
   442     CleanupStack::PushL( buffer );
       
   443     TPtr8 buf8 = buffer->Des();
       
   444 
       
   445     CMsvMimeHeaders* mimeHeaders = CMsvMimeHeaders::NewL();
       
   446     CleanupStack::PushL( mimeHeaders );
       
   447 
       
   448     // attaInfo must be on top of stack because the ownership will be transferred
       
   449     // to attacment manager.    
       
   450     CMsvAttachment* attaInfo = CMsvAttachment::NewL( CMsvAttachment::EMsvFile );
       
   451     CleanupStack::PushL( attaInfo );
       
   452     
       
   453     TPtrC8 contentType;
       
   454     contentType.Set( KMmsTextPlain );
       
   455     
       
   456     TInt position = contentType.Find( KMmsSlash8 );
       
   457     mimeHeaders->SetContentTypeL( contentType.Left( position ) );
       
   458     mimeHeaders->SetContentSubTypeL( contentType.Mid( position + 1 ) );
       
   459     attaInfo->SetMimeTypeL( contentType );
       
   460     
       
   461     filename->Copy( TPtrC() );
       
   462   	aFile.Name( *filename ); // if this returns error, filename should be empty - no suggestion.
       
   463     attaInfo->SetAttachmentNameL( *filename );
       
   464     mimeHeaders->SetSuggestedFilenameL( *filename );
       
   465     mimeHeaders->SetMimeCharset( KMmsUtf8 );
       
   466     
       
   467     if ( TMmsGenUtils::DiskSpaceBelowCriticalLevelL( 
       
   468         &aFs,
       
   469         size * KMmsUnicodeToUtf2MaxIncrease + mimeHeaders->Size() + KMmsIndexEntryExtra,
       
   470         aMessageDrive ) )
       
   471         {
       
   472         // we use standard error code here
       
   473         User::Leave( KErrDiskFull );
       
   474         }
       
   475        
       
   476     mimeHeaders->StoreL( *attaInfo ); // Mime headers are streamed into atta info
       
   477 
       
   478     MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
       
   479     
       
   480     RFile attaFile;
       
   481     attaManSync.CreateAttachmentL( *filename, attaFile, attaInfo );
       
   482     CleanupStack::Pop( attaInfo ); // attaInfo ownership was transferred.
       
   483     aAttachmentId = attaInfo->Id();
       
   484 
       
   485     // Now our file handle is open for writing
       
   486     
       
   487     error = KErrNone;
       
   488     TMmsFileText textFile;
       
   489     textFile.Set( aFile );
       
   490 
       
   491     while ( error == KErrNone || error == KErrTooBig )
       
   492         {
       
   493         error = textFile.Read( textPtr );
       
   494         TBool appendCRLF = ETrue;
       
   495         if ( error == KErrTooBig )
       
   496             {
       
   497             appendCRLF = EFalse;
       
   498             error = KErrNone;
       
   499             }
       
   500         if ( error != KErrEof )
       
   501             {
       
   502             // if conversion fails, something is really seriously wrong
       
   503             error = CnvUtfConverter::ConvertFromUnicodeToUtf8( buf8, textPtr );
       
   504             }
       
   505         if ( error == KErrNone )
       
   506             {
       
   507             error = attaFile.Write( buf8 );
       
   508             if ( error == KErrNone )
       
   509                 {
       
   510                 fileSize += buf8.Length();
       
   511                 if ( appendCRLF )
       
   512                     {
       
   513                     error = attaFile.Write( KMmsCrLf8 );
       
   514                     fileSize += KMmsLengthOfCRlf; // add length of carriage return/line feed
       
   515                     }
       
   516                 }
       
   517             }
       
   518         }
       
   519         
       
   520     if ( error == KErrEof )
       
   521         {
       
   522         // end of file has been reached successfully
       
   523         error = KErrNone;
       
   524         }
       
   525 
       
   526     if ( error == KErrNone )
       
   527         {
       
   528         error = attaFile.Flush();
       
   529         }
       
   530     attaFile.Close();
       
   531     
       
   532     if ( error != KErrNone )
       
   533         {
       
   534         // Something went wrong when we tried to write our data.
       
   535         // We must delete the attachment as it does not contain the
       
   536         // intended data.
       
   537         RemoveAttachmentL( aAttachmentId, aStore );
       
   538         aAttachmentId = 0;
       
   539         }
       
   540     else
       
   541         {
       
   542         // If data writing was successful, the amount of data written
       
   543         // is now stored in fileSize.
       
   544         // Attachment info structure must be updated
       
   545         MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL();
       
   546         attaInfo = attaMan.GetAttachmentInfoL( aAttachmentId );
       
   547         CleanupStack::PushL( attaInfo );
       
   548         attaInfo->SetSize( fileSize );
       
   549         attaManSync.ModifyAttachmentInfoL( attaInfo );
       
   550         // attachment manager now owns the attachment info
       
   551         CleanupStack::Pop( attaInfo ); // attaInfo
       
   552         }
       
   553         
       
   554     CleanupStack::PopAndDestroy( mimeHeaders );
       
   555     CleanupStack::PopAndDestroy( buffer );
       
   556     CleanupStack::PopAndDestroy( textBuffer );
       
   557     CleanupStack::PopAndDestroy( filename );
       
   558     
       
   559     User::LeaveIfError( error );
       
   560     
       
   561     }
       
   562     
       
   563 // ---------------------------------------------------------
       
   564 // 
       
   565 // ---------------------------------------------------------
       
   566 void CMmsAttachmentHandler::RemoveAttachmentL( TMsvAttachmentId aAttaId, CMsvStore& aStore )
       
   567     {
       
   568     MMsvAttachmentManager& attaMan = aStore.AttachmentManagerL();
       
   569     MMsvAttachmentManagerSync& attaManSync = aStore.AttachmentManagerExtensionsL();
       
   570 
       
   571     // can only remove synchronously if index is known.
       
   572     TInt count = attaMan.AttachmentCount();
       
   573     
       
   574     TInt i = count - 1;
       
   575     TBool found = EFalse;
       
   576     while ( i >= 0 && !found )
       
   577         {
       
   578         CMsvAttachment* attachmentInfo = attaMan.GetAttachmentInfoL( i );
       
   579         CleanupStack::PushL( attachmentInfo );
       
   580         if ( attachmentInfo->Id() == aAttaId )
       
   581             {
       
   582             found = ETrue;
       
   583             }
       
   584         else
       
   585             {
       
   586             i--;
       
   587             }
       
   588         CleanupStack::PopAndDestroy( attachmentInfo );    
       
   589         attachmentInfo = NULL;
       
   590         }
       
   591     if ( i >= 0 && found )
       
   592         {
       
   593         attaManSync.RemoveAttachmentL( i );
       
   594         }
       
   595     }
       
   596     
       
   597 
       
   598 // Helper class that is used instead of TFileText
       
   599 // because the TFileText does not behave like we want it to behave    
       
   600     
       
   601 // ---------------------------------------------------------
       
   602 // Default constructor.
       
   603 // ---------------------------------------------------------
       
   604 //
       
   605 TMmsFileText::TMmsFileText()
       
   606 	{}
       
   607 
       
   608 // ---------------------------------------------------------
       
   609 // Sets the file to be read from
       
   610 // ---------------------------------------------------------
       
   611 //
       
   612 void TMmsFileText::Set( RFile& aFile )
       
   613 	{
       
   614     iFile = aFile;
       
   615     iReadBuf.Zero();
       
   616     iNext = ( TText* )iReadBuf.Ptr();
       
   617     iEnd = iNext;
       
   618     TInt pos = 0;
       
   619     iFile.Seek( ESeekStart, pos );
       
   620     iState = EStartOfFile;
       
   621 	}
       
   622 
       
   623 // ---------------------------------------------------------
       
   624 //
       
   625 // ---------------------------------------------------------
       
   626 //
       
   627 TInt TMmsFileText::Read( TDes& aDes )
       
   628 /**
       
   629 Reads single line text record into the specified descriptor.
       
   630 
       
   631 The read operation begins at the current file position, and ends when
       
   632 a line delimiter character is read or the caller's buffer is full or
       
   633 the file ends;
       
   634 
       
   635 If the line is longer than fits into user's buffer, of if the file does
       
   636 not end with a terminator, KErrTooBig is returned.
       
   637 The purpose is to inform the caller that a terminator should not be added
       
   638 to the line when it is written elsewhere.
       
   639 
       
   640 Next time the reading continues from the current position so that a long
       
   641 line may be read in chunks and terminator added when the end of the line
       
   642 has been reached.
       
   643 
       
   644 If Read() is called when the current position is the end of the file (that 
       
   645 is, after the last line delimiter in the file), KErrEof is returned, and the 
       
   646 length of the buffer is set to zero.
       
   647 
       
   648 @param aDes On return, contains the single record read from the file. Any 
       
   649             previous contents are overwritten.
       
   650 
       
   651 @return KErrNone if successful, otherwise one of the other system-wide error 
       
   652         codes. KErrTooBig indicates that the line does not end with a
       
   653         terminator. Buffer is too short to hold the whole line or the line
       
   654         is the last line in the file and the file does not end with a 
       
   655         terminator character.
       
   656 */
       
   657 	{
       
   658 	TText* pD = ( TText* )aDes.Ptr();
       
   659 	TInt len = aDes.MaxLength();
       
   660 	TInt newLen = 0;
       
   661 	TInt r = KErrNone;
       
   662     TBool terminate = EFalse;
       
   663 	while ( newLen < len )
       
   664 		{
       
   665 		if ( iNext >= iEnd )
       
   666 			{
       
   667 			r = FillBuffer();
       
   668 			if ( r != KErrNone && r != KErrEof )
       
   669 			    {
       
   670 				return r;
       
   671 			    }
       
   672 			if ( r == KErrEof )
       
   673 				{
       
   674 				aDes.SetLength( newLen );
       
   675 				return ( newLen ? KErrTooBig : KErrEof );
       
   676 				}
       
   677 			continue;
       
   678 			}
       
   679 		terminate = newLen;
       
   680 		r = CheckForTerminator( terminate );
       
   681 		if ( r != KErrNone || terminate)
       
   682 			{
       
   683 			aDes.SetLength( newLen );
       
   684 			return r;
       
   685 			}
       
   686 		*pD++ = ( *iNext++ );
       
   687 		newLen++;
       
   688 		}
       
   689 	aDes.SetLength( newLen );
       
   690 	terminate = newLen;
       
   691 	r=CheckForTerminator( terminate );
       
   692 	if ( r != KErrNone || terminate )
       
   693 	    {
       
   694 		return r;
       
   695 	    }
       
   696 // don't skip the rest of the line - return the rest the next time.
       
   697 	return KErrTooBig;
       
   698 	}
       
   699 
       
   700 // ---------------------------------------------------------
       
   701 //
       
   702 // ---------------------------------------------------------
       
   703 //
       
   704 static void SwapWords( TText* aStart, TInt aCount )
       
   705  	{
       
   706  	TUint8* p = ( TUint8* )aStart;
       
   707  	while ( aCount-- > 0 )
       
   708  		{
       
   709  		TUint8 temp = *p;
       
   710  		*p = p[1];
       
   711  		p[1] = temp;
       
   712  		p += 2;
       
   713    		}
       
   714    	}
       
   715 
       
   716 // ---------------------------------------------------------
       
   717 // Read the new data from the file
       
   718 // ---------------------------------------------------------
       
   719 //
       
   720 TInt TMmsFileText::FillBuffer()
       
   721 	{
       
   722 	TInt r = iFile.Read( iReadBuf );
       
   723 	if ( r !=KErrNone )
       
   724 	    {
       
   725 		return r;
       
   726 	    }
       
   727 	if ( iReadBuf.Length() == 0 )
       
   728 	    {
       
   729 		return KErrEof;
       
   730 	    }
       
   731 	iNext = ( const TText* )iReadBuf.Ptr();
       
   732 	iEnd = iNext + iReadBuf.Length() / sizeof( TText );
       
   733 	 
       
   734  	// Use any leading byte order marker to determine endianness.
       
   735  	if ( iState == EStartOfFile )
       
   736  		{
       
   737  		iState = ENormal;
       
   738 
       
   739  		// Ignore an ordinary byte order marker.
       
   740  		if ( *iNext == 0xFEFF )
       
   741  		    {
       
   742  			iNext++;
       
   743  		    }
       
   744 
       
   745  		// Set the endianness state to 'reverse' if a reversed byte order marker is found.
       
   746  		else if ( *iNext == 0xFFFE )
       
   747  			{
       
   748  			iNext++;
       
   749  			iState = EReverse;
       
   750  			}
       
   751  
       
   752  		if ( iNext == iEnd )
       
   753  		    {
       
   754  			return KErrEof;
       
   755  		    }
       
   756  		}
       
   757  
       
   758  	if ( iState == EReverse )
       
   759  	    {
       
   760 		SwapWords( ( TText* )iNext, ( iEnd - iNext ) );
       
   761  	    }
       
   762 
       
   763 	return KErrNone;
       
   764 	}
       
   765 
       
   766 // ---------------------------------------------------------
       
   767 // Return ETrue if the next char is a record terminator: PARAGRAPH SEPARATOR (U+2029), LINE SEPARATOR (U+2028),
       
   768 // CR-LF (U+000D, U+000A), or LF (U+000A)
       
   769 // If the file ends without terminator, return KErrTooBig
       
   770 // KErrTooBig actually only means that the line does not end with a terminator
       
   771 // ---------------------------------------------------------
       
   772 //
       
   773 TInt TMmsFileText::CheckForTerminator( TBool& anAnswer )
       
   774 	{
       
   775 	TInt r = KErrNone;
       
   776 	if ( iNext >= iEnd )
       
   777 		{
       
   778 		r = FillBuffer();
       
   779 		if ( r != KErrNone )
       
   780 			{
       
   781 			if ( r == KErrEof && anAnswer )
       
   782 			    {
       
   783 				return KErrTooBig; // no terminator
       
   784 			    }
       
   785 			return r;
       
   786 			}
       
   787 		}
       
   788 
       
   789 	anAnswer = EFalse;
       
   790 	const TText* oldNext = iNext;
       
   791 	TInt oldBufferLength = iReadBuf.Length();
       
   792 	TText c = ( *iNext );
       
   793 	TBool peek = EFalse;
       
   794 
       
   795 	// Check for unambiguous paragraph or line separator.
       
   796  	if ( c == 0x2029 || c == 0x2028 )
       
   797  		{
       
   798  		iNext++;
       
   799  		anAnswer = ETrue;
       
   800 		return KErrNone;
       
   801  		}
       
   802  
       
   803  	// Check for CR-LF or LF.
       
   804  	if ( c == 0x000D )
       
   805 		{
       
   806 		iNext++;
       
   807 		if ( iNext < iEnd )
       
   808 		    {
       
   809 			c = ( *iNext );
       
   810 		    }
       
   811 		else
       
   812 			{
       
   813 			peek = ETrue;
       
   814 			r = FillBuffer();
       
   815 			if ( r != KErrNone && r != KErrEof )
       
   816 			    {
       
   817 				return r;
       
   818 			    }
       
   819 			if ( r == KErrNone )
       
   820 			    {
       
   821 				c = ( *iNext );
       
   822 			    }
       
   823 			}
       
   824 		}
       
   825 
       
   826 	if ( c == 0x000A )
       
   827 		{
       
   828 		iNext++;
       
   829 		anAnswer = ETrue;
       
   830 		return KErrNone;
       
   831 		}
       
   832 
       
   833 	iNext = oldNext;
       
   834 	if ( !peek )
       
   835 	    {
       
   836 		return KErrNone;
       
   837 	    }
       
   838 
       
   839 	TInt pos = ( -1 ) * ( oldBufferLength + iReadBuf.Length() );
       
   840 	r = iFile.Seek( ESeekCurrent, pos );
       
   841 	if ( r == KErrNone )
       
   842 	    {
       
   843 		r = FillBuffer();
       
   844 	    }
       
   845 	if ( r != KErrNone )
       
   846 	    {
       
   847 		return r;
       
   848 	    }
       
   849 	iNext = oldNext;
       
   850 	return KErrNone;
       
   851 	}
       
   852     
       
   853 // ================= OTHER EXPORTED FUNCTIONS ==============
       
   854 
       
   855 //  End of File