[WININET]
[reactos.git] / reactos / dll / win32 / wininet / internet.c
index ca7b982..f4b8a0a 100644 (file)
@@ -105,6 +105,20 @@ static object_header_t **WININET_Handles;
 static UINT WININET_dwNextHandle;
 static UINT WININET_dwMaxHandles;
 
+typedef struct
+{
+    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;
@@ -305,6 +319,140 @@ 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;
+}
+
+/***********************************************************************
+ *           INTERNET_FindProxyForProtocol
+ *
+ * Searches the proxy string for a proxy of the given protocol.
+ * Returns the found proxy, or the default proxy if none of the given
+ * protocol is found.
+ *
+ * PARAMETERS
+ *     szProxy       [In]     proxy string to search
+ *     proto         [In]     protocol to search for, e.g. "http"
+ *     foundProxy    [Out]    found proxy
+ *     foundProxyLen [In/Out] length of foundProxy buffer, in WCHARs
+ *
+ * RETURNS
+ *     TRUE if a proxy is found, FALSE if not.  If foundProxy is too short,
+ *     *foundProxyLen is set to the required size in WCHARs, including the
+ *     NULL terminator, and the last error is set to ERROR_INSUFFICIENT_BUFFER.
+ */
+BOOL INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto, WCHAR *foundProxy, DWORD *foundProxyLen)
+{
+    LPCWSTR ptr;
+    BOOL ret = FALSE;
+
+    TRACE("(%s, %s)\n", debugstr_w(szProxy), debugstr_w(proto));
+
+    /* First, look for the specified protocol (proto=scheme://host:port) */
+    for (ptr = szProxy; !ret && ptr && *ptr; )
+    {
+        LPCWSTR end, equal;
+
+        if (!(end = strchrW(ptr, ' ')))
+            end = ptr + strlenW(ptr);
+        if ((equal = strchrW(ptr, '=')) && equal < end &&
+             equal - ptr == strlenW(proto) &&
+             !strncmpiW(proto, ptr, strlenW(proto)))
+        {
+            if (end - equal > *foundProxyLen)
+            {
+                WARN("buffer too short for %s\n",
+                     debugstr_wn(equal + 1, end - equal - 1));
+                *foundProxyLen = end - equal;
+                SetLastError(ERROR_INSUFFICIENT_BUFFER);
+            }
+            else
+            {
+                memcpy(foundProxy, equal + 1, (end - equal) * sizeof(WCHAR));
+                foundProxy[end - equal] = 0;
+                ret = TRUE;
+            }
+        }
+        if (*end == ' ')
+            ptr = end + 1;
+        else
+            ptr = end;
+    }
+    if (!ret)
+    {
+        /* It wasn't found: look for no protocol */
+        for (ptr = szProxy; !ret && ptr && *ptr; )
+        {
+            LPCWSTR end, equal;
+
+            if (!(end = strchrW(ptr, ' ')))
+                end = ptr + strlenW(ptr);
+            if (!(equal = strchrW(ptr, '=')))
+            {
+                if (end - ptr + 1 > *foundProxyLen)
+                {
+                    WARN("buffer too short for %s\n",
+                         debugstr_wn(ptr, end - ptr));
+                    *foundProxyLen = end - ptr + 1;
+                    SetLastError(ERROR_INSUFFICIENT_BUFFER);
+                }
+                else
+                {
+                    memcpy(foundProxy, ptr, (end - ptr) * sizeof(WCHAR));
+                    foundProxy[end - ptr] = 0;
+                    ret = TRUE;
+                }
+            }
+            if (*end == ' ')
+                ptr = end + 1;
+            else
+                ptr = end;
+        }
+    }
+    if (ret)
+        TRACE("found proxy for %s: %s\n", debugstr_w(proto),
+              debugstr_w(foundProxy));
+    return ret;
+}
 
 /***********************************************************************
  *           InternetInitializeAutoProxyDll   (WININET.@)
@@ -321,7 +469,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;
 }
 
@@ -338,37 +486,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( appinfo_t *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");
 
@@ -381,7 +541,7 @@ static BOOL INTERNET_ConfigureProxy( appinfo_t *lpwai )
             if (!(szProxy = HeapAlloc( GetProcessHeap(), 0, len )))
             {
                 RegCloseKey( key );
-                return FALSE;
+                return ERROR_OUTOFMEMORY;
             }
             RegQueryValueExW( key, szProxyServer, NULL, &type, (BYTE*)szProxy, &len );
 
@@ -395,35 +555,56 @@ static BOOL INTERNET_ConfigureProxy( appinfo_t *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)
+    RegCloseKey( key );
+
+    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)
     {
-        TRACE("Proxy is not enabled.\n");
-        lpwai->dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
+        lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
+        lpwai->lpszProxy = wpi.lpszProxyServer;
+        return TRUE;
     }
-    RegCloseKey( key );
-    return (enabled > 0);
+
+    lpwai->dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
+    return FALSE;
 }
 
 /***********************************************************************
@@ -949,6 +1130,7 @@ HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
 {
     appinfo_t *hIC;
     HINTERNET rc = NULL;
+    DWORD res = ERROR_SUCCESS;
 
     TRACE("(%p, %s, %i, %s, %s, %i, %i, %lx)\n", hInternet, debugstr_w(lpszServerName),
          nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword),
@@ -956,16 +1138,14 @@ HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
 
     if (!lpszServerName)
     {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_PARAMETER);
         return NULL;
     }
 
-    /* Clear any error information */
-    INTERNET_SetLastError(0);
     hIC = (appinfo_t*)WININET_GetObject( hInternet );
     if ( (hIC == NULL) || (hIC->hdr.htype != WH_HINIT) )
     {
-        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
+        res = ERROR_INVALID_HANDLE;
         goto lend;
     }
 
