os/ossrv/lowlevellibsandfws/apputils/multipartparser/src/multipartparser.cpp
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
sl@0
     1
// Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
sl@0
     2
// All rights reserved.
sl@0
     3
// This component and the accompanying materials are made available
sl@0
     4
// under the terms of "Eclipse Public License v1.0"
sl@0
     5
// which accompanies this distribution, and is available
sl@0
     6
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
sl@0
     7
//
sl@0
     8
// Initial Contributors:
sl@0
     9
// Nokia Corporation - initial contribution.
sl@0
    10
//
sl@0
    11
// Contributors:
sl@0
    12
//
sl@0
    13
// Description:
sl@0
    14
//
sl@0
    15
sl@0
    16
// INCLUDE FILES
sl@0
    17
#include <e32def.h>           // First to avoid NULL redefine warning (no #ifndef NULL).
sl@0
    18
#include <e32std.h>
sl@0
    19
#include <string.h>
sl@0
    20
#include <ezdecompressor.h>   // Ezlib.lib, GZip decoders
sl@0
    21
#include <uri8.h>
sl@0
    22
#include <uri16.h>
sl@0
    23
#include <uricommon.h>
sl@0
    24
sl@0
    25
#include <bafl/multipartparser.h>
sl@0
    26
#include "gzipbufmgr.h"
sl@0
    27
#include "tinternetdate.h"
sl@0
    28
sl@0
    29
#include <bafl/qpcodec.h>
sl@0
    30
#include <bsul/clientmessage.h>
sl@0
    31
sl@0
    32
// CONSTANTS
sl@0
    33
const char Multipart_Mixed[] = {"multipart/mixed"};
sl@0
    34
const char Multipart_Related[] = {"multipart/related"};
sl@0
    35
const char Multipart_Boundary_Text[] = {"boundary="};
sl@0
    36
const char Multipart_Content_Base_Text[] = {"Content-Base:"};
sl@0
    37
const char Multipart_Content_Location_Text[] = {"Content-Location:"};
sl@0
    38
const char Multipart_Content_Type_Text[] = {"Content-Type:"};
sl@0
    39
const char Multipart_Content_Transfer_Encoding_Text[] = {"Content-Transfer-Encoding:"};
sl@0
    40
// violate RFC2045; but upon customer request
sl@0
    41
const char Multipart_Content_Encoding_Text[] = {"Content-Encoding:"};
sl@0
    42
const char Multipart_Content_ID_Text[] = {"Content-ID:"};
sl@0
    43
const char Multipart_Hypens_Text[] = {"--"};
sl@0
    44
const char Multipart_CRLF_Text[] = {"\r\n"};
sl@0
    45
const char Multipart_LF_Text[] = {"\n"};
sl@0
    46
const char Multipart_DoubleCRLF_Text[] = {"\r\n\r\n"};
sl@0
    47
const char Multipart_DoubleLF_Text[] = {"\n\n"};
sl@0
    48
const char Multipart_Charset_Text[] = {"charset="};
sl@0
    49
const char Multipart_ContentTypeString_Delimiter_Text[] = {";"};
sl@0
    50
const char Multipart_ContentTypeString_Quotes_Text[] = {"\""};
sl@0
    51
const char Multipart_Content_Transfer_Encoding_Base64[] = {"base64"};
sl@0
    52
const char Multipart_Content_Transfer_Encoding_QuotedPrintable[] = {"quoted-printable"};
sl@0
    53
const char Multipart_Content_Transfer_Encoding_7bit[] = {"7bit"};
sl@0
    54
const char Multipart_Content_Transfer_Encoding_8bit[] = {"8bit"};
sl@0
    55
const char Multipart_Content_Transfer_Encoding_binary[] = {"binary"};
sl@0
    56
const char Multipart_Content_Encoding_GZip[] = {"gzip"};
sl@0
    57
const char Multipart_Content_Type_GZip[] = {"application/x-gzip"};
sl@0
    58
sl@0
    59
// MACROS
sl@0
    60
#define MULTIPART_CONTENT_BASE_LENGTH               13
sl@0
    61
#define MULTIPART_CONTENT_LOCATION_LENGTH           17
sl@0
    62
#define MULTIPART_CONTENT_TYPE_LENGTH               13
sl@0
    63
#define MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH  26
sl@0
    64
// violates RFC2045; but upon vodafone request
sl@0
    65
#define MULTIPART_CONTENT_ENCODING_LENGTH           17
sl@0
    66
#define MULTIPART_CONTENT_LENGTH_LENGTH             15
sl@0
    67
#define MULTIPART_LAST_MODIFIED_LENGTH              14
sl@0
    68
#define MULTIPART_CONTENT_ID_LENGTH                 11
sl@0
    69
sl@0
    70
#define MULTIPART_CONTENT_BASE               1
sl@0
    71
#define MULTIPART_CONTENT_LOCATION           2
sl@0
    72
#define MULTIPART_CONTENT_TRANSFER_ENCODING  3
sl@0
    73
#define MULTIPART_CONTENT_TYPE               4
sl@0
    74
#define MULTIPART_CONTENT_ID                 5
sl@0
    75
sl@0
    76
#define MULTIPART_INTERNET_DATE_STRING_LENGTH       29
sl@0
    77
sl@0
    78
#define SLASH_CHAR    '/'
sl@0
    79
#define DOT_CHAR      '.'
sl@0
    80
#define AT_CHAR       '@'
sl@0
    81
#define COLON_CHAR    ':'
sl@0
    82
// <scheme>://
sl@0
    83
#define SCHEME_SEPARATOR_LENGTH               3
sl@0
    84
sl@0
    85
sl@0
    86
// ============================= LOCAL FUNCTIONS ===============================
sl@0
    87
sl@0
    88
sl@0
    89
// ============================ MEMBER FUNCTIONS ===============================
sl@0
    90
sl@0
    91
sl@0
    92
// ------------------------------------------------------------------------- 
sl@0
    93
// Parse and put each body part to the body part array
sl@0
    94
// ------------------------------------------------------------------------- 
sl@0
    95
EXPORT_C void MultipartParser::ParseL( const TDesC8& aMultipartBody, 
sl@0
    96
                                       const TDesC8& aContentType,
sl@0
    97
                                       const TDesC8& aBoundary,
sl@0
    98
                                       const TDesC16& aUrl,
sl@0
    99
                                       RPointerArray <CBodyPart>& aBodyPartsArray,
sl@0
   100
                                       TInt aMaxToParse )
