[WININET]
authorChristoph von Wittich <christoph_vw@reactos.org>
Mon, 15 Mar 2010 11:35:51 +0000 (11:35 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Mon, 15 Mar 2010 11:35:51 +0000 (11:35 +0000)
sync wininet with wine 1.1.40

svn path=/trunk/; revision=46202

38 files changed:
reactos/dll/win32/wininet/cookie.c
reactos/dll/win32/wininet/dialogs.c
reactos/dll/win32/wininet/ftp.c
reactos/dll/win32/wininet/http.c
reactos/dll/win32/wininet/internet.c
reactos/dll/win32/wininet/internet.h
reactos/dll/win32/wininet/netconnection.c
reactos/dll/win32/wininet/resource.h
reactos/dll/win32/wininet/rsrc.rc
reactos/dll/win32/wininet/urlcache.c
reactos/dll/win32/wininet/utility.c
reactos/dll/win32/wininet/wininet.rbuild
reactos/dll/win32/wininet/wininet.spec
reactos/dll/win32/wininet/wininet_Bg.rc
reactos/dll/win32/wininet/wininet_Cs.rc
reactos/dll/win32/wininet/wininet_Da.rc
reactos/dll/win32/wininet/wininet_De.rc
reactos/dll/win32/wininet/wininet_En.rc
reactos/dll/win32/wininet/wininet_Eo.rc
reactos/dll/win32/wininet/wininet_Es.rc
reactos/dll/win32/wininet/wininet_Fi.rc
reactos/dll/win32/wininet/wininet_Fr.rc
reactos/dll/win32/wininet/wininet_Hu.rc
reactos/dll/win32/wininet/wininet_It.rc
reactos/dll/win32/wininet/wininet_Ja.rc
reactos/dll/win32/wininet/wininet_Ko.rc
reactos/dll/win32/wininet/wininet_Lt.rc [new file with mode: 0644]
reactos/dll/win32/wininet/wininet_Nl.rc
reactos/dll/win32/wininet/wininet_No.rc
reactos/dll/win32/wininet/wininet_Pl.rc
reactos/dll/win32/wininet/wininet_Pt.rc
reactos/dll/win32/wininet/wininet_Ro.rc
reactos/dll/win32/wininet/wininet_Ru.rc
reactos/dll/win32/wininet/wininet_Si.rc
reactos/dll/win32/wininet/wininet_Sv.rc
reactos/dll/win32/wininet/wininet_Tr.rc
reactos/dll/win32/wininet/wininet_Uk.rc
reactos/dll/win32/wininet/wininet_Zh.rc

index 12d841b..75cf049 100644 (file)
 #include "config.h"
 #include "wine/port.h"
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <stdarg.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -48,7 +52,6 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet);
  *     Cookies are currently memory only.
  *     Cookies are NOT THREAD SAFE
  *     Cookies could use A LOT OF MEMORY. We need some kind of memory management here!
- *     Cookies should care about the expiry time
  */
 
 typedef struct _cookie_domain cookie_domain;
@@ -62,7 +65,7 @@ struct _cookie
 
     LPWSTR lpCookieName;
     LPWSTR lpCookieData;
-    time_t expiry; /* FIXME: not used */
+    FILETIME expiry;
 };
 
 struct _cookie_domain
@@ -76,7 +79,7 @@ struct _cookie_domain
 
 static struct list domain_list = LIST_INIT(domain_list);
 
-static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data);
+static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data, FILETIME expiry);
 static cookie *COOKIE_findCookie(cookie_domain *domain, LPCWSTR lpszCookieName);
 static void COOKIE_deleteCookie(cookie *deadCookie, BOOL deleteDomain);
 static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path);
@@ -84,24 +87,16 @@ static void COOKIE_deleteDomain(cookie_domain *deadDomain);
 
 
 /* adds a cookie to the domain */
-static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data)
+static cookie *COOKIE_addCookie(cookie_domain *domain, LPCWSTR name, LPCWSTR data, FILETIME expiry)
 {
     cookie *newCookie = HeapAlloc(GetProcessHeap(), 0, sizeof(cookie));
 
     list_init(&newCookie->entry);
     newCookie->lpCookieName = NULL;
     newCookie->lpCookieData = NULL;
-
-    if (name)
-    {
-       newCookie->lpCookieName = HeapAlloc(GetProcessHeap(), 0, (strlenW(name) + 1)*sizeof(WCHAR));
-        lstrcpyW(newCookie->lpCookieName, name);
-    }
-    if (data)
-    {
-       newCookie->lpCookieData = HeapAlloc(GetProcessHeap(), 0, (strlenW(data) + 1)*sizeof(WCHAR));
-        lstrcpyW(newCookie->lpCookieData, data);
-    }
+    newCookie->expiry = expiry;
+    newCookie->lpCookieName = heap_strdupW(name);
+    newCookie->lpCookieData = heap_strdupW(data);
 
     TRACE("added cookie %p (data is %s)\n", newCookie, debugstr_w(data) );
 
@@ -156,17 +151,8 @@ static cookie_domain *COOKIE_addDomain(LPCWSTR domain, LPCWSTR path)
     list_init(&newDomain->cookie_list);
     newDomain->lpCookieDomain = NULL;
     newDomain->lpCookiePath = NULL;
-
-    if (domain)
-    {
-       newDomain->lpCookieDomain = HeapAlloc(GetProcessHeap(), 0, (strlenW(domain) + 1)*sizeof(WCHAR));
-        strcpyW(newDomain->lpCookieDomain, domain);
-    }
-    if (path)
-    {
-       newDomain->lpCookiePath = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1)*sizeof(WCHAR));
-        lstrcpyW(newDomain->lpCookiePath, path);
-    }
+    newDomain->lpCookieDomain = heap_strdupW(domain);
+    newDomain->lpCookiePath = heap_strdupW(path);
 
     list_add_tail(&domain_list, &newDomain->entry);
 
@@ -191,7 +177,28 @@ static BOOL COOKIE_crackUrlSimple(LPCWSTR lpszUrl, LPWSTR hostName, int hostName
     UrlComponents.dwHostNameLength = hostNameLen;
     UrlComponents.dwUrlPathLength = pathLen;
 
-    return InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents);
+    if (!InternetCrackUrlW(lpszUrl, 0, 0, &UrlComponents)) return FALSE;
+
+    /* discard the webpage off the end of the path */
+    if (UrlComponents.dwUrlPathLength)
+    {
+        if (path[UrlComponents.dwUrlPathLength - 1] != '/')
+        {
+            WCHAR *ptr;
+            if ((ptr = strrchrW(path, '/'))) *(++ptr) = 0;
+            else
+            {
+                path[0] = '/';
+                path[1] = 0;
+            }
+        }
+    }
+    else if (pathLen >= 2)
+    {
+        path[0] = '/';
+        path[1] = 0;
+    }
+    return TRUE;
 }
 
 /* match a domain. domain must match if the domain is not NULL. path must match if the path is not NULL */
@@ -215,11 +222,22 @@ static BOOL COOKIE_matchDomain(LPCWSTR lpszCookieDomain, LPCWSTR lpszCookiePath,
        }
     if (lpszCookiePath)
     {
+        INT len;
         TRACE("comparing paths: %s with %s\n", debugstr_w(lpszCookiePath), debugstr_w(searchDomain->lpCookiePath));
+        /* paths match at the beginning.  so a path of  /foo would match
+         * /foobar and /foo/bar
+         */
         if (!searchDomain->lpCookiePath)
             return FALSE;
-        if (strcmpW(lpszCookiePath, searchDomain->lpCookiePath))
+        if (allow_partial)
+        {
+            len = lstrlenW(searchDomain->lpCookiePath);
+            if (strncmpiW(searchDomain->lpCookiePath, lpszCookiePath, len)!=0)
+                return FALSE;
+        }
+        else if (strcmpW(lpszCookiePath, searchDomain->lpCookiePath))
             return FALSE;
+
        }
        return TRUE;
 }
@@ -262,6 +280,7 @@ BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
     struct list * cursor;
     unsigned int cnt = 0, domain_count = 0, cookie_count = 0;
     WCHAR hostName[2048], path[2048];
+    FILETIME tm;
 
     TRACE("(%s, %s, %p, %p)\n", debugstr_w(lpszUrl),debugstr_w(lpszCookieName),
           lpCookieData, lpdwSize);
@@ -276,10 +295,12 @@ BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
     ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
     if (!ret || !hostName[0]) return FALSE;
 
+    GetSystemTimeAsFileTime(&tm);
+
     LIST_FOR_EACH(cursor, &domain_list)
     {
         cookie_domain *cookiesDomain = LIST_ENTRY(cursor, cookie_domain, entry);
-        if (COOKIE_matchDomain(hostName, NULL /* FIXME: path */, cookiesDomain, TRUE))
+        if (COOKIE_matchDomain(hostName, path, cookiesDomain, TRUE))
         {
             struct list * cursor;
             domain_count++;
@@ -288,6 +309,14 @@ BOOL WINAPI InternetGetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
             LIST_FOR_EACH(cursor, &cookiesDomain->cookie_list)
             {
                 cookie *thisCookie = LIST_ENTRY(cursor, cookie, entry);
+                /* check for expiry */
+                if ((thisCookie->expiry.dwLowDateTime != 0 || thisCookie->expiry.dwHighDateTime != 0) && CompareFileTime(&tm,&thisCookie->expiry)  > 0)
+                {
+                    TRACE("Found expired cookie. deleting\n");
+                    COOKIE_deleteCookie(thisCookie, FALSE);
+                    continue;
+                }
+
                 if (lpCookieData == NULL) /* return the size of the buffer required to lpdwSize */
                 {
                     unsigned int len;
@@ -356,27 +385,16 @@ BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
     LPSTR lpCookieData, LPDWORD lpdwSize)
 {
     DWORD len;
-    LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
+    LPWSTR szCookieData = NULL, url, name;
     BOOL r;
 
     TRACE("(%s,%s,%p)\n", debugstr_a(lpszUrl), debugstr_a(lpszCookieName),
         lpCookieData);
 
-    if( lpszUrl )
-    {
-        len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
-        szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
-    }
+    url = heap_strdupAtoW(lpszUrl);
+    name = heap_strdupAtoW(lpszCookieName);
 
-    if( lpszCookieName )
-    {
-        len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
-        szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
-    }
-
-    r = InternetGetCookieW( szUrl, szCookieName, NULL, &len );
+    r = InternetGetCookieW( url, name, NULL, &len );
     if( r )
     {
         szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
@@ -386,7 +404,7 @@ BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
         }
         else
         {
-            r = InternetGetCookieW( szUrl, szCookieName, szCookieData, &len );
+            r = InternetGetCookieW( url, name, szCookieData, &len );
 
             *lpdwSize = WideCharToMultiByte( CP_ACP, 0, szCookieData, len,
                                     lpCookieData, *lpdwSize, NULL, NULL );
@@ -394,8 +412,8 @@ BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
     }
 
     HeapFree( GetProcessHeap(), 0, szCookieData );
-    HeapFree( GetProcessHeap(), 0, szCookieName );
-    HeapFree( GetProcessHeap(), 0, szUrl );
+    HeapFree( GetProcessHeap(), 0, name );
+    HeapFree( GetProcessHeap(), 0, url );
 
     return r;
 }
@@ -405,27 +423,129 @@ static BOOL set_cookie(LPCWSTR domain, LPCWSTR path, LPCWSTR cookie_name, LPCWST
     cookie_domain *thisCookieDomain = NULL;
     cookie *thisCookie;
     struct list *cursor;
+    LPWSTR data, value;
+    WCHAR *ptr;
+    FILETIME expiry;
+    BOOL expired = FALSE;
+
+    value = data = heap_strdupW(cookie_data);
+    if (!data)
+    {
+        ERR("could not allocate %zu bytes for the cookie data buffer\n", (strlenW(cookie_data) + 1) * sizeof(WCHAR));
+        return FALSE;
+    }
+
+    memset(&expiry,0,sizeof(expiry));
+
+    /* lots of information can be parsed out of the cookie value */
+
+    ptr = data;
+    for (;;)
+    {
+        static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
+        static const WCHAR szPath[] = {'p','a','t','h','=',0};
+        static const WCHAR szExpires[] = {'e','x','p','i','r','e','s','=',0};
+        static const WCHAR szSecure[] = {'s','e','c','u','r','e',0};
+        static const WCHAR szHttpOnly[] = {'h','t','t','p','o','n','l','y',0};
+
+        if (!(ptr = strchrW(ptr,';'))) break;
+        *ptr++ = 0;
+
+        if (value != data)
+            HeapFree(GetProcessHeap(), 0, value);
+        value = HeapAlloc(GetProcessHeap(), 0, (ptr - data) * sizeof(WCHAR));
+        if (value == NULL)
+        {
+            HeapFree(GetProcessHeap(), 0, data);
+            ERR("could not allocate %zu bytes for the cookie value buffer\n", (ptr - data) * sizeof(WCHAR));
+            return FALSE;
+        }
+        strcpyW(value, data);
+
+        while (*ptr == ' ') ptr++; /* whitespace */
+
+        if (strncmpiW(ptr, szDomain, 7) == 0)
+        {
+            ptr+=strlenW(szDomain);
+            domain = ptr;
+            TRACE("Parsing new domain %s\n",debugstr_w(domain));
+        }
+        else if (strncmpiW(ptr, szPath, 5) == 0)
+        {
+            ptr+=strlenW(szPath);
+            path = ptr;
+            TRACE("Parsing new path %s\n",debugstr_w(path));
+        }
+        else if (strncmpiW(ptr, szExpires, 8) == 0)
+        {
+            FILETIME ft;
+            SYSTEMTIME st;
+            FIXME("persistent cookies not handled (%s)\n",debugstr_w(ptr));
+            ptr+=strlenW(szExpires);
+            if (InternetTimeToSystemTimeW(ptr, &st, 0))
+            {
+                SystemTimeToFileTime(&st, &expiry);
+                GetSystemTimeAsFileTime(&ft);
+
+                if (CompareFileTime(&ft,&expiry) > 0)
+                {
+                    TRACE("Cookie already expired.\n");
+                    expired = TRUE;
+                }
+            }
+        }
+        else if (strncmpiW(ptr, szSecure, 6) == 0)
+        {
+            FIXME("secure not handled (%s)\n",debugstr_w(ptr));
+            ptr += strlenW(szSecure);
+        }
+        else if (strncmpiW(ptr, szHttpOnly, 8) == 0)
+        {
+            FIXME("httponly not handled (%s)\n",debugstr_w(ptr));
+            ptr += strlenW(szHttpOnly);
+        }
+        else if (*ptr)
+        {
+            FIXME("Unknown additional option %s\n",debugstr_w(ptr));
+            break;
+        }
+    }
 
     LIST_FOR_EACH(cursor, &domain_list)
     {
         thisCookieDomain = LIST_ENTRY(cursor, cookie_domain, entry);
-        if (COOKIE_matchDomain(domain, NULL /* FIXME: path */, thisCookieDomain, FALSE))
+        if (COOKIE_matchDomain(domain, path, thisCookieDomain, FALSE))
             break;
         thisCookieDomain = NULL;
     }
 
     if (!thisCookieDomain)
-        thisCookieDomain = COOKIE_addDomain(domain, path);
+    {
+        if (!expired)
+            thisCookieDomain = COOKIE_addDomain(domain, path);
+        else
+        {
+            HeapFree(GetProcessHeap(),0,data);
+            if (value != data) HeapFree(GetProcessHeap(), 0, value);
+            return TRUE;
+        }
+    }
 
     if ((thisCookie = COOKIE_findCookie(thisCookieDomain, cookie_name)))
         COOKIE_deleteCookie(thisCookie, FALSE);
 
-    TRACE("setting cookie %s=%s for domain %s\n", debugstr_w(cookie_name),
-          debugstr_w(cookie_data), debugstr_w(thisCookieDomain->lpCookieDomain));
+    TRACE("setting cookie %s=%s for domain %s path %s\n", debugstr_w(cookie_name),
+          debugstr_w(value), debugstr_w(thisCookieDomain->lpCookieDomain),debugstr_w(thisCookieDomain->lpCookiePath));
 
-    if (!COOKIE_addCookie(thisCookieDomain, cookie_name, cookie_data))
+    if (!expired && !COOKIE_addCookie(thisCookieDomain, cookie_name, value, expiry))
+    {
+        HeapFree(GetProcessHeap(),0,data);
+        if (value != data) HeapFree(GetProcessHeap(), 0, value);
         return FALSE;
+    }
 
+    HeapFree(GetProcessHeap(),0,data);
+    if (value != data) HeapFree(GetProcessHeap(), 0, value);
     return TRUE;
 }
 
@@ -454,28 +574,26 @@ BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
         return FALSE;
     }
 
-    hostName[0] = path[0] = 0;
+    hostName[0] = 0;
     ret = COOKIE_crackUrlSimple(lpszUrl, hostName, sizeof(hostName)/sizeof(hostName[0]), path, sizeof(path)/sizeof(path[0]));
     if (!ret || !hostName[0]) return FALSE;
 
     if (!lpszCookieName)
     {
-        unsigned int len;
         WCHAR *cookie, *data;
 
-        len = strlenW(lpCookieData);
-        if (!(cookie = HeapAlloc(GetProcessHeap(), 0, (len + 1) * sizeof(WCHAR))))
+        cookie = heap_strdupW(lpCookieData);
+        if (!cookie)
         {
             SetLastError(ERROR_OUTOFMEMORY);
             return FALSE;
         }
-        strcpyW(cookie, lpCookieData);
 
         /* some apps (or is it us??) try to add a cookie with no cookie name, but
          * the cookie data in the form of name[=data].
          */
-        if (!(data = strchrW(cookie, '='))) data = cookie + len;
-        else data++;
+        if (!(data = strchrW(cookie, '='))) data = cookie + strlenW(cookie);
+        else *data++ = 0;
 
         ret = set_cookie(hostName, path, cookie, data);
 
@@ -499,39 +617,21 @@ BOOL WINAPI InternetSetCookieW(LPCWSTR lpszUrl, LPCWSTR lpszCookieName,
 BOOL WINAPI InternetSetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName,
     LPCSTR lpCookieData)
 {
-    DWORD len;
-    LPWSTR szCookieData = NULL, szUrl = NULL, szCookieName = NULL;
+    LPWSTR data, url, name;
     BOOL r;
 
     TRACE("(%s,%s,%s)\n", debugstr_a(lpszUrl),
         debugstr_a(lpszCookieName), debugstr_a(lpCookieData));
 
-    if( lpszUrl )
-    {
-        len = MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, NULL, 0 );
-        szUrl = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        MultiByteToWideChar( CP_ACP, 0, lpszUrl, -1, szUrl, len );
-    }
+    url = heap_strdupAtoW(lpszUrl);
+    name = heap_strdupAtoW(lpszCookieName);
+    data = heap_strdupAtoW(lpCookieData);
 
-    if( lpszCookieName )
-    {
-        len = MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, NULL, 0 );
-        szCookieName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        MultiByteToWideChar( CP_ACP, 0, lpszCookieName, -1, szCookieName, len );
-    }
+    r = InternetSetCookieW( url, name, data );
 
-    if( lpCookieData )
-    {
-        len = MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, NULL, 0 );
-        szCookieData = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        MultiByteToWideChar( CP_ACP, 0, lpCookieData, -1, szCookieData, len );
-    }
-
-    r = InternetSetCookieW( szUrl, szCookieName, szCookieData );
-
-    HeapFree( GetProcessHeap(), 0, szCookieData );
-    HeapFree( GetProcessHeap(), 0, szCookieName );
-    HeapFree( GetProcessHeap(), 0, szUrl );
+    HeapFree( GetProcessHeap(), 0, data );
+    HeapFree( GetProcessHeap(), 0, name );
+    HeapFree( GetProcessHeap(), 0, url );
 
     return r;
 }
@@ -692,3 +792,28 @@ BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDeci
     FIXME("(%s, 0x%08x) stub\n", debugstr_w(pchHostName), dwDecision);
     return FALSE;
 }
+
+/***********************************************************************
+ *           IsDomainLegalCookieDomainW (WININET.@)
+ */
+BOOL WINAPI IsDomainLegalCookieDomainW( LPCWSTR s1, LPCWSTR s2 )
+{
+    const WCHAR *p;
+
+    FIXME("(%s, %s)\n", debugstr_w(s1), debugstr_w(s2));
+
+    if (!s1 || !s2)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+    if (s1[0] == '.' || !s1[0] || s2[0] == '.' || !s2[0])
+    {
+        SetLastError(ERROR_INVALID_NAME);
+        return FALSE;
+    }
+    if (!(p = strchrW(s2, '.'))) return FALSE;
+    if (strchrW(p + 1, '.') && !strcmpW(p + 1, s1)) return TRUE;
+    else if (!strcmpW(s1, s2)) return TRUE;
+    return FALSE;
+}
index dd5aa31..2c4a527 100644 (file)
 #include "config.h"
 #include "wine/port.h"
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <stdarg.h>
 
 #include "windef.h"
@@ -58,22 +62,23 @@ struct WININET_ErrorDlgParams
  */
 static BOOL WININET_GetProxyServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
 {
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs = NULL;
-    LPWININETAPPINFOW hIC = NULL;
+    http_request_t *lpwhr;
+    http_session_t *lpwhs = NULL;
+    appinfo_t *hIC = NULL;
+    BOOL ret = FALSE;
     LPWSTR p;
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
     if (NULL == lpwhr)
-       return FALSE;
+        return FALSE;
 
     lpwhs = lpwhr->lpHttpSession;
     if (NULL == lpwhs)
-       return FALSE;
+        goto done;
 
     hIC = lpwhs->lpAppInfo;
     if (NULL == hIC)
-       return FALSE;
+        goto done;
 
     lstrcpynW(szBuf, hIC->lpszProxy, sz);
 
@@ -82,7 +87,39 @@ static BOOL WININET_GetProxyServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
     if (p)
         *p = 0;
 
-    return TRUE;
+    ret = TRUE;
+
+done:
+    WININET_Release( &lpwhr->hdr );
+    return ret;
+}
+
+/***********************************************************************
+ *         WININET_GetServer
+ *
+ *  Determine the name of the web server
+ */
+static BOOL WININET_GetServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
+{
+    http_request_t *lpwhr;
+    http_session_t *lpwhs = NULL;
+    BOOL ret = FALSE;
+
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
+    if (NULL == lpwhr)
+        return FALSE;
+
+    lpwhs = lpwhr->lpHttpSession;
+    if (NULL == lpwhs)
+        goto done;
+
+    lstrcpynW(szBuf, lpwhs->lpszHostName, sz);
+
+    ret = TRUE;
+
+done:
+    WININET_Release( &lpwhr->hdr );
+    return ret;
 }
 
 /***********************************************************************
@@ -90,16 +127,20 @@ static BOOL WININET_GetProxyServer( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
  *
  *  Determine the name of the (basic) Authentication realm
  */
-static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
+static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz, BOOL proxy )
 {
     LPWSTR p, q;
-    DWORD index;
+    DWORD index, query;
     static const WCHAR szRealm[] = { 'r','e','a','l','m','=',0 };
 
-    /* extract the Realm from the proxy response and show it */
+    if (proxy)
+        query = HTTP_QUERY_PROXY_AUTHENTICATE;
+    else
+        query = HTTP_QUERY_WWW_AUTHENTICATE;
+
+    /* extract the Realm from the response and show it */
     index = 0;
-    if( !HttpQueryInfoW( hRequest, HTTP_QUERY_PROXY_AUTHENTICATE,
-                         szBuf, &sz, &index) )
+    if( !HttpQueryInfoW( hRequest, query, szBuf, &sz, &index) )
         return FALSE;
 
     /*
@@ -109,11 +150,10 @@ static BOOL WININET_GetAuthRealm( HINTERNET hRequest, LPWSTR szBuf, DWORD sz )
     p = strchrW( szBuf, ' ' );
     if( !p || strncmpW( p+1, szRealm, strlenW(szRealm) ) )
     {
-        ERR("proxy response wrong? (%s)\n", debugstr_w(szBuf));
+        ERR("response wrong? (%s)\n", debugstr_w(szBuf));
         return FALSE;
     }
 
-
     /* remove quotes */
     p += 7;
     if( *p == '"' )
@@ -194,44 +234,62 @@ static BOOL WININET_GetSetPassword( HWND hdlg, LPCWSTR szServer,
 }
 
 /***********************************************************************
- *         WININET_SetProxyAuthorization
+ *         WININET_SetAuthorization
  */
-static BOOL WININET_SetProxyAuthorization( HINTERNET hRequest,
-                                         LPWSTR username, LPWSTR password )
+static BOOL WININET_SetAuthorization( HINTERNET hRequest, LPWSTR username,
+                                      LPWSTR password, BOOL proxy )
 {
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs;
-    LPWININETAPPINFOW hIC;
-    LPWSTR p;
+    http_request_t *lpwhr;
+    http_session_t *lpwhs;
+    BOOL ret = FALSE;
+    LPWSTR p, q;
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
     if( !lpwhr )
-       return FALSE;
-        
+        return FALSE;
+
     lpwhs = lpwhr->lpHttpSession;
     if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
     {
         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       return FALSE;
+        goto done;
     }
 
-    hIC = lpwhs->lpAppInfo;
-
-    p = HeapAlloc( GetProcessHeap(), 0, (strlenW( username ) + 1)*sizeof(WCHAR) );
+    p = heap_strdupW(username);
     if( !p )
-        return FALSE;
-    
-    lstrcpyW( p, username );
-    hIC->lpszProxyUsername = p;
+        goto done;
 
-    p = HeapAlloc( GetProcessHeap(), 0, (strlenW( password ) + 1)*sizeof(WCHAR) );
-    if( !p )
-        return FALSE;
-    
-    lstrcpyW( p, password );
-    hIC->lpszProxyPassword = p;
+    q = heap_strdupW(password);
+    if( !q )
+    {
+        HeapFree(GetProcessHeap(), 0, username);
+        goto done;
+    }
 
-    return TRUE;
+    if (proxy)
+    {
+        appinfo_t *hIC = lpwhs->lpAppInfo;
+
+        HeapFree(GetProcessHeap(), 0, hIC->lpszProxyUsername);
+        hIC->lpszProxyUsername = p;
+
+        HeapFree(GetProcessHeap(), 0, hIC->lpszProxyPassword);
+        hIC->lpszProxyPassword = q;
+    }
+    else
+    {
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
+        lpwhs->lpszUserName = p;
+
+        HeapFree(GetProcessHeap(), 0, lpwhs->lpszPassword);
+        lpwhs->lpszPassword = q;
+    }
+
+    ret = TRUE;
+
+done:
+    WININET_Release( &lpwhr->hdr );
+    return ret;
 }
 
 /***********************************************************************
@@ -254,7 +312,7 @@ static INT_PTR WINAPI WININET_ProxyPasswordDialog(
 
         /* extract the Realm from the proxy response and show it */
         if( WININET_GetAuthRealm( params->hRequest,
-                                  szRealm, sizeof szRealm/sizeof(WCHAR)) )
+                                  szRealm, sizeof szRealm/sizeof(WCHAR), TRUE ) )
         {
             hitem = GetDlgItem( hdlg, IDC_REALM );
             SetWindowTextW( hitem, szRealm );
@@ -297,13 +355,97 @@ static INT_PTR WINAPI WININET_ProxyPasswordDialog(
             if( hitem &&
                 SendMessageW( hitem, BM_GETSTATE, 0, 0 ) &&
                 WININET_GetAuthRealm( params->hRequest,
-                                  szRealm, sizeof szRealm/sizeof(WCHAR)) &&
+                                  szRealm, sizeof szRealm/sizeof(WCHAR), TRUE ) &&
                 WININET_GetProxyServer( params->hRequest, 
                                     szServer, sizeof szServer/sizeof(WCHAR)) )
             {
                 WININET_GetSetPassword( hdlg, szServer, szRealm, TRUE );
             }
-            WININET_SetProxyAuthorization( params->hRequest, username, password );
+            WININET_SetAuthorization( params->hRequest, username, password, TRUE );
+
+            EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY );
+            return TRUE;
+        }
+        if( wParam == IDCANCEL )
+        {
+            EndDialog( hdlg, 0 );
+            return TRUE;
+        }
+        break;
+    }
+    return FALSE;
+}
+
+/***********************************************************************
+ *         WININET_PasswordDialog
+ */
+static INT_PTR WINAPI WININET_PasswordDialog(
+    HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
+{
+    HWND hitem;
+    struct WININET_ErrorDlgParams *params;
+    WCHAR szRealm[0x80], szServer[0x80];
+
+    if( uMsg == WM_INITDIALOG )
+    {
+        TRACE("WM_INITDIALOG (%08lx)\n", lParam);
+
+        /* save the parameter list */
+        params = (struct WININET_ErrorDlgParams*) lParam;
+        SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam );
+
+        /* extract the Realm from the response and show it */
+        if( WININET_GetAuthRealm( params->hRequest,
+                                  szRealm, sizeof szRealm/sizeof(WCHAR), FALSE ) )
+        {
+            hitem = GetDlgItem( hdlg, IDC_REALM );
+            SetWindowTextW( hitem, szRealm );
+        }
+
+        /* extract the name of the server */
+        if( WININET_GetServer( params->hRequest,
+                               szServer, sizeof szServer/sizeof(WCHAR)) )
+        {
+            hitem = GetDlgItem( hdlg, IDC_SERVER );
+            SetWindowTextW( hitem, szServer );
+        }
+
+        WININET_GetSetPassword( hdlg, szServer, szRealm, FALSE );
+
+        return TRUE;
+    }
+
+    params = (struct WININET_ErrorDlgParams*)
+                 GetWindowLongPtrW( hdlg, GWLP_USERDATA );
+
+    switch( uMsg )
+    {
+    case WM_COMMAND:
+        if( wParam == IDOK )
+        {
+            WCHAR username[0x20], password[0x20];
+
+            username[0] = 0;
+            hitem = GetDlgItem( hdlg, IDC_USERNAME );
+            if( hitem )
+                GetWindowTextW( hitem, username, sizeof username/sizeof(WCHAR) );
+
+            password[0] = 0;
+            hitem = GetDlgItem( hdlg, IDC_PASSWORD );
+            if( hitem )
+                GetWindowTextW( hitem, password, sizeof password/sizeof(WCHAR) );
+
+            hitem = GetDlgItem( hdlg, IDC_SAVEPASSWORD );
+            if( hitem &&
+                SendMessageW( hitem, BM_GETSTATE, 0, 0 ) &&
+                WININET_GetAuthRealm( params->hRequest,
+                                  szRealm, sizeof szRealm/sizeof(WCHAR), FALSE ) &&
+                WININET_GetServer( params->hRequest,
+                                   szServer, sizeof szServer/sizeof(WCHAR)) )
+            {
+                WININET_GetSetPassword( hdlg, szServer, szRealm, TRUE );
+            }
+            WININET_SetAuthorization( params->hRequest, username, password, FALSE );
 
             EndDialog( hdlg, ERROR_INTERNET_FORCE_RETRY );
             return TRUE;
@@ -362,17 +504,23 @@ DWORD WINAPI InternetErrorDlg(HWND hWnd, HINTERNET hRequest,
     switch( dwError )
     {
     case ERROR_SUCCESS:
-        if( !(dwFlags & FLAGS_ERROR_UI_FILTER_FOR_ERRORS ) )
+    case ERROR_INTERNET_INCORRECT_PASSWORD:
+        if( !dwError && !(dwFlags & FLAGS_ERROR_UI_FILTER_FOR_ERRORS ) )
             return 0;
-        dwStatus = WININET_GetConnectionStatus( hRequest );
-        if( HTTP_STATUS_PROXY_AUTH_REQ != dwStatus )
-            return ERROR_SUCCESS;
-        return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
-                    hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
 
-    case ERROR_INTERNET_INCORRECT_PASSWORD:
-        return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
-                    hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
+        dwStatus = WININET_GetConnectionStatus( hRequest );
+        switch (dwStatus)
+        {
+        case HTTP_STATUS_PROXY_AUTH_REQ:
+            return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ),
+                                    hWnd, WININET_ProxyPasswordDialog, (LPARAM) &params );
+        case HTTP_STATUS_DENIED:
+            return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_AUTHDLG ),
+                                    hWnd, WININET_PasswordDialog, (LPARAM) &params );
+        default:
+            WARN("unhandled status %u\n", dwStatus);
+            return 0;
+        }
 
     case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
     case ERROR_INTERNET_INVALID_CA:
index 770fef2..1f03199 100644 (file)
 #include "config.h"
 #include "wine/port.h"
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <errno.h>
 #include <stdarg.h>
 #include <stdio.h>
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
 #endif
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
 #ifdef HAVE_UNISTD_H
 # include <unistd.h>
 #endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
 #include <time.h>
 #include <assert.h>
 
 
 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 
-typedef struct _WININETFTPSESSIONW WININETFTPSESSIONW;
+typedef struct _ftp_session_t ftp_session_t;
 
 typedef struct
 {
-    WININETHANDLEHEADER hdr;
-    WININETFTPSESSIONW *lpFtpSession;
+    object_header_t hdr;
+    ftp_session_t *lpFtpSession;
     BOOL session_deleted;
     int nDataSocket;
-} WININETFTPFILE, *LPWININETFTPFILE;
+    WCHAR *cache_file;
+    HANDLE cache_file_handle;
+} ftp_file_t;
 
-typedef struct _WININETFTPSESSIONW
+struct _ftp_session_t
 {
-    WININETHANDLEHEADER hdr;
-    WININETAPPINFOW *lpAppInfo;
+    object_header_t hdr;
+    appinfo_t *lpAppInfo;
     int sndSocket;
     int lstnSocket;
     int pasvSocket; /* data socket connected by us in case of passive FTP */
-    LPWININETFTPFILE download_in_progress;
+    ftp_file_t *download_in_progress;
     struct sockaddr_in socketAddress;
     struct sockaddr_in lstnSocketAddress;
+    LPWSTR servername;
+    INTERNET_PORT serverport;
     LPWSTR  lpszPassword;
     LPWSTR  lpszUserName;
-} *LPWININETFTPSESSIONW;
+};
 
 typedef struct
 {
     BOOL bIsDirectory;
     LPWSTR lpszName;
     DWORD nSize;
-    struct tm tmLastModified;
+    SYSTEMTIME tmLastModified;
     unsigned short permissions;
 } FILEPROPERTIESW, *LPFILEPROPERTIESW;
 
 typedef struct
 {
-    WININETHANDLEHEADER hdr;
-    WININETFTPSESSIONW *lpFtpSession;
+    object_header_t hdr;
+    ftp_session_t *lpFtpSession;
     DWORD index;
     DWORD size;
     LPFILEPROPERTIESW lpafp;
@@ -165,46 +179,51 @@ static const CHAR szMonths[] = "JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC";
 static const WCHAR szNoAccount[] = {'n','o','a','c','c','o','u','n','t','\0'};
 
 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
-       INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext);
-static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
-static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket);
-static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
-static INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext);
-static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType);
-static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile);
-static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType);
-static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs);
-static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs);
+       INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext);
+static BOOL FTP_SendStore(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
+static BOOL FTP_GetDataSocket(ftp_session_t*, LPINT nDataSocket);
+static BOOL FTP_SendData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
+static INT FTP_ReceiveResponse(ftp_session_t*, DWORD_PTR dwContext);
+static BOOL FTP_SendRetrieve(ftp_session_t*, LPCWSTR lpszRemoteFile, DWORD dwType);
+static BOOL FTP_RetrieveFileData(ftp_session_t*, INT nDataSocket, HANDLE hFile);
+static BOOL FTP_InitListenSocket(ftp_session_t*);
+static BOOL FTP_ConnectToHost(ftp_session_t*);
+static BOOL FTP_SendPassword(ftp_session_t*);
+static BOOL FTP_SendAccount(ftp_session_t*);
+static BOOL FTP_SendType(ftp_session_t*, DWORD dwType);
+static BOOL FTP_SendPort(ftp_session_t*);
+static BOOL FTP_DoPassive(ftp_session_t*);
+static BOOL FTP_SendPortOrPasv(ftp_session_t*);
 static BOOL FTP_ParsePermission(LPCSTR lpszPermission, LPFILEPROPERTIESW lpfp);
 static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERTIESW fileprop);
-static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
+static BOOL FTP_ParseDirectory(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
         LPFILEPROPERTIESW *lpafp, LPDWORD dwfp);
-static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
+static HINTERNET FTP_ReceiveFileList(ftp_session_t*, INT nSocket, LPCWSTR lpszSearchFile,
         LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext);
 static DWORD FTP_SetResponseError(DWORD dwResponse);
 static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFindFileData);
-static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
+static BOOL FTP_FtpPutFileW(ftp_session_t*, LPCWSTR lpszLocalFile,
         LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext);
-static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
-static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
-static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
+static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
+static BOOL FTP_FtpCreateDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
+static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t*,
         LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext);
-static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
+static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t*, LPWSTR lpszCurrentDirectory,
         LPDWORD lpdwCurrentDirectory);