@@ -974,11 +1154,13 @@ HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
         case INTERNET_SERVICE_FTP:
             rc = FTP_Connect(hIC, lpszServerName, nServerPort,
             lpszUserName, lpszPassword, dwFlags, dwContext, 0);
+            if(!rc)
+                res = INTERNET_GetLastError();
             break;
 
         case INTERNET_SERVICE_HTTP:
-           rc = HTTP_Connect(hIC, lpszServerName, nServerPort,
-            lpszUserName, lpszPassword, dwFlags, dwContext, 0);
+           res = HTTP_Connect(hIC, lpszServerName, nServerPort,
+                    lpszUserName, lpszPassword, dwFlags, dwContext, 0, &rc);
             break;
 
         case INTERNET_SERVICE_GOPHER:
@@ -990,6 +1172,7 @@ lend:
         WININET_Release( &hIC->hdr );
 
     TRACE("returning %p\n", rc);
+    SetLastError(res);
     return rc;
 }
 
@@ -1133,11 +1316,14 @@ static void ConvertUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentL
         DWORD nASCIILength=WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,NULL,0,NULL,NULL);
         if (*lppszComponent == NULL)
         {
-            int nASCIIOffset=WideCharToMultiByte(CP_ACP,0,lpwszStart,lpwszComponent-lpwszStart,NULL,0,NULL,NULL);
             if (lpwszComponent)
-                *lppszComponent = (LPSTR)lpszStart+nASCIIOffset;
+            {
+                int offset = WideCharToMultiByte(CP_ACP, 0, lpwszStart, lpwszComponent-lpwszStart, NULL, 0, NULL, NULL);
+                *lppszComponent = (LPSTR)lpszStart + offset;
+            }
             else
                 *lppszComponent = NULL;
+
             *dwComponentLen = nASCIILength;
         }
         else
@@ -1184,7 +1370,7 @@ BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
        InternetCrackUrlW should not include it                  */
   if (dwUrlLength == -1) nLength--;
 
-  lpwszUrl=HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*nLength);
+  lpwszUrl = HeapAlloc(GetProcessHeap(), 0, nLength * sizeof(WCHAR));
   MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,lpwszUrl,nLength);
 
   memset(&UCW,0,sizeof(UCW));