sl@0
   101
    {
sl@0
   102
    // check on required parameters
sl@0
   103
    __ASSERT_ALWAYS( aMultipartBody.Ptr() != NULL,
sl@0
   104
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   105
    __ASSERT_ALWAYS( aContentType.Ptr() != NULL,
sl@0
   106
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   107
    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
sl@0
   108
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   109
    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
sl@0
   110
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   111
sl@0
   112
    const TUint8* multipartBuffer = aMultipartBody.Ptr();
sl@0
   113
    TUint32 multipartLen = aMultipartBody.Length();
sl@0
   114
    __ASSERT_ALWAYS( multipartLen != 0,
sl@0
   115
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   116
    TUint8* bodyPartBuffer = NULL;
sl@0
   117
    TUint32 bodyPartBufferLength = 0;
sl@0
   118
    TUint32 startPosition = 0;
sl@0
   119
    char* singleEolChar = NULL;
sl@0
   120
    char* doubleEolChar = NULL;
sl@0
   121
    CBodyPart* bodyPart = NULL;
sl@0
   122
sl@0
   123
    // currently only support mixed and related.
sl@0
   124
    char* contentType = (char*)aContentType.Ptr();
sl@0
   125
    if( strncasecmp( contentType, Multipart_Mixed, strlen(Multipart_Mixed) ) != 0 && 
sl@0
   126
        strncasecmp( contentType, Multipart_Related, strlen(Multipart_Related) ) != 0 )
sl@0
   127
        {
sl@0
   128
        User::Leave( KErrNotSupported );
sl@0
   129
        }
sl@0
   130
sl@0
   131
    // get singleEol and doubleEol
sl@0
   132
    MultipartParser::SetEolCharacters( multipartBuffer,
sl@0
   133
                                       multipartLen,
sl@0
   134
                                       aBoundary,
sl@0
   135
                                       &singleEolChar,
sl@0
   136
                                       &doubleEolChar );
sl@0
   137
sl@0
   138
    // get body parts one by one
sl@0
   139
    // null bodyPartBuffer indicates the end of the multipart body
sl@0
   140
    int counter = 0;
sl@0
   141
    do
sl@0
   142
        {
sl@0
   143
        // stop when we get required number of parse done
sl@0
   144
        if( aMaxToParse != -1 && counter >= aMaxToParse )
sl@0
   145
            {
sl@0
   146
            break;
sl@0
   147
            }
sl@0
   148
        // update counter
sl@0
   149
        counter++;
sl@0
   150
sl@0
   151
        // get the next body part
sl@0
   152
        bodyPartBufferLength = MultipartParser::GetNextBodyPartBuffer( startPosition, 
sl@0
   153
                                                                       multipartBuffer,
sl@0
   154
                                                                       multipartLen,
sl@0
   155
                                                                       aBoundary,
sl@0
   156
                                                                       singleEolChar,
sl@0
   157
                                                                       &bodyPartBuffer );
sl@0
   158
        // break if we are at end
sl@0
   159
        if( bodyPartBuffer == NULL )
sl@0
   160
            {
sl@0
   161
            break;
sl@0
   162
            }
sl@0
   163
        // update start position
sl@0
   164
        startPosition += bodyPartBufferLength;
sl@0
   165
sl@0
   166
        // create new body part
sl@0
   167
        bodyPart = CBodyPart::NewL();
sl@0
   168
        // parse each body part buffer to fill in body part
sl@0
   169
        MultipartParser::ParseBodyPartL( bodyPartBuffer, 
sl@0
   170
                                         bodyPartBufferLength,
sl@0
   171
                                         singleEolChar,
sl@0
   172
                                         doubleEolChar,
sl@0
   173
                                         aUrl, 
sl@0
   174
                                         bodyPart );  
sl@0
   175
        // add the body part to the array
sl@0
   176
        aBodyPartsArray.Append( bodyPart );
sl@0
   177
        }
sl@0
   178
    while( bodyPartBuffer != NULL );
sl@0
   179
    }
sl@0
   180
sl@0
   181
// ------------------------------------------------------------------------- 
sl@0
   182
// Composes RFC1521 compliant multipart document with given bodyparts
sl@0
   183
// Actual task of creating the document is delegated to specialized composer
sl@0
   184
// for each of the subtypes
sl@0
   185
// ------------------------------------------------------------------------- 
sl@0
   186
EXPORT_C HBufC8* MultipartParser::ComposeL( RPointerArray<CBodyPart>& aBodyPartsArray,
sl@0
   187
                                            const TDesC8& aBoundary,
sl@0
   188
                                            TMultipartSubtype aSubtype,
sl@0
   189
                                            TInt aHeaderMask )
sl@0
   190
    {
sl@0
   191
    // check on required parameters
sl@0
   192
    if ( !aBoundary.Ptr() || !aBoundary.Length() )
sl@0
   193
        {
sl@0
   194
        User::Leave( KErrArgument );
sl@0
   195
        }
sl@0
   196
    
sl@0
   197
    HBufC8* multipartDoc = NULL;
sl@0
   198
    switch(aSubtype)
sl@0
   199
        {
sl@0
   200
        case EMultipartSubtypeMixed:
sl@0
   201
            {
sl@0
   202
            multipartDoc = ComposeMixedL( aBodyPartsArray,
sl@0
   203
                                          aBoundary,
sl@0
   204
                                          aHeaderMask );
sl@0
   205
            }
sl@0
   206
        break;
sl@0
   207
        default:
sl@0
   208
            {
sl@0
   209
            User::Leave( KErrArgument );
sl@0
   210
            }
sl@0
   211
        }
sl@0
   212
    return multipartDoc;
sl@0
   213
    }
sl@0
   214
sl@0
   215
// ------------------------------------------------------------------------- 
sl@0
   216
// Default constructor
sl@0
   217
// ------------------------------------------------------------------------- 
sl@0
   218
MultipartParser::MultipartParser()
sl@0
   219
	{
sl@0
   220
	}
sl@0
   221
sl@0
   222
// ------------------------------------------------------------------------- 
sl@0
   223
// Returns with the next body part buffer from start position (offset)
sl@0
   224
// ------------------------------------------------------------------------- 
sl@0
   225
TUint32
sl@0
   226
MultipartParser::GetNextBodyPartBuffer( TUint32 aStartPosition, 
sl@0
   227
                                        const TUint8* aMultipartBody,
sl@0
   228
                                        TUint32 aMultipartLen,
sl@0
   229
                                        const TDesC8& aBoundary,
sl@0
   230
                                        char* aSingleEolChar,
sl@0
   231
                                        TUint8** aBodyPartBuffer )
sl@0
   232
    {
sl@0
   233
    // check on required parameters
sl@0
   234
    __ASSERT_ALWAYS( aMultipartBody != NULL,
sl@0
   235
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   236
    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
sl@0
   237
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   238
    __ASSERT_ALWAYS( aSingleEolChar != NULL,
sl@0
   239
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   240
sl@0
   241
    long startOffset = -1;
sl@0
   242
    long endOffset = -1;
sl@0
   243
    TUint32 length = 0;
sl@0
   244
    TBool badContent = EFalse;
sl@0
   245
    TUint32 offset = 0;
sl@0
   246
    const char* boundaryStr = (const char*) aBoundary.Ptr();
sl@0
   247
    int boundaryLength = aBoundary.Length();
sl@0
   248
    int hypensLength = strlen( Multipart_Hypens_Text );
sl@0
   249
    int singleEolLength = strlen( aSingleEolChar );
sl@0
   250
    TUint32 i = aStartPosition;
sl@0
   251
sl@0
   252
    // from RFC 1341 7.2
sl@0
   253
    // Overall, the body of a multipart entity may be specified as follows:
sl@0
   254
    // multipart-body := preamble 1*encapsulation close-delimiter epilogue
sl@0
   255
    // encapsulation := delimiter CRLF body-part
sl@0
   256
    // delimiter := CRLF "--" boundary   ; taken from Content-Type field.
sl@0
   257
    // when content-type is multipart.
sl@0
   258
    // There must be no space between "--" and boundary.
sl@0
   259
    // close-delimiter := delimiter "--" ; Again, no  space  before
sl@0
   260
    // "--"
sl@0
   261
sl@0
   262
    // boundary = 12xy
sl@0
   263
    // body: here comes some text that we ignore
sl@0
   264
    //       --12xy
sl@0
   265
    //       first body
sl@0
   266
    //       --12xy
sl@0
   267
    //       second body
sl@0
   268
    //       --12xy--
sl@0
   269
    //       closing boundary. we ignore this text here
sl@0
   270
    while( i < (aMultipartLen - hypensLength + 1 ) )
sl@0
   271
        {
sl@0
   272
        // get the first two hypens
sl@0
   273
        // using char comparison to compare "--"
sl@0
   274
        // hopefully this is faster
sl@0
   275
        if( (char)aMultipartBody[ i ] == '-' && 
sl@0
   276
            (char)aMultipartBody[ i+1 ] == '-' )
sl@0
   277
          {
sl@0
   278
          char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
sl@0
   279
          // check if the body is long enough first and then check if boundary matches
sl@0
   280
          if( aMultipartLen >= i + hypensLength + boundaryLength )
sl@0
   281
            {
sl@0
   282
            if( strncasecmp( boundary, boundaryStr, boundaryLength ) == 0 )
sl@0
   283
              {
sl@0
   284
              // we've got the boundary
sl@0
   285
              offset = i + hypensLength + boundaryLength;
sl@0
   286
              // Next two chars must be either two hypens (closing boundary - 2 bytes),
sl@0
   287
              // or single Eol characters (new body).
sl@0
   288
              // Eol = CRLF (2 bytes - windows) or LF (1 byte - unix/mac).
sl@0
   289
              char* eolBuf = (char*)&aMultipartBody[ offset ];
sl@0
   290
              // Check if buffer is long enough for hypens [2 bytes], or eol [1 or 2 bytes]
sl@0
   291
              if( aMultipartLen >= offset + hypensLength )
sl@0
   292
                {
sl@0
   293
                 if( strncmp( eolBuf, aSingleEolChar, singleEolLength ) == 0|| 
sl@0
   294
                     eolBuf[0] == Multipart_LF_Text[0])
sl@0
   295
                   
sl@0
   296
                  {
sl@0
   297
                  // We found Eol, so this is a new multipart body (header and content)
sl@0
   298
                  if( startOffset == -1 )
sl@0
   299
                    {
sl@0
   300
                    // this is the beginning.
sl@0
   301
                    startOffset = offset;
sl@0
   302
                    // let's looking for the end of this body part which either could
sl@0
   303
                    // be closing boundary or an opening boundary for the next body part
sl@0
   304
sl@0
   305
                    // jump over the boundary information
sl@0
   306
                    i = startOffset + singleEolLength;
sl@0
   307
                    }
sl@0
   308
                  else
sl@0
   309
                    {
sl@0
   310
                    // We found the next boundary marker, so this is the
sl@0
   311
                    // beginning of the next body part
sl@0
   312
                    endOffset = offset - boundaryLength - hypensLength;
sl@0
   313
                    // we've got both start and end offset
sl@0
   314
                    break;
sl@0
   315
                    }
sl@0
   316
                  }
sl@0
   317
                 else if( strncmp( eolBuf, Multipart_Hypens_Text, hypensLength ) == 0 )
sl@0
   318
                   {
sl@0
   319
                    // We found the closing boundary marker
sl@0
   320
                    endOffset = offset - boundaryLength - hypensLength;
sl@0
   321
                    break;
sl@0
   322
                   }
sl@0
   323
                 else
sl@0
   324
                   {
sl@0
   325
                   // it's neither Eol nor two hypens "--"
sl@0
   326
                   badContent = ETrue;
sl@0
   327
                   break;
sl@0
   328
                   }
sl@0
   329
                }
sl@0
   330
              else
sl@0
   331
                {
sl@0
   332
                // the buffer is too short and not closed properly
sl@0
   333
                endOffset = i;
sl@0
   334
                break;
sl@0
   335
                }
sl@0
   336
              }
sl@0
   337
            }
sl@0
   338
          else
sl@0
   339
            {
sl@0
   340
            // the buffer is far too short
sl@0
   341
            endOffset = i;
sl@0
   342
            break;
sl@0
   343
            }
sl@0
   344
          }
sl@0
   345
        i++;
sl@0
   346
        } // end of while loop
sl@0
   347
sl@0
   348
    // missing closing boundary check
sl@0
   349
    if( endOffset == -1 )
sl@0
   350
        {
sl@0
   351
        // take the end of the body as closing boundary
sl@0
   352
        endOffset = i - 1;
sl@0
   353
        }
sl@0
   354
sl@0
   355
    if( badContent )
sl@0
   356
        {
sl@0
   357
        endOffset = -1;
sl@0
   358
        startOffset = -1;
sl@0
   359
        }
sl@0
   360
sl@0
   361
    if( startOffset != -1 && endOffset != -1 )
sl@0
   362
        {
sl@0
   363
        *aBodyPartBuffer = (TUint8*)&aMultipartBody[ startOffset ];
sl@0
   364
        length = endOffset - startOffset;
sl@0
   365
        }
sl@0
   366
    else
sl@0
   367
        {
sl@0
   368
        *aBodyPartBuffer = NULL;
sl@0
   369
        length = 0;
sl@0
   370
        }
sl@0
   371
sl@0
   372
    return length;
sl@0
   373
    }
sl@0
   374
sl@0
   375
sl@0
   376
// ------------------------------------------------------------------------- 
sl@0
   377
// Set End-Of-Line characters.  Look at the eol character after the boundary to
sl@0
   378
// determine if it is a CRLF (2 bytes used by windows), or LF (1 byte used by
sl@0
   379
// unix/mac).
sl@0
   380
sl@0
   381
// NOTE: CR = 0x0D = '\r', LF = 0x0A = '\n'
sl@0
   382
sl@0
   383
// Multipart entity is specified as follows:
sl@0
   384
// --boundary123[eol]
sl@0
   385
// Content-type: text/html[eol]
sl@0
   386
// Content-location: http:\\www.example.com\index.html[eol]
sl@0
   387
// [eol]
sl@0
   388
// <html><body>...example...</html>[eol]
sl@0
   389
// --boundary123--[eol]
sl@0
   390
// ------------------------------------------------------------------------- 
sl@0
   391
void
sl@0
   392
MultipartParser::SetEolCharacters( const TUint8* aMultipartBody,
sl@0
   393
                                   TUint32 aMultipartLen,
sl@0
   394
                                   const TDesC8& aBoundary,
sl@0
   395
                                   char** aSingleEolChar,
sl@0
   396
                                   char** aDoubleEolChar )
sl@0
   397
    {
sl@0
   398
    // check on required parameters
sl@0
   399
    __ASSERT_ALWAYS( aMultipartBody != NULL,
sl@0
   400
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   401
    __ASSERT_ALWAYS( aBoundary.Ptr() != NULL,
sl@0
   402
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   403
    __ASSERT_ALWAYS( aSingleEolChar != NULL,
sl@0
   404
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   405
    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
sl@0
   406
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   407
sl@0
   408
    TUint32 i = 0;
sl@0
   409
    const char* boundaryStr = (const char*) aBoundary.Ptr();
sl@0
   410
    int boundaryLength = aBoundary.Length();
sl@0
   411
    int hypensLength = strlen( Multipart_Hypens_Text );
sl@0
   412
    int lfLength = strlen( Multipart_LF_Text );
sl@0
   413
sl@0
   414
    // Set the default eol (CRLF)
sl@0
   415
    *aSingleEolChar = (char *)Multipart_CRLF_Text;
sl@0
   416
    *aDoubleEolChar = (char *)Multipart_DoubleCRLF_Text;
sl@0
   417
sl@0
   418
    while (i < (aMultipartLen - hypensLength + 1))
sl@0
   419
        {
sl@0
   420
        // Get the first two hypens
sl@0
   421
        char* bodyPart = (char*)&aMultipartBody[ i ];
sl@0
   422
        if (strncmp(bodyPart, Multipart_Hypens_Text, hypensLength) == 0)
sl@0
   423
            {
sl@0
   424
            char* boundary = (char*)&aMultipartBody[ i + hypensLength ];
sl@0
   425
            // Check if the body is long enough first and then check if boundary matches
sl@0
   426
            if (aMultipartLen >= i + hypensLength + boundaryLength )
sl@0
   427
                {
sl@0
   428
                if (strncasecmp(boundary, boundaryStr, boundaryLength) == 0)
sl@0
   429
                    {
sl@0
   430
                    // We've got the boundary
sl@0
   431
                    i = i + hypensLength + boundaryLength;
sl@0
   432
                    // Next two chars should be the single Eol characters.
sl@0
   433
                    // Eol = CRLF (2 bytes - windows), or LF (1 byte - unix/mac).
sl@0
   434
                    char* eolBuf = (char*)&aMultipartBody[ i ];
sl@0
   435
                    // Check if buffer is long enough for eol [1 byte LF]
sl@0
   436
                    if (aMultipartLen >= i + lfLength)
sl@0
   437
                        {
sl@0
   438
                        if (strncmp(eolBuf, Multipart_LF_Text, lfLength) == 0)
sl@0
   439
                            {
sl@0
   440
                            // We found LF Eol (unix/mac)
sl@0
   441
                            *aSingleEolChar = (char *)Multipart_LF_Text;
sl@0
   442
                            *aDoubleEolChar = (char *)Multipart_DoubleLF_Text;
sl@0
   443
                            } // end of if compare eol to LF
sl@0
   444
                        } // end of if buffer size ok
sl@0
   445
sl@0
   446
                    // Break in all cases, we will use the default CRLF if we don't
sl@0
   447
                    // find eol=LF, or the remaining buffer is too small
sl@0
   448
                    break;
sl@0
   449
                    } // end of compare/found boundary
sl@0
   450
                }
sl@0
   451
            } // end of looking for first two hypens
sl@0
   452
sl@0
   453
        ++i;
sl@0
   454
        } // end of while
sl@0
   455
    }
sl@0
   456
sl@0
   457
sl@0
   458
// ------------------------------------------------------------------------- 
sl@0
   459
// parse body
sl@0
   460
// The bodyPart parameter can contain the optional headers and response, or
sl@0
   461
// just the response.  In the case of both the (optional) header(s) and the
sl@0
   462
// response, let's cut off header(s) and return the response body.  The
sl@0
   463
// header is seperated from the response by two End-of-line (Eol) characters,
sl@0
   464
// i.e. two CRLF's (windows) or two LF's (unix/mac).
sl@0
   465
// --boundary123 (omitted from bodyPart parameter, starts next line)
sl@0
   466
// Content-type: text/html[eol]
sl@0
   467
// Content-location: http:\\www.example.com\index.html[eol]
sl@0
   468
// [eol]
sl@0
   469
// <html><body>example</body></html>
sl@0
   470
//
sl@0
   471
// In the case of no headers, there may be only one (or more) Eol characters.
sl@0
   472
// --boundary123 (omitted from bodyPart parameter, starts on next line)
sl@0
   473
// [eol]
sl@0
   474
// <html><body>example</body></html>
sl@0
   475
// ------------------------------------------------------------------------- 
sl@0
   476
void
sl@0
   477
MultipartParser::ParseBodyPartL( TUint8* aBodyPartBuffer,
sl@0
   478
                                 TUint32 aBodyPartBufferLength,
sl@0
   479
                                 char* aSingleEolChar,
sl@0
   480
                                 char* aDoubleEolChar,
sl@0
   481
                                 const TDesC16& aResponseUrl,
sl@0
   482
                                 CBodyPart* aBodyPart )
sl@0
   483
    {
sl@0
   484
    // check on required parameters
sl@0
   485
    __ASSERT_ALWAYS( aBodyPartBuffer != NULL,
sl@0
   486
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   487
    __ASSERT_ALWAYS( aSingleEolChar != NULL,
sl@0
   488
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   489
    __ASSERT_ALWAYS( aDoubleEolChar != NULL,
sl@0
   490
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   491
    __ASSERT_ALWAYS( aBodyPart != NULL,
sl@0
   492
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   493
    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
sl@0
   494
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   495
sl@0
   496
    // headers look something like this
sl@0
   497
    // we need to return "text/html" if the requested header is "Content-type"
sl@0
   498
    // --boundary123
sl@0
   499
    // Content-type: text/html
sl@0
   500
    // Content-location: http:\\www.example.com\index.html
sl@0
   501
    //
sl@0
   502
    // <html>
sl@0
   503
    // <body>
sl@0
   504
    // example
sl@0
   505
    int contentHeaderValueCharLen = 0;
sl@0
   506
    TPtrC8 contentHeaderValuePtr( NULL, 0 );
sl@0
   507
    int contentHeaderNameLength = 0;
sl@0
   508
sl@0
   509
    /*lint -e{668} Possibly passing a null pointer to function */
sl@0
   510
    int singleEolLength = strlen( aSingleEolChar );
sl@0
   511
    int doubleEolLength = strlen( aDoubleEolChar );
sl@0
   512
    // start looking for the header name
sl@0
   513
    for( TUint32 i = 0; i < aBodyPartBufferLength ; i++ )
sl@0
   514
        {
sl@0
   515
        int found = 0;
sl@0
   516
        const char* tempBodyPartBuffer = (char*)&aBodyPartBuffer[ i ];
sl@0
   517
sl@0
   518
        // Did we find the Content Header Value
sl@0
   519
	    if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Base_Text, MULTIPART_CONTENT_BASE_LENGTH ) == 0)
sl@0
   520
            {
sl@0
   521
            contentHeaderNameLength = MULTIPART_CONTENT_BASE_LENGTH;
sl@0
   522
            found = MULTIPART_CONTENT_BASE;
sl@0
   523
            }
sl@0
   524
	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Location_Text, MULTIPART_CONTENT_LOCATION_LENGTH ) == 0)
sl@0
   525
            {
sl@0
   526
            contentHeaderNameLength = MULTIPART_CONTENT_LOCATION_LENGTH;
sl@0
   527
            found = MULTIPART_CONTENT_LOCATION;
sl@0
   528
            }
sl@0
   529
	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Transfer_Encoding_Text, MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH ) == 0)
sl@0
   530
            {
sl@0
   531
            contentHeaderNameLength = MULTIPART_CONTENT_TRANSFER_ENCODING_LENGTH;
sl@0
   532
            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
sl@0
   533
            }     
sl@0
   534
	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Encoding_Text, MULTIPART_CONTENT_ENCODING_LENGTH ) == 0)
sl@0
   535
            {
sl@0
   536
            contentHeaderNameLength = MULTIPART_CONTENT_ENCODING_LENGTH;
sl@0
   537
            found = MULTIPART_CONTENT_TRANSFER_ENCODING;
sl@0
   538
            }
sl@0
   539
	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_Type_Text, MULTIPART_CONTENT_TYPE_LENGTH ) == 0)
sl@0
   540
            {
sl@0
   541
            contentHeaderNameLength = MULTIPART_CONTENT_TYPE_LENGTH;
sl@0
   542
            found = MULTIPART_CONTENT_TYPE;
sl@0
   543
            }
sl@0
   544
	    else if (strncasecmp( tempBodyPartBuffer, Multipart_Content_ID_Text, MULTIPART_CONTENT_ID_LENGTH ) == 0)
sl@0
   545
            {
sl@0
   546
            contentHeaderNameLength = MULTIPART_CONTENT_ID_LENGTH;
sl@0
   547
            found = MULTIPART_CONTENT_ID;
sl@0
   548
            }
sl@0
   549
sl@0
   550
        if (found)
sl@0
   551
            {        
sl@0
   552
            // skip spaces
sl@0
   553
            int startPos = i + contentHeaderNameLength;
sl@0
   554
            while ( (char)aBodyPartBuffer[ startPos ] == ' ' )
sl@0
   555
                {
sl@0
   556
                startPos++;
sl@0
   557
                }
sl@0
   558
sl@0
   559
            // used for finding '<' in Content-ID field
sl@0
   560
            char charFirst = aBodyPartBuffer[ startPos ];
sl@0
   561
            // content headers are closed with End-Of-Line (Eol) character
sl@0
   562
            for( TUint32 j = startPos; j < aBodyPartBufferLength - singleEolLength + 1; j++ )
sl@0
   563
                {
sl@0
   564
                char* tmpContentHeaderValue = (char*)&aBodyPartBuffer[ j ];
sl@0
   565
                if( strncmp( tmpContentHeaderValue, aSingleEolChar, singleEolLength ) == 0 
sl@0
   566
                   || tmpContentHeaderValue[0] == Multipart_LF_Text[0])
sl@0
   567
                    {
sl@0
   568
                    if( found == MULTIPART_CONTENT_ID )
sl@0
   569
                        {
sl@0
   570
                        if( charFirst == '<' )
sl@0
   571
                            {
sl@0
   572
                            // length of the value excluding beginging '<' and ending '>'
sl@0
   573
                            contentHeaderValueCharLen = j - startPos - 2;
sl@0
   574
                            contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos+1 ], contentHeaderValueCharLen );
sl@0
   575
                            }
sl@0
   576
                        }
sl@0
   577
                    else
sl@0
   578
                        {
sl@0
   579
                        // length of the value
sl@0
   580
                        contentHeaderValueCharLen = j - startPos;
sl@0
   581
                        contentHeaderValuePtr.Set( (TUint8*)&aBodyPartBuffer[ startPos ], contentHeaderValueCharLen );
sl@0
   582
                        }
sl@0
   583
sl@0
   584
                    // rewind so the double EOL will be checked against later
sl@0
   585
                    i = j - 1;
sl@0
   586
                    // break the inner loop
sl@0
   587
                    break;
sl@0
   588
                    }              
sl@0
   589
                } // end of inner for loop
sl@0
   590
sl@0
   591
            switch( found )
sl@0
   592
                {
sl@0
   593
                case MULTIPART_CONTENT_BASE:
sl@0
   594
                    aBodyPart->SetContentBase( contentHeaderValuePtr );
sl@0
   595
                    break;
sl@0
   596
                case MULTIPART_CONTENT_LOCATION:
sl@0
   597
                    aBodyPart->SetContentLocation( contentHeaderValuePtr );
sl@0
   598
                    break;
sl@0
   599
                case MULTIPART_CONTENT_TRANSFER_ENCODING:
sl@0
   600
                    aBodyPart->SetContentTransferEncoding( contentHeaderValuePtr );
sl@0
   601
                    break;
sl@0
   602
                case MULTIPART_CONTENT_TYPE:
sl@0
   603
                    aBodyPart->SetContentType( contentHeaderValuePtr );
sl@0
   604
                    break;
sl@0
   605
                case MULTIPART_CONTENT_ID:
sl@0
   606
                    aBodyPart->SetContentID( contentHeaderValuePtr );
sl@0
   607
                    break;
sl@0
   608
                default:
sl@0
   609
                    break;
sl@0
   610
                } 
sl@0
   611
            } // end of if (found) 
sl@0
   612
sl@0
   613
        // Did we get to the end of the Content Header. Many of the Content Header Values
sl@0
   614
	    // are optional, so we could get to the end of the Content Header (double Eol) and
sl@0
   615
	    // not find the Content Header Value we were searching for.
sl@0
   616
        // get the response body
sl@0
   617
	    int aEolLength = strlen(Multipart_DoubleLF_Text);
sl@0
   618
	    if (strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength ) == 0 ||
sl@0
   619
	        strncmp( tempBodyPartBuffer, Multipart_DoubleLF_Text,aEolLength)== 0)
sl@0
   620
            {
sl@0
   621
            if(strncmp( tempBodyPartBuffer, aDoubleEolChar, doubleEolLength)== 0)
sl@0
   622
               aEolLength = doubleEolLength;
sl@0
   623
            // set body
sl@0
   624
            TUint8* responseBody = (TUint8*) &tempBodyPartBuffer[aEolLength];
sl@0
   625
            int lengthBody = aBodyPartBufferLength - ( i + aEolLength );
sl@0
   626
            TPtrC8 body( responseBody, lengthBody );
sl@0
   627
            aBodyPart->SetBody( body );
sl@0
   628
sl@0
   629
            // set headers when we have headers
sl@0
   630
            if( i != 0 )
sl@0
   631
                {
sl@0
   632
                // jump over the starting single EOL
sl@0
   633
                TUint8* responseHeaders = (TUint8*) &aBodyPartBuffer[ singleEolLength ];
sl@0
   634
                // // jump over the starting single EOL and the ending double EOL
sl@0
   635
                int lengthHeaders = aBodyPartBufferLength - lengthBody - singleEolLength - aEolLength;
sl@0
   636
                TPtrC8 headers( responseHeaders, lengthHeaders );
sl@0
   637
                aBodyPart->SetHeaders( headers );
sl@0
   638
                }
sl@0
   639
sl@0
   640
            break;
sl@0
   641
            }
sl@0
   642
       } // end of outter for loop