-static BOOL FTP_FtpRenameFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest);
-static BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory);
-static BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName);
-static HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName,
-        DWORD fdwAccess, DWORD dwFlags, DWORD_PTR dwContext);
-static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
+static BOOL FTP_FtpRenameFileW(ftp_session_t*, LPCWSTR lpszSrc, LPCWSTR lpszDest);
+static BOOL FTP_FtpRemoveDirectoryW(ftp_session_t*, LPCWSTR lpszDirectory);
+static BOOL FTP_FtpDeleteFileW(ftp_session_t*, LPCWSTR lpszFileName);
+static BOOL FTP_FtpGetFileW(ftp_session_t*, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
         BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
         DWORD_PTR dwContext);
 
+/* A temporary helper until we get rid of INTERNET_GetLastError calls */
+static BOOL res_to_le(DWORD res)
+{
+    if(res != ERROR_SUCCESS)
+        INTERNET_SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
 
 /***********************************************************************
  *           FtpPutFileA (WININET.@)
@@ -223,8 +242,8 @@ BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
     LPWSTR lpwzNewRemoteFile;
     BOOL ret;
     
-    lpwzLocalFile = lpszLocalFile?WININET_strdup_AtoW(lpszLocalFile):NULL;
-    lpwzNewRemoteFile = lpszNewRemoteFile?WININET_strdup_AtoW(lpszNewRemoteFile):NULL;
+    lpwzLocalFile = heap_strdupAtoW(lpszLocalFile);
+    lpwzNewRemoteFile = heap_strdupAtoW(lpszNewRemoteFile);
     ret = FtpPutFileW(hConnect, lpwzLocalFile, lpwzNewRemoteFile,
                       dwFlags, dwContext);
     HeapFree(GetProcessHeap(), 0, lpwzLocalFile);
@@ -235,7 +254,7 @@ BOOL WINAPI FtpPutFileA(HINTERNET hConnect, LPCSTR lpszLocalFile,
 static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPPUTFILEW const *req = &workRequest->u.FtpPutFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -259,8 +278,8 @@ static void AsyncFtpPutFileProc(WORKREQUEST *workRequest)
 BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
     if (!lpszLocalFile || !lpszNewRemoteFile)
@@ -269,7 +288,7 @@ BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
         return FALSE;
     }
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -302,12 +321,12 @@ BOOL WINAPI FtpPutFileW(HINTERNET hConnect, LPCWSTR lpszLocalFile,
 
         workRequest.asyncproc = AsyncFtpPutFileProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
-        req->lpszLocalFile = WININET_strdupW(lpszLocalFile);
-        req->lpszNewRemoteFile = WININET_strdupW(lpszNewRemoteFile);
+        req->lpszLocalFile = heap_strdupW(lpszLocalFile);
+        req->lpszNewRemoteFile = heap_strdupW(lpszNewRemoteFile);
        req->dwFlags = dwFlags;
        req->dwContext = dwContext;
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -331,12 +350,12 @@ lend:
  *    FALSE on failure
  *
  */
-static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
+static BOOL FTP_FtpPutFileW(ftp_session_t *lpwfs, LPCWSTR lpszLocalFile,
     LPCWSTR lpszNewRemoteFile, DWORD dwFlags, DWORD_PTR dwContext)
 {
     HANDLE hFile;
     BOOL bSuccess = FALSE;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
     INT nResCode;
 
     TRACE(" lpszLocalFile(%s) lpszNewRemoteFile(%s)\n", debugstr_w(lpszLocalFile), debugstr_w(lpszNewRemoteFile));
@@ -375,7 +394,10 @@ static BOOL FTP_FtpPutFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszLocalFile,
     }
 
     if (lpwfs->lstnSocket != -1)
+    {
         closesocket(lpwfs->lstnSocket);
+        lpwfs->lstnSocket = -1;
+    }
 
     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
     {
@@ -408,7 +430,7 @@ BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
     LPWSTR lpwzDirectory;
     BOOL ret;
     
-    lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
+    lpwzDirectory = heap_strdupAtoW(lpszDirectory);
     ret = FtpSetCurrentDirectoryW(hConnect, lpwzDirectory);
     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
     return ret;
@@ -418,7 +440,7 @@ BOOL WINAPI FtpSetCurrentDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
 static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPSETCURRENTDIRECTORYW const *req = &workRequest->u.FtpSetCurrentDirectoryW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -438,8 +460,8 @@ static void AsyncFtpSetCurrentDirectoryProc(WORKREQUEST *workRequest)
  */
 BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
 {
-    LPWININETFTPSESSIONW lpwfs = NULL;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs = NULL;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
     if (!lpszDirectory)
@@ -448,7 +470,7 @@ BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
         goto lend;
     }
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
     {
         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
@@ -472,9 +494,9 @@ BOOL WINAPI FtpSetCurrentDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
         workRequest.asyncproc = AsyncFtpSetCurrentDirectoryProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpSetCurrentDirectoryW;
-        req->lpszDirectory = WININET_strdupW(lpszDirectory);
+        req->lpszDirectory = heap_strdupW(lpszDirectory);
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -499,10 +521,10 @@ lend:
  *    FALSE on failure
  *
  */
-static BOOL FTP_FtpSetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
+static BOOL FTP_FtpSetCurrentDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
 {
     INT nResCode;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
     DWORD bSuccess = FALSE;
 
     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
@@ -554,7 +576,7 @@ BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
     LPWSTR lpwzDirectory;
     BOOL ret;
     
-    lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
+    lpwzDirectory = heap_strdupAtoW(lpszDirectory);
     ret = FtpCreateDirectoryW(hConnect, lpwzDirectory);
     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
     return ret;
@@ -564,7 +586,7 @@ BOOL WINAPI FtpCreateDirectoryA(HINTERNET hConnect, LPCSTR lpszDirectory)
 static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPCREATEDIRECTORYW const *req = &workRequest->u.FtpCreateDirectoryW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE(" %p\n", lpwfs);
 
@@ -584,11 +606,11 @@ static void AsyncFtpCreateDirectoryProc(WORKREQUEST *workRequest)
  */
 BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -622,9 +644,9 @@ BOOL WINAPI FtpCreateDirectoryW(HINTERNET hConnect, LPCWSTR lpszDirectory)
         workRequest.asyncproc = AsyncFtpCreateDirectoryProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpCreateDirectoryW;
-        req->lpszDirectory = WININET_strdupW(lpszDirectory);
+        req->lpszDirectory = heap_strdupW(lpszDirectory);
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -647,11 +669,11 @@ lend:
  *    FALSE on failure
  *
  */
-static BOOL FTP_FtpCreateDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
+static BOOL FTP_FtpCreateDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
 
     TRACE("lpszDirectory(%s)\n", debugstr_w(lpszDirectory));
 
@@ -703,14 +725,14 @@ HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
     LPWIN32_FIND_DATAW lpFindFileDataW;
     HINTERNET ret;
     
-    lpwzSearchFile = lpszSearchFile?WININET_strdup_AtoW(lpszSearchFile):NULL;
+    lpwzSearchFile = heap_strdupAtoW(lpszSearchFile);
     lpFindFileDataW = lpFindFileData?&wfd:NULL;
     ret = FtpFindFirstFileW(hConnect, lpwzSearchFile, lpFindFileDataW, dwFlags, dwContext);
     HeapFree(GetProcessHeap(), 0, lpwzSearchFile);
     
     if (ret && lpFindFileData)
         WININET_find_data_WtoA(lpFindFileDataW, lpFindFileData);
-    
+
     return ret;
 }
 
@@ -718,7 +740,7 @@ HINTERNET WINAPI FtpFindFirstFileA(HINTERNET hConnect,
 static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPFINDFIRSTFILEW const *req = &workRequest->u.FtpFindFirstFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -740,11 +762,11 @@ static void AsyncFtpFindFirstFileProc(WORKREQUEST *workRequest)
 HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     HINTERNET r = NULL;
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
     if (NULL == lpwfs || WH_HFTPSESSION != lpwfs->hdr.htype)
     {
         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
@@ -766,7 +788,7 @@ HINTERNET WINAPI FtpFindFirstFileW(HINTERNET hConnect,
         workRequest.asyncproc = AsyncFtpFindFirstFileProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpFindFirstFileW;
-        req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : WININET_strdupW(lpszSearchFile);
+        req->lpszSearchFile = (lpszSearchFile == NULL) ? NULL : heap_strdupW(lpszSearchFile);
        req->lpFindFileData = lpFindFileData;
        req->dwFlags = dwFlags;
        req->dwContext= dwContext;
@@ -797,11 +819,11 @@ lend:
  *    NULL on failure
  *
  */
-static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
+static HINTERNET FTP_FtpFindFirstFileW(ftp_session_t *lpwfs,
     LPCWSTR lpszSearchFile, LPWIN32_FIND_DATAW lpFindFileData, DWORD dwFlags, DWORD_PTR dwContext)
 {
     INT nResCode;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
     HINTERNET hFindNext = NULL;
 
     TRACE("\n");
@@ -845,7 +867,10 @@ static HINTERNET FTP_FtpFindFirstFileW(LPWININETFTPSESSIONW lpwfs,
 
 lend:
     if (lpwfs->lstnSocket != -1)
+    {
         closesocket(lpwfs->lstnSocket);
+        lpwfs->lstnSocket = -1;
+    }
 
     hIC = lpwfs->lpAppInfo;
     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
@@ -854,13 +879,13 @@ lend:
 
         if (hFindNext)
        {
-            iar.dwResult = (DWORD)hFindNext;
+            iar.dwResult = (DWORD_PTR)hFindNext;
             iar.dwError = ERROR_SUCCESS;
             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
                 &iar, sizeof(INTERNET_ASYNC_RESULT));
        }
 
-        iar.dwResult = (DWORD)hFindNext;
+        iar.dwResult = (DWORD_PTR)hFindNext;
         iar.dwError = hFindNext ? ERROR_SUCCESS : INTERNET_GetLastError();
         SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
             &iar, sizeof(INTERNET_ASYNC_RESULT));
@@ -913,7 +938,7 @@ BOOL WINAPI FtpGetCurrentDirectoryA(HINTERNET hFtpSession, LPSTR lpszCurrentDire
 static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPGETCURRENTDIRECTORYW const *req = &workRequest->u.FtpGetCurrentDirectoryW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -933,13 +958,13 @@ static void AsyncFtpGetCurrentDirectoryProc(WORKREQUEST *workRequest)
 BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDirectory,
     LPDWORD lpdwCurrentDirectory)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
     TRACE("%p %p %p\n", hFtpSession, lpszCurrentDirectory, lpdwCurrentDirectory);
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
     if (NULL == lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -982,7 +1007,7 @@ BOOL WINAPI FtpGetCurrentDirectoryW(HINTERNET hFtpSession, LPWSTR lpszCurrentDir
        req->lpszDirectory = lpszCurrentDirectory;
        req->lpdwDirectory = lpdwCurrentDirectory;
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -1008,11 +1033,11 @@ lend:
  *    FALSE on failure
  *
  */
-static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszCurrentDirectory,
+static BOOL FTP_FtpGetCurrentDirectoryW(ftp_session_t *lpwfs, LPWSTR lpszCurrentDirectory,
        LPDWORD lpdwCurrentDirectory)
 {
     INT nResCode;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
     DWORD bSuccess = FALSE;
 
     /* Clear any error information */
@@ -1029,7 +1054,7 @@ static BOOL FTP_FtpGetCurrentDirectoryW(LPWININETFTPSESSIONW lpwfs, LPWSTR lpszC
         if (nResCode == 257) /* Extract directory name */
         {
             DWORD firstpos, lastpos, len;
-            LPWSTR lpszResponseBuffer = WININET_strdup_AtoW(INTERNET_GetResponseBuffer());
+            LPWSTR lpszResponseBuffer = heap_strdupAtoW(INTERNET_GetResponseBuffer());
 
             for (firstpos = 0, lastpos = 0; lpszResponseBuffer[lastpos]; lastpos++)
             {
@@ -1071,118 +1096,6 @@ lend:
     return bSuccess;
 }
 
-/***********************************************************************
- *           FtpOpenFileA (WININET.@)
- *
- * Open a remote file for writing or reading
- *
- * RETURNS
- *    HINTERNET handle on success
- *    NULL on failure
- *
- */
-HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
-    LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
-    DWORD_PTR dwContext)
-{
-    LPWSTR lpwzFileName;
-    HINTERNET ret;
-    
-    lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
-    ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
-    HeapFree(GetProcessHeap(), 0, lpwzFileName);
-    return ret;
-}
-
-
-static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
-{
-    struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
-
-    TRACE("%p\n", lpwfs);
-
-    FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
-        req->dwAccess, req->dwFlags, req->dwContext);
-    HeapFree(GetProcessHeap(), 0, req->lpszFilename);
-}
-
-/***********************************************************************
- *           FtpOpenFileW (WININET.@)
- *
- * Open a remote file for writing or reading
- *
- * RETURNS
- *    HINTERNET handle on success
- *    NULL on failure
- *
- */
-HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
-    LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
-    DWORD_PTR dwContext)
-{
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
-    HINTERNET r = NULL;
-    
-    TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
-        debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
-
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
-    if (!lpwfs)
-    {
-        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
-        return FALSE;
-    }
-
-    if (WH_HFTPSESSION != lpwfs->hdr.htype)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-        goto lend;
-    }
-
-    if ((!lpszFileName) ||
-        ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
-        ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
-    {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
-        goto lend;
-    }
-
-    if (lpwfs->download_in_progress != NULL)
-    {
-        INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
-        goto lend;
-    }
-
-    hIC = lpwfs->lpAppInfo;
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
-    {
-        WORKREQUEST workRequest;
-        struct WORKREQ_FTPOPENFILEW *req;
-
-        workRequest.asyncproc = AsyncFtpOpenFileProc;
-        workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
-        req = &workRequest.u.FtpOpenFileW;
-       req->lpszFilename = WININET_strdupW(lpszFileName);
-       req->dwAccess = fdwAccess;
-       req->dwFlags = dwFlags;
-       req->dwContext = dwContext;
-
-       INTERNET_AsyncCall(&workRequest);
-       r = NULL;
-    }
-    else
-    {
-       r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
-    }
-
-lend:
-    WININET_Release( &lpwfs->hdr );
-
-    return r;
-}
-
 
 /***********************************************************************
  *           FTPFILE_Destroy(internal)
@@ -1191,15 +1104,18 @@ lend:
  * the 'transfer complete' message (this is a bit of a hack though :-/ )
  *
  */
-static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
+static void FTPFILE_Destroy(object_header_t *hdr)
 {
-    LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
-    LPWININETFTPSESSIONW lpwfs = lpwh->lpFtpSession;
+    ftp_file_t *lpwh = (ftp_file_t*) hdr;
+    ftp_session_t *lpwfs = lpwh->lpFtpSession;
     INT nResCode;
 
     TRACE("\n");
 
-    WININET_Release(&lpwh->lpFtpSession->hdr);
+    if (lpwh->cache_file_handle != INVALID_HANDLE_VALUE)
+        CloseHandle(lpwh->cache_file_handle);
+
+    HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
 
     if (!lpwh->session_deleted)
         lpwfs->download_in_progress = NULL;
@@ -1210,10 +1126,12 @@ static void FTPFILE_Destroy(WININETHANDLEHEADER *hdr)
     nResCode = FTP_ReceiveResponse(lpwfs, lpwfs->hdr.dwContext);
     if (nResCode > 0 && nResCode != 226) WARN("server reports failed transfer\n");
 
+    WININET_Release(&lpwh->lpFtpSession->hdr);
+
     HeapFree(GetProcessHeap(), 0, lpwh);
 }
 
-static DWORD FTPFILE_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static DWORD FTPFILE_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
     switch(option) {
     case INTERNET_OPTION_HANDLE_TYPE:
@@ -1225,15 +1143,47 @@ static DWORD FTPFILE_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
         *size = sizeof(DWORD);
         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_FTP_FILE;
         return ERROR_SUCCESS;
-    }
+    case INTERNET_OPTION_DATAFILE_NAME:
+    {
+        DWORD required;
+        ftp_file_t *file = (ftp_file_t *)hdr;
 
+        TRACE("INTERNET_OPTION_DATAFILE_NAME\n");
+
+        if (!file->cache_file)
+        {
+            *size = 0;
+            return ERROR_INTERNET_ITEM_NOT_FOUND;
+        }
+        if (unicode)
+        {
+            required = (lstrlenW(file->cache_file) + 1) * sizeof(WCHAR);
+            if (*size < required)
+                return ERROR_INSUFFICIENT_BUFFER;
+
+            *size = required;
+            memcpy(buffer, file->cache_file, *size);
+            return ERROR_SUCCESS;
+        }
+        else
+        {
+            required = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, NULL, 0, NULL, NULL);
+            if (required > *size)
+                return ERROR_INSUFFICIENT_BUFFER;
+
+            *size = WideCharToMultiByte(CP_ACP, 0, file->cache_file, -1, buffer, *size, NULL, NULL);
+            return ERROR_SUCCESS;
+        }
+    }
+    }
     return INET_QueryOption(option, buffer, size, unicode);
 }
 
-static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
+static DWORD FTPFILE_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
 {
-    WININETFTPFILE *file = (WININETFTPFILE*)hdr;
+    ftp_file_t *file = (ftp_file_t*)hdr;
     int res;
+    DWORD error;
 
     if (file->nDataSocket == -1)
         return ERROR_INTERNET_DISCONNECTED;
@@ -1242,29 +1192,119 @@ static DWORD FTPFILE_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size
     res = recv(file->nDataSocket, buffer, size, MSG_WAITALL);
     *read = res>0 ? res : 0;
 
-    return res>=0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME*/
+    error = res >= 0 ? ERROR_SUCCESS : INTERNET_ERROR_BASE; /* FIXME */
+    if (error == ERROR_SUCCESS && file->cache_file)
+    {
+        DWORD bytes_written;
+
+        if (!WriteFile(file->cache_file_handle, buffer, *read, &bytes_written, NULL))
+            WARN("WriteFile failed: %u\n", GetLastError());
+    }
+    return error;
+}
+
+static DWORD FTPFILE_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
+    DWORD flags, DWORD_PTR context)
+{
+    return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
 }
 
-static BOOL FTPFILE_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
+static DWORD FTPFILE_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
+    DWORD flags, DWORD_PTR context)
 {
-    LPWININETFTPFILE lpwh = (LPWININETFTPFILE) hdr;
+    return FTPFILE_ReadFile(hdr, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength);
+}
+
+static DWORD FTPFILE_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
+{
+    ftp_file_t *lpwh = (ftp_file_t*) hdr;
     int res;
 
     res = send(lpwh->nDataSocket, buffer, size, 0);
 
     *written = res>0 ? res : 0;
-    return res >= 0;
+    return res >= 0 ? ERROR_SUCCESS : sock_get_error(errno);
+}
+
+static void FTP_ReceiveRequestData(ftp_file_t *file, BOOL first_notif)
+{
+    INTERNET_ASYNC_RESULT iar;
+    BYTE buffer[4096];
+    int available;
+
+    TRACE("%p\n", file);
+
+    available = recv(file->nDataSocket, buffer, sizeof(buffer), MSG_PEEK);
+
+    if(available != -1) {
+        iar.dwResult = (DWORD_PTR)file->hdr.hInternet;
+        iar.dwError = first_notif ? 0 : available;
+    }else {
+        iar.dwResult = 0;
+        iar.dwError = INTERNET_GetLastError();
+    }
+
+    INTERNET_SendCallback(&file->hdr, file->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+}
+
+static void FTPFILE_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
+{
+    ftp_file_t *file = (ftp_file_t*)workRequest->hdr;
+
+    FTP_ReceiveRequestData(file, FALSE);
+}
+
+static DWORD FTPFILE_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
+{
+    ftp_file_t *file = (ftp_file_t*) hdr;
+    int retval, unread = 0;
+
+    TRACE("(%p %p %x %lx)\n", file, available, flags, ctx);
+
+#ifdef FIONREAD
+    retval = ioctlsocket(file->nDataSocket, FIONREAD, &unread);
+    if (!retval)
+        TRACE("%d bytes of queued, but unread data\n", unread);
+#else
+    FIXME("FIONREAD not available\n");
+#endif
+
+    *available = unread;
+
+    if(!unread) {
+        BYTE byte;
+
+        *available = 0;
+
+        retval = recv(file->nDataSocket, &byte, 1, MSG_PEEK);
+        if(retval > 0) {
+            WORKREQUEST workRequest;
+
+            *available = 0;
+            workRequest.asyncproc = FTPFILE_AsyncQueryDataAvailableProc;
+            workRequest.hdr = WININET_AddRef( &file->hdr );
+
+            INTERNET_AsyncCall(&workRequest);
+
+            return ERROR_IO_PENDING;
+        }
+    }
+
+    return ERROR_SUCCESS;
 }
 
-static const HANDLEHEADERVtbl FTPFILEVtbl = {
+
+static const object_vtbl_t FTPFILEVtbl = {
     FTPFILE_Destroy,
     NULL,
     FTPFILE_QueryOption,
     NULL,
     FTPFILE_ReadFile,
-    NULL,
+    FTPFILE_ReadFileExA,
+    FTPFILE_ReadFileExW,
     FTPFILE_WriteFile,
-    NULL,
+    FTPFILE_QueryDataAvailable,
     NULL
 };
 
@@ -1278,14 +1318,14 @@ static const HANDLEHEADERVtbl FTPFILEVtbl = {
  *    NULL on failure
  *
  */
-HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
+static HINTERNET FTP_FtpOpenFileW(ftp_session_t *lpwfs,
        LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
        DWORD_PTR dwContext)
 {
     INT nDataSocket;
     BOOL bSuccess = FALSE;
-    LPWININETFTPFILE lpwh = NULL;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_file_t *lpwh = NULL;
+    appinfo_t *hIC = NULL;
     HINTERNET handle = NULL;
 
     TRACE("\n");
@@ -1307,15 +1347,18 @@ HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
     /* Get data socket to server */
     if (bSuccess && FTP_GetDataSocket(lpwfs, &nDataSocket))
     {
-        lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPFILE));
+        lpwh = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_file_t));
         lpwh->hdr.htype = WH_HFILE;
         lpwh->hdr.vtbl = &FTPFILEVtbl;
         lpwh->hdr.dwFlags = dwFlags;
         lpwh->hdr.dwContext = dwContext;
+        lpwh->hdr.dwInternalFlags = 0;
         lpwh->hdr.refs = 1;
         lpwh->hdr.lpfnStatusCB = lpwfs->hdr.lpfnStatusCB;
         lpwh->nDataSocket = nDataSocket;
-       lpwh->session_deleted = FALSE;
+        lpwh->cache_file = NULL;
+        lpwh->cache_file_handle = INVALID_HANDLE_VALUE;
+        lpwh->session_deleted = FALSE;
 
         WININET_AddRef( &lpwfs->hdr );
         lpwh->lpFtpSession = lpwfs;
@@ -1330,7 +1373,45 @@ HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
     }
 
     if (lpwfs->lstnSocket != -1)
+    {
         closesocket(lpwfs->lstnSocket);
+        lpwfs->lstnSocket = -1;
+    }
+
+    if (bSuccess && fdwAccess == GENERIC_READ)
+    {
+        WCHAR filename[MAX_PATH + 1];
+        URL_COMPONENTSW uc;
+        DWORD len;
+
+        memset(&uc, 0, sizeof(uc));
+        uc.dwStructSize = sizeof(uc);
+        uc.nScheme      = INTERNET_SCHEME_FTP;
+        uc.lpszHostName = lpwfs->servername;
+        uc.nPort        = lpwfs->serverport;
+        uc.lpszUserName = lpwfs->lpszUserName;
+        uc.lpszUrlPath  = heap_strdupW(lpszFileName);
+
+        if (!InternetCreateUrlW(&uc, 0, NULL, &len) && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+        {
+            WCHAR *url = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+
+            if (url && InternetCreateUrlW(&uc, 0, url, &len) && CreateUrlCacheEntryW(url, 0, NULL, filename, 0))
+            {
+                lpwh->cache_file = heap_strdupW(filename);
+                lpwh->cache_file_handle = CreateFileW(filename, GENERIC_WRITE, FILE_SHARE_READ,
+                                                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+                if (lpwh->cache_file_handle == INVALID_HANDLE_VALUE)
+                {
+                    WARN("Could not create cache file: %u\n", GetLastError());
+                    HeapFree(GetProcessHeap(), 0, lpwh->cache_file);
+                    lpwh->cache_file = NULL;
+                }
+            }
+            HeapFree(GetProcessHeap(), 0, url);
+        }
+        HeapFree(GetProcessHeap(), 0, uc.lpszUrlPath);
+    }
 
     hIC = lpwfs->lpAppInfo;
     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
@@ -1339,16 +1420,20 @@ HINTERNET FTP_FtpOpenFileW(LPWININETFTPSESSIONW lpwfs,
 
        if (lpwh)
        {
-            iar.dwResult = (DWORD)handle;
+            iar.dwResult = (DWORD_PTR)handle;
             iar.dwError = ERROR_SUCCESS;
             SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_HANDLE_CREATED,
                 &iar, sizeof(INTERNET_ASYNC_RESULT));
        }
 
-        iar.dwResult = (DWORD)bSuccess;
-        iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
-        SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
-            &iar, sizeof(INTERNET_ASYNC_RESULT));
+        if(bSuccess) {
+            FTP_ReceiveRequestData(lpwh, TRUE);
+        }else {
+            iar.dwResult = 0;
+            iar.dwError = INTERNET_GetLastError();
+            SendAsyncCallback(&lpwfs->hdr, lpwfs->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
+                    &iar, sizeof(INTERNET_ASYNC_RESULT));
+        }
     }
 
 lend:
@@ -1359,6 +1444,119 @@ lend:
 }
 
 
+/***********************************************************************
+ *           FtpOpenFileA (WININET.@)
+ *
+ * Open a remote file for writing or reading
+ *
+ * RETURNS
+ *    HINTERNET handle on success
+ *    NULL on failure
+ *
+ */
+HINTERNET WINAPI FtpOpenFileA(HINTERNET hFtpSession,
+    LPCSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
+    DWORD_PTR dwContext)
+{
+    LPWSTR lpwzFileName;
+    HINTERNET ret;
+
+    lpwzFileName = heap_strdupAtoW(lpszFileName);
+    ret = FtpOpenFileW(hFtpSession, lpwzFileName, fdwAccess, dwFlags, dwContext);
+    HeapFree(GetProcessHeap(), 0, lpwzFileName);
+    return ret;
+}
+
+
+static void AsyncFtpOpenFileProc(WORKREQUEST *workRequest)
+{
+    struct WORKREQ_FTPOPENFILEW const *req = &workRequest->u.FtpOpenFileW;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
+
+    TRACE("%p\n", lpwfs);
+
+    FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
+        req->dwAccess, req->dwFlags, req->dwContext);
+    HeapFree(GetProcessHeap(), 0, req->lpszFilename);
+}
+
+/***********************************************************************
+ *           FtpOpenFileW (WININET.@)
+ *
+ * Open a remote file for writing or reading
+ *
+ * RETURNS
+ *    HINTERNET handle on success
+ *    NULL on failure
+ *
+ */
+HINTERNET WINAPI FtpOpenFileW(HINTERNET hFtpSession,
+    LPCWSTR lpszFileName, DWORD fdwAccess, DWORD dwFlags,
+    DWORD_PTR dwContext)
+{
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
+    HINTERNET r = NULL;
+
+    TRACE("(%p,%s,0x%08x,0x%08x,0x%08lx)\n", hFtpSession,
+        debugstr_w(lpszFileName), fdwAccess, dwFlags, dwContext);
+
+    lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
+    if (!lpwfs)
+    {
+        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
+        return FALSE;
+    }
+
+    if (WH_HFTPSESSION != lpwfs->hdr.htype)
+    {
+        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+        goto lend;
+    }
+
+    if ((!lpszFileName) ||
+        ((fdwAccess != GENERIC_READ) && (fdwAccess != GENERIC_WRITE)) ||
+        ((dwFlags & FTP_CONDITION_MASK) > FTP_TRANSFER_TYPE_BINARY))
+    {
+        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        goto lend;
+    }
+
+    if (lpwfs->download_in_progress != NULL)
+    {
+        INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
+        goto lend;
+    }
+
+    hIC = lpwfs->lpAppInfo;
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+        struct WORKREQ_FTPOPENFILEW *req;
+
+        workRequest.asyncproc = AsyncFtpOpenFileProc;
+        workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
+        req = &workRequest.u.FtpOpenFileW;
+       req->lpszFilename = heap_strdupW(lpszFileName);
+       req->dwAccess = fdwAccess;
+       req->dwFlags = dwFlags;
+       req->dwContext = dwContext;
+
+       INTERNET_AsyncCall(&workRequest);
+       r = NULL;
+    }
+    else
+    {
+       r = FTP_FtpOpenFileW(lpwfs, lpszFileName, fdwAccess, dwFlags, dwContext);
+    }
+
+lend:
+    WININET_Release( &lpwfs->hdr );
+
+    return r;
+}
+
+
 /***********************************************************************
  *           FtpGetFileA (WININET.@)
  *
@@ -1377,8 +1575,8 @@ BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszN
     LPWSTR lpwzNewFile;
     BOOL ret;
     
-    lpwzRemoteFile = lpszRemoteFile?WININET_strdup_AtoW(lpszRemoteFile):NULL;
-    lpwzNewFile = lpszNewFile?WININET_strdup_AtoW(lpszNewFile):NULL;
+    lpwzRemoteFile = heap_strdupAtoW(lpszRemoteFile);
+    lpwzNewFile = heap_strdupAtoW(lpszNewFile);
     ret = FtpGetFileW(hInternet, lpwzRemoteFile, lpwzNewFile, fFailIfExists,
         dwLocalFlagsAttribute, dwInternetFlags, dwContext);
     HeapFree(GetProcessHeap(), 0, lpwzRemoteFile);
@@ -1390,7 +1588,7 @@ BOOL WINAPI FtpGetFileA(HINTERNET hInternet, LPCSTR lpszRemoteFile, LPCSTR lpszN
 static void AsyncFtpGetFileProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPGETFILEW const *req = &workRequest->u.FtpGetFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -1416,8 +1614,8 @@ BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lps
     BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
     DWORD_PTR dwContext)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
     if (!lpszRemoteFile || !lpszNewFile)
@@ -1426,7 +1624,7 @@ BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lps
         return FALSE;
     }
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hInternet );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hInternet );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -1460,14 +1658,14 @@ BOOL WINAPI FtpGetFileW(HINTERNET hInternet, LPCWSTR lpszRemoteFile, LPCWSTR lps
         workRequest.asyncproc = AsyncFtpGetFileProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpGetFileW;
-        req->lpszRemoteFile = WININET_strdupW(lpszRemoteFile);
-        req->lpszNewFile = WININET_strdupW(lpszNewFile);
+        req->lpszRemoteFile = heap_strdupW(lpszRemoteFile);
+        req->lpszNewFile = heap_strdupW(lpszNewFile);
        req->dwLocalFlagsAttribute = dwLocalFlagsAttribute;
        req->fFailIfExists = fFailIfExists;
        req->dwFlags = dwInternetFlags;
        req->dwContext = dwContext;
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -1492,13 +1690,13 @@ lend:
  *    FALSE on failure
  *
  */
-static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
+static BOOL FTP_FtpGetFileW(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, LPCWSTR lpszNewFile,
        BOOL fFailIfExists, DWORD dwLocalFlagsAttribute, DWORD dwInternetFlags,
        DWORD_PTR dwContext)
 {
     BOOL bSuccess = FALSE;
     HANDLE hFile;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
 
     TRACE("lpszRemoteFile(%s) lpszNewFile(%s)\n", debugstr_w(lpszRemoteFile), debugstr_w(lpszNewFile));
 
@@ -1537,7 +1735,10 @@ static BOOL FTP_FtpGetFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile,
     }
 
     if (lpwfs->lstnSocket != -1)
+    {
         closesocket(lpwfs->lstnSocket);
+        lpwfs->lstnSocket = -1;
+    }
 
     CloseHandle(hFile);
 
@@ -1584,7 +1785,7 @@ BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
     LPWSTR lpwzFileName;
     BOOL ret;
     
-    lpwzFileName = lpszFileName?WININET_strdup_AtoW(lpszFileName):NULL;
+    lpwzFileName = heap_strdupAtoW(lpszFileName);
     ret = FtpDeleteFileW(hFtpSession, lpwzFileName);
     HeapFree(GetProcessHeap(), 0, lpwzFileName);
     return ret;
@@ -1593,7 +1794,7 @@ BOOL WINAPI FtpDeleteFileA(HINTERNET hFtpSession, LPCSTR lpszFileName)
 static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPDELETEFILEW const *req = &workRequest->u.FtpDeleteFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -1613,11 +1814,11 @@ static void AsyncFtpDeleteFileProc(WORKREQUEST *workRequest)
  */
 BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -1651,9 +1852,9 @@ BOOL WINAPI FtpDeleteFileW(HINTERNET hFtpSession, LPCWSTR lpszFileName)
         workRequest.asyncproc = AsyncFtpDeleteFileProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpDeleteFileW;
-        req->lpszFilename = WININET_strdupW(lpszFileName);
+        req->lpszFilename = heap_strdupW(lpszFileName);
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -1676,11 +1877,11 @@ lend:
  *    FALSE on failure
  *
  */
-BOOL FTP_FtpDeleteFileW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszFileName)
+BOOL FTP_FtpDeleteFileW(ftp_session_t *lpwfs, LPCWSTR lpszFileName)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
 
     TRACE("%p\n", lpwfs);
 
@@ -1729,7 +1930,7 @@ BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
     LPWSTR lpwzDirectory;
     BOOL ret;
     
-    lpwzDirectory = lpszDirectory?WININET_strdup_AtoW(lpszDirectory):NULL;
+    lpwzDirectory = heap_strdupAtoW(lpszDirectory);
     ret = FtpRemoveDirectoryW(hFtpSession, lpwzDirectory);
     HeapFree(GetProcessHeap(), 0, lpwzDirectory);
     return ret;
@@ -1738,7 +1939,7 @@ BOOL WINAPI FtpRemoveDirectoryA(HINTERNET hFtpSession, LPCSTR lpszDirectory)
 static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPREMOVEDIRECTORYW const *req = &workRequest->u.FtpRemoveDirectoryW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -1758,11 +1959,11 @@ static void AsyncFtpRemoveDirectoryProc(WORKREQUEST *workRequest)
  */
 BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -1796,9 +1997,9 @@ BOOL WINAPI FtpRemoveDirectoryW(HINTERNET hFtpSession, LPCWSTR lpszDirectory)
         workRequest.asyncproc = AsyncFtpRemoveDirectoryProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpRemoveDirectoryW;
-        req->lpszDirectory = WININET_strdupW(lpszDirectory);
+        req->lpszDirectory = heap_strdupW(lpszDirectory);
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -1821,11 +2022,11 @@ lend:
  *    FALSE on failure
  *
  */
-BOOL FTP_FtpRemoveDirectoryW(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszDirectory)
+BOOL FTP_FtpRemoveDirectoryW(ftp_session_t *lpwfs, LPCWSTR lpszDirectory)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
 
     TRACE("\n");
 
@@ -1876,8 +2077,8 @@ BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDes
     LPWSTR lpwzDest;
     BOOL ret;
     
-    lpwzSrc = lpszSrc?WININET_strdup_AtoW(lpszSrc):NULL;
-    lpwzDest = lpszDest?WININET_strdup_AtoW(lpszDest):NULL;
+    lpwzSrc = heap_strdupAtoW(lpszSrc);
+    lpwzDest = heap_strdupAtoW(lpszDest);
     ret = FtpRenameFileW(hFtpSession, lpwzSrc, lpwzDest);
     HeapFree(GetProcessHeap(), 0, lpwzSrc);
     HeapFree(GetProcessHeap(), 0, lpwzDest);
@@ -1887,7 +2088,7 @@ BOOL WINAPI FtpRenameFileA(HINTERNET hFtpSession, LPCSTR lpszSrc, LPCSTR lpszDes
 static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_FTPRENAMEFILEW const *req = &workRequest->u.FtpRenameFileW;
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest->hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) workRequest->hdr;
 
     TRACE("%p\n", lpwfs);
 
