HTTP protocol now supported, InternetCrackUrl fixed, lots of other
fixes.

diff --git a/dlls/wininet/Makefile.in b/dlls/wininet/Makefile.in
index 1bab617..1d79677 100644
--- a/dlls/wininet/Makefile.in
+++ b/dlls/wininet/Makefile.in
@@ -8,6 +8,7 @@
 SPEC_SRCS = wininet.spec
 
 C_SRCS = \
+	http.c \
 	internet.c \
 	ftp.c \
 	utility.c \
diff --git a/dlls/wininet/ftp.c b/dlls/wininet/ftp.c
index 031ff31..b6f1ca9 100644
--- a/dlls/wininet/ftp.c
+++ b/dlls/wininet/ftp.c
@@ -21,10 +21,13 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "windows.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
 #include "wininet.h"
 #include "winerror.h"
 #include "winsock.h"
+#include "heap.h"
 
 #include "debugtools.h"
 #include "internet.h"
@@ -35,7 +38,6 @@
 #define DATA_PACKET_SIZE 	0x2000
 #define szCRLF 			"\r\n"
 #define MAX_BACKLOG 		5
-#define RESPONSE_TIMEOUT        30
 
 typedef enum {
   /* FTP commands with arguments. */
@@ -103,7 +105,6 @@
 BOOL FTP_ParseDirectory(LPWININETFTPSESSIONA lpwfs, INT nSocket, LPFILEPROPERTIESA *lpafp, LPDWORD dwfp);
 HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONA lpwfs, INT nSocket, 
 	LPWIN32_FIND_DATAA lpFindFileData, DWORD dwContext);