sl@0
   643
sl@0
   644
    // prepare more on body part
sl@0
   645
sl@0
   646
    // Check to see if we have a Content-Transfer-Encoding.
sl@0
   647
    TUint8* contentTransferEncodingValue = (TUint8*) aBodyPart->ContentTransferEncoding().Ptr();
sl@0
   648
    // If we have Content-Transfer-Encoding, prepare to decode
sl@0
   649
    if( MultipartParser::IsEncoded(contentTransferEncodingValue) )
sl@0
   650
        {
sl@0
   651
        // Initialize the encoded body, input
sl@0
   652
        TPtrC8 encodedBody( aBodyPart->Body() );
sl@0
   653
sl@0
   654
        // This will contain the "decoded" data.
sl@0
   655
        // The memory (decodedBody.Ptr) is owned by this method, but allocated by
sl@0
   656
        // the DecodeContentTransferEncoding method.
sl@0
   657
        TPtr8 decodedBody( NULL, 0, 0 );
sl@0
   658
sl@0
   659
        // The decoded data will return in decodedBody.Ptr.
sl@0
   660
        // The memory allocated is owned by this method.
sl@0
   661
        TInt err = MultipartParser::DecodeContentTransferEncoding( contentTransferEncodingValue,
sl@0
   662
                                                                   encodedBody,
sl@0
   663
                                                                   decodedBody );                    
sl@0
   664
        User::LeaveIfError(err);
sl@0
   665
sl@0
   666
        // The responseBody pointer is an offset into the response
sl@0
   667
        // buffer, do not delete. Substitute the decodedBody pointer.
sl@0
   668
        aBodyPart->SetBody( decodedBody );
sl@0
   669
        aBodyPart->SetIsDecodedBody( ETrue );
sl@0
   670
        }   // end of if (contentTransferEncodingValue)
