diff -r 000000000000 -r dd21522fd290 webengine/wmlengine/src/utils/src/nw_loadreq.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/webengine/wmlengine/src/utils/src/nw_loadreq.cpp Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,1648 @@ +/* +* Copyright (c) 1998-2004 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +/* +**------------------------------------------------------------------------- +** Component Generic Include +**------------------------------------------------------------------------- +*/ +#include +#include +#include + +#include "MVCShell.h" +#include "nw_object_exceptions.h" +#include "nwx_mem.h" +#include "nwx_string.h" +#include "nwx_url_utils.h" +#include "nw_nvpair.h" +#include "nw_wae_reader.h" +#include "nw_string_string.h" +#include "nwx_http_defs.h" +#include "nwx_settings.h" +#include "nwx_statuscodeconvert.h" + +/* +**------------------------------------------------------------------------- +** Module Specific Includes +**------------------------------------------------------------------------- +*/ +#include "nw_loadreq.h" +#include "urlloader_urlloaderint.h" +#include "BrsrStatusCodes.h" +#include "nwx_http_defs.h" + +/* +**------------------------------------------------------------------------- +** Constants +**------------------------------------------------------------------------- +*/ + +//TODO STREAMING: Need to track down an API for getting these. Initial inquiry +//didn't find one and found other apps are hard-coding these as well. These, +//however, will not be needed when we switch to the streaming implementation. +static const NW_Ucs2 KCDrivePrefix[] = {'c', ':', '\\', '\0'}; +static const NW_Ucs2 KEDrivePrefix[] = {'e', ':', '\\', '\0'}; + +// The boundary string generated will be done using random numbers and +// converting them to ASCII hex strings. The length should be fairly long so as +// to reduce the probability of the boundary matching any data being sent. As +// of this writing, Microsoft I.E. uses a length of 41, of which 14 chars are +// random ASCII hex chars and the rest are all dashes. In our case, all 41 +// chars will be random providing a very low probability of a match. RFC2046 +// states that the max length is 70. +static const int KBoundaryLen = 41; + +static const NW_Uint8 KDefaultContentType[] = +{'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'o', 'c', 't', 'e', 't', '-', 's', 't', 'r', 'e', 'a', 'm'}; + +/* +**------------------------------------------------------------------------- +** Internal Types +**------------------------------------------------------------------------- +*/ + +/* +**------------------------------------------------------------------------- +** Macros +**------------------------------------------------------------------------- +*/ + +#define NW_URLENCODED_TYPE_STR (const char*)"application/x-www-form-urlencoded" +#define NW_URLENCODED_CHARSET_UTF8_TYPE_STR (const char*)"application/x-www-form-urlencoded; charset=utf-8" +#define NW_MULTIPART_TYPE_STR (const char*)"multipart/form-data" + +// The HTTP Post header is created elsewhere. It inserts enough initial CRLFs +// already in preparation for the body; thus, the first boundary string needs +// one fewer CRLF. Inserting one too many CRLF will cause some sites to not +// work on multipart post (e.g., www.gifworks.com). Therefore, there are two +// boundary start strings, one with CRLF and one without. Note, the CRLF does +// belong in the front of and as part of the boundary string. See RFC 1521: +// "...initial CRLF is considered to be attached to encpsulation boundary +// rather than part of the preceding part." +#define NW_BOUNDARY_FIRST_START_STR (const char*)"--" +#define NW_BOUNDARY_START_STR (const char*)"\r\n--" +#define NW_BOUNDARY_END_STR (const char*)"--\r\n" +#define NW_CONTENTTYPE_TEXTPLAIN_CHARSET_STR (const char*)"\r\nContent-Type: text/plain; charset=" +#define NW_CONTENTTYPE_STR (const char*)"\r\nContent-Type: " +#define NW_CONTENTDISPOSITION_STR (const char*)"\r\nContent-Disposition: form-data; name=" +#define NW_FILENAME_STR (const char*)" filename=" +#define NW_HEADEREND_STR (const char*)"\r\n\r\n" + + +/* +**------------------------------------------------------------------------- +** Internal Prototypes +**------------------------------------------------------------------------- +*/ + +/* +**------------------------------------------------------------------------- +** File Scoped Static Variables +**------------------------------------------------------------------------- +*/ + +/* +**------------------------------------------------------------------------- +** Global Variable Definitions +**------------------------------------------------------------------------- +*/ + +/* +**------------------------------------------------------------------------- +** Internal Prototypes +**------------------------------------------------------------------------- +*/ +TBrowserStatusCode Transcode( NW_Byte *p, const NW_Ucs2 *s, + NW_Http_CharSet_t http_charset, NW_Uint32 *byteLen ); +NW_Uint32 TranscodedLength( const NW_Ucs2 *s, + NW_Http_CharSet_t http_charset); +NW_Byte* TranscodeTo10646_ucs2( const NW_Ucs2 *aSrc, NW_Byte *aDst ); + +static TBrowserStatusCode getEscapedPostdataL(NW_NVPair_t *postfields, + NW_Ucs2 ** escapedPostdata, + NW_HED_AppServices_t* appServices, + void * appCtx); +/* +**------------------------------------------------------------------------- +** Internal Functions +**------------------------------------------------------------------------- +*/ + +//------------------------------------------------------------------------- +// getConvertedStringL(): +// aString: A NW_Ucs2* that needs url-escaping and/or foreign charset conversion. +// aConvertedString: Gets filled in with a (NW_Ucs2*) pointing to the result of those operations. +// *aConvertedString is NULL-terminated. +// aConvertedLen: Set to the length of aConvertedString, not including the NULL-terminator +// +static TBrowserStatusCode getConvertedStringL(const NW_Ucs2* aString, + NW_HED_AppServices_t* appServices, + void * appCtx, + NW_Ucs2** convertedString, + NW_Uint32* convertedLen) +{ +NW_Ucs2* escapedString = NULL; +NW_Uint32 escapedLen = 0; +NW_Uint32 originalStringLen = NW_Str_Strlen(aString); +NW_Uint32 copyPos = 0; +NW_Uint32 convertPos = 0; +CBufFlat* buffer = CBufFlat::NewL(20); // arbitrary granularity. + +*convertedString = NULL; +*convertedLen = 0; + +NW_TRY( status ) + { + escapedLen = (3 * originalStringLen + 1); + escapedString = NW_Str_New(escapedLen); + NW_THROW_OOM_ON_NULL(escapedString, status); + + (void)NW_Mem_memset(escapedString, 0, escapedLen * sizeof(NW_Ucs2)); + + // protect the URL from thinking field-data is a delimiter character. + NW_Url_EscapeString(aString, escapedString); + + /* convert the value to foreign charset if necessary */ + if (appServices && appCtx) + { + NW_String_t text; + NW_Uint32 origEscapedLen = NW_Str_Strlen(escapedString); + + NW_Uint32 i = 0; + while (i < origEscapedLen) + { + // Each interation copies 1 ascii sub-string and/or + // converts 1 non-ascii sub string. + + // Copy ascii chars + copyPos = i; + while (i < origEscapedLen && escapedString[i] <= 127) + { + i++; + } + + if (i != copyPos) + { + buffer->InsertL(buffer->Size(), escapedString + copyPos, (i - copyPos) * sizeof(NW_Ucs2)); + } + + convertPos = i; + while (i < origEscapedLen && escapedString[i] > 127) + { + // gather non-ascii chars for conversion. + i++; + } + + if (i != convertPos) + { + // Need to convert foreign char string. + NW_Uint32 foreignLen = i - convertPos ; + NW_Ucs2* foreignString = NW_Str_New( foreignLen); + NW_Ucs2* transmitString = NULL; + + NW_Mem_memcpy( foreignString, escapedString + convertPos, foreignLen * sizeof(NW_Ucs2)); + foreignString[ foreignLen ] = 0; + + // convert the ucs2 into byte* from charset conversion process*/ + status = StatusCodeConvert(NW_String_ucs2CharToString (&text, foreignString, HTTP_iso_10646_ucs_2)); + NW_Mem_Free(foreignString); + NW_THROW_ON_ERROR(status); + + // convert to the foreign charset */ + status = appServices->characterConversionAPI.convertToForeignCharSet(appCtx, &text); + NW_THROW_ON_ERROR(status); + + // TODO: Apply Utf8 encoding here if appropriate. + + // Apply %HH encoding to the foreign charset string. + status = NW_Url_HHEscape((const char*)text.storage, &transmitString); + NW_String_deleteStorage(&text); + NW_THROW_ON_ERROR(status); + + buffer->InsertL(buffer->Size(), transmitString, NW_Str_Strsize(transmitString) - sizeof(NW_Ucs2)); + NW_Mem_Free(transmitString); + } + } + + buffer->Compress(); + *convertedLen = buffer->Size(); + *convertedString = NW_Str_New( *convertedLen ); + NW_THROW_OOM_ON_NULL( *convertedString, status ); + + NW_Mem_memcpy( *convertedString, buffer->Ptr(0).Ptr(), *convertedLen ); + } + else + { + NW_Uint32 charCount = NW_Str_Strlen(escapedString); + *convertedLen = charCount * sizeof(NW_Ucs2); + *convertedString = NW_Str_New(charCount); + NW_THROW_OOM_ON_NULL( *convertedString, status ); + NW_Str_Strcpy( *convertedString, escapedString); + } + } + +NW_CATCH( status ) + { + } + +NW_FINALLY + { + buffer->Reset(); + delete buffer; + NW_Str_Delete(escapedString); + return status; + } + +NW_END_TRY +} + +//TODO CHARSET: These character-handling functions should be in a common +//character translation module. Currently, this type of functionality is +//spread throughout the code rather than centralized where it can be more +//easily maintained and controlled. + +// ----------------------------------------------------------------------------- +// GetMaxTranscodedByteLength +// Gets the max number of bytes required to hold the transcoded version of the +// specified source string. This is not precise. It is an upper bounds. The +// actual transcoded string length may end up being less than this max length. +// @param aSrc Pointer to source string, for which the max transcoded length is +// determined. The source string must be HTTP_iso_10646_ucs_2. +// @param aHttpCharSet Char set to use for transcoding. +// @return Max number of bytes in the transcoded string. +// ----------------------------------------------------------------------------- +// +NW_Uint32 GetMaxTranscodedByteLength( const NW_Ucs2 *aSrc, + NW_Http_CharSet_t aHttpCharSet ) + { + NW_Uint32 len = 0; + + // Return 0 length for NULL pointer or empty string. + if ( ( aSrc == NULL ) || ( aSrc[0] == 0 ) ) + { + return len; + } + + // Get the length (in characters) of the UCS2 source string. + len = NW_Str_Strlen( aSrc ); + + // For performance reasons, the exact byte length is returned for a few + // simple and quick cases. For all the rest, an upper bounds is calculated + // as being 3 byte-sized chars per every UCS2 char. + switch ( aHttpCharSet ) + { + case HTTP_us_ascii: // 1 byte per char; length already set + case HTTP_iso_8859_1: + break; + case HTTP_iso_10646_ucs_2: // 2 bytes per char + len *= sizeof(NW_Ucs2); + break; + default: // upper bounds is 3 byte-sized chars per char + len *= 3; + break; + } + return len; + } + +// ----------------------------------------------------------------------------- +// TranscodeTo10646_ucs2 +// Transcodes from a UCS2 string to a UCS2 byte stream for HTTP posts, which +// results in changing the two-byte chars from little endian to big endian. +// @param aSrc Pointer to source string, which is to be transcoded. +// @param aDst Pointer to destination for the transcoded results. +// @return Pointer to the next available byte within the destination buffer. +// ----------------------------------------------------------------------------- +// +NW_Byte* TranscodeTo10646_ucs2( const NW_Ucs2* aSrc, NW_Byte* aDst ) +{ + while ( *aSrc ) + { + aDst += NW_Reader_WriteUcs2CharBuff( *aSrc, aDst); + aSrc++; + } + return aDst; +} + +// ----------------------------------------------------------------------------- +// Transcode +// Translates string from NW_Ucs2 to specified charset (does not append a +// null-terminator). +// @param aDst Pointer to destination into which transcoded string is returned. +// The size of the destination buffer should be large enough to contain the +// transcoded string and as such the buffer needs to be allocated large enough +// when it is created. +// @param aSrc Pointer to source string, which is to be transcoded into the +// destination. The source string must be HTTP_iso_10646_ucs_2. +// @param aHttpCharSet Pointer to charset name (e.g. "utf-8"). +// @param aBufLen Length of destination buffer (amount of space available for +// transcoded string). +// @param aByteLen Pointer at which the byte length of the transcoded string is +// returned. +// @return KBrsrSuccess: if success. +// KBrsrOutOfMemory: if out of memory. +// KBrsrFailure: any other failure. +// ----------------------------------------------------------------------------- +// +TBrowserStatusCode Transcode( NW_Byte *aDst, const NW_Ucs2 *aSrc, + NW_Http_CharSet_t aHttpCharSet, NW_Uint32 aBufLen, NW_Uint32* aByteLen ) + { + NW_String_t text; + NW_Uint32 srcByteLen; + + // Parameter assertion block. + NW_ASSERT( aDst ); + NW_ASSERT( aByteLen ); + + text.storage = NULL; + + // Initialize returned length to zero for now, in case an error occurs. + *aByteLen = 0; + + NW_TRY( status ) + { + + // If there is no source, just return. + if ( aSrc == NULL ) + { + NW_THROW( KBrsrSuccess ); + } + + srcByteLen = NW_Str_Strlen( aSrc ) * sizeof( NW_Ucs2 ); + + // Special case UCS2 for performance reasons - if both source and + // destination are UCS2 then no intermediate buffer needed. + if ( aHttpCharSet == HTTP_iso_10646_ucs_2 ) + { + // Verify that there is sufficient space for the text. + if ( srcByteLen > aBufLen ) + { + NW_THROW( KBrsrFailure ); + } + aDst = TranscodeTo10646_ucs2( aSrc, aDst ); + // Return the length of transcoded text in destination. + *aByteLen = srcByteLen; + NW_THROW( KBrsrSuccess ); + } + + // Allocate an intermediate buffer. All other char sets go through the + // transcoding methods which unfortunately require an intermediate + // buffer. + text.storage = (NW_Byte*)NW_Mem_Malloc( srcByteLen ); + NW_THROW_OOM_ON_NULL( text.storage, status ); + + // Copy the source into the intermediate buffer. + NW_Mem_memcpy( text.storage, aSrc, srcByteLen ); + text.length = srcByteLen; + + // Tanscode. Note, this assumes transcoding to original character set. +//TODO CHARSET: Make the API more flexible to take the charset as a parameter. + status = CShell::ConvertToForeignCharSet( &text ); + _NW_THROW_ON_ERROR( status ); + // Verify that there is sufficient space for the text. + if ( text.length > aBufLen ) + { + NW_THROW( KBrsrFailure ); + } + // Copy the transcoded text into the destination buffer. Note, the + // transcoded length always includes 2 null bytes. +//TODO CHARSET: Fix ConvertToForeignCharSet and any code that uses it to not +//use 2 extra null bytes. Not as easy as it sounds. That will lead into a +//tangled web. + NW_Mem_memcpy( aDst, text.storage, text.length - 2 ); + // Return the length of transcoded text in destination. + *aByteLen = text.length - 2; + } + // status = KBrsrSuccess; // set by NW_CATCH + NW_CATCH( status ) + { + } + NW_FINALLY + { + NW_String_deleteStorage( &text ); + return status; + } + NW_END_TRY + } + +/************************************************************************ + + Function: GetHttpCharsetAsAsciiText + + Description: Returns the string of the character set. + + Parameters: httpCharset - in: HTTP charset. + + Return Value: Pointer to the charset string. + +**************************************************************************/ +static const char* GetHttpCharsetAsAsciiText(NW_Http_CharSet_t httpCharset) +{ + switch (httpCharset) + { + case HTTP_utf_8: + return NW_Utf8_CharsetStr; + case HTTP_us_ascii: + return NW_UsAscii_CharsetStr; + case HTTP_iso_10646_ucs_2: + return NW_Iso10646Ucs2_CharsetStr; + case HTTP_windows_1255: + return NW_Windows1255_CharsetStr; + case HTTP_windows_1256: + return NW_Windows1256_CharsetStr; + case HTTP_windows_1250: + return NW_Windows1250_CharsetStr; + case HTTP_windows_1251: + return NW_Windows1251_CharsetStr; + case HTTP_windows_1253: + return NW_Windows1253_CharsetStr; + case HTTP_windows_1254: + return NW_Windows1254_CharsetStr; + case HTTP_windows_1257: + return NW_Windows1257_CharsetStr; + case HTTP_big5: + return NW_Big5_CharsetStr; + case HTTP_gb2312: + return NW_Gb2312_CharsetStr; + case HTTP_iso_8859_1: + return NW_Iso88591_CharsetStr; + case HTTP_iso_8859_2: + return NW_Iso88592_CharsetStr; + case HTTP_iso_8859_3: + return NW_Iso88593_CharsetStr; + case HTTP_iso_8859_4: + return NW_Iso88594_CharsetStr; + case HTTP_iso_8859_5: + return NW_Iso88595_CharsetStr; + case HTTP_iso_8859_6: + return NW_Iso88596_CharsetStr; + case HTTP_iso_8859_7: + return NW_Iso88597_CharsetStr; + case HTTP_iso_8859_8: + return NW_Iso88598_CharsetStr; + case HTTP_iso_8859_9: + return NW_Iso88599_CharsetStr; + case HTTP_tis_620: // not flagged because we will not get here unless tis_620 is enabled + return NW_Tis_620_CharsetStr; + case HTTP_shift_JIS: + return NW_ShiftJis_CharsetStr; + case HTTP_jis_x0201_1997: + return NW_JisX0201_1997_CharsetStr; + case HTTP_jis_x0208_1997: + return NW_JisX0208_1997_CharsetStr; + case HTTP_euc_jp: + return NW_EucJp_CharsetStr; + case HTTP_iso_2022_jp: + return NW_Iso2022Jp_CharsetStr; + case HTTP_windows_874: + return NW_Windows874_CharsetStr; + case HTTP_Koi8_r: + return NW_Koi8r_CharsetStr; + case HTTP_Koi8_u: + return NW_Koi8u_CharsetStr; + } + NW_ASSERT(NW_FALSE); + return NULL; /* Make compiler happy */ +} + +// ----------------------------------------------------------------------------- +// getPostDataCharset +// Gets the charset to be used for posting data. This is currently the encoding +// of the original document. +// @return Charset to be used or HTTP_iso_8859_1 if no known value. +// HTTP_iso_8859_1 was chosen as the "default" since it is a very common +// charset. +// ----------------------------------------------------------------------------- +// +static NW_Http_CharSet_t getPostdataCharset() + { + NW_Http_CharSet_t charset; + + charset = (NW_Http_CharSet_t)NW_Settings_GetOriginalEncoding(); + return ((charset == HTTP_unknown) || (charset == 0)) + ? (NW_Http_CharSet_t)HTTP_iso_8859_1 : charset; + } + + + +/************************************************************************ + + Function: getEscapedPostdata + + Description: Give a single url-encoded string containing all + the postfield data, in the form of + + "name1=value1&name2=value2&...&namen=valuen" + + Parameters: *postfields - pointer to the postfield data. + **escaped_postdata - pointer to the encoded string. + + Return Value: KBrsrSuccess + KBrsrOutOfMemory + +**************************************************************************/ +//??? RFD: This section needs to be corrected along with the rest of the URL-encoding fixes underway. +static TBrowserStatusCode getEscapedPostdata(NW_NVPair_t *postfields, + NW_Ucs2 ** escapedPostdata, + NW_HED_AppServices_t* appServices, + void * appCtx) +{ + TBrowserStatusCode status = KBrsrFailure; + + TInt err; + TRAP(err, status = getEscapedPostdataL(postfields, escapedPostdata, appServices, appCtx)); + + return status; +} + +static TBrowserStatusCode getEscapedPostdataL(NW_NVPair_t *postfields, + NW_Ucs2 ** escapedPostdata, + NW_HED_AppServices_t* appServices, + void * appCtx) +{ + + NW_Ucs2 *name; + NW_Ucs2 *value; + NW_Ucs2* convertedName = NULL; + NW_Uint32 convertedNameLen = 0; + NW_Ucs2* convertedValue = NULL; + NW_Uint32 convertedValueLen = 0; + NW_Uint32 parameterLen = 0; + + NW_TRY( status ) + { + + + NW_ASSERT(escapedPostdata != NULL); + + *escapedPostdata = NULL; + + if ( NW_NVPair_IsEmpty(postfields) ) + { + // No postdata fields. Return an empty string for + // escaped_postdata. + *escapedPostdata = NW_Str_NewcpyConst( "" ); + return KBrsrSuccess; + } + + status = KBrsrFailure; + CBufFlat* buffer = CBufFlat::NewL(20); // arbitrary granularity. + NW_Bool needSep = NW_FALSE; + + (void) NW_NVPair_ResetIter( postfields ); + while ( KBrsrSuccess == (status = NW_NVPair_GetNext( postfields, &name, &value ))) + { + + if ( needSep ) + { + // append "&" delimiter, as Ucs2 + buffer->InsertL(buffer->Size(), WAE_URL_POSTFIELD_SEP, 2); + parameterLen++; + } + + // Each name and value pair needs to be escaped and/or converted to foreign charset. + + status = getConvertedStringL(name, appServices, appCtx, &convertedName, &convertedNameLen); + _NW_THROW_ON_ERROR( status ); + buffer->InsertL(buffer->Size(), convertedName, convertedNameLen); + NW_Mem_Free(convertedName); + convertedName = NULL; + + buffer->InsertL(buffer->Size(), "=", 2); // seperator is UCS2 + + status = getConvertedStringL(value, appServices, appCtx, &convertedValue, &convertedValueLen); + _NW_THROW_ON_ERROR( status ); + buffer->InsertL(buffer->Size(), convertedValue, convertedValueLen); + NW_Mem_Free(convertedValue); + convertedValue = NULL; + + needSep = NW_TRUE; + } + + parameterLen = buffer->Size(); + *escapedPostdata = (NW_Ucs2*) NW_Mem_Malloc(parameterLen + 2); + NW_Mem_memcpy( *escapedPostdata, buffer->Ptr(0).Ptr(), parameterLen); + (void)NW_Mem_memset( ((NW_Byte*)(*escapedPostdata)) + parameterLen, 0, 2 ); + buffer->Reset(); + delete buffer; + + } + + NW_CATCH( status ) + { + NW_Mem_Free(convertedName); + NW_Mem_Free(convertedValue); + + } + + NW_FINALLY + { + return status; + } + + NW_END_TRY +} + + +// ----------------------------------------------------------------------------- +// CreateBoundary +// Creates the boundary string that separates each part in the multipart post. +// The boundary string generated is fairly long and has some degree of +// randomness (by using a random number generator). This is to reduce the +// chance that the string generated could actually match any of the data being +// posted. Although highly unlikely, if it does match then the post would fail. +// If the user attempts to re-transmit, a new string will be generated and the +// post is highly likely to succeed. +// @param aBoundary Ptr to address into which the allocated boundary string +// is stored. The caller takes ownership of the allocated memory. +// @return KBrsrSuccess or KBrsrOutOfMemory. +// ----------------------------------------------------------------------------- +// +static TBrowserStatusCode CreateBoundary( NW_Uint8** aBoundary ) + { + // Allocate memory for the boundary string. + NW_Uint8* boundary = (NW_Uint8*)NW_Mem_Malloc( KBoundaryLen + 1 ); + *aBoundary = boundary; + if ( !boundary ) + { + return KBrsrOutOfMemory; + } + // Fill the boundary string with random numbers, converting the numbers to + // hex chars and inserting each char into the string. + NW_Uint8 n; + int random = 0; + for ( int i = 0; i < KBoundaryLen; i++ ) + { + if ( !random ) + { + random = rand(); + } + // Extract lower nibble. + n = (NW_Uint8)(random & 0x0F); + // Convert to ASCII hex. + n = (NW_Uint8)((n < 10) ? (n + '0') : ((n - 10) + 'A')); + // Shift right by a nibble. + random = (random >> 4) & 0x0FFFFFFF; + // Insert char. + *boundary++ = n; + } + // Null terminate. + *boundary = 0; + + return KBrsrSuccess; + } + +// ----------------------------------------------------------------------------- +// GetMimeType +// Gets the mime-type string for a given file (E.g., "image/gif", etc.). +// @param aFileName Ptr to name of file for which to get the mime-type. +// @param aMimeTypeString Ptr to desination into which the newly allocated +// mime-type string is stored. The caller takes ownership of the allocated +// memory. +// @return KBrsrSuccess: if success. +// KBrsrOutOfMemory: if out of memory. +// KBrsrFailure: any other failure. +// ----------------------------------------------------------------------------- +// +static TBrowserStatusCode GetMimeType( + const NW_Ucs2* aFileName, TUint8** aMimeTypeString ) + { + TInt err; + TUid uid; + TDataType dataType; + RApaLsSession apaSession; + TBool apaSessionConnected = EFalse; + + // Parameter assertion block. + NW_ASSERT( aFileName != NULL ); + NW_ASSERT( aMimeTypeString != NULL ); + + *aMimeTypeString = NULL; + + NW_TRY( status ) + { + err = apaSession.Connect(); + if ( err ) + { + NW_THROW( (err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ); + } + apaSessionConnected = ETrue; + + // Ask the application architecture to find the file type. + TPtrC fileNamePtr( (const TUint16*)aFileName, + (TInt)NW_Str_Strlen( aFileName ) ); + + err = apaSession.AppForDocument( fileNamePtr, uid, dataType ); + if ( err ) + { + NW_THROW( (err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ); + } + + TPtrC8 dataTypePtr = dataType.Des8(); + TInt len = dataTypePtr.Length(); + + // If no mime-type yet, then use the default. + if ( !len ) + { + len = sizeof( KDefaultContentType ); + dataTypePtr.Set( KDefaultContentType, len ); + } + + // Allocate a buffer and return the mime-type in it. + len++; // +1 for NULL terminator + *aMimeTypeString = new TUint8 [len]; + if ( *aMimeTypeString == NULL ) + { + NW_THROW( KBrsrOutOfMemory ); + } + TPtr8 mimeTypeStringPtr( *aMimeTypeString, len ); + mimeTypeStringPtr.Copy( dataTypePtr ); + mimeTypeStringPtr.ZeroTerminate(); + } + // status = KBrsrSuccess; // set by NW_CATCH + NW_CATCH( status ) + { + } + NW_FINALLY + { + if ( apaSessionConnected ) + { + apaSession.Close(); + } + return status; + } + NW_END_TRY + } + +// ----------------------------------------------------------------------------- +// GetMimeTypeSize +// Gets the number of characters in the mime-type string. +// @param aFileName Ptr to name of file for which to get the mime-type size. +// @param aSize Ptr to destination into which to return the size. +// @return KBrsrSuccess: if success. +// KBrsrOutOfMemory: if out of memory. +// KBrsrFailure: any other failure. +// ----------------------------------------------------------------------------- +// +static TBrowserStatusCode GetMimeTypeSize( + const NW_Ucs2* aFileName, NW_Uint32* aSize ) + { + TBrowserStatusCode status; + TUint8* mimeType; + + // Parameter assertion block. + NW_ASSERT( aFileName != NULL ); + NW_ASSERT( aSize != NULL ); + + *aSize = 0; // default + status = GetMimeType( aFileName, &mimeType ); + if ( status == KBrsrSuccess ) + { + *aSize = NW_Asc_strlen( (const char*)mimeType ); + delete mimeType; + } + return status; + } + +// ----------------------------------------------------------------------------- +// GetMultipartEntrySize +// Gets the upper bounds on the size of the multipart entry. The size returned +// may not be the exact size but it will be >= exact size. +// +// Each multipart entry is in one of two formats. One is for name/values and +// the other is for files: +// +// name/value: +// --boundary +// Content-Type: text/plain; charset=charsetString +// Content-Disposition: form-data; name="name" +// Content-Length: contentLength +// {value here without the curly braces} +// +// files: +// --boundary +// Content-Disposition: form-data; name="name"; filename="d:\dir\filename.ext" +// {contents of file without the curly braces} +// +// @param aName +// @param aValue +// @param aHttpCharSet +// @param aBoundary +// @param aFirst +// @param aSize +// @return +// ----------------------------------------------------------------------------- +// +static TBrowserStatusCode GetMultipartEntrySize( + const NW_Ucs2* aName, + const NW_Ucs2* aValue, + NW_Http_CharSet_t aHttpCharSet, + NW_Uint8* aBoundary, + TBool aFirst, + NW_Uint32* aSize ) + { + TBrowserStatusCode ret = KBrsrSuccess; + NW_Uint32 size = 0; + + // Parameter assertion block. + NW_ASSERT( aName != NULL ); + NW_ASSERT( aValue != NULL ); + NW_ASSERT( aBoundary != NULL ); + NW_ASSERT( aSize != NULL ); + + // Note, content length is not included in mime part header. Some sites + // do not work properly if included. Microsoft I.E. does not include one. + + // Get size of the boundary, common to all types of mime parts. + size = ( aFirst ) ? NW_Asc_strlen( NW_BOUNDARY_FIRST_START_STR ) : + NW_Asc_strlen( NW_BOUNDARY_START_STR ); + size += NW_Asc_strlen( (const char*)aBoundary ); + + // Add in the rest of the size; depends on type of mime part. + if ( NW_LoadReq_IsUploadFile( aValue ) ) + { + TInt err = KErrNone; + RFs rfs; + TBool rfsConnected = EFalse; + RFile file; + TBool fileOpened = EFalse; + TPtrC fileNamePtr( (TUint16*)aValue ); + TInt fileSize; + + NW_TRY( status ) + { +//TODO STREAMING: This is very in-efficient to connect every time and then to +//reconnect again later to read the file. This will be addressed in next phase +//with streaming implementation. + err = rfs.Connect(); + if ( err ) + { + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + rfsConnected = ETrue; + + err = file.Open( rfs, fileNamePtr, EFileRead | EFileShareExclusive | EFileStream ); + if ( err ) + { +//TODO STREAMING: Also look for missing file error returned. This too will +//change with streaming implementation. + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + fileOpened = ETrue; + + err = file.Size( fileSize ); + if ( err ) + { +//TODO STREAMING: What other errors should be checked? This too will +//change with streaming implementation. + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + + NW_Uint32 mimeTypeSize = 0; + status = GetMimeTypeSize( aValue, &mimeTypeSize ); + _NW_THROW_ON_ERROR( status ); + + NW_Uint32 mimeContentTypeSize = (mimeTypeSize) + ? mimeTypeSize + NW_Asc_strlen( NW_CONTENTTYPE_STR ) : 0; + + size += NW_Asc_strlen( NW_CONTENTDISPOSITION_STR ) + + NW_Str_Strlen( aName ) + 3 //+3 for quotes around name and a semicolon at end + + NW_Asc_strlen( NW_FILENAME_STR ) + + NW_Str_Strlen( aValue ) + 2 //+2 for quotes around filename + + mimeContentTypeSize + + NW_Asc_strlen( NW_HEADEREND_STR ) + + fileSize; + } + // status = KBrsrSuccess; // set by NW_CATCH + NW_CATCH( status ) + { + } + NW_FINALLY + { + if ( fileOpened ) + { + file.Close(); + } + if ( rfsConnected ) + { + rfs.Close(); + } + ret = status; + } + NW_END_TRY + } + else + { + NW_Uint32 transcodedValueLength = GetMaxTranscodedByteLength( aValue, aHttpCharSet ); + size += NW_Asc_strlen( NW_CONTENTTYPE_TEXTPLAIN_CHARSET_STR ) + + NW_Asc_strlen( GetHttpCharsetAsAsciiText( aHttpCharSet ) ) + + NW_Asc_strlen( NW_CONTENTDISPOSITION_STR ) + + NW_Str_Strlen( aName ) + 2/*quotes around name*/ + + NW_Asc_strlen( NW_HEADEREND_STR ) + + transcodedValueLength; + } + + *aSize = (ret == KBrsrSuccess) ? size : 0; + return ret; + } + +// ----------------------------------------------------------------------------- +// GetMultipartSize +// Gets the upper bounds on the size of the multipart data. The size returned +// may not be the exact size but it will be >= exact size. +// @param aPostFields +// @param aHttpCharSet +// @param aBoundary +// @param aSize +// @return +// ----------------------------------------------------------------------------- +// +static TBrowserStatusCode GetMultipartSize( + NW_NVPair_t *aPostFields, + NW_Http_CharSet_t aHttpCharSet, + NW_Uint8* aBoundary, + NW_Uint32* aSize ) + { + NW_Ucs2 *name; + NW_Ucs2 *value; + TBrowserStatusCode status; + NW_Uint32 size; + NW_Uint32 partSize; + TBool first = ETrue; + + // Parameter assertion block. + NW_ASSERT( aPostFields != NULL ); + NW_ASSERT( aBoundary != NULL ); + NW_ASSERT( aSize != NULL ); + + size = 0; + NW_NVPair_ResetIter( aPostFields ); + while ( (status = NW_NVPair_GetNext( aPostFields, &name, &value )) == KBrsrSuccess ) + { + status = GetMultipartEntrySize( name, value, aHttpCharSet, aBoundary, + first, &partSize ); + if ( status != KBrsrSuccess ) + { + return status; + } + size += partSize; + first = EFalse; + } + + // Add the size of the ending boundary marker, plus one for null termination + size += NW_Asc_strlen(NW_BOUNDARY_START_STR) + + NW_Asc_strlen((const char*)aBoundary) + + NW_Asc_strlen(NW_BOUNDARY_END_STR) + 1; + + *aSize = size; + return KBrsrSuccess; + } + +static TBrowserStatusCode StorePart( NW_Uint8* aDataBuffer, NW_Uint32 len, + NW_Uint8* aBoundary, NW_Ucs2* aName, NW_Ucs2* aValue, + NW_Http_CharSet_t aHttpCharSet, TBool aFirst, + NW_Uint8** retPtr ) + { + TBrowserStatusCode ret = KBrsrSuccess; + char* curr = (char*)aDataBuffer; + NW_Ucs2* ptr; + NW_Uint32 neededLen; + NW_Uint32 byteLen = 0; + + // Store the boundary, common to all types of mime parts. + neededLen = NW_Asc_strlen( + ((aFirst) ? NW_BOUNDARY_FIRST_START_STR : NW_BOUNDARY_START_STR) ) + + NW_Asc_strlen( (char*)aBoundary ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + NW_Asc_strcpy( curr, + ((aFirst) ? NW_BOUNDARY_FIRST_START_STR : NW_BOUNDARY_START_STR) ); + NW_Asc_strcat( curr, (char*)aBoundary ); + curr += neededLen; + len -= neededLen; + + // Store the rest of the part; depends on type of mime part. + if ( NW_LoadReq_IsUploadFile( aValue ) ) + { + TInt err = KErrNone; + RFs rfs; + TBool rfsConnected = EFalse; + RFile file; + TBool fileOpened = EFalse; + TPtrC fileNamePtr( (TUint16*)aValue ); + TInt fileSize; + TUint8* mimeType = NULL; + + NW_TRY( status ) + { + // Append the content disposition. + neededLen = NW_Asc_strlen( NW_CONTENTDISPOSITION_STR ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + NW_Asc_strcpy( curr, NW_CONTENTDISPOSITION_STR ); + curr += neededLen; + + // Append the variable name, within quotes. + neededLen = NW_Str_Strlen( aName ) + 3; // +3 for two quotes and semicolon + if ( neededLen >= len ) + { + return KBrsrFailure; + } + *curr++ = '\"'; + ptr = aName; + while (*ptr != 0) + { + *curr++ = (char)*ptr; + ptr++; + } + *curr++ = '\"'; + *curr++ = ';'; // end it with a semicolon + len -= neededLen; + + // Append the file name tag. + neededLen = NW_Asc_strlen( NW_FILENAME_STR ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + NW_Asc_strcpy( curr, NW_FILENAME_STR ); + curr += neededLen; + len -= neededLen; + + // Append the actual file name, within quotes. + neededLen = NW_Str_Strlen( aValue ) + 2; // +2 for quotes + if ( neededLen >= len ) + { + return KBrsrFailure; + } + *curr++ = '\"'; + ptr = aValue; + while (*ptr != 0) + { + *curr++ = (char)*ptr; + ptr++; + } + *curr++ = '\"'; + len -= neededLen; + + // Append the ContentType if the mime-type is known. + status = GetMimeType( aValue, &mimeType ); + _NW_THROW_ON_ERROR( status ); + + if ( mimeType ) + { + neededLen = NW_Asc_strlen( NW_CONTENTTYPE_STR ) + + NW_Asc_strlen( (const char*)mimeType ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + NW_Asc_strcpy( curr, NW_CONTENTTYPE_STR ); + NW_Asc_strcat( curr, (const char*)mimeType ); + curr += neededLen; + len -= neededLen; + } + + // Append the header end. + neededLen = NW_Asc_strlen( NW_HEADEREND_STR ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + NW_Asc_strcpy( curr, NW_HEADEREND_STR ); + curr += neededLen; + len -= neededLen; + + // Append the contents of the file. + err = rfs.Connect(); + if ( err ) + { + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + rfsConnected = ETrue; + + err = file.Open( rfs, fileNamePtr, EFileRead | EFileShareExclusive ); + if ( err ) + { +//TODO STREAMING: Also look for missing file error returned. This too will +//change with streaming implementation. + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + fileOpened = ETrue; + + err = file.Size( fileSize ); + if ( err ) + { +//STREAMING: What other errors should be checked? This too will +//change with streaming implementation. + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + + neededLen = (NW_Uint32)fileSize; + if ( neededLen >= len ) + { + return KBrsrFailure; + } + + TPtr8 tmpPtr( (TUint8*)(curr), 0, fileSize ); + err = file.Read( tmpPtr ); + if ( err ) + { + NW_THROW_STATUS( status, ((err == KErrNoMemory) ? KBrsrOutOfMemory : KBrsrFailure ) ); + } + + curr += neededLen; + len -= neededLen; + } + // status = KBrsrSuccess; // set by NW_CATCH + NW_CATCH( status ) + { + } + NW_FINALLY + { + delete mimeType; + if ( fileOpened ) + { + file.Close(); + } + if ( rfsConnected ) + { + rfs.Close(); + } + } + ret = status; + NW_END_TRY + } + else + { + char* charSetAsText = (char*)GetHttpCharsetAsAsciiText( aHttpCharSet ); + + // Verify the length will not exceed remaining buffer length (not + // including transcoded data yet). + neededLen = NW_Asc_strlen( NW_CONTENTTYPE_TEXTPLAIN_CHARSET_STR ) + + NW_Asc_strlen( charSetAsText ) + + NW_Asc_strlen( NW_CONTENTDISPOSITION_STR ) + + NW_Str_Strlen( aName ) + 2 // +2 for quotes + + NW_Asc_strlen( NW_HEADEREND_STR ); + if ( neededLen >= len ) + { + return KBrsrFailure; + } + + // Append content type and disposition text. + NW_Asc_strcpy( curr, NW_CONTENTTYPE_TEXTPLAIN_CHARSET_STR ); + NW_Asc_strcat( curr, charSetAsText ); + NW_Asc_strcat( curr, NW_CONTENTDISPOSITION_STR ); + curr = curr + NW_Asc_strlen( curr ); + // Append variable name to end of content disposition, within quotes. + *curr++ = '\"'; + ptr = aName; + while (*ptr != 0) + { + *curr++ = (char)*ptr; + ptr++; + } + *curr++ = '\"'; + // Append the header end. + NW_Asc_strcpy( curr, NW_HEADEREND_STR ); + curr = curr + NW_Asc_strlen( curr ); + + // Adjust available buffer length. + len -= neededLen; + + // Append the data. + ret = Transcode( (NW_Uint8*)curr, aValue, aHttpCharSet, len, &byteLen ); + if ( ret != KBrsrSuccess ) + { + if ( ret != KBrsrOutOfMemory ) + { + ret = KBrsrFailure; + } + } + else + { + curr += byteLen; + } + } + + *retPtr = (NW_Uint8*)curr; + return ret; + } + +/***************************************************************** + + Name: BuildMultipartBody + + Description: + Allocate and create a multipart/form-data encoded body for the + postfields. + + Parameters: + aBuffer - out: multipart/form-data encoded postfield body + aPostFields - in: postfields to encode. + aCharset - in: character set to encode the postfield values. + aBoundary - out: boundary value that separates each part; caller + must deallocate regardless of status returned! + + Return Value: + KBrsrSuccess + KBrsrOutOfMemory + KBrsrFailure + +******************************************************************/ +static TBrowserStatusCode BuildMultipartBody( + NW_Buffer_t** aBuffer, + NW_NVPair_t* aPostFields, + const NW_Http_CharSet_t aCharset, + NW_Uint8** aBoundary) + { + NW_Uint32 len; + NW_Byte* p; + NW_Byte* newP; + NW_Ucs2* name; + NW_Ucs2* value; + TBrowserStatusCode status; + + NW_ASSERT( aBuffer != NULL ); + NW_ASSERT( aBoundary != NULL ); + + *aBoundary = NULL; + + status = CreateBoundary( aBoundary ); + if ( status != KBrsrSuccess ) + { + return status; + } + + // Calculate the length of the multipart data. This will be used to + // allocate a buffer to contain the actual data. Note, the length + // is an upper bounds and not necessarily the exact length. + status = GetMultipartSize( aPostFields, aCharset, *aBoundary, &len ); + if ( status != KBrsrSuccess ) + { + return status; + } + NW_ASSERT( len != 0 ); + + // Allocate the buffer for the multipart data. + *aBuffer = NW_Buffer_New( len ); + if ( *aBuffer == NULL) + { + return KBrsrOutOfMemory; + } + + // Insert the multipart content into the buffer. The length was + // determined above; however, remaining length is tracked to make sure + // nothing has changed (like the length of the files) since the time it + // was determined. Tracking the length also provides a sanity check. + p = (*aBuffer)->data; + TBool first = ETrue; + (void)NW_NVPair_ResetIter( aPostFields ); + while ( (status = NW_NVPair_GetNext( aPostFields, &name, &value )) == KBrsrSuccess ) + { + status = StorePart( p, len, *aBoundary, name, value, aCharset, first, &newP ); + if ( status != KBrsrSuccess ) + { + return status; + } + len -= (newP - p); + p = newP; + first = EFalse; + } + + // Make sure there is enough space for ending boundary marker and a null. + NW_Uint32 endingLen = NW_Asc_strlen( NW_BOUNDARY_START_STR ) + + NW_Asc_strlen( (char*)*aBoundary ) + + NW_Asc_strlen( NW_BOUNDARY_END_STR ); + // Add one to make sure there is enough space for the null terminator. + if ( (endingLen + 1) > len ) + { + return KBrsrFailure; + } + + // Add the ending boundary marker and final null terminator. + NW_Asc_strcpy( (char*)p, NW_BOUNDARY_START_STR ); + NW_Asc_strcat( (char*)p, (char*)*aBoundary ); + NW_Asc_strcat( (char*)p, NW_BOUNDARY_END_STR ); + + p += endingLen; + + // Set length in the destination buffer to actual amount of data stored. + (*aBuffer)->length = p - (*aBuffer)->data; + + return KBrsrSuccess; + } + +/* +**------------------------------------------------------------------------- +** External Public (Exported) Functions +**------------------------------------------------------------------------- +*/ + +TBrowserStatusCode NW_LoadReq_Create ( const NW_Ucs2 *url, + const NW_Bool postMethodFlag, + const NW_Ucs2 *referer, + const NW_Ucs2 *acceptCharset, + NW_NVPair_t *postfields, + const NW_Ucs2 *enctype, + const NW_Http_CharSet_t docCharset, + NW_Http_Header_t **header, + NW_Ucs2** resultUrl, + NW_Uint8* method, + NW_Buffer_t** body, + NW_HED_AppServices_t* appServices, + void * appCtx) +{ + TBrowserStatusCode status = KBrsrSuccess; + char* content = NULL; + NW_Http_CharSet_t httpCharset; + NW_Ucs2* escapedPostdata = NULL; + NW_Ucs2* convertedPostdata = NULL; + NW_Uint8* boundary = NULL; + + NW_REQUIRED_PARAM(acceptCharset); + NW_REQUIRED_PARAM(docCharset); + + NW_ASSERT(url != NULL); + + /* Start with empty return values. */ + *resultUrl = NULL; + *method = 0; + *body = NULL; + + /* validate enctype */ + if ( enctype != NULL ) { + if ( postMethodFlag ) { + if ( NW_Str_StricmpConst(enctype, NW_URLENCODED_TYPE_STR) + && NW_Str_StricmpConst(enctype, NW_MULTIPART_TYPE_STR) ) + return KBrsrWmlbrowserBadContent; + } else { + if ( NW_Str_StricmpConst(enctype, NW_URLENCODED_TYPE_STR) ) + return KBrsrWmlbrowserBadContent; + } + } + + /* Get charset to use for the postdata. */ + httpCharset = getPostdataCharset(); + if (httpCharset == HTTP_unknown) { + return KBrsrUnsupportedFormCharset; + } + + if ( postMethodFlag ) { + *resultUrl = NW_Str_Newcpy(url); + if (*resultUrl == NULL) { + goto OutOfMemory; + } + + if ( NW_LoadReq_IsMultipart( enctype ) ) { + + /* post method and multipart/form-data body */ + status = BuildMultipartBody(body, postfields, httpCharset, &boundary); + if (status != KBrsrSuccess) { + goto OutOfMemory; + } + if (*header == NULL) { + *header = UrlLoader_HeadersNew(NULL, + (const unsigned char *)NW_MULTIPART_TYPE_STR, + referer, boundary, (*body)->length, NULL); + } + + if (*header == NULL) { + goto OutOfMemory; + } + + } else { +//??? RFD: This section needs to be corrected along with the rest of the URL-encoding fixes underway. + /* post method and url-encoded body */ + status = getEscapedPostdata(postfields, &escapedPostdata, appServices, appCtx); + if (status != KBrsrSuccess) + goto OutOfMemory; + /* first process the post data to corrected formatted and escaped */ + status = NW_Url_ConvertUnicode( escapedPostdata, &convertedPostdata ); + if (status != KBrsrSuccess) + goto OutOfMemory; + + /* convert the post data from NW_Ucs2 to ascii */ + content = NW_Str_CvtToAscii(convertedPostdata); + if (content == NULL) { + goto OutOfMemory; + } + + /* Create a buffer and insert the content */ + *body = NW_Buffer_New(0); + if (*body == NULL) { + goto OutOfMemory; + } + NW_Buffer_SetData(*body, content); + + /* Remove the null-terminator. */ + (*body)->length -= 1; + if (*header == NULL) { + *header = UrlLoader_HeadersNew(NULL, (const unsigned char *)NW_URLENCODED_TYPE_STR, + referer, boundary, (*body)->length, NULL); + } + + if (*header == NULL) { + goto OutOfMemory; + } + } + } else { + /* GET method */ + /* Don't add Content-Type to GET methods - there is no content */ + /* Create header because the caller expects a header to be created here */ + if (*header == NULL) { + *header = UrlLoader_HeadersNew(NULL, NULL, referer, NULL, 0, NULL); + } + + if (*header == NULL) { + goto OutOfMemory; + } + status = NW_LoadReq_BuildQuery(url, postfields, resultUrl, appServices, appCtx); + if (status != KBrsrSuccess) { + goto OutOfMemory; + } + } + + /* no longer add accept-charsets as headers */ + /* Do the load */ + if ( postMethodFlag ) { + *method = NW_UINT8_CAST(NW_URL_METHOD_POST); + } else { + *method = NW_UINT8_CAST(NW_URL_METHOD_GET); + } + + /* cleanup and return normally */ + NW_Str_Delete(escapedPostdata); + NW_Str_Delete(convertedPostdata); + NW_Mem_Free(boundary); + return status; + + /* + * clean up and return from KBrsrOutOfMemory errors + * Note: NW_Http_Header_Free(), NW_Mem_Free(), NW_Buffer_Free(), + * and NW_Str_Delete() all handle NULL pointers + */ +OutOfMemory: + + NW_Mem_Free(boundary); + NW_Str_Delete(escapedPostdata); + NW_Str_Delete(convertedPostdata); + NW_Mem_Free(content); + UrlLoader_HeadersFree(*header); + *header = NULL; + NW_Str_Delete(*resultUrl); + *resultUrl = NULL; + NW_Buffer_Free(*body); + *body = NULL; + + return KBrsrOutOfMemory; +} + +TBrowserStatusCode NW_LoadReq_BuildQuery(const NW_Ucs2 *url, + NW_NVPair_t *postfields, + NW_Ucs2 **resultUrl, + NW_HED_AppServices_t * appServices, + void * appCtx) +{ + NW_Uint32 len; + NW_Ucs2 *reqUrl = NULL; + NW_Ucs2 *frag = NULL; + NW_Ucs2 *base = NULL; + NW_Ucs2 *pQuery = NULL; + NW_Ucs2 *escapedPostdata = NULL; + TBrowserStatusCode status; + TBrowserStatusCode getQueryStatus; + + NW_ASSERT(url != NULL); + NW_ASSERT(resultUrl != NULL); + + len = NW_Str_Strlen(url) + 1; + status = getEscapedPostdata(postfields, &escapedPostdata, appServices, appCtx); + if (status != KBrsrSuccess) + return status; + + if ((status = NW_Url_GetBase(url, &base)) != KBrsrSuccess) + { + NW_Str_Delete(escapedPostdata); + return status; + } + /* At this point, getQueryStatus cannot be KBrsrMalformedUrl. + If it was the case it would be detected earlier by NW_Url_GetBase() + */ + getQueryStatus = NW_Url_GetQuery(url, &pQuery); + + /* We should free the pQuery - nobody uses it */ + NW_Str_Delete(pQuery); + pQuery = NULL; + + status = NW_Url_GetFragId(url, &frag, NW_FALSE); + if (status != KBrsrSuccess && status != KBrsrFailure) + /* FAILURE means url had no fragment - not a fatal error */ + { + NW_Str_Delete(escapedPostdata); + return status; + } + + len += NW_Str_Strlen(escapedPostdata) + 1; + if (frag) + len += NW_Str_Strlen(frag); + + reqUrl = NW_Str_New(len); + if (reqUrl == NULL) + { + NW_Str_Delete(escapedPostdata); + return KBrsrOutOfMemory; + } + + NW_Str_Strcpy(reqUrl, base); + + /* Prevent a separator to be added to the end of string. + Prevent the WAE_URL_QUERY_SEP to be applied when Url + has the query field: http://www.foo.com/mywml.wml?pf=ONE + */ + if ( ! NW_NVPair_IsEmpty(postfields) ) + { + if (getQueryStatus == KBrsrSuccess) { + NW_Str_StrcatConst(reqUrl, WAE_URL_POSTFIELD_SEP); + } else { + NW_Str_StrcatConst(reqUrl, WAE_URL_QUERY_SEP); + } + } + + NW_Str_Strcat(reqUrl, escapedPostdata); + + if (frag) + { + NW_Str_StrcatConst(reqUrl, WAE_URL_FRAG_SEP); + NW_Str_Strcat(reqUrl, frag); + NW_Str_Delete(frag); + } + + NW_Str_Delete(base); + NW_Str_Delete(escapedPostdata); + + *resultUrl = reqUrl; + return KBrsrSuccess; +} + +// ----------------------------------------------------------------------------- +// NW_LoadReq::IsMultipart +// Checks the encoding type to see if it is for a multipart request. +// (Other items are commented in header.) +// ----------------------------------------------------------------------------- +// +NW_Bool NW_LoadReq_IsMultipart( const NW_Ucs2 *aEncodingType ) + { + NW_Bool ret = NW_FALSE; + if ( aEncodingType ) + { + if ( NW_Str_StricmpConst( aEncodingType, NW_MULTIPART_TYPE_STR ) == 0 ) + { + ret = NW_TRUE; + } + } + return ret; + } + +//TODO STREAMING: Modify the way in which filenames are handled. The current +//implementation is a quick, short-term solution. This is going to change +//with streaming implementation. +// ----------------------------------------------------------------------------- +// NW_LoadReq::IsUploadFile +// Determines whether or not the current part of the multipart post is for a +// file to be uploaded or not. +// (Other items are commented in header.) +// ----------------------------------------------------------------------------- +// +NW_Bool NW_LoadReq_IsUploadFile( const NW_Ucs2 *aValue ) + { + // Checks file name to see if it starts with "C:\" or E:\". + if ( aValue ) + { + if ( (NW_Str_Strnicmp( aValue, KCDrivePrefix, NW_Str_Strlen( KCDrivePrefix ) ) == 0) + || (NW_Str_Strnicmp( aValue, KEDrivePrefix, NW_Str_Strlen( KEDrivePrefix ) ) == 0) ) + { + return NW_TRUE; + } + } + return NW_FALSE; + }