-LPSTR FTP_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer);
 DWORD FTP_SetResponseError(DWORD dwResponse);
 
 /***********************************************************************
@@ -135,8 +136,8 @@
 
 	workRequest.asyncall = FTPPUTFILEA;
 	workRequest.HFTPSESSION = (DWORD)hConnect;
-	workRequest.LPSZLOCALFILE = (DWORD)strdup(lpszLocalFile);
-	workRequest.LPSZNEWREMOTEFILE = (DWORD)strdup(lpszNewRemoteFile);
+	workRequest.LPSZLOCALFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszLocalFile);
+	workRequest.LPSZNEWREMOTEFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszNewRemoteFile);
 	workRequest.DWFLAGS = dwFlags;
 	workRequest.DWCONTEXT = dwContext;
 
@@ -250,7 +251,7 @@
 
         workRequest.asyncall = FTPSETCURRENTDIRECTORYA;
 	workRequest.HFTPSESSION = (DWORD)hConnect;
-        workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory);
+        workRequest.LPSZDIRECTORY = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszDirectory);
 
 	return INTERNET_AsyncCall(&workRequest);
     }
@@ -347,7 +348,7 @@
 
         workRequest.asyncall = FTPCREATEDIRECTORYA;  
 	workRequest.HFTPSESSION = (DWORD)hConnect;
-	workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory);
+	workRequest.LPSZDIRECTORY = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszDirectory);
 
 	return INTERNET_AsyncCall(&workRequest);
     }
@@ -443,7 +444,7 @@
 
         workRequest.asyncall = FTPFINDFIRSTFILEA;
 	workRequest.HFTPSESSION = (DWORD)hConnect;
-	workRequest.LPSZSEARCHFILE = (DWORD)strdup(lpszSearchFile);
+	workRequest.LPSZSEARCHFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszSearchFile);
 	workRequest.LPFINDFILEDATA = (DWORD)lpFindFileData;
 	workRequest.DWFLAGS = dwFlags;
 	workRequest.DWCONTEXT= dwContext;
@@ -706,7 +707,7 @@
 
         workRequest.asyncall = FTPOPENFILEA;
 	workRequest.HFTPSESSION = (DWORD)hFtpSession;
-	workRequest.LPSZFILENAME = (DWORD)strdup(lpszFileName);
+	workRequest.LPSZFILENAME = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszFileName);
 	workRequest.FDWACCESS = fdwAccess;
 	workRequest.DWFLAGS = dwFlags;
 	workRequest.DWCONTEXT = dwContext;
@@ -827,8 +828,8 @@
 
         workRequest.asyncall = FTPGETFILEA;
 	workRequest.HFTPSESSION = (DWORD)hInternet;
-	workRequest.LPSZREMOTEFILE = (DWORD)strdup(lpszRemoteFile);
-	workRequest.LPSZNEWFILE = (DWORD)strdup(lpszNewFile);
+	workRequest.LPSZREMOTEFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszRemoteFile);
+	workRequest.LPSZNEWFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszNewFile);
 	workRequest.DWLOCALFLAGSATTRIBUTE  = dwLocalFlagsAttribute;
 	workRequest.FFAILIFEXISTS = (DWORD)fFailIfExists;
 	workRequest.DWFLAGS = dwInternetFlags;
@@ -954,7 +955,7 @@
 
         workRequest.asyncall = FTPRENAMEFILEA;
 	workRequest.HFTPSESSION = (DWORD)hFtpSession;
-	workRequest.LPSZFILENAME = (DWORD)strdup(lpszFileName);
+	workRequest.LPSZFILENAME = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszFileName);
 
 	return INTERNET_AsyncCall(&workRequest);
     }
@@ -1048,7 +1049,7 @@
 
         workRequest.asyncall = FTPREMOVEDIRECTORYA;
 	workRequest.HFTPSESSION = (DWORD)hFtpSession;
-	workRequest.LPSZDIRECTORY = (DWORD)strdup(lpszDirectory);
+	workRequest.LPSZDIRECTORY = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszDirectory);
 
 	return INTERNET_AsyncCall(&workRequest);
     }
@@ -1143,8 +1144,8 @@
 
         workRequest.asyncall = FTPRENAMEFILEA;
 	workRequest.HFTPSESSION = (DWORD)hFtpSession;
-	workRequest.LPSZSRCFILE = (DWORD)strdup(lpszSrc);
-	workRequest.LPSZDESTFILE = (DWORD)strdup(lpszDest);
+	workRequest.LPSZSRCFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszSrc);
+	workRequest.LPSZDESTFILE = (DWORD)HEAP_strdupA(GetProcessHeap(),0,lpszDest);
 
 	return INTERNET_AsyncCall(&workRequest);
     }
@@ -1309,13 +1310,13 @@
 
         if (NULL == lpszUserName)
         {
-            lpwfs->lpszUserName = strdup("anonymous");
-            lpwfs->lpszPassword = strdup("user@server");
+            lpwfs->lpszUserName = HEAP_strdupA(GetProcessHeap(),0,"anonymous");
+            lpwfs->lpszPassword = HEAP_strdupA(GetProcessHeap(),0,"user@server");
         }
         else
         {
-            lpwfs->lpszUserName = strdup(lpszUserName);
-            lpwfs->lpszPassword = strdup(lpszPassword);
+            lpwfs->lpszUserName = HEAP_strdupA(GetProcessHeap(),0,lpszUserName);
+            lpwfs->lpszPassword = HEAP_strdupA(GetProcessHeap(),0,lpszPassword);
         }
 
         if (FTP_ConnectToHost(lpwfs))
@@ -1481,7 +1482,7 @@
     while(1)
     {
 	nRecv = dwResponse;
-	if (!FTP_GetNextLine(nSocket, lpszResponse, &nRecv))
+	if (!INTERNET_GetNextLine(nSocket, lpszResponse, &nRecv))
 	    goto lerror;
 
         if (nRecv >= 3 && lpszResponse[3] != '-')
@@ -2177,7 +2178,7 @@
         goto lend;
     }
 
-    while ((pszLine = FTP_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
+    while ((pszLine = INTERNET_GetNextLine(nSocket, INTERNET_GetResponseBuffer(), &nBufLen)) != NULL)
     {
         if (sizeFilePropArray <= indexFilePropArray)
         {
@@ -2272,7 +2273,7 @@
         pszToken = strtok(NULL, " \t");
         if(pszToken != NULL)
         {
-            curFileProp->lpszName = strdup(pszToken);
+            curFileProp->lpszName = HEAP_strdupA(GetProcessHeap(),0,pszToken);
             TRACE(": %s\n", curFileProp->lpszName);
         }
 
@@ -2371,71 +2372,6 @@
 
 
 /***********************************************************************
- *           FTP_GetNextLine  (internal)
- *
- * Parse next line in directory string listing
- *
- * RETURNS
- *   Pointer to begining of next line
- *   NULL on failure
- *
- */
-
-LPSTR FTP_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
-{
-    struct timeval tv;
-    fd_set infd;
-    BOOL bSuccess = FALSE;
-    INT nRecv = 0;
-
-    TRACE("\n");
-
-    while (nRecv < *dwBuffer)
-    {
-        FD_ZERO(&infd);
-        FD_SET(nSocket, &infd);
-        tv.tv_sec=RESPONSE_TIMEOUT;
-        tv.tv_usec=0;
-
-        if (select(nSocket+1,&infd,NULL,NULL,&tv))
-        {
-            if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
-            {
-                INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
-                goto lend;
-            }
-
-            if (lpszBuffer[nRecv] == '\n')
-	    {
-		bSuccess = TRUE;
-                break;
-	    }
-            if (lpszBuffer[nRecv] != '\r')
-                nRecv++;
-        }
-	else
-	{
-            INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
-            goto lend;
-        }
-    }
-
-lend:
-    if (bSuccess)
-    {
-        lpszBuffer[nRecv] = '\0';
-	*dwBuffer = nRecv - 1;
-        TRACE(":%d %s\n", nRecv, lpszBuffer);
-        return lpszBuffer;
-    }
-    else
-    {
-        return NULL;
-    }
-}
-
-
-/***********************************************************************
  *           FTP_SetResponseError (internal)
  *
  * Set the appropriate error code for a given response from the server
diff --git a/dlls/wininet/http.c b/dlls/wininet/http.c
new file mode 100644
index 0000000..35e68d0
--- /dev/null
+++ b/dlls/wininet/http.c
@@ -0,0 +1,1306 @@
+/* 
+ * Wininet - Http Implementation
+ *
+ * Copyright 1999 Corel Corporation
+ *
+ * Ulrich Czekalla
+ *
+ */
+
+#include "windows.h"
+#include "wininet.h"
+#include "debugtools.h"
+#include "winerror.h"
+#include "heap.h"
+#include "tchar.h"
+#include "winsock.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "internet.h"
+
+DEFAULT_DEBUG_CHANNEL(wininet)
+
+#define HTTPHEADER " HTTP/1.0"
+#define HTTPHOSTHEADER "\r\nHost: "
+#define MAXHOSTNAME 100
+#define MAX_FIELD_VALUE_LEN 256
+#define MAX_FIELD_LEN 256
+
+
+#define HTTP_REFERER	"Referer"
+#define HTTP_ACCEPT		"Accept"
+
+#define HTTP_ADDHDR_FLAG_ADD				0x20000000
+#define HTTP_ADDHDR_FLAG_ADD_IF_NEW			0x10000000
+#define HTTP_ADDHDR_FLAG_COALESCE			0x40000000
+#define HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA		0x40000000
+#define HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON	0x01000000
+#define HTTP_ADDHDR_FLAG_REPLACE			0x80000000
+#define HTTP_ADDHDR_FLAG_REQ				0x02000000
+
+
+BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr);
+int HTTP_WriteDataToStream(LPWININETHTTPREQA lpwhr, 
+	void *Buffer, int BytesToWrite);
+int HTTP_ReadDataFromStream(LPWININETHTTPREQA lpwhr, 
+	void *Buffer, int BytesToRead);
+BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr);
+BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier);
+void HTTP_CloseConnection(LPWININETHTTPREQA lpwhr);
+BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen);
+INT HTTP_GetStdHeaderIndex(LPCSTR lpszField);
+INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr);
+INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField);
+
+/***********************************************************************
+ *           HttpAddRequestHeadersA (WININET.68)
+ *
+ * Adds one or more HTTP header to the request handler
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+INTERNETAPI BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest, 
+	LPCSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
+{
+    LPSTR lpszStart;
+    LPSTR lpszEnd;
+    LPSTR buffer;
+    CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
+    BOOL bSuccess = FALSE;
+    LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
+
+    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+        return FALSE;
+    }
+
+    buffer = HEAP_strdupA(GetProcessHeap(), 0, lpszHeader);
+    lpszStart = buffer;
+
+    do
+    {
+        lpszEnd = lpszStart;
+
+        while (*lpszEnd != '\0')
+        {
+            if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
+                 break;
+            lpszEnd++;
+        }
+
+        if (*lpszEnd == '\0')
+	    break;
+
+        *lpszEnd = '\0';
+
+        if (HTTP_InterpretHttpHeader(lpszStart, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
+            bSuccess = HTTP_ProcessHeader(lpwhr, field, value, dwModifier | HTTP_ADDHDR_FLAG_REQ);
+
+        lpszStart = lpszEnd + 2; /* Jump over \0\n */
+
+    } while (bSuccess);
+
+    HeapFree(GetProcessHeap(), 0, buffer);
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HttpOpenRequestA (WININET.72)
+ *
+ * Open a HTTP request handle
+ *
+ * RETURNS
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL 	 on failure
+ *
+ */
+INTERNETAPI HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
+	LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
+	LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 
+	DWORD dwFlags, DWORD dwContext)
+{
+    LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
+    LPWININETAPPINFOA hIC = NULL;
+
+    TRACE("\n");
+
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+
+	workRequest.asyncall = HTTPOPENREQUESTA;
+	workRequest.HFTPSESSION = (DWORD)hHttpSession;
+	workRequest.LPSZVERB = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszVerb);
+	workRequest.LPSZOBJECTNAME = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszObjectName);
+	workRequest.LPSZVERSION = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszVersion);
+	workRequest.LPSZREFERRER = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszReferrer);
+	workRequest.LPSZACCEPTTYPES = (DWORD)lpszAcceptTypes;
+	workRequest.DWFLAGS = dwFlags;
+	workRequest.DWCONTEXT = dwContext;
+
+	return (HINTERNET)INTERNET_AsyncCall(&workRequest);
+    }
+    else
+    {
+	return HTTP_HttpOpenRequestA(hHttpSession, lpszVerb, lpszObjectName, 
+		lpszVersion, lpszReferrer, lpszAcceptTypes, dwFlags, dwContext);
+    }
+}
+
+
+/***********************************************************************
+ *           HTTP_HttpOpenRequestA (internal)
+ *
+ * Open a HTTP request handle
+ *
+ * RETURNS
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL 	 on failure
+ *
+ */
+INTERNETAPI HINTERNET WINAPI HTTP_HttpOpenRequestA(HINTERNET hHttpSession,
+	LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
+	LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 
+	DWORD dwFlags, DWORD dwContext)
+{
+    LPWININETHTTPSESSIONA lpwhs = (LPWININETHTTPSESSIONA) hHttpSession;
+    LPWININETAPPINFOA hIC = NULL;
+    LPWININETHTTPREQA lpwhr;
+
+    TRACE("\n");
+
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
+
+    lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQA));
+    if (NULL == lpwhr)
+    {
+        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        return (HINTERNET) NULL;
+    }
+
+    lpwhr->hdr.htype = WH_HHTTPREQ;
+    lpwhr->hdr.lpwhparent = hHttpSession;
+    lpwhr->hdr.dwFlags = dwFlags;
+    lpwhr->hdr.dwContext = dwContext;
+
+    if (NULL != lpszObjectName && strlen(lpszObjectName))
+    	lpwhr->lpszPath = HEAP_strdupA(GetProcessHeap(), 0, lpszObjectName);
+
+    if (NULL != lpszReferrer && strlen(lpszReferrer))
+        HTTP_ProcessHeader(lpwhr, HTTP_REFERER, lpszReferrer, HTTP_ADDHDR_FLAG_COALESCE);
+
+    //! FIXME
+    if (NULL != lpszAcceptTypes && strlen(*lpszAcceptTypes))
+        HTTP_ProcessHeader(lpwhr, HTTP_ACCEPT, *lpszAcceptTypes, HTTP_ADDHDR_FLAG_COALESCE);
+
+    if (NULL == lpszVerb)
+        lpwhr->lpszVerb = HEAP_strdupA(GetProcessHeap(), 0, "GET");
+    else if (strlen(lpszVerb))
+        lpwhr->lpszVerb = HEAP_strdupA(GetProcessHeap(), 0, lpszVerb);
+
+    if (NULL != lpszReferrer)
+    {
+        char buf[MAXHOSTNAME];
+        URL_COMPONENTSA UrlComponents;
+
+        UrlComponents.lpszExtraInfo = NULL;
+        UrlComponents.lpszPassword = NULL;
+        UrlComponents.lpszScheme = NULL;
+        UrlComponents.lpszUrlPath = NULL;
+        UrlComponents.lpszUserName = NULL;
+        UrlComponents.lpszHostName = buf;
+        UrlComponents.dwHostNameLength = MAXHOSTNAME;
+
+        InternetCrackUrlA(lpszReferrer, 0, 0, &UrlComponents);
+        if (strlen(UrlComponents.lpszHostName))
+            lpwhr->lpszHostName = HEAP_strdupA(GetProcessHeap(), 0, UrlComponents.lpszHostName);
+    }
+
+    if (hIC->lpfnStatusCB)
+    {
+        INTERNET_ASYNC_RESULT iar;
+
+        iar.dwResult = (DWORD)lpwhr;
+        iar.dwError = ERROR_SUCCESS;
+
+        hIC->lpfnStatusCB(hHttpSession, dwContext, INTERNET_STATUS_HANDLE_CREATED,
+             &iar, sizeof(INTERNET_ASYNC_RESULT));
+    }
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
+    {
+        INTERNET_ASYNC_RESULT iar;
+	       
+        iar.dwResult = (DWORD)lpwhr;
+        iar.dwError = lpwhr ? ERROR_SUCCESS : INTERNET_GetLastError();
+        hIC->lpfnStatusCB(hHttpSession, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
+            &iar, sizeof(INTERNET_ASYNC_RESULT));
+    }
+
+    return (HINTERNET) lpwhr;
+}
+
+
+/***********************************************************************
+ *           HttpQueryInfoA (WININET.74)
+ *
+ * Queries for information about an HTTP request
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
+	LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+{
+    LPHTTPHEADERA lphttpHdr = NULL; 
+    BOOL bSuccess = FALSE;
+    LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
+		
+    TRACE("(0x%08lx)--> %ld\n", dwInfoLevel, dwInfoLevel);
+
+    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+        return FALSE;
+    }
+
+    /* Find requested header structure */
+    if ((dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK) == HTTP_QUERY_CUSTOM)
+    {
+        INT index = HTTP_GetCustomHeaderIndex(lpwhr, (LPSTR)lpBuffer);
+
+        if (index < 0)
+            goto lend;
+
+        lphttpHdr = &lpwhr->pCustHeaders[index];
+    }
+    else
+    {
+        INT index = dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK;
+
+        if (index == HTTP_QUERY_RAW_HEADERS_CRLF || index == HTTP_QUERY_RAW_HEADERS)
+        {
+            INT i, delim, size = 0, cnt = 0;
+
+            delim = index == HTTP_QUERY_RAW_HEADERS_CRLF ? 2 : 1;
+
+           /* Calculate length of custom reuqest headers */
+           for (i = 0; i < lpwhr->nCustHeaders; i++)
+           {
+               if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->pCustHeaders[i].lpszField &&
+		   lpwhr->pCustHeaders[i].lpszValue)
+	       {
+                  size += strlen(lpwhr->pCustHeaders[i].lpszField) + 
+                       strlen(lpwhr->pCustHeaders[i].lpszValue) + delim + 2;
+	       }
+           }
+
+           /* Calculate the length of stadard request headers */
+           for (i = 0; i <= HTTP_QUERY_MAX; i++)
+           {
+              if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) && lpwhr->StdHeaders[i].lpszField && 
+                   lpwhr->StdHeaders[i].lpszValue)
+              {
+                 size += strlen(lpwhr->StdHeaders[i].lpszField) + 
+                    strlen(lpwhr->StdHeaders[i].lpszValue) + delim + 2;
+              }
+           }
+
+           size += delim;
+
+           if (size + 1 > *lpdwBufferLength)
+           {
+              *lpdwBufferLength = size + 1;
+              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
+              goto lend;
+           }
+
+           /* Append standard request heades */
+           for (i = 0; i <= HTTP_QUERY_MAX; i++)
+           {
+               if ((~lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST) &&
+					   lpwhr->StdHeaders[i].lpszField &&
+					   lpwhr->StdHeaders[i].lpszValue)
+               {
+                   cnt += sprintf(lpBuffer + cnt, "%s: %s%s", lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue,
+                          index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
+               }
+            }
+
+            /* Append custom request heades */
+            for (i = 0; i < lpwhr->nCustHeaders; i++)
+            {
+                if ((~lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST) &&
+						lpwhr->pCustHeaders[i].lpszField &&
+						lpwhr->pCustHeaders[i].lpszValue)
+                {
+                   cnt += sprintf(lpBuffer + cnt, "%s: %s%s", 
+                    lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue,
+					index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "\0");
+                }
+            }
+
+            strcpy(lpBuffer + cnt, index == HTTP_QUERY_RAW_HEADERS_CRLF ? "\r\n" : "");
+
+           *lpdwBufferLength = cnt + delim;
+           bSuccess = TRUE;
+	        goto lend;
+        }
+	else if (index >= 0 && index <= HTTP_QUERY_MAX && lpwhr->StdHeaders[index].lpszValue)
+	{ 
+	    lphttpHdr = &lpwhr->StdHeaders[index];
+	}
+	else
+            goto lend;
+    }
+
+    /* Ensure header satisifies requested attributes */
+    if ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) && 
+	    (~lphttpHdr->wFlags & HDR_ISREQUEST))
+	goto lend;
+     
+    /* coalesce value to reuqested type */
+    if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER)
+    {
+       *(int *)lpBuffer = atoi(lphttpHdr->lpszValue);
+	   bSuccess = TRUE;
+    }
+    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)
+    {
+        time_t tmpTime;
+        struct tm tmpTM;
+        SYSTEMTIME *STHook;
+	     
+        tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
+
+        tmpTM = *gmtime(&tmpTime);
+        STHook = (SYSTEMTIME *) lpBuffer;
+        if(STHook==NULL)
+            goto lend;
+
+	    STHook->wDay = tmpTM.tm_mday;
+	    STHook->wHour = tmpTM.tm_hour;
+	    STHook->wMilliseconds = 0;
+	    STHook->wMinute = tmpTM.tm_min;
+	    STHook->wDayOfWeek = tmpTM.tm_wday;
+	    STHook->wMonth = tmpTM.tm_mon + 1;
+	    STHook->wSecond = tmpTM.tm_sec;
+	    STHook->wYear = tmpTM.tm_year;
+
+	    bSuccess = TRUE;
+    }
+    else if (dwInfoLevel & HTTP_QUERY_FLAG_COALESCE)
+    {
+	    if (*lpdwIndex >= lphttpHdr->wCount)
+		{
+	        INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
+		}
+	    else
+	    {
+	    //! Copy strncpy(lpBuffer, lphttpHdr[*lpdwIndex], len);
+            (*lpdwIndex)++;
+	    }
+    }
+    else
+    {
+        INT len = strlen(lphttpHdr->lpszValue);
+
+        if (len + 1 > *lpdwBufferLength)
+        {
+            *lpdwBufferLength = len + 1;
+            INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
+            goto lend;
+        }
+
+        strncpy(lpBuffer, lphttpHdr->lpszValue, len);
+        *lpdwBufferLength = len;
+        bSuccess = TRUE; 
+    }
+
+lend:
+    TRACE("%d <--\n", bSuccess);
+    return bSuccess;
+}
+
+/***********************************************************************
+ *           HttpSendRequestA (WININET.76)
+ *
+ * Sends the specified request to the HTTP server
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
+	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+{	
+    LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
+    LPWININETHTTPSESSIONA lpwhs = NULL;
+    LPWININETAPPINFOA hIC = NULL;
+
+    TRACE("0x%08lx\n", (unsigned long)hHttpRequest);
+
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
+    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+
+	workRequest.asyncall = HTTPSENDREQUESTA;
+	workRequest.HFTPSESSION = (DWORD)hHttpRequest;
+	workRequest.LPSZHEADER = (DWORD)HEAP_strdupA(GetProcessHeap(), 0, lpszHeaders);
+	workRequest.DWHEADERLENGTH = dwHeaderLength;
+	workRequest.LPOPTIONAL = (DWORD)lpOptional;
+	workRequest.DWOPTIONALLENGTH = dwOptionalLength;
+
+	return INTERNET_AsyncCall(&workRequest);
+    }
+    else
+    {
+	return HTTP_HttpSendRequestA(hHttpRequest, lpszHeaders, 
+		dwHeaderLength, lpOptional, dwOptionalLength);
+    }
+}
+
+
+/***********************************************************************
+ *           HTTP_HttpSendRequestA (internal)
+ *
+ * Sends the specified request to the HTTP server
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HTTP_HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
+	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+{
+    INT cnt;
+    INT i;
+    BOOL bSuccess = FALSE;
+    LPSTR requestString = NULL;
+    INT requestStringLen;
+    INT headerLength = 0;
+    LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hHttpRequest;
+    LPWININETHTTPSESSIONA lpwhs = NULL;
+    LPWININETAPPINFOA hIC = NULL;
+
+    TRACE("0x%08lx\n", (ULONG)hHttpRequest);
+
+    /* Verify our tree of internet handles */
+    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    lpwhs = (LPWININETHTTPSESSIONA) lpwhr->hdr.lpwhparent;
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    hIC = (LPWININETAPPINFOA) lpwhs->hdr.lpwhparent;
+    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    /* Clear any error information */
+    INTERNET_SetLastError(0);
+
+    /* We must have a verb */
+    if (NULL == lpwhr->lpszVerb)
+    {
+	    goto lend;
+    }
+
+    /* If we don't have a path we set it to root */
+    if (NULL == lpwhr->lpszPath)
+        lpwhr->lpszPath = HEAP_strdupA(GetProcessHeap(), 0, "/");
+
+    /* Calculate length of request string */
+    requestStringLen = 
+        strlen(lpwhr->lpszVerb) +
+        strlen(lpwhr->lpszPath) +
+        (lpwhr->lpszHostName ? (strlen(HTTPHOSTHEADER) + strlen(lpwhr->lpszHostName)) : 0) +
+        strlen(HTTPHEADER) +
+        5; /* " \r\n\r\n" */
+
+    /* Add length of passed headers */
+    if (lpszHeaders)
+    {
+        headerLength = -1 == dwHeaderLength ?  strlen(lpszHeaders) : dwHeaderLength; 
+        requestStringLen += headerLength +  2; /* \r\n */
+    }
+
+    /* Calculate length of custom reuqest headers */
+    for (i = 0; i < lpwhr->nCustHeaders; i++)
+    {
+	    if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
+	    {
+            requestStringLen += strlen(lpwhr->pCustHeaders[i].lpszField) + 
+                strlen(lpwhr->pCustHeaders[i].lpszValue) + 4; /*: \r\n */
+	    }
+    }
+
+    /* Calculate the length of stadard request headers */
+    for (i = 0; i <= HTTP_QUERY_MAX; i++)
+    {
+       if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
+       {
+          requestStringLen += strlen(lpwhr->StdHeaders[i].lpszField) + 
+             strlen(lpwhr->StdHeaders[i].lpszValue) + 4; /*: \r\n */
+       }
+    }
+
+    /* Allocate string to hold entire request */
+    requestString = HeapAlloc(GetProcessHeap(), 0, requestStringLen + 1);
+    if (NULL == requestString)
+    {
+        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        goto lend;
+    }
+
+    /* Build request string */
+    cnt = sprintf(requestString, "%s %s%s%s",
+        lpwhr->lpszVerb,
+        lpwhr->lpszPath,
+        lpwhr->lpszHostName ? (HTTPHEADER HTTPHOSTHEADER) : HTTPHEADER,
+        lpwhr->lpszHostName ? lpwhr->lpszHostName : "");
+
+    /* Append standard request heades */
+    for (i = 0; i <= HTTP_QUERY_MAX; i++)
+    {
+       if (lpwhr->StdHeaders[i].wFlags & HDR_ISREQUEST)
+       {
+           cnt += sprintf(requestString + cnt, "\r\n%s: %s", 
+               lpwhr->StdHeaders[i].lpszField, lpwhr->StdHeaders[i].lpszValue);
+       }
+    }
+
+    /* Append custom request heades */
+    for (i = 0; i < lpwhr->nCustHeaders; i++)
+    {
+       if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
+       {
+           cnt += sprintf(requestString + cnt, "\r\n%s: %s", 
+               lpwhr->pCustHeaders[i].lpszField, lpwhr->pCustHeaders[i].lpszValue);
+       }
+    }
+
+    /* Append passed request headers */
+    if (lpszHeaders)
+    {
+        strcpy(requestString + cnt, "\r\n");
+        cnt += 2;
+        strcpy(requestString + cnt, lpszHeaders);
+        cnt += headerLength;
+    }
+
+    /* Set termination string for request */
+    strcpy(requestString + cnt, "\r\n\r\n");
+
+    if (hIC->lpfnStatusCB)
+        hIC->lpfnStatusCB(hHttpRequest, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
+
+    TRACE("(%s) len(%d)\n", requestString, requestStringLen);
+    /* Send the request and store the results */
+    if (!HTTP_OpenConnection(lpwhr))
+        goto lend;
+
+    cnt = INTERNET_WriteDataToStream(lpwhr->nSocketFD, requestString, requestStringLen);
+
+    if (cnt < 0)
+        goto lend;
+
+    if (HTTP_GetResponseHeaders(lpwhr))
+	    bSuccess = TRUE;
+
+lend:
+
+    if (requestString)
+        HeapFree(GetProcessHeap(), 0, requestString);
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC  && hIC->lpfnStatusCB)
+    {
+        INTERNET_ASYNC_RESULT iar;
+	       
+        iar.dwResult = (DWORD)bSuccess;
+        iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
+        hIC->lpfnStatusCB(hHttpRequest, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
+            &iar, sizeof(INTERNET_ASYNC_RESULT));
+    }
+
+    TRACE("<--\n");
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HTTP_Connect  (internal)
+ *
+ * Create http session handle
+ *
+ * RETURNS
+ *   HINTERNET a session handle on success
+ *   NULL on failure
+ *
+ */
+HINTERNET HTTP_Connect(HINTERNET hInternet, LPCSTR lpszServerName, 
+	INTERNET_PORT nServerPort, LPCSTR lpszUserName,
+	LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext)
+{
+    BOOL bSuccess = FALSE;
+    LPWININETAPPINFOA hIC = NULL;
+    LPWININETHTTPSESSIONA lpwhs = NULL;
+
+    TRACE("\n");
+
+    if (((LPWININETHANDLEHEADER)hInternet)->htype != WH_HINIT)
+        goto lerror;
+
+    hIC = (LPWININETAPPINFOA) hInternet;
+
+    lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONA));
+    if (NULL == lpwhs)
+    {
+        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+	goto lerror;
+    }
+
+    if (hIC->lpfnStatusCB)
+        hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_RESOLVING_NAME,
+           lpszServerName, strlen(lpszServerName));
+
+    if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
+	nServerPort = INTERNET_DEFAULT_HTTP_PORT;
+
+    if (!GetAddress(lpszServerName, nServerPort, &lpwhs->phostent, &lpwhs->socketAddress))
+    {
+            INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
+            goto lerror;
+    }
+
+    if (hIC->lpfnStatusCB)
+        hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_NAME_RESOLVED,
+           lpszServerName, strlen(lpszServerName));
+
+    lpwhs->hdr.htype = WH_HHTTPSESSION;
+    lpwhs->hdr.lpwhparent = (LPWININETHANDLEHEADER)hInternet;
+    lpwhs->hdr.dwFlags = dwFlags;
+    lpwhs->hdr.dwContext = dwContext;
+    if (NULL != lpszServerName)
+        lpwhs->lpszServerName = HEAP_strdupA(GetProcessHeap(), 0, lpszServerName);
+    if (NULL != lpszUserName)
+        lpwhs->lpszUserName = HEAP_strdupA(GetProcessHeap(), 0, lpszUserName);
+    lpwhs->nServerPort = nServerPort;
+
+    if (hIC->lpfnStatusCB)
+    {
+        INTERNET_ASYNC_RESULT iar;
+
+        iar.dwResult = (DWORD)lpwhs;
+        iar.dwError = ERROR_SUCCESS;
+
+        hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_HANDLE_CREATED,
+             &iar, sizeof(INTERNET_ASYNC_RESULT));
+    }
+
+    bSuccess = TRUE;
+
+lerror:
+    if (!bSuccess && lpwhs)
+    {
+        HeapFree(GetProcessHeap(), 0, lpwhs);
+	lpwhs = NULL;
+    }
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
+    {
+        INTERNET_ASYNC_RESULT iar;
+	       
+        iar.dwResult = (DWORD)lpwhs;
+        iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
+        hIC->lpfnStatusCB(hInternet, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
+            &iar, sizeof(INTERNET_ASYNC_RESULT));
+    }
+TRACE("<--\n");
+    return (HINTERNET)lpwhs;
+}
+
+
+/***********************************************************************
+ *           HTTP_OpenConnection (internal)
+ *
+ * Connect to a web server
+ *
+ * RETURNS
+ *
+ *   TRUE  on success
+ *   FALSE on failure
+ */
+BOOL HTTP_OpenConnection(LPWININETHTTPREQA lpwhr)
+{
+    BOOL bSuccess = FALSE;
+    INT result;
+    LPWININETHTTPSESSIONA lpwhs;
+
+    TRACE("\n");
+
+    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        goto lend;
+    }
+
+    lpwhs = (LPWININETHTTPSESSIONA)lpwhr->hdr.lpwhparent;
+
+    lpwhr->nSocketFD = socket(lpwhs->phostent->h_addrtype,SOCK_STREAM,0);
+    if (INVALID_SOCKET == lpwhr->nSocketFD)
+    {
+	WARN("Socket creation failed\n");
+        goto lend;
+    }
+
+    result = connect(lpwhr->nSocketFD, (struct sockaddr *)&lpwhs->socketAddress,
+        sizeof(lpwhs->socketAddress));
+
+    if (SOCKET_ERROR == result)
+    {
+       WARN("Unable to connect to host: %d\n", errno);
+       goto lend;
+    }
+
+    bSuccess = TRUE;
+
+lend:
+    TRACE(": %d\n", bSuccess);
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HTTP_GetResponseHeaders (internal)
+ *
+ * Read server response
+ *
+ * RETURNS
+ *
+ *   TRUE  on success
+ *   FALSE on error
+ */
+BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQA lpwhr)
+{
+    INT cbreaks = 0;
+    CHAR buffer[MAX_REPLY_LEN];
+    DWORD buflen = MAX_REPLY_LEN;
+    BOOL bSuccess = FALSE;
+    CHAR value[MAX_FIELD_VALUE_LEN], field[MAX_FIELD_LEN];
+
+    TRACE("\n");
+
+    if (INVALID_SOCKET == lpwhr->nSocketFD)
+        goto lend;
+
+    /* 
+     * We should first receive 'HTTP/1.x nnn' where nnn is the status code.
+     */
+    if (!INTERNET_GetNextLine(lpwhr->nSocketFD, buffer, &buflen))
+        goto lend;
+
+    if (strncmp(buffer, "HTTP", 4) != 0)
+        goto lend;
+	
+    buffer[12]='\0';
+    HTTP_ProcessHeader(lpwhr, "Status", buffer+9, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
+
+    /* Parse each response line */
+    do
+    {
+	buflen = MAX_REPLY_LEN;
+	if (INTERNET_GetNextLine(lpwhr->nSocketFD, buffer, &buflen))
+	{
+            if (!HTTP_InterpretHttpHeader(buffer, field, MAX_FIELD_LEN, value, MAX_FIELD_VALUE_LEN))
+                break;
+
+            HTTP_ProcessHeader(lpwhr, field, value, (HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE));
+	}
+	else
+	{
+	    cbreaks++;
+	    if (cbreaks >= 2)
+	       break;
+	}
+    }while(1);
+
+    bSuccess = TRUE;
+
+lend:
+
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HTTP_InterpretHttpHeader (internal)
+ *
+ * Parse server response
+ *
+ * RETURNS
+ *
+ *   TRUE  on success
+ *   FALSE on error
+ */
+INT stripSpaces(LPCSTR lpszSrc, LPSTR lpszStart, INT *len)
+{
+    LPCSTR lpsztmp;
+    INT srclen;
+
+    srclen = 0;
+
+    while (*lpszSrc == ' ' && *lpszSrc != '\0')
+	lpszSrc++;
+	
+    lpsztmp = lpszSrc;
+    while(*lpsztmp != '\0')
+    {
+        if (*lpsztmp != ' ')
+	    srclen = lpsztmp - lpszSrc + 1;
+
+	lpsztmp++;
+    }
+
+    *len = min(*len, srclen);
+    strncpy(lpszStart, lpszSrc, *len);
+    lpszStart[*len] = '\0';
+
+    return *len;
+}
+
+
+BOOL HTTP_InterpretHttpHeader(LPSTR buffer, LPSTR field, INT fieldlen, LPSTR value, INT valuelen)
+{
+    CHAR *pd;
+    BOOL bSuccess = FALSE;
+
+    TRACE("\n");
+
+    *field = '\0';
+    *value = '\0';
+
+    pd = strchr(buffer, ':');
+    if (pd)
+    {
+	*pd = '\0';
+	if (stripSpaces(buffer, field, &fieldlen) > 0)
+	{
+	    if (stripSpaces(pd+1, value, &valuelen) > 0)
+		bSuccess = TRUE;
+	}
+    }
+
+    TRACE("%d: field(%s) Value(%s)\n", bSuccess, field, value);
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HTTP_GetStdHeaderIndex (internal)
+ *
+ * Lookup field index in stadard http header array
+ *
+ * FIXME: This should be stuffed into a hash table
+ */
+INT HTTP_GetStdHeaderIndex(LPCSTR lpszField)
+{
+    INT index = -1;
+
+    if (!_stricmp(lpszField, "Content-Length"))
+        index = HTTP_QUERY_CONTENT_LENGTH;
+    else if (!_stricmp(lpszField,"Status"))
+        index = HTTP_QUERY_STATUS_CODE;
+    else if (!_stricmp(lpszField,"Content-Type"))
+        index = HTTP_QUERY_CONTENT_TYPE;
+    else if (!_stricmp(lpszField,"Last-Modified"))
+        index = HTTP_QUERY_LAST_MODIFIED;
+    else if (!_stricmp(lpszField,"Location"))
+        index = HTTP_QUERY_LOCATION;
+    else if (!_stricmp(lpszField,"Accept"))
+        index = HTTP_QUERY_ACCEPT;
+    else if (!_stricmp(lpszField,"Referer"))
+        index = HTTP_QUERY_REFERER;
+    else if (!_stricmp(lpszField,"Content-Transfer-Encoding"))
+        index = HTTP_QUERY_CONTENT_TRANSFER_ENCODING;
+    else
+    {
+       FIXME("Couldn't find %s in standard header table\n", lpszField);
+    }
+
+    return index;
+}
+
+
+/***********************************************************************
+ *           HTTP_ProcessHeader (internal)
+ *
+ * Stuff header into header tables according to <dwModifier>
+ *
+ */
+
+#define COALESCEFLASG (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
+
+BOOL HTTP_ProcessHeader(LPWININETHTTPREQA lpwhr, LPCSTR field, LPCSTR value, DWORD dwModifier)
+{
+    LPHTTPHEADERA lphttpHdr = NULL;
+    BOOL bSuccess = FALSE;
+    INT index;
+
+    TRACE("%s:%s - 0x%08x\n", field, value, (unsigned int)dwModifier);
+
+    /* Adjust modifier flags */
+    if (dwModifier & COALESCEFLASG)
+	dwModifier |= HTTP_ADDHDR_FLAG_ADD;
+
+    /* Try to get index into standard header array */
+    index = HTTP_GetStdHeaderIndex(field);
+    if (index >= 0)
+    {
+        lphttpHdr = &lpwhr->StdHeaders[index];
+    }
+    else /* Find or create new custom header */
+    {
+	index = HTTP_GetCustomHeaderIndex(lpwhr, field);
+	if (index >= 0)
+	{
+	    if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
+	    {
+	        return FALSE;
+	    }
+	    lphttpHdr = &lpwhr->pCustHeaders[index];
+	}
+	else
+	{
+	    HTTPHEADERA hdr;
+
+	    hdr.lpszField = field;
+	    hdr.lpszValue = value;
+	    hdr.wFlags = hdr.wCount = 0;
+
+            if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
+                hdr.wFlags |= HDR_ISREQUEST;
+
+	    index = HTTP_InsertCustomHeader(lpwhr, &hdr);
+	    return index >= 0;
+	}
+    }
+ 
+    if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
+	lphttpHdr->wFlags |= HDR_ISREQUEST;
+    else
+        lphttpHdr->wFlags &= ~HDR_ISREQUEST;
+  
+    if (!lphttpHdr->lpszValue && (dwModifier & (HTTP_ADDHDR_FLAG_ADD|HTTP_ADDHDR_FLAG_ADD_IF_NEW)))
+    {
+        INT slen;
+
+        if (!lpwhr->StdHeaders[index].lpszField)
+        {
+            lphttpHdr->lpszField = HEAP_strdupA(GetProcessHeap(), 0, field);
+
+            if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
+                lphttpHdr->wFlags |= HDR_ISREQUEST;
+        }
+
+        slen = strlen(value) + 1;
+        lphttpHdr->lpszValue = HeapAlloc(GetProcessHeap(), 0, slen);
+        if (lphttpHdr->lpszValue)
+        {
+            memcpy(lphttpHdr->lpszValue, value, slen);
+            bSuccess = TRUE;
+        }
+        else
+        {
+            INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        }
+    }
+    else if (lphttpHdr->lpszValue) 
+    {
+        if (dwModifier & HTTP_ADDHDR_FLAG_REPLACE) 
+        {
+            LPSTR lpsztmp;
+            INT len;
+
+            len = strlen(value);
+
+            if (len <= 0)
+            {
+		//! if custom header delete from array
+                HeapFree(GetProcessHeap(), 0, lphttpHdr->lpszValue);
+                lphttpHdr->lpszValue = NULL;
+                bSuccess = TRUE;
+            }
+            else
+            {
+                lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
+                if (lpsztmp)
+                {
+                    lphttpHdr->lpszValue = lpsztmp;
+                    strcpy(lpsztmp, value);
+                    bSuccess = TRUE;
+                }
+                else
+                {
+                    INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+                }
+            }
+        }
+        else if (dwModifier & COALESCEFLASG)
+        {
+            LPSTR lpsztmp;
+            CHAR ch = 0;
+            INT len = 0;
+            INT origlen = strlen(lphttpHdr->lpszValue);
+            INT valuelen = strlen(value);
+
+            if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA)
+            {
+                ch = ',';
+                lphttpHdr->wFlags |= HDR_COMMADELIMITED;
+            }
+            else if (dwModifier & HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
+            {
+                ch = ';';
+                lphttpHdr->wFlags |= HDR_COMMADELIMITED;
+            }
+
+            len = origlen + valuelen + (ch > 0) ? 1 : 0;
+
+            lpsztmp = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,  lphttpHdr->lpszValue, len+1);
+            if (lpsztmp)
+            {
+		/* FIXME: Increment lphttpHdr->wCount. Perhaps lpszValue should be an array */ 
+                if (ch > 0)
+                {
+                    lphttpHdr->lpszValue[origlen] = ch;
+                    origlen++;
+                }
+
+                memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen);
+                lphttpHdr->lpszValue[len] = '\0';
+                bSuccess = TRUE;
+            }
+            else
+            {
+                INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+            }
+        }
+    }
+
+    return bSuccess;
+}
+
+
+/***********************************************************************
+ *           HTTP_CloseConnection (internal)
+ *
+ * Close socket connection
+ *
+ */
+VOID HTTP_CloseConnection(LPWININETHTTPREQA lpwhr)
+{
+	if (lpwhr->nSocketFD != INVALID_SOCKET)
+	{
+		close(lpwhr->nSocketFD);
+		lpwhr->nSocketFD = INVALID_SOCKET;
+	}
+}
+
+
+/***********************************************************************
+ *           HTTP_CloseHTTPRequestHandle (internal)
+ *
+ * Deallocate request handle
+ *
+ */
+void HTTP_CloseHTTPRequestHandle(LPWININETHTTPREQA lpwhr)
+{
+    int i;
+
+    TRACE("\n");
+
+    if (lpwhr->nSocketFD != INVALID_SOCKET)
+        HTTP_CloseConnection(lpwhr);
+
+    if (lpwhr->lpszPath)
+        HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
+    if (lpwhr->lpszVerb)
+        HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+    if (lpwhr->lpszHostName)
+        HeapFree(GetProcessHeap(), 0, lpwhr->lpszHostName);
+
+    for (i = 0; i <= HTTP_QUERY_MAX; i++)
+    {
+	   if (lpwhr->StdHeaders[i].lpszField)
+            HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszField);
+	   if (lpwhr->StdHeaders[i].lpszValue)
+            HeapFree(GetProcessHeap(), 0, lpwhr->StdHeaders[i].lpszValue);
+    }
+
+    for (i = 0; i < lpwhr->nCustHeaders; i++)
+    {
+	   if (lpwhr->pCustHeaders[i].lpszField)
+            HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszField);
+	   if (lpwhr->pCustHeaders[i].lpszValue)
+            HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
+    }
+
+    HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
+    HeapFree(GetProcessHeap(), 0, lpwhr);
+}
+
+
+/***********************************************************************
+ *           HTTP_CloseHTTPSessionHandle (internal)
+ *
+ * Deallocate session handle
+ *
+ */
+void HTTP_CloseHTTPSessionHandle(LPWININETHTTPSESSIONA lpwhs)
+{
+    TRACE("\n");
+
+    if (lpwhs->lpszServerName)
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
+    if (lpwhs->lpszUserName)
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
+    HeapFree(GetProcessHeap(), 0, lpwhs);
+}
+
+
+/***********************************************************************
+ *           HTTP_GetCustomHeaderIndex (internal)
+ *
+ * Return index of custom header from header array
+ *
+ */
+INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQA lpwhr, LPCSTR lpszField)
+{
+    INT index;
+
+    TRACE("%s\n", lpszField);
+
+    for (index = 0; index < lpwhr->nCustHeaders; index++)
+    {
+	if (!_stricmp(lpwhr->pCustHeaders[index].lpszField, lpszField))
+	    break;
+
+    }
+
+    if (index >= lpwhr->nCustHeaders)
+	index = -1;
+
+    TRACE("Return: %d\n", index);
+    return index;
+}
+
+
+/***********************************************************************
+ *           HTTP_InsertCustomHeader (internal)
+ *
+ * Insert header into array
+ *
+ */
+INT HTTP_InsertCustomHeader(LPWININETHTTPREQA lpwhr, LPHTTPHEADERA lpHdr)
+{
+    INT count;
+    LPHTTPHEADERA lph = NULL;
+
+    TRACE("%s: %s\n", lpHdr->lpszField, lpHdr->lpszValue);
+    count = lpwhr->nCustHeaders + 1;
+    if (count > 1)
+	lph = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, lpwhr->pCustHeaders, sizeof(HTTPHEADERA) * count);
+    else
+	lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERA) * count);
+
+    if (NULL != lph)
+    {
+	lpwhr->pCustHeaders = lph;
+        lpwhr->pCustHeaders[count-1].lpszField = HEAP_strdupA(GetProcessHeap(), 0, lpHdr->lpszField);
+        lpwhr->pCustHeaders[count-1].lpszValue = HEAP_strdupA(GetProcessHeap(), 0, lpHdr->lpszValue);
+        lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
+        lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
+	lpwhr->nCustHeaders++;
+    }
+    else
+    {
+        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+	count = 0;
+    }
+
+    TRACE("%d <--\n", count-1);
+    return count - 1;
+}
+
+
+/***********************************************************************
+ *           HTTP_DeleteCustomHeader (internal)
+ *
+ * Delete header from array
+ *
+ */
+BOOL HTTP_DeleteCustomHeader(INT index)
+{
+    TRACE("\n");
+    return FALSE;
+}
diff --git a/dlls/wininet/internet.c b/dlls/wininet/internet.c
index 16e5dbe..2b2f8c0 100644
--- a/dlls/wininet/internet.c
+++ b/dlls/wininet/internet.c
@@ -15,12 +15,15 @@
 # include <sys/socket.h>
 #endif
 #include <stdlib.h>