@@ -1908,11 +2109,11 @@ static void AsyncFtpRenameFileProc(WORKREQUEST *workRequest)
  */
 BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszDest)
 {
-    LPWININETFTPSESSIONW lpwfs;
-    LPWININETAPPINFOW hIC = NULL;
+    ftp_session_t *lpwfs;
+    appinfo_t *hIC = NULL;
     BOOL r = FALSE;
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hFtpSession );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hFtpSession );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -1946,10 +2147,10 @@ BOOL WINAPI FtpRenameFileW(HINTERNET hFtpSession, LPCWSTR lpszSrc, LPCWSTR lpszD
         workRequest.asyncproc = AsyncFtpRenameFileProc;
         workRequest.hdr = WININET_AddRef( &lpwfs->hdr );
         req = &workRequest.u.FtpRenameFileW;
-        req->lpszSrcFile = WININET_strdupW(lpszSrc);
-        req->lpszDestFile = WININET_strdupW(lpszDest);
+        req->lpszSrcFile = heap_strdupW(lpszSrc);
+        req->lpszDestFile = heap_strdupW(lpszDest);
 
-       r = INTERNET_AsyncCall(&workRequest);
+       r = res_to_le(INTERNET_AsyncCall(&workRequest));
     }
     else
     {
@@ -1972,12 +2173,11 @@ lend:
  *    FALSE on failure
  *
  */
-BOOL FTP_FtpRenameFileW( LPWININETFTPSESSIONW lpwfs,
-                         LPCWSTR lpszSrc, LPCWSTR lpszDest)
+BOOL FTP_FtpRenameFileW(ftp_session_t *lpwfs, LPCWSTR lpszSrc, LPCWSTR lpszDest)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
-    LPWININETAPPINFOW hIC = NULL;
+    appinfo_t *hIC = NULL;
 
     TRACE("\n");
 
@@ -2040,7 +2240,7 @@ BOOL WINAPI FtpCommandA( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags
         return FALSE;
     }
 
-    if (!(cmdW = WININET_strdup_AtoW(lpszCommand)))
+    if (!(cmdW = heap_strdupAtoW(lpszCommand)))
     {
         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
         return FALSE;
@@ -2059,7 +2259,7 @@ BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags
                          LPCWSTR lpszCommand, DWORD_PTR dwContext, HINTERNET* phFtpCommand )
 {
     BOOL r = FALSE;
-    LPWININETFTPSESSIONW lpwfs;
+    ftp_session_t *lpwfs;
     LPSTR cmd = NULL;
     DWORD len, nBytesSent= 0;
     INT nResCode, nRC = 0;
@@ -2079,7 +2279,7 @@ BOOL WINAPI FtpCommandW( HINTERNET hConnect, BOOL fExpectResponse, DWORD dwFlags
         return FALSE;
     }
 
-    lpwfs = (LPWININETFTPSESSIONW) WININET_GetObject( hConnect );
+    lpwfs = (ftp_session_t*) WININET_GetObject( hConnect );
     if (!lpwfs)
     {
         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
@@ -2142,9 +2342,9 @@ lend:
  *
  * Deallocate session handle
  */
-static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
+static void FTPSESSION_Destroy(object_header_t *hdr)
 {
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) hdr;
 
     TRACE("\n");
 
@@ -2152,12 +2352,13 @@ static void FTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
 
     HeapFree(GetProcessHeap(), 0, lpwfs->lpszPassword);
     HeapFree(GetProcessHeap(), 0, lpwfs->lpszUserName);
+    HeapFree(GetProcessHeap(), 0, lpwfs->servername);
     HeapFree(GetProcessHeap(), 0, lpwfs);
 }
 
-static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
+static void FTPSESSION_CloseConnection(object_header_t *hdr)
 {
-    LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) hdr;
+    ftp_session_t *lpwfs = (ftp_session_t*) hdr;
 
     TRACE("\n");
 
@@ -2180,7 +2381,7 @@ static void FTPSESSION_CloseConnection(WININETHANDLEHEADER *hdr)
                       INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
 }
 
-static DWORD FTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static DWORD FTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
     switch(option) {
     case INTERNET_OPTION_HANDLE_TYPE:
@@ -2197,7 +2398,7 @@ static DWORD FTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void
     return INET_QueryOption(option, buffer, size, unicode);
 }
 
-static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
+static const object_vtbl_t FTPSESSIONVtbl = {
     FTPSESSION_Destroy,
     FTPSESSION_CloseConnection,
     FTPSESSION_QueryOption,
@@ -2230,7 +2431,7 @@ static const HANDLEHEADERVtbl FTPSESSIONVtbl = {
  *
  */
 
-HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
+HINTERNET FTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
        INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
        LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
        DWORD dwInternalFlags)
@@ -2247,8 +2448,9 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     INT nsocket = -1;
     UINT sock_namelen;
     BOOL bSuccess = FALSE;
-    LPWININETFTPSESSIONW lpwfs = NULL;
+    ftp_session_t *lpwfs = NULL;
     HINTERNET handle = NULL;
+    char szaddr[INET_ADDRSTRLEN];
 
     TRACE("%p  Server(%s) Port(%d) User(%s) Paswd(%s)\n",
            hIC, debugstr_w(lpszServerName),
@@ -2256,13 +2458,13 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
 
     assert( hIC->hdr.htype == WH_HINIT );
 
-    if (NULL == lpszUserName && NULL != lpszPassword)
+    if ((!lpszUserName || !*lpszUserName) && lpszPassword && *lpszPassword)
     {
        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
         goto lerror;
     }
     
-    lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETFTPSESSIONW));
+    lpwfs = HeapAlloc(GetProcessHeap(), 0, sizeof(ftp_session_t));
     if (NULL == lpwfs)
     {
         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
@@ -2270,7 +2472,9 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     }
 
     if (nServerPort == INTERNET_INVALID_PORT_NUMBER)
-       nServerPort = INTERNET_DEFAULT_FTP_PORT;
+        lpwfs->serverport = INTERNET_DEFAULT_FTP_PORT;
+    else
+        lpwfs->serverport = nServerPort;
 
     lpwfs->hdr.htype = WH_HFTPSESSION;
     lpwfs->hdr.vtbl = &FTPSESSIONVtbl;
@@ -2302,12 +2506,12 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
         if(hIC->lpszProxyBypass)
             FIXME("Proxy bypass is ignored.\n");
     }
-    if ( !lpszUserName) {
+    if (!lpszUserName || !strlenW(lpszUserName)) {
         HKEY key;
         WCHAR szPassword[MAX_PATH];
         DWORD len = sizeof(szPassword);
 
-        lpwfs->lpszUserName = WININET_strdupW(szDefaultUsername);
+        lpwfs->lpszUserName = heap_strdupW(szDefaultUsername);
 
         RegOpenKeyW(HKEY_CURRENT_USER, szKey, &key);
         if (RegQueryValueExW(key, szValue, NULL, NULL, (LPBYTE)szPassword, &len)) {
@@ -2320,23 +2524,20 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
         RegCloseKey(key);
 
         TRACE("Password used for anonymous ftp : (%s)\n", debugstr_w(szPassword));
-        lpwfs->lpszPassword = WININET_strdupW(szPassword);
+        lpwfs->lpszPassword = heap_strdupW(szPassword);
     }
     else {
-        lpwfs->lpszUserName = WININET_strdupW(lpszUserName);
-
-        if (lpszPassword)
-            lpwfs->lpszPassword = WININET_strdupW(lpszPassword);
-        else
-            lpwfs->lpszPassword = WININET_strdupW(szEmpty);
+        lpwfs->lpszUserName = heap_strdupW(lpszUserName);
+        lpwfs->lpszPassword = heap_strdupW(lpszPassword ? lpszPassword : szEmpty);
     }
+    lpwfs->servername = heap_strdupW(lpszServerName);
     
     /* Don't send a handle created callback if this handle was created with InternetOpenUrl */
     if (!(lpwfs->hdr.dwInternalFlags & INET_OPENURL))
     {
         INTERNET_ASYNC_RESULT iar;
 
-        iar.dwResult = (DWORD)handle;
+        iar.dwResult = (DWORD_PTR)handle;
         iar.dwError = ERROR_SUCCESS;
 
         SendAsyncCallback(&hIC->hdr, dwContext,
@@ -2345,16 +2546,25 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     }
         
     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_RESOLVING_NAME,
-        (LPWSTR) lpszServerName, strlenW(lpszServerName));
+        (LPWSTR) lpszServerName, (strlenW(lpszServerName)+1) * sizeof(WCHAR));
 
-    if (!GetAddress(lpszServerName, nServerPort, &socketAddr))
+    sock_namelen = sizeof(socketAddr);
+    if (!GetAddress(lpszServerName, lpwfs->serverport, (struct sockaddr *)&socketAddr, &sock_namelen))
     {
        INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
         goto lerror;
     }
 
+    if (socketAddr.sin_family != AF_INET)
+    {
+        WARN("unsupported address family %d\n", socketAddr.sin_family);
+        INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
+        goto lerror;
+    }
+
+    inet_ntop(socketAddr.sin_family, &socketAddr.sin_addr, szaddr, sizeof(szaddr));
     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_NAME_RESOLVED,
-        (LPWSTR) lpszServerName, strlenW(lpszServerName));
+                      szaddr, strlen(szaddr)+1);
 
     nsocket = socket(AF_INET,SOCK_STREAM,0);
     if (nsocket == -1)
@@ -2364,19 +2574,20 @@ HINTERNET FTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     }
 
     SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTING_TO_SERVER,
-        &socketAddr, sizeof(struct sockaddr_in));
+                      szaddr, strlen(szaddr)+1);
 
-    if (connect(nsocket, (struct sockaddr *)&socketAddr, sizeof(socketAddr)) < 0)
+    if (connect(nsocket, (struct sockaddr *)&socketAddr, sock_namelen) < 0)
     {
        ERR("Unable to connect (%s)\n", strerror(errno));
        INTERNET_SetLastError(ERROR_INTERNET_CANNOT_CONNECT);
+       closesocket(nsocket);
     }
     else
     {
         TRACE("Connected to server\n");
        lpwfs->sndSocket = nsocket;
         SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_CONNECTED_TO_SERVER,
-            &socketAddr, sizeof(struct sockaddr_in));
+                          szaddr, strlen(szaddr)+1);
 
        sock_namelen = sizeof(lpwfs->socketAddress);
        getsockname(nsocket, (struct sockaddr *) &lpwfs->socketAddress, &sock_namelen);
@@ -2397,16 +2608,6 @@ lerror:
         handle = NULL;
     }
 
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
-    {
-        INTERNET_ASYNC_RESULT iar;
-
-        iar.dwResult = bSuccess ? (DWORD_PTR)lpwfs : 0;
-        iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
-        SendAsyncCallback(&hIC->hdr, dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
-            &iar, sizeof(INTERNET_ASYNC_RESULT));
-    }
-
     return handle;
 }
 
@@ -2421,7 +2622,7 @@ lerror:
  *   NULL on failure
  *
  */