@@ -1547,7 +1733,7 @@ BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl_orig, DWORD dwUrlLength_orig, DWOR
                     }
 
                     /* If the scheme is "file" and the host is just one letter, it's not a host */
-                    if(lpUC->nScheme==INTERNET_SCHEME_FILE && (lpszPort-lpszHost)==1)
+                    if(lpUC->nScheme==INTERNET_SCHEME_FILE && lpszPort <= lpszHost+1)
                     {
                         lpszcp=lpszHost;
                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
@@ -1600,7 +1786,7 @@ BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl_orig, DWORD dwUrlLength_orig, DWOR
      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      */
-    if (lpszcp != 0 && lpszcp - lpszUrl < dwUrlLength && (!lpszParam || lpszcp < lpszParam))
+    if (lpszcp != 0 && lpszcp - lpszUrl < dwUrlLength && (!lpszParam || lpszcp <= lpszParam))
     {
         INT len;
 
@@ -1859,7 +2045,7 @@ BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer,
        DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
 {
     object_header_t *lpwh;
-    BOOL retval = FALSE;
+    BOOL res;
 
     TRACE("(%p %p %d %p)\n", hFile, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
 
@@ -1871,16 +2057,17 @@ BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer,
     }
 
     if(lpwh->vtbl->WriteFile) {
-        retval = lpwh->vtbl->WriteFile(lpwh, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
+        res = lpwh->vtbl->WriteFile(lpwh, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
     }else {
         WARN("No Writefile method.\n");
-        SetLastError(ERROR_INVALID_HANDLE);
-        retval = FALSE;
+        res = ERROR_INVALID_HANDLE;
     }
 
     WININET_Release( lpwh );
 
-    return retval;
+    if(res != ERROR_SUCCESS)
+        SetLastError(res);
+    return res == ERROR_SUCCESS;
 }
 
 
@@ -2106,16 +2293,17 @@ DWORD INET_QueryOption(DWORD option, void *buffer, DWORD *size, BOOL unicode)
         INTERNET_PER_CONN_OPTION_LISTW *con = buffer;
         INTERNET_PER_CONN_OPTION_LISTA *conA = buffer;
         DWORD res = ERROR_SUCCESS, i;
-        appinfo_t ai;
+        proxyinfo_t pi;
+        LONG ret;
 
         TRACE("Getting global proxy info\n");
-        memset(&ai, 0, sizeof(appinfo_t));
-        INTERNET_ConfigureProxy(&ai);
+        if((ret = INTERNET_LoadProxySettings(&pi)))
+            return ret;
 
         FIXME("INTERNET_OPTION_PER_CONNECTION_OPTION stub\n");
 
         if (*size < sizeof(INTERNET_PER_CONN_OPTION_LISTW)) {
-            APPINFO_Destroy(&ai.hdr);
+            FreeProxyInfo(&pi);
             return ERROR_INSUFFICIENT_BUFFER;
         }
 
@@ -2125,21 +2313,24 @@ DWORD INET_QueryOption(DWORD option, void *buffer, DWORD *size, BOOL unicode)
 
             switch (option->dwOption) {
             case INTERNET_PER_CONN_FLAGS:
-                option->Value.dwValue = ai.dwAccessType;
+                if(pi.dwProxyEnabled)
+                    option->Value.dwValue = PROXY_TYPE_PROXY;
+                else
+                    option->Value.dwValue = PROXY_TYPE_DIRECT;
                 break;
 
             case INTERNET_PER_CONN_PROXY_SERVER:
                 if (unicode)
-                    option->Value.pszValue = heap_strdupW(ai.lpszProxy);
+                    option->Value.pszValue = heap_strdupW(pi.lpszProxyServer);
                 else
-                    optionA->Value.pszValue = heap_strdupWtoA(ai.lpszProxy);
+                    optionA->Value.pszValue = heap_strdupWtoA(pi.lpszProxyServer);
                 break;
 
             case INTERNET_PER_CONN_PROXY_BYPASS:
                 if (unicode)
-                    option->Value.pszValue = heap_strdupW(ai.lpszProxyBypass);
+                    option->Value.pszValue = heap_strdupW(pi.lpszProxyBypass);
                 else
-                    optionA->Value.pszValue = heap_strdupWtoA(ai.lpszProxyBypass);
+                    optionA->Value.pszValue = heap_strdupWtoA(pi.lpszProxyBypass);
                 break;
 
             case INTERNET_PER_CONN_AUTOCONFIG_URL:
@@ -2158,12 +2349,14 @@ DWORD INET_QueryOption(DWORD option, void *buffer, DWORD *size, BOOL unicode)
                 break;
             }
         }
-        APPINFO_Destroy(&ai.hdr);
+        FreeProxyInfo(&pi);
 
         return res;
     }
     case INTERNET_OPTION_USER_AGENT:
         return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
+    case INTERNET_OPTION_POLICY:
+        return ERROR_INVALID_PARAMETER;
     }
 
     FIXME("Stub for %d\n", option);
@@ -2276,11 +2469,11 @@ BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
       {
         if (!lpwhh)
         {
-            INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+            SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
             return FALSE;
         }
         WININET_Release(lpwhh);
-        INTERNET_SetLastError(ERROR_INTERNET_OPTION_NOT_SETTABLE);
+        SetLastError(ERROR_INTERNET_OPTION_NOT_SETTABLE);
         return FALSE;
       }
     case INTERNET_OPTION_HTTP_VERSION:
@@ -2367,37 +2560,94 @@ BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
         break;
     case INTERNET_OPTION_HTTP_DECODING:
         FIXME("INTERNET_OPTION_HTTP_DECODING; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     case INTERNET_OPTION_COOKIES_3RD_PARTY:
         FIXME("INTERNET_OPTION_COOKIES_3RD_PARTY; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     case INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY:
         FIXME("INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     case INTERNET_OPTION_CODEPAGE_PATH:
         FIXME("INTERNET_OPTION_CODEPAGE_PATH; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     case INTERNET_OPTION_CODEPAGE_EXTRA:
         FIXME("INTERNET_OPTION_CODEPAGE_EXTRA; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     case INTERNET_OPTION_IDN:
         FIXME("INTERNET_OPTION_IDN; STUB\n");
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        ret = FALSE;
+        break;
+    case INTERNET_OPTION_POLICY:
+        SetLastError(ERROR_INVALID_PARAMETER);
         ret = FALSE;
         break;
+    case INTERNET_OPTION_PER_CONNECTION_OPTION: {
+        INTERNET_PER_CONN_OPTION_LISTW *con = lpBuffer;
+        LONG res;
+        int i;
+        proxyinfo_t pi;
+
+        INTERNET_LoadProxySettings(&pi);
+
+        for (i = 0; i < con->dwOptionCount; i++) {
+            INTERNET_PER_CONN_OPTIONW *option = con->pOptions + i;
+
+            switch (option->dwOption) {
+            case INTERNET_PER_CONN_PROXY_SERVER:
+                HeapFree(GetProcessHeap(), 0, pi.lpszProxyServer);
+                pi.lpszProxyServer = heap_strdupW(option->Value.pszValue);
+                break;
+
+            case INTERNET_PER_CONN_FLAGS:
+                if(option->Value.dwValue & PROXY_TYPE_PROXY)
+                    pi.dwProxyEnabled = 1;
+                else
+                {
+                    if(option->Value.dwValue != PROXY_TYPE_DIRECT)
+                        FIXME("Unhandled flags: 0x%x\n", option->Value.dwValue);
+                    pi.dwProxyEnabled = 0;
+                }
+                break;
+
+            case INTERNET_PER_CONN_AUTOCONFIG_URL:
+            case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
+            case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
+            case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
+            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
+            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
+            case INTERNET_PER_CONN_PROXY_BYPASS:
+                FIXME("Unhandled dwOption %d\n", option->dwOption);
+                break;
+
+            default:
+                FIXME("Unknown dwOption %d\n", option->dwOption);
+                SetLastError(ERROR_INVALID_PARAMETER);
+                break;
+            }
+        }
+
+        if ((res = INTERNET_SaveProxySettings(&pi)))
+            SetLastError(res);
+
+        FreeProxyInfo(&pi);
+
+        ret = (res == ERROR_SUCCESS);
+        break;
+        }
     default:
         FIXME("Option %d STUB\n",dwOption);
-        INTERNET_SetLastError(ERROR_INTERNET_INVALID_OPTION);
+        SetLastError(ERROR_INTERNET_INVALID_OPTION);
         ret = FALSE;
         break;
     }
@@ -2471,6 +2721,63 @@ BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
         MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
                                    wbuffer, wlen );
         break;
+    case INTERNET_OPTION_PER_CONNECTION_OPTION: {
+        int i;
+        INTERNET_PER_CONN_OPTION_LISTW *listW;
+        INTERNET_PER_CONN_OPTION_LISTA *listA = lpBuffer;
+        wlen = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
+        wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen );
+        listW = wbuffer;
+
+        listW->dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
+        if (listA->pszConnection)
+        {
+            wlen = MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, NULL, 0 );
+            listW->pszConnection = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
+            MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, listW->pszConnection, wlen );
+        }
+        else
+            listW->pszConnection = NULL;
+        listW->dwOptionCount = listA->dwOptionCount;
+        listW->dwOptionError = listA->dwOptionError;
+        listW->pOptions = HeapAlloc( GetProcessHeap(), 0, sizeof(INTERNET_PER_CONN_OPTIONW) * listA->dwOptionCount );
+
+        for (i = 0; i < listA->dwOptionCount; ++i) {
+            INTERNET_PER_CONN_OPTIONA *optA = listA->pOptions + i;
+            INTERNET_PER_CONN_OPTIONW *optW = listW->pOptions + i;
+
+            optW->dwOption = optA->dwOption;
+
+            switch (optA->dwOption) {
+            case INTERNET_PER_CONN_AUTOCONFIG_URL:
+            case INTERNET_PER_CONN_PROXY_BYPASS:
+            case INTERNET_PER_CONN_PROXY_SERVER:
+            case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
+            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
+                if (optA->Value.pszValue)
+                {
+                    wlen = MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, NULL, 0 );
+                    optW->Value.pszValue = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
+                    MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, optW->Value.pszValue, wlen );
+                }
+                else
+                    optW->Value.pszValue = NULL;
+                break;
+            case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
+            case INTERNET_PER_CONN_FLAGS:
+            case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
+                optW->Value.dwValue = optA->Value.dwValue;
+                break;
+            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
+                optW->Value.ftValue = optA->Value.ftValue;
+            default:
+                WARN("Unknown PER_CONN dwOption: %d, guessing at conversion to Wide\n", optA->dwOption);
+                optW->Value.dwValue = optA->Value.dwValue;
+                break;
+            }
+        }
+        }
+        break;
     default:
         wbuffer = lpBuffer;
         wlen = dwBufferLength;