+#include <ctype.h>
 
 #include "windows.h"
 #include "wininet.h"
 #include "debugtools.h"
 #include "winerror.h"
 #include "winsock.h"
+#include "tchar.h"
+#include "heap.h"
 
 #include "internet.h"
 
@@ -28,6 +31,7 @@
 
 #define MAX_IDLE_WORKER 1000*60*1
 #define MAX_WORKER_THREADS 10
+#define RESPONSE_TIMEOUT        30
 
 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
 (LPWININETAPPINFOA)(((LPWININETFTPSESSIONA)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
@@ -38,7 +42,7 @@
     CHAR   response[MAX_REPLY_LEN];
 } WITHREADERROR, *LPWITHREADERROR;
 
-INTERNET_SCHEME GetInternetScheme(LPSTR lpszScheme);
+INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp);
 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData);
 VOID INTERNET_ExecuteWork();
 
@@ -97,7 +101,11 @@
 
         case DLL_THREAD_DETACH:
 	    if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
-	        HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
+			{
+				LPVOID lpwite = TlsGetValue(g_dwTlsErrIndex);
+				if (lpwite)
+                   HeapFree(GetProcessHeap(), 0, lpwite);
+			}
 	    break;
 
         case DLL_PROCESS_DETACH:
@@ -112,7 +120,6 @@
 
 	    CloseHandle(hQuitEvent);
 	    CloseHandle(hWorkEvent);
