browserutilities/multipartparser/src/MultipartParser.cpp
changeset 0 dd21522fd290
child 25 0ed94ceaa377
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/browserutilities/multipartparser/src/MultipartParser.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,1647 @@
+/*
+* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  
+*
+*/
+
+
+// INCLUDE FILES
+#include <e32def.h>           // First to avoid NULL redefine warning (no #ifndef NULL).
+#include <e32std.h>
+#include <string.h>
+#include <imcvcodc.h>         // Imut.lib, where some decoders reside
+#include <EZDecompressor.h>   // Ezlib.lib, GZip decoders
+#include <Uri8.h>
+#include <Uri16.h>
+#include <UriCommon.h>
+
+#include "MultipartParser.h"
+#include "GZipBufMgr.h"
+#include "tinternetdate.h"
+
+
+// CONSTANTS
+const char Multipart_Mixed[] = {"multipart/mixed"};
+const char Multipart_Related[] = {"multipart/related"};
+const char Multipart_Boundary_Text[] = {"boundary="};
+const char Multipart_Content_Base_Text[] = {"Content-Base:"};
+const char Multipart_Content_Location_Text[] = {"Content-Location:"};
+const char Multipart_Content_Type_Text[] = {"Content-Type:"};
+const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
+// violate RFC2045; but upon customer request
+const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
+const char Multipart_Content_ID_Text[] = {"Content-ID:"};
+const char Multipart_Hypens_Text[] = {"--"};
+const char Multipart_CRLF_Text[] = {"\r\n"};
+const char Multipart_LF_Text[] = {"\n"};
+const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
+const char Multipart_DoubleLF_Text[] = {"\n\n"};
+const char Multipart_Charset_Text[] = {"charset="};
+const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
+const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
+const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
+const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
+const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
+const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
+const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
+const char Multipart_Content_Encoding_GZip[] = {"gzip"};
+const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
+
+// MACROS
+#define MULTIPART_CONTENT_BASE_LENGTH               13
+#define MULTIPART_CONTENT_LOCATION_LENGTH           17
+#define MULTIPART_CONTENT_TYPE_LENGTH               13
+#define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH  26
+// violates RFC2045; but upon vodafone request
+#define MULTIPART_CONTENT_ENCODING_LENGTH           17
+#define MULTIPART_CONTENT_LENGTH_LENGTH             15
+#define MULTIPART_LAST_MODIFIED_LENGTH              14
+#define MULTIPART_CONTENT_ID_LENGTH                 11
+
+#define MULTIPART_CONTENT_BASE               1
+#define MULTIPART_CONTENT_LOCATION           2
+#define MULTIPART_CONTENT_TRANSFER_ENCODING  3
+#define MULTIPART_CONTENT_TYPE               4
+#define MULTIPART_CONTENT_ID                 5
+
+#define MULTIPART_INTERNET_DATE_STRING_LENGTH       29
+
+#define SLASH_CHAR    '/'
+#define DOT_CHAR      '.'
+#define AT_CHAR       '@'
+#define COLON_CHAR    ':'
+// <scheme>://
+#define SCHEME_SEPARATOR_LENGTH               3
+
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+
+// ------------------------------------------------------------------------- 
+// Parse and put each body part to the body part array
+// ------------------------------------------------------------------------- 
+EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody, 
+                                       const TDesC8& aContentType,
+                                       const TDesC8& aBoundary,
+                                       const TDesC16& aUrl,
+                                       RPointerArray <CBodyPart>& aBodyPartsArray,
+                                       TInt aMaxToParse )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aContentType.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    const TUint8* multipartBuffer = aMultipartBody.Ptr();
+    TUint32 multipartLen = aMultipartBody.Length();
+    __ASSERT_ALWAYS( multipartLen != 0,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    TUint8* bodyPartBuffer = NULL;
+    TUint32 bodyPartBufferLength = 0;
+    TUint32 startPosition = 0;
+    char* singleEolChar = NULL;
+    char* doubleEolChar = NULL;
+    CBodyPart* bodyPart = NULL;
+
+    // currently only support mixed and related.
+    char* contentType = (char*)aContentType.Ptr();
+    if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 && 
+        strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
+        {
+        User::Leave( KErrNotSupported );
+        }
+
+    // get singleEol and doubleEol
+    MultipartParser::SetEolCharacters( multipartBuffer,
+                                       multipartLen,
+                                       aBoundary,
+                                       &singleEolChar,
+                                       &doubleEolChar );
+
+    // get body parts one by one
+    // null bodyPartBuffer indicates the end of the multipart body
+    int counter = 0;
+    do
+        {
+        // stop when we get required number of parse done
+        if( aMaxToParse != -1 && counter >= aMaxToParse )
+            {
+            break;
+            }
+        // update counter
+        counter++;
+
+        // get the next body part
+        bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition, 
+                                                                       multipartBuffer,
+                                                                       multipartLen,
+                                                                       aBoundary,
+                                                                       singleEolChar,
+                                                                       &bodyPartBuffer );
+        // break if we are at end
+        if( bodyPartBuffer == NULL )
+            {
+            break;
+            }
+        // update start position
+        startPosition += bodyPartBufferLength;
+
+        // create new body part
+        bodyPart = CBodyPart::NewL();
+        // parse each body part buffer to fill in body part
+        MultipartParser::ParseBodyPartL( bodyPartBuffer, 
+                                         bodyPartBufferLength,
+                                         singleEolChar,
+                                         doubleEolChar,
+                                         aUrl, 
+                                         bodyPart );  
+        // add the body part to the array
+        aBodyPartsArray.Append( bodyPart );
+        }
+    while( bodyPartBuffer != NULL );
+    }
+
+// ------------------------------------------------------------------------- 
+// Composes RFC1521 compliant multipart document with given bodyparts
+// Actual task of creating the document is delegated to specialized composer
+// for each of the subtypes
+// ------------------------------------------------------------------------- 
+EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
+                                            const TDesC8& aBoundary,
+                                            TMultipartSubtype aSubtype,
+                                            TInt aHeaderMask )
+    {
+    // check on required parameters
+    if ( !aBoundary.Ptr() || !aBoundary.Length() )
+        {
+        User::Leave( KErrArgument );
+        }
+    
+    HBufC8* multipartDoc = NULL;
+    switch(aSubtype)
+        {
+        case EMultipartSubtypeMixed:
+            {
+            multipartDoc = ComposeMixedL( aBodyPartsArray,
+                                          aBoundary,
+                                          aHeaderMask );
+            }
+        break;
+        default:
+            {
+            User::Leave( KErrArgument );
+            }
+        }
+    return multipartDoc;
+    }
+
+
+// ------------------------------------------------------------------------- 
+// Returns with the next body part buffer from start position (offset)
+// ------------------------------------------------------------------------- 
+TUint32
+MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition, 
+                                        const TUint8* aMultipartBody,
+                                        TUint32 aMultipartLen,
+                                        const TDesC8& aBoundary,
+                                        char* aSingleEolChar,
+                                        TUint8** aBodyPartBuffer )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aMultipartBody != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aSingleEolChar != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    long startOffset = -1;
+    long endOffset = -1;
+    TUint32 length = 0;
+    TBool badContent = EFalse;
+    TUint32 offset = 0;
+    const char* boundaryStr = (const char*) aBoundary.Ptr();
+    int boundaryLength = aBoundary.Length();
+    int hypensLength = strlen( Multipart_Hypens_Text );
+    int singleEolLength = strlen( aSingleEolChar );
+    TUint32 i = aStartPosition;
+
+    // from RFC 1341 7.2
+    // Overall, the body of a multipart entity may be specified as follows:
+    // multipart-body := preamble 1*encapsulation close-delimiter epilogue
+    // encapsulation := delimiter CRLF body-part
+    // delimiter := CRLF "--" boundary   ; taken from Content-Type field.
+    // when content-type is multipart.
+    // There must be no space between "--" and boundary.
+    // close-delimiter := delimiter "--" ; Again, no  space  before
+    // "--"
+
+    // boundary = 12xy
+    // body: here comes some text that we ignore
+    //       --12xy
+    //       first body
+    //       --12xy
+    //       second body
+    //       --12xy--
+    //       closing boundary. we ignore this text here
+    while( i < (aMultipartLen - hypensLength + 1 ) )
+        {
+        // get the first two hypens
+        // using char comparison to compare "--"
+        // hopefully this is faster
+        if( (char)aMultipartBody[ i ] == '-' && 
+            (char)aMultipartBody[ i+1 ] == '-' )
+          {
+          char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
+          // check if the body is long enough first and then check if boundary matches
+          if( aMultipartLen >= i + hypensLength + boundaryLength )
+            {
+            if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
+              {
+              // we've got the boundary
+              offset = i + hypensLength + boundaryLength;
+              // Next two chars must be either two hypens (closing boundary - 2 bytes),
+              // or single Eol characters (new body).
+              // Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
+              char* eolBuf = (char*)&aMultipartBody[ offset ];
+              // Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
+              if( aMultipartLen >= offset + hypensLength )
+                {
+                 if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0|| 
+                     eolBuf[0] == Multipart_LF_Text[0])
+                   
+                  {
+                  // We found Eol, so this is a new multipart body (header and content)
+                  if( startOffset == -1 )
+                    {
+                    // this is the beginning.
+                    startOffset = offset;
+                    // let's looking for the end of this body part which either could
+                    // be closing boundary or an opening boundary for the next body part
+
+                    // jump over the boundary information
+                    i = startOffset + singleEolLength;
+                    }
+                  else
+                    {
+                    // We found the next boundary marker, so this is the
+                    // beginning of the next body part
+                    endOffset = offset - boundaryLength - hypensLength;
+                    // we've got both start and end offset
+                    break;
+                    }
+                  }
+                 else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
+                   {
+                    // We found the closing boundary marker
+                    endOffset = offset - boundaryLength - hypensLength;
+                    break;
+                   }
+                 else
+                   {
+                   // it's neither Eol nor two hypens "--"
+                   badContent = ETrue;
+                   break;
+                   }
+                }
+              else
+                {
+                // the buffer is too short and not closed properly
+                endOffset = i;
+                break;
+                }
+              }
+            }
+          else
+            {
+            // the buffer is far too short
+            endOffset = i;
+            break;
+            }
+          }
+        i++;
+        } // end of while loop
+
+    // missing closing boundary check
+    if( endOffset == -1 )
+        {
+        // take the end of the body as closing boundary
+        endOffset = i - 1;
+        }
+
+    if( badContent )
+        {
+        endOffset = -1;
+        startOffset = -1;
+        }
+
+    if( startOffset != -1 && endOffset != -1 )
+        {
+        *aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
+        length = endOffset - startOffset;
+        }
+    else
+        {
+        *aBodyPartBuffer = NULL;
+        length = 0;
+        }
+
+    return length;
+    }
+
+
+// ------------------------------------------------------------------------- 
+// Set End-Of-Line characters.  Look at the eol character after the boundary to
+// determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
+// unix/mac).
+
+// NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
+
+// Multipart entity is specified as follows:
+// --boundary123[eol]
+// Content-type: text/html[eol]
+// Content-location: http:\\www.nokia.com\index.html[eol]
+// [eol]
+// <html><body>...nokia rules...</html>[eol]
+// --boundary123--[eol]
+// ------------------------------------------------------------------------- 
+void
+MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
+                                   TUint32 aMultipartLen,
+                                   const TDesC8& aBoundary,
+                                   char** aSingleEolChar,
+                                   char** aDoubleEolChar )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aMultipartBody != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aSingleEolChar != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    TUint32 i = 0;
+    const char* boundaryStr = (const char*) aBoundary.Ptr();
+    int boundaryLength = aBoundary.Length();
+    int hypensLength = strlen( Multipart_Hypens_Text );
+    int lfLength = strlen( Multipart_LF_Text );
+
+    // Set the default eol (CRLF)
+    *aSingleEolChar = (char *)Multipart_CRLF_Text;
+    *aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
+
+    while (i < (aMultipartLen - hypensLength + 1))
+        {
+        // Get the first two hypens
+        char* bodyPart = (char*)&aMultipartBody[ i ];
+        if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
+            {
+            char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
+            // Check if the body is long enough first and then check if boundary matches
+            if (aMultipartLen >= i + hypensLength + boundaryLength )
+                {
+                if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
+                    {
+                    // We've got the boundary
+                    i = i + hypensLength + boundaryLength;
+                    // Next two chars should be the single Eol characters.
+                    // Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
+                    char* eolBuf = (char*)&aMultipartBody[ i ];
+                    // Check if buffer is long enough for eol [1 byte LF]
+                    if (aMultipartLen >= i + lfLength)
+                        {
+                        if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
+                            {
+                            // We found LF Eol (unix/mac)
+                            *aSingleEolChar = (char *)Multipart_LF_Text;
+                            *aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
+                            } // end of if compare eol to LF
+                        } // end of if buffer size ok
+
+                    // Break in all cases, we will use the default CRLF if we don't
+                    // find eol=LF, or the remaining buffer is too small
+                    break;
+                    } // end of compare/found boundary
+                }
+            } // end of looking for first two hypens
+
+        ++i;
+        } // end of while
+    }
+
+
+// ------------------------------------------------------------------------- 
+// parse body
+// The bodyPart parameter can contain the optional headers and response, or
+// just the response.  In the case of both the (optional) header(s) and the
+// response, let's cut off header(s) and return the response body.  The
+// header is seperated from the response by two End-of-line (Eol) characters,
+// i.e. two CRLF's (windows) or two LF's (unix/mac).
+// --boundary123 (omitted from bodyPart parameter, starts next line)
+// Content-type: text/html[eol]
+// Content-location: http:\\www.nokia.com\index.html[eol]
+// [eol]
+// <html><body>nokia rules</body></html>
+//
+// In the case of no headers, there may be only one (or more) Eol characters.
+// --boundary123 (omitted from bodyPart parameter, starts on next line)
+// [eol]
+// <html><body>nokia rules</body></html>
+// ------------------------------------------------------------------------- 
+void
+MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
+                                 TUint32 aBodyPartBufferLength,
+                                 char* aSingleEolChar,
+                                 char* aDoubleEolChar,
+                                 const TDesC16& aResponseUrl,
+                                 CBodyPart* aBodyPart )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aBodyPartBuffer != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aSingleEolChar != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aBodyPart != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    // headers look something like this
+    // we need to return "text/html" if the requested header is "Content-type"
+    // --boundary123
+    // Content-type: text/html
+    // Content-location: http:\\www.nokia.com\index.html
+    //
+    // <html>
+    // <body>
+    // nokia rules.
+    int contentHeaderValueCharLen = 0;
+    TPtrC8 contentHeaderValuePtr( NULL, 0 );
+    int contentHeaderNameLength = 0;
+
+    /*lint -e{668} Possibly passing a null pointer to function */
+    int singleEolLength = strlen( aSingleEolChar );
+    int doubleEolLength = strlen( aDoubleEolChar );
+    // start looking for the header name
+    for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
+        {
+        int found = 0;
+        const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
+
+        // Did we find the Content Header Value
+	    if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
+            found = MULTIPART_CONTENT_BASE;
+            }
+	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
+            found = MULTIPART_CONTENT_LOCATION;
+            }
+	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
+            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
+            }     
+	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
+            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
+            }
+	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
+            found = MULTIPART_CONTENT_TYPE;
+            }
+	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
+            {
+            contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
+            found = MULTIPART_CONTENT_ID;
+            }
+
+        if (found)
+            {        
+            // skip spaces
+            int startPos = i + contentHeaderNameLength;
+            while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
+                {
+                startPos++;
+                }
+
+            // used for finding '<' in Content-ID field
+            char charFirst = aBodyPartBuffer[ startPos ];
+            // content headers are closed with End-Of-Line (Eol) character
+            for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
+                {
+                char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
+                if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0 
+                   || tmpContentHeaderValue[0] == Multipart_LF_Text[0])
+                    {
+                    if( found == MULTIPART_CONTENT_ID )
+                        {
+                        if( charFirst == '<' )
+                            {
+                            // length of the value excluding beginging '<' and ending '>'
+                            contentHeaderValueCharLen = j - startPos - 2;
+                            contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
+                            }
+                        }
+                    else
+                        {
+                        // length of the value
+                        contentHeaderValueCharLen = j - startPos;
+                        contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
+                        }
+
+                    // rewind so the double EOL will be checked against later
+                    i = j - 1;
+                    // break the inner loop
+                    break;
+                    }              
+                } // end of inner for loop
+
+            switch( found )
+                {
+                case MULTIPART_CONTENT_BASE:
+                    aBodyPart->SetContentBase( contentHeaderValuePtr );
+                    break;
+                case MULTIPART_CONTENT_LOCATION:
+                    aBodyPart->SetContentLocation( contentHeaderValuePtr );
+                    break;
+                case MULTIPART_CONTENT_TRANSFER_ENCODING:
+                    aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
+                    break;
+                case MULTIPART_CONTENT_TYPE:
+                    aBodyPart->SetContentType( contentHeaderValuePtr );
+                    break;
+                case MULTIPART_CONTENT_ID:
+                    aBodyPart->SetContentID( contentHeaderValuePtr );
+                    break;
+                default:
+                    break;
+                } 
+            } // end of if (found) 
+
+        // Did we get to the end of the Content Header. Many of the Content Header Values
+	    // are optional, so we could get to the end of the Content Header (double Eol) and
+	    // not find the Content Header Value we were searching for.
+        // get the response body
+	    int aEolLength = strlen(Multipart_DoubleLF_Text);
+	    if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
+	        strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
+            {
+            if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
+               aEolLength = doubleEolLength;
+            // set body
+            TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
+            int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
+            TPtrC8 body( responseBody, lengthBody );
+            aBodyPart->SetBody( body );
+
+            // set headers when we have headers
+            if( i != 0 )
+                {
+                // jump over the starting single EOL
+                TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
+                // // jump over the starting single EOL and the ending double EOL
+                int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
+                TPtrC8 headers( responseHeaders, lengthHeaders );
+                aBodyPart->SetHeaders( headers );
+                }
+
+            break;
+            }
+       } // end of outter for loop
+
+    // prepare more on body part
+
+    // Check to see if we have a Content-Transfer-Encoding.
+    TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
+    // If we have Content-Transfer-Encoding, prepare to decode
+    if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
+        {
+        // Initialize the encoded body, input
+        TPtrC8 encodedBody( aBodyPart->Body() );
+
+        // This will contain the "decoded" data.
+        // The memory (decodedBody.Ptr) is owned by this method, but allocated by
+        // the DecodeContentTransferEncoding method.
+        TPtr8 decodedBody( NULL, 0, 0 );
+
+        // The decoded data will return in decodedBody.Ptr.
+        // The memory allocated is owned by this method.
+        TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
+                                                                   encodedBody,
+                                                                   decodedBody );                    
+        User::LeaveIfError(err);
+
+        // The responseBody pointer is an offset into the response
+        // buffer, do not delete. Substitute the decodedBody pointer.
+        aBodyPart->SetBody( decodedBody );
+        aBodyPart->SetIsDecodedBody( ETrue );
+        }   // end of if (contentTransferEncodingValue)
+
+    // Check to see if we have a Content-Type.
+    TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
+    // parse contentType to get new contentType, charset, boundary info
+    if( contentTypeValue )
+        {
+        MultipartParser::CutOffContentTypeAttributes( aBodyPart );
+        // updated content type
+        contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
+        }
+
+    // If we have zipped Content-Type, prepare to unzip
+    if( MultipartParser::IsZipped(contentTypeValue) )
+        {
+        // Initialize the zipped body, input
+        TPtrC8 zippedBody( aBodyPart->Body() );
+
+        // This will contain the "unzipped" data.
+        // The memory (unzippedBody.Ptr) is owned by this method, but allocated by
+        // the Unzip method.
+        TPtr8 unzippedBody( NULL, 0, 0 );
+
+        // The unzipped data will return in unzippedBody.Ptr.
+        // The memory allocated is owned by this method.
+        TInt err = MultipartParser::Unzip( contentTypeValue,
+                                           zippedBody,
+                                           unzippedBody );                    
+        User::LeaveIfError(err);
+
+        if( aBodyPart->IsDecodedBody() )
+            {
+            // old body is not the original buffer, delete it
+            delete (TUint8*) aBodyPart->Body().Ptr();
+            }
+        // unzip happend, use unzippedBody; delete decodedBody
+        else
+            {
+            // The responseBody pointer is an offset into the response
+            // buffer, do not delete. Substitute the decodedBody pointer.
+
+            aBodyPart->SetIsDecodedBody( ETrue );
+            }
+
+        aBodyPart->SetBody( unzippedBody );
+        }
+
+    // Get the url of the current body part
+    HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
+                                                             aBodyPart->ContentLocation(),
+                                                             aResponseUrl );
+    aBodyPart->SetUrl( responseUrl );
+    }
+
+
+// ------------------------------------------------------------------------- 
+// From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
+//   Section 6.2.  Content-Transfer-Encodings Semantics
+//
+//   The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
+//   mean that the identity (i.e. NO) encoding transformation has been
+//   performed.  As such, they serve simply as indicators of the domain of
+//   the body data, and provide useful information about the sort of
+//   encoding that might be needed for transmission in a given transport
+//   system.  The terms "7bit data", "8bit data", and "binary data" are
+//   all defined in Section 2.
+
+//  Returns true if contentTransferEncodingValue is neither NULL nor a domain.
+// ------------------------------------------------------------------------- 
+TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
+    {        
+    if( !aContentTransferEncodingValue )
+        {
+        return EFalse;
+        }
+
+    char* encoding = (char*)aContentTransferEncodingValue;
+
+    if ( strncasecmp( encoding,
+                      Multipart_Content_Transfer_Encoding_7bit,
+                      strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
+        {
+        return EFalse;
+        }
+
+    if ( strncasecmp( encoding,
+                      Multipart_Content_Transfer_Encoding_8bit,
+                      strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
+        {
+        return EFalse;
+        }
+
+    if ( strncasecmp( encoding,
+                      Multipart_Content_Transfer_Encoding_binary,
+                      strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
+        {
+        return EFalse;
+        }      
+
+    return ETrue;
+    }
+
+
+// ----------------------------------------------------------------------------
+// DecodeContentTransferEncoding
+//
+// Decodes the Content-Transfer-Encoding.  The returned length of decodedBody
+// is zero if decoding failed.
+// NOTES:
+// 1. This method should be called with a non-null string, i.e.
+// aContentTransferEncodingValue.
+// 2. Memory is allocated in this method, but ownership is with the calling method.
+// ----------------------------------------------------------------------------
+TInt
+MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
+                                                const TDesC8& aEncodedBody,
+                                                TPtr8& aDecodedBody )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    TInt status = KErrNone;
+    char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
+
+    // Set the decodedBody.Length to zero, length > 0 if successful decode
+    aDecodedBody.SetLength(0);
+
+    // Is the Content-Transfer-Encoding = "base64"
+    if( strncasecmp( contentTransferEncodingString,
+                     Multipart_Content_Transfer_Encoding_Base64, 
+                     strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
+        {
+        // We have base64, create base64 codec
+        TImCodecB64 base64Codec;
+
+        // The decoded length of base64 is about half (use same) encoded length
+        TUint maxBodyLength = aEncodedBody.Length();
+        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
+        if( decodedDataPtr )
+            {
+            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
+
+            // Decode the base64 Content-Transfer-Encoding
+            base64Codec.Decode(aEncodedBody, aDecodedBody);
+
+            if (aDecodedBody.Length() == 0)
+                {
+                status = KErrGeneral;
+                }
+            }
+        else // decodedDataPtr is NULL
+            {
+            status = KErrNoMemory;
+            }
+        }   // end of base64 decoding
+
+    // Is the Content-Transfer-Encoding = "quoted-printable"
+    else if( strncasecmp( contentTransferEncodingString,
+                          Multipart_Content_Transfer_Encoding_QuotedPrintable, 
+                          strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
+        {
+        // We have quoted-printable, create quoted-printable codec
+        TImCodecQP qpCodec;
+
+        // The decoded length of QP is the same as the encoded length
+        TUint maxBodyLength = aEncodedBody.Length();
+        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
+        if( decodedDataPtr )
+            {
+            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
+
+            // Decode the quoted-printable Content-Transfer-Encoding
+            qpCodec.Decode(aEncodedBody, aDecodedBody);
+
+            if (aDecodedBody.Length() == 0)
+                {
+                status = KErrGeneral;
+                }
+            }
+        else // decodedDataPtr is NULL
+            {
+            status = KErrNoMemory;
+            }
+        }   // end of quoted-printed decoding
+
+    // Is the Content-Encoding = "gzip"
+    else if( strncasecmp( contentTransferEncodingString,
+                          Multipart_Content_Encoding_GZip, 
+                          strlen(Multipart_Content_Encoding_GZip) ) == 0 )
+        {
+        // Our GZip decoder parts
+        GZipBufMgr* gZipBufMgr = NULL;
+        CEZDecompressor* ezDecompressor = NULL;
+
+        // We have gzip, lets decompress the encoded data.
+        // Set up the encoded data into a GZip buffer manager.
+        TInt err = 0;
+        TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
+
+        // Get the GZip decompressor
+        if( gZipBufMgr )
+            {
+            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
+
+            // Inflate the GZip data
+            if( ezDecompressor )
+                {
+                TRAP(err, ezDecompressor->InflateL());
+                // Set the finalize flag
+                if (err == KErrNone)
+                    {
+                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
+                    // Get the inflated data, it is much larger then the encoded data
+                    if (err == KErrNone)
+                        {
+                        TPtrC8 output = ezDecompressor->OutputDescriptor();
+                        if (output.Length() != 0)
+                            {
+                            TInt size = output.Size();
+                            TUint8* outBuf = new TUint8[size];
+                            if( outBuf )
+                                {
+                                memcpy(outBuf, output.Ptr(), size);
+
+                                aDecodedBody.Set((TUint8*)outBuf, size, size);
+                                }
+                            else // outBuf is NULL
+                                {
+                                status = KErrNoMemory;
+                                }
+                            }
+                        else
+                            {
+                            status = KErrGeneral;
+                            }
+                        }
+                    else
+                        {
+                        status = KErrGeneral;
+                        }
+                    }
+                else
+                    {
+                    status = KErrGeneral;
+                    }
+                }
+            else // ezDecompressor is NULL
+                {
+                status = KErrNoMemory;
+                }
+            }
+        else // gZipBufMgr is NULL
+            {
+            status = KErrNoMemory;
+            }
+
+        // Clean up our memory
+        delete gZipBufMgr;
+        delete ezDecompressor;
+        }   // end of gzip 
+
+    // We can add additional decodings here.
+    // When adding additional decoding be aware of the decodedBody.Ptr()
+    // max size. Do the realloc here, AND allow the decodedBody.Ptr()
+    // ownership to be passed back to calling method.
+
+    return status;
+    }
+
+    
+// ------------------------------------------------------------------------- 
+// only support application/x-gzip
+// ------------------------------------------------------------------------- 
+TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
+    {        
+    if( !aContentTypeValue )
+        {
+        return EFalse;
+        }
+
+    char* contentType = (char*)aContentTypeValue;
+
+    if ( strncasecmp( contentType,
+                      Multipart_Content_Type_GZip,
+                      strlen(Multipart_Content_Type_GZip) ) == 0 )
+        {
+        return ETrue;
+        }
+
+    return EFalse;
+    }
+
+
+// ----------------------------------------------------------------------------
+// Unzip
+//
+// Unzip the .gz.  The returned length of unzippedBody
+// is zero if unzip failed.
+// NOTES:
+// 1. This method should be called with a non-null string, i.e.
+// aContentType.
+// 2. Memory is allocated in this method, but ownership is with the calling method.
+// ----------------------------------------------------------------------------
+TInt
+MultipartParser::Unzip( TUint8* aContentType,
+                        const TDesC8& aZippedBody,
+                        TPtr8& aUnzippedBody )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aContentType != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    TInt status = KErrNone;
+    char* contentTypeStr = (char*)aContentType;
+
+    // Set the aUnzippedBody.Length to zero, length > 0 if successful decode
+    aUnzippedBody.SetLength(0);
+
+    // Our GZip decoder parts
+    GZipBufMgr* gZipBufMgr = NULL;
+    CEZDecompressor* ezDecompressor = NULL;
+
+    // Is the Content-Type = "application/x-gzip"
+    if( strncasecmp( contentTypeStr,
+                     Multipart_Content_Type_GZip, 
+                     strlen(Multipart_Content_Type_GZip) ) == 0 )
+        {
+        // We have gzip, lets decompress the encoded data.
+        // Set up the encoded data into a GZip buffer manager.
+        TInt err = 0;
+        TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
+
+        // Get the GZip decompressor
+        if( gZipBufMgr )
+            {
+            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
+
+            // Inflate the GZip data
+            if( ezDecompressor )
+                {
+                TRAP(err, ezDecompressor->InflateL());
+                // Set the finalize flag
+                if (err == KErrNone)
+                    {
+                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
+                    // Get the inflated data, it is much larger then the encoded data
+                    if (err == KErrNone)
+                        {
+                        TPtrC8 output = ezDecompressor->OutputDescriptor();
+                        if (output.Length() != 0)
+                            {
+                            TInt size = output.Size();
+                            TUint8* outBuf = new TUint8[size];
+                            if( outBuf )
+                                {
+                                memcpy(outBuf, output.Ptr(), size);
+
+                                aUnzippedBody.Set((TUint8*)outBuf, size, size);
+                                }
+                            else // outBuf is NULL
+                                {
+                                status = KErrNoMemory;
+                                }
+                            }
+                        else
+                            {
+                            status = KErrGeneral;
+                            }
+                        }
+                    else
+                        {
+                        status = KErrGeneral;
+                        }
+                    }
+                else
+                    {
+                    status = KErrGeneral;
+                    }
+                }
+            else // ezDecompressor is NULL
+                {
+                status = KErrNoMemory;
+                }
+            }
+        else // gZipBufMgr is NULL
+            {
+            status = KErrNoMemory;
+            }
+        }   // end of gzip 
+
+    // Clean up our memory
+    delete gZipBufMgr;
+    delete ezDecompressor;
+
+    return status;
+    }
+
+
+// ----------------------------------------------------------------------------
+// It cuts off the charset value from the content type header
+// content type string looks like as follows:
+// text/plain; charset=us-ascii; boundary="abc"
+// ----------------------------------------------------------------------------
+void
+MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart ) 
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aBodyPart != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    TPtrC8 aContentType( aBodyPart->ContentType() );
+
+    // check if there is a delimiter ';'
+    TInt lenCT = aContentType.Length();
+    TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
+                                     strlen(Multipart_ContentTypeString_Delimiter_Text) );
+    if (offset != KErrNotFound)
+        {
+        // ; is meant to be the end of the content type value.
+        // cut off content type unrelated part
+        aBodyPart->SetContentType( aContentType.Left( offset ) );
+
+        // extract boundary and charset info
+        if( lenCT > offset )
+            {
+            TPtrC8 unrelated = aContentType.Right( lenCT - offset );
+            TInt lenU = unrelated.Length();
+            
+            // check the boundary information
+            TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text, 
+                                           strlen(Multipart_Boundary_Text) );
+            if (offsetB != KErrNotFound)
+                {
+                // now, we are at the beginning of "boundary="abc" string.
+                // move to the "abc" part
+                TPtrC8 boundary = unrelated.Right( lenU - 
+                                                   offsetB - 
+                                                   strlen( Multipart_Boundary_Text ) );
+                TInt lenB = boundary.Length();
+
+                // look for where to end
+                TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
+                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
+                if (offsetQ != KErrNotFound)
+                    {
+                    // skip the quote (") char
+                    boundary.Set( boundary.Right( lenB - offsetQ ) );
+                    }
+
+                // look for where to end
+                // check "
+                TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
+                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
+                if (offsetE == KErrNotFound)
+                    {
+                    // check ;
+                    offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
+                                             strlen(Multipart_ContentTypeString_Delimiter_Text) );
+                    }
+                if (offsetE != KErrNotFound)
+                    {
+                    boundary.Set( boundary.Left( offsetE ) );
+                    }
+
+                // set it on to the input parameter
+                aBodyPart->SetBoundary( boundary );
+                } // end of if (offsetB != KErrNotFound)
+
+            // check the charset information
+            TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text, 
+                                            strlen(Multipart_Charset_Text) );
+            if (offsetCh != KErrNotFound)
+                {
+                // now, we are at the beginning of "charset=us-ascii" string.
+                // move to the us-ascii part
+                TPtrC8 charset = unrelated.Right( lenU - 
+                                                  offsetCh - 
+                                                  strlen( Multipart_Charset_Text ) );
+                TInt lenCh = charset.Length();
+
+                // look for where to end
+                TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
+                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
+                if (offsetQ != KErrNotFound)
+                    {
+                    // skip the quote (") char
+                    charset.Set( charset.Right( lenCh - offsetQ ) );
+                    }
+
+                // look for where to end
+                // check "
+                TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
+                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
+                if (offsetE == KErrNotFound)
+                    {
+                    // check ;
+                    offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
+                                            strlen(Multipart_ContentTypeString_Delimiter_Text) );
+                    }
+                if (offsetE != KErrNotFound)
+                    {
+                    charset.Set( charset.Left( offsetE ) );
+                    }
+
+                // set it on to the input parameter
+                aBodyPart->SetCharset( charset );
+                } // end of if (offsetCh != KErrNotFound)
+
+            } // end of if( lenCT > offset )
+        } // end of if (offset != KErrNotFound)
+    }
+
+
+// ----------------------------------------------------------------------------
+// MultipartParser::GetBodyPartUrl
+//
+// Builds up the URL which refers to this particular body part
+// ----------------------------------------------------------------------------
+HBufC16*
+MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
+                                  const TDesC8& aContentLocation,
+                                  const TDesC16& aResponseUrl )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    // Body url builds up as follows:
+    // Take global (respond header) Content Base header first.
+    // If local (in the body part) Content Base header is present, then
+    // take it as default Content Base header (by overruling global Content
+    // Base header).  Check if the Content Location header is either an
+    // absolute or relative URL.  If it is an absolute, then ignore the
+    // Content Base, otherwise concatenate them.
+    // Error cases:
+    //   No Content Base header + Content Location is relative URL
+    //   No Content Location header
+    //   Content Base header is relative URL
+    TBool contentBaseInvalid = EFalse;
+    HBufC16* url = NULL;
+    
+    if (aContentBase.Ptr())
+        {
+        // Check if it is a relative url
+        if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
+            {
+            // Relative URL is not valid here as base location.
+            contentBaseInvalid = ETrue;
+            }
+        }
+    else
+        {
+        // no content base header
+        contentBaseInvalid = ETrue;
+        } // end of if (aContentBase)
+
+    if (contentBaseInvalid)
+        {
+        if( aResponseUrl.Ptr() )
+            {
+            // Copy response url
+            TInt lenU = aResponseUrl.Length();
+            url = HBufC::NewLC( lenU + 1 );
+            url->Des().Copy( aResponseUrl );
+            url->Des().ZeroTerminate();
+            }
+        }
+    else
+        {
+        // Copy global content "base" location 
+        TInt lenCB = aContentBase.Length();
+        url = HBufC::NewLC( lenCB + 1 );
+        url->Des().Copy( aContentBase );
+        url->Des().ZeroTerminate();
+        } // end of if (contentBaseInvalid)
+
+    // Check if Content Localtion is valid
+    if( aContentLocation.Ptr() )
+        {
+        TInt lenCL = aContentLocation.Length();
+
+        // If the Content Location is an absolute URL, then Content Base value is ignored,
+        // otherwise the absolute path is going to be built unless Content Base is missing
+        if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
+            {
+            // clean up memory
+            if( url )
+                {
+                CleanupStack::PopAndDestroy();  // url
+                }
+
+            // fill url with content location
+            url = HBufC::NewL( lenCL + 1 );
+            url->Des().Copy( aContentLocation );
+            url->Des().ZeroTerminate();
+            }
+        else
+            {
+            if( url )
+                {
+                HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
+
+                CleanupStack::PopAndDestroy(); // url
+
+                url = urlN;
+                }
+            }
+        } // end of if( aContentLocation
+    else
+        {
+        if( url )
+            {
+            CleanupStack::Pop(); // url
+            }
+        }
+
+    return url;
+    }
+
+
+// ----------------------------------------------------------------------------
+// MultipartParser::IsUrlRelativeL
+//
+// ----------------------------------------------------------------------------
+TBool
+MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    TUriParser8 uriParser;
+
+    User::LeaveIfError(uriParser.Parse(aUrl));
+
+    if( uriParser.Extract(EUriScheme).Ptr() )
+        {
+        return EFalse;
+        }
+    else
+        {
+        return ETrue;
+        }
+    }
+
+
+// ----------------------------------------------------------------------------
+// MultipartParser::UrlRelToAbsL
+//
+// Absolute path is built as : Base + Relative
+// ----------------------------------------------------------------------------
+HBufC16*
+MultipartParser::UrlRelToAbsL( TDesC16& aBase, 
+                               const TDesC8& aRelativeUrl )
+    {
+    // check on required parameters
+    __ASSERT_ALWAYS( aBase.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+    __ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
+                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
+
+    // length of absolute url
+    TInt len = 0;
+    // length of relative url
+    TInt lenR = 0;
+    TBool appendSlash = EFalse;
+    // path of absolute url
+    TPtrC16 path( NULL, 0 );
+
+    // must to have aRelativeUrl
+    User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
+
+    TUriParser16 uriParser;
+    User::LeaveIfError( uriParser.Parse( aBase ) );
+
+    // <scheme>://
+    TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
+    // must to have scheme
+    User::LeaveIfNull( (TUint16*)scheme.Ptr() );
+    len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
+
+    // <user>:<password>@
+    TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
+    if( user.Ptr() )
+        {
+        len += user.Length() + 1;
+        }
+
+    // <host>
+    TPtrC16 host( uriParser.Extract( EUriHost ) );
+    // must to have host
+    User::LeaveIfNull( (TUint16*)host.Ptr() );
+    len += host.Length();
+
+    // :<port>
+    TPtrC16 port( uriParser.Extract( EUriPort ) );
+    if( port.Ptr() )
+        {
+        len += port.Length();
+        }
+
+    // If the relative url begins with "./", remove it
+    TPtrC8 relativeUrl( NULL, 0 );
+    TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
+    TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
+    if ( indexD == 0 && indexS == 1)
+        {
+        // Found a dot-slash at beginning of relative url
+        relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
+        }
+    else
+        {
+        relativeUrl.Set( aRelativeUrl );
+        }
+
+    lenR = relativeUrl.Length();
+    len += lenR;
+    // If the relative url begins with a slash, then it is an absolute path
+    // Does relative url start with slash?
+    indexS = relativeUrl.Locate( SLASH_CHAR );
+    // no, need to extract path from base url
+    if( indexS != 0 )
+        {
+      // <path>
+        path.Set( uriParser.Extract( EUriPath ) );
+        if( path.Ptr() )
+            {
+            // cut off the file path
+            if ( path.LocateReverse( DOT_CHAR ) )
+                {
+                // case: dir/index.html
+                if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
+                    {
+                    // to keep the slash
+                    path.Set( path.Left( indexS2 + 1 ) );
+                    }
+                // case: index.html
+                else
+                    {
+                    path.Set( NULL, 0 );
+                    }
+                }
+
+            // figure out the end slash
+            if( path.Ptr() )
+                {
+                if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
+                    {
+                    appendSlash = ETrue;
+                    }
+    
+                len += path.Length();
+                }
+            else
+                {
+                appendSlash = ETrue;
+                }
+            }
+        }
+    // yes, no need to extract path from base url
+    if( appendSlash )
+        {
+        ++len;
+        }
+
+    // NULL terminator
+	// In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator) 
+	// for proper String Termination
+    len += 2;
+
+    // new absolute url
+    HBufC16* urlAbs = HBufC16::NewL( len );
+    TPtr16 urlAbsPtr = urlAbs->Des();
+
+    // copy base into absolute url
+
+    // scheme
+    urlAbsPtr.Copy( scheme );
+    urlAbsPtr.Append( COLON_CHAR );
+    urlAbsPtr.Append( SLASH_CHAR );
+    urlAbsPtr.Append( SLASH_CHAR );
+
+    // user
+    if( user.Ptr() )
+        {
+        urlAbsPtr.Append( user );
+        urlAbsPtr.Append( AT_CHAR );
+        }
+
+    // host
+    urlAbsPtr.Append( host );
+
+    // port
+    if( port.Ptr() )
+        {
+        urlAbsPtr.Append( COLON_CHAR );
+        urlAbsPtr.Append( port );
+        }
+
+    // path
+    if( path.Ptr() )
+        {
+        urlAbsPtr.Append( path );
+        }
+
+    // slash between path and relative url
+    if( appendSlash )
+        {
+        urlAbsPtr.Append( SLASH_CHAR );
+        }
+
+    // relative url
+    TUint16* relUrlInt = new TUint16[ lenR ];
+    TPtr16 relUrl16( relUrlInt, lenR );
+    relUrl16.Copy( relativeUrl );
+    urlAbsPtr.Append( relUrl16 );
+    delete[] relUrlInt;
+
+    // null terminate
+    urlAbsPtr.ZeroTerminate();
+
+    return urlAbs;
+    }
+    
+// ------------------------------------------------------------------------- 
+// Composes multipart/mixed document
+// ------------------------------------------------------------------------- 
+HBufC8*
+MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
+                                const TDesC8& aBoundary,
+                                TInt aHeaderMask )
+    {
+    // --(aBoundary)
+    _LIT8(KBoundary, "--%S\r\n");
+    HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
+    boundary->Des().Format( KBoundary, &aBoundary );
+    
+    // Calculate the size of this document.
+    TInt bodySize = 0;
+    //    a. for each CBodyPart
+    //       boundaries + CRLF between headers and body (constant addition)
+    bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
+    for (TInt i = 0; i < aBodyArray.Count(); i++)
+        {
+        if (!(aBodyArray[i]->Headers().Length() +
+            aBodyArray[i]->Body().Length()))
+            {
+            // one less boundary
+            bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
+            // skip empty bodypart
+            continue;
+            }
+        bodySize += aBodyArray[i]->Headers().Length();
+    //       ensure there are only 2 CRLFs between header and body
+        if (aBodyArray[i]->Headers().Length() > 0)
+            {
+            TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
+            TUint newEnd = bodyHeaders.Length() - 1;
+            while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
+                {
+                --newEnd;
+                --bodySize;
+                }
+            // two CRLFs
+            bodySize += strlen(Multipart_CRLF_Text);
+            }
+        bodySize += aBodyArray[i]->Body().Length();
+    //       CRLF (end of body, add one only if there is body AND does not end with CRLF)
+        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
+        if (bodyBody.Length() > 0 
+            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
+            {
+            bodySize += strlen(Multipart_CRLF_Text);
+            }
+        }
+    //     end boundary (boundary - '\r\n' + "--")
+    bodySize += boundary->Length();
+    TInt docSize = bodySize; 
+    //     calculate the size of Headers
+    _LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
+    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
+        {
+        docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
+        docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
+        docSize += strlen(Multipart_CRLF_Text); // eol
+        }
+    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
+        {
+        docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
+        // calculate number of chars needed to represent bodySize
+        HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
+        bodySizeSize->Des().Num( bodySize );
+        docSize += bodySizeSize->Length(); // content length (bodySize)
+        docSize += strlen(Multipart_CRLF_Text); // eol
+        CleanupStack::PopAndDestroy( bodySizeSize );
+        }
+    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
+        {
+        docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
+        docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
+        docSize += strlen(Multipart_CRLF_Text); // eol
+        }
+    //    extra CRLF for separating header and body
+    docSize += strlen(Multipart_CRLF_Text);
+    //  CALCULATION COMPLETE
+    //  at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
+    //  and docSize contains the size of the entire document (use it to create HBufC8*
+    //  of appropriate size)
+  
+    //  construct multipart document
+    HBufC8* document = HBufC8::NewLC(docSize);
+    TPtr8 docAppend(document->Des());
+    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
+        {
+        docAppend.Format( KContentType, &aBoundary );
+        }
+    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
+        {
+        _LIT8( KContentLength, "Content-Length: %d\r\n" );
+        docAppend.AppendFormat( KContentLength, bodySize );
+        }
+    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
+        {
+        _LIT8( KLastModified, "Last-Modified: %S\r\n" );
+        TTime current;
+        current.UniversalTime();
+        TInternetDate modDate(current.DateTime());
+        HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
+        docAppend.AppendFormat( KLastModified, dateString );
+        delete dateString;
+        }
+    // required CRLF
+    docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
+    //  BodyParts
+    for (TInt i = 0; i < aBodyArray.Count(); i++)
+        {
+        if (!(aBodyArray[i]->Headers().Length() +
+            aBodyArray[i]->Body().Length()))
+            {
+            // skip empty bodypart
+            continue;
+            }
+        docAppend.Append( *boundary );
+        TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
+        while ( headerLength > 0 &&
+                (aBodyArray[i]->Headers()[headerLength] == '\r'
+                || aBodyArray[i]->Headers()[headerLength] == '\n' ))
+            {
+            --headerLength;
+            }
+        docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
+
+        if ( headerLength > 0 )
+            {
+            docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
+            }
+        else
+            {
+            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
+            }
+    //  body
+        docAppend.Append(aBodyArray[i]->Body());
+    //  CRLF only if body exists and doesn't end with CRLF
+        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
+        if (bodyBody.Length() > 0
+            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
+            {
+            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
+            }
+        }
+    //  end boundary
+    _LIT8(KEndBoundary, "--%S--");
+    docAppend.AppendFormat(KEndBoundary, &aBoundary);
+    CleanupStack::Pop( document );
+    CleanupStack::PopAndDestroy( boundary );
+    return document;
+    }