browserutilities/multipartparser/src/MultipartParser.cpp
changeset 0 dd21522fd290
child 25 0ed94ceaa377
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2 * Copyright (c) 2005 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 the License "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 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include <e32def.h>           // First to avoid NULL redefine warning (no #ifndef NULL).
       
    21 #include <e32std.h>
       
    22 #include <string.h>
       
    23 #include <imcvcodc.h>         // Imut.lib, where some decoders reside
       
    24 #include <EZDecompressor.h>   // Ezlib.lib, GZip decoders
       
    25 #include <Uri8.h>
       
    26 #include <Uri16.h>
       
    27 #include <UriCommon.h>
       
    28 
       
    29 #include "MultipartParser.h"
       
    30 #include "GZipBufMgr.h"
       
    31 #include "tinternetdate.h"
       
    32 
       
    33 
       
    34 // CONSTANTS
       
    35 const char Multipart_Mixed[] = {"multipart/mixed"};
       
    36 const char Multipart_Related[] = {"multipart/related"};
       
    37 const char Multipart_Boundary_Text[] = {"boundary="};
       
    38 const char Multipart_Content_Base_Text[] = {"Content-Base:"};
       
    39 const char Multipart_Content_Location_Text[] = {"Content-Location:"};
       
    40 const char Multipart_Content_Type_Text[] = {"Content-Type:"};
       
    41 const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
       
    42 // violate RFC2045; but upon customer request
       
    43 const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
       
    44 const char Multipart_Content_ID_Text[] = {"Content-ID:"};
       
    45 const char Multipart_Hypens_Text[] = {"--"};
       
    46 const char Multipart_CRLF_Text[] = {"\r\n"};
       
    47 const char Multipart_LF_Text[] = {"\n"};
       
    48 const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
       
    49 const char Multipart_DoubleLF_Text[] = {"\n\n"};
       
    50 const char Multipart_Charset_Text[] = {"charset="};
       
    51 const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
       
    52 const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
       
    53 const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
       
    54 const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
       
    55 const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
       
    56 const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
       
    57 const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
       
    58 const char Multipart_Content_Encoding_GZip[] = {"gzip"};
       
    59 const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
       
    60 
       
    61 // MACROS
       
    62 #define MULTIPART_CONTENT_BASE_LENGTH               13
       
    63 #define MULTIPART_CONTENT_LOCATION_LENGTH           17
       
    64 #define MULTIPART_CONTENT_TYPE_LENGTH               13
       
    65 #define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH  26
       
    66 // violates RFC2045; but upon vodafone request
       
    67 #define MULTIPART_CONTENT_ENCODING_LENGTH           17
       
    68 #define MULTIPART_CONTENT_LENGTH_LENGTH             15
       
    69 #define MULTIPART_LAST_MODIFIED_LENGTH              14
       
    70 #define MULTIPART_CONTENT_ID_LENGTH                 11
       
    71 
       
    72 #define MULTIPART_CONTENT_BASE               1
       
    73 #define MULTIPART_CONTENT_LOCATION           2
       
    74 #define MULTIPART_CONTENT_TRANSFER_ENCODING  3
       
    75 #define MULTIPART_CONTENT_TYPE               4
       
    76 #define MULTIPART_CONTENT_ID                 5
       
    77 
       
    78 #define MULTIPART_INTERNET_DATE_STRING_LENGTH       29
       
    79 
       
    80 #define SLASH_CHAR    '/'
       
    81 #define DOT_CHAR      '.'
       
    82 #define AT_CHAR       '@'
       
    83 #define COLON_CHAR    ':'
       
    84 // <scheme>://
       
    85 #define SCHEME_SEPARATOR_LENGTH               3
       
    86 
       
    87 
       
    88 // ============================= LOCAL FUNCTIONS ===============================
       
    89 
       
    90 
       
    91 // ============================ MEMBER FUNCTIONS ===============================
       
    92 
       
    93 
       
    94 // ------------------------------------------------------------------------- 
       
    95 // Parse and put each body part to the body part array
       
    96 // ------------------------------------------------------------------------- 
       
    97 EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody, 
       
    98                                        const TDesC8& aContentType,
       
    99                                        const TDesC8& aBoundary,
       
   100                                        const TDesC16& aUrl,
       
   101                                        RPointerArray <CBodyPart>& aBodyPartsArray,
       
   102                                        TInt aMaxToParse )
       
   103     {
       
   104     // check on required parameters
       
   105     __ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
       
   106                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   107     __ASSERT_ALWAYS( aContentType.Ptr() != NULL,
       
   108                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   109     __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
       
   110                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   111     __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
       
   112                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   113 
       
   114     const TUint8* multipartBuffer = aMultipartBody.Ptr();
       
   115     TUint32 multipartLen = aMultipartBody.Length();
       
   116     __ASSERT_ALWAYS( multipartLen != 0,
       
   117                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   118     TUint8* bodyPartBuffer = NULL;
       
   119     TUint32 bodyPartBufferLength = 0;
       
   120     TUint32 startPosition = 0;
       
   121     char* singleEolChar = NULL;
       
   122     char* doubleEolChar = NULL;
       
   123     CBodyPart* bodyPart = NULL;
       
   124 
       
   125     // currently only support mixed and related.
       
   126     char* contentType = (char*)aContentType.Ptr();
       
   127     if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 && 
       
   128         strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
       
   129         {
       
   130         User::Leave( KErrNotSupported );
       
   131         }
       
   132 
       
   133     // get singleEol and doubleEol
       
   134     MultipartParser::SetEolCharacters( multipartBuffer,
       
   135                                        multipartLen,
       
   136                                        aBoundary,
       
   137                                        &singleEolChar,
       
   138                                        &doubleEolChar );
       
   139 
       
   140     // get body parts one by one
       
   141     // null bodyPartBuffer indicates the end of the multipart body
       
   142     int counter = 0;
       
   143     do
       
   144         {
       
   145         // stop when we get required number of parse done
       
   146         if( aMaxToParse != -1 && counter >= aMaxToParse )
       
   147             {
       
   148             break;
       
   149             }
       
   150         // update counter
       
   151         counter++;
       
   152 
       
   153         // get the next body part
       
   154         bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition, 
       
   155                                                                        multipartBuffer,
       
   156                                                                        multipartLen,
       
   157                                                                        aBoundary,
       
   158                                                                        singleEolChar,
       
   159                                                                        &bodyPartBuffer );
       
   160         // break if we are at end
       
   161         if( bodyPartBuffer == NULL )
       
   162             {
       
   163             break;
       
   164             }
       
   165         // update start position
       
   166         startPosition += bodyPartBufferLength;
       
   167 
       
   168         // create new body part
       
   169         bodyPart = CBodyPart::NewL();
       
   170         // parse each body part buffer to fill in body part
       
   171         MultipartParser::ParseBodyPartL( bodyPartBuffer, 
       
   172                                          bodyPartBufferLength,
       
   173                                          singleEolChar,
       
   174                                          doubleEolChar,
       
   175                                          aUrl, 
       
   176                                          bodyPart );  
       
   177         // add the body part to the array
       
   178         aBodyPartsArray.Append( bodyPart );
       
   179         }
       
   180     while( bodyPartBuffer != NULL );
       
   181     }
       
   182 
       
   183 // ------------------------------------------------------------------------- 
       
   184 // Composes RFC1521 compliant multipart document with given bodyparts
       
   185 // Actual task of creating the document is delegated to specialized composer
       
   186 // for each of the subtypes
       
   187 // ------------------------------------------------------------------------- 
       
   188 EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
       
   189                                             const TDesC8& aBoundary,
       
   190                                             TMultipartSubtype aSubtype,
       
   191                                             TInt aHeaderMask )
       
   192     {
       
   193     // check on required parameters
       
   194     if ( !aBoundary.Ptr() || !aBoundary.Length() )
       
   195         {
       
   196         User::Leave( KErrArgument );
       
   197         }
       
   198     
       
   199     HBufC8* multipartDoc = NULL;
       
   200     switch(aSubtype)
       
   201         {
       
   202         case EMultipartSubtypeMixed:
       
   203             {
       
   204             multipartDoc = ComposeMixedL( aBodyPartsArray,
       
   205                                           aBoundary,
       
   206                                           aHeaderMask );
       
   207             }
       
   208         break;
       
   209         default:
       
   210             {
       
   211             User::Leave( KErrArgument );
       
   212             }
       
   213         }
       
   214     return multipartDoc;
       
   215     }
       
   216 
       
   217 
       
   218 // ------------------------------------------------------------------------- 
       
   219 // Returns with the next body part buffer from start position (offset)
       
   220 // ------------------------------------------------------------------------- 
       
   221 TUint32
       
   222 MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition, 
       
   223                                         const TUint8* aMultipartBody,
       
   224                                         TUint32 aMultipartLen,
       
   225                                         const TDesC8& aBoundary,
       
   226                                         char* aSingleEolChar,
       
   227                                         TUint8** aBodyPartBuffer )
       
   228     {
       
   229     // check on required parameters
       
   230     __ASSERT_ALWAYS( aMultipartBody != NULL,
       
   231                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   232     __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
       
   233                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   234     __ASSERT_ALWAYS( aSingleEolChar != NULL,
       
   235                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   236 
       
   237     long startOffset = -1;
       
   238     long endOffset = -1;
       
   239     TUint32 length = 0;
       
   240     TBool badContent = EFalse;
       
   241     TUint32 offset = 0;
       
   242     const char* boundaryStr = (const char*) aBoundary.Ptr();
       
   243     int boundaryLength = aBoundary.Length();
       
   244     int hypensLength = strlen( Multipart_Hypens_Text );
       
   245     int singleEolLength = strlen( aSingleEolChar );
       
   246     TUint32 i = aStartPosition;
       
   247 
       
   248     // from RFC 1341 7.2
       
   249     // Overall, the body of a multipart entity may be specified as follows:
       
   250     // multipart-body := preamble 1*encapsulation close-delimiter epilogue
       
   251     // encapsulation := delimiter CRLF body-part
       
   252     // delimiter := CRLF "--" boundary   ; taken from Content-Type field.
       
   253     // when content-type is multipart.
       
   254     // There must be no space between "--" and boundary.
       
   255     // close-delimiter := delimiter "--" ; Again, no  space  before
       
   256     // "--"
       
   257 
       
   258     // boundary = 12xy
       
   259     // body: here comes some text that we ignore
       
   260     //       --12xy
       
   261     //       first body
       
   262     //       --12xy
       
   263     //       second body
       
   264     //       --12xy--
       
   265     //       closing boundary. we ignore this text here
       
   266     while( i < (aMultipartLen - hypensLength + 1 ) )
       
   267         {
       
   268         // get the first two hypens
       
   269         // using char comparison to compare "--"
       
   270         // hopefully this is faster
       
   271         if( (char)aMultipartBody[ i ] == '-' && 
       
   272             (char)aMultipartBody[ i+1 ] == '-' )
       
   273           {
       
   274           char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
       
   275           // check if the body is long enough first and then check if boundary matches
       
   276           if( aMultipartLen >= i + hypensLength + boundaryLength )
       
   277             {
       
   278             if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
       
   279               {
       
   280               // we've got the boundary
       
   281               offset = i + hypensLength + boundaryLength;
       
   282               // Next two chars must be either two hypens (closing boundary - 2 bytes),
       
   283               // or single Eol characters (new body).
       
   284               // Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
       
   285               char* eolBuf = (char*)&aMultipartBody[ offset ];
       
   286               // Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
       
   287               if( aMultipartLen >= offset + hypensLength )
       
   288                 {
       
   289                  if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0|| 
       
   290                      eolBuf[0] == Multipart_LF_Text[0])
       
   291                    
       
   292                   {
       
   293                   // We found Eol, so this is a new multipart body (header and content)
       
   294                   if( startOffset == -1 )
       
   295                     {
       
   296                     // this is the beginning.
       
   297                     startOffset = offset;
       
   298                     // let's looking for the end of this body part which either could
       
   299                     // be closing boundary or an opening boundary for the next body part
       
   300 
       
   301                     // jump over the boundary information
       
   302                     i = startOffset + singleEolLength;
       
   303                     }
       
   304                   else
       
   305                     {
       
   306                     // We found the next boundary marker, so this is the
       
   307                     // beginning of the next body part
       
   308                     endOffset = offset - boundaryLength - hypensLength;
       
   309                     // we've got both start and end offset
       
   310                     break;
       
   311                     }
       
   312                   }
       
   313                  else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
       
   314                    {
       
   315                     // We found the closing boundary marker
       
   316                     endOffset = offset - boundaryLength - hypensLength;
       
   317                     break;
       
   318                    }
       
   319                  else
       
   320                    {
       
   321                    // it's neither Eol nor two hypens "--"
       
   322                    badContent = ETrue;
       
   323                    break;
       
   324                    }
       
   325                 }
       
   326               else
       
   327                 {
       
   328                 // the buffer is too short and not closed properly
       
   329                 endOffset = i;
       
   330                 break;
       
   331                 }
       
   332               }
       
   333             }
       
   334           else
       
   335             {
       
   336             // the buffer is far too short
       
   337             endOffset = i;
       
   338             break;
       
   339             }
       
   340           }
       
   341         i++;
       
   342         } // end of while loop
       
   343 
       
   344     // missing closing boundary check
       
   345     if( endOffset == -1 )
       
   346         {
       
   347         // take the end of the body as closing boundary
       
   348         endOffset = i - 1;
       
   349         }
       
   350 
       
   351     if( badContent )
       
   352         {
       
   353         endOffset = -1;
       
   354         startOffset = -1;
       
   355         }
       
   356 
       
   357     if( startOffset != -1 && endOffset != -1 )
       
   358         {
       
   359         *aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
       
   360         length = endOffset - startOffset;
       
   361         }
       
   362     else
       
   363         {
       
   364         *aBodyPartBuffer = NULL;
       
   365         length = 0;
       
   366         }
       
   367 
       
   368     return length;
       
   369     }
       
   370 
       
   371 
       
   372 // ------------------------------------------------------------------------- 
       
   373 // Set End-Of-Line characters.  Look at the eol character after the boundary to
       
   374 // determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
       
   375 // unix/mac).
       
   376 
       
   377 // NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
       
   378 
       
   379 // Multipart entity is specified as follows:
       
   380 // --boundary123[eol]
       
   381 // Content-type: text/html[eol]
       
   382 // Content-location: http:\\www.nokia.com\index.html[eol]
       
   383 // [eol]
       
   384 // <html><body>...nokia rules...</html>[eol]
       
   385 // --boundary123--[eol]
       
   386 // ------------------------------------------------------------------------- 
       
   387 void
       
   388 MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
       
   389                                    TUint32 aMultipartLen,
       
   390                                    const TDesC8& aBoundary,
       
   391                                    char** aSingleEolChar,
       
   392                                    char** aDoubleEolChar )
       
   393     {
       
   394     // check on required parameters
       
   395     __ASSERT_ALWAYS( aMultipartBody != NULL,
       
   396                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   397     __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
       
   398                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   399     __ASSERT_ALWAYS( aSingleEolChar != NULL,
       
   400                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   401     __ASSERT_ALWAYS( aDoubleEolChar != NULL,
       
   402                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   403 
       
   404     TUint32 i = 0;
       
   405     const char* boundaryStr = (const char*) aBoundary.Ptr();
       
   406     int boundaryLength = aBoundary.Length();
       
   407     int hypensLength = strlen( Multipart_Hypens_Text );
       
   408     int lfLength = strlen( Multipart_LF_Text );
       
   409 
       
   410     // Set the default eol (CRLF)
       
   411     *aSingleEolChar = (char *)Multipart_CRLF_Text;
       
   412     *aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
       
   413 
       
   414     while (i < (aMultipartLen - hypensLength + 1))
       
   415         {
       
   416         // Get the first two hypens
       
   417         char* bodyPart = (char*)&aMultipartBody[ i ];
       
   418         if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
       
   419             {
       
   420             char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
       
   421             // Check if the body is long enough first and then check if boundary matches
       
   422             if (aMultipartLen >= i + hypensLength + boundaryLength )
       
   423                 {
       
   424                 if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
       
   425                     {
       
   426                     // We've got the boundary
       
   427                     i = i + hypensLength + boundaryLength;
       
   428                     // Next two chars should be the single Eol characters.
       
   429                     // Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
       
   430                     char* eolBuf = (char*)&aMultipartBody[ i ];
       
   431                     // Check if buffer is long enough for eol [1 byte LF]
       
   432                     if (aMultipartLen >= i + lfLength)
       
   433                         {
       
   434                         if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
       
   435                             {
       
   436                             // We found LF Eol (unix/mac)
       
   437                             *aSingleEolChar = (char *)Multipart_LF_Text;
       
   438                             *aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
       
   439                             } // end of if compare eol to LF
       
   440                         } // end of if buffer size ok
       
   441 
       
   442                     // Break in all cases, we will use the default CRLF if we don't
       
   443                     // find eol=LF, or the remaining buffer is too small
       
   444                     break;
       
   445                     } // end of compare/found boundary
       
   446                 }
       
   447             } // end of looking for first two hypens
       
   448 
       
   449         ++i;
       
   450         } // end of while
       
   451     }
       
   452 
       
   453 
       
   454 // ------------------------------------------------------------------------- 
       
   455 // parse body
       
   456 // The bodyPart parameter can contain the optional headers and response, or
       
   457 // just the response.  In the case of both the (optional) header(s) and the
       
   458 // response, let's cut off header(s) and return the response body.  The
       
   459 // header is seperated from the response by two End-of-line (Eol) characters,
       
   460 // i.e. two CRLF's (windows) or two LF's (unix/mac).
       
   461 // --boundary123 (omitted from bodyPart parameter, starts next line)
       
   462 // Content-type: text/html[eol]
       
   463 // Content-location: http:\\www.nokia.com\index.html[eol]
       
   464 // [eol]
       
   465 // <html><body>nokia rules</body></html>
       
   466 //
       
   467 // In the case of no headers, there may be only one (or more) Eol characters.
       
   468 // --boundary123 (omitted from bodyPart parameter, starts on next line)
       
   469 // [eol]
       
   470 // <html><body>nokia rules</body></html>
       
   471 // ------------------------------------------------------------------------- 
       
   472 void
       
   473 MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
       
   474                                  TUint32 aBodyPartBufferLength,
       
   475                                  char* aSingleEolChar,
       
   476                                  char* aDoubleEolChar,
       
   477                                  const TDesC16& aResponseUrl,
       
   478                                  CBodyPart* aBodyPart )
       
   479     {
       
   480     // check on required parameters
       
   481     __ASSERT_ALWAYS( aBodyPartBuffer != NULL,
       
   482                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   483     __ASSERT_ALWAYS( aSingleEolChar != NULL,
       
   484                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   485     __ASSERT_ALWAYS( aDoubleEolChar != NULL,
       
   486                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   487     __ASSERT_ALWAYS( aBodyPart != NULL,
       
   488                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   489     __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
       
   490                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   491 
       
   492     // headers look something like this
       
   493     // we need to return "text/html" if the requested header is "Content-type"
       
   494     // --boundary123
       
   495     // Content-type: text/html
       
   496     // Content-location: http:\\www.nokia.com\index.html
       
   497     //
       
   498     // <html>
       
   499     // <body>
       
   500     // nokia rules.
       
   501     int contentHeaderValueCharLen = 0;
       
   502     TPtrC8 contentHeaderValuePtr( NULL, 0 );
       
   503     int contentHeaderNameLength = 0;
       
   504 
       
   505     /*lint -e{668} Possibly passing a null pointer to function */
       
   506     int singleEolLength = strlen( aSingleEolChar );
       
   507     int doubleEolLength = strlen( aDoubleEolChar );
       
   508     // start looking for the header name
       
   509     for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
       
   510         {
       
   511         int found = 0;
       
   512         const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
       
   513 
       
   514         // Did we find the Content Header Value
       
   515 	    if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
       
   516             {
       
   517             contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
       
   518             found = MULTIPART_CONTENT_BASE;
       
   519             }
       
   520 	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
       
   521             {
       
   522             contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
       
   523             found = MULTIPART_CONTENT_LOCATION;
       
   524             }
       
   525 	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
       
   526             {
       
   527             contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
       
   528             found = MULTIPART_CONTENT_TRANSFER_ENCODING;
       
   529             }     
       
   530 	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
       
   531             {
       
   532             contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
       
   533             found = MULTIPART_CONTENT_TRANSFER_ENCODING;
       
   534             }
       
   535 	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
       
   536             {
       
   537             contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
       
   538             found = MULTIPART_CONTENT_TYPE;
       
   539             }
       
   540 	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
       
   541             {
       
   542             contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
       
   543             found = MULTIPART_CONTENT_ID;
       
   544             }
       
   545 
       
   546         if (found)
       
   547             {        
       
   548             // skip spaces
       
   549             int startPos = i + contentHeaderNameLength;
       
   550             while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
       
   551                 {
       
   552                 startPos++;
       
   553                 }
       
   554 
       
   555             // used for finding '<' in Content-ID field
       
   556             char charFirst = aBodyPartBuffer[ startPos ];
       
   557             // content headers are closed with End-Of-Line (Eol) character
       
   558             for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
       
   559                 {
       
   560                 char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
       
   561                 if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0 
       
   562                    || tmpContentHeaderValue[0] == Multipart_LF_Text[0])
       
   563                     {
       
   564                     if( found == MULTIPART_CONTENT_ID )
       
   565                         {
       
   566                         if( charFirst == '<' )
       
   567                             {
       
   568                             // length of the value excluding beginging '<' and ending '>'
       
   569                             contentHeaderValueCharLen = j - startPos - 2;
       
   570                             contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
       
   571                             }
       
   572                         }
       
   573                     else
       
   574                         {
       
   575                         // length of the value
       
   576                         contentHeaderValueCharLen = j - startPos;
       
   577                         contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
       
   578                         }
       
   579 
       
   580                     // rewind so the double EOL will be checked against later
       
   581                     i = j - 1;
       
   582                     // break the inner loop
       
   583                     break;
       
   584                     }              
       
   585                 } // end of inner for loop
       
   586 
       
   587             switch( found )
       
   588                 {
       
   589                 case MULTIPART_CONTENT_BASE:
       
   590                     aBodyPart->SetContentBase( contentHeaderValuePtr );
       
   591                     break;
       
   592                 case MULTIPART_CONTENT_LOCATION:
       
   593                     aBodyPart->SetContentLocation( contentHeaderValuePtr );
       
   594                     break;
       
   595                 case MULTIPART_CONTENT_TRANSFER_ENCODING:
       
   596                     aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
       
   597                     break;
       
   598                 case MULTIPART_CONTENT_TYPE:
       
   599                     aBodyPart->SetContentType( contentHeaderValuePtr );
       
   600                     break;
       
   601                 case MULTIPART_CONTENT_ID:
       
   602                     aBodyPart->SetContentID( contentHeaderValuePtr );
       
   603                     break;
       
   604                 default:
       
   605                     break;
       
   606                 } 
       
   607             } // end of if (found) 
       
   608 
       
   609         // Did we get to the end of the Content Header. Many of the Content Header Values
       
   610 	    // are optional, so we could get to the end of the Content Header (double Eol) and
       
   611 	    // not find the Content Header Value we were searching for.
       
   612         // get the response body
       
   613 	    int aEolLength = strlen(Multipart_DoubleLF_Text);
       
   614 	    if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
       
   615 	        strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
       
   616             {
       
   617             if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
       
   618                aEolLength = doubleEolLength;
       
   619             // set body
       
   620             TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
       
   621             int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
       
   622             TPtrC8 body( responseBody, lengthBody );
       
   623             aBodyPart->SetBody( body );
       
   624 
       
   625             // set headers when we have headers
       
   626             if( i != 0 )
       
   627                 {
       
   628                 // jump over the starting single EOL
       
   629                 TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
       
   630                 // // jump over the starting single EOL and the ending double EOL
       
   631                 int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
       
   632                 TPtrC8 headers( responseHeaders, lengthHeaders );
       
   633                 aBodyPart->SetHeaders( headers );
       
   634                 }
       
   635 
       
   636             break;
       
   637             }
       
   638        } // end of outter for loop
       
   639 
       
   640     // prepare more on body part
       
   641 
       
   642     // Check to see if we have a Content-Transfer-Encoding.
       
   643     TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
       
   644     // If we have Content-Transfer-Encoding, prepare to decode
       
   645     if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
       
   646         {
       
   647         // Initialize the encoded body, input
       
   648         TPtrC8 encodedBody( aBodyPart->Body() );
       
   649 
       
   650         // This will contain the "decoded" data.
       
   651         // The memory (decodedBody.Ptr) is owned by this method, but allocated by
       
   652         // the DecodeContentTransferEncoding method.
       
   653         TPtr8 decodedBody( NULL, 0, 0 );
       
   654 
       
   655         // The decoded data will return in decodedBody.Ptr.
       
   656         // The memory allocated is owned by this method.
       
   657         TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
       
   658                                                                    encodedBody,
       
   659                                                                    decodedBody );                    
       
   660         User::LeaveIfError(err);
       
   661 
       
   662         // The responseBody pointer is an offset into the response
       
   663         // buffer, do not delete. Substitute the decodedBody pointer.
       
   664         aBodyPart->SetBody( decodedBody );
       
   665         aBodyPart->SetIsDecodedBody( ETrue );
       
   666         }   // end of if (contentTransferEncodingValue)
       
   667 
       
   668     // Check to see if we have a Content-Type.
       
   669     TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
       
   670     // parse contentType to get new contentType, charset, boundary info
       
   671     if( contentTypeValue )
       
   672         {
       
   673         MultipartParser::CutOffContentTypeAttributes( aBodyPart );
       
   674         // updated content type
       
   675         contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
       
   676         }
       
   677 
       
   678     // If we have zipped Content-Type, prepare to unzip
       
   679     if( MultipartParser::IsZipped(contentTypeValue) )
       
   680         {
       
   681         // Initialize the zipped body, input
       
   682         TPtrC8 zippedBody( aBodyPart->Body() );
       
   683 
       
   684         // This will contain the "unzipped" data.
       
   685         // The memory (unzippedBody.Ptr) is owned by this method, but allocated by
       
   686         // the Unzip method.
       
   687         TPtr8 unzippedBody( NULL, 0, 0 );
       
   688 
       
   689         // The unzipped data will return in unzippedBody.Ptr.
       
   690         // The memory allocated is owned by this method.
       
   691         TInt err = MultipartParser::Unzip( contentTypeValue,
       
   692                                            zippedBody,
       
   693                                            unzippedBody );                    
       
   694         User::LeaveIfError(err);
       
   695 
       
   696         if( aBodyPart->IsDecodedBody() )
       
   697             {
       
   698             // old body is not the original buffer, delete it
       
   699             delete (TUint8*) aBodyPart->Body().Ptr();
       
   700             }
       
   701         // unzip happend, use unzippedBody; delete decodedBody
       
   702         else
       
   703             {
       
   704             // The responseBody pointer is an offset into the response
       
   705             // buffer, do not delete. Substitute the decodedBody pointer.
       
   706 
       
   707             aBodyPart->SetIsDecodedBody( ETrue );
       
   708             }
       
   709 
       
   710         aBodyPart->SetBody( unzippedBody );
       
   711         }
       
   712 
       
   713     // Get the url of the current body part
       
   714     HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
       
   715                                                              aBodyPart->ContentLocation(),
       
   716                                                              aResponseUrl );
       
   717     aBodyPart->SetUrl( responseUrl );
       
   718     }
       
   719 
       
   720 
       
   721 // ------------------------------------------------------------------------- 
       
   722 // From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
       
   723 //   Section 6.2.  Content-Transfer-Encodings Semantics
       
   724 //
       
   725 //   The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
       
   726 //   mean that the identity (i.e. NO) encoding transformation has been
       
   727 //   performed.  As such, they serve simply as indicators of the domain of
       
   728 //   the body data, and provide useful information about the sort of
       
   729 //   encoding that might be needed for transmission in a given transport
       
   730 //   system.  The terms "7bit data", "8bit data", and "binary data" are
       
   731 //   all defined in Section 2.
       
   732 
       
   733 //  Returns true if contentTransferEncodingValue is neither NULL nor a domain.
       
   734 // ------------------------------------------------------------------------- 
       
   735 TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
       
   736     {        
       
   737     if( !aContentTransferEncodingValue )
       
   738         {
       
   739         return EFalse;
       
   740         }
       
   741 
       
   742     char* encoding = (char*)aContentTransferEncodingValue;
       
   743 
       
   744     if ( strncasecmp( encoding,
       
   745                       Multipart_Content_Transfer_Encoding_7bit,
       
   746                       strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
       
   747         {
       
   748         return EFalse;
       
   749         }
       
   750 
       
   751     if ( strncasecmp( encoding,
       
   752                       Multipart_Content_Transfer_Encoding_8bit,
       
   753                       strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
       
   754         {
       
   755         return EFalse;
       
   756         }
       
   757 
       
   758     if ( strncasecmp( encoding,
       
   759                       Multipart_Content_Transfer_Encoding_binary,
       
   760                       strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
       
   761         {
       
   762         return EFalse;
       
   763         }      
       
   764 
       
   765     return ETrue;
       
   766     }
       
   767 
       
   768 
       
   769 // ----------------------------------------------------------------------------
       
   770 // DecodeContentTransferEncoding
       
   771 //
       
   772 // Decodes the Content-Transfer-Encoding.  The returned length of decodedBody
       
   773 // is zero if decoding failed.
       
   774 // NOTES:
       
   775 // 1. This method should be called with a non-null string, i.e.
       
   776 // aContentTransferEncodingValue.
       
   777 // 2. Memory is allocated in this method, but ownership is with the calling method.
       
   778 // ----------------------------------------------------------------------------
       
   779 TInt
       
   780 MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
       
   781                                                 const TDesC8& aEncodedBody,
       
   782                                                 TPtr8& aDecodedBody )
       
   783     {
       
   784     // check on required parameters
       
   785     __ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
       
   786                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   787     __ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
       
   788                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   789 
       
   790     TInt status = KErrNone;
       
   791     char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
       
   792 
       
   793     // Set the decodedBody.Length to zero, length > 0 if successful decode
       
   794     aDecodedBody.SetLength(0);
       
   795 
       
   796     // Is the Content-Transfer-Encoding = "base64"
       
   797     if( strncasecmp( contentTransferEncodingString,
       
   798                      Multipart_Content_Transfer_Encoding_Base64, 
       
   799                      strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
       
   800         {
       
   801         // We have base64, create base64 codec
       
   802         TImCodecB64 base64Codec;
       
   803 
       
   804         // The decoded length of base64 is about half (use same) encoded length
       
   805         TUint maxBodyLength = aEncodedBody.Length();
       
   806         TUint8* decodedDataPtr = new TUint8[maxBodyLength];
       
   807         if( decodedDataPtr )
       
   808             {
       
   809             aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
       
   810 
       
   811             // Decode the base64 Content-Transfer-Encoding
       
   812             base64Codec.Decode(aEncodedBody, aDecodedBody);
       
   813 
       
   814             if (aDecodedBody.Length() == 0)
       
   815                 {
       
   816                 status = KErrGeneral;
       
   817                 }
       
   818             }
       
   819         else // decodedDataPtr is NULL
       
   820             {
       
   821             status = KErrNoMemory;
       
   822             }
       
   823         }   // end of base64 decoding
       
   824 
       
   825     // Is the Content-Transfer-Encoding = "quoted-printable"
       
   826     else if( strncasecmp( contentTransferEncodingString,
       
   827                           Multipart_Content_Transfer_Encoding_QuotedPrintable, 
       
   828                           strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
       
   829         {
       
   830         // We have quoted-printable, create quoted-printable codec
       
   831         TImCodecQP qpCodec;
       
   832 
       
   833         // The decoded length of QP is the same as the encoded length
       
   834         TUint maxBodyLength = aEncodedBody.Length();
       
   835         TUint8* decodedDataPtr = new TUint8[maxBodyLength];
       
   836         if( decodedDataPtr )
       
   837             {
       
   838             aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
       
   839 
       
   840             // Decode the quoted-printable Content-Transfer-Encoding
       
   841             qpCodec.Decode(aEncodedBody, aDecodedBody);
       
   842 
       
   843             if (aDecodedBody.Length() == 0)
       
   844                 {
       
   845                 status = KErrGeneral;
       
   846                 }
       
   847             }
       
   848         else // decodedDataPtr is NULL
       
   849             {
       
   850             status = KErrNoMemory;
       
   851             }
       
   852         }   // end of quoted-printed decoding
       
   853 
       
   854     // Is the Content-Encoding = "gzip"
       
   855     else if( strncasecmp( contentTransferEncodingString,
       
   856                           Multipart_Content_Encoding_GZip, 
       
   857                           strlen(Multipart_Content_Encoding_GZip) ) == 0 )
       
   858         {
       
   859         // Our GZip decoder parts
       
   860         GZipBufMgr* gZipBufMgr = NULL;
       
   861         CEZDecompressor* ezDecompressor = NULL;
       
   862 
       
   863         // We have gzip, lets decompress the encoded data.
       
   864         // Set up the encoded data into a GZip buffer manager.
       
   865         TInt err = 0;
       
   866         TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
       
   867 
       
   868         // Get the GZip decompressor
       
   869         if( gZipBufMgr )
       
   870             {
       
   871             TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
       
   872 
       
   873             // Inflate the GZip data
       
   874             if( ezDecompressor )
       
   875                 {
       
   876                 TRAP(err, ezDecompressor->InflateL());
       
   877                 // Set the finalize flag
       
   878                 if (err == KErrNone)
       
   879                     {
       
   880                     TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
       
   881                     // Get the inflated data, it is much larger then the encoded data
       
   882                     if (err == KErrNone)
       
   883                         {
       
   884                         TPtrC8 output = ezDecompressor->OutputDescriptor();
       
   885                         if (output.Length() != 0)
       
   886                             {
       
   887                             TInt size = output.Size();
       
   888                             TUint8* outBuf = new TUint8[size];
       
   889                             if( outBuf )
       
   890                                 {
       
   891                                 memcpy(outBuf, output.Ptr(), size);
       
   892 
       
   893                                 aDecodedBody.Set((TUint8*)outBuf, size, size);
       
   894                                 }
       
   895                             else // outBuf is NULL
       
   896                                 {
       
   897                                 status = KErrNoMemory;
       
   898                                 }
       
   899                             }
       
   900                         else
       
   901                             {
       
   902                             status = KErrGeneral;
       
   903                             }
       
   904                         }
       
   905                     else
       
   906                         {
       
   907                         status = KErrGeneral;
       
   908                         }
       
   909                     }
       
   910                 else
       
   911                     {
       
   912                     status = KErrGeneral;
       
   913                     }
       
   914                 }
       
   915             else // ezDecompressor is NULL
       
   916                 {
       
   917                 status = KErrNoMemory;
       
   918                 }
       
   919             }
       
   920         else // gZipBufMgr is NULL
       
   921             {
       
   922             status = KErrNoMemory;
       
   923             }
       
   924 
       
   925         // Clean up our memory
       
   926         delete gZipBufMgr;
       
   927         delete ezDecompressor;
       
   928         }   // end of gzip 
       
   929 
       
   930     // We can add additional decodings here.
       
   931     // When adding additional decoding be aware of the decodedBody.Ptr()
       
   932     // max size. Do the realloc here, AND allow the decodedBody.Ptr()
       
   933     // ownership to be passed back to calling method.
       
   934 
       
   935     return status;
       
   936     }
       
   937 
       
   938     
       
   939 // ------------------------------------------------------------------------- 
       
   940 // only support application/x-gzip
       
   941 // ------------------------------------------------------------------------- 
       
   942 TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
       
   943     {        
       
   944     if( !aContentTypeValue )
       
   945         {
       
   946         return EFalse;
       
   947         }
       
   948 
       
   949     char* contentType = (char*)aContentTypeValue;
       
   950 
       
   951     if ( strncasecmp( contentType,
       
   952                       Multipart_Content_Type_GZip,
       
   953                       strlen(Multipart_Content_Type_GZip) ) == 0 )
       
   954         {
       
   955         return ETrue;
       
   956         }
       
   957 
       
   958     return EFalse;
       
   959     }
       
   960 
       
   961 
       
   962 // ----------------------------------------------------------------------------
       
   963 // Unzip
       
   964 //
       
   965 // Unzip the .gz.  The returned length of unzippedBody
       
   966 // is zero if unzip failed.
       
   967 // NOTES:
       
   968 // 1. This method should be called with a non-null string, i.e.
       
   969 // aContentType.
       
   970 // 2. Memory is allocated in this method, but ownership is with the calling method.
       
   971 // ----------------------------------------------------------------------------
       
   972 TInt
       
   973 MultipartParser::Unzip( TUint8* aContentType,
       
   974                         const TDesC8& aZippedBody,
       
   975                         TPtr8& aUnzippedBody )
       
   976     {
       
   977     // check on required parameters
       
   978     __ASSERT_ALWAYS( aContentType != NULL,
       
   979                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   980     __ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
       
   981                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
   982 
       
   983     TInt status = KErrNone;
       
   984     char* contentTypeStr = (char*)aContentType;
       
   985 
       
   986     // Set the aUnzippedBody.Length to zero, length > 0 if successful decode
       
   987     aUnzippedBody.SetLength(0);
       
   988 
       
   989     // Our GZip decoder parts
       
   990     GZipBufMgr* gZipBufMgr = NULL;
       
   991     CEZDecompressor* ezDecompressor = NULL;
       
   992 
       
   993     // Is the Content-Type = "application/x-gzip"
       
   994     if( strncasecmp( contentTypeStr,
       
   995                      Multipart_Content_Type_GZip, 
       
   996                      strlen(Multipart_Content_Type_GZip) ) == 0 )
       
   997         {
       
   998         // We have gzip, lets decompress the encoded data.
       
   999         // Set up the encoded data into a GZip buffer manager.
       
  1000         TInt err = 0;
       
  1001         TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
       
  1002 
       
  1003         // Get the GZip decompressor
       
  1004         if( gZipBufMgr )
       
  1005             {
       
  1006             TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
       
  1007 
       
  1008             // Inflate the GZip data
       
  1009             if( ezDecompressor )
       
  1010                 {
       
  1011                 TRAP(err, ezDecompressor->InflateL());
       
  1012                 // Set the finalize flag
       
  1013                 if (err == KErrNone)
       
  1014                     {
       
  1015                     TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
       
  1016                     // Get the inflated data, it is much larger then the encoded data
       
  1017                     if (err == KErrNone)
       
  1018                         {
       
  1019                         TPtrC8 output = ezDecompressor->OutputDescriptor();
       
  1020                         if (output.Length() != 0)
       
  1021                             {
       
  1022                             TInt size = output.Size();
       
  1023                             TUint8* outBuf = new TUint8[size];
       
  1024                             if( outBuf )
       
  1025                                 {
       
  1026                                 memcpy(outBuf, output.Ptr(), size);
       
  1027 
       
  1028                                 aUnzippedBody.Set((TUint8*)outBuf, size, size);
       
  1029                                 }
       
  1030                             else // outBuf is NULL
       
  1031                                 {
       
  1032                                 status = KErrNoMemory;
       
  1033                                 }
       
  1034                             }
       
  1035                         else
       
  1036                             {
       
  1037                             status = KErrGeneral;
       
  1038                             }
       
  1039                         }
       
  1040                     else
       
  1041                         {
       
  1042                         status = KErrGeneral;
       
  1043                         }
       
  1044                     }
       
  1045                 else
       
  1046                     {
       
  1047                     status = KErrGeneral;
       
  1048                     }
       
  1049                 }
       
  1050             else // ezDecompressor is NULL
       
  1051                 {
       
  1052                 status = KErrNoMemory;
       
  1053                 }
       
  1054             }
       
  1055         else // gZipBufMgr is NULL
       
  1056             {
       
  1057             status = KErrNoMemory;
       
  1058             }
       
  1059         }   // end of gzip 
       
  1060 
       
  1061     // Clean up our memory
       
  1062     delete gZipBufMgr;
       
  1063     delete ezDecompressor;
       
  1064 
       
  1065     return status;
       
  1066     }
       
  1067 
       
  1068 
       
  1069 // ----------------------------------------------------------------------------
       
  1070 // It cuts off the charset value from the content type header
       
  1071 // content type string looks like as follows:
       
  1072 // text/plain; charset=us-ascii; boundary="abc"
       
  1073 // ----------------------------------------------------------------------------
       
  1074 void
       
  1075 MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart ) 
       
  1076     {
       
  1077     // check on required parameters
       
  1078     __ASSERT_ALWAYS( aBodyPart != NULL,
       
  1079                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
  1080 
       
  1081     TPtrC8 aContentType( aBodyPart->ContentType() );
       
  1082 
       
  1083     // check if there is a delimiter ';'
       
  1084     TInt lenCT = aContentType.Length();
       
  1085     TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
       
  1086                                      strlen(Multipart_ContentTypeString_Delimiter_Text) );
       
  1087     if (offset != KErrNotFound)
       
  1088         {
       
  1089         // ; is meant to be the end of the content type value.
       
  1090         // cut off content type unrelated part
       
  1091         aBodyPart->SetContentType( aContentType.Left( offset ) );
       
  1092 
       
  1093         // extract boundary and charset info
       
  1094         if( lenCT > offset )
       
  1095             {
       
  1096             TPtrC8 unrelated = aContentType.Right( lenCT - offset );
       
  1097             TInt lenU = unrelated.Length();
       
  1098             
       
  1099             // check the boundary information
       
  1100             TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text, 
       
  1101                                            strlen(Multipart_Boundary_Text) );
       
  1102             if (offsetB != KErrNotFound)
       
  1103                 {
       
  1104                 // now, we are at the beginning of "boundary="abc" string.
       
  1105                 // move to the "abc" part
       
  1106                 TPtrC8 boundary = unrelated.Right( lenU - 
       
  1107                                                    offsetB - 
       
  1108                                                    strlen( Multipart_Boundary_Text ) );
       
  1109                 TInt lenB = boundary.Length();
       
  1110 
       
  1111                 // look for where to end
       
  1112                 TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
       
  1113                                               strlen(Multipart_ContentTypeString_Quotes_Text) );
       
  1114                 if (offsetQ != KErrNotFound)
       
  1115                     {
       
  1116                     // skip the quote (") char
       
  1117                     boundary.Set( boundary.Right( lenB - offsetQ ) );
       
  1118                     }
       
  1119 
       
  1120                 // look for where to end
       
  1121                 // check "
       
  1122                 TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
       
  1123                                               strlen(Multipart_ContentTypeString_Quotes_Text) );
       
  1124                 if (offsetE == KErrNotFound)
       
  1125                     {
       
  1126                     // check ;
       
  1127                     offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
       
  1128                                              strlen(Multipart_ContentTypeString_Delimiter_Text) );
       
  1129                     }
       
  1130                 if (offsetE != KErrNotFound)
       
  1131                     {
       
  1132                     boundary.Set( boundary.Left( offsetE ) );
       
  1133                     }
       
  1134 
       
  1135                 // set it on to the input parameter
       
  1136                 aBodyPart->SetBoundary( boundary );
       
  1137                 } // end of if (offsetB != KErrNotFound)
       
  1138 
       
  1139             // check the charset information
       
  1140             TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text, 
       
  1141                                             strlen(Multipart_Charset_Text) );
       
  1142             if (offsetCh != KErrNotFound)
       
  1143                 {
       
  1144                 // now, we are at the beginning of "charset=us-ascii" string.
       
  1145                 // move to the us-ascii part
       
  1146                 TPtrC8 charset = unrelated.Right( lenU - 
       
  1147                                                   offsetCh - 
       
  1148                                                   strlen( Multipart_Charset_Text ) );
       
  1149                 TInt lenCh = charset.Length();
       
  1150 
       
  1151                 // look for where to end
       
  1152                 TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
       
  1153                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
       
  1154                 if (offsetQ != KErrNotFound)
       
  1155                     {
       
  1156                     // skip the quote (") char
       
  1157                     charset.Set( charset.Right( lenCh - offsetQ ) );
       
  1158                     }
       
  1159 
       
  1160                 // look for where to end
       
  1161                 // check "
       
  1162                 TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
       
  1163                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
       
  1164                 if (offsetE == KErrNotFound)
       
  1165                     {
       
  1166                     // check ;
       
  1167                     offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
       
  1168                                             strlen(Multipart_ContentTypeString_Delimiter_Text) );
       
  1169                     }
       
  1170                 if (offsetE != KErrNotFound)
       
  1171                     {
       
  1172                     charset.Set( charset.Left( offsetE ) );
       
  1173                     }
       
  1174 
       
  1175                 // set it on to the input parameter
       
  1176                 aBodyPart->SetCharset( charset );
       
  1177                 } // end of if (offsetCh != KErrNotFound)
       
  1178 
       
  1179             } // end of if( lenCT > offset )
       
  1180         } // end of if (offset != KErrNotFound)
       
  1181     }
       
  1182 
       
  1183 
       
  1184 // ----------------------------------------------------------------------------
       
  1185 // MultipartParser::GetBodyPartUrl
       
  1186 //
       
  1187 // Builds up the URL which refers to this particular body part
       
  1188 // ----------------------------------------------------------------------------
       
  1189 HBufC16*
       
  1190 MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
       
  1191                                   const TDesC8& aContentLocation,
       
  1192                                   const TDesC16& aResponseUrl )
       
  1193     {
       
  1194     // check on required parameters
       
  1195     __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
       
  1196                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
  1197 
       
  1198     // Body url builds up as follows:
       
  1199     // Take global (respond header) Content Base header first.
       
  1200     // If local (in the body part) Content Base header is present, then
       
  1201     // take it as default Content Base header (by overruling global Content
       
  1202     // Base header).  Check if the Content Location header is either an
       
  1203     // absolute or relative URL.  If it is an absolute, then ignore the
       
  1204     // Content Base, otherwise concatenate them.
       
  1205     // Error cases:
       
  1206     //   No Content Base header + Content Location is relative URL
       
  1207     //   No Content Location header
       
  1208     //   Content Base header is relative URL
       
  1209     TBool contentBaseInvalid = EFalse;
       
  1210     HBufC16* url = NULL;
       
  1211     
       
  1212     if (aContentBase.Ptr())
       
  1213         {
       
  1214         // Check if it is a relative url
       
  1215         if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
       
  1216             {
       
  1217             // Relative URL is not valid here as base location.
       
  1218             contentBaseInvalid = ETrue;
       
  1219             }
       
  1220         }
       
  1221     else
       
  1222         {
       
  1223         // no content base header
       
  1224         contentBaseInvalid = ETrue;
       
  1225         } // end of if (aContentBase)
       
  1226 
       
  1227     if (contentBaseInvalid)
       
  1228         {
       
  1229         if( aResponseUrl.Ptr() )
       
  1230             {
       
  1231             // Copy response url
       
  1232             TInt lenU = aResponseUrl.Length();
       
  1233             url = HBufC::NewLC( lenU + 1 );
       
  1234             url->Des().Copy( aResponseUrl );
       
  1235             url->Des().ZeroTerminate();
       
  1236             }
       
  1237         }
       
  1238     else
       
  1239         {
       
  1240         // Copy global content "base" location 
       
  1241         TInt lenCB = aContentBase.Length();
       
  1242         url = HBufC::NewLC( lenCB + 1 );
       
  1243         url->Des().Copy( aContentBase );
       
  1244         url->Des().ZeroTerminate();
       
  1245         } // end of if (contentBaseInvalid)
       
  1246 
       
  1247     // Check if Content Localtion is valid
       
  1248     if( aContentLocation.Ptr() )
       
  1249         {
       
  1250         TInt lenCL = aContentLocation.Length();
       
  1251 
       
  1252         // If the Content Location is an absolute URL, then Content Base value is ignored,
       
  1253         // otherwise the absolute path is going to be built unless Content Base is missing
       
  1254         if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
       
  1255             {
       
  1256             // clean up memory
       
  1257             if( url )
       
  1258                 {
       
  1259                 CleanupStack::PopAndDestroy();  // url
       
  1260                 }
       
  1261 
       
  1262             // fill url with content location
       
  1263             url = HBufC::NewL( lenCL + 1 );
       
  1264             url->Des().Copy( aContentLocation );
       
  1265             url->Des().ZeroTerminate();
       
  1266             }
       
  1267         else
       
  1268             {
       
  1269             if( url )
       
  1270                 {
       
  1271                 HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
       
  1272 
       
  1273                 CleanupStack::PopAndDestroy(); // url
       
  1274 
       
  1275                 url = urlN;
       
  1276                 }
       
  1277             }
       
  1278         } // end of if( aContentLocation
       
  1279     else
       
  1280         {
       
  1281         if( url )
       
  1282             {
       
  1283             CleanupStack::Pop(); // url
       
  1284             }
       
  1285         }
       
  1286 
       
  1287     return url;
       
  1288     }
       
  1289 
       
  1290 
       
  1291 // ----------------------------------------------------------------------------
       
  1292 // MultipartParser::IsUrlRelativeL
       
  1293 //
       
  1294 // ----------------------------------------------------------------------------
       
  1295 TBool
       
  1296 MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
       
  1297     {
       
  1298     // check on required parameters
       
  1299     __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
       
  1300                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
  1301 
       
  1302     TUriParser8 uriParser;
       
  1303 
       
  1304     User::LeaveIfError(uriParser.Parse(aUrl));
       
  1305 
       
  1306     if( uriParser.Extract(EUriScheme).Ptr() )
       
  1307         {
       
  1308         return EFalse;
       
  1309         }
       
  1310     else
       
  1311         {
       
  1312         return ETrue;
       
  1313         }
       
  1314     }
       
  1315 
       
  1316 
       
  1317 // ----------------------------------------------------------------------------
       
  1318 // MultipartParser::UrlRelToAbsL
       
  1319 //
       
  1320 // Absolute path is built as : Base + Relative
       
  1321 // ----------------------------------------------------------------------------
       
  1322 HBufC16*
       
  1323 MultipartParser::UrlRelToAbsL( TDesC16& aBase, 
       
  1324                                const TDesC8& aRelativeUrl )
       
  1325     {
       
  1326     // check on required parameters
       
  1327     __ASSERT_ALWAYS( aBase.Ptr() != NULL,
       
  1328                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
  1329     __ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
       
  1330                      User::Panic(_L("MultipartParser Panic"), KErrArgument) );
       
  1331 
       
  1332     // length of absolute url
       
  1333     TInt len = 0;
       
  1334     // length of relative url
       
  1335     TInt lenR = 0;
       
  1336     TBool appendSlash = EFalse;
       
  1337     // path of absolute url
       
  1338     TPtrC16 path( NULL, 0 );
       
  1339 
       
  1340     // must to have aRelativeUrl
       
  1341     User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
       
  1342 
       
  1343     TUriParser16 uriParser;
       
  1344     User::LeaveIfError( uriParser.Parse( aBase ) );
       
  1345 
       
  1346     // <scheme>://
       
  1347     TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
       
  1348     // must to have scheme
       
  1349     User::LeaveIfNull( (TUint16*)scheme.Ptr() );
       
  1350     len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
       
  1351 
       
  1352     // <user>:<password>@
       
  1353     TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
       
  1354     if( user.Ptr() )
       
  1355         {
       
  1356         len += user.Length() + 1;
       
  1357         }
       
  1358 
       
  1359     // <host>
       
  1360     TPtrC16 host( uriParser.Extract( EUriHost ) );
       
  1361     // must to have host
       
  1362     User::LeaveIfNull( (TUint16*)host.Ptr() );
       
  1363     len += host.Length();
       
  1364 
       
  1365     // :<port>
       
  1366     TPtrC16 port( uriParser.Extract( EUriPort ) );
       
  1367     if( port.Ptr() )
       
  1368         {
       
  1369         len += port.Length();
       
  1370         }
       
  1371 
       
  1372     // If the relative url begins with "./", remove it
       
  1373     TPtrC8 relativeUrl( NULL, 0 );
       
  1374     TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
       
  1375     TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
       
  1376     if ( indexD == 0 && indexS == 1)
       
  1377         {
       
  1378         // Found a dot-slash at beginning of relative url
       
  1379         relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
       
  1380         }
       
  1381     else
       
  1382         {
       
  1383         relativeUrl.Set( aRelativeUrl );
       
  1384         }
       
  1385 
       
  1386     lenR = relativeUrl.Length();
       
  1387     len += lenR;
       
  1388     // If the relative url begins with a slash, then it is an absolute path
       
  1389     // Does relative url start with slash?
       
  1390     indexS = relativeUrl.Locate( SLASH_CHAR );
       
  1391     // no, need to extract path from base url
       
  1392     if( indexS != 0 )
       
  1393         {
       
  1394       // <path>
       
  1395         path.Set( uriParser.Extract( EUriPath ) );
       
  1396         if( path.Ptr() )
       
  1397             {
       
  1398             // cut off the file path
       
  1399             if ( path.LocateReverse( DOT_CHAR ) )
       
  1400                 {
       
  1401                 // case: dir/index.html
       
  1402                 if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
       
  1403                     {
       
  1404                     // to keep the slash
       
  1405                     path.Set( path.Left( indexS2 + 1 ) );
       
  1406                     }
       
  1407                 // case: index.html
       
  1408                 else
       
  1409                     {
       
  1410                     path.Set( NULL, 0 );
       
  1411                     }
       
  1412                 }
       
  1413 
       
  1414             // figure out the end slash
       
  1415             if( path.Ptr() )
       
  1416                 {
       
  1417                 if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
       
  1418                     {
       
  1419                     appendSlash = ETrue;
       
  1420                     }
       
  1421     
       
  1422                 len += path.Length();
       
  1423                 }
       
  1424             else
       
  1425                 {
       
  1426                 appendSlash = ETrue;
       
  1427                 }
       
  1428             }
       
  1429         }
       
  1430     // yes, no need to extract path from base url
       
  1431     if( appendSlash )
       
  1432         {
       
  1433         ++len;
       
  1434         }
       
  1435 
       
  1436     // NULL terminator
       
  1437 	// In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator) 
       
  1438 	// for proper String Termination
       
  1439     len += 2;
       
  1440 
       
  1441     // new absolute url
       
  1442     HBufC16* urlAbs = HBufC16::NewL( len );
       
  1443     TPtr16 urlAbsPtr = urlAbs->Des();
       
  1444 
       
  1445     // copy base into absolute url
       
  1446 
       
  1447     // scheme
       
  1448     urlAbsPtr.Copy( scheme );
       
  1449     urlAbsPtr.Append( COLON_CHAR );
       
  1450     urlAbsPtr.Append( SLASH_CHAR );
       
  1451     urlAbsPtr.Append( SLASH_CHAR );
       
  1452 
       
  1453     // user
       
  1454     if( user.Ptr() )
       
  1455         {
       
  1456         urlAbsPtr.Append( user );
       
  1457         urlAbsPtr.Append( AT_CHAR );
       
  1458         }
       
  1459 
       
  1460     // host
       
  1461     urlAbsPtr.Append( host );
       
  1462 
       
  1463     // port
       
  1464     if( port.Ptr() )
       
  1465         {
       
  1466         urlAbsPtr.Append( COLON_CHAR );
       
  1467         urlAbsPtr.Append( port );
       
  1468         }
       
  1469 
       
  1470     // path
       
  1471     if( path.Ptr() )
       
  1472         {
       
  1473         urlAbsPtr.Append( path );
       
  1474         }
       
  1475 
       
  1476     // slash between path and relative url
       
  1477     if( appendSlash )
       
  1478         {
       
  1479         urlAbsPtr.Append( SLASH_CHAR );
       
  1480         }
       
  1481 
       
  1482     // relative url
       
  1483     TUint16* relUrlInt = new TUint16[ lenR ];
       
  1484     TPtr16 relUrl16( relUrlInt, lenR );
       
  1485     relUrl16.Copy( relativeUrl );
       
  1486     urlAbsPtr.Append( relUrl16 );
       
  1487     delete[] relUrlInt;
       
  1488 
       
  1489     // null terminate
       
  1490     urlAbsPtr.ZeroTerminate();
       
  1491 
       
  1492     return urlAbs;
       
  1493     }
       
  1494     
       
  1495 // ------------------------------------------------------------------------- 
       
  1496 // Composes multipart/mixed document
       
  1497 // ------------------------------------------------------------------------- 
       
  1498 HBufC8*
       
  1499 MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
       
  1500                                 const TDesC8& aBoundary,
       
  1501                                 TInt aHeaderMask )
       
  1502     {
       
  1503     // --(aBoundary)
       
  1504     _LIT8(KBoundary, "--%S\r\n");
       
  1505     HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
       
  1506     boundary->Des().Format( KBoundary, &aBoundary );
       
  1507     
       
  1508     // Calculate the size of this document.
       
  1509     TInt bodySize = 0;
       
  1510     //    a. for each CBodyPart
       
  1511     //       boundaries + CRLF between headers and body (constant addition)
       
  1512     bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
       
  1513     for (TInt i = 0; i < aBodyArray.Count(); i++)
       
  1514         {
       
  1515         if (!(aBodyArray[i]->Headers().Length() +
       
  1516             aBodyArray[i]->Body().Length()))
       
  1517             {
       
  1518             // one less boundary
       
  1519             bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
       
  1520             // skip empty bodypart
       
  1521             continue;
       
  1522             }
       
  1523         bodySize += aBodyArray[i]->Headers().Length();
       
  1524     //       ensure there are only 2 CRLFs between header and body
       
  1525         if (aBodyArray[i]->Headers().Length() > 0)
       
  1526             {
       
  1527             TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
       
  1528             TUint newEnd = bodyHeaders.Length() - 1;
       
  1529             while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
       
  1530                 {
       
  1531                 --newEnd;
       
  1532                 --bodySize;
       
  1533                 }
       
  1534             // two CRLFs
       
  1535             bodySize += strlen(Multipart_CRLF_Text);
       
  1536             }
       
  1537         bodySize += aBodyArray[i]->Body().Length();
       
  1538     //       CRLF (end of body, add one only if there is body AND does not end with CRLF)
       
  1539         TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
       
  1540         if (bodyBody.Length() > 0 
       
  1541             && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
       
  1542             {
       
  1543             bodySize += strlen(Multipart_CRLF_Text);
       
  1544             }
       
  1545         }
       
  1546     //     end boundary (boundary - '\r\n' + "--")
       
  1547     bodySize += boundary->Length();
       
  1548     TInt docSize = bodySize; 
       
  1549     //     calculate the size of Headers
       
  1550     _LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
       
  1551     if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
       
  1552         {
       
  1553         docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
       
  1554         docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
       
  1555         docSize += strlen(Multipart_CRLF_Text); // eol
       
  1556         }
       
  1557     if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
       
  1558         {
       
  1559         docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
       
  1560         // calculate number of chars needed to represent bodySize
       
  1561         HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
       
  1562         bodySizeSize->Des().Num( bodySize );
       
  1563         docSize += bodySizeSize->Length(); // content length (bodySize)
       
  1564         docSize += strlen(Multipart_CRLF_Text); // eol
       
  1565         CleanupStack::PopAndDestroy( bodySizeSize );
       
  1566         }
       
  1567     if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
       
  1568         {
       
  1569         docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
       
  1570         docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
       
  1571         docSize += strlen(Multipart_CRLF_Text); // eol
       
  1572         }
       
  1573     //    extra CRLF for separating header and body
       
  1574     docSize += strlen(Multipart_CRLF_Text);
       
  1575     //  CALCULATION COMPLETE
       
  1576     //  at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
       
  1577     //  and docSize contains the size of the entire document (use it to create HBufC8*
       
  1578     //  of appropriate size)
       
  1579   
       
  1580     //  construct multipart document
       
  1581     HBufC8* document = HBufC8::NewLC(docSize);
       
  1582     TPtr8 docAppend(document->Des());
       
  1583     if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
       
  1584         {
       
  1585         docAppend.Format( KContentType, &aBoundary );
       
  1586         }
       
  1587     if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
       
  1588         {
       
  1589         _LIT8( KContentLength, "Content-Length: %d\r\n" );
       
  1590         docAppend.AppendFormat( KContentLength, bodySize );
       
  1591         }
       
  1592     if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
       
  1593         {
       
  1594         _LIT8( KLastModified, "Last-Modified: %S\r\n" );
       
  1595         TTime current;
       
  1596         current.UniversalTime();
       
  1597         TInternetDate modDate(current.DateTime());
       
  1598         HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
       
  1599         docAppend.AppendFormat( KLastModified, dateString );
       
  1600         delete dateString;
       
  1601         }
       
  1602     // required CRLF
       
  1603     docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
       
  1604     //  BodyParts
       
  1605     for (TInt i = 0; i < aBodyArray.Count(); i++)
       
  1606         {
       
  1607         if (!(aBodyArray[i]->Headers().Length() +
       
  1608             aBodyArray[i]->Body().Length()))
       
  1609             {
       
  1610             // skip empty bodypart
       
  1611             continue;
       
  1612             }
       
  1613         docAppend.Append( *boundary );
       
  1614         TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
       
  1615         while ( headerLength > 0 &&
       
  1616                 (aBodyArray[i]->Headers()[headerLength] == '\r'
       
  1617                 || aBodyArray[i]->Headers()[headerLength] == '\n' ))
       
  1618             {
       
  1619             --headerLength;
       
  1620             }
       
  1621         docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
       
  1622 
       
  1623         if ( headerLength > 0 )
       
  1624             {
       
  1625             docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
       
  1626             }
       
  1627         else
       
  1628             {
       
  1629             docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
       
  1630             }
       
  1631     //  body
       
  1632         docAppend.Append(aBodyArray[i]->Body());
       
  1633     //  CRLF only if body exists and doesn't end with CRLF
       
  1634         TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
       
  1635         if (bodyBody.Length() > 0
       
  1636             && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
       
  1637             {
       
  1638             docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
       
  1639             }
       
  1640         }
       
  1641     //  end boundary
       
  1642     _LIT8(KEndBoundary, "--%S--");
       
  1643     docAppend.AppendFormat(KEndBoundary, &aBoundary);
       
  1644     CleanupStack::Pop( document );
       
  1645     CleanupStack::PopAndDestroy( boundary );
       
  1646     return document;
       
  1647     }