-
 	    DeleteCriticalSection(&csQueue);
             break;
     }
@@ -152,11 +159,11 @@
         lpwai->hdr.lpwhparent = NULL;
         lpwai->hdr.dwFlags = dwFlags;
         if (NULL != lpszAgent)
-            lpwai->lpszAgent = strdup(lpszAgent);
+            lpwai->lpszAgent = HEAP_strdupA(GetProcessHeap(),0,lpszAgent);
         if (NULL != lpszProxy)
-            lpwai->lpszProxy = strdup(lpszProxy);
+            lpwai->lpszProxy = HEAP_strdupA(GetProcessHeap(),0,lpszProxy);
         if (NULL != lpszProxyBypass)
-            lpwai->lpszProxyBypass = strdup(lpszProxyBypass);
+            lpwai->lpszProxyBypass = HEAP_strdupA(GetProcessHeap(),0,lpszProxyBypass);
         lpwai->dwAccessType = dwAccessType;
     }
 
@@ -242,6 +249,8 @@
             break;
 
         case INTERNET_SERVICE_HTTP:
+	    rc = HTTP_Connect(hInternet, lpszServerName, nServerPort,
+            lpszUserName, lpszPassword, dwFlags, dwContext);
             break;
 
         case INTERNET_SERVICE_GOPHER:
