os/ossrv/lowlevellibsandfws/apputils/multipartparser/src/multipartparser.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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     }