sl@0
   671
sl@0
   672
    // Check to see if we have a Content-Type.
sl@0
   673
    TUint8* contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
sl@0
   674
    // parse contentType to get new contentType, charset, boundary info
sl@0
   675
    if( contentTypeValue )
sl@0
   676
        {
sl@0
   677
        MultipartParser::CutOffContentTypeAttributes( aBodyPart );
sl@0
   678
        // updated content type
sl@0
   679
        contentTypeValue = (TUint8*) aBodyPart->ContentType().Ptr();
sl@0
   680
        }
sl@0
   681
sl@0
   682
    // If we have zipped Content-Type, prepare to unzip
sl@0
   683
    if( MultipartParser::IsZipped(contentTypeValue) )
sl@0
   684
        {
sl@0
   685
        // Initialize the zipped body, input
sl@0
   686
        TPtrC8 zippedBody( aBodyPart->Body() );
sl@0
   687
sl@0
   688
        // This will contain the "unzipped" data.
sl@0
   689
        // The memory (unzippedBody.Ptr) is owned by this method, but allocated by
sl@0
   690
        // the Unzip method.
sl@0
   691
        TPtr8 unzippedBody( NULL, 0, 0 );
sl@0
   692
sl@0
   693
        // The unzipped data will return in unzippedBody.Ptr.
sl@0
   694
        // The memory allocated is owned by this method.
sl@0
   695
        TInt err = MultipartParser::Unzip( contentTypeValue,
sl@0
   696
                                           zippedBody,
sl@0
   697
                                           unzippedBody );                    
sl@0
   698
        User::LeaveIfError(err);
sl@0
   699
sl@0
   700
        if( aBodyPart->IsDecodedBody() )
sl@0
   701
            {
sl@0
   702
            // old body is not the original buffer, delete it
sl@0
   703
            delete (TUint8*) aBodyPart->Body().Ptr();
sl@0
   704
            }
sl@0
   705
        // unzip happend, use unzippedBody; delete decodedBody
sl@0
   706
        else
sl@0
   707
            {
sl@0
   708
            // The responseBody pointer is an offset into the response
sl@0
   709
            // buffer, do not delete. Substitute the decodedBody pointer.
sl@0
   710
sl@0
   711
            aBodyPart->SetIsDecodedBody( ETrue );
sl@0
   712
            }
sl@0
   713
sl@0
   714
        aBodyPart->SetBody( unzippedBody );
sl@0
   715
        }
sl@0
   716
sl@0
   717
    // Get the url of the current body part
sl@0
   718
    HBufC16* responseUrl = MultipartParser::GetBodyPartUrlL( aBodyPart->ContentBase(),
sl@0
   719
                                                             aBodyPart->ContentLocation(),
sl@0
   720
                                                             aResponseUrl );
sl@0
   721
    aBodyPart->SetUrl( responseUrl );
sl@0
   722
    }
sl@0
   723
sl@0
   724
sl@0
   725
// ------------------------------------------------------------------------- 
sl@0
   726
// From RFC 2045 Multipurpose Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies
sl@0
   727
//   Section 6.2.  Content-Transfer-Encodings Semantics
sl@0
   728
//
sl@0
   729
//   The Content-Transfer-Encoding values "7bit", "8bit", and "binary" all
sl@0
   730
//   mean that the identity (i.e. NO) encoding transformation has been
sl@0
   731
//   performed.  As such, they serve simply as indicators of the domain of
sl@0
   732
//   the body data, and provide useful information about the sort of
sl@0
   733
//   encoding that might be needed for transmission in a given transport
sl@0
   734
//   system.  The terms "7bit data", "8bit data", and "binary data" are
sl@0
   735
//   all defined in Section 2.
sl@0
   736
sl@0
   737
//  Returns true if contentTransferEncodingValue is neither NULL nor a domain.
sl@0
   738
// ------------------------------------------------------------------------- 
sl@0
   739
TBool MultipartParser::IsEncoded( TUint8* aContentTransferEncodingValue )
sl@0
   740
    {        
sl@0
   741
    if( !aContentTransferEncodingValue )
sl@0
   742
        {
sl@0
   743
        return EFalse;
sl@0
   744
        }
sl@0
   745
sl@0
   746
    char* encoding = (char*)aContentTransferEncodingValue;
sl@0
   747
sl@0
   748
    if ( strncasecmp( encoding,
sl@0
   749
                      Multipart_Content_Transfer_Encoding_7bit,
sl@0
   750
                      strlen(Multipart_Content_Transfer_Encoding_7bit) ) == 0 )
sl@0
   751
        {
sl@0
   752
        return EFalse;
sl@0
   753
        }
sl@0
   754
sl@0
   755
    if ( strncasecmp( encoding,
sl@0
   756
                      Multipart_Content_Transfer_Encoding_8bit,
sl@0
   757
                      strlen(Multipart_Content_Transfer_Encoding_8bit) ) == 0 )
sl@0
   758
        {
sl@0
   759
        return EFalse;
sl@0
   760
        }
sl@0
   761
sl@0
   762
    if ( strncasecmp( encoding,
sl@0
   763
                      Multipart_Content_Transfer_Encoding_binary,
sl@0
   764
                      strlen(Multipart_Content_Transfer_Encoding_binary) ) == 0 )
sl@0
   765
        {
sl@0
   766
        return EFalse;
sl@0
   767
        }      
sl@0
   768
sl@0
   769
    return ETrue;
sl@0
   770
    }
sl@0
   771
sl@0
   772
sl@0
   773
// ----------------------------------------------------------------------------
sl@0
   774
// DecodeContentTransferEncoding
sl@0
   775
//
sl@0
   776
// Decodes the Content-Transfer-Encoding.  The returned length of decodedBody
sl@0
   777
// is zero if decoding failed.
sl@0
   778
// NOTES:
sl@0
   779
// 1. This method should be called with a non-null string, i.e.
sl@0
   780
// aContentTransferEncodingValue.
sl@0
   781
// 2. Memory is allocated in this method, but ownership is with the calling method.
sl@0
   782
// ----------------------------------------------------------------------------
sl@0
   783
TInt
sl@0
   784
MultipartParser::DecodeContentTransferEncoding( TUint8* aContentTransferEncodingValue,
sl@0
   785
                                                const TDesC8& aEncodedBody,
sl@0
   786
                                                TPtr8& aDecodedBody )