-static BOOL FTP_ConnectToHost(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_ConnectToHost(ftp_session_t *lpwfs)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2465,7 +2666,7 @@ lend:
  *
  */
 static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
-       INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
+       INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
 {
        DWORD len;
        CHAR *buf;
@@ -2520,10 +2721,10 @@ static BOOL FTP_SendCommandA(INT nSocket, FTP_COMMAND ftpCmd, LPCSTR lpszParam,
  *
  */
 static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
-       INTERNET_STATUS_CALLBACK lpfnStatusCB, LPWININETHANDLEHEADER hdr, DWORD_PTR dwContext)
+       INTERNET_STATUS_CALLBACK lpfnStatusCB, object_header_t *hdr, DWORD_PTR dwContext)
 {
     BOOL ret;
-    LPSTR lpszParamA = lpszParam?WININET_strdup_WtoA(lpszParam):NULL;
+    LPSTR lpszParamA = heap_strdupWtoA(lpszParam);
     ret = FTP_SendCommandA(nSocket, ftpCmd, lpszParamA, lpfnStatusCB, hdr, dwContext);
     HeapFree(GetProcessHeap(), 0, lpszParamA);
     return ret;
@@ -2539,7 +2740,7 @@ static BOOL FTP_SendCommand(INT nSocket, FTP_COMMAND ftpCmd, LPCWSTR lpszParam,
  *   0 on failure
  *
  */
-INT FTP_ReceiveResponse(LPWININETFTPSESSIONW lpwfs, DWORD_PTR dwContext)
+INT FTP_ReceiveResponse(ftp_session_t *lpwfs, DWORD_PTR dwContext)
 {
     LPSTR lpszResponse = INTERNET_GetResponseBuffer();
     DWORD nRecv;
@@ -2602,7 +2803,7 @@ lerror:
  *   NULL on failure
  *
  */
-static BOOL FTP_SendPassword(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_SendPassword(ftp_session_t *lpwfs)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2642,7 +2843,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_SendAccount(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_SendAccount(ftp_session_t *lpwfs)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2672,7 +2873,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_SendStore(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
+static BOOL FTP_SendStore(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2719,10 +2920,10 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_InitListenSocket(ftp_session_t *lpwfs)
 {
     BOOL bSuccess = FALSE;
-    socklen_t namelen = sizeof(struct sockaddr_in);
+    socklen_t namelen = sizeof(lpwfs->lstnSocketAddress);
 
     TRACE("\n");
 
@@ -2737,9 +2938,9 @@ static BOOL FTP_InitListenSocket(LPWININETFTPSESSIONW lpwfs)
     lpwfs->lstnSocketAddress = lpwfs->socketAddress;
 
     /* and get the system to assign us a port */
-    lpwfs->lstnSocketAddress.sin_port = htons((u_short) 0);
+    lpwfs->lstnSocketAddress.sin_port = htons(0);
 
-    if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(struct sockaddr_in)) == -1)
+    if (bind(lpwfs->lstnSocket,(struct sockaddr *) &lpwfs->lstnSocketAddress, sizeof(lpwfs->lstnSocketAddress)) == -1)
     {
         TRACE("Unable to bind socket\n");
         goto lend;
@@ -2778,7 +2979,7 @@ lend:
  * (i.e. it sends it always),
  * so we probably don't want to do that either.
  */
-static BOOL FTP_SendType(LPWININETFTPSESSIONW lpwfs, DWORD dwType)
+static BOOL FTP_SendType(ftp_session_t *lpwfs, DWORD dwType)
 {
     INT nResCode;
     WCHAR type[] = { 'I','\0' };
@@ -2816,7 +3017,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_GetFileSize(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
+static BOOL FTP_GetFileSize(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD *dwSize)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2860,7 +3061,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_SendPort(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_SendPort(ftp_session_t *lpwfs)
 {
     static const WCHAR szIPFormat[] = {'%','d',',','%','d',',','%','d',',','%','d',',','%','d',',','%','d','\0'};
     INT nResCode;
@@ -2904,7 +3105,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_DoPassive(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_DoPassive(ftp_session_t *lpwfs)
 {
     INT nResCode;
     BOOL bSuccess = FALSE;
@@ -2976,7 +3177,7 @@ lend:
 }
 
 
-static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
+static BOOL FTP_SendPortOrPasv(ftp_session_t *lpwfs)
 {
     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
     {
@@ -3005,7 +3206,7 @@ static BOOL FTP_SendPortOrPasv(LPWININETFTPSESSIONW lpwfs)
  *   FALSE on failure
  *
  */
-static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
+static BOOL FTP_GetDataSocket(ftp_session_t *lpwfs, LPINT nDataSocket)
 {
     struct sockaddr_in saddr;
     socklen_t addrlen = sizeof(struct sockaddr);
@@ -3014,6 +3215,7 @@ static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
     if (lpwfs->hdr.dwFlags & INTERNET_FLAG_PASSIVE)
     {
        *nDataSocket = lpwfs->pasvSocket;
+       lpwfs->pasvSocket = -1;
     }
     else
     {
@@ -3035,7 +3237,7 @@ static BOOL FTP_GetDataSocket(LPWININETFTPSESSIONW lpwfs, LPINT nDataSocket)
  *   FALSE on failure
  *
  */
-static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
+static BOOL FTP_SendData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
 {
     BY_HANDLE_FILE_INFORMATION fi;
     DWORD nBytesRead = 0;
@@ -3116,7 +3318,7 @@ static BOOL FTP_SendData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFi
  *   0 on failure
  *
  */
-static BOOL FTP_SendRetrieve(LPWININETFTPSESSIONW lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
+static BOOL FTP_SendRetrieve(ftp_session_t *lpwfs, LPCWSTR lpszRemoteFile, DWORD dwType)
 {
     INT nResCode;
     BOOL ret;
@@ -3162,7 +3364,7 @@ lend:
  *   FALSE on failure
  *
  */
-static BOOL FTP_RetrieveFileData(LPWININETFTPSESSIONW lpwfs, INT nDataSocket, HANDLE hFile)
+static BOOL FTP_RetrieveFileData(ftp_session_t *lpwfs, INT nDataSocket, HANDLE hFile)
 {
     DWORD nBytesWritten;
     INT nRC = 0;
@@ -3202,7 +3404,7 @@ recv_end:
  *
  * Deallocate session handle
  */
-static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
+static void FTPFINDNEXT_Destroy(object_header_t *hdr)
 {
     LPWININETFTPFINDNEXTW lpwfn = (LPWININETFTPFINDNEXTW) hdr;
     DWORD i;
@@ -3220,7 +3422,7 @@ static void FTPFINDNEXT_Destroy(WININETHANDLEHEADER *hdr)
     HeapFree(GetProcessHeap(), 0, lpwfn);
 }
 
-static DWORD WINAPI FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
+static DWORD FTPFINDNEXT_FindNextFileProc(WININETFTPFINDNEXTW *find, LPVOID data)
 {
     WIN32_FIND_DATAW *find_data = data;
     DWORD res = ERROR_SUCCESS;
@@ -3260,7 +3462,7 @@ static void FTPFINDNEXT_AsyncFindNextFileProc(WORKREQUEST *workRequest)
     FTPFINDNEXT_FindNextFileProc((WININETFTPFINDNEXTW*)workRequest->hdr, req->lpFindFileData);
 }
 
-static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static DWORD FTPFINDNEXT_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
     switch(option) {
     case INTERNET_OPTION_HANDLE_TYPE:
@@ -3277,7 +3479,7 @@ static DWORD FTPFINDNEXT_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, voi
     return INET_QueryOption(option, buffer, size, unicode);
 }
 
-static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
+static DWORD FTPFINDNEXT_FindNextFileW(object_header_t *hdr, void *data)
 {
     WININETFTPFINDNEXTW *find = (WININETFTPFINDNEXTW*)hdr;
 
@@ -3299,7 +3501,7 @@ static DWORD FTPFINDNEXT_FindNextFileW(WININETHANDLEHEADER *hdr, void *data)
     return FTPFINDNEXT_FindNextFileProc(find, data);
 }
 
-static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
+static const object_vtbl_t FTPFINDNEXTVtbl = {
     FTPFINDNEXT_Destroy,
     NULL,
     FTPFINDNEXT_QueryOption,
@@ -3308,6 +3510,7 @@ static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
     NULL,
     NULL,
     NULL,
+    NULL,
     FTPFINDNEXT_FindNextFileW
 };
 
@@ -3321,7 +3524,7 @@ static const HANDLEHEADERVtbl FTPFINDNEXTVtbl = {
  *   NULL on failure
  *
  */
-static HINTERNET FTP_ReceiveFileList(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
+static HINTERNET FTP_ReceiveFileList(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
        LPWIN32_FIND_DATAW lpFindFileData, DWORD_PTR dwContext)
 {
     DWORD dwSize = 0;
@@ -3382,9 +3585,7 @@ static BOOL FTP_ConvertFileProp(LPFILEPROPERTIESW lpafp, LPWIN32_FIND_DATAW lpFi
 
     if (lpafp)
     {
-       /* Convert 'Unix' time to Windows time */
-       RtlSecondsSince1970ToTime(mktime(&lpafp->tmLastModified),
-                                 (LARGE_INTEGER *) &(lpFindFileData->ftLastAccessTime));
+        SystemTimeToFileTime( &lpafp->tmLastModified, &lpFindFileData->ftLastAccessTime );
        lpFindFileData->ftLastWriteTime = lpFindFileData->ftLastAccessTime;
        lpFindFileData->ftCreationTime = lpFindFileData->ftLastAccessTime;
        
@@ -3452,12 +3653,12 @@ static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERT
                 lpfp->nSize = atol(pszToken);
             }
             
-            lpfp->tmLastModified.tm_sec  = 0;
-            lpfp->tmLastModified.tm_min  = 0;
-            lpfp->tmLastModified.tm_hour = 0;
-            lpfp->tmLastModified.tm_mday = 0;
-            lpfp->tmLastModified.tm_mon  = 0;
-            lpfp->tmLastModified.tm_year = 0;
+            lpfp->tmLastModified.wSecond = 0;
+            lpfp->tmLastModified.wMinute = 0;
+            lpfp->tmLastModified.wHour   = 0;
+            lpfp->tmLastModified.wDay    = 0;
+            lpfp->tmLastModified.wMonth  = 0;
+            lpfp->tmLastModified.wYear   = 0;
             
             /* Determine month */
             pszToken = strtok(NULL, szSpace);
@@ -3465,38 +3666,35 @@ static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERT
             if(strlen(pszToken) >= 3) {
                 pszToken[3] = 0;
                 if((pszTmp = StrStrIA(szMonths, pszToken)))
-                    lpfp->tmLastModified.tm_mon = ((pszTmp - szMonths) / 3)+1;
+                    lpfp->tmLastModified.wMonth = ((pszTmp - szMonths) / 3)+1;
             }
             /* Determine day */
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
-            lpfp->tmLastModified.tm_mday = atoi(pszToken);
+            lpfp->tmLastModified.wDay = atoi(pszToken);
             /* Determine time or year */
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
             if((pszTmp = strchr(pszToken, ':'))) {
-                struct tm* apTM;
-                time_t aTime;
+                SYSTEMTIME curr_time;
                 *pszTmp = 0;
                 pszTmp++;
-                lpfp->tmLastModified.tm_min = atoi(pszTmp);
-                lpfp->tmLastModified.tm_hour = atoi(pszToken);
-                time(&aTime);
-                apTM = localtime(&aTime);
-                lpfp->tmLastModified.tm_year = apTM->tm_year;
+                lpfp->tmLastModified.wMinute = atoi(pszTmp);
+                lpfp->tmLastModified.wHour = atoi(pszToken);
+                GetLocalTime( &curr_time );
+                lpfp->tmLastModified.wYear = curr_time.wYear;
             }
             else {
-                lpfp->tmLastModified.tm_year = atoi(pszToken) - 1900;
-                lpfp->tmLastModified.tm_hour = 12;
+                lpfp->tmLastModified.wYear = atoi(pszToken);
+                lpfp->tmLastModified.wHour = 12;
             }
-            TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
-                lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
-                (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
-                lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
+            TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
+                  lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
+                  lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
 
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
-            lpfp->lpszName = WININET_strdup_AtoW(pszToken);
+            lpfp->lpszName = heap_strdupAtoW(pszToken);
             TRACE("File: %s\n", debugstr_w(lpfp->lpszName));
         }
         /* NT way of parsing ... :
@@ -3505,32 +3703,31 @@ static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERT
                 05-09-03  06:02PM             12656686 2003-04-21bgm_cmd_e.rgz
         */
         else if(isdigit(pszToken[0]) && 8 == strlen(pszToken)) {
+            int mon, mday, year, hour, min;
             lpfp->permissions = 0xFFFF; /* No idea, put full permission :-) */
             
-            sscanf(pszToken, "%d-%d-%d",
-                &lpfp->tmLastModified.tm_mon,
-                &lpfp->tmLastModified.tm_mday,
-                &lpfp->tmLastModified.tm_year);
+            sscanf(pszToken, "%d-%d-%d", &mon, &mday, &year);
+            lpfp->tmLastModified.wDay   = mday;
+            lpfp->tmLastModified.wMonth = mon;
+            lpfp->tmLastModified.wYear  = year;
 
             /* Hacky and bad Y2K protection :-) */
-            if (lpfp->tmLastModified.tm_year < 70)
-                lpfp->tmLastModified.tm_year += 100;
-            
+            if (lpfp->tmLastModified.wYear < 70) lpfp->tmLastModified.wYear += 2000;
+
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
-            sscanf(pszToken, "%d:%d",
-                &lpfp->tmLastModified.tm_hour,
-                &lpfp->tmLastModified.tm_min);
+            sscanf(pszToken, "%d:%d", &hour, &min);
+            lpfp->tmLastModified.wHour   = hour;
+            lpfp->tmLastModified.wMinute = min;
             if((pszToken[5] == 'P') && (pszToken[6] == 'M')) {
-                lpfp->tmLastModified.tm_hour += 12;
+                lpfp->tmLastModified.wHour += 12;
             }
-            lpfp->tmLastModified.tm_sec = 0;
+            lpfp->tmLastModified.wSecond = 0;
+
+            TRACE("Mod time: %02d:%02d:%02d  %04d/%02d/%02d\n",
+                  lpfp->tmLastModified.wHour, lpfp->tmLastModified.wMinute, lpfp->tmLastModified.wSecond,
+                  lpfp->tmLastModified.wYear, lpfp->tmLastModified.wMonth, lpfp->tmLastModified.wDay);
 
-            TRACE("Mod time: %02d:%02d:%02d  %02d/%02d/%02d\n",
-                lpfp->tmLastModified.tm_hour, lpfp->tmLastModified.tm_min, lpfp->tmLastModified.tm_sec,
-                (lpfp->tmLastModified.tm_year >= 100) ? lpfp->tmLastModified.tm_year - 100 : lpfp->tmLastModified.tm_year,
-                lpfp->tmLastModified.tm_mon, lpfp->tmLastModified.tm_mday);
-            
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
             if(!strcasecmp(pszToken, "<DIR>")) {
@@ -3546,7 +3743,7 @@ static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERT
             
             pszToken = strtok(NULL, szSpace);
             if(!pszToken) continue;
-            lpfp->lpszName = WININET_strdup_AtoW(pszToken);
+            lpfp->lpszName = heap_strdupAtoW(pszToken);
             TRACE("Name: %s\n", debugstr_w(lpfp->lpszName));
         }
         /* EPLF format - http://cr.yp.to/ftp/list/eplf.html */
@@ -3578,7 +3775,7 @@ static BOOL FTP_ParseNextFile(INT nSocket, LPCWSTR lpszSearchFile, LPFILEPROPERT
  *   TRUE on success
  *   FALSE on failure
  */
-static BOOL FTP_ParseDirectory(LPWININETFTPSESSIONW lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
+static BOOL FTP_ParseDirectory(ftp_session_t *lpwfs, INT nSocket, LPCWSTR lpszSearchFile,
     LPFILEPROPERTIESW *lpafp, LPDWORD dwfp)
 {
     BOOL bSuccess = TRUE;
index f0ad6be..5b7459d 100644 (file)
 #include "config.h"
 #include "wine/port.h"
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <sys/types.h>
 #ifdef HAVE_SYS_SOCKET_H
 # include <sys/socket.h>
@@ -44,6 +48,9 @@
 #endif
 #include <time.h>
 #include <assert.h>
+#ifdef HAVE_ZLIB
+#  include <zlib.h>
+#endif
 
 #include "windef.h"
 #include "winbase.h"
@@ -59,6 +66,7 @@
 
 #include "internet.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 #include "wine/unicode.h"
 
 #include "inet_ntop.c"
@@ -67,23 +75,77 @@ WINE_DEFAULT_DEBUG_CHANNEL(wininet);
 
 static const WCHAR g_szHttp1_0[] = {'H','T','T','P','/','1','.','0',0};
 static const WCHAR g_szHttp1_1[] = {'H','T','T','P','/','1','.','1',0};
-static const WCHAR g_szReferer[] = {'R','e','f','e','r','e','r',0};
-static const WCHAR g_szAccept[] = {'A','c','c','e','p','t',0};
-static const WCHAR g_szUserAgent[] = {'U','s','e','r','-','A','g','e','n','t',0};
-static const WCHAR szHost[] = { 'H','o','s','t',0 };
+static const WCHAR szOK[] = {'O','K',0};
+static const WCHAR szDefaultHeader[] = {'H','T','T','P','/','1','.','0',' ','2','0','0',' ','O','K',0};
+static const WCHAR hostW[] = { 'H','o','s','t',0 };
 static const WCHAR szAuthorization[] = { 'A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 static const WCHAR szProxy_Authorization[] = { 'P','r','o','x','y','-','A','u','t','h','o','r','i','z','a','t','i','o','n',0 };
 static const WCHAR szStatus[] = { 'S','t','a','t','u','s',0 };
 static const WCHAR szKeepAlive[] = {'K','e','e','p','-','A','l','i','v','e',0};
 static const WCHAR szGET[] = { 'G','E','T', 0 };
+static const WCHAR szHEAD[] = { 'H','E','A','D', 0 };
+static const WCHAR szCrLf[] = {'\r','\n', 0};
+
+static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
+static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
+static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
+static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
+static const WCHAR szAge[] = { 'A','g','e',0 };
+static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
+static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
+static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
+static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
+static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
+static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
+static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
+static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
+static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
+static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
+static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
+static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
+static const WCHAR szDate[] = { 'D','a','t','e',0 };
+static const WCHAR szFrom[] = { 'F','r','o','m',0 };
+static const WCHAR szETag[] = { 'E','T','a','g',0 };
+static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
+static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
+static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
+static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
+static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
+static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
+static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
+static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
+static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
+static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
+static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
+static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
+static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
+static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
+static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
+static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
+static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
+static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
+static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
+static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
+static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
+static const WCHAR szURI[] = { 'U','R','I',0 };
+static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
+static const WCHAR szVary[] = { 'V','a','r','y',0 };
+static const WCHAR szVia[] = { 'V','i','a',0 };
+static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
+static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
 
 #define MAXHOSTNAME 100
 #define MAX_FIELD_VALUE_LEN 256
 #define MAX_FIELD_LEN 256
 
-#define HTTP_REFERER    g_szReferer
-#define HTTP_ACCEPT     g_szAccept
-#define HTTP_USERAGENT  g_szUserAgent
+#define HTTP_REFERER    szReferer
+#define HTTP_ACCEPT     szAccept
+#define HTTP_USERAGENT  szUser_Agent
 
 #define HTTP_ADDHDR_FLAG_ADD                           0x20000000
 #define HTTP_ADDHDR_FLAG_ADD_IF_NEW                    0x10000000
@@ -108,23 +170,54 @@ struct HttpAuthInfo
     BOOL finished; /* finished authenticating */
 };
 
-static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr);
-static BOOL HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear);
-static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
+
+struct gzip_stream_t {
+#ifdef HAVE_ZLIB
+    z_stream zstream;
+#endif
+    BYTE buf[8192];
+    DWORD buf_size;
+    DWORD buf_pos;
+    BOOL end_of_data;
+};
+
+typedef struct _authorizationData
+{
+    struct list entry;
+
+    LPWSTR lpszwHost;
+    LPWSTR lpszwRealm;
+    LPSTR  lpszAuthorization;
+    UINT   AuthorizationLen;
+} authorizationData;
+
+static struct list basicAuthorizationCache = LIST_INIT(basicAuthorizationCache);
+
+static CRITICAL_SECTION authcache_cs;
+static CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &authcache_cs,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": authcache_cs") }
+};
+static CRITICAL_SECTION authcache_cs = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+static DWORD HTTP_OpenConnection(http_request_t *req);
+static BOOL HTTP_GetResponseHeaders(http_request_t *req, BOOL clear);
+static DWORD HTTP_ProcessHeader(http_request_t *req, LPCWSTR field, LPCWSTR value, DWORD dwModifier);
 static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer);
-static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr);
-static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField, INT index, BOOL Request);
-static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index);
+static DWORD HTTP_InsertCustomHeader(http_request_t *req, LPHTTPHEADERW lpHdr);
+static INT HTTP_GetCustomHeaderIndex(http_request_t *req, LPCWSTR lpszField, INT index, BOOL Request);
+static BOOL HTTP_DeleteCustomHeader(http_request_t *req, DWORD index);
 static LPWSTR HTTP_build_req( LPCWSTR *list, int len );
-static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD
-        dwInfoLevel, LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD
-        lpdwIndex);
-static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl);
+static DWORD HTTP_HttpQueryInfoW(http_request_t*, DWORD, LPVOID, LPDWORD, LPDWORD);
+static LPWSTR HTTP_GetRedirectURL(http_request_t *req, LPCWSTR lpszUrl);
 static UINT HTTP_DecodeBase64(LPCWSTR base64, LPSTR bin);
-static BOOL HTTP_VerifyValidHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field);
-static void HTTP_DrainContent(WININETHTTPREQW *req);
+static BOOL HTTP_VerifyValidHeader(http_request_t *req, LPCWSTR field);
+static void HTTP_DrainContent(http_request_t *req);
+static BOOL HTTP_FinishedReading(http_request_t *req);
 
-LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
+static LPHTTPHEADERW HTTP_GetHeader(http_request_t *req, LPCWSTR head)
 {
     int HeaderIndex = 0;
     HeaderIndex = HTTP_GetCustomHeaderIndex(req, head, 0, TRUE);
@@ -134,6 +227,91 @@ LPHTTPHEADERW HTTP_GetHeader(LPWININETHTTPREQW req, LPCWSTR head)
         return &req->pCustHeaders[HeaderIndex];
 }
 
+#ifdef HAVE_ZLIB
+
+static voidpf wininet_zalloc(voidpf opaque, uInt items, uInt size)
+{
+    return HeapAlloc(GetProcessHeap(), 0, items*size);
+}
+
+static void wininet_zfree(voidpf opaque, voidpf address)
+{
+    HeapFree(GetProcessHeap(), 0, address);
+}
+
+static void init_gzip_stream(http_request_t *req)
+{
+    gzip_stream_t *gzip_stream;
+    int index, zres;
+
+    gzip_stream = HeapAlloc(GetProcessHeap(), 0, sizeof(gzip_stream_t));
+    gzip_stream->zstream.zalloc = wininet_zalloc;
+    gzip_stream->zstream.zfree = wininet_zfree;
+    gzip_stream->zstream.opaque = NULL;
+    gzip_stream->zstream.next_in = NULL;
+    gzip_stream->zstream.avail_in = 0;
+    gzip_stream->zstream.next_out = NULL;
+    gzip_stream->zstream.avail_out = 0;
+    gzip_stream->buf_pos = 0;
+    gzip_stream->buf_size = 0;
+    gzip_stream->end_of_data = FALSE;
+
+    zres = inflateInit2(&gzip_stream->zstream, 0x1f);
+    if(zres != Z_OK) {
+        ERR("inflateInit failed: %d\n", zres);
+        HeapFree(GetProcessHeap(), 0, gzip_stream);
+        return;
+    }
+
+    req->gzip_stream = gzip_stream;
+
+    index = HTTP_GetCustomHeaderIndex(req, szContent_Length, 0, FALSE);
+    if(index != -1)
+        HTTP_DeleteCustomHeader(req, index);
+}
+
+#else
+
+static void init_gzip_stream(http_request_t *req)
+{
+    ERR("gzip stream not supported, missing zlib.\n");
+}
+
+#endif
+
+/* set the request content length based on the headers */
+static DWORD set_content_length( http_request_t *lpwhr )
+{
+    static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
+    WCHAR encoding[20];
+    DWORD size;
+
+    size = sizeof(lpwhr->dwContentLength);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
+                            &lpwhr->dwContentLength, &size, NULL) != ERROR_SUCCESS)
+        lpwhr->dwContentLength = ~0u;
+
+    size = sizeof(encoding);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &size, NULL) == ERROR_SUCCESS &&
+        !strcmpiW(encoding, szChunked))
+    {
+        lpwhr->dwContentLength = ~0u;
+        lpwhr->read_chunked = TRUE;
+    }
+
+    if(lpwhr->decoding) {
+        int encoding_idx;
+
+        static const WCHAR gzipW[] = {'g','z','i','p',0};
+
+        encoding_idx = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Encoding, 0, FALSE);
+        if(encoding_idx != -1 && !strcmpiW(lpwhr->pCustHeaders[encoding_idx].lpszValue, gzipW))
+            init_gzip_stream(lpwhr);
+    }
+
+    return lpwhr->dwContentLength;
+}
+
 /***********************************************************************
  *           HTTP_Tokenize (internal)
  *
@@ -146,21 +324,26 @@ static LPWSTR * HTTP_Tokenize(LPCWSTR string, LPCWSTR token_string)
     int i;
     LPCWSTR next_token;
 
-    /* empty string has no tokens */
-    if (*string)
-        tokens++;
-    /* count tokens */
-    for (i = 0; string[i]; i++)
-        if (!strncmpW(string+i, token_string, strlenW(token_string)))
-        {
-            DWORD j;
+    if (string)
+    {
+        /* empty string has no tokens */
+        if (*string)
             tokens++;
-            /* we want to skip over separators, but not the null terminator */
-            for (j = 0; j < strlenW(token_string) - 1; j++)
-                if (!string[i+j])
-                    break;
-            i += j;
+        /* count tokens */
+        for (i = 0; string[i]; i++)
+        {
+            if (!strncmpW(string+i, token_string, strlenW(token_string)))
+            {
+                DWORD j;
+                tokens++;
+                /* we want to skip over separators, but not the null terminator */
+                for (j = 0; j < strlenW(token_string) - 1; j++)
+                    if (!string[i+j])
+                        break;
+                i += j;
+            }
         }
+    }
 
     /* add 1 for terminating NULL */
     token_array = HeapAlloc(GetProcessHeap(), 0, (tokens+1) * sizeof(*token_array));
@@ -194,33 +377,14 @@ static void HTTP_FreeTokens(LPWSTR * token_array)
     HeapFree(GetProcessHeap(), 0, token_array);
 }
 
-/* **********************************************************************
- * 
- * Helper functions for the HttpSendRequest(Ex) functions
- * 
- */
-static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
-{
-    struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
-    LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) workRequest->hdr;
-
-    TRACE("%p\n", lpwhr);
-
-    HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
-            req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
-            req->dwContentLength, req->bEndRequest);
-
-    HeapFree(GetProcessHeap(), 0, req->lpszHeader);
-}
-
-static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
+static void HTTP_FixURL(http_request_t *lpwhr)
 {
     static const WCHAR szSlash[] = { '/',0 };
     static const WCHAR szHttp[] = { 'h','t','t','p',':','/','/', 0 };
 
     /* If we don't have a path we set it to root */
     if (NULL == lpwhr->lpszPath)
-        lpwhr->lpszPath = WININET_strdupW(szSlash);
+        lpwhr->lpszPath = heap_strdupW(szSlash);
     else /* remove \r and \n*/
     {
         int nLen = strlenW(lpwhr->lpszPath);
@@ -249,7 +413,7 @@ static void HTTP_FixURL( LPWININETHTTPREQW lpwhr)
     }
 }
 
-static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
+static LPWSTR HTTP_BuildHeaderRequestString( http_request_t *lpwhr, LPCWSTR verb, LPCWSTR path, LPCWSTR version )
 {
     LPWSTR requestString;
     DWORD len, n;
@@ -258,7 +422,6 @@ static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR ve
     LPWSTR p;
 
     static const WCHAR szSpace[] = { ' ',0 };
-    static const WCHAR szcrlf[] = {'\r','\n', 0};
     static const WCHAR szColon[] = { ':',' ',0 };
     static const WCHAR sztwocrlf[] = {'\r','\n','\r','\n', 0};
 
@@ -279,7 +442,7 @@ static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR ve
     {
         if (lpwhr->pCustHeaders[i].wFlags & HDR_ISREQUEST)
         {
-            req[n++] = szcrlf;
+            req[n++] = szCrLf;
             req[n++] = lpwhr->pCustHeaders[i].lpszField;
             req[n++] = szColon;
             req[n++] = lpwhr->pCustHeaders[i].lpszValue;
@@ -309,103 +472,176 @@ static LPWSTR HTTP_BuildHeaderRequestString( LPWININETHTTPREQW lpwhr, LPCWSTR ve
     return requestString;
 }
 
-static void HTTP_ProcessCookies( LPWININETHTTPREQW lpwhr )
+static void HTTP_ProcessCookies( http_request_t *lpwhr )
 {
-    static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
     int HeaderIndex;
+    int numCookies = 0;
     LPHTTPHEADERW setCookieHeader;
 
-    HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, 0, FALSE);
-    if (HeaderIndex == -1)
-            return;
-    setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
-
-    if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
+    while((HeaderIndex = HTTP_GetCustomHeaderIndex(lpwhr, szSet_Cookie, numCookies, FALSE)) != -1)
     {
-        int nPosStart = 0, nPosEnd = 0, len;
-        static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','/',0};
+        setCookieHeader = &lpwhr->pCustHeaders[HeaderIndex];
 
-        while (setCookieHeader->lpszValue[nPosEnd] != '\0')
+        if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_COOKIES) && setCookieHeader->lpszValue)
         {
-            LPWSTR buf_cookie, cookie_name, cookie_data;
+            int len;
+            static const WCHAR szFmt[] = { 'h','t','t','p',':','/','/','%','s','%','s',0};
             LPWSTR buf_url;
-            LPWSTR domain = NULL;
             LPHTTPHEADERW Host;
 
-            int nEqualPos = 0;
-            while (setCookieHeader->lpszValue[nPosEnd] != ';' && setCookieHeader->lpszValue[nPosEnd] != ',' &&
-                   setCookieHeader->lpszValue[nPosEnd] != '\0')
-            {
-                nPosEnd++;
-            }
-            if (setCookieHeader->lpszValue[nPosEnd] == ';')
-            {
-                /* fixme: not case sensitive, strcasestr is gnu only */
-                int nDomainPosEnd = 0;
-                int nDomainPosStart = 0, nDomainLength = 0;
-                static const WCHAR szDomain[] = {'d','o','m','a','i','n','=',0};
-                LPWSTR lpszDomain = strstrW(&setCookieHeader->lpszValue[nPosEnd], szDomain);
-                if (lpszDomain)
-                { /* they have specified their own domain, lets use it */
-                    while (lpszDomain[nDomainPosEnd] != ';' && lpszDomain[nDomainPosEnd] != ',' &&
-                           lpszDomain[nDomainPosEnd] != '\0')
-                    {
-                        nDomainPosEnd++;
-                    }
-                    nDomainPosStart = strlenW(szDomain);
-                    nDomainLength = (nDomainPosEnd - nDomainPosStart) + 1;
-                    domain = HeapAlloc(GetProcessHeap(), 0, (nDomainLength + 1)*sizeof(WCHAR));
-                    lstrcpynW(domain, &lpszDomain[nDomainPosStart], nDomainLength + 1);
-                }
-            }
-            if (setCookieHeader->lpszValue[nPosEnd] == '\0') break;
-            buf_cookie = HeapAlloc(GetProcessHeap(), 0, ((nPosEnd - nPosStart) + 1)*sizeof(WCHAR));
-            lstrcpynW(buf_cookie, &setCookieHeader->lpszValue[nPosStart], (nPosEnd - nPosStart) + 1);
-            TRACE("%s\n", debugstr_w(buf_cookie));
-            while (buf_cookie[nEqualPos] != '=' && buf_cookie[nEqualPos] != '\0')
-            {
-                nEqualPos++;
-            }
-            if (buf_cookie[nEqualPos] == '\0' || buf_cookie[nEqualPos + 1] == '\0')
-            {
-                HeapFree(GetProcessHeap(), 0, buf_cookie);
-                break;
-            }
-
-            cookie_name = HeapAlloc(GetProcessHeap(), 0, (nEqualPos + 1)*sizeof(WCHAR));
-            lstrcpynW(cookie_name, buf_cookie, nEqualPos + 1);
-            cookie_data = &buf_cookie[nEqualPos + 1];
-
-            Host = HTTP_GetHeader(lpwhr,szHost);
-            len = lstrlenW((domain ? domain : (Host?Host->lpszValue:NULL))) + 
-                strlenW(lpwhr->lpszPath) + 9;
+            Host = HTTP_GetHeader(lpwhr, hostW);
+            len = lstrlenW(Host->lpszValue) + 9 + lstrlenW(lpwhr->lpszPath);
             buf_url = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
-            sprintfW(buf_url, szFmt, (domain ? domain : (Host?Host->lpszValue:NULL))); /* FIXME PATH!!! */
-            InternetSetCookieW(buf_url, cookie_name, cookie_data);
+            sprintfW(buf_url, szFmt, Host->lpszValue, lpwhr->lpszPath);
+            InternetSetCookieW(buf_url, NULL, setCookieHeader->lpszValue);
 
             HeapFree(GetProcessHeap(), 0, buf_url);
-            HeapFree(GetProcessHeap(), 0, buf_cookie);
-            HeapFree(GetProcessHeap(), 0, cookie_name);
-            HeapFree(GetProcessHeap(), 0, domain);
-            nPosStart = nPosEnd;
         }
+        numCookies++;
+    }
+}
+
+static void strip_spaces(LPWSTR start)
+{
+    LPWSTR str = start;
+    LPWSTR end;
+
+    while (*str == ' ' && *str != '\0')
+        str++;
+
+    if (str != start)
+        memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
+
+    end = start + strlenW(start) - 1;
+    while (end >= start && *end == ' ')
+    {
+        *end = '\0';
+        end--;
     }
 }
 
-static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue )
+static inline BOOL is_basic_auth_value( LPCWSTR pszAuthValue, LPWSTR *pszRealm )
 {
     static const WCHAR szBasic[] = {'B','a','s','i','c'}; /* Note: not nul-terminated */
-    return !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
+    static const WCHAR szRealm[] = {'r','e','a','l','m'}; /* Note: not nul-terminated */
+    BOOL is_basic;
+    is_basic = !strncmpiW(pszAuthValue, szBasic, ARRAYSIZE(szBasic)) &&
         ((pszAuthValue[ARRAYSIZE(szBasic)] == ' ') || !pszAuthValue[ARRAYSIZE(szBasic)]);
+    if (is_basic && pszRealm)
+    {
+        LPCWSTR token;
+        LPCWSTR ptr = &pszAuthValue[ARRAYSIZE(szBasic)];
+        LPCWSTR realm;
+        ptr++;
+        *pszRealm=NULL;
+        token = strchrW(ptr,'=');
+        if (!token)
+            return TRUE;
+        realm = ptr;
+        while (*realm == ' ' && *realm != '\0')
+            realm++;
+        if(!strncmpiW(realm, szRealm, ARRAYSIZE(szRealm)) &&
+            (realm[ARRAYSIZE(szRealm)] == ' ' || realm[ARRAYSIZE(szRealm)] == '='))
+        {
+            token++;
+            while (*token == ' ' && *token != '\0')
+                token++;
+            if (*token == '\0')
+                return TRUE;
+            *pszRealm = heap_strdupW(token);
+            strip_spaces(*pszRealm);
+        }
+    }
+
+    return is_basic;
+}
+
+static void destroy_authinfo( struct HttpAuthInfo *authinfo )
+{
+    if (!authinfo) return;
+
+    if (SecIsValidHandle(&authinfo->ctx))
+        DeleteSecurityContext(&authinfo->ctx);
+    if (SecIsValidHandle(&authinfo->cred))
+        FreeCredentialsHandle(&authinfo->cred);
+
+    HeapFree(GetProcessHeap(), 0, authinfo->auth_data);
+    HeapFree(GetProcessHeap(), 0, authinfo->scheme);
+    HeapFree(GetProcessHeap(), 0, authinfo);
+}
+
+static UINT retrieve_cached_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR *auth_data)
+{
+    authorizationData *ad;
+    UINT rc = 0;
+
+    TRACE("Looking for authorization for %s:%s\n",debugstr_w(host),debugstr_w(realm));
+
+    EnterCriticalSection(&authcache_cs);
+    LIST_FOR_EACH_ENTRY(ad, &basicAuthorizationCache, authorizationData, entry)
+    {
+        if (!strcmpiW(host,ad->lpszwHost) && !strcmpW(realm,ad->lpszwRealm))
+        {
+            TRACE("Authorization found in cache\n");
+            *auth_data = HeapAlloc(GetProcessHeap(),0,ad->AuthorizationLen);
+            memcpy(*auth_data,ad->lpszAuthorization,ad->AuthorizationLen);
+            rc = ad->AuthorizationLen;
+            break;
+        }
+    }
+    LeaveCriticalSection(&authcache_cs);
+    return rc;
+}
+
+static void cache_basic_authorization(LPWSTR host, LPWSTR realm, LPSTR auth_data, UINT auth_data_len)
+{
+    struct list *cursor;
+    authorizationData* ad = NULL;
+
+    TRACE("caching authorization for %s:%s = %s\n",debugstr_w(host),debugstr_w(realm),debugstr_an(auth_data,auth_data_len));
+
+    EnterCriticalSection(&authcache_cs);
+    LIST_FOR_EACH(cursor, &basicAuthorizationCache)
+    {
+        authorizationData *check = LIST_ENTRY(cursor,authorizationData,entry);
+        if (!strcmpiW(host,check->lpszwHost) && !strcmpW(realm,check->lpszwRealm))
+        {
+            ad = check;
+            break;
+        }
+    }
+
+    if (ad)
+    {
+        TRACE("Found match in cache, replacing\n");
+        HeapFree(GetProcessHeap(),0,ad->lpszAuthorization);
+        ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
+        memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
+        ad->AuthorizationLen = auth_data_len;
+    }
+    else
+    {
+        ad = HeapAlloc(GetProcessHeap(),0,sizeof(authorizationData));
+        ad->lpszwHost = heap_strdupW(host);
+        ad->lpszwRealm = heap_strdupW(realm);
+        ad->lpszAuthorization = HeapAlloc(GetProcessHeap(),0,auth_data_len);
+        memcpy(ad->lpszAuthorization, auth_data, auth_data_len);
+        ad->AuthorizationLen = auth_data_len;
+        list_add_head(&basicAuthorizationCache,&ad->entry);
+        TRACE("authorization cached\n");
+    }
+    LeaveCriticalSection(&authcache_cs);
 }
 
-static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
+static BOOL HTTP_DoAuthorization( http_request_t *lpwhr, LPCWSTR pszAuthValue,
                                   struct HttpAuthInfo **ppAuthInfo,
-                                  LPWSTR domain_and_username, LPWSTR password )
+                                  LPWSTR domain_and_username, LPWSTR password,
+                                  LPWSTR host )
 {
     SECURITY_STATUS sec_status;
     struct HttpAuthInfo *pAuthInfo = *ppAuthInfo;
     BOOL first = FALSE;
+    LPWSTR szRealm = NULL;
 
     TRACE("%s\n", debugstr_w(pszAuthValue));
 
@@ -426,10 +662,10 @@ static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
         pAuthInfo->auth_data_len = 0;
         pAuthInfo->finished = FALSE;
 
-        if (is_basic_auth_value(pszAuthValue))
+        if (is_basic_auth_value(pszAuthValue,NULL))
         {
             static const WCHAR szBasic[] = {'B','a','s','i','c',0};
-            pAuthInfo->scheme = WININET_strdupW(szBasic);
+            pAuthInfo->scheme = heap_strdupW(szBasic);
             if (!pAuthInfo->scheme)
             {
                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
@@ -441,7 +677,7 @@ static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
             PVOID pAuthData;
             SEC_WINNT_AUTH_IDENTITY_W nt_auth_identity;
 
-            pAuthInfo->scheme = WININET_strdupW(pszAuthValue);
+            pAuthInfo->scheme = heap_strdupW(pszAuthValue);
             if (!pAuthInfo->scheme)
             {
                 HeapFree(GetProcessHeap(), 0, pAuthInfo);
@@ -513,33 +749,50 @@ static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
         return FALSE;
     }
 
-    if (is_basic_auth_value(pszAuthValue))
+    if (is_basic_auth_value(pszAuthValue,&szRealm))
     {
         int userlen;
         int passlen;
-        char *auth_data;
-
-        TRACE("basic authentication\n");
+        char *auth_data = NULL;
+        UINT auth_data_len = 0;
 
-        /* we don't cache credentials for basic authentication, so we can't
-         * retrieve them if the application didn't pass us any credentials */
-        if (!domain_and_username) return FALSE;
+        TRACE("basic authentication realm %s\n",debugstr_w(szRealm));
 
-        userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
-        passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
+        if (!domain_and_username)
+        {
+            if (host && szRealm)
+                auth_data_len = retrieve_cached_basic_authorization(host, szRealm,&auth_data);
+            if (auth_data_len == 0)
+            {
+                HeapFree(GetProcessHeap(),0,szRealm);
+                return FALSE;
+            }
+        }
+        else
+        {
+            userlen = WideCharToMultiByte(CP_UTF8, 0, domain_and_username, lstrlenW(domain_and_username), NULL, 0, NULL, NULL);
+            passlen = WideCharToMultiByte(CP_UTF8, 0, password, lstrlenW(password), NULL, 0, NULL, NULL);
 
-        /* length includes a nul terminator, which will be re-used for the ':' */
-        auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
-        if (!auth_data)
-            return FALSE;
+            /* length includes a nul terminator, which will be re-used for the ':' */
+            auth_data = HeapAlloc(GetProcessHeap(), 0, userlen + 1 + passlen);
+            if (!auth_data)
+            {
+                HeapFree(GetProcessHeap(),0,szRealm);
+                return FALSE;
+            }
 
-        WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
-        auth_data[userlen] = ':';
-        WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
+            WideCharToMultiByte(CP_UTF8, 0, domain_and_username, -1, auth_data, userlen, NULL, NULL);
+            auth_data[userlen] = ':';
+            WideCharToMultiByte(CP_UTF8, 0, password, -1, &auth_data[userlen+1], passlen, NULL, NULL);
+            auth_data_len = userlen + 1 + passlen;
+            if (host && szRealm)
+                cache_basic_authorization(host, szRealm, auth_data, auth_data_len);
+        }
 
         pAuthInfo->auth_data = auth_data;
-        pAuthInfo->auth_data_len = userlen + 1 + passlen;
+        pAuthInfo->auth_data_len = auth_data_len;
         pAuthInfo->finished = TRUE;
+        HeapFree(GetProcessHeap(),0,szRealm);
 
         return TRUE;
     }
@@ -602,8 +855,9 @@ static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
         else
         {
             ERR("InitializeSecurityContextW returned error 0x%08x\n", sec_status);
-            pAuthInfo->finished = TRUE;
             HeapFree(GetProcessHeap(), 0, out.pvBuffer);
+            destroy_authinfo(pAuthInfo);
+            *ppAuthInfo = NULL;
             return FALSE;
         }
     }
@@ -614,14 +868,13 @@ static BOOL HTTP_DoAuthorization( LPWININETHTTPREQW lpwhr, LPCWSTR pszAuthValue,
 /***********************************************************************
  *           HTTP_HttpAddRequestHeadersW (internal)
  */
-static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
+static DWORD HTTP_HttpAddRequestHeadersW(http_request_t *lpwhr,
        LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
 {
     LPWSTR lpszStart;
     LPWSTR lpszEnd;
     LPWSTR buffer;
-    BOOL bSuccess = FALSE;
-    DWORD len;
+    DWORD len, res = ERROR_HTTP_INVALID_HEADER;
 
     TRACE("copying header: %s\n", debugstr_wn(lpszHeader, dwHeaderLength));
 
@@ -642,7 +895,7 @@ static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
 
         while (*lpszEnd != '\0')
         {
-            if (*lpszEnd == '\r' && *(lpszEnd + 1) == '\n')
+            if (*lpszEnd == '\r' || *lpszEnd == '\n')
                  break;
             lpszEnd++;
         }
@@ -650,28 +903,35 @@ static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
         if (*lpszStart == '\0')
            break;
 
-        if (*lpszEnd == '\r')
+        if (*lpszEnd == '\r' || *lpszEnd == '\n')
         {
             *lpszEnd = '\0';
-            lpszEnd += 2; /* Jump over \r\n */
+            lpszEnd++; /* Jump over newline */
         }
         TRACE("interpreting header %s\n", debugstr_w(lpszStart));
+        if (*lpszStart == '\0')
+        {
+            /* Skip 0-length headers */
+            lpszStart = lpszEnd;
+            res = ERROR_SUCCESS;
+            continue;
+        }
         pFieldAndValue = HTTP_InterpretHttpHeader(lpszStart);
         if (pFieldAndValue)
         {
-            bSuccess = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
-            if (bSuccess)
-                bSuccess = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
+            res = HTTP_VerifyValidHeader(lpwhr, pFieldAndValue[0]);
+            if (res == ERROR_SUCCESS)
+                res = HTTP_ProcessHeader(lpwhr, pFieldAndValue[0],
                     pFieldAndValue[1], dwModifier | HTTP_ADDHDR_FLAG_REQ);
             HTTP_FreeTokens(pFieldAndValue);
         }
 
         lpszStart = lpszEnd;
-    } while (bSuccess);
+    } while (res == ERROR_SUCCESS);
 
     HeapFree(GetProcessHeap(), 0, buffer);
 
-    return bSuccess;
+    return res;
 }
 
 /***********************************************************************
@@ -693,26 +953,23 @@ static BOOL WINAPI HTTP_HttpAddRequestHeadersW(LPWININETHTTPREQW lpwhr,
 BOOL WINAPI HttpAddRequestHeadersW(HINTERNET hHttpRequest,
        LPCWSTR lpszHeader, DWORD dwHeaderLength, DWORD dwModifier)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPREQW lpwhr;
+    http_request_t *lpwhr;
+    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
 
     TRACE("%p, %s, %i, %i\n", hHttpRequest, debugstr_wn(lpszHeader, dwHeaderLength), dwHeaderLength, dwModifier);
 
     if (!lpszHeader) 
       return TRUE;
 
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
-    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-        goto lend;
-    }
-    bSuccess = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
-lend:
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
+    if (lpwhr && lpwhr->hdr.htype == WH_HHTTPREQ)
+        res = HTTP_HttpAddRequestHeadersW( lpwhr, lpszHeader, dwHeaderLength, dwModifier );
     if( lpwhr )
         WININET_Release( &lpwhr->hdr );
 
-    return bSuccess;
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
 }
 
 /***********************************************************************
@@ -748,267 +1005,57 @@ BOOL WINAPI HttpAddRequestHeadersA(HINTERNET hHttpRequest,
 }
 
 /***********************************************************************
- *           HttpEndRequestA (WININET.@)
+ *           HttpOpenRequestA (WININET.@)
  *
- * Ends an HTTP request that was started by HttpSendRequestEx
+ * Open a HTTP request handle
  *
  * RETURNS
- *    TRUE     if successful
- *    FALSE    on failure
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL      on failure
  *
  */
-BOOL WINAPI HttpEndRequestA(HINTERNET hRequest, 
-        LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
+HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
+       LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
+       LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
+       DWORD dwFlags, DWORD_PTR dwContext)
 {
-    LPINTERNET_BUFFERSA ptr;
-    LPINTERNET_BUFFERSW lpBuffersOutW,ptrW;
-    BOOL rc = FALSE;
-
-    TRACE("(%p, %p, %08x, %08lx): stub\n", hRequest, lpBuffersOut, dwFlags,
-            dwContext);
+    LPWSTR szVerb = NULL, szObjectName = NULL;
+    LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
+    INT acceptTypesCount;
+    HINTERNET rc = FALSE;
+    LPCSTR *types;
 
-    ptr = lpBuffersOut;
-    if (ptr)
-        lpBuffersOutW = (LPINTERNET_BUFFERSW)HeapAlloc(GetProcessHeap(),
-                HEAP_ZERO_MEMORY, sizeof(INTERNET_BUFFERSW));
-    else
-        lpBuffersOutW = NULL;
+    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
+          debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
+          debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
+          dwFlags, dwContext);
 
-    ptrW = lpBuffersOutW;
-    while (ptr)
+    if (lpszVerb)
     {
-        if (ptr->lpvBuffer && ptr->dwBufferLength)
-            ptrW->lpvBuffer = HeapAlloc(GetProcessHeap(),0,ptr->dwBufferLength);
-        ptrW->dwBufferLength = ptr->dwBufferLength;
-        ptrW->dwBufferTotal= ptr->dwBufferTotal;
-
-        if (ptr->Next)
-            ptrW->Next = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
-                    sizeof(INTERNET_BUFFERSW));
-
-        ptr = ptr->Next;
-        ptrW = ptrW->Next;
-    }
-
-    rc = HttpEndRequestW(hRequest, lpBuffersOutW, dwFlags, dwContext);
-
-    if (lpBuffersOutW)
-    {
-        ptrW = lpBuffersOutW;
-        while (ptrW)
-        {
-            LPINTERNET_BUFFERSW ptrW2;
-
-            FIXME("Do we need to translate info out of these buffer?\n");
-
-            HeapFree(GetProcessHeap(),0,ptrW->lpvBuffer);
-            ptrW2 = ptrW->Next;
-            HeapFree(GetProcessHeap(),0,ptrW);
-            ptrW = ptrW2;
-        }
-    }
-
-    return rc;
-}
-
-/***********************************************************************
- *           HttpEndRequestW (WININET.@)
- *
- * Ends an HTTP request that was started by HttpSendRequestEx
- *
- * RETURNS
- *    TRUE     if successful
- *    FALSE    on failure
- *
- */
-BOOL WINAPI HttpEndRequestW(HINTERNET hRequest, 
-        LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
-{
-    BOOL rc = FALSE;
-    LPWININETHTTPREQW lpwhr;
-    INT responseLen;
-    DWORD dwBufferSize;
-
-    TRACE("-->\n");
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
-
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-        if (lpwhr)
-            WININET_Release( &lpwhr->hdr );
-       return FALSE;
-    }
-
-    lpwhr->hdr.dwFlags |= dwFlags;
-    lpwhr->hdr.dwContext = dwContext;
-
-    /* We appear to do nothing with lpBuffersOut.. is that correct? */
-
-    SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-            INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
-
-    responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
-    if (responseLen)
-           rc = TRUE;
-
-    SendAsyncCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-            INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
-
-    /* process cookies here. Is this right? */
-    HTTP_ProcessCookies(lpwhr);
-
-    dwBufferSize = sizeof(lpwhr->dwContentLength);
-    if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
-                             &lpwhr->dwContentLength,&dwBufferSize,NULL))
-        lpwhr->dwContentLength = -1;
-
-    if (lpwhr->dwContentLength == 0)
-        HTTP_FinishedReading(lpwhr);
-
-    if(!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
-    {
-        DWORD dwCode,dwCodeLength=sizeof(DWORD);
-        if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,&dwCode,&dwCodeLength,NULL) &&
-            (dwCode==302 || dwCode==301))
-        {
-            WCHAR szNewLocation[INTERNET_MAX_URL_LENGTH];
-            dwBufferSize=sizeof(szNewLocation);
-            if(HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
-            {
-                /* redirects are always GETs */
-                HeapFree(GetProcessHeap(),0,lpwhr->lpszVerb);
-                lpwhr->lpszVerb = WININET_strdupW(szGET);
-                HTTP_DrainContent(lpwhr);
-                INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                                      INTERNET_STATUS_REDIRECT, szNewLocation,
-                                      dwBufferSize);
-                rc = HTTP_HandleRedirect(lpwhr, szNewLocation);
-                if (rc)
-                    rc = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
-            }
-        }
-    }
-
-    WININET_Release( &lpwhr->hdr );
-    TRACE("%i <--\n",rc);
-    return rc;
-}
-
-/***********************************************************************
- *           HttpOpenRequestW (WININET.@)
- *
- * Open a HTTP request handle
- *
- * RETURNS
- *    HINTERNET  a HTTP request handle on success
- *    NULL      on failure
- *
- */
-HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
-       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
-       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD_PTR dwContext)
-{
-    LPWININETHTTPSESSIONW lpwhs;
-    HINTERNET handle = NULL;
-
-    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
-          debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
-          debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
-          dwFlags, dwContext);
-    if(lpszAcceptTypes!=NULL)
-    {
-        int i;
-        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
-            TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
-    }    
-
-    lpwhs = (LPWININETHTTPSESSIONW) WININET_GetObject( hHttpSession );
-    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       goto lend;
-    }
-
-    /*
-     * My tests seem to show that the windows version does not
-     * become asynchronous until after this point. And anyhow
-     * if this call was asynchronous then how would you get the
-     * necessary HINTERNET pointer returned by this function.
-     *
-     */
-    handle = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
-                                   lpszVersion, lpszReferrer, lpszAcceptTypes,
-                                   dwFlags, dwContext);
-lend:
-    if( lpwhs )
-        WININET_Release( &lpwhs->hdr );
-    TRACE("returning %p\n", handle);
-    return handle;
-}
-
-
-/***********************************************************************
- *           HttpOpenRequestA (WININET.@)
- *
- * Open a HTTP request handle
- *
- * RETURNS
- *    HINTERNET  a HTTP request handle on success
- *    NULL      on failure
- *
- */
-HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
-       LPCSTR lpszVerb, LPCSTR lpszObjectName, LPCSTR lpszVersion,
-       LPCSTR lpszReferrer , LPCSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD_PTR dwContext)
-{
-    LPWSTR szVerb = NULL, szObjectName = NULL;
-    LPWSTR szVersion = NULL, szReferrer = NULL, *szAcceptTypes = NULL;
-    INT len, acceptTypesCount;
-    HINTERNET rc = FALSE;
-    LPCSTR *types;
-
-    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
-          debugstr_a(lpszVerb), debugstr_a(lpszObjectName),
-          debugstr_a(lpszVersion), debugstr_a(lpszReferrer), lpszAcceptTypes,
-          dwFlags, dwContext);
-
-    if (lpszVerb)
-    {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, NULL, 0 );
-        szVerb = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
-        if ( !szVerb )
-            goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszVerb, -1, szVerb, len);
+        szVerb = heap_strdupAtoW(lpszVerb);
+        if ( !szVerb )
+            goto end;
     }
 
     if (lpszObjectName)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, NULL, 0 );
-        szObjectName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR) );
+        szObjectName = heap_strdupAtoW(lpszObjectName);
         if ( !szObjectName )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszObjectName, -1, szObjectName, len );
     }
 
     if (lpszVersion)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, NULL, 0 );
-        szVersion = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        szVersion = heap_strdupAtoW(lpszVersion);
         if ( !szVersion )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszVersion, -1, szVersion, len );
     }
 
     if (lpszReferrer)
     {
-        len = MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, NULL, 0 );
-        szReferrer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+        szReferrer = heap_strdupAtoW(lpszReferrer);
         if ( !szReferrer )
             goto end;
-        MultiByteToWideChar(CP_ACP, 0, lpszReferrer, -1, szReferrer, len );
     }
 
     if (lpszAcceptTypes)
@@ -1017,12 +1064,20 @@ HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
         types = lpszAcceptTypes;
         while (*types)
         {
-            /* find out how many there are */
-            if (((ULONG_PTR)*types >> 16) && **types)
+            __TRY
+            {
+                /* find out how many there are */
+                if (*types && **types)
+                {
+                    TRACE("accept type: %s\n", debugstr_a(*types));
+                    acceptTypesCount++;
+                }
+            }
+            __EXCEPT_PAGE_FAULT
             {
-                TRACE("accept type: %s\n", debugstr_a(*types));
-                acceptTypesCount++;
+                WARN("invalid accept type pointer\n");
             }
+            __ENDTRY;
             types++;
         }
         szAcceptTypes = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR *) * (acceptTypesCount+1));
@@ -1032,20 +1087,20 @@ HINTERNET WINAPI HttpOpenRequestA(HINTERNET hHttpSession,
         types = lpszAcceptTypes;
         while (*types)
         {
-            if (((ULONG_PTR)*types >> 16) && **types)
+            __TRY
             {
-                len = MultiByteToWideChar(CP_ACP, 0, *types, -1, NULL, 0 );
-                szAcceptTypes[acceptTypesCount] = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
-                if (!szAcceptTypes[acceptTypesCount]) goto end;
-
-                MultiByteToWideChar(CP_ACP, 0, *types, -1, szAcceptTypes[acceptTypesCount], len);
-                acceptTypesCount++;
+                if (*types && **types)
+                    szAcceptTypes[acceptTypesCount++] = heap_strdupAtoW(*types);
+            }
+            __EXCEPT_PAGE_FAULT
+            {
+                /* ignore invalid pointer */
             }
+            __ENDTRY;
             types++;
         }
         szAcceptTypes[acceptTypesCount] = NULL;
     }
-    else szAcceptTypes = 0;
 
     rc = HttpOpenRequestW(hHttpSession, szVerb, szObjectName,
                           szVersion, szReferrer,
@@ -1207,7 +1262,7 @@ static UINT HTTP_DecodeBase64( LPCWSTR base64, LPSTR bin )
  *
  *   Insert or delete the authorization field in the request header.
  */
-static BOOL HTTP_InsertAuthorization( LPWININETHTTPREQW lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
+static BOOL HTTP_InsertAuthorization( http_request_t *lpwhr, struct HttpAuthInfo *pAuthInfo, LPCWSTR header )
 {
     if (pAuthInfo)
     {
@@ -1250,13 +1305,13 @@ static BOOL HTTP_InsertAuthorization( LPWININETHTTPREQW lpwhr, struct HttpAuthIn
     return TRUE;
 }
 
-static WCHAR *HTTP_BuildProxyRequestUrl(WININETHTTPREQW *req)
+static WCHAR *HTTP_BuildProxyRequestUrl(http_request_t *req)
 {
     WCHAR new_location[INTERNET_MAX_URL_LENGTH], *url;
     DWORD size;
 
     size = sizeof(new_location);
-    if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL))
+    if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_LOCATION, new_location, &size, NULL) == ERROR_SUCCESS)
     {
         if (!(url = HeapAlloc( GetProcessHeap(), 0, size + sizeof(WCHAR) ))) return NULL;
         strcpyW( url, new_location );
@@ -1266,7 +1321,7 @@ static WCHAR *HTTP_BuildProxyRequestUrl(WININETHTTPREQW *req)
         static const WCHAR slash[] = { '/',0 };
         static const WCHAR format[] = { 'h','t','t','p',':','/','/','%','s',':','%','d',0 };
         static const WCHAR formatSSL[] = { 'h','t','t','p','s',':','/','/','%','s',':','%','d',0 };
-        WININETHTTPSESSIONW *session = req->lpHttpSession;
+        http_session_t *session = req->lpHttpSession;
 
         size = 16; /* "https://" + sizeof(port#) + ":/\0" */
         size += strlenW( session->lpszHostName ) + strlenW( req->lpszPath );
@@ -1287,8 +1342,7 @@ static WCHAR *HTTP_BuildProxyRequestUrl(WININETHTTPREQW *req)
 /***********************************************************************
  *           HTTP_DealWithProxy
  */
-static BOOL HTTP_DealWithProxy( LPWININETAPPINFOW hIC,
-    LPWININETHTTPSESSIONW lpwhs, LPWININETHTTPREQW lpwhr)
+static BOOL HTTP_DealWithProxy(appinfo_t *hIC, http_session_t *lpwhs, http_request_t *lpwhr)
 {
     WCHAR buf[MAXHOSTNAME];
     WCHAR proxy[MAXHOSTNAME + 15]; /* 15 == "http://" + sizeof(port#) + ":/\0" */
@@ -1319,38 +1373,52 @@ static BOOL HTTP_DealWithProxy( LPWININETAPPINFOW hIC,
         UrlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
 
     HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
-    lpwhs->lpszServerName = WININET_strdupW(UrlComponents.lpszHostName);
+    lpwhs->lpszServerName = heap_strdupW(UrlComponents.lpszHostName);
     lpwhs->nServerPort = UrlComponents.nPort;
 
     TRACE("proxy server=%s port=%d\n", debugstr_w(lpwhs->lpszServerName), lpwhs->nServerPort);
     return TRUE;
 }
 
-static BOOL HTTP_ResolveName(LPWININETHTTPREQW lpwhr)
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN 46
+#endif
+
+static DWORD HTTP_ResolveName(http_request_t *lpwhr)
 {
-    char szaddr[32];
-    LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
+    char szaddr[INET6_ADDRSTRLEN];
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    const void *addr;
 
     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                           INTERNET_STATUS_RESOLVING_NAME,
                           lpwhs->lpszServerName,
-                          strlenW(lpwhs->lpszServerName)+1);
+                          (strlenW(lpwhs->lpszServerName)+1) * sizeof(WCHAR));
 
+    lpwhs->sa_len = sizeof(lpwhs->socketAddress);
     if (!GetAddress(lpwhs->lpszServerName, lpwhs->nServerPort,
-                    &lpwhs->socketAddress))
+                    (struct sockaddr *)&lpwhs->socketAddress, &lpwhs->sa_len))
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
+
+    switch (lpwhs->socketAddress.ss_family)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_NAME_NOT_RESOLVED);
-        return FALSE;
+    case AF_INET:
+        addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
+        break;
+    case AF_INET6:
+        addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
+        break;
+    default:
+        WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
     }
-
-    inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
-              szaddr, sizeof(szaddr));
+    inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                           INTERNET_STATUS_NAME_RESOLVED,
                           szaddr, strlen(szaddr)+1);
 
     TRACE("resolved %s to %s\n", debugstr_w(lpwhs->lpszServerName), szaddr);
-    return TRUE;
+    return ERROR_SUCCESS;
 }
 
 
@@ -1360,9 +1428,9 @@ static BOOL HTTP_ResolveName(LPWININETHTTPREQW lpwhr)
  * Deallocate request handle
  *
  */
-static void HTTPREQ_Destroy(WININETHANDLEHEADER *hdr)
+static void HTTPREQ_Destroy(object_header_t *hdr)
 {
-    LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) hdr;
+    http_request_t *lpwhr = (http_request_t*) hdr;
     DWORD i;
 
     TRACE("\n");
@@ -1370,13 +1438,14 @@ static void HTTPREQ_Destroy(WININETHANDLEHEADER *hdr)
     if(lpwhr->hCacheFile)
         CloseHandle(lpwhr->hCacheFile);
 
-    if(lpwhr->lpszCacheFile) {
-        DeleteFileW(lpwhr->lpszCacheFile); /* FIXME */
-        HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
-    }
+    HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
 
+    DeleteCriticalSection( &lpwhr->read_section );
     WININET_Release(&lpwhr->lpHttpSession->hdr);
 
+    destroy_authinfo(lpwhr->pAuthInfo);
+    destroy_authinfo(lpwhr->pProxyAuthInfo);
+
     HeapFree(GetProcessHeap(), 0, lpwhr->lpszPath);
     HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
@@ -1389,44 +1458,27 @@ static void HTTPREQ_Destroy(WININETHANDLEHEADER *hdr)
         HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders[i].lpszValue);
     }
 
+#ifdef HAVE_ZLIB
+    if(lpwhr->gzip_stream) {
+        if(!lpwhr->gzip_stream->end_of_data)
+            inflateEnd(&lpwhr->gzip_stream->zstream);
+        HeapFree(GetProcessHeap(), 0, lpwhr->gzip_stream);
+    }
+#endif
+
     HeapFree(GetProcessHeap(), 0, lpwhr->pCustHeaders);
     HeapFree(GetProcessHeap(), 0, lpwhr);
 }
 
