email/mail/PluginSrc/MailPlainView/MsgMailViewerHtmlConv.cpp
changeset 0 72b543305e3a
equal deleted inserted replaced
-1:000000000000 0:72b543305e3a
       
     1 /*
       
     2 * Copyright (c) 2002 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 *     An HTML stripper
       
    16 *
       
    17 */
       
    18 
       
    19 
       
    20 // INCLUDE FILES
       
    21 #include "MsgMailViewerHtmlConv.h"
       
    22 
       
    23 #include <barsread.h>
       
    24 #include <eikenv.h>
       
    25 #include <f32file.h>
       
    26 #include <aknnotewrappers.h>
       
    27 #include <charconv.h>
       
    28 #include <MailPlainView.rsg>
       
    29 #include <MsgMailViewer.rsg>
       
    30 #include "MailLog.h"
       
    31 
       
    32 
       
    33 
       
    34 // LOCAL CONSTANTS AND MACROS
       
    35 _LIT (KLineBreak, "<br>");
       
    36 _LIT (KUnnumberedList, "<ul>");
       
    37 _LIT (KUnnumberedListEnd, "</ul>");
       
    38 _LIT (KOrderedList, "<ol>");
       
    39 _LIT (KOrderedListEnd, "</ol>");
       
    40 _LIT (KListItem, "<li>");
       
    41 _LIT (KLessThan, "<");
       
    42 _LIT (KGreaterThan, ">");
       
    43 _LIT (KAmpersand, "&");
       
    44 _LIT (KDefinitionList, "<dl>");
       
    45 _LIT (KDefinitionListEnd, "</dl>");
       
    46 _LIT (KDefinitionListTerm, "<dt>");
       
    47 _LIT (KDefinitionListDef, "<dd>");
       
    48 //_LIT (KDefinitionListDefEnd, "</dd>");
       
    49 _LIT (KBodyTag, "<body>");
       
    50 _LIT (KScript, "<script");
       
    51 _LIT (KScriptEnd, "</script>");
       
    52 _LIT (KNumberedItemSeparator, ". ");
       
    53 _LIT (KNewParagraph, "<p>");
       
    54 _LIT (KHeadStart, "<head>");
       
    55 _LIT (KHeadEnd, "</head>");
       
    56 _LIT (KHeading, "<h");
       
    57 _LIT (KHeadingEnd, "</h");
       
    58 _LIT (KStartHtml, "<html>");
       
    59 _LIT (KEndHtml, "</html>");
       
    60 _LIT (KHorizontalRuler, "<hr>");
       
    61 _LIT (KImage, "<img");
       
    62 _LIT (KImageAlt, "alt");
       
    63 _LIT (KImageSrc, "src");
       
    64 _LIT (KSpace, " ");
       
    65 _LIT (KNewline, "\n");
       
    66 _LIT (KListItemSymbol, "- ");
       
    67 _LIT (KSemicolon, ";");
       
    68 //_LIT (KQuotationMark, "\"");
       
    69 _LIT (KBlock, "<div>");
       
    70 _LIT (KBlockEnd, "</div>");
       
    71 _LIT(KCharSet, "charset=");
       
    72 _LIT( KCommentTagEnd, "-->");
       
    73 _LIT( KOlId, "olid" );
       
    74 
       
    75 const TInt KMaxOrdinalLength(5);
       
    76 // "10000 is a reasonable size for a converted block, it has been
       
    77 // tested that bigger size doesn't provide any faster conversion"
       
    78 // Blocksize has been lowered to 1000 to make the UI more
       
    79 // responsive during processing. Based on tests, the performance 
       
    80 // is not noticeably changed by the smaller block size.
       
    81 //
       
    82 const TInt KMaxConvBlockLength( 1000 );
       
    83 
       
    84 const TInt KSampleBufferSize = 256;
       
    85 const TInt KMimimumConfidenceLevel = 50;
       
    86 const TInt KMaxAmountOfSamples = KSampleBufferSize / 2;
       
    87 
       
    88 enum TConversionState {
       
    89     EDeleteHeader = 1,
       
    90     EInitCharConverter,
       
    91     EConvert,
       
    92     ERemoveTags};
       
    93 
       
    94 enum TStatusFlags {
       
    95     EForceUseCharacterSet = KBit0,
       
    96     EOwnsFileNameArray = KBit1
       
    97     };
       
    98 
       
    99 // MODULE DATA STRUCTURES
       
   100 
       
   101 
       
   102 // ================= MEMBER FUNCTIONS =======================
       
   103 
       
   104 CStringPair::CStringPair()
       
   105     {
       
   106     }
       
   107 
       
   108 CStringPair::~CStringPair()
       
   109     {
       
   110     delete iName;
       
   111     delete iValue;
       
   112     }
       
   113 
       
   114 CStringPair* CStringPair::NewLC(const TDesC& aName,const TDesC& aValue)
       
   115     {
       
   116     CStringPair* temp = new(ELeave) CStringPair();
       
   117     CleanupStack::PushL(temp);
       
   118     temp->ConstructL(aName, aValue);
       
   119     return temp;
       
   120     }
       
   121 
       
   122 void CStringPair::ConstructL(const TDesC& aName,const TDesC& aValue)
       
   123     {
       
   124     iName = aName.AllocL();
       
   125     iValue = aValue.AllocL();
       
   126     }
       
   127 
       
   128 // C++ default constructor cannot contain any code that might leave
       
   129 CMsgMailViewerHtmlConv::CMsgMailViewerHtmlConv()
       
   130     : CActive( CActive::EPriorityLow ),
       
   131     iPos(0), 
       
   132     iReturnValue(KErrNone),
       
   133     iErrorPos(KMaxTInt),
       
   134     iConversionState(EDeleteHeader),
       
   135     iCharacterSetId(0),
       
   136     iStatusFlags(EOwnsFileNameArray)
       
   137     {
       
   138     CActiveScheduler::Add( this );
       
   139     }
       
   140 
       
   141 // Symbian OS default constructor can leave.
       
   142 void CMsgMailViewerHtmlConv::ConstructL(RFile& aHandle,
       
   143     TUint aCharSet)
       
   144     {
       
   145     iFileIdArray = new(ELeave) RPointerArray<HBufC>;
       
   146 	LOG1("CMsgMailViewerHtmlConv::ConstructL:%08x", aCharSet);
       
   147     iCharacterSetId = aCharSet;
       
   148 
       
   149     // read HTML content
       
   150     TInt size;
       
   151     User::LeaveIfError(aHandle.Size(size));
       
   152     HBufC8 *buf = HBufC8::NewLC(size);
       
   153     TPtr8 ptr(buf->Des());
       
   154     User::LeaveIfError(aHandle.Read(ptr));
       
   155 
       
   156     iOrigText = HBufC::NewL(buf->Length());
       
   157     TPtr ptr16(iOrigText->Des());
       
   158     ptr16.Copy(ptr);
       
   159     CleanupStack::PopAndDestroy(); // buf
       
   160     }
       
   161 
       
   162 
       
   163 // Two-phased constructor.
       
   164 CMsgMailViewerHtmlConv* CMsgMailViewerHtmlConv::NewLC(
       
   165 	RFile& aHandle, TUint aCharset, TBool aForced)
       
   166 	{
       
   167     CMsgMailViewerHtmlConv* self = new (ELeave) CMsgMailViewerHtmlConv();
       
   168 
       
   169     CleanupStack::PushL( self );
       
   170     if (aForced)
       
   171     	{
       
   172     	self->iStatusFlags |= EForceUseCharacterSet;
       
   173     	}
       
   174     self->ConstructL(aHandle, aCharset);
       
   175 
       
   176     return self;
       
   177 	}
       
   178 
       
   179 CMsgMailViewerHtmlConv* CMsgMailViewerHtmlConv::NewL(
       
   180     RFile& aHandle, TUint aCharset, TBool aForced)
       
   181     {
       
   182     CMsgMailViewerHtmlConv* self = 
       
   183         CMsgMailViewerHtmlConv::NewLC( aHandle, aCharset, aForced );
       
   184     CleanupStack::Pop( self );
       
   185     return self;    
       
   186     }
       
   187     
       
   188 // Destructor
       
   189 CMsgMailViewerHtmlConv::~CMsgMailViewerHtmlConv()
       
   190     {
       
   191     Cancel();
       
   192     delete iOrigText;
       
   193     delete iCharSetName;
       
   194     if (iMacros)
       
   195         {
       
   196         iMacros->ResetAndDestroy();
       
   197         }
       
   198     delete iMacros;
       
   199 
       
   200     if (iFileIdArray && ( iStatusFlags & EOwnsFileNameArray ) )
       
   201         {
       
   202         iFileIdArray->ResetAndDestroy();
       
   203         delete iFileIdArray;
       
   204         }
       
   205     delete iCharConv;
       
   206     }
       
   207 
       
   208 TInt CMsgMailViewerHtmlConv::Convert()
       
   209     {
       
   210     TRAP( iReturnValue, DoConversionL() );
       
   211     LOG1( "CMsgMailViewerHtmlConv::Convert return:%d", iReturnValue );
       
   212     return iReturnValue;
       
   213     }
       
   214 
       
   215 void CMsgMailViewerHtmlConv::Convert( TRequestStatus* aStatus )
       
   216     {
       
   217     LOG( "CMsgMailViewerHtmlConv::Convert (Async)");
       
   218     ASSERT( aStatus );
       
   219     Cancel();
       
   220     iReqStatus = aStatus;
       
   221     *aStatus = KRequestPending;
       
   222     ContinueAsyncConvert();
       
   223     }
       
   224 
       
   225 void CMsgMailViewerHtmlConv::DoConvertL()
       
   226     {
       
   227     if ( !iCharConv )
       
   228         {
       
   229         //missing charconv is considered to be a
       
   230         //corrupt message situtation, the html part
       
   231         //will be available in attachment view
       
   232         ShowErrorL();
       
   233         User::Leave( KErrCorrupt );
       
   234         }
       
   235 
       
   236     // Conversion "cursor" location, initially start of descriptor
       
   237     TInt toConvertStart( 0 );
       
   238     // Length of text to still convert
       
   239     TInt toConvertLength( iOrigText->Length() );
       
   240     // Conversion is done in blocks to control heap consumption,
       
   241     // size of a block is always <= KMaxConvBlockLength
       
   242     TInt blockLength( Min( toConvertLength, KMaxConvBlockLength ) );
       
   243     // Initially no block is partially converted -> remainder == 0
       
   244     TInt blockRemainder( 0 );
       
   245     // Converter's state variable, mustn't be tampered with during conversion
       
   246     TInt state( CCnvCharacterSetConverter::KStateDefault );
       
   247 
       
   248     // 8bit source buffer and 16bit target buffer for a block. We use
       
   249     // first block's size as buffer size, since the following blocks
       
   250     // are either of same size or smaller
       
   251     HBufC8* buf8 = HBufC8::NewLC( blockLength );
       
   252     HBufC* buf = HBufC::NewLC( blockLength );
       
   253 
       
   254     // Pointers to the source and target texts.
       
   255     TPtr origPtr( iOrigText->Des() );
       
   256     TPtr8 srcPtr( buf8->Des() );
       
   257     TPtr dstPtr( buf->Des() );
       
   258 
       
   259     while ( toConvertLength > 0 ) // conversion loop
       
   260         {
       
   261         // Create a block for this conversion round
       
   262         blockLength = Min( toConvertLength, KMaxConvBlockLength );
       
   263         srcPtr.Copy( iOrigText->Mid( toConvertStart, blockLength ) );
       
   264 
       
   265         // Convert a block, return number of characters that weren't converted
       
   266         blockRemainder = iCharConv->ConvertToUnicode( dstPtr, srcPtr, state );
       
   267         // Handle possible errors
       
   268         if ( blockRemainder < 0 )
       
   269             {
       
   270             iPos = toConvertStart;
       
   271             ShowErrorL();
       
   272             break;
       
   273             }
       
   274 
       
   275         // Store the contents of the output buffer
       
   276         // Here unicodeLength may be less than convertedLength, e.g. in
       
   277         // case of "&auml;" ->"ä", and that must be taken into account
       
   278         TInt convertedLength = srcPtr.Length() - blockRemainder;
       
   279         TInt unicodeLength = dstPtr.Length();
       
   280         // Replace convertedLength characters with unicodeLength characters
       
   281         origPtr.Replace( toConvertStart, convertedLength, dstPtr );
       
   282         // Update progress status, move starting point to next
       
   283         // unconverted character
       
   284         toConvertStart += unicodeLength;
       
   285         toConvertLength = iOrigText->Length() - toConvertStart;
       
   286         }
       
   287 
       
   288     CleanupStack::PopAndDestroy( 2 ); // buf8, buf
       
   289     }
       
   290 
       
   291 void CMsgMailViewerHtmlConv::DoConversionL()
       
   292     {
       
   293     switch (iConversionState)
       
   294         {
       
   295         case EDeleteHeader:
       
   296             {
       
   297             LOG("EDeleteHeader");
       
   298 
       
   299             // Try to find charset name if not set already
       
   300             if ( !(iStatusFlags & EForceUseCharacterSet) )
       
   301             	{
       
   302 	            TInt charSetPos(iOrigText->FindF(KCharSet()));
       
   303 	            if ( charSetPos != KErrNotFound)
       
   304 	                {
       
   305                 	// Begin of charset name
       
   306                 	charSetPos += KCharSet().Length();
       
   307 	                TInt maxIdLength(charSetPos+20); // Give up if id end not found at this point.
       
   308 	                for (TInt i = charSetPos; i<maxIdLength; ++i)
       
   309 	                    {
       
   310 	                    if((*iOrigText)[i] == '"')
       
   311 	                        {
       
   312 	                        iCharSetName = iOrigText->Mid(
       
   313 	                            charSetPos, i-charSetPos).AllocL();
       
   314 	                        break;
       
   315 	                        }
       
   316 	                    }
       
   317 	                }
       
   318             	}
       
   319 
       
   320             // 5 for <body
       
   321             TInt bodyPos(iOrigText->FindF(KBodyTag().Left(5)));
       
   322             if ( bodyPos != KErrNotFound)
       
   323                 {
       
   324                 // Delete from start to body tag
       
   325                 Delete(bodyPos);
       
   326                 // Delete body tag
       
   327                 RemoveTagL();
       
   328                 }
       
   329             else // no body tag, invalid HTML, search for <html> then
       
   330                 {
       
   331                 bodyPos = iOrigText->FindF(KStartHtml().Left(5));
       
   332                 if (bodyPos != KErrNotFound)
       
   333                     {
       
   334                     Delete(bodyPos);
       
   335                     RemoveTagL();
       
   336                     }
       
   337                 }
       
   338 
       
   339             iConversionState++;
       
   340             break;
       
   341             }
       
   342 
       
   343         case EInitCharConverter:
       
   344             {
       
   345             RFs fs = CEikonEnv::Static()->FsSession();
       
   346             iCharConv = CCnvCharacterSetConverter::NewL();
       
   347             // Try to find character set id from the HTML header
       
   348             if ( iCharSetName && iCharSetName->Length() )
       
   349                 {
       
   350                 HBufC8* buf8 = HBufC8::NewLC( iCharSetName->Length() );
       
   351                     buf8->Des().Copy( *iCharSetName );
       
   352 
       
   353                 iCharacterSetId = iCharConv->
       
   354                     ConvertStandardNameOfCharacterSetToIdentifierL(
       
   355                     *buf8, fs);
       
   356 
       
   357                 CleanupStack::PopAndDestroy(); // buf8
       
   358                 }
       
   359 
       
   360             if (iCharacterSetId > 0)
       
   361                 {
       
   362                 LOG1("CMsgMailViewerHtmlConv::EInitCharConverter:%08x",
       
   363                 	iCharacterSetId);
       
   364                 const TInt ret(iCharConv->PrepareToConvertToOrFromL(
       
   365                     iCharacterSetId,
       
   366                     fs));
       
   367 
       
   368                 if (ret == CCnvCharacterSetConverter::ENotAvailable)
       
   369                     {
       
   370 					LOG( "CMsgMailViewerHtmlConv::EInitCharConverter: ENotAvailable" );
       
   371                     iCharacterSetId = 0;
       
   372                     delete iCharConv;
       
   373                     iCharConv = NULL;
       
   374                     }
       
   375 
       
   376                 }
       
   377             else
       
   378                 {
       
   379                 //regular method of getting charset id failed.
       
   380                 //Try autodetect instead.
       
   381 
       
   382                 TInt confidence=0;
       
   383                 const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>* availableSets =
       
   384                 CCnvCharacterSetConverter::CreateArrayOfCharacterSetsAvailableLC( fs );
       
   385 
       
   386                 HBufC8* sample = HBufC8::NewLC( KSampleBufferSize );
       
   387 	            TPtr8 sampleDes = sample->Des();
       
   388 
       
   389 	            sampleDes.Copy(iOrigText->Left( iOrigText->Length() > KMaxAmountOfSamples ?
       
   390 	                                                KMaxAmountOfSamples : iOrigText->Length()));
       
   391 
       
   392                 iCharConv->AutoDetectCharSetL(confidence, iCharacterSetId, *availableSets, sampleDes);
       
   393 
       
   394                 if(confidence > KMimimumConfidenceLevel)
       
   395                     {
       
   396                     const TInt ret(iCharConv->PrepareToConvertToOrFromL(
       
   397                     iCharacterSetId,
       
   398                     fs));
       
   399                     }
       
   400                 else
       
   401                     {
       
   402                     //autodetect not reliable enough.
       
   403                     delete iCharConv;
       
   404                     iCharConv = NULL;
       
   405                     }
       
   406 
       
   407                 CleanupStack::PopAndDestroy(2); // availableSets, sample
       
   408                 }
       
   409 
       
   410             iConversionState++;
       
   411             break;
       
   412             }
       
   413 
       
   414         case EConvert:
       
   415             {
       
   416             LOG("EConvert");
       
   417             DoConvertL();
       
   418 
       
   419             // reset position to beginning before step to next state.
       
   420             iPos = 0;
       
   421 			iConversionState++;
       
   422             break;
       
   423             }
       
   424 
       
   425         case ERemoveTags:
       
   426             {
       
   427             LOG("ERemoveTags");
       
   428             /**
       
   429             * This operation is done inside the CAknWaitnoteWrapper StepL
       
   430             * (CActive::RunL) and thus it should not take too much time.
       
   431             * We use maxloops to break out from time consuming operation,
       
   432             * so that wait note can be updated.
       
   433             */
       
   434             TInt maxloops(10000);
       
   435             while (iReturnValue == KErrNone && iPos < iOrigText->Length())
       
   436                 {
       
   437                 if (maxloops < 0)
       
   438                     {
       
   439                     LOG("maxloops");
       
   440                     // return to update wait note.
       
   441                     return;
       
   442                     }
       
   443                 TPtrC character = iOrigText->Mid(iPos, 1);
       
   444                 if (character == KAmpersand)
       
   445                     {
       
   446                     // Replace
       
   447                     ReplaceMacroL();
       
   448                     // takes more time, so decrease by ten
       
   449                     maxloops -= 10;
       
   450                     }
       
   451                 else if (character == KLessThan)
       
   452                     {
       
   453                     // Remove possible tag
       
   454                     HandleTagL(0);
       
   455                     // takes more time, so decrease by ten
       
   456                     maxloops -= 10;
       
   457                     }
       
   458                 // replace linefeeds with one space
       
   459                 else if (character[0] == 0x0A || character[0] == 0x0D)
       
   460                 	{
       
   461                 	// +1 peek next character
       
   462                 	if (iPos+1 < iOrigText->Length() &&
       
   463                 		(iOrigText->Mid(iPos+1, 1)[0] == 0x0A ||
       
   464                 		iOrigText->Mid(iPos+1, 1)[0] == 0x0D))
       
   465                 		{
       
   466                 		Delete(1); // delete character
       
   467                    		}
       
   468                 	else
       
   469                 		{
       
   470                 		// insert space
       
   471                 		TPtr ptr(iOrigText->Des());
       
   472                 		ptr.Replace(iPos, 1, KSpace);
       
   473                 	    // move to next char
       
   474                         iPos++;
       
   475                 		}
       
   476                 	}
       
   477                 else
       
   478                     {
       
   479                     // +1 peek next char
       
   480                     if (character == KSpace &&
       
   481                         iPos+1 < iOrigText->Length() &&
       
   482                         iOrigText->Mid(iPos+1, 1) == KSpace)
       
   483                         {
       
   484                         // delete multible spaces
       
   485                         Delete(1);
       
   486                         }
       
   487                     else
       
   488                         {
       
   489                         // move to next char
       
   490                         iPos++;
       
   491                         }
       
   492                     maxloops--;
       
   493                     }
       
   494                 }
       
   495             iConversionState++;
       
   496             // conversion ended
       
   497             if (iReturnValue == KErrNone)
       
   498                 {
       
   499                 iReturnValue = KErrEof;
       
   500                 }
       
   501             break;
       
   502             }
       
   503 
       
   504         default:
       
   505             LOG("default");
       
   506             // conversion ended
       
   507             iReturnValue = KErrEof;
       
   508             break;
       
   509         }
       
   510     }
       
   511 
       
   512 
       
   513 TInt CMsgMailViewerHtmlConv::HandleListL(const TInt aIndent,
       
   514                                          const TBool aNumbered)
       
   515     {
       
   516     TInt item(1);
       
   517     TInt len(RemoveTagL()); // remove <ol>
       
   518     TInt remaining(len - iPos);
       
   519     // length of the buf is 4, so we can find and distinguish <li> and </ol
       
   520     TBuf<4> buf(iOrigText->Mid(iPos, Min(4, remaining)));
       
   521     buf.LowerCase();
       
   522     const TInt currPos(iPos);
       
   523 
       
   524     while (iPos < len && buf != (aNumbered ? KOrderedListEnd().Left(4) :
       
   525         KUnnumberedListEnd().Left(4)))
       
   526         {
       
   527         if (buf == KListItem)
       
   528             {
       
   529             RemoveTagL();
       
   530 
       
   531             HBufC *fill = HBufC::NewLC(aIndent + KMaxOrdinalLength);
       
   532             TPtr fillPtr = fill->Des();
       
   533 
       
   534             fillPtr.AppendFill(' ', aIndent);
       
   535             if (aNumbered)
       
   536                 {
       
   537                 fillPtr.AppendNum(item);
       
   538                 fillPtr.Append(KNumberedItemSeparator);
       
   539                 }
       
   540             else
       
   541                 {
       
   542                 fillPtr.Append(KListItemSymbol);
       
   543                 }
       
   544 
       
   545             InsertTextL(fillPtr);
       
   546             CleanupStack::PopAndDestroy(); // fill
       
   547 
       
   548             InsertLinefeedL();
       
   549 
       
   550             len = iOrigText->Length();
       
   551             iPos += aIndent + (aNumbered ? 3 : 2); // iPos is always moved at least by 1
       
   552             item++;
       
   553             }
       
   554         else if ( buf.Left(1)[0] == 0x0A || buf.Left(1)[0] == 0x0D )
       
   555             {
       
   556             Delete(1); // delete linefeeds
       
   557             }
       
   558         else if (buf.Left(1) == KLessThan)
       
   559             {
       
   560             len = HandleTagL(aIndent + 2);
       
   561             }
       
   562         else if (buf.Left(1) == KAmpersand)
       
   563             {
       
   564             len = ReplaceMacroL();
       
   565             }
       
   566         else
       
   567             {
       
   568             iPos++;
       
   569             }
       
   570         remaining = len - iPos;
       
   571         buf = iOrigText->Mid(iPos, Min(4, remaining));
       
   572         buf.LowerCase();
       
   573         }
       
   574 
       
   575     if ( iPos != len)
       
   576         {
       
   577         InsertLinefeedL();
       
   578         len = RemoveTagL(); // delete </ol>
       
   579         }
       
   580     else
       
   581         {
       
   582         iPos = currPos;
       
   583         ShowErrorL();
       
   584         len = iOrigText->Length();
       
   585         }
       
   586 
       
   587     return len;
       
   588     }
       
   589 
       
   590 TInt CMsgMailViewerHtmlConv::HandleDListL(TInt aIndent)
       
   591     {
       
   592     TInt len(Delete(4)); // remove <dl>
       
   593     TInt remaining(len - iPos);
       
   594     // length of the buf is 4, so we can find and distinguish <li> and </dl
       
   595     TBuf<4> buf(iOrigText->Mid(iPos, Min(4, remaining)));
       
   596     buf.LowerCase();
       
   597     const TInt currPos(iPos);
       
   598 
       
   599     while (iPos < len && buf != KDefinitionListEnd().Left(4))
       
   600         {
       
   601         if (buf == KDefinitionListTerm)
       
   602             {
       
   603             len = RemoveTagL();
       
   604             HBufC *fill = HBufC::NewLC(aIndent);
       
   605             TPtr fillPtr = fill->Des();
       
   606 
       
   607             fillPtr.AppendFill(' ', aIndent);
       
   608             InsertTextL(fillPtr);
       
   609             CleanupStack::PopAndDestroy(); // fill
       
   610 
       
   611             InsertLinefeedL();
       
   612             iPos += aIndent; // skip spaces
       
   613             len = iOrigText->Length();
       
   614             }
       
   615         else if (buf == KDefinitionListDef)
       
   616             {
       
   617             len = RemoveTagL();
       
   618             InsertTextL(KSpace);
       
   619             }
       
   620         else if (buf.Left(1) == KLessThan)
       
   621             {
       
   622             len = HandleTagL(aIndent + 2);
       
   623             }
       
   624         else if (buf.Left(1) == KAmpersand)
       
   625             {
       
   626             len = ReplaceMacroL();
       
   627             }
       
   628         else
       
   629             {
       
   630             iPos++;
       
   631             }
       
   632         remaining = len - iPos;
       
   633         buf = iOrigText->Mid(iPos, Min(4, remaining));
       
   634         buf.LowerCase();
       
   635         }
       
   636 
       
   637     if ( iPos != len)
       
   638         {
       
   639         len = RemoveTagL(); // delete </dl>
       
   640         }
       
   641     else // </dl> missing
       
   642         {
       
   643         iPos = currPos;
       
   644         ShowErrorL();
       
   645         len = iOrigText->Length();
       
   646         }
       
   647 
       
   648     return len;
       
   649     }
       
   650 
       
   651 TInt CMsgMailViewerHtmlConv::HandleTagL(TInt aIndent)
       
   652     {
       
   653     TInt length(iOrigText->Length());
       
   654     const TInt remaining(length - iPos);
       
   655 
       
   656     if (!remaining)
       
   657         {
       
   658         // Nothing to remove
       
   659         iReturnValue = KErrEof;
       
   660         return iOrigText->Length();
       
   661         }
       
   662 
       
   663     // Longest HTML tag is 6 characters long, thus six char buffer
       
   664     TBuf<6> buf(iOrigText->Mid(iPos+1, Min(6, remaining - 1)));
       
   665     buf.LowerCase();
       
   666     buf.TrimAll();
       
   667 
       
   668     if (buf.Left(2) == KLineBreak().Mid(1,2) ||
       
   669         buf.Left(1) == KNewParagraph().Mid(1,1) ||
       
   670         buf.Left(2) == KHorizontalRuler().Mid(1,2) ||
       
   671 		buf.Left(3) == KBlock().Mid(1,3))
       
   672         {
       
   673         length = RemoveTagL(); // Delete <br>, <p>, <hr> or <div>
       
   674         InsertLinefeedL();
       
   675         return length;
       
   676         }
       
   677 	else if (buf.Left(4) == KBlockEnd().Mid(1,4))
       
   678 		{
       
   679         length = RemoveTagL(); // Delete </div>
       
   680 	    if ( (length - iPos) > 6 ) // still data to peek to?
       
   681 			{
       
   682 			// peek the next tag and if it's <div>, don't add newline
       
   683 			TPtrC peekPtr(iOrigText->Mid(iPos));
       
   684 			TInt found = peekPtr.Find(KLessThan);
       
   685 			// found + 3 there needs to be at least 3 characters for "div" check
       
   686 			if ( found != KErrNotFound && iPos+found+3 < length )
       
   687 				{
       
   688 				// +1 step over "<"
       
   689 				TBuf<6> peekbuf( iOrigText->Mid(
       
   690 				    iPos+found+1, Min(6, length - iPos - 1)) );
       
   691 			    peekbuf.LowerCase();
       
   692 				peekbuf.TrimAll();
       
   693 				// compare "div" part
       
   694 				if ( peekbuf.Length() > 3 &&
       
   695 				    peekbuf.Left(3) != KBlock().Mid(1,3) )
       
   696 				    {
       
   697 					// next tag is not <div>, add newline
       
   698 					InsertLinefeedL();
       
   699 				    }
       
   700 				}
       
   701 			}
       
   702         return length;
       
   703 		}
       
   704     else if (buf.Left(2) == KUnnumberedList().Mid(1,2))
       
   705         {
       
   706         return HandleUListL(aIndent);
       
   707         }
       
   708     else if (buf.Left(2) == KOrderedList().Mid(1,2))
       
   709         {
       
   710 		// check for Outlook's olid tag, not to be confused
       
   711 		// with ordered list tag <ol>
       
   712 		if( buf.Left( 4 ).Match( KOlId ) == 0 )
       
   713 			{
       
   714 			return RemoveTagL(); // Delete <olid ....>
       
   715 			}
       
   716 		return HandleOListL(aIndent);
       
   717         }
       
   718     else if (buf.Left(2) == KDefinitionList().Mid(1,2))
       
   719         {
       
   720         return HandleDListL(aIndent);
       
   721         }
       
   722     else if (buf.Left(4) == KHeadStart().Mid(1,4))
       
   723         {
       
   724         const TInt currPos(iPos);
       
   725         length = RemoveTagL(); // Remove <head ...>
       
   726         TPtrC tagPos(iOrigText->Mid(iPos));
       
   727         TBool found(EFalse);
       
   728         TInt endTag(tagPos.Find(KLessThan));
       
   729         while (!found && endTag != KErrNotFound
       
   730             && iOrigText->Length() >= endTag+5 )
       
   731             {
       
   732             buf = iOrigText->Mid(endTag+1, 4);
       
   733             buf.TrimAll();
       
   734             buf.LowerCase();
       
   735             if (buf == KHeadEnd().Mid(1, 4))
       
   736                 {
       
   737                 found = ETrue;
       
   738                 Delete(endTag - iPos);
       
   739                 return RemoveTagL();
       
   740                 }
       
   741             else
       
   742                 {
       
   743                 TPtrC tmpPos(iOrigText->Mid(endTag+1));
       
   744                 const TInt p(tmpPos.Find(KLessThan));
       
   745                 endTag = p == KErrNotFound ? KErrNotFound : endTag + p + 1;
       
   746                 }
       
   747             }
       
   748         if (endTag == KErrNotFound)
       
   749             {
       
   750             iPos = currPos;
       
   751             ShowErrorL();
       
   752             return iOrigText->Length();
       
   753             }
       
   754         }
       
   755     else if (buf.Left(1) == KHeading().Mid(1,1))
       
   756         {
       
   757         CDesCArrayFlat *headArray = new(ELeave) CDesCArrayFlat(6);
       
   758         CleanupStack::PushL(headArray);
       
   759         for (TInt i = 1; i < 7; i++)
       
   760             {
       
   761             TBuf<3> tag = KHeading().Mid(1);
       
   762             tag.AppendNum(i);
       
   763             tag.Append(KGreaterThan);
       
   764             headArray->AppendL(tag);
       
   765             }
       
   766         TInt foundInPos;
       
   767         if (headArray->Find(buf.Left(3), foundInPos) == 0)
       
   768             {
       
   769             length = RemoveTagL(); // remove <h?>
       
   770             InsertLinefeedL();
       
   771             CleanupStack::PopAndDestroy(); // headArray
       
   772             return length;
       
   773             }
       
   774         CleanupStack::PopAndDestroy(); // headArray
       
   775         }
       
   776     else if (buf.Left(2) == KHeadingEnd().Mid(1,2) &&
       
   777              buf != KEndHtml().Mid(1, 6))
       
   778         {
       
   779         CDesCArrayFlat *headArray = new(ELeave) CDesCArrayFlat(6);
       
   780         CleanupStack::PushL(headArray);
       
   781         for (TInt i = 1; i < 7; i++)
       
   782             {
       
   783             TBuf<4> tag = KHeadingEnd().Mid(1);
       
   784             tag.AppendNum(i);
       
   785             tag.Append(KGreaterThan);
       
   786             headArray->AppendL(tag);
       
   787             }
       
   788         TInt foundInPos;
       
   789         if (headArray->Find(buf.Left(4), foundInPos) == 0)
       
   790             {
       
   791             length = RemoveTagL(); // remove </h?>
       
   792             InsertLinefeedL();
       
   793             CleanupStack::PopAndDestroy(); // headArray
       
   794             return length;
       
   795             }
       
   796         CleanupStack::PopAndDestroy(); // headArray
       
   797         }
       
   798     else if (buf == KScript().Mid(1))
       
   799         {
       
   800         const TInt currPos(iPos);
       
   801         length = RemoveTagL(); // Remove <script ...>
       
   802         TPtrC tagPos(iOrigText->Mid(iPos));
       
   803         TBool found(EFalse);
       
   804         TInt endTag(tagPos.Find(KLessThan));
       
   805         while (!found && endTag != KErrNotFound
       
   806             && iOrigText->Length() >= endTag+7 )
       
   807             {
       
   808             buf = iOrigText->Mid(endTag+1, 6);
       
   809             buf.TrimAll();
       
   810             buf.LowerCase();
       
   811             if (buf == KScriptEnd().Mid(1, 6))
       
   812                 {
       
   813                 found = ETrue;
       
   814                 Delete(endTag - iPos);
       
   815                 return RemoveTagL();
       
   816                 }
       
   817             else
       
   818                 {
       
   819                 TPtrC tmpPos(iOrigText->Mid(endTag+1));
       
   820                 const TInt p(tmpPos.Find(KLessThan));
       
   821                 endTag = p == KErrNotFound ? KErrNotFound : endTag + p + 1;
       
   822                 }
       
   823             }
       
   824         if (endTag == KErrNotFound)
       
   825             {
       
   826             iPos = currPos;
       
   827             ShowErrorL();
       
   828             return iOrigText->Length();
       
   829             }
       
   830         }
       
   831     else if (buf.Left(3) == KImage().Mid(1,3))
       
   832         {
       
   833         TBool inQuoted(EFalse); // in quoted string
       
   834         TBool inAltPart(EFalse);
       
   835         TBool inAltQuotedPart(EFalse);
       
   836         TBool inSrcPart(EFalse);
       
   837         const TInt KInitialSize(50);
       
   838         HBufC* inlineImage = HBufC::NewLC(KInitialSize);
       
   839 
       
   840         while ((*iOrigText)[iPos] != '>' && iPos < iOrigText->Length())
       
   841             {
       
   842             TChar ch((*iOrigText)[iPos]);
       
   843             const TInt remaining(iOrigText->Length() - iPos);
       
   844             HBufC* lowerCaseCopy = iOrigText->Mid(iPos,
       
   845                 remaining > 3 ? 3 : remaining).AllocLC();
       
   846             TPtr lowerCasePtr(lowerCaseCopy->Des());
       
   847             lowerCasePtr.LowerCase();
       
   848 
       
   849             if (inQuoted && ch != '"')
       
   850                 {
       
   851                 if ( inlineImage->Length() == inlineImage->Des().MaxLength() )
       
   852                     {
       
   853                     HBufC* temp = inlineImage->ReAllocL(
       
   854                         inlineImage->Length() + KInitialSize);
       
   855                     CleanupStack::Pop(2); //lowerCaseCopy, inlineImage
       
   856                     CleanupStack::PushL( inlineImage = temp );
       
   857                     CleanupStack::PushL( lowerCaseCopy );
       
   858                     }
       
   859 
       
   860                 // Leave alt quoted to the body
       
   861                 if ( inAltQuotedPart )
       
   862                     {
       
   863                     iPos++;
       
   864                     }
       
   865                 else if ( inSrcPart )
       
   866                     {
       
   867                     inlineImage->Des().Append(ch);
       
   868                     Delete(1);
       
   869                     }
       
   870                 else
       
   871                     {
       
   872                     // other queted parts are deleted
       
   873                     Delete(1);
       
   874                     }
       
   875 
       
   876                 }
       
   877             else if (ch == '"')
       
   878                 {
       
   879                 Delete(1);
       
   880                 inQuoted = !inQuoted;
       
   881                 if (inAltPart)
       
   882                     {
       
   883                     inAltQuotedPart = ETrue;
       
   884                     inAltPart = EFalse;
       
   885                     }
       
   886                 else if (inAltQuotedPart)
       
   887                     {
       
   888                     inAltQuotedPart = EFalse;
       
   889                     }
       
   890                 else if (inSrcPart && !inQuoted)
       
   891                     {
       
   892                     // end of Src Quoted
       
   893                     inSrcPart = EFalse;
       
   894                     }
       
   895                 }
       
   896             else if (!lowerCaseCopy->Compare(KImageAlt))
       
   897                 {
       
   898                 Delete(KImageAlt().Length()); // delete alt
       
   899                 inAltPart = ETrue;
       
   900                 inSrcPart = EFalse;
       
   901                 }
       
   902             else if (!lowerCaseCopy->Compare(KImageSrc))
       
   903                 {
       
   904                 Delete(KImageSrc().Length()); // delete src
       
   905                 inAltPart = EFalse;
       
   906                 inSrcPart = ETrue;
       
   907 				}
       
   908             else
       
   909                 {
       
   910                 Delete(1);
       
   911                 }
       
   912             CleanupStack::PopAndDestroy(); // lowerCaseCopy
       
   913             }
       
   914         if ( inlineImage->Length() )
       
   915             {
       
   916             User::LeaveIfError( iFileIdArray->Append(inlineImage) );
       
   917             CleanupStack::Pop(); // inlineImage
       
   918             }
       
   919         else
       
   920             {
       
   921             CleanupStack::PopAndDestroy(); // inlineImage
       
   922             }
       
   923 
       
   924         if (iPos < iOrigText->Length())
       
   925             {
       
   926             Delete(1); // delete >
       
   927             }
       
   928         return iOrigText->Length();
       
   929         }
       
   930 
       
   931     return RemoveTagL();
       
   932     }
       
   933 
       
   934 TInt CMsgMailViewerHtmlConv::RemoveTagL()
       
   935     {
       
   936     const TInt currPos(iPos);
       
   937     TPtrC tagPos(iOrigText->Mid(iPos));
       
   938     const TInt tagLength(tagPos.Find(KGreaterThan));
       
   939     if (tagLength == KErrNotFound)
       
   940         {
       
   941         iReturnValue = KErrEof; //  ">" not found at the end
       
   942         iPos = 0;
       
   943         return 0;
       
   944         }
       
   945 
       
   946     if (tagPos[1] == '!') // start of the comment
       
   947         {
       
   948         const TInt commentLength( tagPos.Find( KCommentTagEnd ) );
       
   949         if (commentLength != KErrNotFound)
       
   950             return Delete(commentLength + 3); // 3 for -->
       
   951 
       
   952         return Delete(tagLength+1);// not comment delete whole tag;
       
   953         }
       
   954     // Check that > really belongs to this tag
       
   955     TPtrC tmpPos(iOrigText->Mid(iPos+1));
       
   956     const TInt firstTagStart(tmpPos.Find(KLessThan));
       
   957     if (tagLength != KErrNotFound &&
       
   958         ( firstTagStart == KErrNotFound ||
       
   959           tagLength <= firstTagStart ) )
       
   960         {
       
   961         return Delete(tagLength+1);
       
   962         }
       
   963     else
       
   964         {
       
   965         // check that found < really belongs to tag which end was found
       
   966         // and it's not only wrongly encoded
       
   967         if (firstTagStart < tagLength)
       
   968             {
       
   969             iPos++;
       
   970             return iOrigText->Length();
       
   971             }
       
   972         else
       
   973             {
       
   974             iPos = currPos;
       
   975             ShowErrorL();
       
   976             return iOrigText->Length();
       
   977             }
       
   978         }
       
   979     }
       
   980 
       
   981 RPointerArray<HBufC>* CMsgMailViewerHtmlConv::FileIdArray()
       
   982     {
       
   983     iStatusFlags &= ~EOwnsFileNameArray;
       
   984     return iFileIdArray;
       
   985     }
       
   986 
       
   987 TBool CMsgMailViewerHtmlConv::AutoParsedCharSetNameAndIdentifier() const
       
   988     {
       
   989     TBool autoParsed = EFalse;
       
   990     //
       
   991     if ( !(iStatusFlags & EForceUseCharacterSet) )
       
   992         {
       
   993         autoParsed = ( iCharSetName != NULL ) && ( iCharacterSetId != 0);
       
   994         }
       
   995     //
       
   996     return autoParsed;
       
   997     }
       
   998 
       
   999 TUint CMsgMailViewerHtmlConv::AutoParsedCharSetIdentifier() const
       
  1000     {
       
  1001     return iCharacterSetId;
       
  1002     }
       
  1003 
       
  1004 
       
  1005 
       
  1006 TInt CMsgMailViewerHtmlConv::Delete(const TInt aLen)
       
  1007     {
       
  1008     TPtr ptr(iOrigText->Des());
       
  1009     ptr.Delete(iPos, aLen);
       
  1010     return iOrigText->Length();
       
  1011     }
       
  1012 
       
  1013 // InsertTextL()
       
  1014 // It is checked that text to be inserted fits into a descriptor and
       
  1015 // a descriptor's size is doubled if not. In practice this should never
       
  1016 // happen because insertions are usually one character to the place where
       
  1017 // there used to be i.e. a 4-char macro.
       
  1018 //
       
  1019 TInt CMsgMailViewerHtmlConv::InsertTextL(const TDesC& aChars)
       
  1020     {
       
  1021     if (iOrigText->Des().MaxLength() < iOrigText->Length() + aChars.Length())
       
  1022         {
       
  1023         iOrigText = iOrigText->ReAllocL((
       
  1024             iOrigText->Length() + aChars.Length()) * 2);
       
  1025         }
       
  1026 
       
  1027     TPtr ptr(iOrigText->Des());
       
  1028     ptr.Insert(iPos, aChars);
       
  1029     return iOrigText->Length();
       
  1030     }
       
  1031 
       
  1032 TInt CMsgMailViewerHtmlConv::ReplaceMacroL()
       
  1033     {
       
  1034     if (!iMacros)
       
  1035         {
       
  1036         InitMacroArrayL();
       
  1037         }
       
  1038 
       
  1039     TInt len(iOrigText->Length());
       
  1040     const TPtrC macroPos(iOrigText->Mid(iPos+1));
       
  1041     const TInt macroLength(macroPos.Find(KSemicolon));
       
  1042     if (macroLength > 0)
       
  1043         {
       
  1044         const TPtrC macroPtr(iOrigText->Mid(iPos+1, macroLength));
       
  1045 
       
  1046         TInt pos;
       
  1047         // A dummy string pair for Find-method
       
  1048         TDesC tmpDesC(KSpace);
       
  1049         CStringPair* tmpPair = CStringPair::NewLC(macroPtr, tmpDesC);
       
  1050         if (iMacros->FindInOrder(tmpPair, pos,
       
  1051             TLinearOrder<CStringPair>(CStringPair::Compare)) != KErrNotFound)
       
  1052             {
       
  1053             // + 2 for '&' + ';'
       
  1054             Delete( ((*iMacros)[pos])->GetName()->Length() + 2);
       
  1055             len = InsertTextL( *((*iMacros)[pos]->GetValue()) );
       
  1056             iPos++;
       
  1057             CleanupStack::PopAndDestroy(); // tmpPair
       
  1058             return len;
       
  1059             }
       
  1060         CleanupStack::PopAndDestroy(); // tmpPair
       
  1061         if (macroPtr[0] == '#') // numeric form
       
  1062             {
       
  1063             const TPtrC ptr(iOrigText->Mid(iPos+2, macroLength - 1)); // skip #
       
  1064             TLex lex(ptr);
       
  1065             TUint value(0);
       
  1066             TInt err(KErrNone);
       
  1067             if (User::LowerCase(lex.Peek()) == 'x')
       
  1068                 { // character in hex
       
  1069                 lex.Inc(1); // skip x in &#x20AC
       
  1070                 err = lex.Val(value, EHex);
       
  1071                 }
       
  1072             else
       
  1073                 { // character in decimal
       
  1074                 err = lex.Val(value);
       
  1075                 }
       
  1076             if (err == KErrNone)
       
  1077                 {
       
  1078                 // Check how many chars we have actually consumed.
       
  1079                 TInt offset = lex.Offset(); 
       
  1080                 // NCR could be missing the semicolon.
       
  1081                 if( lex.Peek() == ';' )
       
  1082                     {
       
  1083                     offset++;
       
  1084                     }
       
  1085 
       
  1086                 // + 2 for # & ;
       
  1087                 Delete( offset + 2 );
       
  1088                 TBuf<1> buf;
       
  1089                 buf.Append(value);
       
  1090                 len = InsertTextL(buf);
       
  1091                 iPos++;
       
  1092                 return len;
       
  1093                 }
       
  1094             }
       
  1095         }
       
  1096 
       
  1097     // this isn't correctly encoded macro, but let's skip it instead of
       
  1098     // showing an error, so user can see as much of the message as possible
       
  1099     iPos++;
       
  1100     return iOrigText->Length();
       
  1101     }
       
  1102 
       
  1103 void CMsgMailViewerHtmlConv::InitMacroArrayL()
       
  1104     {
       
  1105     TResourceReader reader;
       
  1106     CEikonEnv::Static()->CreateResourceReaderLC(reader, R_MAIL_HTML_MACRO);
       
  1107     const TInt count(reader.ReadInt16());
       
  1108     iMacros = new(ELeave) RPointerArray<CStringPair>(count);
       
  1109     for (TInt i=0; i<count; i++)
       
  1110         {
       
  1111         HBufC* macro = reader.ReadHBufCL();
       
  1112         CleanupStack::PushL(macro);
       
  1113         HBufC* realText = reader.ReadHBufCL();
       
  1114         CleanupStack::PushL(realText);
       
  1115         CStringPair* tmp = CStringPair::NewLC(*macro, *realText);
       
  1116         // this should never fail, because we allocated enough memory in
       
  1117         // construction
       
  1118         User::LeaveIfError(iMacros->InsertInOrder(
       
  1119             tmp, TLinearOrder<CStringPair>(CStringPair::Compare)));
       
  1120         CleanupStack::Pop(); // tmp
       
  1121         CleanupStack::PopAndDestroy(2); // macro, realText
       
  1122         }
       
  1123     CleanupStack::PopAndDestroy(); // reader
       
  1124     iMacros->Compress();
       
  1125     }
       
  1126 
       
  1127 void CMsgMailViewerHtmlConv::ShowErrorL()
       
  1128     {
       
  1129     iErrorPos = iPos;
       
  1130     TInt deleteRest(iOrigText->Length() - iPos);
       
  1131     if (deleteRest > 0)
       
  1132         {
       
  1133         Delete(deleteRest);
       
  1134         }
       
  1135     InsertLinefeedL();
       
  1136     HBufC* text =
       
  1137         CEikonEnv::Static()->AllocReadResourceLC(R_ERROR_IN_HTML_TEXT);
       
  1138     InsertTextL(*text);
       
  1139     CleanupStack::PopAndDestroy(); // text
       
  1140     iReturnValue = KErrCorrupt;
       
  1141     iPos = iOrigText->Length();
       
  1142     }
       
  1143 
       
  1144 void CMsgMailViewerHtmlConv::InsertLinefeedL()
       
  1145     {
       
  1146     if (iPos > 0 && iPos < iOrigText->Length() )
       
  1147         {
       
  1148         TPtr ptr(iOrigText->Des());
       
  1149         ptr.Insert(iPos, KNewline);
       
  1150         iPos++;
       
  1151         }
       
  1152     }
       
  1153 
       
  1154 void CMsgMailViewerHtmlConv::RunL()
       
  1155     {
       
  1156     TInt err = Convert();
       
  1157     if( !err ) // KErrNone
       
  1158         {
       
  1159         // still data to convert. Continue work on next loop.
       
  1160         ContinueAsyncConvert();
       
  1161         }
       
  1162     else if( err == KErrEof )
       
  1163         {
       
  1164         // success
       
  1165         User::RequestComplete( iReqStatus, KErrNone );
       
  1166         }
       
  1167     else
       
  1168         {
       
  1169         // error occured
       
  1170         User::RequestComplete( iReqStatus, err );
       
  1171         }
       
  1172     }
       
  1173 
       
  1174 void CMsgMailViewerHtmlConv::DoCancel()
       
  1175     {
       
  1176     User::RequestComplete( iReqStatus, KErrCancel );
       
  1177     }
       
  1178 
       
  1179 void CMsgMailViewerHtmlConv::ContinueAsyncConvert()
       
  1180     {
       
  1181     iStatus = KRequestPending;
       
  1182     TRequestStatus* status = &iStatus;
       
  1183     User::RequestComplete( status, KErrNone );
       
  1184     SetActive();
       
  1185     }
       
  1186 
       
  1187 // ================= OTHER EXPORTED FUNCTIONS ==============
       
  1188 
       
  1189 
       
  1190 //  End of File