sl@0
   787
    {
sl@0
   788
    // check on required parameters
sl@0
   789
    __ASSERT_ALWAYS( aContentTransferEncodingValue != NULL,
sl@0
   790
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   791
    __ASSERT_ALWAYS( aEncodedBody.Ptr() != NULL,
sl@0
   792
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   793
sl@0
   794
    TInt status = KErrNone;
sl@0
   795
    char* contentTransferEncodingString = (char*)aContentTransferEncodingValue;
sl@0
   796
sl@0
   797
    // Set the decodedBody.Length to zero, length > 0 if successful decode
sl@0
   798
    aDecodedBody.SetLength(0);
sl@0
   799
sl@0
   800
    // Is the Content-Transfer-Encoding = "base64"
sl@0
   801
    if( strncasecmp( contentTransferEncodingString,
sl@0
   802
                     Multipart_Content_Transfer_Encoding_Base64, 
sl@0
   803
                     strlen(Multipart_Content_Transfer_Encoding_Base64) ) == 0 )
sl@0
   804
        {
sl@0
   805
        // The decoded length of base64 is about half (use same) encoded length
sl@0
   806
        TUint maxBodyLength = aEncodedBody.Length();
sl@0
   807
        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
sl@0
   808
        if( decodedDataPtr )
sl@0
   809
            {
sl@0
   810
            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
sl@0
   811
sl@0
   812
			using namespace BSUL;
sl@0
   813
            // Decode the base64 Content-Transfer-Encoding
sl@0
   814
            Base64Codec::Decode(aEncodedBody, aDecodedBody);
sl@0
   815
sl@0
   816
            if (aDecodedBody.Length() == 0)
sl@0
   817
                {
sl@0
   818
                status = KErrGeneral;
sl@0
   819
                }
sl@0
   820
            }
sl@0
   821
        else // decodedDataPtr is NULL
sl@0
   822
            {
sl@0
   823
            status = KErrNoMemory;
sl@0
   824
            }
sl@0
   825
        }   // end of base64 decoding
sl@0
   826
sl@0
   827
    // Is the Content-Transfer-Encoding = "quoted-printable"
sl@0
   828
    else if( strncasecmp( contentTransferEncodingString,
sl@0
   829
                          Multipart_Content_Transfer_Encoding_QuotedPrintable, 
sl@0
   830
                          strlen(Multipart_Content_Transfer_Encoding_QuotedPrintable) ) == 0 )
sl@0
   831
        {
sl@0
   832
        // The decoded length of QP is the same as the encoded length
sl@0
   833
        TUint maxBodyLength = aEncodedBody.Length();
sl@0
   834
        TUint8* decodedDataPtr = new TUint8[maxBodyLength];
sl@0
   835
        if( decodedDataPtr )
sl@0
   836
            {
sl@0
   837
            aDecodedBody.Set(decodedDataPtr, 0, maxBodyLength);
sl@0
   838
sl@0
   839
            // Decode the quoted-printable Content-Transfer-Encoding
sl@0
   840
            QuotedPrintableCodec::Decode(aEncodedBody, aDecodedBody);
sl@0
   841
sl@0
   842
            if (aDecodedBody.Length() == 0)
sl@0
   843
                {
sl@0
   844
                status = KErrGeneral;
sl@0
   845
                }
sl@0
   846
            }
sl@0
   847
        else // decodedDataPtr is NULL
sl@0
   848
            {
sl@0
   849
            status = KErrNoMemory;
sl@0
   850
            }
sl@0
   851
        }   // end of quoted-printed decoding
sl@0
   852
sl@0
   853
    // Is the Content-Encoding = "gzip"
sl@0
   854
    else if( strncasecmp( contentTransferEncodingString,
sl@0
   855
                          Multipart_Content_Encoding_GZip, 
sl@0
   856
                          strlen(Multipart_Content_Encoding_GZip) ) == 0 )
sl@0
   857
        {
sl@0
   858
        // Our GZip decoder parts
sl@0
   859
        GZipBufMgr* gZipBufMgr = NULL;
sl@0
   860
        CEZDecompressor* ezDecompressor = NULL;
sl@0
   861
sl@0
   862
        // We have gzip, lets decompress the encoded data.
sl@0
   863
        // Set up the encoded data into a GZip buffer manager.
sl@0
   864
        TInt err = 0;
sl@0
   865
        TRAP(err, gZipBufMgr = GZipBufMgr::NewL( aEncodedBody ));
sl@0
   866
sl@0
   867
        // Get the GZip decompressor
sl@0
   868
        if( gZipBufMgr )
sl@0
   869
            {
sl@0
   870
            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
sl@0
   871
sl@0
   872
            // Inflate the GZip data
sl@0
   873
            if( ezDecompressor )
sl@0
   874
                {
sl@0
   875
                TRAP(err, ezDecompressor->InflateL());
sl@0
   876
                // Set the finalize flag
sl@0
   877
                if (err == KErrNone)
sl@0
   878
                    {
sl@0
   879
                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
sl@0
   880
                    // Get the inflated data, it is much larger then the encoded data
sl@0
   881
                    if (err == KErrNone)
sl@0
   882
                        {
sl@0
   883
                        TPtrC8 output = ezDecompressor->OutputDescriptor();
sl@0
   884
                        if (output.Length() != 0)
sl@0
   885
                            {
sl@0
   886
                            TInt size = output.Size();
sl@0
   887
                            TUint8* outBuf = new TUint8[size];
sl@0
   888
                            if( outBuf )
sl@0
   889
                                {
sl@0
   890
                                memcpy(outBuf, output.Ptr(), size);
sl@0
   891
sl@0
   892
                                aDecodedBody.Set((TUint8*)outBuf, size, size);
sl@0
   893
                                }
sl@0
   894
                            else // outBuf is NULL
sl@0
   895
                                {
sl@0
   896
                                status = KErrNoMemory;
sl@0
   897
                                }
sl@0
   898
                            }
sl@0
   899
                        else
sl@0
   900
                            {
sl@0
   901
                            status = KErrGeneral;
sl@0
   902
                            }
sl@0
   903
                        }
sl@0
   904
                    else
sl@0
   905
                        {
sl@0
   906
                        status = KErrGeneral;
sl@0
   907
                        }
sl@0
   908
                    }
sl@0
   909
                else
sl@0
   910
                    {
sl@0
   911
                    status = KErrGeneral;
sl@0
   912
                    }
sl@0
   913
                }
sl@0
   914
            else // ezDecompressor is NULL
sl@0
   915
                {
sl@0
   916
                status = KErrNoMemory;
sl@0
   917
                }
sl@0
   918
            }
sl@0
   919
        else // gZipBufMgr is NULL
sl@0
   920
            {
sl@0
   921
            status = KErrNoMemory;
sl@0
   922
            }
sl@0
   923
sl@0
   924
        // Clean up our memory
sl@0
   925
        delete gZipBufMgr;
sl@0
   926
        delete ezDecompressor;
sl@0
   927
        }   // end of gzip 
sl@0
   928
sl@0
   929
    // We can add additional decodings here.
sl@0
   930
    // When adding additional decoding be aware of the decodedBody.Ptr()
sl@0
   931
    // max size. Do the realloc here, AND allow the decodedBody.Ptr()
sl@0
   932
    // ownership to be passed back to calling method.
sl@0
   933
sl@0
   934
    return status;
sl@0
   935
    }
sl@0
   936
sl@0
   937
    
sl@0
   938
// ------------------------------------------------------------------------- 
sl@0
   939
// only support application/x-gzip
sl@0
   940
// ------------------------------------------------------------------------- 
sl@0
   941
TBool MultipartParser::IsZipped( TUint8* aContentTypeValue )
sl@0
   942
    {        
sl@0
   943
    if( !aContentTypeValue )
sl@0
   944
        {
sl@0
   945
        return EFalse;
sl@0
   946
        }
sl@0
   947
sl@0
   948
    char* contentType = (char*)aContentTypeValue;
sl@0
   949
sl@0
   950
    if ( strncasecmp( contentType,
sl@0
   951
                      Multipart_Content_Type_GZip,
sl@0
   952
                      strlen(Multipart_Content_Type_GZip) ) == 0 )
sl@0
   953
        {
sl@0
   954
        return ETrue;
sl@0
   955
        }
sl@0
   956
sl@0
   957
    return EFalse;
sl@0
   958
    }
sl@0
   959
sl@0
   960
sl@0
   961
// ----------------------------------------------------------------------------
sl@0
   962
// Unzip
sl@0
   963
//
sl@0
   964
// Unzip the .gz.  The returned length of unzippedBody
sl@0
   965
// is zero if unzip failed.
sl@0
   966
// NOTES:
sl@0
   967
// 1. This method should be called with a non-null string, i.e.
sl@0
   968
// aContentType.
sl@0
   969
// 2. Memory is allocated in this method, but ownership is with the calling method.
sl@0
   970
// ----------------------------------------------------------------------------
sl@0
   971
TInt
sl@0
   972
MultipartParser::Unzip( TUint8* aContentType,
sl@0
   973
                        const TDesC8& aZippedBody,
sl@0
   974
                        TPtr8& aUnzippedBody )
sl@0
   975
    {
sl@0
   976
    // check on required parameters
sl@0
   977
    __ASSERT_ALWAYS( aContentType != NULL,
sl@0
   978
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   979
    __ASSERT_ALWAYS( aZippedBody.Ptr() != NULL,
sl@0
   980
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
   981
sl@0
   982
    TInt status = KErrNone;
sl@0
   983
    char* contentTypeStr = (char*)aContentType;
sl@0
   984
sl@0
   985
    // Set the aUnzippedBody.Length to zero, length > 0 if successful decode
sl@0
   986
    aUnzippedBody.SetLength(0);
sl@0
   987
sl@0
   988
    // Our GZip decoder parts
sl@0
   989
    GZipBufMgr* gZipBufMgr = NULL;
sl@0
   990
    CEZDecompressor* ezDecompressor = NULL;
sl@0
   991
sl@0
   992
    // Is the Content-Type = "application/x-gzip"
sl@0
   993
    if( strncasecmp( contentTypeStr,
sl@0
   994
                     Multipart_Content_Type_GZip, 
sl@0
   995
                     strlen(Multipart_Content_Type_GZip) ) == 0 )
sl@0
   996
        {
sl@0
   997
        // We have gzip, lets decompress the encoded data.
sl@0
   998
        // Set up the encoded data into a GZip buffer manager.
sl@0
   999
        TInt err = 0;
sl@0
  1000
        TRAP(err, gZipBufMgr = GZipBufMgr::NewL(aZippedBody));
sl@0
  1001
sl@0
  1002
        // Get the GZip decompressor
sl@0
  1003
        if( gZipBufMgr )
sl@0
  1004
            {
sl@0
  1005
            TRAP(err, ezDecompressor = CEZDecompressor::NewL(*gZipBufMgr, -CEZDecompressor::EMaxWBits));
sl@0
  1006
sl@0
  1007
            // Inflate the GZip data
sl@0
  1008
            if( ezDecompressor )
sl@0
  1009
                {
sl@0
  1010
                TRAP(err, ezDecompressor->InflateL());
sl@0
  1011
                // Set the finalize flag
sl@0
  1012
                if (err == KErrNone)
sl@0
  1013
                    {
sl@0
  1014
                    TRAP(err, gZipBufMgr->FinalizeL(*ezDecompressor));
sl@0
  1015
                    // Get the inflated data, it is much larger then the encoded data
sl@0
  1016
                    if (err == KErrNone)
sl@0
  1017
                        {
sl@0
  1018
                        TPtrC8 output = ezDecompressor->OutputDescriptor();
sl@0
  1019
                        if (output.Length() != 0)
sl@0
  1020
                            {
sl@0
  1021
                            TInt size = output.Size();
sl@0
  1022
                            TUint8* outBuf = new TUint8[size];
sl@0
  1023
                            if( outBuf )
sl@0
  1024
                                {
sl@0
  1025
                                memcpy(outBuf, output.Ptr(), size);
sl@0
  1026
sl@0
  1027
                                aUnzippedBody.Set((TUint8*)outBuf, size, size);
sl@0
  1028
                                }
sl@0
  1029
                            else // outBuf is NULL
sl@0
  1030
                                {
sl@0
  1031
                                status = KErrNoMemory;
sl@0
  1032
                                }
sl@0
  1033
                            }
sl@0
  1034
                        else
sl@0
  1035
                            {
sl@0
  1036
                            status = KErrGeneral;
sl@0
  1037
                            }
sl@0
  1038
                        }
sl@0
  1039
                    else
sl@0
  1040
                        {
sl@0
  1041
                        status = KErrGeneral;
sl@0
  1042
                        }
sl@0
  1043
                    }
sl@0
  1044
                else
sl@0
  1045
                    {
sl@0
  1046
                    status = KErrGeneral;
sl@0
  1047
                    }
sl@0
  1048
                }
sl@0
  1049
            else // ezDecompressor is NULL
sl@0
  1050
                {
sl@0
  1051
                status = KErrNoMemory;
sl@0
  1052
                }
sl@0
  1053
            }
sl@0
  1054
        else // gZipBufMgr is NULL
sl@0
  1055
            {
sl@0
  1056
            status = KErrNoMemory;
sl@0
  1057
            }
sl@0
  1058
        }   // end of gzip 
sl@0
  1059
sl@0
  1060
    // Clean up our memory
sl@0
  1061
    delete gZipBufMgr;
sl@0
  1062
    delete ezDecompressor;
sl@0
  1063
sl@0
  1064
    return status;
sl@0
  1065
    }
sl@0
  1066
sl@0
  1067
sl@0
  1068
// ----------------------------------------------------------------------------
sl@0
  1069
// It cuts off the charset value from the content type header
sl@0
  1070
// content type string looks like as follows:
sl@0
  1071
// text/plain; charset=us-ascii; boundary="abc"
sl@0
  1072
// ----------------------------------------------------------------------------
sl@0
  1073
void
sl@0
  1074
MultipartParser::CutOffContentTypeAttributes( CBodyPart* aBodyPart ) 
sl@0
  1075
    {
sl@0
  1076
    // check on required parameters
sl@0
  1077
    __ASSERT_ALWAYS( aBodyPart != NULL,
sl@0
  1078
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
  1079
sl@0
  1080
    TPtrC8 aContentType( aBodyPart->ContentType() );
sl@0
  1081
sl@0
  1082
    // check if there is a delimiter ';'
sl@0
  1083
    TInt lenCT = aContentType.Length();
sl@0
  1084
    TInt offset = aContentType.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
sl@0
  1085
                                     strlen(Multipart_ContentTypeString_Delimiter_Text) );
sl@0
  1086
    if (offset != KErrNotFound)
sl@0
  1087
        {
sl@0
  1088
        // ; is meant to be the end of the content type value.
sl@0
  1089
        // cut off content type unrelated part
sl@0
  1090
        aBodyPart->SetContentType( aContentType.Left( offset ) );
sl@0
  1091
sl@0
  1092
        // extract boundary and charset info
sl@0
  1093
        if( lenCT > offset )
sl@0
  1094
            {
sl@0
  1095
            TPtrC8 unrelated = aContentType.Right( lenCT - offset );
sl@0
  1096
            TInt lenU = unrelated.Length();
sl@0
  1097
            
sl@0
  1098
            // check the boundary information
sl@0
  1099
            TInt offsetB = unrelated.Find( (TUint8*)Multipart_Boundary_Text, 
sl@0
  1100
                                           strlen(Multipart_Boundary_Text) );
sl@0
  1101
            if (offsetB != KErrNotFound)
sl@0
  1102
                {
sl@0
  1103
                // now, we are at the beginning of "boundary="abc" string.
sl@0
  1104
                // move to the "abc" part
sl@0
  1105
                TPtrC8 boundary = unrelated.Right( lenU - 
sl@0
  1106
                                                   offsetB - 
sl@0
  1107
                                                   strlen( Multipart_Boundary_Text ) );
sl@0
  1108
                TInt lenB = boundary.Length();
sl@0
  1109
sl@0
  1110
                // look for where to end
sl@0
  1111
                TInt offsetQ = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
sl@0
  1112
                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
sl@0
  1113
                if (offsetQ != KErrNotFound)
sl@0
  1114
                    {
sl@0
  1115
                    // skip the quote (") char
sl@0
  1116
                    boundary.Set( boundary.Right( lenB - offsetQ ) );
sl@0
  1117
                    }
sl@0
  1118
sl@0
  1119
                // look for where to end
sl@0
  1120
                // check "
sl@0
  1121
                TInt offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
sl@0
  1122
                                              strlen(Multipart_ContentTypeString_Quotes_Text) );
sl@0
  1123
                if (offsetE == KErrNotFound)
sl@0
  1124
                    {
sl@0
  1125
                    // check ;
sl@0
  1126
                    offsetE = boundary.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
sl@0
  1127
                                             strlen(Multipart_ContentTypeString_Delimiter_Text) );
sl@0
  1128
                    }
sl@0
  1129
                if (offsetE != KErrNotFound)
sl@0
  1130
                    {
sl@0
  1131
                    boundary.Set( boundary.Left( offsetE ) );
sl@0
  1132
                    }
sl@0
  1133
sl@0
  1134
                // set it on to the input parameter
sl@0
  1135
                aBodyPart->SetBoundary( boundary );
sl@0
  1136
                } // end of if (offsetB != KErrNotFound)
sl@0
  1137
sl@0
  1138
            // check the charset information
sl@0
  1139
            TInt offsetCh = unrelated.Find( (TUint8*)Multipart_Charset_Text, 
sl@0
  1140
                                            strlen(Multipart_Charset_Text) );
sl@0
  1141
            if (offsetCh != KErrNotFound)
sl@0
  1142
                {
sl@0
  1143
                // now, we are at the beginning of "charset=us-ascii" string.
sl@0
  1144
                // move to the us-ascii part
sl@0
  1145
                TPtrC8 charset = unrelated.Right( lenU - 
sl@0
  1146
                                                  offsetCh - 
sl@0
  1147
                                                  strlen( Multipart_Charset_Text ) );
sl@0
  1148
                TInt lenCh = charset.Length();
sl@0
  1149
sl@0
  1150
                // look for where to end
sl@0
  1151
                TInt offsetQ = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
sl@0
  1152
                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
sl@0
  1153
                if (offsetQ != KErrNotFound)
sl@0
  1154
                    {
sl@0
  1155
                    // skip the quote (") char
sl@0
  1156
                    charset.Set( charset.Right( lenCh - offsetQ ) );
sl@0
  1157
                    }
sl@0
  1158
sl@0
  1159
                // look for where to end
sl@0
  1160
                // check "
sl@0
  1161
                TInt offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Quotes_Text, 
sl@0
  1162
                                             strlen(Multipart_ContentTypeString_Quotes_Text) );
sl@0
  1163
                if (offsetE == KErrNotFound)
sl@0
  1164
                    {
sl@0
  1165
                    // check ;
sl@0
  1166
                    offsetE = charset.Find( (TUint8*)Multipart_ContentTypeString_Delimiter_Text, 
sl@0
  1167
                                            strlen(Multipart_ContentTypeString_Delimiter_Text) );
sl@0
  1168
                    }
sl@0
  1169
                if (offsetE != KErrNotFound)
sl@0
  1170
                    {
sl@0
  1171
                    charset.Set( charset.Left( offsetE ) );
sl@0
  1172
                    }
sl@0
  1173
sl@0
  1174
                // set it on to the input parameter
sl@0
  1175
                aBodyPart->SetCharset( charset );
sl@0
  1176
                } // end of if (offsetCh != KErrNotFound)
sl@0
  1177
sl@0
  1178
            } // end of if( lenCT > offset )
sl@0
  1179
        } // end of if (offset != KErrNotFound)
sl@0
  1180
    }
sl@0
  1181
sl@0
  1182
sl@0
  1183
// ----------------------------------------------------------------------------
sl@0
  1184
// MultipartParser::GetBodyPartUrl
sl@0
  1185
//
sl@0
  1186
// Builds up the URL which refers to this particular body part
sl@0
  1187
// ----------------------------------------------------------------------------
sl@0
  1188
HBufC16*
sl@0
  1189
MultipartParser::GetBodyPartUrlL( const TDesC8& aContentBase,
sl@0
  1190
                                  const TDesC8& aContentLocation,
sl@0
  1191
                                  const TDesC16& aResponseUrl )
sl@0
  1192
    {
sl@0
  1193
    // check on required parameters
sl@0
  1194
    __ASSERT_ALWAYS( aResponseUrl.Ptr() != NULL,
sl@0
  1195
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
  1196
sl@0
  1197
    // Body url builds up as follows:
sl@0
  1198
    // Take global (respond header) Content Base header first.
sl@0
  1199
    // If local (in the body part) Content Base header is present, then
sl@0
  1200
    // take it as default Content Base header (by overruling global Content
sl@0
  1201
    // Base header).  Check if the Content Location header is either an
sl@0
  1202
    // absolute or relative URL.  If it is an absolute, then ignore the
sl@0
  1203
    // Content Base, otherwise concatenate them.
sl@0
  1204
    // Error cases:
sl@0
  1205
    //   No Content Base header + Content Location is relative URL
sl@0
  1206
    //   No Content Location header
sl@0
  1207
    //   Content Base header is relative URL
sl@0
  1208
    TBool contentBaseInvalid = EFalse;
sl@0
  1209
    HBufC16* url = NULL;
sl@0
  1210
    
sl@0
  1211
    if (aContentBase.Ptr())
sl@0
  1212
        {
sl@0
  1213
        // Check if it is a relative url
sl@0
  1214
        if ( MultipartParser::IsUrlRelativeL( aContentBase ) )
sl@0
  1215
            {
sl@0
  1216
            // Relative URL is not valid here as base location.
sl@0
  1217
            contentBaseInvalid = ETrue;
sl@0
  1218
            }
sl@0
  1219
        }
sl@0
  1220
    else
sl@0
  1221
        {
sl@0
  1222
        // no content base header
sl@0
  1223
        contentBaseInvalid = ETrue;
sl@0
  1224
        } // end of if (aContentBase)
sl@0
  1225
sl@0
  1226
    if (contentBaseInvalid)
sl@0
  1227
        {
sl@0
  1228
        if( aResponseUrl.Ptr() )
sl@0
  1229
            {
sl@0
  1230
            // Copy response url
sl@0
  1231
            TInt lenU = aResponseUrl.Length();
sl@0
  1232
            url = HBufC::NewLC( lenU + 1 );
sl@0
  1233
            url->Des().Copy( aResponseUrl );
sl@0
  1234
            url->Des().ZeroTerminate();
sl@0
  1235
            }
sl@0
  1236
        }
sl@0
  1237
    else
sl@0
  1238
        {
sl@0
  1239
        // Copy global content "base" location 
sl@0
  1240
        TInt lenCB = aContentBase.Length();
sl@0
  1241
        url = HBufC::NewLC( lenCB + 1 );
sl@0
  1242
        url->Des().Copy( aContentBase );
sl@0
  1243
        url->Des().ZeroTerminate();
sl@0
  1244
        } // end of if (contentBaseInvalid)
sl@0
  1245
sl@0
  1246
    // Check if Content Localtion is valid
sl@0
  1247
    if( aContentLocation.Ptr() )
sl@0
  1248
        {
sl@0
  1249
        TInt lenCL = aContentLocation.Length();
sl@0
  1250
sl@0
  1251
        // If the Content Location is an absolute URL, then Content Base value is ignored,
sl@0
  1252
        // otherwise the absolute path is going to be built unless Content Base is missing
sl@0
  1253
        if ( !MultipartParser::IsUrlRelativeL( aContentLocation ) )
sl@0
  1254
            {
sl@0
  1255
            // clean up memory
sl@0
  1256
            if( url )
sl@0
  1257
                {
sl@0
  1258
                CleanupStack::PopAndDestroy();  // url
sl@0
  1259
                }
sl@0
  1260
sl@0
  1261
            // fill url with content location
sl@0
  1262
            url = HBufC::NewL( lenCL + 1 );
sl@0
  1263
            url->Des().Copy( aContentLocation );
sl@0
  1264
            url->Des().ZeroTerminate();
sl@0
  1265
            }
sl@0
  1266
        else
sl@0
  1267
            {
sl@0
  1268
            if( url )
sl@0
  1269
                {
sl@0
  1270
                HBufC16* urlN = MultipartParser::UrlRelToAbsL( *url, aContentLocation );
sl@0
  1271
sl@0
  1272
                CleanupStack::PopAndDestroy(); // url
sl@0
  1273
sl@0
  1274
                url = urlN;
sl@0
  1275
                }
sl@0
  1276
            }
sl@0
  1277
        } // end of if( aContentLocation
sl@0
  1278
    else
sl@0
  1279
        {
sl@0
  1280
        if( url )
sl@0
  1281
            {
sl@0
  1282
            CleanupStack::Pop(); // url
sl@0
  1283
            }
sl@0
  1284
        }
sl@0
  1285
sl@0
  1286
    return url;
sl@0
  1287
    }
sl@0
  1288
sl@0
  1289
sl@0
  1290
// ----------------------------------------------------------------------------
sl@0
  1291
// MultipartParser::IsUrlRelativeL
sl@0
  1292
//
sl@0
  1293
// ----------------------------------------------------------------------------
sl@0
  1294
TBool
sl@0
  1295
MultipartParser::IsUrlRelativeL( const TDesC8& aUrl )
sl@0
  1296
    {
sl@0
  1297
    // check on required parameters
sl@0
  1298
    __ASSERT_ALWAYS( aUrl.Ptr() != NULL,
sl@0
  1299
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
  1300
sl@0
  1301
    TUriParser8 uriParser;
sl@0
  1302
sl@0
  1303
    User::LeaveIfError(uriParser.Parse(aUrl));
sl@0
  1304
sl@0
  1305
    if( uriParser.Extract(EUriScheme).Ptr() )
sl@0
  1306
        {
sl@0
  1307
        return EFalse;
sl@0
  1308
        }
sl@0
  1309
    else
sl@0
  1310
        {
sl@0
  1311
        return ETrue;
sl@0
  1312
        }
sl@0
  1313
    }
sl@0
  1314
sl@0
  1315
sl@0
  1316
// ----------------------------------------------------------------------------
sl@0
  1317
// MultipartParser::UrlRelToAbsL
sl@0
  1318
//
sl@0
  1319
// Absolute path is built as : Base + Relative
sl@0
  1320
// ----------------------------------------------------------------------------
sl@0
  1321
HBufC16*
sl@0
  1322
MultipartParser::UrlRelToAbsL( TDesC16& aBase, 
sl@0
  1323
                               const TDesC8& aRelativeUrl )
sl@0
  1324
    {
sl@0
  1325
    // check on required parameters
sl@0
  1326
    __ASSERT_ALWAYS( aBase.Ptr() != NULL,
sl@0
  1327
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
  1328
    __ASSERT_ALWAYS( aRelativeUrl.Ptr() != NULL,
sl@0
  1329
                     User::Panic(_L("MultipartParser Panic"), KErrArgument) );
sl@0
  1330
sl@0
  1331
    // length of absolute url
sl@0
  1332
    TInt len = 0;
sl@0
  1333
    // length of relative url
sl@0
  1334
    TInt lenR = 0;
sl@0
  1335
    TBool appendSlash = EFalse;
sl@0
  1336
    // path of absolute url
sl@0
  1337
    TPtrC16 path( NULL, 0 );
sl@0
  1338
sl@0
  1339
    // must to have aRelativeUrl
sl@0
  1340
    User::LeaveIfNull( (TUint8*)aRelativeUrl.Ptr() );
sl@0
  1341
sl@0
  1342
    TUriParser16 uriParser;
sl@0
  1343
    User::LeaveIfError( uriParser.Parse( aBase ) );
sl@0
  1344
sl@0
  1345
    // <scheme>://
sl@0
  1346
    TPtrC16 scheme( uriParser.Extract( EUriScheme ) );
sl@0
  1347
    // must to have scheme
sl@0
  1348
    User::LeaveIfNull( (TUint16*)scheme.Ptr() );
sl@0
  1349
    len += scheme.Length() + SCHEME_SEPARATOR_LENGTH;
sl@0
  1350
sl@0
  1351
    // <user>:<password>@
sl@0
  1352
    TPtrC16 user( uriParser.Extract( EUriUserinfo ) );
sl@0
  1353
    if( user.Ptr() )
sl@0
  1354
        {
sl@0
  1355
        len += user.Length() + 1;
sl@0
  1356
        }
sl@0
  1357
sl@0
  1358
    // <host>
sl@0
  1359
    TPtrC16 host( uriParser.Extract( EUriHost ) );
sl@0
  1360
    // must to have host
sl@0
  1361
    User::LeaveIfNull( (TUint16*)host.Ptr() );
sl@0
  1362
    len += host.Length();
sl@0
  1363
sl@0
  1364
    // :<port>
sl@0
  1365
    TPtrC16 port( uriParser.Extract( EUriPort ) );
sl@0
  1366
    if( port.Ptr() )
sl@0
  1367
        {
sl@0
  1368
        len += port.Length();
sl@0
  1369
        }
sl@0
  1370
sl@0
  1371
    // If the relative url begins with "./", remove it
sl@0
  1372
    TPtrC8 relativeUrl( NULL, 0 );
sl@0
  1373
    TInt indexD = aRelativeUrl.Locate( DOT_CHAR );
sl@0
  1374
    TInt indexS = aRelativeUrl.Locate( SLASH_CHAR );
sl@0
  1375
    if ( indexD == 0 && indexS == 1)
sl@0
  1376
        {
sl@0
  1377
        // Found a dot-slash at beginning of relative url
sl@0
  1378
        relativeUrl.Set( aRelativeUrl.Mid( 2 ) );
sl@0
  1379
        }
sl@0
  1380
    else
sl@0
  1381
        {
sl@0
  1382
        relativeUrl.Set( aRelativeUrl );
sl@0
  1383
        }
sl@0
  1384
sl@0
  1385
    lenR = relativeUrl.Length();
sl@0
  1386
    len += lenR;
sl@0
  1387
    // If the relative url begins with a slash, then it is an absolute path
sl@0
  1388
    // Does relative url start with slash?
sl@0
  1389
    indexS = relativeUrl.Locate( SLASH_CHAR );
sl@0
  1390
    // no, need to extract path from base url
sl@0
  1391
    if( indexS != 0 )
sl@0
  1392
        {
sl@0
  1393
      // <path>
sl@0
  1394
        path.Set( uriParser.Extract( EUriPath ) );
sl@0
  1395
        if( path.Ptr() )
sl@0
  1396
            {
sl@0
  1397
            // cut off the file path
sl@0
  1398
            if ( path.LocateReverse( DOT_CHAR ) )
sl@0
  1399
                {
sl@0
  1400
                // case: dir/index.html
sl@0
  1401
                if ( TInt indexS2 = path.LocateReverse( SLASH_CHAR ) )
sl@0
  1402
                    {
sl@0
  1403
                    // to keep the slash
sl@0
  1404
                    path.Set( path.Left( indexS2 + 1 ) );
sl@0
  1405
                    }
sl@0
  1406
                // case: index.html
sl@0
  1407
                else
sl@0
  1408
                    {
sl@0
  1409
                    path.Set( NULL, 0 );
sl@0
  1410
                    }
sl@0
  1411
                }
sl@0
  1412
sl@0
  1413
            // figure out the end slash
sl@0
  1414
            if( path.Ptr() )
sl@0
  1415
                {
sl@0
  1416
                if( path.LocateReverse( SLASH_CHAR ) != (path.Length() - 1) )
sl@0
  1417
                    {
sl@0
  1418
                    appendSlash = ETrue;
sl@0
  1419
                    }
sl@0
  1420
    
sl@0
  1421
                len += path.Length();
sl@0
  1422
                }
sl@0
  1423
            else
sl@0
  1424
                {
sl@0
  1425
                appendSlash = ETrue;
sl@0
  1426
                }
sl@0
  1427
            }
sl@0
  1428
        }
sl@0
  1429
    // yes, no need to extract path from base url
sl@0
  1430
    if( appendSlash )
sl@0
  1431
        {
sl@0
  1432
        ++len;
sl@0
  1433
        }
sl@0
  1434
sl@0
  1435
    // NULL terminator
sl@0
  1436
	// In certain operator cases, need to have an extra space(size of a 2-byte NULL terminator) 
sl@0
  1437
	// for proper String Termination
sl@0
  1438
    len += 2;
sl@0
  1439
sl@0
  1440
    // new absolute url
sl@0
  1441
    HBufC16* urlAbs = HBufC16::NewL( len );
sl@0
  1442
    TPtr16 urlAbsPtr = urlAbs->Des();
sl@0
  1443
sl@0
  1444
    // copy base into absolute url
sl@0
  1445
sl@0
  1446
    // scheme
sl@0
  1447
    urlAbsPtr.Copy( scheme );
sl@0
  1448
    urlAbsPtr.Append( COLON_CHAR );
sl@0
  1449
    urlAbsPtr.Append( SLASH_CHAR );
sl@0
  1450
    urlAbsPtr.Append( SLASH_CHAR );
sl@0
  1451
sl@0
  1452
    // user
sl@0
  1453
    if( user.Ptr() )
sl@0
  1454
        {
sl@0
  1455
        urlAbsPtr.Append( user );
sl@0
  1456
        urlAbsPtr.Append( AT_CHAR );
sl@0
  1457
        }
sl@0
  1458
sl@0
  1459
    // host
sl@0
  1460
    urlAbsPtr.Append( host );
sl@0
  1461
sl@0
  1462
    // port
sl@0
  1463
    if( port.Ptr() )
sl@0
  1464
        {
sl@0
  1465
        urlAbsPtr.Append( COLON_CHAR );
sl@0
  1466
        urlAbsPtr.Append( port );
sl@0
  1467
        }
sl@0
  1468
sl@0
  1469
    // path
sl@0
  1470
    if( path.Ptr() )
sl@0
  1471
        {
sl@0
  1472
        urlAbsPtr.Append( path );
sl@0
  1473
        }
sl@0
  1474
sl@0
  1475
    // slash between path and relative url
sl@0
  1476
    if( appendSlash )
sl@0
  1477
        {
sl@0
  1478
        urlAbsPtr.Append( SLASH_CHAR );
sl@0
  1479
        }
sl@0
  1480
sl@0
  1481
    // relative url
sl@0
  1482
    TUint16* relUrlInt = new TUint16[ lenR ];
sl@0
  1483
    TPtr16 relUrl16( relUrlInt, lenR );
sl@0
  1484
    relUrl16.Copy( relativeUrl );
sl@0
  1485
    urlAbsPtr.Append( relUrl16 );
sl@0
  1486
    delete[] relUrlInt;
sl@0
  1487
sl@0
  1488
    // null terminate
sl@0
  1489
    urlAbsPtr.ZeroTerminate();
sl@0
  1490
sl@0
  1491
    return urlAbs;
sl@0
  1492
    }
sl@0
  1493
    
sl@0
  1494
// ------------------------------------------------------------------------- 
sl@0
  1495
// Composes multipart/mixed document
sl@0
  1496
// ------------------------------------------------------------------------- 
sl@0
  1497
HBufC8*
sl@0
  1498
MultipartParser::ComposeMixedL( RPointerArray<CBodyPart>& aBodyArray,
sl@0
  1499
                                const TDesC8& aBoundary,
sl@0
  1500
                                TInt aHeaderMask )
sl@0
  1501
    {
sl@0
  1502
    // --(aBoundary)
sl@0
  1503
    _LIT8(KBoundary, "--%S\r\n");
sl@0
  1504
    HBufC8* boundary = HBufC8::NewLC( aBoundary.Length() + 4 );
sl@0
  1505
    boundary->Des().Format( KBoundary, &aBoundary );
sl@0
  1506
    
sl@0
  1507
    // Calculate the size of this document.
sl@0
  1508
    TInt bodySize = 0;
sl@0
  1509
    //    a. for each CBodyPart
sl@0
  1510
    //       boundaries + CRLF between headers and body (constant addition)
sl@0
  1511
    bodySize += (boundary->Length() + strlen(Multipart_CRLF_Text)) * aBodyArray.Count() ;
sl@0
  1512
    for (TInt i = 0; i < aBodyArray.Count(); i++)
sl@0
  1513
        {
sl@0
  1514
        if (!(aBodyArray[i]->Headers().Length() +
sl@0
  1515
            aBodyArray[i]->Body().Length()))
sl@0
  1516
            {
sl@0
  1517
            // one less boundary
sl@0
  1518
            bodySize -= boundary->Length() + strlen(Multipart_CRLF_Text);
sl@0
  1519
            // skip empty bodypart
sl@0
  1520
            continue;
sl@0
  1521
            }
sl@0
  1522
        bodySize += aBodyArray[i]->Headers().Length();
sl@0
  1523
    //       ensure there are only 2 CRLFs between header and body
sl@0
  1524
        if (aBodyArray[i]->Headers().Length() > 0)
sl@0
  1525
            {
sl@0
  1526
            TPtrC8 bodyHeaders(aBodyArray[i]->Headers().Ptr(), aBodyArray[i]->Headers().Length());
sl@0
  1527
            TUint newEnd = bodyHeaders.Length() - 1;
sl@0
  1528
            while( bodyHeaders[ newEnd ] == '\r' || bodyHeaders[ newEnd ] == '\n' )
sl@0
  1529
                {
sl@0
  1530
                --newEnd;
sl@0
  1531
                --bodySize;
sl@0
  1532
                }
sl@0
  1533
            // two CRLFs
sl@0
  1534
            bodySize += strlen(Multipart_CRLF_Text);
sl@0
  1535
            }
sl@0
  1536
        bodySize += aBodyArray[i]->Body().Length();
sl@0
  1537
    //       CRLF (end of body, add one only if there is body AND does not end with CRLF)
sl@0
  1538
        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
sl@0
  1539
        if (bodyBody.Length() > 0 
sl@0
  1540
            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
sl@0
  1541
            {
sl@0
  1542
            bodySize += strlen(Multipart_CRLF_Text);
sl@0
  1543
            }
sl@0
  1544
        }
sl@0
  1545
    //     end boundary (boundary - '\r\n' + "--")
sl@0
  1546
    bodySize += boundary->Length();
sl@0
  1547
    TInt docSize = bodySize; 
sl@0
  1548
    //     calculate the size of Headers
sl@0
  1549
    _LIT8(KContentType, "Content-Type: multipart/mixed; boundary=\"%S\"\r\n");
sl@0
  1550
    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
sl@0
  1551
        {
sl@0
  1552
        docSize += MULTIPART_CONTENT_TYPE_LENGTH + 1; // Content-Type: + empty space
sl@0
  1553
        docSize += KContentType().Length() - 2 + aBoundary.Length(); // multipart/mixed; boundary="{aBoundary}"
sl@0
  1554
        docSize += strlen(Multipart_CRLF_Text); // eol
sl@0
  1555
        }
sl@0
  1556
    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
sl@0
  1557
        {
sl@0
  1558
        docSize += MULTIPART_CONTENT_LENGTH_LENGTH + 1; // Content-Length: + empty space
sl@0
  1559
        // calculate number of chars needed to represent bodySize
sl@0
  1560
        HBufC8* bodySizeSize = HBufC8::NewLC( 16 );
sl@0
  1561
        bodySizeSize->Des().Num( bodySize );
sl@0
  1562
        docSize += bodySizeSize->Length(); // content length (bodySize)
sl@0
  1563
        docSize += strlen(Multipart_CRLF_Text); // eol
sl@0
  1564
        CleanupStack::PopAndDestroy( bodySizeSize );
sl@0
  1565
        }
sl@0
  1566
    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
sl@0
  1567
        {
sl@0
  1568
        docSize += MULTIPART_LAST_MODIFIED_LENGTH + 1;
sl@0
  1569
        docSize += MULTIPART_INTERNET_DATE_STRING_LENGTH; // timestamp (fixed length)
sl@0
  1570
        docSize += strlen(Multipart_CRLF_Text); // eol
sl@0
  1571
        }
sl@0
  1572
    //    extra CRLF for separating header and body
sl@0
  1573
    docSize += strlen(Multipart_CRLF_Text);
sl@0
  1574
    //  CALCULATION COMPLETE
sl@0
  1575
    //  at this point, bodySize contains the size of bodyparts, i.e. Content-Length:
sl@0
  1576
    //  and docSize contains the size of the entire document (use it to create HBufC8*
sl@0
  1577
    //  of appropriate size)
sl@0
  1578
  
sl@0
  1579
    //  construct multipart document
sl@0
  1580
    HBufC8* document = HBufC8::NewLC(docSize);
sl@0
  1581
    TPtr8 docAppend(document->Des());
sl@0
  1582
    if ( aHeaderMask & EMultipartTopLevelHeaderContentType )
sl@0
  1583
        {
sl@0
  1584
        docAppend.Format( KContentType, &aBoundary );
sl@0
  1585
        }
sl@0
  1586
    if ( aHeaderMask & EMultipartTopLevelHeaderContentLength )
sl@0
  1587
        {
sl@0
  1588
        _LIT8( KContentLength, "Content-Length: %d\r\n" );
sl@0
  1589
        docAppend.AppendFormat( KContentLength, bodySize );
sl@0
  1590
        }
sl@0
  1591
    if ( aHeaderMask & EMultipartTopLevelHeaderLastModified )
sl@0
  1592
        {
sl@0
  1593
        _LIT8( KLastModified, "Last-Modified: %S\r\n" );
sl@0
  1594
        TTime current;
sl@0
  1595
        current.UniversalTime();
sl@0
  1596
        TInternetDate modDate(current.DateTime());
sl@0
  1597
        HBufC8* dateString = modDate.InternetDateTimeL( TInternetDate::ERfc1123Format );
sl@0
  1598
        docAppend.AppendFormat( KLastModified, dateString );
sl@0
  1599
        delete dateString;
sl@0
  1600
        }
sl@0
  1601
    // required CRLF
sl@0
  1602
    docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
sl@0
  1603
    //  BodyParts
sl@0
  1604
    for (TInt i = 0; i < aBodyArray.Count(); i++)
sl@0
  1605
        {
sl@0
  1606
        if (!(aBodyArray[i]->Headers().Length() +
sl@0
  1607
            aBodyArray[i]->Body().Length()))
sl@0
  1608
            {
sl@0
  1609
            // skip empty bodypart
sl@0
  1610
            continue;
sl@0
  1611
            }
sl@0
  1612
        docAppend.Append( *boundary );
sl@0
  1613
        TInt headerLength = aBodyArray[i]->Headers().Length() - 1;
sl@0
  1614
        while ( headerLength > 0 &&
sl@0
  1615
                (aBodyArray[i]->Headers()[headerLength] == '\r'
sl@0
  1616
                || aBodyArray[i]->Headers()[headerLength] == '\n' ))
sl@0
  1617
            {
sl@0
  1618
            --headerLength;
sl@0
  1619
            }
sl@0
  1620
        docAppend.Append( aBodyArray[i]->Headers().Ptr(), headerLength + 1 );
sl@0
  1621
sl@0
  1622
        if ( headerLength > 0 )
sl@0
  1623
            {
sl@0
  1624
            docAppend.Append((TUint8*)Multipart_DoubleCRLF_Text, strlen(Multipart_DoubleCRLF_Text));
sl@0
  1625
            }
sl@0
  1626
        else
sl@0
  1627
            {
sl@0
  1628
            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
sl@0
  1629
            }
sl@0
  1630
    //  body
sl@0
  1631
        docAppend.Append(aBodyArray[i]->Body());
sl@0
  1632
    //  CRLF only if body exists and doesn't end with CRLF
sl@0
  1633
        TPtrC8 bodyBody(aBodyArray[i]->Body().Ptr(), aBodyArray[i]->Body().Length());
sl@0
  1634
        if (bodyBody.Length() > 0
sl@0
  1635
            && bodyBody.Right(2) != TPtrC8((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text)))
sl@0
  1636
            {
sl@0
  1637
            docAppend.Append((TUint8*)Multipart_CRLF_Text, strlen(Multipart_CRLF_Text));
sl@0
  1638
            }
sl@0
  1639
        }
sl@0
  1640
    //  end boundary
sl@0
  1641
    _LIT8(KEndBoundary, "--%S--");
sl@0
  1642
    docAppend.AppendFormat(KEndBoundary, &aBoundary);
sl@0
  1643
    CleanupStack::Pop( document );
sl@0
  1644
    CleanupStack::PopAndDestroy( boundary );
sl@0
  1645
    return document;
sl@0
  1646
    }