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