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