@@ -2479,7 +2786,29 @@ BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
 
     if( lpBuffer != wbuffer )
+    {
+        if (dwOption == INTERNET_OPTION_PER_CONNECTION_OPTION)
+        {
+            INTERNET_PER_CONN_OPTION_LISTW *list = wbuffer;
+            int i;
+            for (i = 0; i < list->dwOptionCount; ++i) {
+                INTERNET_PER_CONN_OPTIONW *opt = list->pOptions + i;
+                switch (opt->dwOption) {
+                case INTERNET_PER_CONN_AUTOCONFIG_URL:
+                case INTERNET_PER_CONN_PROXY_BYPASS:
+                case INTERNET_PER_CONN_PROXY_SERVER:
+                case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
+                case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
+                    HeapFree( GetProcessHeap(), 0, opt->Value.pszValue );
+                    break;
+                default:
+                    break;
+                }
+            }
+            HeapFree( GetProcessHeap(), 0, list->pOptions );
+        }
         HeapFree( GetProcessHeap(), 0, wbuffer );
+    }
 
     return r;
 }
@@ -2504,7 +2833,7 @@ BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
     FIXME("Flags %08x ignored\n", dwFlags);
     if( dwFlags & ~ISO_VALID_FLAGS )
     {
-        INTERNET_SetLastError( ERROR_INVALID_PARAMETER );
+        SetLastError( ERROR_INVALID_PARAMETER );
         return FALSE;
     }
     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
