Update contrib.
1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
17 #include <e32def.h> // First to avoid NULL redefine warning (no #ifndef NULL).
20 #include <ezdecompressor.h> // Ezlib.lib, GZip decoders
23 #include <uricommon.h>
25 #include <bafl/multipartparser.h>
26 #include "gzipbufmgr.h"
27 #include "tinternetdate.h"
29 #include <bafl/qpcodec.h>
30 #include <bsul/clientmessage.h>
33 const char Multipart_Mixed[] = {"multipart/mixed"};
34 const char Multipart_Related[] = {"multipart/related"};
35 const char Multipart_Boundary_Text[] = {"boundary="};
36 const char Multipart_Content_Base_Text[] = {"Content-Base:"};
37 const char Multipart_Content_Location_Text[] = {"Content-Location:"};
38 const char Multipart_Content_Type_Text[] = {"Content-Type:"};
39 const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
40 // violate RFC2045; but upon customer request
41 const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
42 const char Multipart_Content_ID_Text[] = {"Content-ID:"};
43 const char Multipart_Hypens_Text[] = {"--"};
44 const char Multipart_CRLF_Text[] = {"\r\n"};
45 const char Multipart_LF_Text[] = {"\n"};
46 const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
47 const char Multipart_DoubleLF_Text[] = {"\n\n"};
48 const char Multipart_Charset_Text[] = {"charset="};
49 const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
50 const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
51 const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
52 const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
53 const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
54 const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
55 const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
56 const char Multipart_Content_Encoding_GZip[] = {"gzip"};
57 const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
60 #define MULTIPART_CONTENT_BASE_LENGTH 13
61 #define MULTIPART_CONTENT_LOCATION_LENGTH 17
62 #define MULTIPART_CONTENT_TYPE_LENGTH 13
63 #define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH 26
64 // violates RFC2045; but upon vodafone request
65 #define MULTIPART_CONTENT_ENCODING_LENGTH 17
66 #define MULTIPART_CONTENT_LENGTH_LENGTH 15
67 #define MULTIPART_LAST_MODIFIED_LENGTH 14
68 #define MULTIPART_CONTENT_ID_LENGTH 11
70 #define MULTIPART_CONTENT_BASE 1
71 #define MULTIPART_CONTENT_LOCATION 2
72 #define MULTIPART_CONTENT_TRANSFER_ENCODING 3
73 #define MULTIPART_CONTENT_TYPE 4
74 #define MULTIPART_CONTENT_ID 5
76 #define MULTIPART_INTERNET_DATE_STRING_LENGTH 29
78 #define SLASH_CHAR '/'
81 #define COLON_CHAR ':'
83 #define SCHEME_SEPARATOR_LENGTH 3
86 // ============================= LOCAL FUNCTIONS ===============================
89 // ============================ MEMBER FUNCTIONS ===============================
92 // -------------------------------------------------------------------------
93 // Parse and put each body part to the body part array
94 // -------------------------------------------------------------------------
95 EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody,
96 const TDesC8& aContentType,
97 const TDesC8& aBoundary,
99 RPointerArray <CBodyPart>& aBodyPartsArray,
102 // check on required parameters
103 __ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
104 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
105 __ASSERT_ALWAYS( aContentType.Ptr() != NULL,
106 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
107 __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
108 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
109 __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
110 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
112 const TUint8* multipartBuffer = aMultipartBody.Ptr();
113 TUint32 multipartLen = aMultipartBody.Length();
114 __ASSERT_ALWAYS( multipartLen != 0,
115 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
116 TUint8* bodyPartBuffer = NULL;
117 TUint32 bodyPartBufferLength = 0;
118 TUint32 startPosition = 0;
119 char* singleEolChar = NULL;
120 char* doubleEolChar = NULL;
121 CBodyPart* bodyPart = NULL;
123 // currently only support mixed and related.
124 char* contentType = (char*)aContentType.Ptr();
125 if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 &&
126 strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
128 User::Leave( KErrNotSupported );
131 // get singleEol and doubleEol
132 MultipartParser::SetEolCharacters( multipartBuffer,
138 // get body parts one by one
139 // null bodyPartBuffer indicates the end of the multipart body
143 // stop when we get required number of parse done
144 if( aMaxToParse != -1 && counter >= aMaxToParse )
151 // get the next body part
152 bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition,
158 // break if we are at end
159 if( bodyPartBuffer == NULL )
163 // update start position
164 startPosition += bodyPartBufferLength;
166 // create new body part
167 bodyPart = CBodyPart::NewL();
168 // parse each body part buffer to fill in body part
169 MultipartParser::ParseBodyPartL( bodyPartBuffer,
170 bodyPartBufferLength,
175 // add the body part to the array
176 aBodyPartsArray.Append( bodyPart );
178 while( bodyPartBuffer != NULL );
181 // -------------------------------------------------------------------------
182 // Composes RFC1521 compliant multipart document with given bodyparts
183 // Actual task of creating the document is delegated to specialized composer
184 // for each of the subtypes
185 // -------------------------------------------------------------------------
186 EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
187 const TDesC8& aBoundary,
188 TMultipartSubtype aSubtype,
191 // check on required parameters
192 if ( !aBoundary.Ptr() || !aBoundary.Length() )
194 User::Leave( KErrArgument );
197 HBufC8* multipartDoc = NULL;
200 case EMultipartSubtypeMixed:
202 multipartDoc = ComposeMixedL( aBodyPartsArray,
209 User::Leave( KErrArgument );
215 // -------------------------------------------------------------------------
216 // Default constructor
217 // -------------------------------------------------------------------------
218 MultipartParser::MultipartParser()
222 // -------------------------------------------------------------------------
223 // Returns with the next body part buffer from start position (offset)
224 // -------------------------------------------------------------------------
226 MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition,
227 const TUint8* aMultipartBody,
228 TUint32 aMultipartLen,
229 const TDesC8& aBoundary,
230 char* aSingleEolChar,
231 TUint8** aBodyPartBuffer )
233 // check on required parameters
234 __ASSERT_ALWAYS( aMultipartBody != NULL,
235 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
236 __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
237 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
238 __ASSERT_ALWAYS( aSingleEolChar != NULL,
239 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
241 long startOffset = -1;
244 TBool badContent = EFalse;
246 const char* boundaryStr = (const char*) aBoundary.Ptr();
247 int boundaryLength = aBoundary.Length();
248 int hypensLength = strlen( Multipart_Hypens_Text );
249 int singleEolLength = strlen( aSingleEolChar );
250 TUint32 i = aStartPosition;
253 // Overall, the body of a multipart entity may be specified as follows:
254 // multipart-body := preamble 1*encapsulation close-delimiter epilogue
255 // encapsulation := delimiter CRLF body-part
256 // delimiter := CRLF "--" boundary ; taken from Content-Type field.
257 // when content-type is multipart.
258 // There must be no space between "--" and boundary.
259 // close-delimiter := delimiter "--" ; Again, no space before
263 // body: here comes some text that we ignore
269 // closing boundary. we ignore this text here
270 while( i < (aMultipartLen - hypensLength + 1 ) )
272 // get the first two hypens
273 // using char comparison to compare "--"
274 // hopefully this is faster
275 if( (char)aMultipartBody[ i ] == '-' &&
276 (char)aMultipartBody[ i+1 ] == '-' )
278 char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
279 // check if the body is long enough first and then check if boundary matches
280 if( aMultipartLen >= i + hypensLength + boundaryLength )
282 if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
284 // we've got the boundary
285 offset = i + hypensLength + boundaryLength;
286 // Next two chars must be either two hypens (closing boundary - 2 bytes),
287 // or single Eol characters (new body).
288 // Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
289 char* eolBuf = (char*)&aMultipartBody[ offset ];
290 // Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
291 if( aMultipartLen >= offset + hypensLength )
293 if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0||
294 eolBuf[0] == Multipart_LF_Text[0])
297 // We found Eol, so this is a new multipart body (header and content)
298 if( startOffset == -1 )
300 // this is the beginning.
301 startOffset = offset;
302 // let's looking for the end of this body part which either could
303 // be closing boundary or an opening boundary for the next body part
305 // jump over the boundary information
306 i = startOffset + singleEolLength;
310 // We found the next boundary marker, so this is the
311 // beginning of the next body part
312 endOffset = offset - boundaryLength - hypensLength;
313 // we've got both start and end offset
317 else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
319 // We found the closing boundary marker
320 endOffset = offset - boundaryLength - hypensLength;
325 // it's neither Eol nor two hypens "--"
332 // the buffer is too short and not closed properly
340 // the buffer is far too short
346 } // end of while loop
348 // missing closing boundary check
349 if( endOffset == -1 )
351 // take the end of the body as closing boundary
361 if( startOffset != -1 && endOffset != -1 )
363 *aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
364 length = endOffset - startOffset;
368 *aBodyPartBuffer = NULL;
376 // -------------------------------------------------------------------------
377 // Set End-Of-Line characters. Look at the eol character after the boundary to
378 // determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
381 // NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
383 // Multipart entity is specified as follows:
384 // --boundary123[eol]
385 // Content-type: text/html[eol]
386 // Content-location: http:\\www.example.com\index.html[eol]
388 // <html><body>...example...</html>[eol]
389 // --boundary123--[eol]
390 // -------------------------------------------------------------------------
392 MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
393 TUint32 aMultipartLen,
394 const TDesC8& aBoundary,
395 char** aSingleEolChar,
396 char** aDoubleEolChar )
398 // check on required parameters
399 __ASSERT_ALWAYS( aMultipartBody != NULL,
400 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
401 __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
402 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
403 __ASSERT_ALWAYS( aSingleEolChar != NULL,
404 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
405 __ASSERT_ALWAYS( aDoubleEolChar != NULL,
406 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
409 const char* boundaryStr = (const char*) aBoundary.Ptr();
410 int boundaryLength = aBoundary.Length();
411 int hypensLength = strlen( Multipart_Hypens_Text );
412 int lfLength = strlen( Multipart_LF_Text );
414 // Set the default eol (CRLF)
415 *aSingleEolChar = (char *)Multipart_CRLF_Text;
416 *aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
418 while (i < (aMultipartLen - hypensLength + 1))
420 // Get the first two hypens
421 char* bodyPart = (char*)&aMultipartBody[ i ];
422 if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
424 char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
425 // Check if the body is long enough first and then check if boundary matches
426 if (aMultipartLen >= i + hypensLength + boundaryLength )
428 if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
430 // We've got the boundary
431 i = i + hypensLength + boundaryLength;
432 // Next two chars should be the single Eol characters.
433 // Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
434 char* eolBuf = (char*)&aMultipartBody[ i ];
435 // Check if buffer is long enough for eol [1 byte LF]
436 if (aMultipartLen >= i + lfLength)
438 if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
440 // We found LF Eol (unix/mac)
441 *aSingleEolChar = (char *)Multipart_LF_Text;
442 *aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
443 } // end of if compare eol to LF
444 } // end of if buffer size ok
446 // Break in all cases, we will use the default CRLF if we don't
447 // find eol=LF, or the remaining buffer is too small
449 } // end of compare/found boundary
451 } // end of looking for first two hypens
458 // -------------------------------------------------------------------------
460 // The bodyPart parameter can contain the optional headers and response, or
461 // just the response. In the case of both the (optional) header(s) and the
462 // response, let's cut off header(s) and return the response body. The
463 // header is seperated from the response by two End-of-line (Eol) characters,
464 // i.e. two CRLF's (windows) or two LF's (unix/mac).
465 // --boundary123 (omitted from bodyPart parameter, starts next line)
466 // Content-type: text/html[eol]
467 // Content-location: http:\\www.example.com\index.html[eol]
469 // <html><body>example</body></html>
471 // In the case of no headers, there may be only one (or more) Eol characters.
472 // --boundary123 (omitted from bodyPart parameter, starts on next line)
474 // <html><body>example</body></html>
475 // -------------------------------------------------------------------------
477 MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
478 TUint32 aBodyPartBufferLength,
479 char* aSingleEolChar,
480 char* aDoubleEolChar,
481 const TDesC16& aResponseUrl,
482 CBodyPart* aBodyPart )
484 // check on required parameters
485 __ASSERT_ALWAYS( aBodyPartBuffer != NULL,
486 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
487 __ASSERT_ALWAYS( aSingleEolChar != NULL,
488 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
489 __ASSERT_ALWAYS( aDoubleEolChar != NULL,
490 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
491 __ASSERT_ALWAYS( aBodyPart != NULL,
492 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
493 __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
494 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
496 // headers look something like this
497 // we need to return "text/html" if the requested header is "Content-type"
499 // Content-type: text/html
500 // Content-location: http:\\www.example.com\index.html
505 int contentHeaderValueCharLen = 0;
506 TPtrC8 contentHeaderValuePtr( NULL, 0 );
507 int contentHeaderNameLength = 0;
509 /*lint -e{668} Possibly passing a null pointer to function */
510 int singleEolLength = strlen( aSingleEolChar );
511 int doubleEolLength = strlen( aDoubleEolChar );
512 // start looking for the header name
513 for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
516 const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
518 // Did we find the Content Header Value
519 if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
521 contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
522 found = MULTIPART_CONTENT_BASE;
524 else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
526 contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
527 found = MULTIPART_CONTENT_LOCATION;
529 else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
531 contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
532 found = MULTIPART_CONTENT_TRANSFER_ENCODING;
534 else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
536 contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
537 found = MULTIPART_CONTENT_TRANSFER_ENCODING;
539 else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
541 contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
542 found = MULTIPART_CONTENT_TYPE;
544 else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
546 contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
547 found = MULTIPART_CONTENT_ID;
553 int startPos = i + contentHeaderNameLength;
554 while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
559 // used for finding '<' in Content-ID field
560 char charFirst = aBodyPartBuffer[ startPos ];
561 // content headers are closed with End-Of-Line (Eol) character
562 for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
564 char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
565 if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0
566 || tmpContentHeaderValue[0] == Multipart_LF_Text[0])
568 if( found == MULTIPART_CONTENT_ID )
570 if( charFirst == '<' )
572 // length of the value excluding beginging '<' and ending '>'
573 contentHeaderValueCharLen = j - startPos - 2;
574 contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
579 // length of the value
580 contentHeaderValueCharLen = j - startPos;
581 contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
584 // rewind so the double EOL will be checked against later
586 // break the inner loop
589 } // end of inner for loop
593 case MULTIPART_CONTENT_BASE:
594 aBodyPart->SetContentBase( contentHeaderValuePtr );
596 case MULTIPART_CONTENT_LOCATION:
597 aBodyPart->SetContentLocation( contentHeaderValuePtr );
599 case MULTIPART_CONTENT_TRANSFER_ENCODING:
600 aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
602 case MULTIPART_CONTENT_TYPE:
603 aBodyPart->SetContentType( contentHeaderValuePtr );
605 case MULTIPART_CONTENT_ID:
606 aBodyPart->SetContentID( contentHeaderValuePtr );
611 } // end of if (found)
613 // Did we get to the end of the Content Header. Many of the Content Header Values
614 // are optional, so we could get to the end of the Content Header (double Eol) and
615 // not find the Content Header Value we were searching for.
616 // get the response body
617 int aEolLength = strlen(Multipart_DoubleLF_Text);
618 if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
619 strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
621 if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
622 aEolLength = doubleEolLength;
624 TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
625 int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
626 TPtrC8 body( responseBody, lengthBody );
627 aBodyPart->SetBody( body );
629 // set headers when we have headers
632 // jump over the starting single EOL
633 TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
634 // // jump over the starting single EOL and the ending double EOL
635 int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
636 TPtrC8 headers( responseHeaders, lengthHeaders );
637 aBodyPart->SetHeaders( headers );
642 } // end of outter for loop
644 // prepare more on body part
646 // Check to see if we have a Content-Transfer-Encoding.
647 TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
648 // If we have Content-Transfer-Encoding, prepare to decode
649 if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
651 // Initialize the encoded body, input
652 TPtrC8 encodedBody( aBodyPart->Body() );
654 // This will contain the "decoded" data.
655 // The memory (decodedBody.Ptr) is owned by this method, but allocated by
656 // the DecodeContentTransferEncoding method.
657 TPtr8 decodedBody( NULL, 0, 0 );
659 // The decoded data will return in decodedBody.Ptr.
660 // The memory allocated is owned by this method.
661 TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
664 User::LeaveIfError(err);
666 // The responseBody pointer is an offset into the response
667 // buffer, do not delete. Substitute the decodedBody pointer.
668 aBodyPart->SetBody( decodedBody );
669 aBodyPart->SetIsDecodedBody( ETrue );
670 } // end of if (contentTransferEncodingValue)
672 // Check to see if we have a Content-Type.
673 TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
674 // parse contentType to get new contentType, charset, boundary info
675 if( contentTypeValue )
677 MultipartParser::CutOffContentTypeAttributes( aBodyPart );
678 // updated content type
679 contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
682 // If we have zipped Content-Type, prepare to unzip
683 if( MultipartParser::IsZipped(contentTypeValue) )
685 // Initialize the zipped body, input
686 TPtrC8 zippedBody( aBodyPart->Body() );
688 // This will contain the "unzipped" data.
689 // The memory (unzippedBody.Ptr) is owned by this method, but allocated by
691 TPtr8 unzippedBody( NULL, 0, 0 );
693 // The unzipped data will return in unzippedBody.Ptr.
694 // The memory allocated is owned by this method.
695 TInt err = MultipartParser::Unzip( contentTypeValue,
698 User::LeaveIfError(err);
700 if( aBodyPart->IsDecodedBody() )
702 // old body is not the original buffer, delete it
703 delete (TUint8*) aBodyPart->Body().Ptr();
705 // unzip happend, use unzippedBody; delete decodedBody
708 // The responseBody pointer is an offset into the response
709 // buffer, do not delete. Substitute the decodedBody pointer.
711 aBodyPart->SetIsDecodedBody( ETrue );
714 aBodyPart->SetBody( unzippedBody );
717 // Get the url of the current body part
718 HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
719 aBodyPart->ContentLocation(),
721 aBodyPart->SetUrl( responseUrl );
725 // -------------------------------------------------------------------------
726 // From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
727 // Section 6.2. Content-Transfer-Encodings Semantics
729 // The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
730 // mean that the identity (i.e. NO) encoding transformation has been
731 // performed. As such, they serve simply as indicators of the domain of
732 // the body data, and provide useful information about the sort of
733 // encoding that might be needed for transmission in a given transport
734 // system. The terms "7bit data", "8bit data", and "binary data" are
735 // all defined in Section 2.
737 // Returns true if contentTransferEncodingValue is neither NULL nor a domain.
738 // -------------------------------------------------------------------------
739 TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
741 if( !aContentTransferEncodingValue )
746 char* encoding = (char*)aContentTransferEncodingValue;
748 if ( strncasecmp( encoding,
749 Multipart_Content_Transfer_Encoding_7bit,
750 strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
755 if ( strncasecmp( encoding,
756 Multipart_Content_Transfer_Encoding_8bit,
757 strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
762 if ( strncasecmp( encoding,
763 Multipart_Content_Transfer_Encoding_binary,
764 strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
773 // ----------------------------------------------------------------------------
774 // DecodeContentTransferEncoding
776 // Decodes the Content-Transfer-Encoding. The returned length of decodedBody
777 // is zero if decoding failed.
779 // 1. This method should be called with a non-null string, i.e.
780 // aContentTransferEncodingValue.
781 // 2. Memory is allocated in this method, but ownership is with the calling method.
782 // ----------------------------------------------------------------------------
784 MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
785 const TDesC8& aEncodedBody,
786 TPtr8& aDecodedBody )
788 // check on required parameters
789 __ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
790 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
791 __ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
792 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
794 TInt status = KErrNone;
795 char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
797 // Set the decodedBody.Length to zero, length > 0 if successful decode
798 aDecodedBody.SetLength(0);
800 // Is the Content-Transfer-Encoding = "base64"
801 if( strncasecmp( contentTransferEncodingString,
802 Multipart_Content_Transfer_Encoding_Base64,
803 strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
805 // The decoded length of base64 is about half (use same) encoded length
806 TUint maxBodyLength = aEncodedBody.Length();
807 TUint8* decodedDataPtr = new TUint8[maxBodyLength];
810 aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
812 using namespace BSUL;
813 // Decode the base64 Content-Transfer-Encoding
814 Base64Codec::Decode(aEncodedBody, aDecodedBody);
816 if (aDecodedBody.Length() == 0)
818 status = KErrGeneral;
821 else // decodedDataPtr is NULL
823 status = KErrNoMemory;
825 } // end of base64 decoding
827 // Is the Content-Transfer-Encoding = "quoted-printable"
828 else if( strncasecmp( contentTransferEncodingString,
829 Multipart_Content_Transfer_Encoding_QuotedPrintable,
830 strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
832 // The decoded length of QP is the same as the encoded length
833 TUint maxBodyLength = aEncodedBody.Length();
834 TUint8* decodedDataPtr = new TUint8[maxBodyLength];
837 aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
839 // Decode the quoted-printable Content-Transfer-Encoding
840 QuotedPrintableCodec::Decode(aEncodedBody, aDecodedBody);
842 if (aDecodedBody.Length() == 0)
844 status = KErrGeneral;
847 else // decodedDataPtr is NULL
849 status = KErrNoMemory;
851 } // end of quoted-printed decoding
853 // Is the Content-Encoding = "gzip"
854 else if( strncasecmp( contentTransferEncodingString,
855 Multipart_Content_Encoding_GZip,
856 strlen(Multipart_Content_Encoding_GZip) ) == 0 )
858 // Our GZip decoder parts
859 GZipBufMgr* gZipBufMgr = NULL;
860 CEZDecompressor* ezDecompressor = NULL;
862 // We have gzip, lets decompress the encoded data.
863 // Set up the encoded data into a GZip buffer manager.
865 TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
867 // Get the GZip decompressor
870 TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
872 // Inflate the GZip data
875 TRAP(err, ezDecompressor->InflateL());
876 // Set the finalize flag
879 TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
880 // Get the inflated data, it is much larger then the encoded data
883 TPtrC8 output = ezDecompressor->OutputDescriptor();
884 if (output.Length() != 0)
886 TInt size = output.Size();
887 TUint8* outBuf = new TUint8[size];
890 memcpy(outBuf, output.Ptr(), size);
892 aDecodedBody.Set((TUint8*)outBuf, size, size);
894 else // outBuf is NULL
896 status = KErrNoMemory;
901 status = KErrGeneral;
906 status = KErrGeneral;
911 status = KErrGeneral;
914 else // ezDecompressor is NULL
916 status = KErrNoMemory;
919 else // gZipBufMgr is NULL
921 status = KErrNoMemory;
924 // Clean up our memory
926 delete ezDecompressor;
929 // We can add additional decodings here.
930 // When adding additional decoding be aware of the decodedBody.Ptr()
931 // max size. Do the realloc here, AND allow the decodedBody.Ptr()
932 // ownership to be passed back to calling method.
938 // -------------------------------------------------------------------------
939 // only support application/x-gzip
940 // -------------------------------------------------------------------------
941 TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
943 if( !aContentTypeValue )
948 char* contentType = (char*)aContentTypeValue;
950 if ( strncasecmp( contentType,
951 Multipart_Content_Type_GZip,
952 strlen(Multipart_Content_Type_GZip) ) == 0 )
961 // ----------------------------------------------------------------------------
964 // Unzip the .gz. The returned length of unzippedBody
965 // is zero if unzip failed.
967 // 1. This method should be called with a non-null string, i.e.
969 // 2. Memory is allocated in this method, but ownership is with the calling method.
970 // ----------------------------------------------------------------------------
972 MultipartParser::Unzip( TUint8* aContentType,
973 const TDesC8& aZippedBody,
974 TPtr8& aUnzippedBody )
976 // check on required parameters
977 __ASSERT_ALWAYS( aContentType != NULL,
978 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
979 __ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
980 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
982 TInt status = KErrNone;
983 char* contentTypeStr = (char*)aContentType;
985 // Set the aUnzippedBody.Length to zero, length > 0 if successful decode
986 aUnzippedBody.SetLength(0);
988 // Our GZip decoder parts
989 GZipBufMgr* gZipBufMgr = NULL;
990 CEZDecompressor* ezDecompressor = NULL;
992 // Is the Content-Type = "application/x-gzip"
993 if( strncasecmp( contentTypeStr,
994 Multipart_Content_Type_GZip,
995 strlen(Multipart_Content_Type_GZip) ) == 0 )
997 // We have gzip, lets decompress the encoded data.
998 // Set up the encoded data into a GZip buffer manager.
1000 TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
1002 // Get the GZip decompressor
1005 TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
1007 // Inflate the GZip data
1008 if( ezDecompressor )
1010 TRAP(err, ezDecompressor->InflateL());
1011 // Set the finalize flag
1012 if (err == KErrNone)
1014 TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
1015 // Get the inflated data, it is much larger then the encoded data
1016 if (err == KErrNone)
1018 TPtrC8 output = ezDecompressor->OutputDescriptor();
1019 if (output.Length() != 0)
1021 TInt size = output.Size();
1022 TUint8* outBuf = new TUint8[size];
1025 memcpy(outBuf, output.Ptr(), size);
1027 aUnzippedBody.Set((TUint8*)outBuf, size, size);
1029 else // outBuf is NULL
1031 status = KErrNoMemory;
1036 status = KErrGeneral;
1041 status = KErrGeneral;
1046 status = KErrGeneral;
1049 else // ezDecompressor is NULL
1051 status = KErrNoMemory;
1054 else // gZipBufMgr is NULL
1056 status = KErrNoMemory;
1060 // Clean up our memory
1062 delete ezDecompressor;
1068 // ----------------------------------------------------------------------------
1069 // It cuts off the charset value from the content type header
1070 // content type string looks like as follows:
1071 // text/plain; charset=us-ascii; boundary="abc"
1072 // ----------------------------------------------------------------------------
1074 MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart )
1076 // check on required parameters
1077 __ASSERT_ALWAYS( aBodyPart != NULL,
1078 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
1080 TPtrC8 aContentType( aBodyPart->ContentType() );
1082 // check if there is a delimiter ';'
1083 TInt lenCT = aContentType.Length();
1084 TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
1085 strlen(Multipart_ContentTypeString_Delimiter_Text) );
1086 if (offset != KErrNotFound)
1088 // ; is meant to be the end of the content type value.
1089 // cut off content type unrelated part
1090 aBodyPart->SetContentType( aContentType.Left( offset ) );
1092 // extract boundary and charset info
1093 if( lenCT > offset )
1095 TPtrC8 unrelated = aContentType.Right( lenCT - offset );
1096 TInt lenU = unrelated.Length();
1098 // check the boundary information
1099 TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text,
1100 strlen(Multipart_Boundary_Text) );
1101 if (offsetB != KErrNotFound)
1103 // now, we are at the beginning of "boundary="abc" string.
1104 // move to the "abc" part
1105 TPtrC8 boundary = unrelated.Right( lenU -
1107 strlen( Multipart_Boundary_Text ) );
1108 TInt lenB = boundary.Length();
1110 // look for where to end
1111 TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
1112 strlen(Multipart_ContentTypeString_Quotes_Text) );
1113 if (offsetQ != KErrNotFound)
1115 // skip the quote (") char
1116 boundary.Set( boundary.Right( lenB - offsetQ ) );
1119 // look for where to end
1121 TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
1122 strlen(Multipart_ContentTypeString_Quotes_Text) );
1123 if (offsetE == KErrNotFound)
1126 offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
1127 strlen(Multipart_ContentTypeString_Delimiter_Text) );
1129 if (offsetE != KErrNotFound)
1131 boundary.Set( boundary.Left( offsetE ) );
1134 // set it on to the input parameter
1135 aBodyPart->SetBoundary( boundary );
1136 } // end of if (offsetB != KErrNotFound)
1138 // check the charset information
1139 TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text,
1140 strlen(Multipart_Charset_Text) );
1141 if (offsetCh != KErrNotFound)
1143 // now, we are at the beginning of "charset=us-ascii" string.
1144 // move to the us-ascii part
1145 TPtrC8 charset = unrelated.Right( lenU -
1147 strlen( Multipart_Charset_Text ) );
1148 TInt lenCh = charset.Length();
1150 // look for where to end
1151 TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
1152 strlen(Multipart_ContentTypeString_Quotes_Text) );
1153 if (offsetQ != KErrNotFound)
1155 // skip the quote (") char
1156 charset.Set( charset.Right( lenCh - offsetQ ) );
1159 // look for where to end
1161 TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text,
1162 strlen(Multipart_ContentTypeString_Quotes_Text) );
1163 if (offsetE == KErrNotFound)
1166 offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text,
1167 strlen(Multipart_ContentTypeString_Delimiter_Text) );
1169 if (offsetE != KErrNotFound)
1171 charset.Set( charset.Left( offsetE ) );
1174 // set it on to the input parameter
1175 aBodyPart->SetCharset( charset );
1176 } // end of if (offsetCh != KErrNotFound)
1178 } // end of if( lenCT > offset )
1179 } // end of if (offset != KErrNotFound)
1183 // ----------------------------------------------------------------------------
1184 // MultipartParser::GetBodyPartUrl
1186 // Builds up the URL which refers to this particular body part
1187 // ----------------------------------------------------------------------------
1189 MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
1190 const TDesC8& aContentLocation,
1191 const TDesC16& aResponseUrl )
1193 // check on required parameters
1194 __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
1195 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
1197 // Body url builds up as follows:
1198 // Take global (respond header) Content Base header first.
1199 // If local (in the body part) Content Base header is present, then
1200 // take it as default Content Base header (by overruling global Content
1201 // Base header). Check if the Content Location header is either an
1202 // absolute or relative URL. If it is an absolute, then ignore the
1203 // Content Base, otherwise concatenate them.
1205 // No Content Base header + Content Location is relative URL
1206 // No Content Location header
1207 // Content Base header is relative URL
1208 TBool contentBaseInvalid = EFalse;
1209 HBufC16* url = NULL;
1211 if (aContentBase.Ptr())
1213 // Check if it is a relative url
1214 if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
1216 // Relative URL is not valid here as base location.
1217 contentBaseInvalid = ETrue;
1222 // no content base header
1223 contentBaseInvalid = ETrue;
1224 } // end of if (aContentBase)
1226 if (contentBaseInvalid)
1228 if( aResponseUrl.Ptr() )
1230 // Copy response url
1231 TInt lenU = aResponseUrl.Length();
1232 url = HBufC::NewLC( lenU + 1 );
1233 url->Des().Copy( aResponseUrl );
1234 url->Des().ZeroTerminate();
1239 // Copy global content "base" location
1240 TInt lenCB = aContentBase.Length();
1241 url = HBufC::NewLC( lenCB + 1 );
1242 url->Des().Copy( aContentBase );
1243 url->Des().ZeroTerminate();
1244 } // end of if (contentBaseInvalid)
1246 // Check if Content Localtion is valid
1247 if( aContentLocation.Ptr() )
1249 TInt lenCL = aContentLocation.Length();
1251 // If the Content Location is an absolute URL, then Content Base value is ignored,
1252 // otherwise the absolute path is going to be built unless Content Base is missing
1253 if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
1258 CleanupStack::PopAndDestroy(); // url
1261 // fill url with content location
1262 url = HBufC::NewL( lenCL + 1 );
1263 url->Des().Copy( aContentLocation );
1264 url->Des().ZeroTerminate();
1270 HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
1272 CleanupStack::PopAndDestroy(); // url
1277 } // end of if( aContentLocation
1282 CleanupStack::Pop(); // url
1290 // ----------------------------------------------------------------------------
1291 // MultipartParser::IsUrlRelativeL
1293 // ----------------------------------------------------------------------------
1295 MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
1297 // check on required parameters
1298 __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
1299 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
1301 TUriParser8 uriParser;
1303 User::LeaveIfError(uriParser.Parse(aUrl));
1305 if( uriParser.Extract(EUriScheme).Ptr() )
1316 // ----------------------------------------------------------------------------
1317 // MultipartParser::UrlRelToAbsL
1319 // Absolute path is built as : Base + Relative
1320 // ----------------------------------------------------------------------------
1322 MultipartParser::UrlRelToAbsL( TDesC16& aBase,
1323 const TDesC8& aRelativeUrl )
1325 // check on required parameters
1326 __ASSERT_ALWAYS( aBase.Ptr() != NULL,
1327 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
1328 __ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
1329 User::Panic(_L("MultipartParser Panic"), KErrArgument) );
1331 // length of absolute url
1333 // length of relative url
1335 TBool appendSlash = EFalse;
1336 // path of absolute url
1337 TPtrC16 path( NULL, 0 );
1339 // must to have aRelativeUrl
1340 User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
1342 TUriParser16 uriParser;
1343 User::LeaveIfError( uriParser.Parse( aBase ) );
1346 TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
1347 // must to have scheme
1348 User::LeaveIfNull( (TUint16*)scheme.Ptr() );
1349 len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
1351 // <user>:<password>@
1352 TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
1355 len += user.Length() + 1;
1359 TPtrC16 host( uriParser.Extract( EUriHost ) );
1360 // must to have host
1361 User::LeaveIfNull( (TUint16*)host.Ptr() );
1362 len += host.Length();
1365 TPtrC16 port( uriParser.Extract( EUriPort ) );
1368 len += port.Length();
1371 // If the relative url begins with "./", remove it
1372 TPtrC8 relativeUrl( NULL, 0 );
1373 TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
1374 TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
1375 if ( indexD == 0 && indexS == 1)
1377 // Found a dot-slash at beginning of relative url
1378 relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
1382 relativeUrl.Set( aRelativeUrl );
1385 lenR = relativeUrl.Length();
1387 // If the relative url begins with a slash, then it is an absolute path
1388 // Does relative url start with slash?
1389 indexS = relativeUrl.Locate( SLASH_CHAR );
1390 // no, need to extract path from base url
1394 path.Set( uriParser.Extract( EUriPath ) );
1397 // cut off the file path
1398 if ( path.LocateReverse( DOT_CHAR ) )
1400 // case: dir/index.html
1401 if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
1403 // to keep the slash
1404 path.Set( path.Left( indexS2 + 1 ) );
1409 path.Set( NULL, 0 );
1413 // figure out the end slash
1416 if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
1418 appendSlash = ETrue;
1421 len += path.Length();
1425 appendSlash = ETrue;
1429 // yes, no need to extract path from base url
1436 // In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator)
1437 // for proper String Termination
1441 HBufC16* urlAbs = HBufC16::NewL( len );
1442 TPtr16 urlAbsPtr = urlAbs->Des();
1444 // copy base into absolute url
1447 urlAbsPtr.Copy( scheme );
1448 urlAbsPtr.Append( COLON_CHAR );
1449 urlAbsPtr.Append( SLASH_CHAR );
1450 urlAbsPtr.Append( SLASH_CHAR );
1455 urlAbsPtr.Append( user );
1456 urlAbsPtr.Append( AT_CHAR );
1460 urlAbsPtr.Append( host );
1465 urlAbsPtr.Append( COLON_CHAR );
1466 urlAbsPtr.Append( port );
1472 urlAbsPtr.Append( path );
1475 // slash between path and relative url
1478 urlAbsPtr.Append( SLASH_CHAR );
1482 TUint16* relUrlInt = new TUint16[ lenR ];
1483 TPtr16 relUrl16( relUrlInt, lenR );
1484 relUrl16.Copy( relativeUrl );
1485 urlAbsPtr.Append( relUrl16 );
1489 urlAbsPtr.ZeroTerminate();
1494 // -------------------------------------------------------------------------
1495 // Composes multipart/mixed document
1496 // -------------------------------------------------------------------------
1498 MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
1499 const TDesC8& aBoundary,
1503 _LIT8(KBoundary, "--%S\r\n");
1504 HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
1505 boundary->Des().Format( KBoundary, &aBoundary );
1507 // Calculate the size of this document.
1509 // a. for each CBodyPart
1510 // boundaries + CRLF between headers and body (constant addition)
1511 bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
1512 for (TInt i = 0; i < aBodyArray.Count(); i++)
1514 if (!(aBodyArray[i]->Headers().Length() +
1515 aBodyArray[i]->Body().Length()))
1517 // one less boundary
1518 bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
1519 // skip empty bodypart
1522 bodySize += aBodyArray[i]->Headers().Length();
1523 // ensure there are only 2 CRLFs between header and body
1524 if (aBodyArray[i]->Headers().Length() > 0)
1526 TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
1527 TUint newEnd = bodyHeaders.Length() - 1;
1528 while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
1534 bodySize += strlen(Multipart_CRLF_Text);
1536 bodySize += aBodyArray[i]->Body().Length();
1537 // CRLF (end of body, add one only if there is body AND does not end with CRLF)
1538 TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
1539 if (bodyBody.Length() > 0
1540 && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
1542 bodySize += strlen(Multipart_CRLF_Text);
1545 // end boundary (boundary - '\r\n' + "--")
1546 bodySize += boundary->Length();
1547 TInt docSize = bodySize;
1548 // calculate the size of Headers
1549 _LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
1550 if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
1552 docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
1553 docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
1554 docSize += strlen(Multipart_CRLF_Text); // eol
1556 if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
1558 docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
1559 // calculate number of chars needed to represent bodySize
1560 HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
1561 bodySizeSize->Des().Num( bodySize );
1562 docSize += bodySizeSize->Length(); // content length (bodySize)
1563 docSize += strlen(Multipart_CRLF_Text); // eol
1564 CleanupStack::PopAndDestroy( bodySizeSize );
1566 if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
1568 docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
1569 docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
1570 docSize += strlen(Multipart_CRLF_Text); // eol
1572 // extra CRLF for separating header and body
1573 docSize += strlen(Multipart_CRLF_Text);
1574 // CALCULATION COMPLETE
1575 // at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
1576 // and docSize contains the size of the entire document (use it to create HBufC8*
1577 // of appropriate size)
1579 // construct multipart document
1580 HBufC8* document = HBufC8::NewLC(docSize);
1581 TPtr8 docAppend(document->Des());
1582 if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
1584 docAppend.Format( KContentType, &aBoundary );
1586 if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
1588 _LIT8( KContentLength, "Content-Length: %d\r\n" );
1589 docAppend.AppendFormat( KContentLength, bodySize );
1591 if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
1593 _LIT8( KLastModified, "Last-Modified: %S\r\n" );
1595 current.UniversalTime();
1596 TInternetDate modDate(current.DateTime());
1597 HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
1598 docAppend.AppendFormat( KLastModified, dateString );
1602 docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
1604 for (TInt i = 0; i < aBodyArray.Count(); i++)
1606 if (!(aBodyArray[i]->Headers().Length() +
1607 aBodyArray[i]->Body().Length()))
1609 // skip empty bodypart
1612 docAppend.Append( *boundary );
1613 TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
1614 while ( headerLength > 0 &&
1615 (aBodyArray[i]->Headers()[headerLength] == '\r'
1616 || aBodyArray[i]->Headers()[headerLength] == '\n' ))
1620 docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
1622 if ( headerLength > 0 )
1624 docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
1628 docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
1631 docAppend.Append(aBodyArray[i]->Body());
1632 // CRLF only if body exists and doesn't end with CRLF
1633 TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
1634 if (bodyBody.Length() > 0
1635 && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
1637 docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
1641 _LIT8(KEndBoundary, "--%S--");
1642 docAppend.AppendFormat(KEndBoundary, &aBoundary);
1643 CleanupStack::Pop( document );
1644 CleanupStack::PopAndDestroy( boundary );