@@ -363,9 +372,33 @@
 
 
 /***********************************************************************
+ *           INTERNET_CloseHandle (internal)
+ *
+ * Close internet handle
+ *
+ * RETURNS
+ *    Void
+ *
+ */
+VOID INTERNET_CloseHandle(LPWININETAPPINFOA lpwai)
+{
+    if (lpwai->lpszAgent)
+        HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
+
+    if (lpwai->lpszProxy)
+        HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
+
+    if (lpwai->lpszProxyBypass)
+        HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
+
+    HeapFree(GetProcessHeap(), 0, lpwai);
+}
+
+
+/***********************************************************************
  *           InternetCloseHandle (WININET.89)
  *
- * Continues a file search from a previous call to FindFirstFile
+ * Generic close handle function
  *
  * RETURNS
  *    TRUE on success
@@ -387,9 +420,17 @@
     switch (lpwh->htype)
     {
         case WH_HINIT:
+            INTERNET_CloseHandle((LPWININETAPPINFOA) lpwh);
+            break; 
+
         case WH_HHTTPSESSION:
+	    HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
+	    break;
+
         case WH_HHTTPREQ:
+            HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
             break;
+
         case WH_HFTPSESSION:
             retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
             break;
@@ -407,10 +448,46 @@
 
 
 /***********************************************************************
+ *           SetUrlComponentValue (Internal)
+ *
+ * Helper function for InternetCrackUrlA
+ *
+ * RETURNS
+ *    TRUE on success
+ *    FALSE on failure
+ *
+ */
+BOOL SetUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen, LPCSTR lpszStart, INT len)
+{
+    TRACE("%s (%d)\n", lpszStart, len);
+
+    if (*dwComponentLen != 0)
+    {
+	if (*lppszComponent == NULL)
+	{
+            *lppszComponent = lpszStart;
+	    *dwComponentLen = len;
+	}
+	else
+	{
+            INT ncpylen = min((*dwComponentLen)-1, len);
+            strncpy(*lppszComponent, lpszStart, ncpylen);
+            (*lppszComponent)[ncpylen] = '\0';
+	    *dwComponentLen = ncpylen;
+	}
+    }
+
+    return TRUE;
+}
+
+
+/***********************************************************************
  *           InternetCrackUrlA (WININET.95)
  *
  * Break up URL into its components
  *
+ * TODO: Hadnle dwFlags
+ *
  * RETURNS
  *    TRUE on success
  *    FALSE on failure
@@ -424,165 +501,162 @@
    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
    *
    */