-static void HTTPREQ_CloseConnection(WININETHANDLEHEADER *hdr)
+static void HTTPREQ_CloseConnection(object_header_t *hdr)
 {
-    LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) hdr;
+    http_request_t *lpwhr = (http_request_t*) hdr;
 
     TRACE("%p\n",lpwhr);
 
     if (!NETCON_connected(&lpwhr->netConnection))
         return;
 
-    if (lpwhr->pAuthInfo)
-    {
-        if (SecIsValidHandle(&lpwhr->pAuthInfo->ctx))
-            DeleteSecurityContext(&lpwhr->pAuthInfo->ctx);
-        if (SecIsValidHandle(&lpwhr->pAuthInfo->cred))
-            FreeCredentialsHandle(&lpwhr->pAuthInfo->cred);
-
-        HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->auth_data);
-        HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo->scheme);
-        HeapFree(GetProcessHeap(), 0, lpwhr->pAuthInfo);
-        lpwhr->pAuthInfo = NULL;
-    }
-    if (lpwhr->pProxyAuthInfo)
-    {
-        if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->ctx))
-            DeleteSecurityContext(&lpwhr->pProxyAuthInfo->ctx);
-        if (SecIsValidHandle(&lpwhr->pProxyAuthInfo->cred))
-            FreeCredentialsHandle(&lpwhr->pProxyAuthInfo->cred);
-
-        HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->auth_data);
-        HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo->scheme);
-        HeapFree(GetProcessHeap(), 0, lpwhr->pProxyAuthInfo);
-        lpwhr->pProxyAuthInfo = NULL;
-    }
-
     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                           INTERNET_STATUS_CLOSING_CONNECTION, 0, 0);
 
@@ -1436,11 +1488,95 @@ static void HTTPREQ_CloseConnection(WININETHANDLEHEADER *hdr)
                           INTERNET_STATUS_CONNECTION_CLOSED, 0, 0);
 }
 
-static DWORD HTTPREQ_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static BOOL HTTP_GetRequestURL(http_request_t *req, LPWSTR buf)
+{
+    LPHTTPHEADERW host_header;
+
+    static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
+
+    host_header = HTTP_GetHeader(req, hostW);
+    if(!host_header)
+        return FALSE;
+
+    sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
+    return TRUE;
+}
+
+static BOOL HTTP_KeepAlive(http_request_t *lpwhr)
+{
+    WCHAR szVersion[10];
+    WCHAR szConnectionResponse[20];
+    DWORD dwBufferSize = sizeof(szVersion);
+    BOOL keepalive = FALSE;
+
+    /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
+     * the connection is keep-alive by default */
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion, &dwBufferSize, NULL) == ERROR_SUCCESS
+        && !strcmpiW(szVersion, g_szHttp1_1))
+    {
+        keepalive = TRUE;
+    }
+
+    dwBufferSize = sizeof(szConnectionResponse);
+    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS
+        || HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) == ERROR_SUCCESS)
+    {
+        keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
+    }
+
+    return keepalive;
+}
+
+static DWORD HTTPREQ_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
-    WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
+    http_request_t *req = (http_request_t*)hdr;
 
     switch(option) {
+    case INTERNET_OPTION_DIAGNOSTIC_SOCKET_INFO:
+    {
+        http_session_t *lpwhs = req->lpHttpSession;
+        INTERNET_DIAGNOSTIC_SOCKET_INFO *info = buffer;
+
+        FIXME("INTERNET_DIAGNOSTIC_SOCKET_INFO stub\n");
+
+        if (*size < sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO))
+            return ERROR_INSUFFICIENT_BUFFER;
+        *size = sizeof(INTERNET_DIAGNOSTIC_SOCKET_INFO);
+        /* FIXME: can't get a SOCKET from our connection since we don't use
+         * winsock
+         */
+        info->Socket = 0;
+        /* FIXME: get source port from req->netConnection */
+        info->SourcePort = 0;
+        info->DestPort = lpwhs->nHostPort;
+        info->Flags = 0;
+        if (HTTP_KeepAlive(req))
+            info->Flags |= IDSI_FLAG_KEEP_ALIVE;
+        if (lpwhs->lpAppInfo->lpszProxy && lpwhs->lpAppInfo->lpszProxy[0] != 0)
+            info->Flags |= IDSI_FLAG_PROXY;
+        if (req->netConnection.useSSL)
+            info->Flags |= IDSI_FLAG_SECURE;
+
+        return ERROR_SUCCESS;
+    }
+
+    case INTERNET_OPTION_SECURITY_FLAGS:
+    {
+        http_session_t *lpwhs;
+        lpwhs = req->lpHttpSession;
+
+        if (*size < sizeof(ULONG))
+            return ERROR_INSUFFICIENT_BUFFER;
+
+        *size = sizeof(DWORD);
+        if (lpwhs->hdr.dwFlags & INTERNET_FLAG_SECURE)
+            *(DWORD*)buffer = SECURITY_FLAG_SECURE;
+        else
+            *(DWORD*)buffer = 0;
+        FIXME("Semi-STUB INTERNET_OPTION_SECURITY_FLAGS: %x\n",*(DWORD*)buffer);
+        return ERROR_SUCCESS;
+    }
+
     case INTERNET_OPTION_HANDLE_TYPE:
         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
 
@@ -1458,7 +1594,6 @@ static DWORD HTTPREQ_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
         WCHAR *pch;
 
         static const WCHAR httpW[] = {'h','t','t','p',':','/','/',0};
-        static const WCHAR hostW[] = {'H','o','s','t',0};
 
         TRACE("INTERNET_OPTION_URL\n");
 
@@ -1489,6 +1624,41 @@ static DWORD HTTPREQ_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
         }
     }
 
+    case INTERNET_OPTION_CACHE_TIMESTAMPS: {
+        INTERNET_CACHE_ENTRY_INFOW *info;
+        INTERNET_CACHE_TIMESTAMPS *ts = buffer;
+        WCHAR url[INTERNET_MAX_URL_LENGTH];
+        DWORD nbytes, error;
+        BOOL ret;
+
+        TRACE("INTERNET_OPTION_CACHE_TIMESTAMPS\n");
+
+        if (*size < sizeof(*ts))
+        {
+            *size = sizeof(*ts);
+            return ERROR_INSUFFICIENT_BUFFER;
+        }
+        nbytes = 0;
+        HTTP_GetRequestURL(req, url);
+        ret = GetUrlCacheEntryInfoW(url, NULL, &nbytes);
+        error = GetLastError();
+        if (!ret && error == ERROR_INSUFFICIENT_BUFFER)
+        {
+            if (!(info = HeapAlloc(GetProcessHeap(), 0, nbytes)))
+                return ERROR_OUTOFMEMORY;
+
+            GetUrlCacheEntryInfoW(url, info, &nbytes);
+
+            ts->ftExpires = info->ExpireTime;
+            ts->ftLastModified = info->LastModifiedTime;
+
+            HeapFree(GetProcessHeap(), 0, info);
+            *size = sizeof(*ts);
+            return ERROR_SUCCESS;
+        }
+        return error;
+    }
+
     case INTERNET_OPTION_DATAFILE_NAME: {
         DWORD req_size;
 
@@ -1584,9 +1754,9 @@ static DWORD HTTPREQ_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
     return INET_QueryOption(option, buffer, size, unicode);
 }
 
-static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD size)
+static DWORD HTTPREQ_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
 {
-    WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
+    http_request_t *req = (http_request_t*)hdr;
 
     switch(option) {
     case INTERNET_OPTION_SEND_TIMEOUT:
@@ -1598,151 +1768,365 @@ static DWORD HTTPREQ_SetOption(WININETHANDLEHEADER *hdr, DWORD option, void *buf
 
         return NETCON_set_timeout(&req->netConnection, option == INTERNET_OPTION_SEND_TIMEOUT,
                     *(DWORD*)buffer);
+
+    case INTERNET_OPTION_USERNAME:
+        HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszUserName);
+        if (!(req->lpHttpSession->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+
+    case INTERNET_OPTION_PASSWORD:
+        HeapFree(GetProcessHeap(), 0, req->lpHttpSession->lpszPassword);
+        if (!(req->lpHttpSession->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    case INTERNET_OPTION_HTTP_DECODING:
+        if(size != sizeof(BOOL))
+            return ERROR_INVALID_PARAMETER;
+        req->decoding = *(BOOL*)buffer;
+        return ERROR_SUCCESS;
     }
 
     return ERROR_INTERNET_INVALID_OPTION;
 }
 
-static DWORD HTTP_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
+/* read some more data into the read buffer (the read section must be held) */
+static DWORD read_more_data( http_request_t *req, int maxlen )
 {
-    int bytes_read;
-
-    if(!NETCON_recv(&req->netConnection, buffer, min(size, req->dwContentLength - req->dwContentRead),
-                     sync ? MSG_WAITALL : 0, &bytes_read)) {
-        if(req->dwContentLength != -1 && req->dwContentRead != req->dwContentLength)
-            ERR("not all data received %d/%d\n", req->dwContentRead, req->dwContentLength);
+    DWORD res;
+    int len;
 
-        /* always return success, even if the network layer returns an error */
-        *read = 0;
-        HTTP_FinishedReading(req);
-        return ERROR_SUCCESS;
+    if (req->read_pos)
+    {
+        /* move existing data to the start of the buffer */
+        if(req->read_size)
+            memmove( req->read_buf, req->read_buf + req->read_pos, req->read_size );
+        req->read_pos = 0;
     }
 
-    req->dwContentRead += bytes_read;
-    *read = bytes_read;
-
-    if(req->lpszCacheFile) {
-        BOOL res;
-        DWORD dwBytesWritten;
-
-        res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
-        if(!res)
-            WARN("WriteFile failed: %u\n", GetLastError());
-    }
+    if (maxlen == -1) maxlen = sizeof(req->read_buf);
 
-    if(!bytes_read && (req->dwContentRead == req->dwContentLength))
-        HTTP_FinishedReading(req);
+    res = NETCON_recv( &req->netConnection, req->read_buf + req->read_size,
+                       maxlen - req->read_size, 0, &len );
+    if(res == ERROR_SUCCESS)
+        req->read_size += len;
 
-    return ERROR_SUCCESS;
+    return res;
 }
 
-static DWORD get_chunk_size(const char *buffer)
+/* remove some amount of data from the read buffer (the read section must be held) */
+static void remove_data( http_request_t *req, int count )
 {
-    const char *p;
-    DWORD size = 0;
-
-    for (p = buffer; *p; p++)
-    {
-        if (*p >= '0' && *p <= '9') size = size * 16 + *p - '0';
-        else if (*p >= 'a' && *p <= 'f') size = size * 16 + *p - 'a' + 10;
-        else if (*p >= 'A' && *p <= 'F') size = size * 16 + *p - 'A' + 10;
-        else if (*p == ';') break;
-    }
-    return size;
+    if (!(req->read_size -= count)) req->read_pos = 0;
+    else req->read_pos += count;
 }
 
-static DWORD HTTP_ReadChunked(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
+static BOOL read_line( http_request_t *req, LPSTR buffer, DWORD *len )
 {
-    char reply[MAX_REPLY_LEN], *p = buffer;
-    DWORD buflen, to_read, to_write = size;
-    int bytes_read;
+    int count, bytes_read, pos = 0;
+    DWORD res;
 
-    *read = 0;
+    EnterCriticalSection( &req->read_section );
     for (;;)
     {
-        if (*read == size) break;
+        BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
 
-        if (req->dwContentLength == ~0UL) /* new chunk */
+        if (eol)
         {
-            buflen = sizeof(reply);
-            if (!NETCON_getNextLine(&req->netConnection, reply, &buflen)) break;
-
-            if (!(req->dwContentLength = get_chunk_size(reply)))
-            {
-                /* zero sized chunk marks end of transfer; read any trailing headers and return */
-                HTTP_GetResponseHeaders(req, FALSE);
-                break;
-            }
+            count = eol - (req->read_buf + req->read_pos);
+            bytes_read = count + 1;
         }
-        to_read = min(to_write, req->dwContentLength - req->dwContentRead);
+        else count = bytes_read = req->read_size;
 
-        if (!NETCON_recv(&req->netConnection, p, to_read, sync ? MSG_WAITALL : 0, &bytes_read))
-        {
-            if (bytes_read != to_read)
-                ERR("Not all data received %d/%d\n", bytes_read, to_read);
+        count = min( count, *len - pos );
+        memcpy( buffer + pos, req->read_buf + req->read_pos, count );
+        pos += count;
+        remove_data( req, bytes_read );
+        if (eol) break;
 
-            /* always return success, even if the network layer returns an error */
-            *read = 0;
-            break;
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS || !req->read_size)
+        {
+            *len = 0;
+            TRACE( "returning empty string\n" );
+            LeaveCriticalSection( &req->read_section );
+            INTERNET_SetLastError(res);
+            return FALSE;
         }
-        if (!bytes_read) break;
+    }
+    LeaveCriticalSection( &req->read_section );
 
-        req->dwContentRead += bytes_read;
-        to_write -= bytes_read;
-        *read += bytes_read;
+    if (pos < *len)
+    {
+        if (pos && buffer[pos - 1] == '\r') pos--;
+        *len = pos + 1;
+    }
+    buffer[*len - 1] = 0;
+    TRACE( "returning %s\n", debugstr_a(buffer));
+    return TRUE;
+}
 
-        if (req->lpszCacheFile)
-        {
-            DWORD dwBytesWritten;
+/* discard data contents until we reach end of line (the read section must be held) */
+static DWORD discard_eol( http_request_t *req )
+{
+    DWORD res;
 
-            if (!WriteFile(req->hCacheFile, p, bytes_read, &dwBytesWritten, NULL))
-                WARN("WriteFile failed: %u\n", GetLastError());
+    do
+    {
+        BYTE *eol = memchr( req->read_buf + req->read_pos, '\n', req->read_size );
+        if (eol)
+        {
+            remove_data( req, (eol + 1) - (req->read_buf + req->read_pos) );
+            break;
         }
-        p += bytes_read;
+        req->read_pos = req->read_size = 0;  /* discard everything */
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
+    } while (req->read_size);
+    return ERROR_SUCCESS;
+}
 
-        if (req->dwContentRead == req->dwContentLength) /* chunk complete */
-        {
-            req->dwContentRead = 0;
-            req->dwContentLength = ~0UL;
+/* read the size of the next chunk (the read section must be held) */
+static DWORD start_next_chunk( http_request_t *req )
+{
+    DWORD chunk_size = 0, res;
 
-            buflen = sizeof(reply);
-            if (!NETCON_getNextLine(&req->netConnection, reply, &buflen))
+    if (!req->dwContentLength) return ERROR_SUCCESS;
+    if (req->dwContentLength == req->dwContentRead)
+    {
+        /* read terminator for the previous chunk */
+        if ((res = discard_eol( req )) != ERROR_SUCCESS) return res;
+        req->dwContentLength = ~0u;
+        req->dwContentRead = 0;
+    }
+    for (;;)
+    {
+        while (req->read_size)
+        {
+            char ch = req->read_buf[req->read_pos];
+            if (ch >= '0' && ch <= '9') chunk_size = chunk_size * 16 + ch - '0';
+            else if (ch >= 'a' && ch <= 'f') chunk_size = chunk_size * 16 + ch - 'a' + 10;
+            else if (ch >= 'A' && ch <= 'F') chunk_size = chunk_size * 16 + ch - 'A' + 10;
+            else if (ch == ';' || ch == '\r' || ch == '\n')
             {
-                ERR("Malformed chunk\n");
-                *read = 0;
-                break;
+                TRACE( "reading %u byte chunk\n", chunk_size );
+                req->dwContentLength = chunk_size;
+                req->dwContentRead = 0;
+                return discard_eol( req );
             }
+            remove_data( req, 1 );
+        }
+        if ((res = read_more_data( req, -1 )) != ERROR_SUCCESS) return res;
+        if (!req->read_size)
+        {
+            req->dwContentLength = req->dwContentRead = 0;
+            return ERROR_SUCCESS;
         }
     }
-    if (!*read) HTTP_FinishedReading(req);
+}
+
+/* check if we have reached the end of the data to read (the read section must be held) */
+static BOOL end_of_read_data( http_request_t *req )
+{
+    if (req->gzip_stream) return req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
+    if (req->read_chunked) return (req->dwContentLength == 0);
+    if (req->dwContentLength == ~0u) return FALSE;
+    return (req->dwContentLength == req->dwContentRead);
+}
+
+/* fetch some more data into the read buffer (the read section must be held) */
+static DWORD refill_buffer( http_request_t *req )
+{
+    int len = sizeof(req->read_buf);
+    DWORD res;
+
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
+    {
+        if ((res = start_next_chunk( req )) != ERROR_SUCCESS) return res;
+    }
+
+    if (req->dwContentLength != ~0u) len = min( len, req->dwContentLength - req->dwContentRead );
+    if (len <= req->read_size) return ERROR_SUCCESS;
+
+    if ((res = read_more_data( req, len )) != ERROR_SUCCESS) return res;
+    if (!req->read_size) req->dwContentLength = req->dwContentRead = 0;
     return ERROR_SUCCESS;
 }
 
-static DWORD HTTPREQ_Read(WININETHTTPREQW *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
+static DWORD read_gzip_data(http_request_t *req, BYTE *buf, int size, BOOL sync, int *read_ret)
 {
-    WCHAR encoding[20];
-    DWORD buflen = sizeof(encoding);
-    static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
+    DWORD ret = ERROR_SUCCESS;
+    int read = 0;
 
-    if (HTTP_HttpQueryInfoW(req, HTTP_QUERY_TRANSFER_ENCODING, encoding, &buflen, NULL) &&
-        !strcmpiW(encoding, szChunked))
+#ifdef HAVE_ZLIB
+    z_stream *zstream = &req->gzip_stream->zstream;
+    DWORD buf_avail;
+    int zres;
+
+    while(read < size && !req->gzip_stream->end_of_data) {
+        if(!req->read_size) {
+            if(!sync || refill_buffer(req) != ERROR_SUCCESS)
+                break;
+        }
+
+        buf_avail = req->dwContentLength == ~0 ? req->read_size : min(req->read_size, req->dwContentLength-req->dwContentRead);
+
+        zstream->next_in = req->read_buf+req->read_pos;
+        zstream->avail_in = buf_avail;
+        zstream->next_out = buf+read;
+        zstream->avail_out = size-read;
+        zres = inflate(zstream, Z_FULL_FLUSH);
+        read = size - zstream->avail_out;
+        req->dwContentRead += buf_avail-zstream->avail_in;
+        remove_data(req, buf_avail-zstream->avail_in);
+        if(zres == Z_STREAM_END) {
+            TRACE("end of data\n");
+            req->gzip_stream->end_of_data = TRUE;
+            inflateEnd(&req->gzip_stream->zstream);
+        }else if(zres != Z_OK) {
+            WARN("inflate failed %d\n", zres);
+            if(!read)
+                ret = ERROR_INTERNET_DECODING_FAILED;
+            break;
+        }
+    }
+#endif
+
+    *read_ret = read;
+    return ret;
+}
+
+static void refill_gzip_buffer(http_request_t *req)
+{
+    DWORD res;
+    int len;
+
+    if(!req->gzip_stream || !req->read_size || req->gzip_stream->buf_size == sizeof(req->gzip_stream->buf))
+        return;
+
+    if(req->gzip_stream->buf_pos) {
+        if(req->gzip_stream->buf_size)
+            memmove(req->gzip_stream->buf, req->gzip_stream->buf + req->gzip_stream->buf_pos, req->gzip_stream->buf_size);
+        req->gzip_stream->buf_pos = 0;
+    }
+
+    res = read_gzip_data(req, req->gzip_stream->buf + req->gzip_stream->buf_size,
+            sizeof(req->gzip_stream->buf) - req->gzip_stream->buf_size, FALSE, &len);
+    if(res == ERROR_SUCCESS)
+        req->gzip_stream->buf_size += len;
+}
+
+/* return the size of data available to be read immediately (the read section must be held) */
+static DWORD get_avail_data( http_request_t *req )
+{
+    if (req->gzip_stream) {
+        refill_gzip_buffer(req);
+        return req->gzip_stream->buf_size;
+    }
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
+        return 0;
+    return min( req->read_size, req->dwContentLength - req->dwContentRead );
+}
+
+static void HTTP_ReceiveRequestData(http_request_t *req, BOOL first_notif)
+{
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res;
+
+    TRACE("%p\n", req);
+
+    EnterCriticalSection( &req->read_section );
+    if ((res = refill_buffer( req )) == ERROR_SUCCESS) {
+        iar.dwResult = (DWORD_PTR)req->hdr.hInternet;
+        iar.dwError = first_notif ? 0 : get_avail_data(req);
+    }else {
+        iar.dwResult = 0;
+        iar.dwError = res;
+    }
+    LeaveCriticalSection( &req->read_section );
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+}
+
+/* read data from the http connection (the read section must be held) */
+static DWORD HTTPREQ_Read(http_request_t *req, void *buffer, DWORD size, DWORD *read, BOOL sync)
+{
+    BOOL finished_reading = FALSE;
+    int len, bytes_read = 0;
+    DWORD ret = ERROR_SUCCESS;
+
+    EnterCriticalSection( &req->read_section );
+
+    if (req->read_chunked && (req->dwContentLength == ~0u || req->dwContentLength == req->dwContentRead))
     {
-        return HTTP_ReadChunked(req, buffer, size, read, sync);
+        if (start_next_chunk( req ) != ERROR_SUCCESS) goto done;
     }
-    else
-        return HTTP_Read(req, buffer, size, read, sync);
+
+    if(req->gzip_stream) {
+        if(req->gzip_stream->buf_size) {
+            bytes_read = min(req->gzip_stream->buf_size, size);
+            memcpy(buffer, req->gzip_stream->buf + req->gzip_stream->buf_pos, bytes_read);
+            req->gzip_stream->buf_pos += bytes_read;
+            req->gzip_stream->buf_size -= bytes_read;
+        }else if(!req->read_size && !req->gzip_stream->end_of_data) {
+            refill_buffer(req);
+        }
+
+        if(size > bytes_read) {
+            ret = read_gzip_data(req, (BYTE*)buffer+bytes_read, size-bytes_read, sync, &len);
+            if(ret == ERROR_SUCCESS)
+                bytes_read += len;
+        }
+
+        finished_reading = req->gzip_stream->end_of_data && !req->gzip_stream->buf_size;
+    }else {
+        if (req->dwContentLength != ~0u) size = min( size, req->dwContentLength - req->dwContentRead );
+
+        if (req->read_size) {
+            bytes_read = min( req->read_size, size );
+            memcpy( buffer, req->read_buf + req->read_pos, bytes_read );
+            remove_data( req, bytes_read );
+        }
+
+        if (size > bytes_read && (!bytes_read || sync)) {
+            if (NETCON_recv( &req->netConnection, (char *)buffer + bytes_read, size - bytes_read,
+                             sync ? MSG_WAITALL : 0, &len) == ERROR_SUCCESS)
+                bytes_read += len;
+            /* always return success, even if the network layer returns an error */
+        }
+
+        finished_reading = !bytes_read && req->dwContentRead == req->dwContentLength;
+        req->dwContentRead += bytes_read;
+    }
+done:
+    *read = bytes_read;
+
+    TRACE( "retrieved %u bytes (%u/%u)\n", bytes_read, req->dwContentRead, req->dwContentLength );
+    LeaveCriticalSection( &req->read_section );
+
+    if(ret == ERROR_SUCCESS && req->lpszCacheFile) {
+        BOOL res;
+        DWORD dwBytesWritten;
+
+        res = WriteFile(req->hCacheFile, buffer, bytes_read, &dwBytesWritten, NULL);
+        if(!res)
+            WARN("WriteFile failed: %u\n", GetLastError());
+    }
+
+    if(finished_reading)
+        HTTP_FinishedReading(req);
+
+    return ret;
 }
 
-static DWORD HTTPREQ_ReadFile(WININETHANDLEHEADER *hdr, void *buffer, DWORD size, DWORD *read)
+
+static DWORD HTTPREQ_ReadFile(object_header_t *hdr, void *buffer, DWORD size, DWORD *read)
 {
-    WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
+    http_request_t *req = (http_request_t*)hdr;
     return HTTPREQ_Read(req, buffer, size, read, TRUE);
 }
 
-static void HTTPREQ_AsyncReadFileExProc(WORKREQUEST *workRequest)
+static void HTTPREQ_AsyncReadFileExAProc(WORKREQUEST *workRequest)
 {
     struct WORKREQ_INTERNETREADFILEEXA const *data = &workRequest->u.InternetReadFileExA;
-    WININETHTTPREQW *req = (WININETHTTPREQW*)workRequest->hdr;
+    http_request_t *req = (http_request_t*)workRequest->hdr;
     INTERNET_ASYNC_RESULT iar;
     DWORD res;
 
@@ -1759,11 +2143,10 @@ static void HTTPREQ_AsyncReadFileExProc(WORKREQUEST *workRequest)
                           sizeof(INTERNET_ASYNC_RESULT));
 }
 
-static DWORD HTTPREQ_ReadFileExA(WININETHANDLEHEADER *hdr, INTERNET_BUFFERSA *buffers,
+static DWORD HTTPREQ_ReadFileExA(object_header_t *hdr, INTERNET_BUFFERSA *buffers,
         DWORD flags, DWORD_PTR context)
 {
-
-    WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
+    http_request_t *req = (http_request_t*)hdr;
     DWORD res;
 
     if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
@@ -1774,27 +2157,35 @@ static DWORD HTTPREQ_ReadFileExA(WININETHANDLEHEADER *hdr, INTERNET_BUFFERSA *bu
 
     INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
 
-    if (hdr->dwFlags & INTERNET_FLAG_ASYNC) {
-        DWORD available = 0;
+    if ((hdr->dwFlags & INTERNET_FLAG_ASYNC) && !get_avail_data(req))
+    {
+        WORKREQUEST workRequest;
 
-        NETCON_query_data_available(&req->netConnection, &available);
-        if (!available)
+        if (TryEnterCriticalSection( &req->read_section ))
         {
-            WORKREQUEST workRequest;
+            if (get_avail_data(req))
+            {
+                res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
+                                   &buffers->dwBufferLength, FALSE);
+                LeaveCriticalSection( &req->read_section );
+                goto done;
+            }
+            LeaveCriticalSection( &req->read_section );
+        }
 
-            workRequest.asyncproc = HTTPREQ_AsyncReadFileExProc;
-            workRequest.hdr = WININET_AddRef(&req->hdr);
-            workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
+        workRequest.asyncproc = HTTPREQ_AsyncReadFileExAProc;
+        workRequest.hdr = WININET_AddRef(&req->hdr);
+        workRequest.u.InternetReadFileExA.lpBuffersOut = buffers;
 
-            INTERNET_AsyncCall(&workRequest);
+        INTERNET_AsyncCall(&workRequest);
 
-            return ERROR_IO_PENDING;
-        }
+        return ERROR_IO_PENDING;
     }
 
     res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
             !(flags & IRF_NO_WAIT));
 
+done:
     if (res == ERROR_SUCCESS) {
         DWORD size = buffers->dwBufferLength;
         INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
@@ -1804,52 +2195,120 @@ static DWORD HTTPREQ_ReadFileExA(WININETHANDLEHEADER *hdr, INTERNET_BUFFERSA *bu
     return res;
 }
 
-static BOOL HTTPREQ_WriteFile(WININETHANDLEHEADER *hdr, const void *buffer, DWORD size, DWORD *written)
+static void HTTPREQ_AsyncReadFileExWProc(WORKREQUEST *workRequest)
 {
-    LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW)hdr;
+    struct WORKREQ_INTERNETREADFILEEXW const *data = &workRequest->u.InternetReadFileExW;
+    http_request_t *req = (http_request_t*)workRequest->hdr;
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res;
+
+    TRACE("INTERNETREADFILEEXW %p\n", workRequest->hdr);
+
+    res = HTTPREQ_Read(req, data->lpBuffersOut->lpvBuffer,
+            data->lpBuffersOut->dwBufferLength, &data->lpBuffersOut->dwBufferLength, TRUE);
+
+    iar.dwResult = res == ERROR_SUCCESS;
+    iar.dwError = res;
 
-    return NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext,
+                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
 }
 
-static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
+static DWORD HTTPREQ_ReadFileExW(object_header_t *hdr, INTERNET_BUFFERSW *buffers,
+        DWORD flags, DWORD_PTR context)
 {
-    WININETHTTPREQW *req = (WININETHTTPREQW*)workRequest->hdr;
-    INTERNET_ASYNC_RESULT iar;
-    char buffer[4048];
 
-    TRACE("%p\n", workRequest->hdr);
+    http_request_t *req = (http_request_t*)hdr;
+    DWORD res;
 
-    iar.dwResult = NETCON_recv(&req->netConnection, buffer,
-                               min(sizeof(buffer), req->dwContentLength - req->dwContentRead),
-                               MSG_PEEK, (int *)&iar.dwError);
+    if (flags & ~(IRF_ASYNC|IRF_NO_WAIT))
+        FIXME("these dwFlags aren't implemented: 0x%x\n", flags & ~(IRF_ASYNC|IRF_NO_WAIT));
 
-    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE, &iar,
-                          sizeof(INTERNET_ASYNC_RESULT));
+    if (buffers->dwStructSize != sizeof(*buffers))
+        return ERROR_INVALID_PARAMETER;
+
+    INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+
+    if (hdr->dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+
+        if (TryEnterCriticalSection( &req->read_section ))
+        {
+            if (get_avail_data(req))
+            {
+                res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength,
+                                   &buffers->dwBufferLength, FALSE);
+                LeaveCriticalSection( &req->read_section );
+                goto done;
+            }
+            LeaveCriticalSection( &req->read_section );
+        }
+
+        workRequest.asyncproc = HTTPREQ_AsyncReadFileExWProc;
+        workRequest.hdr = WININET_AddRef(&req->hdr);
+        workRequest.u.InternetReadFileExW.lpBuffersOut = buffers;
+
+        INTERNET_AsyncCall(&workRequest);
+
+        return ERROR_IO_PENDING;
+    }
+
+    res = HTTPREQ_Read(req, buffers->lpvBuffer, buffers->dwBufferLength, &buffers->dwBufferLength,
+            !(flags & IRF_NO_WAIT));
+
+done:
+    if (res == ERROR_SUCCESS) {
+        DWORD size = buffers->dwBufferLength;
+        INTERNET_SendCallback(&req->hdr, req->hdr.dwContext, INTERNET_STATUS_RESPONSE_RECEIVED,
+                &size, sizeof(size));
+    }
+
+    return res;
 }
 
-static DWORD HTTPREQ_QueryDataAvailable(WININETHANDLEHEADER *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
+static DWORD HTTPREQ_WriteFile(object_header_t *hdr, const void *buffer, DWORD size, DWORD *written)
 {
-    WININETHTTPREQW *req = (WININETHTTPREQW*)hdr;
-    BYTE buffer[4048];
-    BOOL async;
+    DWORD res;
+    http_request_t *lpwhr = (http_request_t*)hdr;
 
-    TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
 
-    if(!NETCON_query_data_available(&req->netConnection, available) || *available)
-        return ERROR_SUCCESS;
+    *written = 0;
+    res = NETCON_send(&lpwhr->netConnection, buffer, size, 0, (LPINT)written);
+    if (res == ERROR_SUCCESS)
+        lpwhr->dwBytesWritten += *written;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REQUEST_SENT, written, sizeof(DWORD));
+    return res;
+}
 
-    /* Even if we are in async mode, we need to determine whether
-     * there is actually more data available. We do this by trying
-     * to peek only a single byte in async mode. */
-    async = (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC) != 0;
+static void HTTPREQ_AsyncQueryDataAvailableProc(WORKREQUEST *workRequest)
+{
+    http_request_t *req = (http_request_t*)workRequest->hdr;
+
+    HTTP_ReceiveRequestData(req, FALSE);
+}
+
+static DWORD HTTPREQ_QueryDataAvailable(object_header_t *hdr, DWORD *available, DWORD flags, DWORD_PTR ctx)
+{
+    http_request_t *req = (http_request_t*)hdr;
+
+    TRACE("(%p %p %x %lx)\n", req, available, flags, ctx);
 
-    if (NETCON_recv(&req->netConnection, buffer,
-                    min(async ? 1 : sizeof(buffer), req->dwContentLength - req->dwContentRead),
-                    MSG_PEEK, (int *)available) && async && *available)
+    if (req->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
     {
         WORKREQUEST workRequest;
 
-        *available = 0;
+        /* never wait, if we can't enter the section we queue an async request right away */
+        if (TryEnterCriticalSection( &req->read_section ))
+        {
+            if ((*available = get_avail_data( req ))) goto done;
+            if (end_of_read_data( req )) goto done;
+            LeaveCriticalSection( &req->read_section );
+        }
+
         workRequest.asyncproc = HTTPREQ_AsyncQueryDataAvailableProc;
         workRequest.hdr = WININET_AddRef( &req->hdr );
 
@@ -1858,16 +2317,35 @@ static DWORD HTTPREQ_QueryDataAvailable(WININETHANDLEHEADER *hdr, DWORD *availab
         return ERROR_IO_PENDING;
     }
 
+    EnterCriticalSection( &req->read_section );
+
+    if (!(*available = get_avail_data( req )) && !end_of_read_data( req ))
+    {
+        refill_buffer( req );
+        *available = get_avail_data( req );
+    }
+
+done:
+    if (*available == sizeof(req->read_buf) && !req->gzip_stream)  /* check if we have even more pending in the socket */
+    {
+        DWORD extra;
+        if (NETCON_query_data_available(&req->netConnection, &extra))
+            *available = min( *available + extra, req->dwContentLength - req->dwContentRead );
+    }
+    LeaveCriticalSection( &req->read_section );
+
+    TRACE( "returning %u\n", *available );
     return ERROR_SUCCESS;
 }
 
-static const HANDLEHEADERVtbl HTTPREQVtbl = {
+static const object_vtbl_t HTTPREQVtbl = {
     HTTPREQ_Destroy,
     HTTPREQ_CloseConnection,
     HTTPREQ_QueryOption,
     HTTPREQ_SetOption,
     HTTPREQ_ReadFile,
     HTTPREQ_ReadFileExA,
+    HTTPREQ_ReadFileExW,
     HTTPREQ_WriteFile,
     HTTPREQ_QueryDataAvailable,
     NULL
@@ -1883,27 +2361,27 @@ static const HANDLEHEADERVtbl HTTPREQVtbl = {
  *    NULL      on failure
  *
  */
-HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
-       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
-       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
-       DWORD dwFlags, DWORD_PTR dwContext)
+static DWORD HTTP_HttpOpenRequestW(http_session_t *lpwhs,
+        LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
+        LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
+        DWORD dwFlags, DWORD_PTR dwContext, HINTERNET *ret)
 {
-    LPWININETAPPINFOW hIC = NULL;
-    LPWININETHTTPREQW lpwhr;
+    appinfo_t *hIC = NULL;
+    http_request_t *lpwhr;
     LPWSTR lpszHostName = NULL;
     HINTERNET handle = NULL;
     static const WCHAR szHostForm[] = {'%','s',':','%','u',0};
-    DWORD len;
+    DWORD len, res;
 
     TRACE("-->\n");
 
     assert( lpwhs->hdr.htype == WH_HHTTPSESSION );
     hIC = lpwhs->lpAppInfo;
 
-    lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPREQW));
+    lpwhr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_request_t));
     if (NULL == lpwhr)
     {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
     lpwhr->hdr.htype = WH_HHTTPREQ;
@@ -1913,6 +2391,8 @@ HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
     lpwhr->hdr.refs = 1;
     lpwhr->hdr.lpfnStatusCB = lpwhs->hdr.lpfnStatusCB;
     lpwhr->hdr.dwInternalFlags = lpwhs->hdr.dwInternalFlags & INET_CALLBACKW;
+    lpwhr->dwContentLength = ~0u;
+    InitializeCriticalSection( &lpwhr->read_section );
 
     WININET_AddRef( &lpwhs->hdr );
     lpwhr->lpHttpSession = lpwhs;
@@ -1922,18 +2402,18 @@ HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
             (strlenW(lpwhs->lpszHostName) + 7 /* length of ":65535" + 1 */));
     if (NULL == lpszHostName)
     {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
 
     handle = WININET_AllocHandle( &lpwhr->hdr );
     if (NULL == handle)
     {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+        res = ERROR_OUTOFMEMORY;
         goto lend;
     }
 
-    if (!NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE))
+    if ((res = NETCON_init(&lpwhr->netConnection, dwFlags & INTERNET_FLAG_SECURE)) != ERROR_SUCCESS)
     {
         InternetCloseHandle( handle );
         handle = NULL;
@@ -1950,11 +2430,15 @@ HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
         rc = UrlEscapeW(lpszObjectName, lpwhr->lpszPath, &len,
                    URL_ESCAPE_SPACES_ONLY);
-        if (rc)
+        if (rc != S_OK)
         {
             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(lpszObjectName),rc);
             strcpyW(lpwhr->lpszPath,lpszObjectName);
         }
+    }else {
+        static const WCHAR slashW[] = {'/',0};
+
+        lpwhr->lpszPath = heap_strdupW(slashW);
     }
 
     if (lpszReferrer && *lpszReferrer)
@@ -1973,23 +2457,19 @@ HINTERNET WINAPI HTTP_HttpOpenRequestW(LPWININETHTTPSESSIONW lpwhs,
         }
     }
 
-    lpwhr->lpszVerb = WININET_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
-
-    if (lpszVersion)
-        lpwhr->lpszVersion = WININET_strdupW(lpszVersion);
-    else
-        lpwhr->lpszVersion = WININET_strdupW(g_szHttp1_1);
+    lpwhr->lpszVerb = heap_strdupW(lpszVerb && *lpszVerb ? lpszVerb : szGET);
+    lpwhr->lpszVersion = heap_strdupW(lpszVersion ? lpszVersion : g_szHttp1_1);
 
     if (lpwhs->nHostPort != INTERNET_INVALID_PORT_NUMBER &&
         lpwhs->nHostPort != INTERNET_DEFAULT_HTTP_PORT &&
         lpwhs->nHostPort != INTERNET_DEFAULT_HTTPS_PORT)
     {
         sprintfW(lpszHostName, szHostForm, lpwhs->lpszHostName, lpwhs->nHostPort);
-        HTTP_ProcessHeader(lpwhr, szHost, lpszHostName,
+        HTTP_ProcessHeader(lpwhr, hostW, lpszHostName,
                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
     }
     else
-        HTTP_ProcessHeader(lpwhr, szHost, lpwhs->lpszHostName,
+        HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName,
                 HTTP_ADDREQ_FLAG_ADD | HTTP_ADDHDR_FLAG_REQ);
 
     if (lpwhs->nServerPort == INTERNET_INVALID_PORT_NUMBER)
@@ -2015,80 +2495,88 @@ lend:
         WININET_Release( &lpwhr->hdr );
 
     TRACE("<-- %p (%p)\n", handle, lpwhr);
-    return handle;
+    *ret = handle;
+    return res;
 }
 
-/* read any content returned by the server so that the connection can be
- * reused */
-static void HTTP_DrainContent(WININETHTTPREQW *req)
+/***********************************************************************
+ *           HttpOpenRequestW (WININET.@)
+ *
+ * Open a HTTP request handle
+ *
+ * RETURNS
+ *    HINTERNET  a HTTP request handle on success
+ *    NULL      on failure
+ *
+ */
+HINTERNET WINAPI HttpOpenRequestW(HINTERNET hHttpSession,
+       LPCWSTR lpszVerb, LPCWSTR lpszObjectName, LPCWSTR lpszVersion,
+       LPCWSTR lpszReferrer , LPCWSTR *lpszAcceptTypes,
+       DWORD dwFlags, DWORD_PTR dwContext)
 {
-    DWORD bytes_read;
-
-    if (!NETCON_connected(&req->netConnection)) return;
+    http_session_t *lpwhs;
+    HINTERNET handle = NULL;
+    DWORD res;
 
-    if (req->dwContentLength == -1)
-        NETCON_close(&req->netConnection);
+    TRACE("(%p, %s, %s, %s, %s, %p, %08x, %08lx)\n", hHttpSession,
+          debugstr_w(lpszVerb), debugstr_w(lpszObjectName),
+          debugstr_w(lpszVersion), debugstr_w(lpszReferrer), lpszAcceptTypes,
+          dwFlags, dwContext);
+    if(lpszAcceptTypes!=NULL)
+    {
+        int i;
+        for(i=0;lpszAcceptTypes[i]!=NULL;i++)
+            TRACE("\taccept type: %s\n",debugstr_w(lpszAcceptTypes[i]));
+    }
 
-    do
+    lpwhs = (http_session_t*) WININET_GetObject( hHttpSession );
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
     {
-        char buffer[2048];
-        if (HTTP_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
-            return;
-    } while (bytes_read);
-}
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-static const WCHAR szAccept[] = { 'A','c','c','e','p','t',0 };
-static const WCHAR szAccept_Charset[] = { 'A','c','c','e','p','t','-','C','h','a','r','s','e','t', 0 };
-static const WCHAR szAccept_Encoding[] = { 'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szAccept_Language[] = { 'A','c','c','e','p','t','-','L','a','n','g','u','a','g','e',0 };
-static const WCHAR szAccept_Ranges[] = { 'A','c','c','e','p','t','-','R','a','n','g','e','s',0 };
-static const WCHAR szAge[] = { 'A','g','e',0 };
-static const WCHAR szAllow[] = { 'A','l','l','o','w',0 };
-static const WCHAR szCache_Control[] = { 'C','a','c','h','e','-','C','o','n','t','r','o','l',0 };
-static const WCHAR szConnection[] = { 'C','o','n','n','e','c','t','i','o','n',0 };
-static const WCHAR szContent_Base[] = { 'C','o','n','t','e','n','t','-','B','a','s','e',0 };
-static const WCHAR szContent_Encoding[] = { 'C','o','n','t','e','n','t','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szContent_ID[] = { 'C','o','n','t','e','n','t','-','I','D',0 };
-static const WCHAR szContent_Language[] = { 'C','o','n','t','e','n','t','-','L','a','n','g','u','a','g','e',0 };
-static const WCHAR szContent_Length[] = { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0 };
-static const WCHAR szContent_Location[] = { 'C','o','n','t','e','n','t','-','L','o','c','a','t','i','o','n',0 };
-static const WCHAR szContent_MD5[] = { 'C','o','n','t','e','n','t','-','M','D','5',0 };
-static const WCHAR szContent_Range[] = { 'C','o','n','t','e','n','t','-','R','a','n','g','e',0 };
-static const WCHAR szContent_Transfer_Encoding[] = { 'C','o','n','t','e','n','t','-','T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szContent_Type[] = { 'C','o','n','t','e','n','t','-','T','y','p','e',0 };
-static const WCHAR szCookie[] = { 'C','o','o','k','i','e',0 };
-static const WCHAR szDate[] = { 'D','a','t','e',0 };
-static const WCHAR szFrom[] = { 'F','r','o','m',0 };
-static const WCHAR szETag[] = { 'E','T','a','g',0 };
-static const WCHAR szExpect[] = { 'E','x','p','e','c','t',0 };
-static const WCHAR szExpires[] = { 'E','x','p','i','r','e','s',0 };
-static const WCHAR szIf_Match[] = { 'I','f','-','M','a','t','c','h',0 };
-static const WCHAR szIf_Modified_Since[] = { 'I','f','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szIf_None_Match[] = { 'I','f','-','N','o','n','e','-','M','a','t','c','h',0 };
-static const WCHAR szIf_Range[] = { 'I','f','-','R','a','n','g','e',0 };
-static const WCHAR szIf_Unmodified_Since[] = { 'I','f','-','U','n','m','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szLast_Modified[] = { 'L','a','s','t','-','M','o','d','i','f','i','e','d',0 };
-static const WCHAR szLocation[] = { 'L','o','c','a','t','i','o','n',0 };
-static const WCHAR szMax_Forwards[] = { 'M','a','x','-','F','o','r','w','a','r','d','s',0 };
-static const WCHAR szMime_Version[] = { 'M','i','m','e','-','V','e','r','s','i','o','n',0 };
-static const WCHAR szPragma[] = { 'P','r','a','g','m','a',0 };
-static const WCHAR szProxy_Authenticate[] = { 'P','r','o','x','y','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
-static const WCHAR szProxy_Connection[] = { 'P','r','o','x','y','-','C','o','n','n','e','c','t','i','o','n',0 };
-static const WCHAR szPublic[] = { 'P','u','b','l','i','c',0 };
-static const WCHAR szRange[] = { 'R','a','n','g','e',0 };
-static const WCHAR szReferer[] = { 'R','e','f','e','r','e','r',0 };
-static const WCHAR szRetry_After[] = { 'R','e','t','r','y','-','A','f','t','e','r',0 };
-static const WCHAR szServer[] = { 'S','e','r','v','e','r',0 };
-static const WCHAR szSet_Cookie[] = { 'S','e','t','-','C','o','o','k','i','e',0 };
-static const WCHAR szTransfer_Encoding[] = { 'T','r','a','n','s','f','e','r','-','E','n','c','o','d','i','n','g',0 };
-static const WCHAR szUnless_Modified_Since[] = { 'U','n','l','e','s','s','-','M','o','d','i','f','i','e','d','-','S','i','n','c','e',0 };
-static const WCHAR szUpgrade[] = { 'U','p','g','r','a','d','e',0 };
-static const WCHAR szURI[] = { 'U','R','I',0 };
-static const WCHAR szUser_Agent[] = { 'U','s','e','r','-','A','g','e','n','t',0 };
-static const WCHAR szVary[] = { 'V','a','r','y',0 };
-static const WCHAR szVia[] = { 'V','i','a',0 };
-static const WCHAR szWarning[] = { 'W','a','r','n','i','n','g',0 };
-static const WCHAR szWWW_Authenticate[] = { 'W','W','W','-','A','u','t','h','e','n','t','i','c','a','t','e',0 };
+    /*
+     * My tests seem to show that the windows version does not
+     * become asynchronous until after this point. And anyhow
+     * if this call was asynchronous then how would you get the
+     * necessary HINTERNET pointer returned by this function.
+     *
+     */
+    res = HTTP_HttpOpenRequestW(lpwhs, lpszVerb, lpszObjectName,
+                                lpszVersion, lpszReferrer, lpszAcceptTypes,
+                                dwFlags, dwContext, &handle);
+lend:
+    if( lpwhs )
+        WININET_Release( &lpwhs->hdr );
+    TRACE("returning %p\n", handle);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return handle;
+}
+
+/* read any content returned by the server so that the connection can be
+ * reused */
+static void HTTP_DrainContent(http_request_t *req)
+{
+    DWORD bytes_read;
+
+    if (!NETCON_connected(&req->netConnection)) return;
+
+    if (req->dwContentLength == -1)
+    {
+        NETCON_close(&req->netConnection);
+        return;
+    }
+    if (!strcmpW(req->lpszVerb, szHEAD)) return;
+
+    do
+    {
+        char buffer[2048];
+        if (HTTPREQ_Read(req, buffer, sizeof(buffer), &bytes_read, TRUE) != ERROR_SUCCESS)
+            return;
+    } while (bytes_read);
+}
 
 static const LPCWSTR header_lookup[] = {
     szMime_Version,            /* HTTP_QUERY_MIME_VERSION = 0 */
@@ -2146,7 +2634,7 @@ static const LPCWSTR header_lookup[] = {
     szContent_MD5,             /* HTTP_QUERY_CONTENT_MD5 = 52 */
     szContent_Range,           /* HTTP_QUERY_CONTENT_RANGE = 53 */
     szETag,                    /* HTTP_QUERY_ETAG = 54 */
-    szHost,                    /* HTTP_QUERY_HOST = 55 */
+    hostW,                     /* HTTP_QUERY_HOST = 55 */
     szIf_Match,                        /* HTTP_QUERY_IF_MATCH = 56 */
     szIf_None_Match,           /* HTTP_QUERY_IF_NONE_MATCH = 57 */
     szIf_Range,                        /* HTTP_QUERY_IF_RANGE = 58 */
@@ -2169,58 +2657,64 @@ static const LPCWSTR header_lookup[] = {
 /***********************************************************************
  *           HTTP_HttpQueryInfoW (internal)
  */
-static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLevel,
-       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+static DWORD HTTP_HttpQueryInfoW(http_request_t *lpwhr, DWORD dwInfoLevel,
+        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
 {
     LPHTTPHEADERW lphttpHdr = NULL;
-    BOOL bSuccess = FALSE;
     BOOL request_only = dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS;
     INT requested_index = lpdwIndex ? *lpdwIndex : 0;
-    INT level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
+    DWORD level = (dwInfoLevel & ~HTTP_QUERY_MODIFIER_FLAGS_MASK);
     INT index = -1;
 
     /* Find requested header structure */
     switch (level)
     {
     case HTTP_QUERY_CUSTOM:
+        if (!lpBuffer) return ERROR_INVALID_PARAMETER;
         index = HTTP_GetCustomHeaderIndex(lpwhr, lpBuffer, requested_index, request_only);
         break;
-
     case HTTP_QUERY_RAW_HEADERS_CRLF:
         {
             LPWSTR headers;
-            DWORD len;
-            BOOL ret = FALSE;
+            DWORD len = 0;
+            DWORD res = ERROR_INVALID_PARAMETER;
 
             if (request_only)
                 headers = HTTP_BuildHeaderRequestString(lpwhr, lpwhr->lpszVerb, lpwhr->lpszPath, lpwhr->lpszVersion);
             else
                 headers = lpwhr->lpszRawHeaders;
 
-            len = (strlenW(headers) + 1) * sizeof(WCHAR);
-            if (len > *lpdwBufferLength)
+            if (headers)
+                len = strlenW(headers) * sizeof(WCHAR);
+
+            if (len + sizeof(WCHAR) > *lpdwBufferLength)
             {
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                ret = FALSE;
+                len += sizeof(WCHAR);
+                res = ERROR_INSUFFICIENT_BUFFER;
             }
             else if (lpBuffer)
             {
-                memcpy(lpBuffer, headers, len);
+                if (headers)
+                    memcpy(lpBuffer, headers, len + sizeof(WCHAR));
+                else
+                {
+                    len = strlenW(szCrLf) * sizeof(WCHAR);
+                    memcpy(lpBuffer, szCrLf, sizeof(szCrLf));
+                }
                 TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len / sizeof(WCHAR)));
-                ret = TRUE;
+                res = ERROR_SUCCESS;
             }
             *lpdwBufferLength = len;
 
             if (request_only)
                 HeapFree(GetProcessHeap(), 0, headers);
-            return ret;
+            return res;
         }
     case HTTP_QUERY_RAW_HEADERS:
         {
-            static const WCHAR szCrLf[] = {'\r','\n',0};
             LPWSTR * ppszRawHeaderLines = HTTP_Tokenize(lpwhr->lpszRawHeaders, szCrLf);
             DWORD i, size = 0;
-            LPWSTR pszString = (WCHAR*)lpBuffer;
+            LPWSTR pszString = lpBuffer;
 
             for (i = 0; ppszRawHeaderLines[i]; i++)
                 size += strlenW(ppszRawHeaderLines[i]) + 1;
@@ -2229,24 +2723,23 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
             {
                 HTTP_FreeTokens(ppszRawHeaderLines);
                 *lpdwBufferLength = (size + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
+                return ERROR_INSUFFICIENT_BUFFER;
             }
-
-            for (i = 0; ppszRawHeaderLines[i]; i++)
+            if (pszString)
             {
-                DWORD len = strlenW(ppszRawHeaderLines[i]);
-                memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
-                pszString += len+1;
+                for (i = 0; ppszRawHeaderLines[i]; i++)
+                {
+                    DWORD len = strlenW(ppszRawHeaderLines[i]);
+                    memcpy(pszString, ppszRawHeaderLines[i], (len+1)*sizeof(WCHAR));
+                    pszString += len+1;
+                }
+                *pszString = '\0';
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, size));
             }
-            *pszString = '\0';
-
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, size));
-
             *lpdwBufferLength = size * sizeof(WCHAR);
             HTTP_FreeTokens(ppszRawHeaderLines);
 
-            return TRUE;
+            return ERROR_SUCCESS;
         }
     case HTTP_QUERY_STATUS_TEXT:
         if (lpwhr->lpszStatusText)
@@ -2255,15 +2748,15 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
             {
                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
+            if (lpBuffer)
+            {
+                memcpy(lpBuffer, lpwhr->lpszStatusText, (len + 1) * sizeof(WCHAR));
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
             }
-            memcpy(lpBuffer, lpwhr->lpszStatusText, (len+1)*sizeof(WCHAR));
             *lpdwBufferLength = len * sizeof(WCHAR);
-
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, len));
-
-            return TRUE;
+            return ERROR_SUCCESS;
         }
         break;
     case HTTP_QUERY_VERSION:
@@ -2273,21 +2766,25 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
             if (len + 1 > *lpdwBufferLength/sizeof(WCHAR))
             {
                 *lpdwBufferLength = (len + 1) * sizeof(WCHAR);
-                INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-                return FALSE;
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
+            if (lpBuffer)
+            {
+                memcpy(lpBuffer, lpwhr->lpszVersion, (len + 1) * sizeof(WCHAR));
+                TRACE("returning data: %s\n", debugstr_wn(lpBuffer, len));
             }
-            memcpy(lpBuffer, lpwhr->lpszVersion, (len+1)*sizeof(WCHAR));
             *lpdwBufferLength = len * sizeof(WCHAR);
-
-            TRACE("returning data: %s\n", debugstr_wn((WCHAR*)lpBuffer, len));
-
-            return TRUE;
+            return ERROR_SUCCESS;
         }
         break;
+    case HTTP_QUERY_CONTENT_ENCODING:
+        index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[lpwhr->gzip_stream ? HTTP_QUERY_CONTENT_TYPE : level],
+                requested_index,request_only);
+        break;
     default:
         assert (LAST_TABLE_HEADER == (HTTP_QUERY_UNLESS_MODIFIED_SINCE + 1));
 
-        if (level >= 0 && level < LAST_TABLE_HEADER && header_lookup[level])
+        if (level < LAST_TABLE_HEADER && header_lookup[level])
             index = HTTP_GetCustomHeaderIndex(lpwhr, header_lookup[level],
                                               requested_index,request_only);
     }
@@ -2300,22 +2797,18 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
         ((dwInfoLevel & HTTP_QUERY_FLAG_REQUEST_HEADERS) &&
          (~lphttpHdr->wFlags & HDR_ISREQUEST)))
     {
-        INTERNET_SetLastError(ERROR_HTTP_HEADER_NOT_FOUND);
-        return bSuccess;
+        return ERROR_HTTP_HEADER_NOT_FOUND;
     }
 
-    if (lpdwIndex)
-        (*lpdwIndex)++;
+    if (lpdwIndex && level != HTTP_QUERY_STATUS_CODE) (*lpdwIndex)++;
 
     /* coalesce value to requested type */
-    if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER)
+    if (dwInfoLevel & HTTP_QUERY_FLAG_NUMBER && lpBuffer)
     {
-       *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
-       bSuccess = TRUE;
-
-       TRACE(" returning number : %d\n", *(int *)lpBuffer);
-    }
-    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME)
+        *(int *)lpBuffer = atoiW(lphttpHdr->lpszValue);
+        TRACE(" returning number: %d\n", *(int *)lpBuffer);
+     }
+    else if (dwInfoLevel & HTTP_QUERY_FLAG_SYSTEMTIME && lpBuffer)
     {
         time_t tmpTime;
         struct tm tmpTM;
@@ -2324,24 +2817,19 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
         tmpTime = ConvertTimeString(lphttpHdr->lpszValue);
 
         tmpTM = *gmtime(&tmpTime);
-        STHook = (SYSTEMTIME *) lpBuffer;
-        if(STHook==NULL)
-            return bSuccess;
-
-       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;
-       
-       TRACE(" returning time : %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n", 
-             STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
-             STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
+        STHook = (SYSTEMTIME *)lpBuffer;
+        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;
+
+        TRACE(" returning time: %04d/%02d/%02d - %d - %02d:%02d:%02d.%02d\n",
+              STHook->wYear, STHook->wMonth, STHook->wDay, STHook->wDayOfWeek,
+              STHook->wHour, STHook->wMinute, STHook->wSecond, STHook->wMilliseconds);
     }
     else if (lphttpHdr->lpszValue)
     {
@@ -2350,17 +2838,16 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
         if (len > *lpdwBufferLength)
         {
             *lpdwBufferLength = len;
-            INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
-            return bSuccess;
+            return ERROR_INSUFFICIENT_BUFFER;
+        }
+        if (lpBuffer)
+        {
+            memcpy(lpBuffer, lphttpHdr->lpszValue, len);
+            TRACE("! returning string: %s\n", debugstr_w(lpBuffer));
         }
-
-        memcpy(lpBuffer, lphttpHdr->lpszValue, len);
         *lpdwBufferLength = len - sizeof(WCHAR);
-        bSuccess = TRUE;
-
-       TRACE(" returning string : %s\n", debugstr_w(lpBuffer));
     }
-    return bSuccess;
+    return ERROR_SUCCESS;
 }
 
 /***********************************************************************
@@ -2374,10 +2861,10 @@ static BOOL WINAPI HTTP_HttpQueryInfoW( LPWININETHTTPREQW lpwhr, DWORD dwInfoLev
  *
  */
 BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
-       LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
+        LPVOID lpBuffer, LPDWORD lpdwBufferLength, LPDWORD lpdwIndex)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPREQW lpwhr;
+    http_request_t *lpwhr;
+    DWORD res;
 
     if (TRACE_ON(wininet)) {
 #define FE(x) { x, #x }
@@ -2463,7 +2950,7 @@ BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
        DWORD info = dwInfoLevel & HTTP_QUERY_HEADER_MASK;
        DWORD i;
 
-       TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, dwInfoLevel);
+       TRACE("(%p, 0x%08x)--> %d\n", hHttpRequest, dwInfoLevel, info);
        TRACE("  Attribute:");
        for (i = 0; i < (sizeof(query_flags) / sizeof(query_flags[0])); i++) {
            if (query_flags[i].val == info) {
@@ -2489,24 +2976,26 @@ BOOL WINAPI HttpQueryInfoW(HINTERNET hHttpRequest, DWORD dwInfoLevel,
        TRACE("\n");
     }
     
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
     if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       goto lend;
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
     }
 
     if (lpBuffer == NULL)
         *lpdwBufferLength = 0;
-    bSuccess = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
-                                   lpBuffer, lpdwBufferLength, lpdwIndex);
+    res = HTTP_HttpQueryInfoW( lpwhr, dwInfoLevel,
+                               lpBuffer, lpdwBufferLength, lpdwIndex);
 
 lend:
     if( lpwhr )
          WININET_Release( &lpwhr->hdr );
 
-    TRACE("%d <--\n", bSuccess);
-    return bSuccess;
+    TRACE("%u <--\n", res);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
 }
 
 /***********************************************************************
@@ -2577,379 +3066,112 @@ BOOL WINAPI HttpQueryInfoA(HINTERNET hHttpRequest, DWORD dwInfoLevel,
 }
 
 /***********************************************************************
- *           HttpSendRequestExA (WININET.@)
- *
- * Sends the specified request to the HTTP server and allows chunked
- * transfers.
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE, call GetLastError() for more information.
+ *           HTTP_GetRedirectURL (internal)
  */
-BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
-                              LPINTERNET_BUFFERSA lpBuffersIn,
-                              LPINTERNET_BUFFERSA lpBuffersOut,
-                              DWORD dwFlags, DWORD_PTR dwContext)
+static LPWSTR HTTP_GetRedirectURL(http_request_t *lpwhr, LPCWSTR lpszUrl)
 {
-    INTERNET_BUFFERSW BuffersInW;
-    BOOL rc = FALSE;
-    DWORD headerlen;
-    LPWSTR header = NULL;
+    static WCHAR szHttp[] = {'h','t','t','p',0};
+    static WCHAR szHttps[] = {'h','t','t','p','s',0};
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    URL_COMPONENTSW urlComponents;
+    DWORD url_length = 0;
+    LPWSTR orig_url;
+    LPWSTR combined_url;
+
+    urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
+    urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
+    urlComponents.dwSchemeLength = 0;
+    urlComponents.lpszHostName = lpwhs->lpszHostName;
+    urlComponents.dwHostNameLength = 0;
+    urlComponents.nPort = lpwhs->nHostPort;
+    urlComponents.lpszUserName = lpwhs->lpszUserName;
+    urlComponents.dwUserNameLength = 0;
+    urlComponents.lpszPassword = NULL;
+    urlComponents.dwPasswordLength = 0;
+    urlComponents.lpszUrlPath = lpwhr->lpszPath;
+    urlComponents.dwUrlPathLength = 0;
+    urlComponents.lpszExtraInfo = NULL;
+    urlComponents.dwExtraInfoLength = 0;
+
+    if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
+        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+        return NULL;
 
-    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
-           lpBuffersOut, dwFlags, dwContext);
+    orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
 
-    if (lpBuffersIn)
+    /* convert from bytes to characters */
+    url_length = url_length / sizeof(WCHAR) - 1;
+    if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
     {
-        BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
-        if (lpBuffersIn->lpcszHeader)
-        {
-            headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
-                    lpBuffersIn->dwHeadersLength,0,0);
-            header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
-            if (!(BuffersInW.lpcszHeader = header))
-            {
-                INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-                return FALSE;
-            }
-            BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
-                    lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
-                    header, headerlen);
-        }
-        else
-            BuffersInW.lpcszHeader = NULL;
-        BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
-        BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
-        BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
-        BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
-        BuffersInW.Next = NULL;
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        return NULL;
     }
 
-    rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
-
-    HeapFree(GetProcessHeap(),0,header);
+    url_length = 0;
+    if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
+        (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
+    {
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        return NULL;
+    }
+    combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
 
-    return rc;
+    if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
+    {
+        HeapFree(GetProcessHeap(), 0, orig_url);
+        HeapFree(GetProcessHeap(), 0, combined_url);
+        return NULL;
+    }
+    HeapFree(GetProcessHeap(), 0, orig_url);
+    return combined_url;
 }
 
+
 /***********************************************************************
- *           HttpSendRequestExW (WININET.@)
- *
- * Sends the specified request to the HTTP server and allows chunked
- * transfers
- *
- * RETURNS
- *  Success: TRUE
- *  Failure: FALSE, call GetLastError() for more information.
+ *           HTTP_HandleRedirect (internal)
  */
-BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
-                   LPINTERNET_BUFFERSW lpBuffersIn,
-                   LPINTERNET_BUFFERSW lpBuffersOut,
-                   DWORD dwFlags, DWORD_PTR dwContext)
+static DWORD HTTP_HandleRedirect(http_request_t *lpwhr, LPCWSTR lpszUrl)
 {
-    BOOL ret = FALSE;
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs;
-    LPWININETAPPINFOW hIC;
-
-    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
-            lpBuffersOut, dwFlags, dwContext);
-
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hRequest );
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
+    appinfo_t *hIC = lpwhs->lpAppInfo;
+    BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
+    WCHAR path[INTERNET_MAX_URL_LENGTH];
+    int index;
 
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    if(lpszUrl[0]=='/')
     {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-        goto lend;
+        /* if it's an absolute path, keep the same session info */
+        lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
     }