@@ -2831,6 +3160,7 @@ static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
     WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
     WCHAR password[1024], path[2048], extra[1024];
     HINTERNET client = NULL, client1 = NULL;
+    DWORD res;
     
     TRACE("(%p, %s, %s, %08x, %08x, %08lx)\n", hIC, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
          dwHeadersLength, dwFlags, dwContext);
@@ -2878,10 +3208,12 @@ static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
         if (urlComponents.nScheme == INTERNET_SCHEME_HTTPS) dwFlags |= INTERNET_FLAG_SECURE;
 
         /* FIXME: should use pointers, not handles, as handles are not thread-safe */
-       client = HTTP_Connect(hIC, hostName, urlComponents.nPort,
-                             userName, password, dwFlags, dwContext, INET_OPENURL);
-       if(client == NULL)
+       res = HTTP_Connect(hIC, hostName, urlComponents.nPort,
+                           userName, password, dwFlags, dwContext, INET_OPENURL, &client);
+        if(res != ERROR_SUCCESS) {
+            INTERNET_SetLastError(res);
            break;
+        }
 
        if (urlComponents.dwExtraInfoLength) {
                WCHAR *path_extra;
@@ -2916,7 +3248,7 @@ static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
        /* gopher doesn't seem to be implemented in wine, but it's supposed
         * to be supported by InternetOpenUrlA. */
     default:
-        INTERNET_SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
+        SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
        break;
     }
 
