os/ossrv/lowlevellibsandfws/apputils/multipartparser/src/multipartparser.cpp
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/ossrv/lowlevellibsandfws/apputils/multipartparser/src/multipartparser.cpp	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1646 @@
     1.4 +// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
     1.5 +// All rights reserved.
     1.6 +// This component and the accompanying materials are made available
     1.7 +// under the terms of "Eclipse Public License v1.0"
     1.8 +// which accompanies this distribution, and is available
     1.9 +// at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.10 +//
    1.11 +// Initial Contributors:
    1.12 +// Nokia Corporation - initial contribution.
    1.13 +//
    1.14 +// Contributors:
    1.15 +//
    1.16 +// Description:
    1.17 +//
    1.18 +
    1.19 +// INCLUDE FILES
    1.20 +#include <e32def.h>           // First to avoid NULL redefine warning (no #ifndef NULL).
    1.21 +#include <e32std.h>
    1.22 +#include <string.h>
    1.23 +#include <ezdecompressor.h>   // Ezlib.lib, GZip decoders
    1.24 +#include <uri8.h>
    1.25 +#include <uri16.h>
    1.26 +#include <uricommon.h>
    1.27 +
    1.28 +#include <bafl/multipartparser.h>
    1.29 +#include "gzipbufmgr.h"
    1.30 +#include "tinternetdate.h"
    1.31 +
    1.32 +#include <bafl/qpcodec.h>
    1.33 +#include <bsul/clientmessage.h>
    1.34 +
    1.35 +// CONSTANTS
    1.36 +const char Multipart_Mixed[] = {"multipart/mixed"};
    1.37 +const char Multipart_Related[] = {"multipart/related"};
    1.38 +const char Multipart_Boundary_Text[] = {"boundary="};
    1.39 +const char Multipart_Content_Base_Text[] = {"Content-Base:"};
    1.40 +const char Multipart_Content_Location_Text[] = {"Content-Location:"};
    1.41 +const char Multipart_Content_Type_Text[] = {"Content-Type:"};
    1.42 +const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
    1.43 +// violate RFC2045; but upon customer request
    1.44 +const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
    1.45 +const char Multipart_Content_ID_Text[] = {"Content-ID:"};
    1.46 +const char Multipart_Hypens_Text[] = {"--"};
    1.47 +const char Multipart_CRLF_Text[] = {"\r\n"};
    1.48 +const char Multipart_LF_Text[] = {"\n"};
    1.49 +const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
    1.50 +const char Multipart_DoubleLF_Text[] = {"\n\n"};
    1.51 +const char Multipart_Charset_Text[] = {"charset="};
    1.52 +const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
    1.53 +const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
    1.54 +const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
    1.55 +const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
    1.56 +const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
    1.57 +const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
    1.58 +const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
    1.59 +const char Multipart_Content_Encoding_GZip[] = {"gzip"};
    1.60 +const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
    1.61 +
    1.62 +// MACROS
    1.63 +#define MULTIPART_CONTENT_BASE_LENGTH               13
    1.64 +#define MULTIPART_CONTENT_LOCATION_LENGTH           17
    1.65 +#define MULTIPART_CONTENT_TYPE_LENGTH               13
    1.66 +#define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH  26
    1.67 +// violates RFC2045; but upon vodafone request
    1.68 +#define MULTIPART_CONTENT_ENCODING_LENGTH           17
    1.69 +#define MULTIPART_CONTENT_LENGTH_LENGTH             15
    1.70 +#define MULTIPART_LAST_MODIFIED_LENGTH              14
    1.71 +#define MULTIPART_CONTENT_ID_LENGTH                 11
    1.72 +
    1.73 +#define MULTIPART_CONTENT_BASE               1
    1.74 +#define MULTIPART_CONTENT_LOCATION           2
    1.75 +#define MULTIPART_CONTENT_TRANSFER_ENCODING  3
    1.76 +#define MULTIPART_CONTENT_TYPE               4
    1.77 +#define MULTIPART_CONTENT_ID                 5
    1.78 +
    1.79 +#define MULTIPART_INTERNET_DATE_STRING_LENGTH       29
    1.80 +
    1.81 +#define SLASH_CHAR    '/'
    1.82 +#define DOT_CHAR      '.'
    1.83 +#define AT_CHAR       '@'
    1.84 +#define COLON_CHAR    ':'
    1.85 +// <scheme>://
    1.86 +#define SCHEME_SEPARATOR_LENGTH               3
    1.87 +
    1.88 +
    1.89 +// ============================= LOCAL FUNCTIONS ===============================
    1.90 +
    1.91 +
    1.92 +// ============================ MEMBER FUNCTIONS ===============================
    1.93 +
    1.94 +
    1.95 +// ------------------------------------------------------------------------- 
    1.96 +// Parse and put each body part to the body part array
    1.97 +// ------------------------------------------------------------------------- 
    1.98 +EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody, 
    1.99 +                                       const TDesC8& aContentType,
   1.100 +                                       const TDesC8& aBoundary,
   1.101 +                                       const TDesC16& aUrl,
   1.102 +                                       RPointerArray <CBodyPart>& aBodyPartsArray,
   1.103 +                                       TInt aMaxToParse )
   1.104 +    {
   1.105 +    // check on required parameters
   1.106 +    __ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
   1.107 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.108 +    __ASSERT_ALWAYS( aContentType.Ptr() != NULL,
   1.109 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.110 +    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
   1.111 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.112 +    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
   1.113 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.114 +
   1.115 +    const TUint8* multipartBuffer = aMultipartBody.Ptr();
   1.116 +    TUint32 multipartLen = aMultipartBody.Length();
   1.117 +    __ASSERT_ALWAYS( multipartLen != 0,
   1.118 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.119 +    TUint8* bodyPartBuffer = NULL;
   1.120 +    TUint32 bodyPartBufferLength = 0;
   1.121 +    TUint32 startPosition = 0;
   1.122 +    char* singleEolChar = NULL;
   1.123 +    char* doubleEolChar = NULL;
   1.124 +    CBodyPart* bodyPart = NULL;
   1.125 +
   1.126 +    // currently only support mixed and related.
   1.127 +    char* contentType = (char*)aContentType.Ptr();
   1.128 +    if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 && 
   1.129 +        strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
   1.130 +        {
   1.131 +        User::Leave( KErrNotSupported );
   1.132 +        }
   1.133 +
   1.134 +    // get singleEol and doubleEol
   1.135 +    MultipartParser::SetEolCharacters( multipartBuffer,
   1.136 +                                       multipartLen,
   1.137 +                                       aBoundary,
   1.138 +                                       &singleEolChar,
   1.139 +                                       &doubleEolChar );
   1.140 +
   1.141 +    // get body parts one by one
   1.142 +    // null bodyPartBuffer indicates the end of the multipart body
   1.143 +    int counter = 0;
   1.144 +    do
   1.145 +        {
   1.146 +        // stop when we get required number of parse done
   1.147 +        if( aMaxToParse != -1 && counter >= aMaxToParse )
   1.148 +            {
   1.149 +            break;
   1.150 +            }
   1.151 +        // update counter
   1.152 +        counter++;
   1.153 +
   1.154 +        // get the next body part
   1.155 +        bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition, 
   1.156 +                                                                       multipartBuffer,
   1.157 +                                                                       multipartLen,
   1.158 +                                                                       aBoundary,
   1.159 +                                                                       singleEolChar,
   1.160 +                                                                       &bodyPartBuffer );
   1.161 +        // break if we are at end
   1.162 +        if( bodyPartBuffer == NULL )
   1.163 +            {
   1.164 +            break;
   1.165 +            }
   1.166 +        // update start position
   1.167 +        startPosition += bodyPartBufferLength;
   1.168 +
   1.169 +        // create new body part
   1.170 +        bodyPart = CBodyPart::NewL();
   1.171 +        // parse each body part buffer to fill in body part
   1.172 +        MultipartParser::ParseBodyPartL( bodyPartBuffer, 
   1.173 +                                         bodyPartBufferLength,
   1.174 +                                         singleEolChar,
   1.175 +                                         doubleEolChar,
   1.176 +                                         aUrl, 
   1.177 +                                         bodyPart );  
   1.178 +        // add the body part to the array
   1.179 +        aBodyPartsArray.Append( bodyPart );
   1.180 +        }
   1.181 +    while( bodyPartBuffer != NULL );
   1.182 +    }
   1.183 +
   1.184 +// ------------------------------------------------------------------------- 
   1.185 +// Composes RFC1521 compliant multipart document with given bodyparts
   1.186 +// Actual task of creating the document is delegated to specialized composer
   1.187 +// for each of the subtypes
   1.188 +// ------------------------------------------------------------------------- 
   1.189 +EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
   1.190 +                                            const TDesC8& aBoundary,
   1.191 +                                            TMultipartSubtype aSubtype,
   1.192 +                                            TInt aHeaderMask )
   1.193 +    {
   1.194 +    // check on required parameters
   1.195 +    if ( !aBoundary.Ptr() || !aBoundary.Length() )
   1.196 +        {
   1.197 +        User::Leave( KErrArgument );
   1.198 +        }
   1.199 +    
   1.200 +    HBufC8* multipartDoc = NULL;
   1.201 +    switch(aSubtype)
   1.202 +        {
   1.203 +        case EMultipartSubtypeMixed:
   1.204 +            {
   1.205 +            multipartDoc = ComposeMixedL( aBodyPartsArray,
   1.206 +                                          aBoundary,
   1.207 +                                          aHeaderMask );
   1.208 +            }
   1.209 +        break;
   1.210 +        default:
   1.211 +            {
   1.212 +            User::Leave( KErrArgument );
   1.213 +            }
   1.214 +        }
   1.215 +    return multipartDoc;
   1.216 +    }
   1.217 +
   1.218 +// ------------------------------------------------------------------------- 
   1.219 +// Default constructor
   1.220 +// ------------------------------------------------------------------------- 
   1.221 +MultipartParser::MultipartParser()
   1.222 +	{
   1.223 +	}
   1.224 +
   1.225 +// ------------------------------------------------------------------------- 
   1.226 +// Returns with the next body part buffer from start position (offset)
   1.227 +// ------------------------------------------------------------------------- 
   1.228 +TUint32
   1.229 +MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition, 
   1.230 +                                        const TUint8* aMultipartBody,
   1.231 +                                        TUint32 aMultipartLen,
   1.232 +                                        const TDesC8& aBoundary,
   1.233 +                                        char* aSingleEolChar,
   1.234 +                                        TUint8** aBodyPartBuffer )
   1.235 +    {
   1.236 +    // check on required parameters
   1.237 +    __ASSERT_ALWAYS( aMultipartBody != NULL,
   1.238 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.239 +    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
   1.240 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.241 +    __ASSERT_ALWAYS( aSingleEolChar != NULL,
   1.242 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.243 +
   1.244 +    long startOffset = -1;
   1.245 +    long endOffset = -1;
   1.246 +    TUint32 length = 0;
   1.247 +    TBool badContent = EFalse;
   1.248 +    TUint32 offset = 0;
   1.249 +    const char* boundaryStr = (const char*) aBoundary.Ptr();
   1.250 +    int boundaryLength = aBoundary.Length();
   1.251 +    int hypensLength = strlen( Multipart_Hypens_Text );
   1.252 +    int singleEolLength = strlen( aSingleEolChar );
   1.253 +    TUint32 i = aStartPosition;
   1.254 +
   1.255 +    // from RFC 1341 7.2
   1.256 +    // Overall, the body of a multipart entity may be specified as follows:
   1.257 +    // multipart-body := preamble 1*encapsulation close-delimiter epilogue
   1.258 +    // encapsulation := delimiter CRLF body-part
   1.259 +    // delimiter := CRLF "--" boundary   ; taken from Content-Type field.
   1.260 +    // when content-type is multipart.
   1.261 +    // There must be no space between "--" and boundary.
   1.262 +    // close-delimiter := delimiter "--" ; Again, no  space  before
   1.263 +    // "--"
   1.264 +
   1.265 +    // boundary = 12xy
   1.266 +    // body: here comes some text that we ignore
   1.267 +    //       --12xy
   1.268 +    //       first body
   1.269 +    //       --12xy
   1.270 +    //       second body
   1.271 +    //       --12xy--
   1.272 +    //       closing boundary. we ignore this text here
   1.273 +    while( i < (aMultipartLen - hypensLength + 1 ) )
   1.274 +        {
   1.275 +        // get the first two hypens
   1.276 +        // using char comparison to compare "--"
   1.277 +        // hopefully this is faster
   1.278 +        if( (char)aMultipartBody[ i ] == '-' && 
   1.279 +            (char)aMultipartBody[ i+1 ] == '-' )
   1.280 +          {
   1.281 +          char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
   1.282 +          // check if the body is long enough first and then check if boundary matches
   1.283 +          if( aMultipartLen >= i + hypensLength + boundaryLength )
   1.284 +            {
   1.285 +            if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
   1.286 +              {
   1.287 +              // we've got the boundary
   1.288 +              offset = i + hypensLength + boundaryLength;
   1.289 +              // Next two chars must be either two hypens (closing boundary - 2 bytes),
   1.290 +              // or single Eol characters (new body).
   1.291 +              // Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
   1.292 +              char* eolBuf = (char*)&aMultipartBody[ offset ];
   1.293 +              // Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
   1.294 +              if( aMultipartLen >= offset + hypensLength )
   1.295 +                {
   1.296 +                 if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0|| 
   1.297 +                     eolBuf[0] == Multipart_LF_Text[0])
   1.298 +                   
   1.299 +                  {
   1.300 +                  // We found Eol, so this is a new multipart body (header and content)
   1.301 +                  if( startOffset == -1 )
   1.302 +                    {
   1.303 +                    // this is the beginning.
   1.304 +                    startOffset = offset;
   1.305 +                    // let's looking for the end of this body part which either could
   1.306 +                    // be closing boundary or an opening boundary for the next body part
   1.307 +
   1.308 +                    // jump over the boundary information
   1.309 +                    i = startOffset + singleEolLength;
   1.310 +                    }
   1.311 +                  else
   1.312 +                    {
   1.313 +                    // We found the next boundary marker, so this is the
   1.314 +                    // beginning of the next body part
   1.315 +                    endOffset = offset - boundaryLength - hypensLength;
   1.316 +                    // we've got both start and end offset
   1.317 +                    break;
   1.318 +                    }
   1.319 +                  }
   1.320 +                 else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
   1.321 +                   {
   1.322 +                    // We found the closing boundary marker
   1.323 +                    endOffset = offset - boundaryLength - hypensLength;
   1.324 +                    break;
   1.325 +                   }
   1.326 +                 else
   1.327 +                   {
   1.328 +                   // it's neither Eol nor two hypens "--"
   1.329 +                   badContent = ETrue;
   1.330 +                   break;
   1.331 +                   }
   1.332 +                }
   1.333 +              else
   1.334 +                {
   1.335 +                // the buffer is too short and not closed properly
   1.336 +                endOffset = i;
   1.337 +                break;
   1.338 +                }
   1.339 +              }
   1.340 +            }
   1.341 +          else
   1.342 +            {
   1.343 +            // the buffer is far too short
   1.344 +            endOffset = i;
   1.345 +            break;
   1.346 +            }
   1.347 +          }
   1.348 +        i++;
   1.349 +        } // end of while loop
   1.350 +
   1.351 +    // missing closing boundary check
   1.352 +    if( endOffset == -1 )
   1.353 +        {
   1.354 +        // take the end of the body as closing boundary
   1.355 +        endOffset = i - 1;
   1.356 +        }
   1.357 +
   1.358 +    if( badContent )
   1.359 +        {
   1.360 +        endOffset = -1;
   1.361 +        startOffset = -1;
   1.362 +        }
   1.363 +
   1.364 +    if( startOffset != -1 && endOffset != -1 )
   1.365 +        {
   1.366 +        *aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
   1.367 +        length = endOffset - startOffset;
   1.368 +        }
   1.369 +    else
   1.370 +        {
   1.371 +        *aBodyPartBuffer = NULL;
   1.372 +        length = 0;
   1.373 +        }
   1.374 +
   1.375 +    return length;
   1.376 +    }
   1.377 +
   1.378 +
   1.379 +// ------------------------------------------------------------------------- 
   1.380 +// Set End-Of-Line characters.  Look at the eol character after the boundary to
   1.381 +// determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
   1.382 +// unix/mac).
   1.383 +
   1.384 +// NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
   1.385 +
   1.386 +// Multipart entity is specified as follows:
   1.387 +// --boundary123[eol]
   1.388 +// Content-type: text/html[eol]
   1.389 +// Content-location: http:\\www.example.com\index.html[eol]
   1.390 +// [eol]
   1.391 +// <html><body>...example...</html>[eol]
   1.392 +// --boundary123--[eol]
   1.393 +// ------------------------------------------------------------------------- 
   1.394 +void
   1.395 +MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
   1.396 +                                   TUint32 aMultipartLen,
   1.397 +                                   const TDesC8& aBoundary,
   1.398 +                                   char** aSingleEolChar,
   1.399 +                                   char** aDoubleEolChar )
   1.400 +    {
   1.401 +    // check on required parameters
   1.402 +    __ASSERT_ALWAYS( aMultipartBody != NULL,
   1.403 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.404 +    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
   1.405 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.406 +    __ASSERT_ALWAYS( aSingleEolChar != NULL,
   1.407 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.408 +    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
   1.409 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.410 +
   1.411 +    TUint32 i = 0;
   1.412 +    const char* boundaryStr = (const char*) aBoundary.Ptr();
   1.413 +    int boundaryLength = aBoundary.Length();
   1.414 +    int hypensLength = strlen( Multipart_Hypens_Text );
   1.415 +    int lfLength = strlen( Multipart_LF_Text );
   1.416 +
   1.417 +    // Set the default eol (CRLF)
   1.418 +    *aSingleEolChar = (char *)Multipart_CRLF_Text;
   1.419 +    *aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
   1.420 +
   1.421 +    while (i < (aMultipartLen - hypensLength + 1))
   1.422 +        {
   1.423 +        // Get the first two hypens
   1.424 +        char* bodyPart = (char*)&aMultipartBody[ i ];
   1.425 +        if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
   1.426 +            {
   1.427 +            char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
   1.428 +            // Check if the body is long enough first and then check if boundary matches
   1.429 +            if (aMultipartLen >= i + hypensLength + boundaryLength )
   1.430 +                {
   1.431 +                if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
   1.432 +                    {
   1.433 +                    // We've got the boundary
   1.434 +                    i = i + hypensLength + boundaryLength;
   1.435 +                    // Next two chars should be the single Eol characters.
   1.436 +                    // Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
   1.437 +                    char* eolBuf = (char*)&aMultipartBody[ i ];
   1.438 +                    // Check if buffer is long enough for eol [1 byte LF]
   1.439 +                    if (aMultipartLen >= i + lfLength)
   1.440 +                        {
   1.441 +                        if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
   1.442 +                            {
   1.443 +                            // We found LF Eol (unix/mac)
   1.444 +                            *aSingleEolChar = (char *)Multipart_LF_Text;
   1.445 +                            *aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
   1.446 +                            } // end of if compare eol to LF
   1.447 +                        } // end of if buffer size ok
   1.448 +
   1.449 +                    // Break in all cases, we will use the default CRLF if we don't
   1.450 +                    // find eol=LF, or the remaining buffer is too small
   1.451 +                    break;
   1.452 +                    } // end of compare/found boundary
   1.453 +                }
   1.454 +            } // end of looking for first two hypens
   1.455 +
   1.456 +        ++i;
   1.457 +        } // end of while
   1.458 +    }
   1.459 +
   1.460 +
   1.461 +// ------------------------------------------------------------------------- 
   1.462 +// parse body
   1.463 +// The bodyPart parameter can contain the optional headers and response, or
   1.464 +// just the response.  In the case of both the (optional) header(s) and the
   1.465 +// response, let's cut off header(s) and return the response body.  The
   1.466 +// header is seperated from the response by two End-of-line (Eol) characters,
   1.467 +// i.e. two CRLF's (windows) or two LF's (unix/mac).
   1.468 +// --boundary123 (omitted from bodyPart parameter, starts next line)
   1.469 +// Content-type: text/html[eol]
   1.470 +// Content-location: http:\\www.example.com\index.html[eol]
   1.471 +// [eol]
   1.472 +// <html><body>example</body></html>
   1.473 +//
   1.474 +// In the case of no headers, there may be only one (or more) Eol characters.
   1.475 +// --boundary123 (omitted from bodyPart parameter, starts on next line)
   1.476 +// [eol]
   1.477 +// <html><body>example</body></html>
   1.478 +// ------------------------------------------------------------------------- 
   1.479 +void
   1.480 +MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
   1.481 +                                 TUint32 aBodyPartBufferLength,
   1.482 +                                 char* aSingleEolChar,
   1.483 +                                 char* aDoubleEolChar,
   1.484 +                                 const TDesC16& aResponseUrl,
   1.485 +                                 CBodyPart* aBodyPart )
   1.486 +    {
   1.487 +    // check on required parameters
   1.488 +    __ASSERT_ALWAYS( aBodyPartBuffer != NULL,
   1.489 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.490 +    __ASSERT_ALWAYS( aSingleEolChar != NULL,
   1.491 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.492 +    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
   1.493 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.494 +    __ASSERT_ALWAYS( aBodyPart != NULL,
   1.495 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.496 +    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
   1.497 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.498 +
   1.499 +    // headers look something like this
   1.500 +    // we need to return "text/html" if the requested header is "Content-type"
   1.501 +    // --boundary123
   1.502 +    // Content-type: text/html
   1.503 +    // Content-location: http:\\www.example.com\index.html
   1.504 +    //
   1.505 +    // <html>
   1.506 +    // <body>
   1.507 +    // example
   1.508 +    int contentHeaderValueCharLen = 0;
   1.509 +    TPtrC8 contentHeaderValuePtr( NULL, 0 );
   1.510 +    int contentHeaderNameLength = 0;
   1.511 +
   1.512 +    /*lint -e{668} Possibly passing a null pointer to function */
   1.513 +    int singleEolLength = strlen( aSingleEolChar );
   1.514 +    int doubleEolLength = strlen( aDoubleEolChar );
   1.515 +    // start looking for the header name
   1.516 +    for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
   1.517 +        {
   1.518 +        int found = 0;
   1.519 +        const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
   1.520 +
   1.521 +        // Did we find the Content Header Value
   1.522 +	    if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
   1.523 +            {
   1.524 +            contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
   1.525 +            found = MULTIPART_CONTENT_BASE;
   1.526 +            }
   1.527 +	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
   1.528 +            {
   1.529 +            contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
   1.530 +            found = MULTIPART_CONTENT_LOCATION;
   1.531 +            }
   1.532 +	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
   1.533 +            {
   1.534 +            contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
   1.535 +            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
   1.536 +            }     
   1.537 +	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
   1.538 +            {
   1.539 +            contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
   1.540 +            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
   1.541 +            }
   1.542 +	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
   1.543 +            {
   1.544 +            contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
   1.545 +            found = MULTIPART_CONTENT_TYPE;
   1.546 +            }
   1.547 +	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
   1.548 +            {
   1.549 +            contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
   1.550 +            found = MULTIPART_CONTENT_ID;
   1.551 +            }
   1.552 +
   1.553 +        if (found)
   1.554 +            {        
   1.555 +            // skip spaces
   1.556 +            int startPos = i + contentHeaderNameLength;
   1.557 +            while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
   1.558 +                {
   1.559 +                startPos++;
   1.560 +                }
   1.561 +
   1.562 +            // used for finding '<' in Content-ID field
   1.563 +            char charFirst = aBodyPartBuffer[ startPos ];
   1.564 +            // content headers are closed with End-Of-Line (Eol) character
   1.565 +            for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
   1.566 +                {
   1.567 +                char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
   1.568 +                if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0 
   1.569 +                   || tmpContentHeaderValue[0] == Multipart_LF_Text[0])
   1.570 +                    {
   1.571 +                    if( found == MULTIPART_CONTENT_ID )
   1.572 +                        {
   1.573 +                        if( charFirst == '<' )
   1.574 +                            {
   1.575 +                            // length of the value excluding beginging '<' and ending '>'
   1.576 +                            contentHeaderValueCharLen = j - startPos - 2;
   1.577 +                            contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
   1.578 +                            }
   1.579 +                        }
   1.580 +                    else
   1.581 +                        {
   1.582 +                        // length of the value
   1.583 +                        contentHeaderValueCharLen = j - startPos;
   1.584 +                        contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
   1.585 +                        }
   1.586 +
   1.587 +                    // rewind so the double EOL will be checked against later
   1.588 +                    i = j - 1;
   1.589 +                    // break the inner loop
   1.590 +                    break;
   1.591 +                    }              
   1.592 +                } // end of inner for loop
   1.593 +
   1.594 +            switch( found )
   1.595 +                {
   1.596 +                case MULTIPART_CONTENT_BASE:
   1.597 +                    aBodyPart->SetContentBase( contentHeaderValuePtr );
   1.598 +                    break;
   1.599 +                case MULTIPART_CONTENT_LOCATION:
   1.600 +                    aBodyPart->SetContentLocation( contentHeaderValuePtr );
   1.601 +                    break;
   1.602 +                case MULTIPART_CONTENT_TRANSFER_ENCODING:
   1.603 +                    aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
   1.604 +                    break;
   1.605 +                case MULTIPART_CONTENT_TYPE:
   1.606 +                    aBodyPart->SetContentType( contentHeaderValuePtr );
   1.607 +                    break;
   1.608 +                case MULTIPART_CONTENT_ID:
   1.609 +                    aBodyPart->SetContentID( contentHeaderValuePtr );
   1.610 +                    break;
   1.611 +                default:
   1.612 +                    break;
   1.613 +                } 
   1.614 +            } // end of if (found) 
   1.615 +
   1.616 +        // Did we get to the end of the Content Header. Many of the Content Header Values
   1.617 +	    // are optional, so we could get to the end of the Content Header (double Eol) and
   1.618 +	    // not find the Content Header Value we were searching for.
   1.619 +        // get the response body
   1.620 +	    int aEolLength = strlen(Multipart_DoubleLF_Text);
   1.621 +	    if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
   1.622 +	        strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
   1.623 +            {
   1.624 +            if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
   1.625 +               aEolLength = doubleEolLength;
   1.626 +            // set body
   1.627 +            TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
   1.628 +            int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
   1.629 +            TPtrC8 body( responseBody, lengthBody );
   1.630 +            aBodyPart->SetBody( body );
   1.631 +
   1.632 +            // set headers when we have headers
   1.633 +            if( i != 0 )
   1.634 +                {
   1.635 +                // jump over the starting single EOL
   1.636 +                TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
   1.637 +                // // jump over the starting single EOL and the ending double EOL
   1.638 +                int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
   1.639 +                TPtrC8 headers( responseHeaders, lengthHeaders );
   1.640 +                aBodyPart->SetHeaders( headers );
   1.641 +                }
   1.642 +
   1.643 +            break;
   1.644 +            }
   1.645 +       } // end of outter for loop
   1.646 +
   1.647 +    // prepare more on body part
   1.648 +
   1.649 +    // Check to see if we have a Content-Transfer-Encoding.
   1.650 +    TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
   1.651 +    // If we have Content-Transfer-Encoding, prepare to decode
   1.652 +    if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
   1.653 +        {
   1.654 +        // Initialize the encoded body, input
   1.655 +        TPtrC8 encodedBody( aBodyPart->Body() );
   1.656 +
   1.657 +        // This will contain the "decoded" data.
   1.658 +        // The memory (decodedBody.Ptr) is owned by this method, but allocated by
   1.659 +        // the DecodeContentTransferEncoding method.
   1.660 +        TPtr8 decodedBody( NULL, 0, 0 );
   1.661 +
   1.662 +        // The decoded data will return in decodedBody.Ptr.
   1.663 +        // The memory allocated is owned by this method.
   1.664 +        TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
   1.665 +                                                                   encodedBody,
   1.666 +                                                                   decodedBody );                    
   1.667 +        User::LeaveIfError(err);
   1.668 +
   1.669 +        // The responseBody pointer is an offset into the response
   1.670 +        // buffer, do not delete. Substitute the decodedBody pointer.
   1.671 +        aBodyPart->SetBody( decodedBody );
   1.672 +        aBodyPart->SetIsDecodedBody( ETrue );
   1.673 +        }   // end of if (contentTransferEncodingValue)
   1.674 +
   1.675 +    // Check to see if we have a Content-Type.
   1.676 +    TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
   1.677 +    // parse contentType to get new contentType, charset, boundary info
   1.678 +    if( contentTypeValue )
   1.679 +        {
   1.680 +        MultipartParser::CutOffContentTypeAttributes( aBodyPart );
   1.681 +        // updated content type
   1.682 +        contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
   1.683 +        }
   1.684 +
   1.685 +    // If we have zipped Content-Type, prepare to unzip
   1.686 +    if( MultipartParser::IsZipped(contentTypeValue) )
   1.687 +        {
   1.688 +        // Initialize the zipped body, input
   1.689 +        TPtrC8 zippedBody( aBodyPart->Body() );
   1.690 +
   1.691 +        // This will contain the "unzipped" data.
   1.692 +        // The memory (unzippedBody.Ptr) is owned by this method, but allocated by
   1.693 +        // the Unzip method.
   1.694 +        TPtr8 unzippedBody( NULL, 0, 0 );
   1.695 +
   1.696 +        // The unzipped data will return in unzippedBody.Ptr.
   1.697 +        // The memory allocated is owned by this method.
   1.698 +        TInt err = MultipartParser::Unzip( contentTypeValue,
   1.699 +                                           zippedBody,
   1.700 +                                           unzippedBody );                    
   1.701 +        User::LeaveIfError(err);
   1.702 +
   1.703 +        if( aBodyPart->IsDecodedBody() )
   1.704 +            {
   1.705 +            // old body is not the original buffer, delete it
   1.706 +            delete (TUint8*) aBodyPart->Body().Ptr();
   1.707 +            }
   1.708 +        // unzip happend, use unzippedBody; delete decodedBody
   1.709 +        else
   1.710 +            {
   1.711 +            // The responseBody pointer is an offset into the response
   1.712 +            // buffer, do not delete. Substitute the decodedBody pointer.
   1.713 +
   1.714 +            aBodyPart->SetIsDecodedBody( ETrue );
   1.715 +            }
   1.716 +
   1.717 +        aBodyPart->SetBody( unzippedBody );
   1.718 +        }
   1.719 +
   1.720 +    // Get the url of the current body part
   1.721 +    HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
   1.722 +                                                             aBodyPart->ContentLocation(),
   1.723 +                                                             aResponseUrl );
   1.724 +    aBodyPart->SetUrl( responseUrl );
   1.725 +    }
   1.726 +
   1.727 +
   1.728 +// ------------------------------------------------------------------------- 
   1.729 +// From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
   1.730 +//   Section 6.2.  Content-Transfer-Encodings Semantics
   1.731 +//
   1.732 +//   The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
   1.733 +//   mean that the identity (i.e. NO) encoding transformation has been
   1.734 +//   performed.  As such, they serve simply as indicators of the domain of
   1.735 +//   the body data, and provide useful information about the sort of
   1.736 +//   encoding that might be needed for transmission in a given transport
   1.737 +//   system.  The terms "7bit data", "8bit data", and "binary data" are
   1.738 +//   all defined in Section 2.
   1.739 +
   1.740 +//  Returns true if contentTransferEncodingValue is neither NULL nor a domain.
   1.741 +// ------------------------------------------------------------------------- 
   1.742 +TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
   1.743 +    {        
   1.744 +    if( !aContentTransferEncodingValue )
   1.745 +        {
   1.746 +        return EFalse;
   1.747 +        }
   1.748 +
   1.749 +    char* encoding = (char*)aContentTransferEncodingValue;
   1.750 +
   1.751 +    if ( strncasecmp( encoding,
   1.752 +                      Multipart_Content_Transfer_Encoding_7bit,
   1.753 +                      strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
   1.754 +        {
   1.755 +        return EFalse;
   1.756 +        }
   1.757 +
   1.758 +    if ( strncasecmp( encoding,
   1.759 +                      Multipart_Content_Transfer_Encoding_8bit,
   1.760 +                      strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
   1.761 +        {
   1.762 +        return EFalse;
   1.763 +        }
   1.764 +
   1.765 +    if ( strncasecmp( encoding,
   1.766 +                      Multipart_Content_Transfer_Encoding_binary,
   1.767 +                      strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
   1.768 +        {
   1.769 +        return EFalse;
   1.770 +        }      
   1.771 +
   1.772 +    return ETrue;
   1.773 +    }
   1.774 +
   1.775 +
   1.776 +// ----------------------------------------------------------------------------
   1.777 +// DecodeContentTransferEncoding
   1.778 +//
   1.779 +// Decodes the Content-Transfer-Encoding.  The returned length of decodedBody
   1.780 +// is zero if decoding failed.
   1.781 +// NOTES:
   1.782 +// 1. This method should be called with a non-null string, i.e.
   1.783 +// aContentTransferEncodingValue.
   1.784 +// 2. Memory is allocated in this method, but ownership is with the calling method.
   1.785 +// ----------------------------------------------------------------------------
   1.786 +TInt
   1.787 +MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
   1.788 +                                                const TDesC8& aEncodedBody,
   1.789 +                                                TPtr8& aDecodedBody )
   1.790 +    {
   1.791 +    // check on required parameters
   1.792 +    __ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
   1.793 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.794 +    __ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
   1.795 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.796 +
   1.797 +    TInt status = KErrNone;
   1.798 +    char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
   1.799 +
   1.800 +    // Set the decodedBody.Length to zero, length > 0 if successful decode
   1.801 +    aDecodedBody.SetLength(0);
   1.802 +
   1.803 +    // Is the Content-Transfer-Encoding = "base64"
   1.804 +    if( strncasecmp( contentTransferEncodingString,
   1.805 +                     Multipart_Content_Transfer_Encoding_Base64, 
   1.806 +                     strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
   1.807 +        {
   1.808 +        // The decoded length of base64 is about half (use same) encoded length
   1.809 +        TUint maxBodyLength = aEncodedBody.Length();
   1.810 +        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
   1.811 +        if( decodedDataPtr )
   1.812 +            {
   1.813 +            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
   1.814 +
   1.815 +			using namespace BSUL;
   1.816 +            // Decode the base64 Content-Transfer-Encoding
   1.817 +            Base64Codec::Decode(aEncodedBody, aDecodedBody);
   1.818 +
   1.819 +            if (aDecodedBody.Length() == 0)
   1.820 +                {
   1.821 +                status = KErrGeneral;
   1.822 +                }
   1.823 +            }
   1.824 +        else // decodedDataPtr is NULL
   1.825 +            {
   1.826 +            status = KErrNoMemory;
   1.827 +            }
   1.828 +        }   // end of base64 decoding
   1.829 +
   1.830 +    // Is the Content-Transfer-Encoding = "quoted-printable"
   1.831 +    else if( strncasecmp( contentTransferEncodingString,
   1.832 +                          Multipart_Content_Transfer_Encoding_QuotedPrintable, 
   1.833 +                          strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
   1.834 +        {
   1.835 +        // The decoded length of QP is the same as the encoded length
   1.836 +        TUint maxBodyLength = aEncodedBody.Length();
   1.837 +        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
   1.838 +        if( decodedDataPtr )
   1.839 +            {
   1.840 +            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
   1.841 +
   1.842 +            // Decode the quoted-printable Content-Transfer-Encoding
   1.843 +            QuotedPrintableCodec::Decode(aEncodedBody, aDecodedBody);
   1.844 +
   1.845 +            if (aDecodedBody.Length() == 0)
   1.846 +                {
   1.847 +                status = KErrGeneral;
   1.848 +                }
   1.849 +            }
   1.850 +        else // decodedDataPtr is NULL
   1.851 +            {
   1.852 +            status = KErrNoMemory;
   1.853 +            }
   1.854 +        }   // end of quoted-printed decoding
   1.855 +
   1.856 +    // Is the Content-Encoding = "gzip"
   1.857 +    else if( strncasecmp( contentTransferEncodingString,
   1.858 +                          Multipart_Content_Encoding_GZip, 
   1.859 +                          strlen(Multipart_Content_Encoding_GZip) ) == 0 )
   1.860 +        {
   1.861 +        // Our GZip decoder parts
   1.862 +        GZipBufMgr* gZipBufMgr = NULL;
   1.863 +        CEZDecompressor* ezDecompressor = NULL;
   1.864 +
   1.865 +        // We have gzip, lets decompress the encoded data.
   1.866 +        // Set up the encoded data into a GZip buffer manager.
   1.867 +        TInt err = 0;
   1.868 +        TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
   1.869 +
   1.870 +        // Get the GZip decompressor
   1.871 +        if( gZipBufMgr )
   1.872 +            {
   1.873 +            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
   1.874 +
   1.875 +            // Inflate the GZip data
   1.876 +            if( ezDecompressor )
   1.877 +                {
   1.878 +                TRAP(err, ezDecompressor->InflateL());
   1.879 +                // Set the finalize flag
   1.880 +                if (err == KErrNone)
   1.881 +                    {
   1.882 +                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
   1.883 +                    // Get the inflated data, it is much larger then the encoded data
   1.884 +                    if (err == KErrNone)
   1.885 +                        {
   1.886 +                        TPtrC8 output = ezDecompressor->OutputDescriptor();
   1.887 +                        if (output.Length() != 0)
   1.888 +                            {
   1.889 +                            TInt size = output.Size();
   1.890 +                            TUint8* outBuf = new TUint8[size];
   1.891 +                            if( outBuf )
   1.892 +                                {
   1.893 +                                memcpy(outBuf, output.Ptr(), size);
   1.894 +
   1.895 +                                aDecodedBody.Set((TUint8*)outBuf, size, size);
   1.896 +                                }
   1.897 +                            else // outBuf is NULL
   1.898 +                                {
   1.899 +                                status = KErrNoMemory;
   1.900 +                                }
   1.901 +                            }
   1.902 +                        else
   1.903 +                            {
   1.904 +                            status = KErrGeneral;
   1.905 +                            }
   1.906 +                        }
   1.907 +                    else
   1.908 +                        {
   1.909 +                        status = KErrGeneral;
   1.910 +                        }
   1.911 +                    }
   1.912 +                else
   1.913 +                    {
   1.914 +                    status = KErrGeneral;
   1.915 +                    }
   1.916 +                }
   1.917 +            else // ezDecompressor is NULL
   1.918 +                {
   1.919 +                status = KErrNoMemory;
   1.920 +                }
   1.921 +            }
   1.922 +        else // gZipBufMgr is NULL
   1.923 +            {
   1.924 +            status = KErrNoMemory;
   1.925 +            }
   1.926 +
   1.927 +        // Clean up our memory
   1.928 +        delete gZipBufMgr;
   1.929 +        delete ezDecompressor;
   1.930 +        }   // end of gzip 
   1.931 +
   1.932 +    // We can add additional decodings here.
   1.933 +    // When adding additional decoding be aware of the decodedBody.Ptr()
   1.934 +    // max size. Do the realloc here, AND allow the decodedBody.Ptr()
   1.935 +    // ownership to be passed back to calling method.
   1.936 +
   1.937 +    return status;
   1.938 +    }
   1.939 +
   1.940 +    
   1.941 +// ------------------------------------------------------------------------- 
   1.942 +// only support application/x-gzip
   1.943 +// ------------------------------------------------------------------------- 
   1.944 +TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
   1.945 +    {        
   1.946 +    if( !aContentTypeValue )
   1.947 +        {
   1.948 +        return EFalse;
   1.949 +        }
   1.950 +
   1.951 +    char* contentType = (char*)aContentTypeValue;
   1.952 +
   1.953 +    if ( strncasecmp( contentType,
   1.954 +                      Multipart_Content_Type_GZip,
   1.955 +                      strlen(Multipart_Content_Type_GZip) ) == 0 )
   1.956 +        {
   1.957 +        return ETrue;
   1.958 +        }
   1.959 +
   1.960 +    return EFalse;
   1.961 +    }
   1.962 +
   1.963 +
   1.964 +// ----------------------------------------------------------------------------
   1.965 +// Unzip
   1.966 +//
   1.967 +// Unzip the .gz.  The returned length of unzippedBody
   1.968 +// is zero if unzip failed.
   1.969 +// NOTES:
   1.970 +// 1. This method should be called with a non-null string, i.e.
   1.971 +// aContentType.
   1.972 +// 2. Memory is allocated in this method, but ownership is with the calling method.
   1.973 +// ----------------------------------------------------------------------------
   1.974 +TInt
   1.975 +MultipartParser::Unzip( TUint8* aContentType,
   1.976 +                        const TDesC8& aZippedBody,
   1.977 +                        TPtr8& aUnzippedBody )
   1.978 +    {
   1.979 +    // check on required parameters
   1.980 +    __ASSERT_ALWAYS( aContentType != NULL,
   1.981 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.982 +    __ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
   1.983 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
   1.984 +
   1.985 +    TInt status = KErrNone;
   1.986 +    char* contentTypeStr = (char*)aContentType;
   1.987 +
   1.988 +    // Set the aUnzippedBody.Length to zero, length > 0 if successful decode
   1.989 +    aUnzippedBody.SetLength(0);
   1.990 +
   1.991 +    // Our GZip decoder parts
   1.992 +    GZipBufMgr* gZipBufMgr = NULL;
   1.993 +    CEZDecompressor* ezDecompressor = NULL;
   1.994 +
   1.995 +    // Is the Content-Type = "application/x-gzip"
   1.996 +    if( strncasecmp( contentTypeStr,
   1.997 +                     Multipart_Content_Type_GZip, 
   1.998 +                     strlen(Multipart_Content_Type_GZip) ) == 0 )
   1.999 +        {
  1.1000 +        // We have gzip, lets decompress the encoded data.
  1.1001 +        // Set up the encoded data into a GZip buffer manager.
  1.1002 +        TInt err = 0;
  1.1003 +        TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
  1.1004 +
  1.1005 +        // Get the GZip decompressor
  1.1006 +        if( gZipBufMgr )
  1.1007 +            {
  1.1008 +            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
  1.1009 +
  1.1010 +            // Inflate the GZip data
  1.1011 +            if( ezDecompressor )
  1.1012 +                {
  1.1013 +                TRAP(err, ezDecompressor->InflateL());
  1.1014 +                // Set the finalize flag
  1.1015 +                if (err == KErrNone)
  1.1016 +                    {
  1.1017 +                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
  1.1018 +                    // Get the inflated data, it is much larger then the encoded data
  1.1019 +                    if (err == KErrNone)
  1.1020 +                        {
  1.1021 +                        TPtrC8 output = ezDecompressor->OutputDescriptor();
  1.1022 +                        if (output.Length() != 0)
  1.1023 +                            {
  1.1024 +                            TInt size = output.Size();
  1.1025 +                            TUint8* outBuf = new TUint8[size];
  1.1026 +                            if( outBuf )
  1.1027 +                                {
  1.1028 +                                memcpy(outBuf, output.Ptr(), size);
  1.1029 +
  1.1030 +                                aUnzippedBody.Set((TUint8*)outBuf, size, size);
  1.1031 +                                }
  1.1032 +                            else // outBuf is NULL
  1.1033 +                                {
  1.1034 +                                status = KErrNoMemory;
  1.1035 +                                }
  1.1036 +                            }
  1.1037 +                        else
  1.1038 +                            {
  1.1039 +                            status = KErrGeneral;
  1.1040 +                            }
  1.1041 +                        }
  1.1042 +                    else
  1.1043 +                        {
  1.1044 +                        status = KErrGeneral;
  1.1045 +                        }
  1.1046 +                    }
  1.1047 +                else
  1.1048 +                    {
  1.1049 +                    status = KErrGeneral;
  1.1050 +                    }
  1.1051 +                }
  1.1052 +            else // ezDecompressor is NULL
  1.1053 +                {
  1.1054 +                status = KErrNoMemory;
  1.1055 +                }
  1.1056 +            }
  1.1057 +        else // gZipBufMgr is NULL
  1.1058 +            {
  1.1059 +            status = KErrNoMemory;
  1.1060 +            }
  1.1061 +        }   // end of gzip 
  1.1062 +
  1.1063 +    // Clean up our memory
  1.1064 +    delete gZipBufMgr;
  1.1065 +    delete ezDecompressor;
  1.1066 +
  1.1067 +    return status;
  1.1068 +    }
  1.1069 +
  1.1070 +
  1.1071 +// ----------------------------------------------------------------------------
  1.1072 +// It cuts off the charset value from the content type header
  1.1073 +// content type string looks like as follows:
  1.1074 +// text/plain; charset=us-ascii; boundary="abc"
  1.1075 +// ----------------------------------------------------------------------------
  1.1076 +void
  1.1077 +MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart ) 
  1.1078 +    {
  1.1079 +    // check on required parameters
  1.1080 +    __ASSERT_ALWAYS( aBodyPart != NULL,
  1.1081 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
  1.1082 +
  1.1083 +    TPtrC8 aContentType( aBodyPart->ContentType() );
  1.1084 +
  1.1085 +    // check if there is a delimiter ';'
  1.1086 +    TInt lenCT = aContentType.Length();
  1.1087 +    TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
  1.1088 +                                     strlen(Multipart_ContentTypeString_Delimiter_Text) );
  1.1089 +    if (offset != KErrNotFound)
  1.1090 +        {
  1.1091 +        // ; is meant to be the end of the content type value.
  1.1092 +        // cut off content type unrelated part
  1.1093 +        aBodyPart->SetContentType( aContentType.Left( offset ) );
  1.1094 +
  1.1095 +        // extract boundary and charset info
  1.1096 +        if( lenCT > offset )
  1.1097 +            {
  1.1098 +            TPtrC8 unrelated = aContentType.Right( lenCT - offset );
  1.1099 +            TInt lenU = unrelated.Length();
  1.1100 +            
  1.1101 +            // check the boundary information
  1.1102 +            TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text, 
  1.1103 +                                           strlen(Multipart_Boundary_Text) );
  1.1104 +            if (offsetB != KErrNotFound)
  1.1105 +                {
  1.1106 +                // now, we are at the beginning of "boundary="abc" string.
  1.1107 +                // move to the "abc" part
  1.1108 +                TPtrC8 boundary = unrelated.Right( lenU - 
  1.1109 +                                                   offsetB - 
  1.1110 +                                                   strlen( Multipart_Boundary_Text ) );
  1.1111 +                TInt lenB = boundary.Length();
  1.1112 +
  1.1113 +                // look for where to end
  1.1114 +                TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
  1.1115 +                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
  1.1116 +                if (offsetQ != KErrNotFound)
  1.1117 +                    {
  1.1118 +                    // skip the quote (") char
  1.1119 +                    boundary.Set( boundary.Right( lenB - offsetQ ) );
  1.1120 +                    }
  1.1121 +
  1.1122 +                // look for where to end
  1.1123 +                // check "
  1.1124 +                TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
  1.1125 +                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
  1.1126 +                if (offsetE == KErrNotFound)
  1.1127 +                    {
  1.1128 +                    // check ;
  1.1129 +                    offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
  1.1130 +                                             strlen(Multipart_ContentTypeString_Delimiter_Text) );
  1.1131 +                    }
  1.1132 +                if (offsetE != KErrNotFound)
  1.1133 +                    {
  1.1134 +                    boundary.Set( boundary.Left( offsetE ) );
  1.1135 +                    }
  1.1136 +
  1.1137 +                // set it on to the input parameter
  1.1138 +                aBodyPart->SetBoundary( boundary );
  1.1139 +                } // end of if (offsetB != KErrNotFound)
  1.1140 +
  1.1141 +            // check the charset information
  1.1142 +            TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text, 
  1.1143 +                                            strlen(Multipart_Charset_Text) );
  1.1144 +            if (offsetCh != KErrNotFound)
  1.1145 +                {
  1.1146 +                // now, we are at the beginning of "charset=us-ascii" string.
  1.1147 +                // move to the us-ascii part
  1.1148 +                TPtrC8 charset = unrelated.Right( lenU - 
  1.1149 +                                                  offsetCh - 
  1.1150 +                                                  strlen( Multipart_Charset_Text ) );
  1.1151 +                TInt lenCh = charset.Length();
  1.1152 +
  1.1153 +                // look for where to end
  1.1154 +                TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
  1.1155 +                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
  1.1156 +                if (offsetQ != KErrNotFound)
  1.1157 +                    {
  1.1158 +                    // skip the quote (") char
  1.1159 +                    charset.Set( charset.Right( lenCh - offsetQ ) );
  1.1160 +                    }
  1.1161 +
  1.1162 +                // look for where to end
  1.1163 +                // check "
  1.1164 +                TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
  1.1165 +                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
  1.1166 +                if (offsetE == KErrNotFound)
  1.1167 +                    {
  1.1168 +                    // check ;
  1.1169 +                    offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
  1.1170 +                                            strlen(Multipart_ContentTypeString_Delimiter_Text) );
  1.1171 +                    }
  1.1172 +                if (offsetE != KErrNotFound)
  1.1173 +                    {
  1.1174 +                    charset.Set( charset.Left( offsetE ) );
  1.1175 +                    }
  1.1176 +
  1.1177 +                // set it on to the input parameter
  1.1178 +                aBodyPart->SetCharset( charset );
  1.1179 +                } // end of if (offsetCh != KErrNotFound)
  1.1180 +
  1.1181 +            } // end of if( lenCT > offset )
  1.1182 +        } // end of if (offset != KErrNotFound)
  1.1183 +    }
  1.1184 +
  1.1185 +
  1.1186 +// ----------------------------------------------------------------------------
  1.1187 +// MultipartParser::GetBodyPartUrl
  1.1188 +//
  1.1189 +// Builds up the URL which refers to this particular body part
  1.1190 +// ----------------------------------------------------------------------------
  1.1191 +HBufC16*
  1.1192 +MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
  1.1193 +                                  const TDesC8& aContentLocation,
  1.1194 +                                  const TDesC16& aResponseUrl )
  1.1195 +    {
  1.1196 +    // check on required parameters
  1.1197 +    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
  1.1198 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
  1.1199 +
  1.1200 +    // Body url builds up as follows:
  1.1201 +    // Take global (respond header) Content Base header first.
  1.1202 +    // If local (in the body part) Content Base header is present, then
  1.1203 +    // take it as default Content Base header (by overruling global Content
  1.1204 +    // Base header).  Check if the Content Location header is either an
  1.1205 +    // absolute or relative URL.  If it is an absolute, then ignore the
  1.1206 +    // Content Base, otherwise concatenate them.
  1.1207 +    // Error cases:
  1.1208 +    //   No Content Base header + Content Location is relative URL
  1.1209 +    //   No Content Location header
  1.1210 +    //   Content Base header is relative URL
  1.1211 +    TBool contentBaseInvalid = EFalse;
  1.1212 +    HBufC16* url = NULL;
  1.1213 +    
  1.1214 +    if (aContentBase.Ptr())
  1.1215 +        {
  1.1216 +        // Check if it is a relative url
  1.1217 +        if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
  1.1218 +            {
  1.1219 +            // Relative URL is not valid here as base location.
  1.1220 +            contentBaseInvalid = ETrue;
  1.1221 +            }
  1.1222 +        }
  1.1223 +    else
  1.1224 +        {
  1.1225 +        // no content base header
  1.1226 +        contentBaseInvalid = ETrue;
  1.1227 +        } // end of if (aContentBase)
  1.1228 +
  1.1229 +    if (contentBaseInvalid)
  1.1230 +        {
  1.1231 +        if( aResponseUrl.Ptr() )
  1.1232 +            {
  1.1233 +            // Copy response url
  1.1234 +            TInt lenU = aResponseUrl.Length();
  1.1235 +            url = HBufC::NewLC( lenU + 1 );
  1.1236 +            url->Des().Copy( aResponseUrl );
  1.1237 +            url->Des().ZeroTerminate();
  1.1238 +            }
  1.1239 +        }
  1.1240 +    else
  1.1241 +        {
  1.1242 +        // Copy global content "base" location 
  1.1243 +        TInt lenCB = aContentBase.Length();
  1.1244 +        url = HBufC::NewLC( lenCB + 1 );
  1.1245 +        url->Des().Copy( aContentBase );
  1.1246 +        url->Des().ZeroTerminate();
  1.1247 +        } // end of if (contentBaseInvalid)
  1.1248 +
  1.1249 +    // Check if Content Localtion is valid
  1.1250 +    if( aContentLocation.Ptr() )
  1.1251 +        {
  1.1252 +        TInt lenCL = aContentLocation.Length();
  1.1253 +
  1.1254 +        // If the Content Location is an absolute URL, then Content Base value is ignored,
  1.1255 +        // otherwise the absolute path is going to be built unless Content Base is missing
  1.1256 +        if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
  1.1257 +            {
  1.1258 +            // clean up memory
  1.1259 +            if( url )
  1.1260 +                {
  1.1261 +                CleanupStack::PopAndDestroy();  // url
  1.1262 +                }
  1.1263 +
  1.1264 +            // fill url with content location
  1.1265 +            url = HBufC::NewL( lenCL + 1 );
  1.1266 +            url->Des().Copy( aContentLocation );
  1.1267 +            url->Des().ZeroTerminate();
  1.1268 +            }
  1.1269 +        else
  1.1270 +            {
  1.1271 +            if( url )
  1.1272 +                {
  1.1273 +                HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
  1.1274 +
  1.1275 +                CleanupStack::PopAndDestroy(); // url
  1.1276 +
  1.1277 +                url = urlN;
  1.1278 +                }
  1.1279 +            }
  1.1280 +        } // end of if( aContentLocation
  1.1281 +    else
  1.1282 +        {
  1.1283 +        if( url )
  1.1284 +            {
  1.1285 +            CleanupStack::Pop(); // url
  1.1286 +            }
  1.1287 +        }
  1.1288 +
  1.1289 +    return url;
  1.1290 +    }
  1.1291 +
  1.1292 +
  1.1293 +// ----------------------------------------------------------------------------
  1.1294 +// MultipartParser::IsUrlRelativeL
  1.1295 +//
  1.1296 +// ----------------------------------------------------------------------------
  1.1297 +TBool
  1.1298 +MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
  1.1299 +    {
  1.1300 +    // check on required parameters
  1.1301 +    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
  1.1302 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
  1.1303 +
  1.1304 +    TUriParser8 uriParser;
  1.1305 +
  1.1306 +    User::LeaveIfError(uriParser.Parse(aUrl));
  1.1307 +
  1.1308 +    if( uriParser.Extract(EUriScheme).Ptr() )
  1.1309 +        {
  1.1310 +        return EFalse;
  1.1311 +        }
  1.1312 +    else
  1.1313 +        {
  1.1314 +        return ETrue;
  1.1315 +        }
  1.1316 +    }
  1.1317 +
  1.1318 +
  1.1319 +// ----------------------------------------------------------------------------
  1.1320 +// MultipartParser::UrlRelToAbsL
  1.1321 +//
  1.1322 +// Absolute path is built as : Base + Relative
  1.1323 +// ----------------------------------------------------------------------------
  1.1324 +HBufC16*
  1.1325 +MultipartParser::UrlRelToAbsL( TDesC16& aBase, 
  1.1326 +                               const TDesC8& aRelativeUrl )
  1.1327 +    {
  1.1328 +    // check on required parameters
  1.1329 +    __ASSERT_ALWAYS( aBase.Ptr() != NULL,
  1.1330 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
  1.1331 +    __ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
  1.1332 +                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
  1.1333 +
  1.1334 +    // length of absolute url
  1.1335 +    TInt len = 0;
  1.1336 +    // length of relative url
  1.1337 +    TInt lenR = 0;
  1.1338 +    TBool appendSlash = EFalse;
  1.1339 +    // path of absolute url
  1.1340 +    TPtrC16 path( NULL, 0 );
  1.1341 +
  1.1342 +    // must to have aRelativeUrl
  1.1343 +    User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
  1.1344 +
  1.1345 +    TUriParser16 uriParser;
  1.1346 +    User::LeaveIfError( uriParser.Parse( aBase ) );
  1.1347 +
  1.1348 +    // <scheme>://
  1.1349 +    TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
  1.1350 +    // must to have scheme
  1.1351 +    User::LeaveIfNull( (TUint16*)scheme.Ptr() );
  1.1352 +    len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
  1.1353 +
  1.1354 +    // <user>:<password>@
  1.1355 +    TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
  1.1356 +    if( user.Ptr() )
  1.1357 +        {
  1.1358 +        len += user.Length() + 1;
  1.1359 +        }
  1.1360 +
  1.1361 +    // <host>
  1.1362 +    TPtrC16 host( uriParser.Extract( EUriHost ) );
  1.1363 +    // must to have host
  1.1364 +    User::LeaveIfNull( (TUint16*)host.Ptr() );
  1.1365 +    len += host.Length();
  1.1366 +
  1.1367 +    // :<port>
  1.1368 +    TPtrC16 port( uriParser.Extract( EUriPort ) );
  1.1369 +    if( port.Ptr() )
  1.1370 +        {
  1.1371 +        len += port.Length();
  1.1372 +        }
  1.1373 +
  1.1374 +    // If the relative url begins with "./", remove it
  1.1375 +    TPtrC8 relativeUrl( NULL, 0 );
  1.1376 +    TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
  1.1377 +    TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
  1.1378 +    if ( indexD == 0 && indexS == 1)
  1.1379 +        {
  1.1380 +        // Found a dot-slash at beginning of relative url
  1.1381 +        relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
  1.1382 +        }
  1.1383 +    else
  1.1384 +        {
  1.1385 +        relativeUrl.Set( aRelativeUrl );
  1.1386 +        }
  1.1387 +
  1.1388 +    lenR = relativeUrl.Length();
  1.1389 +    len += lenR;
  1.1390 +    // If the relative url begins with a slash, then it is an absolute path
  1.1391 +    // Does relative url start with slash?
  1.1392 +    indexS = relativeUrl.Locate( SLASH_CHAR );
  1.1393 +    // no, need to extract path from base url
  1.1394 +    if( indexS != 0 )
  1.1395 +        {
  1.1396 +      // <path>
  1.1397 +        path.Set( uriParser.Extract( EUriPath ) );
  1.1398 +        if( path.Ptr() )
  1.1399 +            {
  1.1400 +            // cut off the file path
  1.1401 +            if ( path.LocateReverse( DOT_CHAR ) )
  1.1402 +                {
  1.1403 +                // case: dir/index.html
  1.1404 +                if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
  1.1405 +                    {
  1.1406 +                    // to keep the slash
  1.1407 +                    path.Set( path.Left( indexS2 + 1 ) );
  1.1408 +                    }
  1.1409 +                // case: index.html
  1.1410 +                else
  1.1411 +                    {
  1.1412 +                    path.Set( NULL, 0 );
  1.1413 +                    }
  1.1414 +                }
  1.1415 +
  1.1416 +            // figure out the end slash
  1.1417 +            if( path.Ptr() )
  1.1418 +                {
  1.1419 +                if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
  1.1420 +                    {
  1.1421 +                    appendSlash = ETrue;
  1.1422 +                    }
  1.1423 +    
  1.1424 +                len += path.Length();
  1.1425 +                }
  1.1426 +            else
  1.1427 +                {
  1.1428 +                appendSlash = ETrue;
  1.1429 +                }
  1.1430 +            }
  1.1431 +        }
  1.1432 +    // yes, no need to extract path from base url
  1.1433 +    if( appendSlash )
  1.1434 +        {
  1.1435 +        ++len;
  1.1436 +        }
  1.1437 +
  1.1438 +    // NULL terminator
  1.1439 +	// In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator) 
  1.1440 +	// for proper String Termination
  1.1441 +    len += 2;
  1.1442 +
  1.1443 +    // new absolute url
  1.1444 +    HBufC16* urlAbs = HBufC16::NewL( len );
  1.1445 +    TPtr16 urlAbsPtr = urlAbs->Des();
  1.1446 +
  1.1447 +    // copy base into absolute url
  1.1448 +
  1.1449 +    // scheme
  1.1450 +    urlAbsPtr.Copy( scheme );
  1.1451 +    urlAbsPtr.Append( COLON_CHAR );
  1.1452 +    urlAbsPtr.Append( SLASH_CHAR );
  1.1453 +    urlAbsPtr.Append( SLASH_CHAR );
  1.1454 +
  1.1455 +    // user
  1.1456 +    if( user.Ptr() )
  1.1457 +        {
  1.1458 +        urlAbsPtr.Append( user );
  1.1459 +        urlAbsPtr.Append( AT_CHAR );
  1.1460 +        }
  1.1461 +
  1.1462 +    // host
  1.1463 +    urlAbsPtr.Append( host );
  1.1464 +
  1.1465 +    // port
  1.1466 +    if( port.Ptr() )
  1.1467 +        {
  1.1468 +        urlAbsPtr.Append( COLON_CHAR );
  1.1469 +        urlAbsPtr.Append( port );
  1.1470 +        }
  1.1471 +
  1.1472 +    // path
  1.1473 +    if( path.Ptr() )
  1.1474 +        {
  1.1475 +        urlAbsPtr.Append( path );
  1.1476 +        }
  1.1477 +
  1.1478 +    // slash between path and relative url
  1.1479 +    if( appendSlash )
  1.1480 +        {
  1.1481 +        urlAbsPtr.Append( SLASH_CHAR );
  1.1482 +        }
  1.1483 +
  1.1484 +    // relative url
  1.1485 +    TUint16* relUrlInt = new TUint16[ lenR ];
  1.1486 +    TPtr16 relUrl16( relUrlInt, lenR );
  1.1487 +    relUrl16.Copy( relativeUrl );
  1.1488 +    urlAbsPtr.Append( relUrl16 );
  1.1489 +    delete[] relUrlInt;
  1.1490 +
  1.1491 +    // null terminate
  1.1492 +    urlAbsPtr.ZeroTerminate();
  1.1493 +
  1.1494 +    return urlAbs;
  1.1495 +    }
  1.1496 +    
  1.1497 +// ------------------------------------------------------------------------- 
  1.1498 +// Composes multipart/mixed document
  1.1499 +// ------------------------------------------------------------------------- 
  1.1500 +HBufC8*
  1.1501 +MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
  1.1502 +                                const TDesC8& aBoundary,
  1.1503 +                                TInt aHeaderMask )
  1.1504 +    {
  1.1505 +    // --(aBoundary)
  1.1506 +    _LIT8(KBoundary, "--%S\r\n");
  1.1507 +    HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
  1.1508 +    boundary->Des().Format( KBoundary, &aBoundary );
  1.1509 +    
  1.1510 +    // Calculate the size of this document.
  1.1511 +    TInt bodySize = 0;
  1.1512 +    //    a. for each CBodyPart
  1.1513 +    //       boundaries + CRLF between headers and body (constant addition)
  1.1514 +    bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
  1.1515 +    for (TInt i = 0; i < aBodyArray.Count(); i++)
  1.1516 +        {
  1.1517 +        if (!(aBodyArray[i]->Headers().Length() +
  1.1518 +            aBodyArray[i]->Body().Length()))
  1.1519 +            {
  1.1520 +            // one less boundary
  1.1521 +            bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
  1.1522 +            // skip empty bodypart
  1.1523 +            continue;
  1.1524 +            }
  1.1525 +        bodySize += aBodyArray[i]->Headers().Length();
  1.1526 +    //       ensure there are only 2 CRLFs between header and body
  1.1527 +        if (aBodyArray[i]->Headers().Length() > 0)
  1.1528 +            {
  1.1529 +            TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
  1.1530 +            TUint newEnd = bodyHeaders.Length() - 1;
  1.1531 +            while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
  1.1532 +                {
  1.1533 +                --newEnd;
  1.1534 +                --bodySize;
  1.1535 +                }
  1.1536 +            // two CRLFs
  1.1537 +            bodySize += strlen(Multipart_CRLF_Text);
  1.1538 +            }
  1.1539 +        bodySize += aBodyArray[i]->Body().Length();
  1.1540 +    //       CRLF (end of body, add one only if there is body AND does not end with CRLF)
  1.1541 +        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
  1.1542 +        if (bodyBody.Length() > 0 
  1.1543 +            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
  1.1544 +            {
  1.1545 +            bodySize += strlen(Multipart_CRLF_Text);
  1.1546 +            }
  1.1547 +        }
  1.1548 +    //     end boundary (boundary - '\r\n' + "--")
  1.1549 +    bodySize += boundary->Length();
  1.1550 +    TInt docSize = bodySize; 
  1.1551 +    //     calculate the size of Headers
  1.1552 +    _LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
  1.1553 +    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
  1.1554 +        {
  1.1555 +        docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
  1.1556 +        docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
  1.1557 +        docSize += strlen(Multipart_CRLF_Text); // eol
  1.1558 +        }
  1.1559 +    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
  1.1560 +        {
  1.1561 +        docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
  1.1562 +        // calculate number of chars needed to represent bodySize
  1.1563 +        HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
  1.1564 +        bodySizeSize->Des().Num( bodySize );
  1.1565 +        docSize += bodySizeSize->Length(); // content length (bodySize)
  1.1566 +        docSize += strlen(Multipart_CRLF_Text); // eol
  1.1567 +        CleanupStack::PopAndDestroy( bodySizeSize );
  1.1568 +        }
  1.1569 +    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
  1.1570 +        {
  1.1571 +        docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
  1.1572 +        docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
  1.1573 +        docSize += strlen(Multipart_CRLF_Text); // eol
  1.1574 +        }
  1.1575 +    //    extra CRLF for separating header and body
  1.1576 +    docSize += strlen(Multipart_CRLF_Text);
  1.1577 +    //  CALCULATION COMPLETE
  1.1578 +    //  at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
  1.1579 +    //  and docSize contains the size of the entire document (use it to create HBufC8*
  1.1580 +    //  of appropriate size)
  1.1581 +  
  1.1582 +    //  construct multipart document
  1.1583 +    HBufC8* document = HBufC8::NewLC(docSize);
  1.1584 +    TPtr8 docAppend(document->Des());
  1.1585 +    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
  1.1586 +        {
  1.1587 +        docAppend.Format( KContentType, &aBoundary );
  1.1588 +        }
  1.1589 +    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
  1.1590 +        {
  1.1591 +        _LIT8( KContentLength, "Content-Length: %d\r\n" );
  1.1592 +        docAppend.AppendFormat( KContentLength, bodySize );
  1.1593 +        }
  1.1594 +    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
  1.1595 +        {
  1.1596 +        _LIT8( KLastModified, "Last-Modified: %S\r\n" );
  1.1597 +        TTime current;
  1.1598 +        current.UniversalTime();
  1.1599 +        TInternetDate modDate(current.DateTime());
  1.1600 +        HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
  1.1601 +        docAppend.AppendFormat( KLastModified, dateString );
  1.1602 +        delete dateString;
  1.1603 +        }
  1.1604 +    // required CRLF
  1.1605 +    docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
  1.1606 +    //  BodyParts
  1.1607 +    for (TInt i = 0; i < aBodyArray.Count(); i++)
  1.1608 +        {
  1.1609 +        if (!(aBodyArray[i]->Headers().Length() +
  1.1610 +            aBodyArray[i]->Body().Length()))
  1.1611 +            {
  1.1612 +            // skip empty bodypart
  1.1613 +            continue;
  1.1614 +            }
  1.1615 +        docAppend.Append( *boundary );
  1.1616 +        TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
  1.1617 +        while ( headerLength > 0 &&
  1.1618 +                (aBodyArray[i]->Headers()[headerLength] == '\r'
  1.1619 +                || aBodyArray[i]->Headers()[headerLength] == '\n' ))
  1.1620 +            {
  1.1621 +            --headerLength;
  1.1622 +            }
  1.1623 +        docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
  1.1624 +
  1.1625 +        if ( headerLength > 0 )
  1.1626 +            {
  1.1627 +            docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
  1.1628 +            }
  1.1629 +        else
  1.1630 +            {
  1.1631 +            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
  1.1632 +            }
  1.1633 +    //  body
  1.1634 +        docAppend.Append(aBodyArray[i]->Body());
  1.1635 +    //  CRLF only if body exists and doesn't end with CRLF
  1.1636 +        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
  1.1637 +        if (bodyBody.Length() > 0
  1.1638 +            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
  1.1639 +            {
  1.1640 +            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
  1.1641 +            }
  1.1642 +        }
  1.1643 +    //  end boundary
  1.1644 +    _LIT8(KEndBoundary, "--%S--");
  1.1645 +    docAppend.AppendFormat(KEndBoundary, &aBoundary);
  1.1646 +    CleanupStack::Pop( document );
  1.1647 +    CleanupStack::PopAndDestroy( boundary );
  1.1648 +    return document;
  1.1649 +    }