+    else
+    {
+        URL_COMPONENTSW urlComponents;
+        WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
+        static WCHAR szHttp[] = {'h','t','t','p',0};
+        static WCHAR szHttps[] = {'h','t','t','p','s',0};
 
-    lpwhs = lpwhr->lpHttpSession;
-    assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
-    hIC = lpwhs->lpAppInfo;
-    assert(hIC->hdr.htype == WH_HINIT);
+        userName[0] = 0;
+        hostName[0] = 0;
+        protocol[0] = 0;
 
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
-    {
-        WORKREQUEST workRequest;
-        struct WORKREQ_HTTPSENDREQUESTW *req;
+        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
+        urlComponents.lpszScheme = protocol;
+        urlComponents.dwSchemeLength = 32;
+        urlComponents.lpszHostName = hostName;
+        urlComponents.dwHostNameLength = MAXHOSTNAME;
+        urlComponents.lpszUserName = userName;
+        urlComponents.dwUserNameLength = 1024;
+        urlComponents.lpszPassword = NULL;
+        urlComponents.dwPasswordLength = 0;
+        urlComponents.lpszUrlPath = path;
+        urlComponents.dwUrlPathLength = 2048;
+        urlComponents.lpszExtraInfo = NULL;
+        urlComponents.dwExtraInfoLength = 0;
+        if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
+            return INTERNET_GetLastError();
 
-        workRequest.asyncproc = AsyncHttpSendRequestProc;
-        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
-        req = &workRequest.u.HttpSendRequestW;
-        if (lpBuffersIn)
-        {
-            if (lpBuffersIn->lpcszHeader)
-                /* FIXME: this should use dwHeadersLength or may not be necessary at all */
-                req->lpszHeader = WININET_strdupW(lpBuffersIn->lpcszHeader);
-            else
-                req->lpszHeader = NULL;
-            req->dwHeaderLength = lpBuffersIn->dwHeadersLength;
-            req->lpOptional = lpBuffersIn->lpvBuffer;
-            req->dwOptionalLength = lpBuffersIn->dwBufferLength;
-            req->dwContentLength = lpBuffersIn->dwBufferTotal;
-        }
-        else
-        {
-            req->lpszHeader = NULL;
-            req->dwHeaderLength = 0;
-            req->lpOptional = NULL;
-            req->dwOptionalLength = 0;
-            req->dwContentLength = 0;
-        }
-
-        req->bEndRequest = FALSE;
-
-        INTERNET_AsyncCall(&workRequest);
-        /*
-         * This is from windows.
-         */
-        INTERNET_SetLastError(ERROR_IO_PENDING);
-    }
-    else
-    {
-        if (lpBuffersIn)
-            ret = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
-                                        lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
-                                        lpBuffersIn->dwBufferTotal, FALSE);
-        else
-            ret = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
-    }
-
-lend:
-    if ( lpwhr )
-        WININET_Release( &lpwhr->hdr );
-
-    TRACE("<---\n");
-    return ret;
-}
-
-/***********************************************************************
- *           HttpSendRequestW (WININET.@)
- *
- * Sends the specified request to the HTTP server
- *
- * RETURNS
- *    TRUE  on success
- *    FALSE on failure
- *
- */
-BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
-       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
-{
-    LPWININETHTTPREQW lpwhr;
-    LPWININETHTTPSESSIONW lpwhs = NULL;
-    LPWININETAPPINFOW hIC = NULL;
-    BOOL r;
-
-    TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
-            debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
-
-    lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hHttpRequest );
-    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
-        goto lend;
-    }
-
-    lpwhs = lpwhr->lpHttpSession;
-    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
-        goto lend;
-    }
-
-    hIC = lpwhs->lpAppInfo;
-    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
-    {
-        INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
-       r = FALSE;
-        goto lend;
-    }
-
-    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
-    {
-        WORKREQUEST workRequest;
-        struct WORKREQ_HTTPSENDREQUESTW *req;
-
-        workRequest.asyncproc = AsyncHttpSendRequestProc;
-        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
-        req = &workRequest.u.HttpSendRequestW;
-        if (lpszHeaders)
-        {
-            req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, dwHeaderLength * sizeof(WCHAR));
-            memcpy(req->lpszHeader, lpszHeaders, dwHeaderLength * sizeof(WCHAR));
-        }
-        else
-            req->lpszHeader = 0;
-        req->dwHeaderLength = dwHeaderLength;
-        req->lpOptional = lpOptional;
-        req->dwOptionalLength = dwOptionalLength;
-        req->dwContentLength = dwOptionalLength;
-        req->bEndRequest = TRUE;
-
-        INTERNET_AsyncCall(&workRequest);
-        /*
-         * This is from windows.
-         */
-        INTERNET_SetLastError(ERROR_IO_PENDING);
-        r = FALSE;
-    }
-    else
-    {
-       r = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
-               dwHeaderLength, lpOptional, dwOptionalLength,
-               dwOptionalLength, TRUE);
-    }
-lend:
-    if( lpwhr )
-        WININET_Release( &lpwhr->hdr );
-    return r;
-}
-
-/***********************************************************************
- *           HttpSendRequestA (WININET.@)
- *
- * 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)
-{
-    BOOL result;
-    LPWSTR szHeaders=NULL;
-    DWORD nLen=dwHeaderLength;
-    if(lpszHeaders!=NULL)
-    {
-        nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
-        szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
-        MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
-    }
-    result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
-    HeapFree(GetProcessHeap(),0,szHeaders);
-    return result;
-}
-
-static BOOL HTTP_GetRequestURL(WININETHTTPREQW *req, LPWSTR buf)
-{
-    LPHTTPHEADERW host_header;
-
-    static const WCHAR formatW[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
-
-    host_header = HTTP_GetHeader(req, szHost);
-    if(!host_header)
-        return FALSE;
-
-    sprintfW(buf, formatW, host_header->lpszValue, req->lpszPath); /* FIXME */
-    return TRUE;
-}
-
-/***********************************************************************
- *           HTTP_HandleRedirect (internal)
- */
-static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
-{
-    static const WCHAR szContentType[] = {'C','o','n','t','e','n','t','-','T','y','p','e',0};
-    static const WCHAR szContentLength[] = {'C','o','n','t','e','n','t','-','L','e','n','g','t','h',0};
-    LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
-    LPWININETAPPINFOW hIC = lpwhs->lpAppInfo;
-    BOOL using_proxy = hIC->lpszProxy && hIC->lpszProxy[0];
-    WCHAR path[INTERNET_MAX_URL_LENGTH];
-    int index;
-
-    if(lpszUrl[0]=='/')
-    {
-        /* if it's an absolute path, keep the same session info */
-        lstrcpynW(path, lpszUrl, INTERNET_MAX_URL_LENGTH);
-    }
-    else
-    {
-        URL_COMPONENTSW urlComponents;
-        WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
-        static WCHAR szHttp[] = {'h','t','t','p',0};
-        static WCHAR szHttps[] = {'h','t','t','p','s',0};
-        DWORD url_length = 0;
-        LPWSTR orig_url;
-        LPWSTR combined_url;
-
-        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
-        urlComponents.lpszScheme = (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE) ? szHttps : szHttp;
-        urlComponents.dwSchemeLength = 0;
-        urlComponents.lpszHostName = lpwhs->lpszHostName;
-        urlComponents.dwHostNameLength = 0;
-        urlComponents.nPort = lpwhs->nHostPort;
-        urlComponents.lpszUserName = lpwhs->lpszUserName;
-        urlComponents.dwUserNameLength = 0;
-        urlComponents.lpszPassword = NULL;
-        urlComponents.dwPasswordLength = 0;
-        urlComponents.lpszUrlPath = lpwhr->lpszPath;
-        urlComponents.dwUrlPathLength = 0;
-        urlComponents.lpszExtraInfo = NULL;
-        urlComponents.dwExtraInfoLength = 0;
-
-        if (!InternetCreateUrlW(&urlComponents, 0, NULL, &url_length) &&
-            (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
-            return FALSE;
-
-        orig_url = HeapAlloc(GetProcessHeap(), 0, url_length);
-
-        /* convert from bytes to characters */
-        url_length = url_length / sizeof(WCHAR) - 1;
-        if (!InternetCreateUrlW(&urlComponents, 0, orig_url, &url_length))
-        {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            return FALSE;
-        }
-
-        url_length = 0;
-        if (!InternetCombineUrlW(orig_url, lpszUrl, NULL, &url_length, ICU_ENCODE_SPACES_ONLY) &&
-            (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
-        {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            return FALSE;
-        }
-        combined_url = HeapAlloc(GetProcessHeap(), 0, url_length * sizeof(WCHAR));
-
-        if (!InternetCombineUrlW(orig_url, lpszUrl, combined_url, &url_length, ICU_ENCODE_SPACES_ONLY))
-        {
-            HeapFree(GetProcessHeap(), 0, orig_url);
-            HeapFree(GetProcessHeap(), 0, combined_url);
-            return FALSE;
-        }
-        HeapFree(GetProcessHeap(), 0, orig_url);
-
-        userName[0] = 0;
-        hostName[0] = 0;
-        protocol[0] = 0;
-
-        urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
-        urlComponents.lpszScheme = protocol;
-        urlComponents.dwSchemeLength = 32;
-        urlComponents.lpszHostName = hostName;
-        urlComponents.dwHostNameLength = MAXHOSTNAME;
-        urlComponents.lpszUserName = userName;
-        urlComponents.dwUserNameLength = 1024;
-        urlComponents.lpszPassword = NULL;
-        urlComponents.dwPasswordLength = 0;
-        urlComponents.lpszUrlPath = path;
-        urlComponents.dwUrlPathLength = 2048;
-        urlComponents.lpszExtraInfo = NULL;
-        urlComponents.dwExtraInfoLength = 0;
-        if(!InternetCrackUrlW(combined_url, strlenW(combined_url), 0, &urlComponents))
-        {
-            HeapFree(GetProcessHeap(), 0, combined_url);
-            return FALSE;
-        }
-
-        HeapFree(GetProcessHeap(), 0, combined_url);
-
-        if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
-            (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
+        if (!strncmpW(szHttp, urlComponents.lpszScheme, strlenW(szHttp)) &&
+            (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE))
         {
             TRACE("redirect from secure page to non-secure page\n");
             /* FIXME: warn about from secure redirect to non-secure page */
@@ -3000,26 +3222,35 @@ static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
             sprintfW(lpwhs->lpszHostName, fmt, hostName, urlComponents.nPort);
         }
         else
-            lpwhs->lpszHostName = WININET_strdupW(hostName);
+            lpwhs->lpszHostName = heap_strdupW(hostName);
 
-        HTTP_ProcessHeader(lpwhr, szHost, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
+        HTTP_ProcessHeader(lpwhr, hostW, lpwhs->lpszHostName, HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE | HTTP_ADDHDR_FLAG_REQ);
 
         HeapFree(GetProcessHeap(), 0, lpwhs->lpszUserName);
         lpwhs->lpszUserName = NULL;
         if (userName[0])
-            lpwhs->lpszUserName = WININET_strdupW(userName);
+            lpwhs->lpszUserName = heap_strdupW(userName);
 
         if (!using_proxy)
         {
             if (strcmpiW(lpwhs->lpszServerName, hostName) || lpwhs->nServerPort != urlComponents.nPort)
             {
+                DWORD res;
+
                 HeapFree(GetProcessHeap(), 0, lpwhs->lpszServerName);
-                lpwhs->lpszServerName = WININET_strdupW(hostName);
+                lpwhs->lpszServerName = heap_strdupW(hostName);
                 lpwhs->nServerPort = urlComponents.nPort;
 
                 NETCON_close(&lpwhr->netConnection);
-                if (!HTTP_ResolveName(lpwhr)) return FALSE;
-                if (!NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)) return FALSE;
+                if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS)
+                    return res;
+
+                res = NETCON_init(&lpwhr->netConnection, lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE);
+                if (res != ERROR_SUCCESS)
+                    return res;
+
+                lpwhr->read_pos = lpwhr->read_size = 0;
+                lpwhr->read_chunked = FALSE;
             }
         }
         else
@@ -3039,7 +3270,7 @@ static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
         lpwhr->lpszPath = HeapAlloc(GetProcessHeap(), 0, needed*sizeof(WCHAR));
         rc = UrlEscapeW(path, lpwhr->lpszPath, &needed,
                         URL_ESCAPE_SPACES_ONLY);
-        if (rc)
+        if (rc != S_OK)
         {
             ERR("Unable to escape string!(%s) (%d)\n",debugstr_w(path),rc);
             strcpyW(lpwhr->lpszPath,path);
@@ -3047,14 +3278,14 @@ static BOOL HTTP_HandleRedirect(LPWININETHTTPREQW lpwhr, LPCWSTR lpszUrl)
     }
 
     /* Remove custom content-type/length headers on redirects.  */
-    index = HTTP_GetCustomHeaderIndex(lpwhr, szContentType, 0, TRUE);
+    index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Type, 0, TRUE);
     if (0 <= index)
         HTTP_DeleteCustomHeader(lpwhr, index);
-    index = HTTP_GetCustomHeaderIndex(lpwhr, szContentLength, 0, TRUE);
+    index = HTTP_GetCustomHeaderIndex(lpwhr, szContent_Length, 0, TRUE);
     if (0 <= index)
         HTTP_DeleteCustomHeader(lpwhr, index);
 
-    return TRUE;
+    return ERROR_SUCCESS;
 }
 
 /***********************************************************************
@@ -3080,7 +3311,7 @@ static LPWSTR HTTP_build_req( LPCWSTR *list, int len )
     return str;
 }
 
-static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
+static DWORD HTTP_SecureProxyConnect(http_request_t *lpwhr)
 {
     LPWSTR lpszPath;
     LPWSTR requestString;
@@ -3088,10 +3319,10 @@ static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
     INT cnt;
     INT responseLen;
     char *ascii_req;
-    BOOL ret;
+    DWORD res;
     static const WCHAR szConnect[] = {'C','O','N','N','E','C','T',0};
     static const WCHAR szFormat[] = {'%','s',':','%','d',0};
-    LPWININETHTTPSESSIONW lpwhs = lpwhr->lpHttpSession;
+    http_session_t *lpwhs = lpwhr->lpHttpSession;
 
     TRACE("\n");
 
@@ -3110,43 +3341,42 @@ static BOOL HTTP_SecureProxyConnect(LPWININETHTTPREQW lpwhr)
 
     TRACE("full request -> %s\n", debugstr_an( ascii_req, len ) );
 
-    ret = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
+    res = NETCON_send( &lpwhr->netConnection, ascii_req, len, 0, &cnt );
     HeapFree( GetProcessHeap(), 0, ascii_req );
-    if (!ret || cnt < 0)
-        return FALSE;
+    if (res != ERROR_SUCCESS)
+        return res;
 
     responseLen = HTTP_GetResponseHeaders( lpwhr, TRUE );
     if (!responseLen)
-        return FALSE;
+        return ERROR_HTTP_INVALID_HEADER;
 
-    return TRUE;
+    return ERROR_SUCCESS;
 }
 
-static void HTTP_InsertCookies(LPWININETHTTPREQW lpwhr)
+static void HTTP_InsertCookies(http_request_t *lpwhr)
 {
-    static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s',0};
+    static const WCHAR szUrlForm[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
     LPWSTR lpszCookies, lpszUrl = NULL;
     DWORD nCookieSize, size;
-    LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr,szHost);
+    LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
 
-    size = (strlenW(Host->lpszValue) + strlenW(szUrlForm)) * sizeof(WCHAR);
+    size = (strlenW(Host->lpszValue) + strlenW(szUrlForm) + strlenW(lpwhr->lpszPath)) * sizeof(WCHAR);
     if (!(lpszUrl = HeapAlloc(GetProcessHeap(), 0, size))) return;
-    sprintfW( lpszUrl, szUrlForm, Host->lpszValue );
+    sprintfW( lpszUrl, szUrlForm, Host->lpszValue, lpwhr->lpszPath);
 
     if (InternetGetCookieW(lpszUrl, NULL, NULL, &nCookieSize))
     {
         int cnt = 0;
         static const WCHAR szCookie[] = {'C','o','o','k','i','e',':',' ',0};
-        static const WCHAR szcrlf[] = {'\r','\n',0};
 
-        size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szcrlf);
+        size = sizeof(szCookie) + nCookieSize * sizeof(WCHAR) + sizeof(szCrLf);
         if ((lpszCookies = HeapAlloc(GetProcessHeap(), 0, size)))
         {
             cnt += sprintfW(lpszCookies, szCookie);
             InternetGetCookieW(lpszUrl, NULL, lpszCookies + cnt, &nCookieSize);
-            strcatW(lpszCookies, szcrlf);
+            strcatW(lpszCookies, szCrLf);
 
-            HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_ADD);
+            HTTP_HttpAddRequestHeadersW(lpwhr, lpszCookies, strlenW(lpszCookies), HTTP_ADDREQ_FLAG_REPLACE);
             HeapFree(GetProcessHeap(), 0, lpszCookies);
         }
     }
@@ -3163,12 +3393,12 @@ static void HTTP_InsertCookies(LPWININETHTTPREQW lpwhr)
  *    FALSE on failure
  *
  */
-BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
+static DWORD HTTP_HttpSendRequestW(http_request_t *lpwhr, LPCWSTR lpszHeaders,
        DWORD dwHeaderLength, LPVOID lpOptional, DWORD dwOptionalLength,
        DWORD dwContentLength, BOOL bEndRequest)
 {
     INT cnt;
-    BOOL bSuccess = FALSE;
+    BOOL redirected = FALSE;
     LPWSTR requestString = NULL;
     INT responseLen;
     BOOL loop_next;
@@ -3177,6 +3407,7 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
     static const WCHAR szContentLength[] =
         { 'C','o','n','t','e','n','t','-','L','e','n','g','t','h',':',' ','%','l','i','\r','\n',0 };
     WCHAR contentLengthStr[sizeof szContentLength/2 /* includes \r\n */ + 20 /* int */ ];
+    DWORD res;
 
     TRACE("--> %p\n", lpwhr);
 
@@ -3184,12 +3415,13 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
 
     /* if the verb is NULL default to GET */
     if (!lpwhr->lpszVerb)
-        lpwhr->lpszVerb = WININET_strdupW(szGET);
+        lpwhr->lpszVerb = heap_strdupW(szGET);
 
-    if (dwContentLength || !strcmpW(lpwhr->lpszVerb, szPost))
+    if (dwContentLength || strcmpW(lpwhr->lpszVerb, szGET))
     {
         sprintfW(contentLengthStr, szContentLength, dwContentLength);
-        HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_ADD_IF_NEW);
+        HTTP_HttpAddRequestHeadersW(lpwhr, contentLengthStr, -1L, HTTP_ADDREQ_FLAG_REPLACE);
+        lpwhr->dwBytesToWrite = dwContentLength;
     }
     if (lpwhr->lpHttpSession->lpAppInfo->lpszAgent)
     {
@@ -3230,7 +3462,7 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
 
         if (TRACE_ON(wininet))
         {
-            LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr,szHost);
+            LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
             TRACE("Going to url %s %s\n", debugstr_w(Host->lpszValue), debugstr_w(lpwhr->lpszPath));
         }
 
@@ -3265,11 +3497,11 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
         TRACE("Request header -> %s\n", debugstr_w(requestString) );
 
         /* Send the request and store the results */
-        if (!HTTP_OpenConnection(lpwhr))
+        if ((res = HTTP_OpenConnection(lpwhr)) != ERROR_SUCCESS)
             goto lend;
 
         /* send the request as ASCII, tack on the optional data */
-        if( !lpOptional )
+        if (!lpOptional || redirected)
             dwOptionalLength = 0;
         len = WideCharToMultiByte( CP_ACP, 0, requestString, -1,
                                    NULL, 0, NULL, NULL );
@@ -3285,9 +3517,11 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                               INTERNET_STATUS_SENDING_REQUEST, NULL, 0);
 
-        NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
+        res = NETCON_send(&lpwhr->netConnection, ascii_req, len, 0, &cnt);
         HeapFree( GetProcessHeap(), 0, ascii_req );
 
+        lpwhr->dwBytesWritten = dwOptionalLength;
+
         INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                               INTERNET_STATUS_REQUEST_SENT,
                               &len, sizeof(DWORD));
@@ -3296,18 +3530,14 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
         {
             DWORD dwBufferSize;
             DWORD dwStatusCode;
-            WCHAR encoding[20];
-            static const WCHAR szChunked[] = {'c','h','u','n','k','e','d',0};
 
             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                                 INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
     
-            if (cnt < 0)
+            if (res != ERROR_SUCCESS)
                 goto lend;
     
             responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
-            if (responseLen)
-                bSuccess = TRUE;
     
             INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                                 INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen,
@@ -3315,61 +3545,58 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
 
             HTTP_ProcessCookies(lpwhr);
 
-            dwBufferSize = sizeof(lpwhr->dwContentLength);
-            if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_CONTENT_LENGTH,
-                                     &lpwhr->dwContentLength,&dwBufferSize,NULL))
-                lpwhr->dwContentLength = -1;
-
-            if (lpwhr->dwContentLength == 0)
-                HTTP_FinishedReading(lpwhr);
-
-            /* Correct the case where both a Content-Length and Transfer-encoding = chunked are set */
-
-            dwBufferSize = sizeof(encoding);
-            if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_TRANSFER_ENCODING, encoding, &dwBufferSize, NULL) &&
-                !strcmpiW(encoding, szChunked))
-            {
-                lpwhr->dwContentLength = -1;
-            }
+            if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
 
             dwBufferSize = sizeof(dwStatusCode);