@@ -2961,13 +3293,13 @@ HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
 
     if (!lpszUrl)
     {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_PARAMETER);
         goto lend;
     }
 
     hIC = (appinfo_t*)WININET_GetObject( hInternet );
     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT) {
-       INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
+       SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
        goto lend;
     }
     
@@ -2988,7 +3320,7 @@ HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
        /*
         * This is from windows.
         */
-       INTERNET_SetLastError(ERROR_IO_PENDING);
+       SetLastError(ERROR_IO_PENDING);
     } else {
        ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
     }
@@ -3123,8 +3455,13 @@ static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
     HeapFree(GetProcessHeap(), 0, lpRequest);
 
     workRequest.asyncproc(&workRequest);
-
     WININET_Release( workRequest.hdr );
+
+    if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
+    {
+        HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
+        TlsSetValue(g_dwTlsErrIndex, NULL);
+    }
     return TRUE;
 }
 
@@ -3137,7 +3474,7 @@ static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
  * RETURNS
  *
  */
-BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
+DWORD INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
 {
     BOOL bSuccess;
     LPWORKREQUEST lpNewRequest;
@@ -3146,7 +3483,7 @@ BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
 
     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
     if (!lpNewRequest)
-        return FALSE;
+        return ERROR_OUTOFMEMORY;
 
     *lpNewRequest = *lpWorkRequest;
 
@@ -3154,10 +3491,10 @@ BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
     if (!bSuccess)
     {
         HeapFree(GetProcessHeap(), 0, lpNewRequest);
-        INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
+        return ERROR_INTERNET_ASYNC_THREAD_FAILED;
     }
 
-    return bSuccess;
+    return ERROR_SUCCESS;
 }
 
 
@@ -3265,7 +3602,7 @@ BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
 
     hdr = WININET_GetObject( hFile );
     if (!hdr) {
-        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
+        SetLastError(ERROR_INVALID_HANDLE);
         return FALSE;
     }
 
@@ -3603,7 +3940,7 @@ BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
 
     if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
     {
-        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
+        SetLastError(ERROR_INVALID_PARAMETER);
         return FALSE;
     }