-    char* szScheme   = NULL;
-    char* szUser     = NULL;
-    char* szPass     = NULL;
-    char* szHost     = NULL;
-    char* szUrlPath  = NULL;
-    char* szParam    = NULL;
-    char* szNetLoc   = NULL;
-    int   nPort      = 80;
-    int   nSchemeLen = 0;
-    int   nUserLen   = 0;
-    int   nPassLen   = 0;
-    int   nHostLen   = 0;
-    int   nUrlLen    = 0;
-
-    /* Find out if the URI is absolute... */
+    LPSTR lpszParam    = NULL;
     BOOL  bIsAbsolute = FALSE;
-    char  cAlphanum;
-    char* ap = (char*)lpszUrl;
-    char* cp = NULL;
+    LPSTR lpszap = (char*)lpszUrl;
+    LPSTR lpszcp = NULL;
 
     TRACE("\n");
-    while( (cAlphanum = *ap) != '\0' )
+
+    /* Determine if the URI is absolute. */
+    while (*lpszap != '\0')
     {
-        if( ((cAlphanum >= 'a') && (cAlphanum <= 'z')) ||
-            ((cAlphanum >= 'A') && (cAlphanum <= 'Z')) ||
-            ((cAlphanum >= '0') && (cAlphanum <= '9')) )
+        if (isalnum(*lpszap))
         {
-            ap++;
+            lpszap++;
             continue;
         }
-        if( (cAlphanum == ':') && (ap - lpszUrl >= 2) )
+        if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
         {
             bIsAbsolute = TRUE;
-            cp = ap;
-            break;
+            lpszcp = lpszap;
         }
+	else
+	{
+	    lpszcp = lpszUrl; /* Relative url */
+        }
+
         break;
     }
 
-    /* Absolute URI...
-       FIXME!!!! This should work on relative urls too!*/
-    if( bIsAbsolute )
+    /* Parse <params> */
+    lpszParam = strpbrk(lpszap, ";?");
+    if (lpszParam != NULL)
     {
-        /* Get scheme first... */
-        nSchemeLen = cp - lpszUrl;
-        szScheme   = strdup( lpszUrl );
-        szScheme[ nSchemeLen ] = '\0';
-
-        /* Eat ':' in protocol... */
-        cp++;
-
-        /* Parse <params>... */
-        szParam = strpbrk( lpszUrl, ";" );
-        if( szParam != NULL )
+        if (!SetUrlComponentValue(&lpUrlComponents->lpszExtraInfo, 
+	     &lpUrlComponents->dwExtraInfoLength, lpszParam+1, strlen(lpszParam+1)))
         {
-            char* sParam;
-            /* Eat ';' in Params... */
-            szParam++;
-            sParam    = strdup( szParam );
-            *szParam = '\0';
+	    return FALSE;
+        }
         }
 