-            if (!HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
-                                     &dwStatusCode,&dwBufferSize,NULL))
+            if (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE,
+                                    &dwStatusCode,&dwBufferSize,NULL) != ERROR_SUCCESS)
                 dwStatusCode = 0;
 
-            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && bSuccess)
+            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT) && responseLen)
             {
-                WCHAR szNewLocation[INTERNET_MAX_URL_LENGTH];
+                WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
                 dwBufferSize=sizeof(szNewLocation);
                 if ((dwStatusCode==HTTP_STATUS_REDIRECT || dwStatusCode==HTTP_STATUS_MOVED) &&
-                    HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL))
+                    HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_LOCATION,szNewLocation,&dwBufferSize,NULL) == ERROR_SUCCESS)
                 {
+                    if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
+                    {
+                        HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+                        lpwhr->lpszVerb = heap_strdupW(szGET);
+                    }
                     HTTP_DrainContent(lpwhr);
-                    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                                          INTERNET_STATUS_REDIRECT, szNewLocation,
-                                          dwBufferSize);
-                    bSuccess = HTTP_HandleRedirect(lpwhr, szNewLocation);
-                    if (bSuccess)
+                    if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
                     {
-                        HeapFree(GetProcessHeap(), 0, requestString);
-                        loop_next = TRUE;
+                        INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
+                                              new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
+                        res = HTTP_HandleRedirect(lpwhr, new_url);
+                        if (res == ERROR_SUCCESS)
+                        {
+                            HeapFree(GetProcessHeap(), 0, requestString);
+                            loop_next = TRUE;
+                        }
+                        HeapFree( GetProcessHeap(), 0, new_url );
                     }
+                    redirected = TRUE;
                 }
             }
-            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && bSuccess)
+            if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTH) && res == ERROR_SUCCESS)
             {
                 WCHAR szAuthValue[2048];
                 dwBufferSize=2048;
                 if (dwStatusCode == HTTP_STATUS_DENIED)
                 {
+                    LPHTTPHEADERW Host = HTTP_GetHeader(lpwhr, hostW);
                     DWORD dwIndex = 0;
-                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
+                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_WWW_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
                     {
                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
                                                  &lpwhr->pAuthInfo,
                                                  lpwhr->lpHttpSession->lpszUserName,
-                                                 lpwhr->lpHttpSession->lpszPassword))
+                                                 lpwhr->lpHttpSession->lpszPassword,
+                                                 Host->lpszValue))
                         {
+                            HeapFree(GetProcessHeap(), 0, requestString);
                             loop_next = TRUE;
                             break;
                         }
@@ -3378,12 +3605,13 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
                 if (dwStatusCode == HTTP_STATUS_PROXY_AUTH_REQ)
                 {
                     DWORD dwIndex = 0;
-                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex))
+                    while (HTTP_HttpQueryInfoW(lpwhr,HTTP_QUERY_PROXY_AUTHENTICATE,szAuthValue,&dwBufferSize,&dwIndex) == ERROR_SUCCESS)
                     {
                         if (HTTP_DoAuthorization(lpwhr, szAuthValue,
                                                  &lpwhr->pProxyAuthInfo,
                                                  lpwhr->lpHttpSession->lpAppInfo->lpszProxyUsername,
-                                                 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword))
+                                                 lpwhr->lpHttpSession->lpAppInfo->lpszProxyPassword,
+                                                 NULL))
                         {
                             loop_next = TRUE;
                             break;
@@ -3393,52 +3621,507 @@ BOOL WINAPI HTTP_HttpSendRequestW(LPWININETHTTPREQW lpwhr, LPCWSTR lpszHeaders,
             }
         }
         else
-            bSuccess = TRUE;
+            res = ERROR_SUCCESS;
+    }
+    while (loop_next);
+
+    if(res == ERROR_SUCCESS) {
+        WCHAR url[INTERNET_MAX_URL_LENGTH];
+        WCHAR cacheFileName[MAX_PATH+1];
+        BOOL b;
+
+        b = HTTP_GetRequestURL(lpwhr, url);
+        if(!b) {
+            WARN("Could not get URL\n");
+            goto lend;
+        }
+
+        b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
+        if(b) {
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszCacheFile);
+            CloseHandle(lpwhr->hCacheFile);
+
+            lpwhr->lpszCacheFile = heap_strdupW(cacheFileName);
+            lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
+                      NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+            if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
+                WARN("Could not create file: %u\n", GetLastError());
+                lpwhr->hCacheFile = NULL;
+            }
+        }else {
+            WARN("Could not create cache entry: %08x\n", GetLastError());
+        }
+    }
+
+lend:
+
+    HeapFree(GetProcessHeap(), 0, requestString);
+
+    /* TODO: send notification for P3P header */
+
+    if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        if (res == ERROR_SUCCESS && lpwhr->dwBytesWritten == lpwhr->dwBytesToWrite)
+            HTTP_ReceiveRequestData(lpwhr, TRUE);
+        else
+        {
+            iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
+            iar.dwError = res;
+
+            INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                                  INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                                  sizeof(INTERNET_ASYNC_RESULT));
+        }
+    }
+
+    TRACE("<--\n");
+    return res;
+}
+
+/***********************************************************************
+ *
+ * Helper functions for the HttpSendRequest(Ex) functions
+ *
+ */
+static void AsyncHttpSendRequestProc(WORKREQUEST *workRequest)
+{
+    struct WORKREQ_HTTPSENDREQUESTW const *req = &workRequest->u.HttpSendRequestW;
+    http_request_t *lpwhr = (http_request_t*) workRequest->hdr;
+
+    TRACE("%p\n", lpwhr);
+
+    HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
+            req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
+            req->dwContentLength, req->bEndRequest);
+
+    HeapFree(GetProcessHeap(), 0, req->lpszHeader);
+}
+
+
+static DWORD HTTP_HttpEndRequestW(http_request_t *lpwhr, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    INT responseLen;
+    DWORD dwBufferSize;
+    INTERNET_ASYNC_RESULT iar;
+    DWORD res = ERROR_SUCCESS;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                  INTERNET_STATUS_RECEIVING_RESPONSE, NULL, 0);
+
+    responseLen = HTTP_GetResponseHeaders(lpwhr, TRUE);
+    if (!responseLen)
+        res = ERROR_HTTP_HEADER_NOT_FOUND;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                  INTERNET_STATUS_RESPONSE_RECEIVED, &responseLen, sizeof(DWORD));
+
+    /* process cookies here. Is this right? */
+    HTTP_ProcessCookies(lpwhr);
+
+    if (!set_content_length( lpwhr )) HTTP_FinishedReading(lpwhr);
+
+    if (!(lpwhr->hdr.dwFlags & INTERNET_FLAG_NO_AUTO_REDIRECT))
+    {
+        DWORD dwCode,dwCodeLength = sizeof(DWORD);
+        if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_FLAG_NUMBER|HTTP_QUERY_STATUS_CODE, &dwCode, &dwCodeLength, NULL) == ERROR_SUCCESS
+            && (dwCode == 302 || dwCode == 301 || dwCode == 303))
+        {
+            WCHAR *new_url, szNewLocation[INTERNET_MAX_URL_LENGTH];
+            dwBufferSize=sizeof(szNewLocation);
+            if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_LOCATION, szNewLocation, &dwBufferSize, NULL) == ERROR_SUCCESS)
+            {
+                if (strcmpW(lpwhr->lpszVerb, szGET) && strcmpW(lpwhr->lpszVerb, szHEAD))
+                {
+                    HeapFree(GetProcessHeap(), 0, lpwhr->lpszVerb);
+                    lpwhr->lpszVerb = heap_strdupW(szGET);
+                }
+                HTTP_DrainContent(lpwhr);
+                if ((new_url = HTTP_GetRedirectURL( lpwhr, szNewLocation )))
+                {
+                    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext, INTERNET_STATUS_REDIRECT,
+                                          new_url, (strlenW(new_url) + 1) * sizeof(WCHAR));
+                    res = HTTP_HandleRedirect(lpwhr, new_url);
+                    if (res == ERROR_SUCCESS)
+                        res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, TRUE);
+                    HeapFree( GetProcessHeap(), 0, new_url );
+                }
+            }
+        }
+    }
+
+    iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
+    iar.dwError = res;
+
+    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
+                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
+                          sizeof(INTERNET_ASYNC_RESULT));
+    return res;
+}
+
+/***********************************************************************
+ *           HttpEndRequestA (WININET.@)
+ *
+ * Ends an HTTP request that was started by HttpSendRequestEx
+ *
+ * RETURNS
+ *    TRUE     if successful
+ *    FALSE    on failure
+ *
+ */
+BOOL WINAPI HttpEndRequestA(HINTERNET hRequest,
+        LPINTERNET_BUFFERSA lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    TRACE("(%p, %p, %08x, %08lx)\n", hRequest, lpBuffersOut, dwFlags, dwContext);
+
+    if (lpBuffersOut)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    return HttpEndRequestW(hRequest, NULL, dwFlags, dwContext);
+}
+
+static void AsyncHttpEndRequestProc(WORKREQUEST *work)
+{
+    struct WORKREQ_HTTPENDREQUESTW const *req = &work->u.HttpEndRequestW;
+    http_request_t *lpwhr = (http_request_t*)work->hdr;
+
+    TRACE("%p\n", lpwhr);
+
+    HTTP_HttpEndRequestW(lpwhr, req->dwFlags, req->dwContext);
+}
+
+/***********************************************************************
+ *           HttpEndRequestW (WININET.@)
+ *
+ * Ends an HTTP request that was started by HttpSendRequestEx
+ *
+ * RETURNS
+ *    TRUE     if successful
+ *    FALSE    on failure
+ *
+ */
+BOOL WINAPI HttpEndRequestW(HINTERNET hRequest,
+        LPINTERNET_BUFFERSW lpBuffersOut, DWORD dwFlags, DWORD_PTR dwContext)
+{
+    http_request_t *lpwhr;
+    DWORD res;
+
+    TRACE("-->\n");
+
+    if (lpBuffersOut)
+    {
+        SetLastError(ERROR_INVALID_PARAMETER);
+        return FALSE;
+    }
+
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
+
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+        if (lpwhr)
+            WININET_Release( &lpwhr->hdr );
+        return FALSE;
+    }
+    lpwhr->hdr.dwFlags |= dwFlags;
+
+    if (lpwhr->lpHttpSession->lpAppInfo->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST work;
+        struct WORKREQ_HTTPENDREQUESTW *request;
+
+        work.asyncproc = AsyncHttpEndRequestProc;
+        work.hdr = WININET_AddRef( &lpwhr->hdr );
+
+        request = &work.u.HttpEndRequestW;
+        request->dwFlags = dwFlags;
+        request->dwContext = dwContext;
+
+        INTERNET_AsyncCall(&work);
+        res = ERROR_IO_PENDING;
+    }
+    else
+        res = HTTP_HttpEndRequestW(lpwhr, dwFlags, dwContext);
+
+    WININET_Release( &lpwhr->hdr );
+    TRACE("%u <--\n", res);
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           HttpSendRequestExA (WININET.@)
+ *
+ * Sends the specified request to the HTTP server and allows chunked
+ * transfers.
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE, call GetLastError() for more information.
+ */
+BOOL WINAPI HttpSendRequestExA(HINTERNET hRequest,
+                              LPINTERNET_BUFFERSA lpBuffersIn,
+                              LPINTERNET_BUFFERSA lpBuffersOut,
+                              DWORD dwFlags, DWORD_PTR dwContext)
+{
+    INTERNET_BUFFERSW BuffersInW;
+    BOOL rc = FALSE;
+    DWORD headerlen;
+    LPWSTR header = NULL;
+
+    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
+           lpBuffersOut, dwFlags, dwContext);
+
+    if (lpBuffersIn)
+    {
+        BuffersInW.dwStructSize = sizeof(LPINTERNET_BUFFERSW);
+        if (lpBuffersIn->lpcszHeader)
+        {
+            headerlen = MultiByteToWideChar(CP_ACP,0,lpBuffersIn->lpcszHeader,
+                    lpBuffersIn->dwHeadersLength,0,0);
+            header = HeapAlloc(GetProcessHeap(),0,headerlen*sizeof(WCHAR));
+            if (!(BuffersInW.lpcszHeader = header))
+            {
+                SetLastError(ERROR_OUTOFMEMORY);
+                return FALSE;
+            }
+            BuffersInW.dwHeadersLength = MultiByteToWideChar(CP_ACP, 0,
+                    lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
+                    header, headerlen);
+        }
+        else
+            BuffersInW.lpcszHeader = NULL;
+        BuffersInW.dwHeadersTotal = lpBuffersIn->dwHeadersTotal;
+        BuffersInW.lpvBuffer = lpBuffersIn->lpvBuffer;
+        BuffersInW.dwBufferLength = lpBuffersIn->dwBufferLength;
+        BuffersInW.dwBufferTotal = lpBuffersIn->dwBufferTotal;
+        BuffersInW.Next = NULL;
+    }
+
+    rc = HttpSendRequestExW(hRequest, lpBuffersIn ? &BuffersInW : NULL, NULL, dwFlags, dwContext);
+
+    HeapFree(GetProcessHeap(),0,header);
+
+    return rc;
+}
+
+/***********************************************************************
+ *           HttpSendRequestExW (WININET.@)
+ *
+ * Sends the specified request to the HTTP server and allows chunked
+ * transfers
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE, call GetLastError() for more information.
+ */
+BOOL WINAPI HttpSendRequestExW(HINTERNET hRequest,
+                   LPINTERNET_BUFFERSW lpBuffersIn,
+                   LPINTERNET_BUFFERSW lpBuffersOut,
+                   DWORD dwFlags, DWORD_PTR dwContext)
+{
+    http_request_t *lpwhr;
+    http_session_t *lpwhs;
+    appinfo_t *hIC;
+    DWORD res;
+
+    TRACE("(%p, %p, %p, %08x, %08lx)\n", hRequest, lpBuffersIn,
+            lpBuffersOut, dwFlags, dwContext);
+
+    lpwhr = (http_request_t*) WININET_GetObject( hRequest );
+
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
+
+    lpwhs = lpwhr->lpHttpSession;
+    assert(lpwhs->hdr.htype == WH_HHTTPSESSION);
+    hIC = lpwhs->lpAppInfo;
+    assert(hIC->hdr.htype == WH_HINIT);
+
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+        struct WORKREQ_HTTPSENDREQUESTW *req;
+
+        workRequest.asyncproc = AsyncHttpSendRequestProc;
+        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
+        req = &workRequest.u.HttpSendRequestW;
+        if (lpBuffersIn)
+        {
+            DWORD size = 0;
+
+            if (lpBuffersIn->lpcszHeader)
+            {
+                if (lpBuffersIn->dwHeadersLength == ~0u)
+                    size = (strlenW( lpBuffersIn->lpcszHeader ) + 1) * sizeof(WCHAR);
+                else
+                    size = lpBuffersIn->dwHeadersLength * sizeof(WCHAR);
+
+                req->lpszHeader = HeapAlloc( GetProcessHeap(), 0, size );
+                memcpy( req->lpszHeader, lpBuffersIn->lpcszHeader, size );
+            }
+            else req->lpszHeader = NULL;
+
+            req->dwHeaderLength = size / sizeof(WCHAR);
+            req->lpOptional = lpBuffersIn->lpvBuffer;
+            req->dwOptionalLength = lpBuffersIn->dwBufferLength;
+            req->dwContentLength = lpBuffersIn->dwBufferTotal;
+        }
+        else
+        {
+            req->lpszHeader = NULL;
+            req->dwHeaderLength = 0;
+            req->lpOptional = NULL;
+            req->dwOptionalLength = 0;
+            req->dwContentLength = 0;
+        }
+
+        req->bEndRequest = FALSE;
+
+        INTERNET_AsyncCall(&workRequest);
+        /*
+         * This is from windows.
+         */
+        res = ERROR_IO_PENDING;
+    }
+    else
+    {
+        if (lpBuffersIn)
+            res = HTTP_HttpSendRequestW(lpwhr, lpBuffersIn->lpcszHeader, lpBuffersIn->dwHeadersLength,
+                                        lpBuffersIn->lpvBuffer, lpBuffersIn->dwBufferLength,
+                                        lpBuffersIn->dwBufferTotal, FALSE);
+        else
+            res = HTTP_HttpSendRequestW(lpwhr, NULL, 0, NULL, 0, 0, FALSE);
     }
-    while (loop_next);
 
-    /* FIXME: Better check, when we have to create the cache file */
-    if(bSuccess && (lpwhr->hdr.dwFlags & INTERNET_FLAG_NEED_FILE)) {
-        WCHAR url[INTERNET_MAX_URL_LENGTH];
-        WCHAR cacheFileName[MAX_PATH+1];
-        BOOL b;
+lend:
+    if ( lpwhr )
+        WININET_Release( &lpwhr->hdr );
 
-        b = HTTP_GetRequestURL(lpwhr, url);
-        if(!b) {
-            WARN("Could not get URL\n");
-            goto lend;
-        }
+    TRACE("<---\n");
+    SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
 
-        b = CreateUrlCacheEntryW(url, lpwhr->dwContentLength > 0 ? lpwhr->dwContentLength : 0, NULL, cacheFileName, 0);
-        if(b) {
-            lpwhr->lpszCacheFile = WININET_strdupW(cacheFileName);
-            lpwhr->hCacheFile = CreateFileW(lpwhr->lpszCacheFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE,
-                      NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
-            if(lpwhr->hCacheFile == INVALID_HANDLE_VALUE) {
-                WARN("Could not create file: %u\n", GetLastError());
-                lpwhr->hCacheFile = NULL;
-            }
-        }else {
-            WARN("Could not create cache entry: %08x\n", GetLastError());
-        }
+/***********************************************************************
+ *           HttpSendRequestW (WININET.@)
+ *
+ * Sends the specified request to the HTTP server
+ *
+ * RETURNS
+ *    TRUE  on success
+ *    FALSE on failure
+ *
+ */
+BOOL WINAPI HttpSendRequestW(HINTERNET hHttpRequest, LPCWSTR lpszHeaders,
+       DWORD dwHeaderLength, LPVOID lpOptional ,DWORD dwOptionalLength)
+{
+    http_request_t *lpwhr;
+    http_session_t *lpwhs = NULL;
+    appinfo_t *hIC = NULL;
+    DWORD res = ERROR_SUCCESS;
+
+    TRACE("%p, %s, %i, %p, %i)\n", hHttpRequest,
+            debugstr_wn(lpszHeaders, dwHeaderLength), dwHeaderLength, lpOptional, dwOptionalLength);
+
+    lpwhr = (http_request_t*) WININET_GetObject( hHttpRequest );
+    if (NULL == lpwhr || lpwhr->hdr.htype != WH_HHTTPREQ)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
     }
 
-lend:
+    lpwhs = lpwhr->lpHttpSession;
+    if (NULL == lpwhs ||  lpwhs->hdr.htype != WH_HHTTPSESSION)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-    HeapFree(GetProcessHeap(), 0, requestString);
+    hIC = lpwhs->lpAppInfo;
+    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT)
+    {
+        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+        goto lend;
+    }
 
-    /* TODO: send notification for P3P header */
+    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
+    {
+        WORKREQUEST workRequest;
+        struct WORKREQ_HTTPSENDREQUESTW *req;
 
-    iar.dwResult = (DWORD_PTR)lpwhr->hdr.hInternet;
-    iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
+        workRequest.asyncproc = AsyncHttpSendRequestProc;
+        workRequest.hdr = WININET_AddRef( &lpwhr->hdr );
+        req = &workRequest.u.HttpSendRequestW;
+        if (lpszHeaders)
+        {
+            DWORD size;
 
-    INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
-                          INTERNET_STATUS_REQUEST_COMPLETE, &iar,
-                          sizeof(INTERNET_ASYNC_RESULT));
+            if (dwHeaderLength == ~0u) size = (strlenW(lpszHeaders) + 1) * sizeof(WCHAR);
+            else size = dwHeaderLength * sizeof(WCHAR);
 
-    TRACE("<--\n");
-    if (bSuccess) INTERNET_SetLastError(ERROR_SUCCESS);
-    return bSuccess;
+            req->lpszHeader = HeapAlloc(GetProcessHeap(), 0, size);
+            memcpy(req->lpszHeader, lpszHeaders, size);
+        }
+        else
+            req->lpszHeader = 0;
+        req->dwHeaderLength = dwHeaderLength;
+        req->lpOptional = lpOptional;
+        req->dwOptionalLength = dwOptionalLength;
+        req->dwContentLength = dwOptionalLength;
+        req->bEndRequest = TRUE;
+
+        INTERNET_AsyncCall(&workRequest);
+        /*
+         * This is from windows.
+         */
+        res = ERROR_IO_PENDING;
+    }
+    else
+    {
+       res = HTTP_HttpSendRequestW(lpwhr, lpszHeaders,
+               dwHeaderLength, lpOptional, dwOptionalLength,
+               dwOptionalLength, TRUE);
+    }
+lend:
+    if( lpwhr )
+        WININET_Release( &lpwhr->hdr );
+
+    SetLastError(res);
+    return res == ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           HttpSendRequestA (WININET.@)
+ *
+ * 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)
+{
+    BOOL result;
+    LPWSTR szHeaders=NULL;
+    DWORD nLen=dwHeaderLength;
+    if(lpszHeaders!=NULL)
+    {
+        nLen=MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,NULL,0);
+        szHeaders=HeapAlloc(GetProcessHeap(),0,nLen*sizeof(WCHAR));
+        MultiByteToWideChar(CP_ACP,0,lpszHeaders,dwHeaderLength,szHeaders,nLen);
+    }
+    result=HttpSendRequestW(hHttpRequest, szHeaders, nLen, lpOptional, dwOptionalLength);
+    HeapFree(GetProcessHeap(),0,szHeaders);
+    return result;
 }
 
 /***********************************************************************
@@ -3447,9 +4130,9 @@ lend:
  * Deallocate session handle
  *
  */
-static void HTTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
+static void HTTPSESSION_Destroy(object_header_t *hdr)
 {
-    LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW) hdr;
+    http_session_t *lpwhs = (http_session_t*) hdr;
 
     TRACE("%p\n", lpwhs);
 
@@ -3462,7 +4145,7 @@ static void HTTPSESSION_Destroy(WININETHANDLEHEADER *hdr)
     HeapFree(GetProcessHeap(), 0, lpwhs);
 }
 
-static DWORD HTTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static DWORD HTTPSESSION_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
     switch(option) {
     case INTERNET_OPTION_HANDLE_TYPE:
@@ -3479,11 +4162,34 @@ static DWORD HTTPSESSION_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, voi
     return INET_QueryOption(option, buffer, size, unicode);
 }
 
-static const HANDLEHEADERVtbl HTTPSESSIONVtbl = {
+static DWORD HTTPSESSION_SetOption(object_header_t *hdr, DWORD option, void *buffer, DWORD size)
+{
+    http_session_t *ses = (http_session_t*)hdr;
+
+    switch(option) {
+    case INTERNET_OPTION_USERNAME:
+    {
+        HeapFree(GetProcessHeap(), 0, ses->lpszUserName);
+        if (!(ses->lpszUserName = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    }
+    case INTERNET_OPTION_PASSWORD:
+    {
+        HeapFree(GetProcessHeap(), 0, ses->lpszPassword);
+        if (!(ses->lpszPassword = heap_strdupW(buffer))) return ERROR_OUTOFMEMORY;
+        return ERROR_SUCCESS;
+    }
+    default: break;
+    }
+
+    return ERROR_INTERNET_INVALID_OPTION;
+}
+
+static const object_vtbl_t HTTPSESSIONVtbl = {
     HTTPSESSION_Destroy,
     NULL,
     HTTPSESSION_QueryOption,
-    NULL,
+    HTTPSESSION_SetOption,
     NULL,
     NULL,
     NULL,
@@ -3502,30 +4208,25 @@ static const HANDLEHEADERVtbl HTTPSESSIONVtbl = {
  *   NULL on failure
  *
  */
-HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
-       INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
-       LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
-       DWORD dwInternalFlags)
+DWORD HTTP_Connect(appinfo_t *hIC, LPCWSTR lpszServerName,
+        INTERNET_PORT nServerPort, LPCWSTR lpszUserName,
+        LPCWSTR lpszPassword, DWORD dwFlags, DWORD_PTR dwContext,
+        DWORD dwInternalFlags, HINTERNET *ret)
 {
-    LPWININETHTTPSESSIONW lpwhs = NULL;
+    http_session_t *lpwhs = NULL;
     HINTERNET handle = NULL;
+    DWORD res = ERROR_SUCCESS;
 
     TRACE("-->\n");
 
     if (!lpszServerName || !lpszServerName[0])
-    {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
-        goto lerror;
-    }
+        return ERROR_INVALID_PARAMETER;
 
     assert( hIC->hdr.htype == WH_HINIT );
 
-    lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WININETHTTPSESSIONW));
-    if (NULL == lpwhs)
-    {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-       goto lerror;
-    }
+    lpwhs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(http_session_t));
+    if (!lpwhs)
+        return ERROR_OUTOFMEMORY;
 
    /*
     * According to my tests. The name is not resolved until a request is sent
@@ -3547,8 +4248,8 @@ HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     if (NULL == handle)
     {
         ERR("Failed to alloc handle\n");
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-       goto lerror;
+        res = ERROR_OUTOFMEMORY;
+        goto lerror;
     }
 
     if(hIC->lpszProxy && hIC->dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
@@ -3559,13 +4260,13 @@ HINTERNET HTTP_Connect(LPWININETAPPINFOW hIC, LPCWSTR lpszServerName,
     }
     if (lpszServerName && lpszServerName[0])
     {
-        lpwhs->lpszServerName = WININET_strdupW(lpszServerName);
-        lpwhs->lpszHostName = WININET_strdupW(lpszServerName);
+        lpwhs->lpszServerName = heap_strdupW(lpszServerName);
+        lpwhs->lpszHostName = heap_strdupW(lpszServerName);
     }
     if (lpszUserName && lpszUserName[0])
-        lpwhs->lpszUserName = WININET_strdupW(lpszUserName);
+        lpwhs->lpszUserName = heap_strdupW(lpszUserName);
     if (lpszPassword && lpszPassword[0])
-        lpwhs->lpszPassword = WININET_strdupW(lpszPassword);
+        lpwhs->lpszPassword = heap_strdupW(lpszPassword);
     lpwhs->nServerPort = nServerPort;
     lpwhs->nHostPort = nServerPort;
 
@@ -3587,7 +4288,10 @@ lerror:
  */
 
     TRACE("%p --> %p (%p)\n", hIC, handle, lpwhs);
-    return handle;
+
+    if(res == ERROR_SUCCESS)
+        *ret = handle;
+    return res;
 }
 
 
@@ -3601,48 +4305,58 @@ lerror:
  *   TRUE  on success
  *   FALSE on failure
  */
-static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
+static DWORD HTTP_OpenConnection(http_request_t *lpwhr)
 {
-    BOOL bSuccess = FALSE;
-    LPWININETHTTPSESSIONW lpwhs;
-    LPWININETAPPINFOW hIC = NULL;
-    char szaddr[32];
+    http_session_t *lpwhs;
+    appinfo_t *hIC = NULL;
+    char szaddr[INET6_ADDRSTRLEN];
+    const void *addr;
+    DWORD res = ERROR_SUCCESS;
 
     TRACE("-->\n");
 
 
-    if (NULL == lpwhr ||  lpwhr->hdr.htype != WH_HHTTPREQ)
+    if (lpwhr->hdr.htype != WH_HHTTPREQ)
     {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        res = ERROR_INVALID_PARAMETER;
         goto lend;
     }
 
     if (NETCON_connected(&lpwhr->netConnection))
-    {
-        bSuccess = TRUE;
         goto lend;
-    }
-    if (!HTTP_ResolveName(lpwhr)) goto lend;
+    if ((res = HTTP_ResolveName(lpwhr)) != ERROR_SUCCESS) goto lend;
 
     lpwhs = lpwhr->lpHttpSession;
 
     hIC = lpwhs->lpAppInfo;
-    inet_ntop(lpwhs->socketAddress.sin_family, &lpwhs->socketAddress.sin_addr,
-              szaddr, sizeof(szaddr));
+    switch (lpwhs->socketAddress.ss_family)
+    {
+    case AF_INET:
+        addr = &((struct sockaddr_in *)&lpwhs->socketAddress)->sin_addr;
+        break;
+    case AF_INET6:
+        addr = &((struct sockaddr_in6 *)&lpwhs->socketAddress)->sin6_addr;
+        break;
+    default:
+        WARN("unsupported family %d\n", lpwhs->socketAddress.ss_family);
+        return ERROR_INTERNET_NAME_NOT_RESOLVED;
+    }
+    inet_ntop(lpwhs->socketAddress.ss_family, addr, szaddr, sizeof(szaddr));
     INTERNET_SendCallback(&lpwhr->hdr, lpwhr->hdr.dwContext,
                           INTERNET_STATUS_CONNECTING_TO_SERVER,
                           szaddr,
                           strlen(szaddr)+1);
 
-    if (!NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.sin_family,
-                         SOCK_STREAM, 0))
+    res = NETCON_create(&lpwhr->netConnection, lpwhs->socketAddress.ss_family, SOCK_STREAM, 0);
+    if (res != ERROR_SUCCESS)
     {
-        WARN("Socket creation failed: %u\n", INTERNET_GetLastError());
+        WARN("Socket creation failed: %u\n", res);
         goto lend;
     }
 
-    if (!NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
-                      sizeof(lpwhs->socketAddress)))
+    res = NETCON_connect(&lpwhr->netConnection, (struct sockaddr *)&lpwhs->socketAddress,
+                         lpwhs->sa_len);
+    if(res != ERROR_SUCCESS)
        goto lend;
 
     if (lpwhr->hdr.dwFlags & INTERNET_FLAG_SECURE)
