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 + }