-        /* Skip over slashes...*/
-        if( *cp == '/' )
+    if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
         {
-            cp++;
-            if( *cp == '/' )
+	LPSTR lpszNetLoc;
+
+        /* Get scheme first. */
+        lpUrlComponents->nScheme = GetInternetScheme(lpszUrl, lpszcp - lpszUrl);
+        if (!SetUrlComponentValue(&lpUrlComponents->lpszScheme, 
+		    &lpUrlComponents->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
+	    return FALSE;
+
+        /* Eat ':' in protocol. */
+        lpszcp++;
+
+        /* Skip over slashes. */
+        if (*lpszcp == '/')
+        {
+            lpszcp++;
+            if (*lpszcp == '/')
             {
-                cp++;
-                if( *cp == '/' )
-                    cp++;
+                lpszcp++;
+                if (*lpszcp == '/')
+                    lpszcp++;
             }
         }
 
-        /* Parse the <net-loc>...*/
-        if( GetInternetScheme( szScheme ) == INTERNET_SCHEME_FILE )
+        lpszNetLoc = strpbrk(lpszcp, "/");
+	if (lpszParam)
         {
-            szUrlPath = strdup( cp );
-            nUrlLen   = strlen( szUrlPath );
-            if( nUrlLen >= 2 && szUrlPath[ 1 ] == '|' )
-                szUrlPath[ 1 ] = ':';
-        }
+	    if (lpszNetLoc)
+               lpszNetLoc = min(lpszNetLoc, lpszParam);
         else
+               lpszNetLoc = lpszParam;
+        }
+        else if (!lpszNetLoc)
+            lpszNetLoc = lpszcp + strlen(lpszcp);
+
+        /* Parse net-loc */
+        if (lpszNetLoc)
         {
-            size_t nNetLocLen;
-            szUrlPath = strpbrk(cp, "/");
-            if( szUrlPath != NULL )
-                nUrlLen = strlen( szUrlPath );
+	    LPSTR lpszHost;
+	    LPSTR lpszPort;
 
-            /* Find the end of our net-loc... */
-            nNetLocLen = strcspn( cp, "/" );
-            szNetLoc   = strdup( cp );
-            szNetLoc[ nNetLocLen ] = '\0';
-            if( szNetLoc != NULL )
-            {
-                char* lpszPort;
-                int   nPortLen;
                 /* [<user>[<:password>]@]<host>[:<port>] */
-                /* First find the user and password if they exist...*/
+            /* First find the user and password if they exist */
 			
-                szHost = strchr( szNetLoc, '@' );
-                if( szHost == NULL )
+            lpszHost = strchr(lpszcp, '@');
+            if (lpszHost == NULL || lpszHost > lpszNetLoc)
                 {
-                    /* username and password not specified... */
-                    szHost   = szNetLoc;
-                    nHostLen = nNetLocLen;
+                /* username and password not specified. */
+		SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
+			&lpUrlComponents->dwUserNameLength, NULL, 0);
+		SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
+			&lpUrlComponents->dwPasswordLength, NULL, 0);
                 }
-                else
+            else /* Parse out username and password */
                 {
-                    int   nUserPassLen = nNetLocLen - nHostLen - 1;
-		    char* szUserPass         = strdup( szNetLoc );
-		    /* Get username and/or password... */
-		    /* Eat '@' in domain... */
-                    ++szHost;
-                    nHostLen = strlen( szHost );
+		LPSTR lpszUser = lpszcp;
+		LPSTR lpszPasswd = lpszHost;
 
-                    szUserPass[ nUserPassLen ] = '\0';
-                    if( szUserPass != NULL )
-                    {
-		        szPass = strpbrk( szUserPass, ":" );
-                        if( szPass != NULL )
+		while (lpszcp < lpszHost)
                         {
-                            /* Eat ':' in UserPass... */
-                            ++szPass;
-                            nPassLen = strlen( szPass );
-                            nUserLen = nUserPassLen - nPassLen - 1;
-                            szUser   = strdup( szUserPass );
-                            szUser[ nUserLen ] = '\0';
-                        }
-                        else
-                        {
-                            /* password not specified... */
-                            szUser = strdup( szUserPass );
-                            nUserLen = strlen( szUser );
-                        }
+		   if (*lpszcp == ':')
+		       lpszPasswd = lpszcp;
+
+		   lpszcp++;
                     }        
+		    
+		SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
+			&lpUrlComponents->dwUserNameLength, lpszUser, lpszPasswd - lpszUser);
+
+		SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
+			&lpUrlComponents->dwPasswordLength, 
+			lpszPasswd == lpszHost ? NULL : ++lpszPasswd, 
+			lpszHost - lpszPasswd);
+
+		lpszcp++; /* Advance to beginning of host */
                 }
 
-                /* <host><:port>...*/
-                /* Then get the port if it exists... */
-                lpszPort = strpbrk( szHost, ":" );
-                nPortLen = 0;
-                if( lpszPort != NULL )
-                {
-                    char* szPort = lpszPort + 1;
-		    if( szPort != NULL )
+            /* Parse <host><:port> */
+
+	    lpszHost = lpszcp;
+	    lpszPort = lpszNetLoc;
+
+	    while (lpszcp < lpszNetLoc)
 		    {
-                        nPortLen = strlen( szPort );
-                        nPort    = atoi( szPort );
-                    }
-                    *lpszPort = '\0';
-                    nHostLen = strlen(szHost);
+		if (*lpszcp == ':')
+                    lpszPort = lpszcp;
+
+		lpszcp++;
                 }
+
+            SetUrlComponentValue(&lpUrlComponents->lpszHostName, 
+               &lpUrlComponents->dwHostNameLength, lpszHost, lpszPort - lpszHost);
+
+	    if (lpszPort != lpszNetLoc)
+                lpUrlComponents->nPort = atoi(++lpszPort);
             }
         }
+
+    /* Here lpszcp points to:
+     *
+     * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
+     *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+     */
+    if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
+    {
+        if (!SetUrlComponentValue(&lpUrlComponents->lpszUrlPath, 
+         &lpUrlComponents->dwUrlPathLength, lpszcp, 
+	 lpszParam ? lpszParam - lpszcp : strlen(lpszcp)))
+         return FALSE;
     }
-    /* Relative URI... */
     else
-        return FALSE;
+    {
+        lpUrlComponents->dwUrlPathLength = 0;
+    }
+
+    TRACE("%s: host(%s) path(%s)\n", lpszUrl, lpUrlComponents->lpszHostName, lpUrlComponents->lpszUrlPath);
 
     return TRUE;
 }
@@ -758,6 +832,60 @@
 
 
 /***********************************************************************
+ *           InternetQueryOptionA
+ *
+ * Queries an options on the specified handle
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption, 
+	LPVOID lpBuffer, LPDWORD lpdwBufferLength)
+{
+    LPWININETHANDLEHEADER lpwhh;
+    BOOL bSuccess = FALSE;
+
+    TRACE("0x%08lx\n", dwOption);
+
+    if (NULL == hInternet)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+	return FALSE;
+    }
+
+    lpwhh = (LPWININETHANDLEHEADER) hInternet;
+
+    switch (dwOption)
+    {
+       case INTERNET_OPTION_HANDLE_TYPE:
+       {
+         ULONG type = lpwhh->htype;
+	 TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
+
+         if (*lpdwBufferLength < sizeof(ULONG))
+             INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
+	 else
+	 {
+	     memcpy(lpBuffer, &type, sizeof(ULONG));
+             *lpdwBufferLength = sizeof(ULONG);
+             bSuccess = TRUE;
+         }
+
+         break;
+       }
+
+       default:
+         FIXME("Stub!");
+         break;
+    }
+
+    return bSuccess;
+}
+
+
+/***********************************************************************
  *           GetInternetScheme (internal)
  *
  * Get scheme of url
@@ -767,31 +895,24 @@
  *    INTERNET_SCHEME_UNKNOWN on failure
  *
  */