@@ -3653,10 +4367,11 @@ static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
          * behaviour to be more correct and to not cause any incompatibilities
          * because using a secure connection through a proxy server is a rare
          * case that would be hard for anyone to depend on */
-        if (hIC->lpszProxy && !HTTP_SecureProxyConnect(lpwhr))
+        if (hIC->lpszProxy && (res = HTTP_SecureProxyConnect(lpwhr)) != ERROR_SUCCESS)
             goto lend;
 
-        if (!NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName))
+        res = NETCON_secure_connect(&lpwhr->netConnection, lpwhs->lpszHostName);
+        if(res != ERROR_SUCCESS)
         {
             WARN("Couldn't connect securely to host\n");
             goto lend;
@@ -3667,11 +4382,12 @@ static BOOL HTTP_OpenConnection(LPWININETHTTPREQW lpwhr)
                           INTERNET_STATUS_CONNECTED_TO_SERVER,
                           szaddr, strlen(szaddr)+1);
 
-    bSuccess = TRUE;
-
 lend:
-    TRACE("%d <--\n", bSuccess);
-    return bSuccess;
+    lpwhr->read_pos = lpwhr->read_size = 0;
+    lpwhr->read_chunked = FALSE;
+
+    TRACE("%d <--\n", res);
+    return res;
 }
 
 
@@ -3680,7 +4396,7 @@ lend:
  *
  * clear out any old response headers
  */
-static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
+static void HTTP_clear_response_headers( http_request_t *lpwhr )
 {
     DWORD i;
 
@@ -3707,20 +4423,20 @@ static void HTTP_clear_response_headers( LPWININETHTTPREQW lpwhr )
  *   TRUE  on success
  *   FALSE on error
  */
-static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
+static INT HTTP_GetResponseHeaders(http_request_t *lpwhr, BOOL clear)
 {
     INT cbreaks = 0;
     WCHAR buffer[MAX_REPLY_LEN];
     DWORD buflen = MAX_REPLY_LEN;
     BOOL bSuccess = FALSE;
     INT  rc = 0;
-    static const WCHAR szCrLf[] = {'\r','\n',0};
-    static const WCHAR szHundred[] = {'1','0','0',0};
     char bufferA[MAX_REPLY_LEN];
-    LPWSTR status_code, status_text;
+    LPWSTR status_code = NULL, status_text = NULL;
     DWORD cchMaxRawHeaders = 1024;
-    LPWSTR lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+    LPWSTR lpszRawHeaders = NULL;
+    LPWSTR temp;
     DWORD cchRawHeaders = 0;
+    BOOL codeHundred = FALSE;
 
     TRACE("-->\n");
 
@@ -3731,36 +4447,52 @@ static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
         goto lend;
 
     do {
-        /*
-         * HACK peek at the buffer
-         */
-        buflen = MAX_REPLY_LEN;
-        NETCON_recv(&lpwhr->netConnection, buffer, buflen, MSG_PEEK, &rc);
-
+        static const WCHAR szHundred[] = {'1','0','0',0};
         /*
          * We should first receive 'HTTP/1.x nnn OK' where nnn is the status code.
          */
-        memset(buffer, 0, MAX_REPLY_LEN);
-        if (!NETCON_getNextLine(&lpwhr->netConnection, bufferA, &buflen))
+        buflen = MAX_REPLY_LEN;
+        if (!read_line(lpwhr, bufferA, &buflen))
             goto lend;
+        rc += buflen;
         MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
+        /* check is this a status code line? */
+        if (!strncmpW(buffer, g_szHttp1_0, 4))
+        {
+            /* split the version from the status code */
+            status_code = strchrW( buffer, ' ' );
+            if( !status_code )
+                goto lend;
+            *status_code++=0;
 
-        /* split the version from the status code */
-        status_code = strchrW( buffer, ' ' );
-        if( !status_code )
-            goto lend;
-        *status_code++=0;
+            /* split the status code from the status text */
+            status_text = strchrW( status_code, ' ' );
+            if( !status_text )
+                goto lend;
+            *status_text++=0;
 
-        /* split the status code from the status text */
-        status_text = strchrW( status_code, ' ' );
-        if( !status_text )
-            goto lend;
-        *status_text++=0;
+            TRACE("version [%s] status code [%s] status text [%s]\n",
+               debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
+
+            codeHundred = (!strcmpW(status_code, szHundred));
+        }
+        else if (!codeHundred)
+        {
+            WARN("No status line at head of response (%s)\n", debugstr_w(buffer));
 
-        TRACE("version [%s] status code [%s] status text [%s]\n",
-           debugstr_w(buffer), debugstr_w(status_code), debugstr_w(status_text) );
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszVersion);
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszStatusText);
 
-    } while (!strcmpW(status_code, szHundred)); /* ignore "100 Continue" responses */
+            lpwhr->lpszVersion = heap_strdupW(g_szHttp1_0);
+            lpwhr->lpszStatusText = heap_strdupW(szOK);
+
+            HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
+            lpwhr->lpszRawHeaders = heap_strdupW(szDefaultHeader);
+
+            bSuccess = TRUE;
+            goto lend;
+        }
+    } while (codeHundred);
 
     /* Add status code */
     HTTP_ProcessHeader(lpwhr, szStatus, status_code,
@@ -3769,19 +4501,22 @@ static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
     HeapFree(GetProcessHeap(),0,lpwhr->lpszVersion);
     HeapFree(GetProcessHeap(),0,lpwhr->lpszStatusText);
 
-    lpwhr->lpszVersion= WININET_strdupW(buffer);
-    lpwhr->lpszStatusText = WININET_strdupW(status_text);
+    lpwhr->lpszVersion = heap_strdupW(buffer);
+    lpwhr->lpszStatusText = heap_strdupW(status_text);
 
     /* Restore the spaces */
     *(status_code-1) = ' ';
     *(status_text-1) = ' ';
 
     /* regenerate raw headers */
+    lpszRawHeaders = HeapAlloc(GetProcessHeap(), 0, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
+    if (!lpszRawHeaders) goto lend;
+
     while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
-    {
         cchMaxRawHeaders *= 2;
-        lpszRawHeaders = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
-    }
+    temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+    if (temp == NULL) goto lend;
+    lpszRawHeaders = temp;
     memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
     cchRawHeaders += (buflen-1);
     memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
@@ -3792,33 +4527,35 @@ static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
     do
     {
        buflen = MAX_REPLY_LEN;
-        if (NETCON_getNextLine(&lpwhr->netConnection, bufferA, &buflen))
+        if (read_line(lpwhr, bufferA, &buflen))
         {
             LPWSTR * pFieldAndValue;
 
             TRACE("got line %s, now interpreting\n", debugstr_a(bufferA));
+
+            if (!bufferA[0]) break;
             MultiByteToWideChar( CP_ACP, 0, bufferA, buflen, buffer, MAX_REPLY_LEN );
 
-            while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
+            pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
+            if (pFieldAndValue)
             {
-                cchMaxRawHeaders *= 2;
-                lpszRawHeaders = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+                while (cchRawHeaders + buflen + strlenW(szCrLf) > cchMaxRawHeaders)
+                    cchMaxRawHeaders *= 2;
+                temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders+1)*sizeof(WCHAR));
+                if (temp == NULL) goto lend;
+                lpszRawHeaders = temp;
+                memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
+                cchRawHeaders += (buflen-1);
+                memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
+                cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
+                lpszRawHeaders[cchRawHeaders] = '\0';
+
+                HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1],
+                                   HTTP_ADDREQ_FLAG_ADD );
+
+                HTTP_FreeTokens(pFieldAndValue);
             }
-            memcpy(lpszRawHeaders+cchRawHeaders, buffer, (buflen-1)*sizeof(WCHAR));
-            cchRawHeaders += (buflen-1);
-            memcpy(lpszRawHeaders+cchRawHeaders, szCrLf, sizeof(szCrLf));
-            cchRawHeaders += sizeof(szCrLf)/sizeof(szCrLf[0])-1;
-            lpszRawHeaders[cchRawHeaders] = '\0';
-
-            pFieldAndValue = HTTP_InterpretHttpHeader(buffer);
-            if (!pFieldAndValue)
-                break;
-
-            HTTP_ProcessHeader(lpwhr, pFieldAndValue[0], pFieldAndValue[1], 
-                HTTP_ADDREQ_FLAG_ADD );
-
-            HTTP_FreeTokens(pFieldAndValue);
-       }
+        }
        else
        {
            cbreaks++;
@@ -3827,6 +4564,19 @@ static INT HTTP_GetResponseHeaders(LPWININETHTTPREQW lpwhr, BOOL clear)
        }
     }while(1);
 
+    /* make sure the response header is terminated with an empty line.  Some apps really
+       truly care about that empty line being there for some reason.  Just add it to the
+       header. */
+    if (cchRawHeaders + strlenW(szCrLf) > cchMaxRawHeaders)
+    {
+        cchMaxRawHeaders = cchRawHeaders + strlenW(szCrLf);
+        temp = HeapReAlloc(GetProcessHeap(), 0, lpszRawHeaders, (cchMaxRawHeaders + 1) * sizeof(WCHAR));
+        if (temp == NULL) goto lend;
+        lpszRawHeaders = temp;
+    }
+
+    memcpy(&lpszRawHeaders[cchRawHeaders], szCrLf, sizeof(szCrLf));
+
     HeapFree(GetProcessHeap(), 0, lpwhr->lpszRawHeaders);
     lpwhr->lpszRawHeaders = lpszRawHeaders;
     TRACE("raw headers: %s\n", debugstr_w(lpszRawHeaders));
@@ -3844,27 +4594,6 @@ lend:
     }
 }
 
-
-static void strip_spaces(LPWSTR start)
-{
-    LPWSTR str = start;
-    LPWSTR end;
-
-    while (*str == ' ' && *str != '\0')
-        str++;
-
-    if (str != start)
-        memmove(start, str, sizeof(WCHAR) * (strlenW(str) + 1));
-
-    end = start + strlenW(start) - 1;
-    while (end >= start && *end == ' ')
-    {
-        *end = '\0';
-        end--;
-    }
-}
-
-
 /***********************************************************************
  *           HTTP_InterpretHttpHeader (internal)
  *
@@ -3929,12 +4658,12 @@ static LPWSTR * HTTP_InterpretHttpHeader(LPCWSTR buffer)
 
 #define COALESCEFLAGS (HTTP_ADDHDR_FLAG_COALESCE|HTTP_ADDHDR_FLAG_COALESCE_WITH_COMMA|HTTP_ADDHDR_FLAG_COALESCE_WITH_SEMICOLON)
 
-static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
+static DWORD HTTP_ProcessHeader(http_request_t *lpwhr, LPCWSTR field, LPCWSTR value, DWORD dwModifier)
 {
     LPHTTPHEADERW lphttpHdr = NULL;
-    BOOL bSuccess = FALSE;
     INT index = -1;
     BOOL request_only = dwModifier & HTTP_ADDHDR_FLAG_REQ;
+    DWORD res = ERROR_HTTP_INVALID_HEADER;
 
     TRACE("--> %s: %s - 0x%08x\n", debugstr_w(field), debugstr_w(value), dwModifier);
 
@@ -3950,9 +4679,7 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
     if (index >= 0)
     {
         if (dwModifier & HTTP_ADDHDR_FLAG_ADD_IF_NEW)
-        {
-            return FALSE;
-        }
+            return ERROR_HTTP_INVALID_HEADER;
         lphttpHdr = &lpwhr->pCustHeaders[index];
     }
     else if (value)
@@ -3969,7 +4696,7 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
         return HTTP_InsertCustomHeader(lpwhr, &hdr);
     }
     /* no value to delete */
-    else return TRUE;
+    else return ERROR_SUCCESS;
 
     if (dwModifier & HTTP_ADDHDR_FLAG_REQ)
            lphttpHdr->wFlags |= HDR_ISREQUEST;
@@ -3994,7 +4721,7 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
             return HTTP_InsertCustomHeader(lpwhr, &hdr);
         }
 
-        return TRUE;
+        return ERROR_SUCCESS;
     }
     else if (dwModifier & COALESCEFLAGS)
     {
@@ -4032,16 +4759,16 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
 
             memcpy(&lphttpHdr->lpszValue[origlen], value, valuelen*sizeof(WCHAR));
             lphttpHdr->lpszValue[len] = '\0';
-            bSuccess = TRUE;
+            res = ERROR_SUCCESS;
         }
         else
         {
             WARN("HeapReAlloc (%d bytes) failed\n",len+1);
-            INTERNET_SetLastError(ERROR_OUTOFMEMORY);
+            res = ERROR_OUTOFMEMORY;
         }
     }
-    TRACE("<-- %d\n",bSuccess);
-    return bSuccess;
+    TRACE("<-- %d\n", res);
+    return res;
 }
 
 
@@ -4051,30 +4778,12 @@ static BOOL HTTP_ProcessHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field, LPCWSTR v
  * Called when all content from server has been read by client.
  *
  */
-BOOL HTTP_FinishedReading(LPWININETHTTPREQW lpwhr)
+static BOOL HTTP_FinishedReading(http_request_t *lpwhr)
 {
-    WCHAR szVersion[10];
-    WCHAR szConnectionResponse[20];
-    DWORD dwBufferSize = sizeof(szVersion);
-    BOOL keepalive = FALSE;
+    BOOL keepalive = HTTP_KeepAlive(lpwhr);
 
     TRACE("\n");
 
-    /* as per RFC 2068, S8.1.2.1, if the client is HTTP/1.1 then assume that
-     * the connection is keep-alive by default */
-    if (!HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_VERSION, szVersion,
-                             &dwBufferSize, NULL) ||
-        strcmpiW(szVersion, g_szHttp1_1))
-    {
-        keepalive = TRUE;
-    }
-
-    dwBufferSize = sizeof(szConnectionResponse);
-    if (HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_PROXY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL) ||
-        HTTP_HttpQueryInfoW(lpwhr, HTTP_QUERY_CONNECTION, szConnectionResponse, &dwBufferSize, NULL))
-    {
-        keepalive = !strcmpiW(szConnectionResponse, szKeepAlive);
-    }
 
     if (!keepalive)
     {
@@ -4093,12 +4802,12 @@ BOOL HTTP_FinishedReading(LPWININETHTTPREQW lpwhr)
  * Return index of custom header from header array
  *
  */
-static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField,
+static INT HTTP_GetCustomHeaderIndex(http_request_t *lpwhr, LPCWSTR lpszField,
                                      int requested_index, BOOL request_only)
 {
     DWORD index;
 
-    TRACE("%s\n", debugstr_w(lpszField));
+    TRACE("%s, %d, %d\n", debugstr_w(lpszField), requested_index, request_only);
 
     for (index = 0; index < lpwhr->nCustHeaders; index++)
     {
@@ -4130,11 +4839,10 @@ static INT HTTP_GetCustomHeaderIndex(LPWININETHTTPREQW lpwhr, LPCWSTR lpszField,
  * Insert header into array
  *
  */
-static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr)
+static DWORD HTTP_InsertCustomHeader(http_request_t *lpwhr, LPHTTPHEADERW lpHdr)
 {
     INT count;
     LPHTTPHEADERW lph = NULL;
-    BOOL r = FALSE;
 
     TRACE("--> %s: %s\n", debugstr_w(lpHdr->lpszField), debugstr_w(lpHdr->lpszValue));
     count = lpwhr->nCustHeaders + 1;
@@ -4143,22 +4851,17 @@ static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr
     else
        lph = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(HTTPHEADERW) * count);
 
-    if (NULL != lph)
-    {
-       lpwhr->pCustHeaders = lph;
-        lpwhr->pCustHeaders[count-1].lpszField = WININET_strdupW(lpHdr->lpszField);
-        lpwhr->pCustHeaders[count-1].lpszValue = WININET_strdupW(lpHdr->lpszValue);
-        lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
-        lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
-       lpwhr->nCustHeaders++;
-        r = TRUE;
-    }
-    else
-    {
-        INTERNET_SetLastError(ERROR_OUTOFMEMORY);
-    }
+    if (!lph)
+        return ERROR_OUTOFMEMORY;
 
-    return r;
+    lpwhr->pCustHeaders = lph;
+    lpwhr->pCustHeaders[count-1].lpszField = heap_strdupW(lpHdr->lpszField);
+    lpwhr->pCustHeaders[count-1].lpszValue = heap_strdupW(lpHdr->lpszValue);
+    lpwhr->pCustHeaders[count-1].wFlags = lpHdr->wFlags;
+    lpwhr->pCustHeaders[count-1].wCount= lpHdr->wCount;
+    lpwhr->nCustHeaders++;
+
+    return ERROR_SUCCESS;
 }
 
 
@@ -4168,7 +4871,7 @@ static BOOL HTTP_InsertCustomHeader(LPWININETHTTPREQW lpwhr, LPHTTPHEADERW lpHdr
  * Delete header from array
  *  If this function is called, the indexs may change.
  */
-static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
+static BOOL HTTP_DeleteCustomHeader(http_request_t *lpwhr, DWORD index)
 {
     if( lpwhr->nCustHeaders <= 0 )
         return FALSE;
@@ -4193,13 +4896,13 @@ static BOOL HTTP_DeleteCustomHeader(LPWININETHTTPREQW lpwhr, DWORD index)
  * Verify the given header is not invalid for the given http request
  *
  */
-static BOOL HTTP_VerifyValidHeader(LPWININETHTTPREQW lpwhr, LPCWSTR field)
+static BOOL HTTP_VerifyValidHeader(http_request_t *lpwhr, LPCWSTR field)
 {
     /* Accept-Encoding is stripped from HTTP/1.0 requests. It is invalid */
     if (!strcmpW(lpwhr->lpszVersion, g_szHttp1_0) && !strcmpiW(field, szAccept_Encoding))
-        return FALSE;
+        return ERROR_HTTP_INVALID_HEADER;
 
-    return TRUE;
+    return ERROR_SUCCESS;
 }
 
 /***********************************************************************
index bad795d..45b6aa7 100644 (file)
 
 #define MAXHOSTNAME 100 /* from http.c */
 
+#if defined(__MINGW32__) || defined (_MSC_VER)
+#include <ws2tcpip.h>
+#endif
+
 #include <string.h>
 #include <stdarg.h>
 #include <stdio.h>
@@ -97,13 +101,27 @@ static CRITICAL_SECTION_DEBUG WININET_cs_debug =
 };
 static CRITICAL_SECTION WININET_cs = { &WININET_cs_debug, -1, 0, 0, 0, 0 };
 
-static LPWININETHANDLEHEADER *WININET_Handles;
+static object_header_t **WININET_Handles;
 static UINT WININET_dwNextHandle;
 static UINT WININET_dwMaxHandles;
 
-HINTERNET WININET_AllocHandle( LPWININETHANDLEHEADER info )
+typedef struct
 {
-    LPWININETHANDLEHEADER *p;
+    DWORD dwProxyEnabled;
+    LPWSTR lpszProxyServer;
+    LPWSTR lpszProxyBypass;
+} proxyinfo_t;
+
+static const WCHAR szInternetSettings[] =
+    { 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
+      'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+      'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0 };
+static const WCHAR szProxyServer[] = { 'P','r','o','x','y','S','e','r','v','e','r', 0 };
+static const WCHAR szProxyEnable[] = { 'P','r','o','x','y','E','n','a','b','l','e', 0 };
+
+HINTERNET WININET_AllocHandle( object_header_t *info )
+{
+    object_header_t **p;
     UINT handle = 0, num;
 
     list_init( &info->children );
@@ -113,7 +131,7 @@ HINTERNET WININET_AllocHandle( LPWININETHANDLEHEADER info )
     {
         num = HANDLE_CHUNK_SIZE;
         p = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
-                   sizeof (UINT)* num);
+                   sizeof (*WININET_Handles)* num);
         if( !p )
             goto end;
         WININET_Handles = p;
@@ -123,7 +141,7 @@ HINTERNET WININET_AllocHandle( LPWININETHANDLEHEADER info )
     {
         num = WININET_dwMaxHandles + HANDLE_CHUNK_SIZE;
         p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
-                   WININET_Handles, sizeof (UINT)* num);
+                   WININET_Handles, sizeof (*WININET_Handles)* num);
         if( !p )
             goto end;
         WININET_Handles = p;
@@ -145,16 +163,16 @@ end:
     return info->hInternet = (HINTERNET) (handle+1);
 }
 
-LPWININETHANDLEHEADER WININET_AddRef( LPWININETHANDLEHEADER info )
+object_header_t *WININET_AddRef( object_header_t *info )
 {
     ULONG refs = InterlockedIncrement(&info->refs);
     TRACE("%p -> refcount = %d\n", info, refs );
     return info;
 }
 
-LPWININETHANDLEHEADER WININET_GetObject( HINTERNET hinternet )
+object_header_t *WININET_GetObject( HINTERNET hinternet )
 {
-    LPWININETHANDLEHEADER info = NULL;
+    object_header_t *info = NULL;
     UINT handle = (UINT) hinternet;
 
     EnterCriticalSection( &WININET_cs );
@@ -170,7 +188,7 @@ LPWININETHANDLEHEADER WININET_GetObject( HINTERNET hinternet )
     return info;
 }
 
-BOOL WININET_Release( LPWININETHANDLEHEADER info )
+BOOL WININET_Release( object_header_t *info )
 {
     ULONG refs = InterlockedDecrement(&info->refs);
     TRACE( "object %p refcount = %d\n", info, refs );
@@ -182,7 +200,8 @@ BOOL WININET_Release( LPWININETHANDLEHEADER info )
             info->vtbl->CloseConnection( info );
         }
         /* Don't send a callback if this is a session handle created with InternetOpenUrl */
-        if (info->htype != WH_HHTTPSESSION || !(info->dwInternalFlags & INET_OPENURL))
+        if ((info->htype != WH_HHTTPSESSION && info->htype != WH_HFTPSESSION)
+            || !(info->dwInternalFlags & INET_OPENURL))
         {
             INTERNET_SendCallback(info, info->dwContext,
                                   INTERNET_STATUS_HANDLE_CLOSING, &info->hInternet,
@@ -200,7 +219,7 @@ BOOL WININET_FreeHandle( HINTERNET hinternet )
 {
     BOOL ret = FALSE;
     UINT handle = (UINT) hinternet;
-    LPWININETHANDLEHEADER info = NULL, child, next;
+    object_header_t *info = NULL, *child, *next;
 
     EnterCriticalSection( &WININET_cs );
 
@@ -224,7 +243,7 @@ BOOL WININET_FreeHandle( HINTERNET hinternet )
     if( info )
     {
         /* Free all children as native does */
-        LIST_FOR_EACH_ENTRY_SAFE( child, next, &info->children, WININETHANDLEHEADER, entry )
+        LIST_FOR_EACH_ENTRY_SAFE( child, next, &info->children, object_header_t, entry )
         {
             TRACE( "freeing child handle %d for parent handle %d\n",
                    (UINT)child->hInternet, handle+1);
@@ -285,6 +304,8 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 
         case DLL_PROCESS_DETACH:
 
+            NETCON_unload();
+
            URLCacheContainers_DeleteAll();
 
            if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
@@ -298,6 +319,48 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
     return TRUE;
 }
 
+/***********************************************************************
+ *           INTERNET_SaveProxySettings
+ *
+ * Stores the proxy settings given by lpwai into the registry
+ *
+ * RETURNS
+ *     ERROR_SUCCESS if no error, or error code on fail
+ */
+static LONG INTERNET_SaveProxySettings( proxyinfo_t *lpwpi )
+{
+    HKEY key;
+    LONG ret;
+
+    if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
+        return ret;
+
+    if ((ret = RegSetValueExW( key, szProxyEnable, 0, REG_DWORD, (BYTE*)&lpwpi->dwProxyEnabled, sizeof(DWORD))))
+    {
+        RegCloseKey( key );
+        return ret;
+    }
+
+    if (lpwpi->lpszProxyServer)
+    {
+        if ((ret = RegSetValueExW( key, szProxyServer, 0, REG_SZ, (BYTE*)lpwpi->lpszProxyServer, sizeof(WCHAR) * (lstrlenW(lpwpi->lpszProxyServer) + 1))))
+        {
+            RegCloseKey( key );
+            return ret;
+        }
+    }
+    else
+    {
+        if ((ret = RegDeleteValueW( key, szProxyServer )))
+        {
+            RegCloseKey( key );
+            return ret;
+        }
+    }
+
+    RegCloseKey(key);
+    return ERROR_SUCCESS;
+}
 
 /***********************************************************************
  *           InternetInitializeAutoProxyDll   (WININET.@)
@@ -314,7 +377,7 @@ BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
 {
     FIXME("STUB\n");
-    INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
     return FALSE;
 }
 
@@ -331,37 +394,49 @@ BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
        DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
 {
     FIXME("STUB\n");
-    INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
+    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
     return FALSE;
 }
 
+static void FreeProxyInfo( proxyinfo_t *lpwpi )
+{
+    HeapFree(GetProcessHeap(), 0, lpwpi->lpszProxyServer);
+    HeapFree(GetProcessHeap(), 0, lpwpi->lpszProxyBypass);
+}
 
 /***********************************************************************
- *           INTERNET_ConfigureProxy
+ *          INTERNET_LoadProxySettings
+ *
+ * Loads proxy information from the registry or environment into lpwpi.
+ *
+ * The caller should call FreeProxyInfo when done with lpwpi.
  *
  * FIXME:
  * The proxy may be specified in the form 'http=proxy.my.org'
  * Presumably that means there can be ftp=ftpproxy.my.org too.
  */
-static BOOL INTERNET_ConfigureProxy( LPWININETAPPINFOW lpwai )
+static LONG INTERNET_LoadProxySettings( proxyinfo_t *lpwpi )
 {
     HKEY key;
-    DWORD type, len, enabled = 0;
+    DWORD type, len;
     LPCSTR envproxy;
-    static const WCHAR szInternetSettings[] =
-        { 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
-          'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
-          'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0 };
-    static const WCHAR szProxyServer[] = { 'P','r','o','x','y','S','e','r','v','e','r', 0 };
-    static const WCHAR szProxyEnable[] = { 'P','r','o','x','y','E','n','a','b','l','e', 0 };
+    LONG ret;
 
-    if (RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )) return FALSE;
+    if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
+        return ret;
 
-    len = sizeof enabled;
-    if (RegQueryValueExW( key, szProxyEnable, NULL, &type, (BYTE *)&enabled, &len ) || type != REG_DWORD)
-        RegSetValueExW( key, szProxyEnable, 0, REG_DWORD, (BYTE *)&enabled, sizeof(REG_DWORD) );
+    len = sizeof(DWORD);
+    if (RegQueryValueExW( key, szProxyEnable, NULL, &type, (BYTE *)&lpwpi->dwProxyEnabled, &len ) || type != REG_DWORD)
+    {
+        lpwpi->dwProxyEnabled = 0;
+        if((ret = RegSetValueExW( key, szProxyEnable, 0, REG_DWORD, (BYTE *)&lpwpi->dwProxyEnabled, sizeof(DWORD) )))
+        {
+            RegCloseKey( key );
+            return ret;
+        }
+    }
 
-    if (enabled)
+    if (!(envproxy = getenv( "http_proxy" )) || lpwpi->dwProxyEnabled)
     {
         TRACE("Proxy is enabled.\n");
 
@@ -374,7 +449,7 @@ static BOOL INTERNET_ConfigureProxy( LPWININETAPPINFOW lpwai )
             if (!(szProxy = HeapAlloc( GetProcessHeap(), 0, len )))
             {
                 RegCloseKey( key );
-                return FALSE;
+                return ERROR_OUTOFMEMORY;
             }
             RegQueryValueExW( key, szProxyServer, NULL, &type, (BYTE*)szProxy, &len );
 
@@ -388,32 +463,56 @@ static BOOL INTERNET_ConfigureProxy( LPWININETAPPINFOW lpwai )
             p = strchrW( szProxy, ' ' );
             if (p) *p = 0;
 
-            lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
-            lpwai->lpszProxy = szProxy;
+            lpwpi->lpszProxyServer = szProxy;
 
-            TRACE("http proxy = %s\n", debugstr_w(lpwai->lpszProxy));
+            TRACE("http proxy = %s\n", debugstr_w(lpwpi->lpszProxyServer));
         }
         else
-            ERR("Couldn't read proxy server settings from registry.\n");
+        {
+            TRACE("No proxy server settings in registry.\n");
+            lpwpi->lpszProxyServer = NULL;
+        }
     }
-    else if ((envproxy = getenv( "http_proxy" )))
+    else if (envproxy)
     {
         WCHAR *envproxyW;
 
         len = MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, NULL, 0 );
-        if (!(envproxyW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR)))) return FALSE;
+        if (!(envproxyW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR))))
+            return ERROR_OUTOFMEMORY;
         MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, envproxyW, len );
 
-        lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
-        lpwai->lpszProxy = envproxyW;
+        lpwpi->dwProxyEnabled = 1;
+        lpwpi->lpszProxyServer = envproxyW;
 
-        TRACE("http proxy (from environment) = %s\n", debugstr_w(lpwai->lpszProxy));
-        enabled = 1;
+        TRACE("http proxy (from environment) = %s\n", debugstr_w(lpwpi->lpszProxyServer));
     }
-    if (!enabled) TRACE("Proxy is not enabled.\n");
-
     RegCloseKey( key );
-    return (enabled > 0);
+
+    lpwpi->lpszProxyBypass = NULL;
+
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           INTERNET_ConfigureProxy
+ */
+static BOOL INTERNET_ConfigureProxy( appinfo_t *lpwai )
+{
+    proxyinfo_t wpi;
+
+    if (INTERNET_LoadProxySettings( &wpi ))
+        return FALSE;
+
+    if (wpi.dwProxyEnabled)
+    {
+        lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
+        lpwai->lpszProxy = wpi.lpszProxyServer;
+        return TRUE;
+    }
+
+    lpwai->dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
+    return FALSE;
 }
 
 /***********************************************************************
@@ -459,8 +558,8 @@ static void dump_INTERNET_FLAGS(DWORD dwFlags)
         FE(INTERNET_FLAG_TRANSFER_BINARY)
     };
 #undef FE
-    int i;
-    
+    unsigned int i;
+
     for (i = 0; i < (sizeof(flag) / sizeof(flag[0])); i++) {
        if (flag[i].val & dwFlags) {
            TRACE(" %s", flag[i].name);
@@ -479,9 +578,9 @@ static void dump_INTERNET_FLAGS(DWORD dwFlags)
  * Close internet handle
  *
  */
-static VOID APPINFO_Destroy(WININETHANDLEHEADER *hdr)
+static VOID APPINFO_Destroy(object_header_t *hdr)
 {
-    LPWININETAPPINFOW lpwai = (LPWININETAPPINFOW) hdr;
+    appinfo_t *lpwai = (appinfo_t*)hdr;
 
     TRACE("%p\n",lpwai);
 
@@ -493,9 +592,9 @@ static VOID APPINFO_Destroy(WININETHANDLEHEADER *hdr)
     HeapFree(GetProcessHeap(), 0, lpwai);
 }
 
-static DWORD APPINFO_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
+static DWORD APPINFO_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
 {
-    LPWININETAPPINFOW ai = (LPWININETAPPINFOW)hdr;
+    appinfo_t *ai = (appinfo_t*)hdr;
 
     switch(option) {
     case INTERNET_OPTION_HANDLE_TYPE:
@@ -516,17 +615,36 @@ static DWORD APPINFO_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
         bufsize = *size;
 
         if (unicode) {
-            *size = (strlenW(ai->lpszAgent) + 1) * sizeof(WCHAR);
+            DWORD len = ai->lpszAgent ? strlenW(ai->lpszAgent) : 0;
+
+            *size = (len + 1) * sizeof(WCHAR);
             if(!buffer || bufsize < *size)
                 return ERROR_INSUFFICIENT_BUFFER;
 
-            strcpyW(buffer, ai->lpszAgent);
+            if (ai->lpszAgent)
+                strcpyW(buffer, ai->lpszAgent);
+            else
+                *(WCHAR *)buffer = 0;
+            /* If the buffer is copied, the returned length doesn't include
+             * the NULL terminator.
+             */
+            *size = len * sizeof(WCHAR);
         }else {
-            *size = WideCharToMultiByte(CP_ACP, 0, ai->lpszAgent, -1, NULL, 0, NULL, NULL);
+            if (ai->lpszAgent)
+                *size = WideCharToMultiByte(CP_ACP, 0, ai->lpszAgent, -1, NULL, 0, NULL, NULL);
+            else
+                *size = 1;
             if(!buffer || bufsize < *size)
                 return ERROR_INSUFFICIENT_BUFFER;
 
-            WideCharToMultiByte(CP_ACP, 0, ai->lpszAgent, -1, buffer, *size, NULL, NULL);
+            if (ai->lpszAgent)
+                WideCharToMultiByte(CP_ACP, 0, ai->lpszAgent, -1, buffer, *size, NULL, NULL);
+            else
+                *(char *)buffer = 0;
+            /* If the buffer is copied, the returned length doesn't include
+             * the NULL terminator.
+             */
+            *size -= 1;
         }
 
         return ERROR_SUCCESS;
@@ -543,8 +661,10 @@ static DWORD APPINFO_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
             if (ai->lpszProxyBypass)
                 proxyBypassBytesRequired = (lstrlenW(ai->lpszProxyBypass) + 1) * sizeof(WCHAR);
             if (*size < sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired)
-                    return ERROR_INSUFFICIENT_BUFFER;
-
+            {
+                *size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
+                return ERROR_INSUFFICIENT_BUFFER;
+            }
             proxy = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW));
             proxy_bypass = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired);
 
@@ -574,8 +694,10 @@ static DWORD APPINFO_QueryOption(WININETHANDLEHEADER *hdr, DWORD option, void *b
                 proxyBypassBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->lpszProxyBypass, -1,
       &nbs