webengine/wmlengine/src/utils/src/nw_loadreq.cpp
changeset 0 dd21522fd290
child 10 a359256acfc6
--- /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 <apgcli.h>
+#include <apmstd.h>
+#include <f32file.h>
+
+#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:
+//    <cr><lf>--boundary<cr><lf>
+//    Content-Type: text/plain; charset=charsetString<cr><lf>
+//    Content-Disposition: form-data; name="name"<cr><lf>
+//    Content-Length: contentLength<cr><lf><cr><lf>
+//    {value here without the curly braces}
+//
+//  files:
+//    <cr><lf>--boundary<cr><lf>
+//    Content-Disposition: form-data; name="name"; filename="d:\dir\filename.ext"<cr><lf><cr><lf>
+//    {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;
+    }