-INTERNET_SCHEME GetInternetScheme(LPSTR lpszScheme)
+INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp)
 {
     if(lpszScheme==NULL)
         return INTERNET_SCHEME_UNKNOWN;
 
-    if( (strcmp("ftp", lpszScheme) == 0) ||
-        (strcmp("FTP", lpszScheme) == 0) )
+    if (!_strnicmp("ftp", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_FTP;
-    else if( (strcmp("gopher", lpszScheme) == 0) ||
-        (strcmp("GOPHER", lpszScheme) == 0) )
+    else if (!_strnicmp("gopher", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_GOPHER;
-    else if( (strcmp("http", lpszScheme) == 0) ||
-        (strcmp("HTTP", lpszScheme) == 0) )
+    else if (!_strnicmp("http", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_HTTP;
-    else if( (strcmp("https", lpszScheme) == 0) ||
-        (strcmp("HTTPS", lpszScheme) == 0) )
+    else if (!_strnicmp("https", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_HTTPS;
-    else if( (strcmp("file", lpszScheme) == 0) ||
-        (strcmp("FILE", lpszScheme) == 0) )
+    else if (!_strnicmp("file", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_FILE;
-    else if( (strcmp("news", lpszScheme) == 0) ||
-        (strcmp("NEWS", lpszScheme) == 0) )
+    else if (!_strnicmp("news", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_NEWS;
-    else if( (strcmp("mailto", lpszScheme) == 0) ||
-        (strcmp("MAILTO", lpszScheme) == 0) )
+    else if (!_strnicmp("mailto", lpszScheme, nMaxCmp))
         return INTERNET_SCHEME_MAILTO;
     else
         return INTERNET_SCHEME_UNKNOWN;
@@ -1111,6 +1232,31 @@
 		INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
 		break;
+
+            case HTTPSENDREQUESTA:
+               HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION, 
+                       (LPCSTR)workRequest.LPSZHEADER,
+                       workRequest.DWHEADERLENGTH,     
+                       (LPVOID)workRequest.LPOPTIONAL, 
+                       workRequest.DWOPTIONALLENGTH);
+               HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
+               break;
+
+            case HTTPOPENREQUESTA:
+               HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION, 
+                       (LPCSTR)workRequest.LPSZVERB,
+                       (LPCSTR)workRequest.LPSZOBJECTNAME,     
+                       (LPCSTR)workRequest.LPSZVERSION, 
+                       (LPCSTR)workRequest.LPSZREFERRER, 
+                       (LPCSTR*)workRequest.LPSZACCEPTTYPES, 
+                       workRequest.DWFLAGS,
+                       workRequest.DWCONTEXT);
+               HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
+               HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
+               HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
+               HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
+                break;
+
 	}
     }
 }
@@ -1127,3 +1273,69 @@
     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
     return lpwite->response;
 }
+
+
+/***********************************************************************
+ *           INTERNET_GetNextLine  (internal)
+ *
+ * Parse next line in directory string listing
+ *
+ * RETURNS
+ *   Pointer to begining of next line
+ *   NULL on failure
+ *
+ */
+
+LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
+{
+    struct timeval tv;
+    fd_set infd;
+    BOOL bSuccess = FALSE;
+    INT nRecv = 0;
+
+    TRACE("\n");
+
+    FD_ZERO(&infd);
+    FD_SET(nSocket, &infd);
+    tv.tv_sec=RESPONSE_TIMEOUT;
+    tv.tv_usec=0;
+
+    while (nRecv < *dwBuffer)
+    {
+        if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
+        {
+            if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
+            {
+                INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
+                goto lend;
+            }
+
+            if (lpszBuffer[nRecv] == '\n')
+	    {
+		bSuccess = TRUE;
+                break;
+	    }
+            if (lpszBuffer[nRecv] != '\r')
+                nRecv++;
+        }
+	else
+	{
+            INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
+            goto lend;
+}
+    }
+
+lend:
+    if (bSuccess)
+    {
+        lpszBuffer[nRecv] = '\0';
+	*dwBuffer = nRecv - 1;
+        TRACE(":%d %s\n", nRecv, lpszBuffer);
+        return lpszBuffer;
+    }
+    else
+    {
+        return NULL;
+    }
+}
+
diff --git a/dlls/wininet/internet.h b/dlls/wininet/internet.h
index feb7fbf..b6cd68a 100644
--- a/dlls/wininet/internet.h
+++ b/dlls/wininet/internet.h
@@ -3,13 +3,13 @@
 
 typedef enum
 {
-    WH_HINIT,
-    WH_HFTPSESSION,
-    WH_HGOPHERSESSION,
-    WH_HHTTPSESSION,
-    WH_HHTTPREQ,
-    WH_HFILE,
-    WH_HFINDNEXT,
+    WH_HINIT = INTERNET_HANDLE_TYPE_INTERNET,
+    WH_HFTPSESSION = INTERNET_HANDLE_TYPE_CONNECT_FTP,
+    WH_HGOPHERSESSION = INTERNET_HANDLE_TYPE_CONNECT_GOPHER,
+    WH_HHTTPSESSION = INTERNET_HANDLE_TYPE_CONNECT_HTTP,
+    WH_HFILE = INTERNET_HANDLE_TYPE_FTP_FILE,
+    WH_HFINDNEXT = INTERNET_HANDLE_TYPE_FTP_FIND,
+    WH_HHTTPREQ = INTERNET_HANDLE_TYPE_HTTP_REQUEST,
 } WH_TYPE;
 
 typedef struct _WININETHANDLEHEADER
@@ -43,20 +43,29 @@
     struct hostent *phostent;
 } WININETHTTPSESSIONA, *LPWININETHTTPSESSIONA;
 
+#define HDR_ISREQUEST		0x0001
+#define HDR_COMMADELIMITED	0x0002
+#define HDR_SEMIDELIMITED	0x0004
+
+typedef struct
+{
+    LPSTR lpszField;
+    LPSTR lpszValue;
+    WORD wFlags;
+    WORD wCount;
+} HTTPHEADERA, *LPHTTPHEADERA;
+
 
 typedef struct
 {
     WININETHANDLEHEADER hdr;
     LPSTR lpszPath;
-    LPSTR lpszReferrer;
-    LPSTR lpszAcceptTypes;
     LPSTR lpszVerb;
     LPSTR lpszHostName;
-    LPSTR lpszRedirect;
-    int	nSocketFD;
-    int	statusCode;
-    int	contentLength;
-    time_t  nSystemTime;
+    INT	nSocketFD;
+    HTTPHEADERA StdHeaders[HTTP_QUERY_MAX+1];
+    HTTPHEADERA *pCustHeaders;
+    INT nCustHeaders;
 } WININETHTTPREQA, *LPWININETHTTPREQA;
 
 
@@ -111,6 +120,8 @@
     FTPREMOVEDIRECTORYA,
     FTPRENAMEFILEA,
     INTERNETFINDNEXTA,
+    HTTPSENDREQUESTA,
+    HTTPOPENREQUESTA,
 } ASYNC_FUNC;
 
 typedef struct WORKREQ
@@ -126,6 +137,8 @@
 #define LPSZSRCFILE       param2
 #define LPSZDIRECTORY     param2
 #define LPSZSEARCHFILE    param2
+#define LPSZHEADER        param2
+#define LPSZVERB          param2
 
     DWORD param3;
 #define LPSZNEWREMOTEFILE param3
@@ -134,18 +147,27 @@
 #define LPDWDIRECTORY     param3
 #define FDWACCESS         param3
 #define LPSZDESTFILE      param3
+#define DWHEADERLENGTH    param3
+#define LPSZOBJECTNAME    param3
 
     DWORD param4;
 #define DWFLAGS           param4
+#define LPOPTIONAL        param4
 
     DWORD param5;
 #define DWCONTEXT         param5
+#define DWOPTIONALLENGTH  param5
 
     DWORD param6;
-#define FFAILIFEXISTS     param4
+#define FFAILIFEXISTS     param6
+#define LPSZVERSION       param6
 
     DWORD param7;
 #define DWLOCALFLAGSATTRIBUTE param7
+#define LPSZREFERRER          param7
+
+    DWORD param8;
+#define LPSZACCEPTTYPES       param8
 
     struct WORKREQ *next;
     struct WORKREQ *prev;
@@ -159,6 +181,10 @@
 	INTERNET_PORT nServerPort, LPCSTR lpszUserName,
 	LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext);
 
+HINTERNET HTTP_Connect(HINTERNET hInterent, LPCSTR lpszServerName, 
+	INTERNET_PORT nServerPort, LPCSTR lpszUserName,
+	LPCSTR lpszPassword, DWORD dwFlags, DWORD dwContext);
+
 BOOL GetAddress(LPCSTR lpszServerName, INTERNET_PORT nServerPort,
 	struct hostent **phe, struct sockaddr_in *psa);
 
@@ -168,6 +194,7 @@
 DWORD INTERNET_GetLastError();
 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest);
 LPSTR INTERNET_GetResponseBuffer();
+LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer);
 
 BOOL FTP_CloseSessionHandle(LPWININETFTPSESSIONA lpwfs);
 BOOL FTP_CloseFindNextHandle(LPWININETFINDNEXTA lpwfn);
@@ -189,6 +216,15 @@
 	BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
 	DWORD dwContext);
 
+BOOLAPI HTTP_HttpSendRequestA(HINTERNET hHttpRequest, LPCSTR lpszHeaders,
+	DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength);
+INTERNETAPI HINTERNET WINAPI HTTP_HttpOpenRequestA(HINTERNET hHttpSession,
+	LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
+	LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes, 
+	DWORD dwFlags, DWORD dwContext);
+void HTTP_CloseHTTPSessionHandle(LPWININETHTTPSESSIONA lpwhs);
+void HTTP_CloseHTTPRequestHandle(LPWININETHTTPREQA lpwhr);
+
 
 #define MAX_REPLY_LEN	 	0x5B4
 
diff --git a/dlls/wininet/utility.c b/dlls/wininet/utility.c
index a09b66f..21e050c 100644
--- a/dlls/wininet/utility.c
+++ b/dlls/wininet/utility.c
@@ -111,10 +111,12 @@
 BOOL GetAddress(LPCSTR lpszServerName, INTERNET_PORT nServerPort,
 	struct hostent **phe, struct sockaddr_in *psa)
 {
+    TRACE("%s\n", lpszServerName);
+
     *phe = gethostbyname(lpszServerName);
     if (NULL == *phe)
     {
-        TRACE("Failed to get hostname %s\n", lpszServerName);
+        TRACE("Failed to get hostname: (%s)\n", lpszServerName);
         return FALSE;
     }
 
diff --git a/dlls/wininet/wininet.spec b/dlls/wininet/wininet.spec
index 6ee7e52..3584505 100644
--- a/dlls/wininet/wininet.spec
+++ b/dlls/wininet/wininet.spec
@@ -69,15 +69,15 @@
 @ stub GopherGetLocatorTypeW
 @ stub GopherOpenFileA
 @ stub GopherOpenFileW
-@ stub HttpAddRequestHeadersA
+@ stdcall HttpAddRequestHeadersA(ptr str long long) HttpAddRequestHeadersA
 @ stub HttpAddRequestHeadersW
 @ stub HttpEndRequestA
 @ stub HttpEndRequestW
-@ stub HttpOpenRequestA
+@ stdcall HttpOpenRequestA(ptr str str str str ptr long long) HttpOpenRequestA
 @ stub HttpOpenRequestW
-@ stub HttpQueryInfoA
+@ stdcall HttpQueryInfoA(ptr long ptr ptr ptr) HttpQueryInfoA
 @ stub HttpQueryInfoW
-@ stub HttpSendRequestA
+@ stdcall HttpSendRequestA(ptr str long ptr long) HttpSendRequestA
 @ stub HttpSendRequestExA
 @ stub HttpSendRequestExW
 @ stub HttpSendRequestW
@@ -120,7 +120,7 @@
 @ stub InternetOpenUrlW
 @ stub InternetOpenW
 @ stub InternetQueryDataAvailable
-@ stub InternetQueryOptionA
+@ stdcall InternetQueryOptionA(ptr long ptr ptr) InternetQueryOptionA
 @ stub InternetQueryOptionW
 @ stdcall InternetReadFile(ptr ptr long ptr) InternetReadFile  
 @ stub InternetReadFileExA