xref: /reactos/dll/win32/wininet/internet.c (revision ebaf247c)
1 /*
2  * Wininet
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2002 CodeWeavers Inc.
6  * Copyright 2002 Jaco Greeff
7  * Copyright 2002 TransGaming Technologies Inc.
8  * Copyright 2004 Mike McCormack for CodeWeavers
9  *
10  * Ulrich Czekalla
11  * Aric Stewart
12  * David Hammerton
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27  */
28 
29 #include "config.h"
30 
31 #ifdef HAVE_CORESERVICES_CORESERVICES_H
32 #define GetCurrentThread MacGetCurrentThread
33 #define LoadResource MacLoadResource
34 #include <CoreServices/CoreServices.h>
35 #undef GetCurrentThread
36 #undef LoadResource
37 #endif
38 
39 #include "winsock2.h"
40 #include "ws2ipdef.h"
41 
42 #include <string.h>
43 #include <stdarg.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <ctype.h>
47 #include <assert.h>
48 
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winreg.h"
52 #include "winuser.h"
53 #include "wininet.h"
54 #include "winnls.h"
55 #include "wine/debug.h"
56 #include "winerror.h"
57 #define NO_SHLWAPI_STREAM
58 #include "shlwapi.h"
59 
60 #include "wine/exception.h"
61 
62 #include "internet.h"
63 #include "resource.h"
64 
65 #include "wine/unicode.h"
66 
67 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
68 
69 typedef struct
70 {
71     DWORD  dwError;
72     CHAR   response[MAX_REPLY_LEN];
73 } WITHREADERROR, *LPWITHREADERROR;
74 
75 static DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
76 HMODULE WININET_hModule;
77 
78 static CRITICAL_SECTION WININET_cs;
79 static CRITICAL_SECTION_DEBUG WININET_cs_debug =
80 {
81     0, 0, &WININET_cs,
82     { &WININET_cs_debug.ProcessLocksList, &WININET_cs_debug.ProcessLocksList },
83       0, 0, { (DWORD_PTR)(__FILE__ ": WININET_cs") }
84 };
85 static CRITICAL_SECTION WININET_cs = { &WININET_cs_debug, -1, 0, 0, 0, 0 };
86 
87 static object_header_t **handle_table;
88 static UINT_PTR next_handle;
89 static UINT_PTR handle_table_size;
90 
91 typedef struct
92 {
93     DWORD  proxyEnabled;
94     LPWSTR proxy;
95     LPWSTR proxyBypass;
96     LPWSTR proxyUsername;
97     LPWSTR proxyPassword;
98 } proxyinfo_t;
99 
100 static ULONG max_conns = 2, max_1_0_conns = 4;
101 static ULONG connect_timeout = 60000;
102 
103 static const WCHAR szInternetSettings[] =
104     { 'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
105       'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
106       'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s',0 };
107 static const WCHAR szProxyServer[] = { 'P','r','o','x','y','S','e','r','v','e','r', 0 };
108 static const WCHAR szProxyEnable[] = { 'P','r','o','x','y','E','n','a','b','l','e', 0 };
109 static const WCHAR szProxyOverride[] = { 'P','r','o','x','y','O','v','e','r','r','i','d','e', 0 };
110 
111 void *alloc_object(object_header_t *parent, const object_vtbl_t *vtbl, size_t size)
112 {
113     UINT_PTR handle = 0, num;
114     object_header_t *ret;
115     object_header_t **p;
116     BOOL res = TRUE;
117 
118     ret = heap_alloc_zero(size);
119     if(!ret)
120         return NULL;
121 
122     list_init(&ret->children);
123 
124     EnterCriticalSection( &WININET_cs );
125 
126     if(!handle_table_size) {
127         num = 16;
128         p = heap_alloc_zero(sizeof(handle_table[0]) * num);
129         if(p) {
130             handle_table = p;
131             handle_table_size = num;
132             next_handle = 1;
133         }else {
134             res = FALSE;
135         }
136     }else if(next_handle == handle_table_size) {
137         num = handle_table_size * 2;
138         p = heap_realloc_zero(handle_table, sizeof(handle_table[0]) * num);
139         if(p) {
140             handle_table = p;
141             handle_table_size = num;
142         }else {
143             res = FALSE;
144         }
145     }
146 
147     if(res) {
148         handle = next_handle;
149         if(handle_table[handle])
150             ERR("handle isn't free but should be\n");
151         handle_table[handle] = ret;
152         ret->valid_handle = TRUE;
153 
154         while(next_handle < handle_table_size && handle_table[next_handle])
155             next_handle++;
156     }
157 
158     LeaveCriticalSection( &WININET_cs );
159 
160     if(!res) {
161         heap_free(ret);
162         return NULL;
163     }
164 
165     ret->vtbl = vtbl;
166     ret->refs = 1;
167     ret->hInternet = (HINTERNET)handle;
168 
169     if(parent) {
170         ret->lpfnStatusCB = parent->lpfnStatusCB;
171         ret->dwInternalFlags = parent->dwInternalFlags & INET_CALLBACKW;
172     }
173 
174     return ret;
175 }
176 
177 object_header_t *WININET_AddRef( object_header_t *info )
178 {
179     ULONG refs = InterlockedIncrement(&info->refs);
180     TRACE("%p -> refcount = %d\n", info, refs );
181     return info;
182 }
183 
184 object_header_t *get_handle_object( HINTERNET hinternet )
185 {
186     object_header_t *info = NULL;
187     UINT_PTR handle = (UINT_PTR) hinternet;
188 
189     EnterCriticalSection( &WININET_cs );
190 
191     if(handle > 0 && handle < handle_table_size && handle_table[handle] && handle_table[handle]->valid_handle)
192         info = WININET_AddRef(handle_table[handle]);
193 
194     LeaveCriticalSection( &WININET_cs );
195 
196     TRACE("handle %ld -> %p\n", handle, info);
197 
198     return info;
199 }
200 
201 static void invalidate_handle(object_header_t *info)
202 {
203     object_header_t *child, *next;
204 
205     if(!info->valid_handle)
206         return;
207     info->valid_handle = FALSE;
208 
209     /* Free all children as native does */
210     LIST_FOR_EACH_ENTRY_SAFE( child, next, &info->children, object_header_t, entry )
211     {
212         TRACE("invalidating child handle %p for parent %p\n", child->hInternet, info);
213         invalidate_handle( child );
214     }
215 
216     WININET_Release(info);
217 }
218 
219 BOOL WININET_Release( object_header_t *info )
220 {
221     ULONG refs = InterlockedDecrement(&info->refs);
222     TRACE( "object %p refcount = %d\n", info, refs );
223     if( !refs )
224     {
225         invalidate_handle(info);
226         if ( info->vtbl->CloseConnection )
227         {
228             TRACE( "closing connection %p\n", info);
229             info->vtbl->CloseConnection( info );
230         }
231         /* Don't send a callback if this is a session handle created with InternetOpenUrl */
232         if ((info->htype != WH_HHTTPSESSION && info->htype != WH_HFTPSESSION)
233             || !(info->dwInternalFlags & INET_OPENURL))
234         {
235             INTERNET_SendCallback(info, info->dwContext,
236                                   INTERNET_STATUS_HANDLE_CLOSING, &info->hInternet,
237                                   sizeof(HINTERNET));
238         }
239         TRACE( "destroying object %p\n", info);
240         if ( info->htype != WH_HINIT )
241             list_remove( &info->entry );
242         info->vtbl->Destroy( info );
243 
244         if(info->hInternet) {
245             UINT_PTR handle = (UINT_PTR)info->hInternet;
246 
247             EnterCriticalSection( &WININET_cs );
248 
249             handle_table[handle] = NULL;
250             if(next_handle > handle)
251                 next_handle = handle;
252 
253             LeaveCriticalSection( &WININET_cs );
254         }
255 
256         heap_free(info);
257     }
258     return TRUE;
259 }
260 
261 /***********************************************************************
262  * DllMain [Internal] Initializes the internal 'WININET.DLL'.
263  *
264  * PARAMS
265  *     hinstDLL    [I] handle to the DLL's instance
266  *     fdwReason   [I]
267  *     lpvReserved [I] reserved, must be NULL
268  *
269  * RETURNS
270  *     Success: TRUE
271  *     Failure: FALSE
272  */
273 
274 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
275 {
276     TRACE("%p,%x,%p\n", hinstDLL, fdwReason, lpvReserved);
277 
278     switch (fdwReason) {
279         case DLL_PROCESS_ATTACH:
280 
281             g_dwTlsErrIndex = TlsAlloc();
282 
283             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
284                 return FALSE;
285 
286             if(!init_urlcache())
287             {
288                 TlsFree(g_dwTlsErrIndex);
289                 return FALSE;
290             }
291 
292             WININET_hModule = hinstDLL;
293             break;
294 
295         case DLL_THREAD_ATTACH:
296             break;
297 
298         case DLL_THREAD_DETACH:
299             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
300             {
301                 heap_free(TlsGetValue(g_dwTlsErrIndex));
302             }
303             break;
304 
305         case DLL_PROCESS_DETACH:
306             if (lpvReserved) break;
307             collect_connections(COLLECT_CLEANUP);
308             NETCON_unload();
309             free_urlcache();
310             free_cookie();
311 
312             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
313             {
314                 heap_free(TlsGetValue(g_dwTlsErrIndex));
315                 TlsFree(g_dwTlsErrIndex);
316             }
317             break;
318     }
319     return TRUE;
320 }
321 
322 /***********************************************************************
323  *		DllInstall (WININET.@)
324  */
325 HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
326 {
327     FIXME("(%x %s): stub\n", bInstall, debugstr_w(cmdline));
328     return S_OK;
329 }
330 
331 /***********************************************************************
332  *           INTERNET_SaveProxySettings
333  *
334  * Stores the proxy settings given by lpwai into the registry
335  *
336  * RETURNS
337  *     ERROR_SUCCESS if no error, or error code on fail
338  */
339 static LONG INTERNET_SaveProxySettings( proxyinfo_t *lpwpi )
340 {
341     HKEY key;
342     LONG ret;
343 
344     if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
345         return ret;
346 
347     if ((ret = RegSetValueExW( key, szProxyEnable, 0, REG_DWORD, (BYTE*)&lpwpi->proxyEnabled, sizeof(DWORD))))
348     {
349         RegCloseKey( key );
350         return ret;
351     }
352 
353     if (lpwpi->proxy)
354     {
355         if ((ret = RegSetValueExW( key, szProxyServer, 0, REG_SZ, (BYTE*)lpwpi->proxy, sizeof(WCHAR) * (lstrlenW(lpwpi->proxy) + 1))))
356         {
357             RegCloseKey( key );
358             return ret;
359         }
360     }
361     else
362     {
363         if ((ret = RegDeleteValueW( key, szProxyServer )) && ret != ERROR_FILE_NOT_FOUND)
364         {
365             RegCloseKey( key );
366             return ret;
367         }
368     }
369 
370     RegCloseKey(key);
371     return ERROR_SUCCESS;
372 }
373 
374 /***********************************************************************
375  *           INTERNET_FindProxyForProtocol
376  *
377  * Searches the proxy string for a proxy of the given protocol.
378  * Returns the found proxy, or the default proxy if none of the given
379  * protocol is found.
380  *
381  * PARAMETERS
382  *     szProxy       [In]     proxy string to search
383  *     proto         [In]     protocol to search for, e.g. "http"
384  *     foundProxy    [Out]    found proxy
385  *     foundProxyLen [In/Out] length of foundProxy buffer, in WCHARs
386  *
387  * RETURNS
388  *     TRUE if a proxy is found, FALSE if not.  If foundProxy is too short,
389  *     *foundProxyLen is set to the required size in WCHARs, including the
390  *     NULL terminator, and the last error is set to ERROR_INSUFFICIENT_BUFFER.
391  */
392 WCHAR *INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto)
393 {
394     WCHAR *ret = NULL;
395     const WCHAR *ptr;
396 
397     TRACE("(%s, %s)\n", debugstr_w(szProxy), debugstr_w(proto));
398 
399     /* First, look for the specified protocol (proto=scheme://host:port) */
400     for (ptr = szProxy; ptr && *ptr; )
401     {
402         LPCWSTR end, equal;
403 
404         if (!(end = strchrW(ptr, ' ')))
405             end = ptr + strlenW(ptr);
406         if ((equal = strchrW(ptr, '=')) && equal < end &&
407              equal - ptr == strlenW(proto) &&
408              !strncmpiW(proto, ptr, strlenW(proto)))
409         {
410             ret = heap_strndupW(equal + 1, end - equal - 1);
411             TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
412             return ret;
413         }
414         if (*end == ' ')
415             ptr = end + 1;
416         else
417             ptr = end;
418     }
419 
420     /* It wasn't found: look for no protocol */
421     for (ptr = szProxy; ptr && *ptr; )
422     {
423         LPCWSTR end;
424 
425         if (!(end = strchrW(ptr, ' ')))
426             end = ptr + strlenW(ptr);
427         if (!strchrW(ptr, '='))
428         {
429             ret = heap_strndupW(ptr, end - ptr);
430             TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
431             return ret;
432         }
433         if (*end == ' ')
434             ptr = end + 1;
435         else
436             ptr = end;
437     }
438 
439     return NULL;
440 }
441 
442 /***********************************************************************
443  *           InternetInitializeAutoProxyDll   (WININET.@)
444  *
445  * Setup the internal proxy
446  *
447  * PARAMETERS
448  *     dwReserved
449  *
450  * RETURNS
451  *     FALSE on failure
452  *
453  */
454 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
455 {
456     FIXME("STUB\n");
457     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
458     return FALSE;
459 }
460 
461 /***********************************************************************
462  *           DetectAutoProxyUrl   (WININET.@)
463  *
464  * Auto detect the proxy url
465  *
466  * RETURNS
467  *     FALSE on failure
468  *
469  */
470 BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
471 	DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
472 {
473     FIXME("STUB\n");
474     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
475     return FALSE;
476 }
477 
478 static void FreeProxyInfo( proxyinfo_t *lpwpi )
479 {
480     heap_free(lpwpi->proxy);
481     heap_free(lpwpi->proxyBypass);
482     heap_free(lpwpi->proxyUsername);
483     heap_free(lpwpi->proxyPassword);
484 }
485 
486 static proxyinfo_t *global_proxy;
487 
488 static void free_global_proxy( void )
489 {
490     EnterCriticalSection( &WININET_cs );
491     if (global_proxy)
492     {
493         FreeProxyInfo( global_proxy );
494         heap_free( global_proxy );
495     }
496     LeaveCriticalSection( &WININET_cs );
497 }
498 
499 static BOOL parse_proxy_url( proxyinfo_t *info, const WCHAR *url )
500 {
501     static const WCHAR fmt[] = {'%','.','*','s',':','%','u',0};
502     URL_COMPONENTSW uc = {sizeof(uc)};
503 
504     uc.dwHostNameLength = 1;
505     uc.dwUserNameLength = 1;
506     uc.dwPasswordLength = 1;
507 
508     if (!InternetCrackUrlW( url, 0, 0, &uc )) return FALSE;
509     if (!uc.dwHostNameLength)
510     {
511         if (!(info->proxy = heap_strdupW( url ))) return FALSE;
512         info->proxyUsername = NULL;
513         info->proxyPassword = NULL;
514         return TRUE;
515     }
516     if (!(info->proxy = heap_alloc( (uc.dwHostNameLength + 12) * sizeof(WCHAR) ))) return FALSE;
517     sprintfW( info->proxy, fmt, uc.dwHostNameLength, uc.lpszHostName, uc.nPort );
518 
519     if (!uc.dwUserNameLength) info->proxyUsername = NULL;
520     else if (!(info->proxyUsername = heap_strndupW( uc.lpszUserName, uc.dwUserNameLength )))
521     {
522         heap_free( info->proxy );
523         return FALSE;
524     }
525     if (!uc.dwPasswordLength) info->proxyPassword = NULL;
526     else if (!(info->proxyPassword = heap_strndupW( uc.lpszPassword, uc.dwPasswordLength )))
527     {
528         heap_free( info->proxyUsername );
529         heap_free( info->proxy );
530         return FALSE;
531     }
532     return TRUE;
533 }
534 
535 /***********************************************************************
536  *          INTERNET_LoadProxySettings
537  *
538  * Loads proxy information from process-wide global settings, the registry,
539  * or the environment into lpwpi.
540  *
541  * The caller should call FreeProxyInfo when done with lpwpi.
542  *
543  * FIXME:
544  * The proxy may be specified in the form 'http=proxy.my.org'
545  * Presumably that means there can be ftp=ftpproxy.my.org too.
546  */
547 static LONG INTERNET_LoadProxySettings( proxyinfo_t *lpwpi )
548 {
549     HKEY key;
550     DWORD type, len;
551     LPCSTR envproxy;
552     LONG ret;
553 
554     memset( lpwpi, 0, sizeof(*lpwpi) );
555 
556     EnterCriticalSection( &WININET_cs );
557     if (global_proxy)
558     {
559         lpwpi->proxyEnabled = global_proxy->proxyEnabled;
560         lpwpi->proxy = heap_strdupW( global_proxy->proxy );
561         lpwpi->proxyBypass = heap_strdupW( global_proxy->proxyBypass );
562     }
563     LeaveCriticalSection( &WININET_cs );
564 
565     if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
566     {
567         FreeProxyInfo( lpwpi );
568         return ret;
569     }
570 
571     len = sizeof(DWORD);
572     if (RegQueryValueExW( key, szProxyEnable, NULL, &type, (BYTE *)&lpwpi->proxyEnabled, &len ) || type != REG_DWORD)
573     {
574         lpwpi->proxyEnabled = 0;
575         if((ret = RegSetValueExW( key, szProxyEnable, 0, REG_DWORD, (BYTE *)&lpwpi->proxyEnabled, sizeof(DWORD) )))
576         {
577             FreeProxyInfo( lpwpi );
578             RegCloseKey( key );
579             return ret;
580         }
581     }
582 
583     if (!(envproxy = getenv( "http_proxy" )) || lpwpi->proxyEnabled)
584     {
585         /* figure out how much memory the proxy setting takes */
586         if (!RegQueryValueExW( key, szProxyServer, NULL, &type, NULL, &len ) && len && (type == REG_SZ))
587         {
588             LPWSTR szProxy, p;
589             static const WCHAR szHttp[] = {'h','t','t','p','=',0};
590 
591             if (!(szProxy = heap_alloc(len)))
592             {
593                 RegCloseKey( key );
594                 FreeProxyInfo( lpwpi );
595                 return ERROR_OUTOFMEMORY;
596             }
597             RegQueryValueExW( key, szProxyServer, NULL, &type, (BYTE*)szProxy, &len );
598 
599             /* find the http proxy, and strip away everything else */
600             p = strstrW( szProxy, szHttp );
601             if (p)
602             {
603                 p += lstrlenW( szHttp );
604                 lstrcpyW( szProxy, p );
605             }
606             p = strchrW( szProxy, ';' );
607             if (p) *p = 0;
608 
609             FreeProxyInfo( lpwpi );
610             lpwpi->proxy = szProxy;
611             lpwpi->proxyBypass = NULL;
612 
613             TRACE("http proxy (from registry) = %s\n", debugstr_w(lpwpi->proxy));
614         }
615         else
616         {
617             TRACE("No proxy server settings in registry.\n");
618             FreeProxyInfo( lpwpi );
619             lpwpi->proxy = NULL;
620             lpwpi->proxyBypass = NULL;
621         }
622     }
623     else if (envproxy)
624     {
625         WCHAR *envproxyW;
626 
627         len = MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, NULL, 0 );
628         if (!(envproxyW = heap_alloc(len * sizeof(WCHAR))))
629         {
630             RegCloseKey( key );
631             return ERROR_OUTOFMEMORY;
632         }
633         MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, envproxyW, len );
634 
635         FreeProxyInfo( lpwpi );
636         if (parse_proxy_url( lpwpi, envproxyW ))
637         {
638             TRACE("http proxy (from environment) = %s\n", debugstr_w(lpwpi->proxy));
639             lpwpi->proxyEnabled = 1;
640             lpwpi->proxyBypass = NULL;
641         }
642         else
643         {
644             WARN("failed to parse http_proxy value %s\n", debugstr_w(envproxyW));
645             lpwpi->proxyEnabled = 0;
646             lpwpi->proxy = NULL;
647             lpwpi->proxyBypass = NULL;
648         }
649         heap_free( envproxyW );
650     }
651 
652     if (lpwpi->proxyEnabled)
653     {
654         TRACE("Proxy is enabled.\n");
655 
656         if (!(envproxy = getenv( "no_proxy" )))
657         {
658             /* figure out how much memory the proxy setting takes */
659             if (!RegQueryValueExW( key, szProxyOverride, NULL, &type, NULL, &len ) && len && (type == REG_SZ))
660             {
661                 LPWSTR szProxy;
662 
663                 if (!(szProxy = heap_alloc(len)))
664                 {
665                     RegCloseKey( key );
666                     return ERROR_OUTOFMEMORY;
667                 }
668                 RegQueryValueExW( key, szProxyOverride, NULL, &type, (BYTE*)szProxy, &len );
669 
670                 heap_free( lpwpi->proxyBypass );
671                 lpwpi->proxyBypass = szProxy;
672 
673                 TRACE("http proxy bypass (from registry) = %s\n", debugstr_w(lpwpi->proxyBypass));
674             }
675             else
676             {
677                 heap_free( lpwpi->proxyBypass );
678                 lpwpi->proxyBypass = NULL;
679 
680                 TRACE("No proxy bypass server settings in registry.\n");
681             }
682         }
683         else
684         {
685             WCHAR *envproxyW;
686 
687             len = MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, NULL, 0 );
688             if (!(envproxyW = heap_alloc(len * sizeof(WCHAR))))
689             {
690                 RegCloseKey( key );
691                 return ERROR_OUTOFMEMORY;
692             }
693             MultiByteToWideChar( CP_UNIXCP, 0, envproxy, -1, envproxyW, len );
694 
695             heap_free( lpwpi->proxyBypass );
696             lpwpi->proxyBypass = envproxyW;
697 
698             TRACE("http proxy bypass (from environment) = %s\n", debugstr_w(lpwpi->proxyBypass));
699         }
700     }
701     else TRACE("Proxy is disabled.\n");
702 
703     RegCloseKey( key );
704     return ERROR_SUCCESS;
705 }
706 
707 /***********************************************************************
708  *           INTERNET_ConfigureProxy
709  */
710 static BOOL INTERNET_ConfigureProxy( appinfo_t *lpwai )
711 {
712     proxyinfo_t wpi;
713 
714     if (INTERNET_LoadProxySettings( &wpi ))
715         return FALSE;
716 
717     if (wpi.proxyEnabled)
718     {
719         TRACE("http proxy = %s bypass = %s\n", debugstr_w(wpi.proxy), debugstr_w(wpi.proxyBypass));
720 
721         lpwai->accessType    = INTERNET_OPEN_TYPE_PROXY;
722         lpwai->proxy         = wpi.proxy;
723         lpwai->proxyBypass   = wpi.proxyBypass;
724         lpwai->proxyUsername = wpi.proxyUsername;
725         lpwai->proxyPassword = wpi.proxyPassword;
726         return TRUE;
727     }
728 
729     lpwai->accessType = INTERNET_OPEN_TYPE_DIRECT;
730     FreeProxyInfo(&wpi);
731     return FALSE;
732 }
733 
734 /***********************************************************************
735  *           dump_INTERNET_FLAGS
736  *
737  * Helper function to TRACE the internet flags.
738  *
739  * RETURNS
740  *    None
741  *
742  */
743 static void dump_INTERNET_FLAGS(DWORD dwFlags)
744 {
745 #define FE(x) { x, #x }
746     static const wininet_flag_info flag[] = {
747         FE(INTERNET_FLAG_RELOAD),
748         FE(INTERNET_FLAG_RAW_DATA),
749         FE(INTERNET_FLAG_EXISTING_CONNECT),
750         FE(INTERNET_FLAG_ASYNC),
751         FE(INTERNET_FLAG_PASSIVE),
752         FE(INTERNET_FLAG_NO_CACHE_WRITE),
753         FE(INTERNET_FLAG_MAKE_PERSISTENT),
754         FE(INTERNET_FLAG_FROM_CACHE),
755         FE(INTERNET_FLAG_SECURE),
756         FE(INTERNET_FLAG_KEEP_CONNECTION),
757         FE(INTERNET_FLAG_NO_AUTO_REDIRECT),
758         FE(INTERNET_FLAG_READ_PREFETCH),
759         FE(INTERNET_FLAG_NO_COOKIES),
760         FE(INTERNET_FLAG_NO_AUTH),
761         FE(INTERNET_FLAG_CACHE_IF_NET_FAIL),
762         FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP),
763         FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS),
764         FE(INTERNET_FLAG_IGNORE_CERT_DATE_INVALID),
765         FE(INTERNET_FLAG_IGNORE_CERT_CN_INVALID),
766         FE(INTERNET_FLAG_RESYNCHRONIZE),
767         FE(INTERNET_FLAG_HYPERLINK),
768         FE(INTERNET_FLAG_NO_UI),
769         FE(INTERNET_FLAG_PRAGMA_NOCACHE),
770         FE(INTERNET_FLAG_CACHE_ASYNC),
771         FE(INTERNET_FLAG_FORMS_SUBMIT),
772         FE(INTERNET_FLAG_NEED_FILE),
773         FE(INTERNET_FLAG_TRANSFER_ASCII),
774         FE(INTERNET_FLAG_TRANSFER_BINARY)
775     };
776 #undef FE
777     unsigned int i;
778 
779     for (i = 0; i < ARRAY_SIZE(flag); i++) {
780 	if (flag[i].val & dwFlags) {
781 	    TRACE(" %s", flag[i].name);
782 	    dwFlags &= ~flag[i].val;
783 	}
784     }
785     if (dwFlags)
786         TRACE(" Unknown flags (%08x)\n", dwFlags);
787     else
788         TRACE("\n");
789 }
790 
791 /***********************************************************************
792  *           INTERNET_CloseHandle (internal)
793  *
794  * Close internet handle
795  *
796  */
797 static VOID APPINFO_Destroy(object_header_t *hdr)
798 {
799     appinfo_t *lpwai = (appinfo_t*)hdr;
800 
801     TRACE("%p\n",lpwai);
802 
803     heap_free(lpwai->agent);
804     heap_free(lpwai->proxy);
805     heap_free(lpwai->proxyBypass);
806     heap_free(lpwai->proxyUsername);
807     heap_free(lpwai->proxyPassword);
808 }
809 
810 static DWORD APPINFO_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
811 {
812     appinfo_t *ai = (appinfo_t*)hdr;
813 
814     switch(option) {
815     case INTERNET_OPTION_HANDLE_TYPE:
816         TRACE("INTERNET_OPTION_HANDLE_TYPE\n");
817 
818         if (*size < sizeof(ULONG))
819             return ERROR_INSUFFICIENT_BUFFER;
820 
821         *size = sizeof(DWORD);
822         *(DWORD*)buffer = INTERNET_HANDLE_TYPE_INTERNET;
823         return ERROR_SUCCESS;
824 
825     case INTERNET_OPTION_USER_AGENT: {
826         DWORD bufsize;
827 
828         TRACE("INTERNET_OPTION_USER_AGENT\n");
829 
830         bufsize = *size;
831 
832         if (unicode) {
833             DWORD len = ai->agent ? strlenW(ai->agent) : 0;
834 
835             *size = (len + 1) * sizeof(WCHAR);
836             if(!buffer || bufsize < *size)
837                 return ERROR_INSUFFICIENT_BUFFER;
838 
839             if (ai->agent)
840                 strcpyW(buffer, ai->agent);
841             else
842                 *(WCHAR *)buffer = 0;
843             /* If the buffer is copied, the returned length doesn't include
844              * the NULL terminator.
845              */
846             *size = len;
847         }else {
848             if (ai->agent)
849                 *size = WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, NULL, 0, NULL, NULL);
850             else
851                 *size = 1;
852             if(!buffer || bufsize < *size)
853                 return ERROR_INSUFFICIENT_BUFFER;
854 
855             if (ai->agent)
856                 WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, buffer, *size, NULL, NULL);
857             else
858                 *(char *)buffer = 0;
859             /* If the buffer is copied, the returned length doesn't include
860              * the NULL terminator.
861              */
862             *size -= 1;
863         }
864 
865         return ERROR_SUCCESS;
866     }
867 
868     case INTERNET_OPTION_PROXY:
869         if(!size) return ERROR_INVALID_PARAMETER;
870         if (unicode) {
871             INTERNET_PROXY_INFOW *pi = (INTERNET_PROXY_INFOW *)buffer;
872             DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
873             LPWSTR proxy, proxy_bypass;
874 
875             if (ai->proxy)
876                 proxyBytesRequired = (lstrlenW(ai->proxy) + 1) * sizeof(WCHAR);
877             if (ai->proxyBypass)
878                 proxyBypassBytesRequired = (lstrlenW(ai->proxyBypass) + 1) * sizeof(WCHAR);
879             if (!pi || *size < sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired)
880             {
881                 *size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
882                 return ERROR_INSUFFICIENT_BUFFER;
883             }
884             proxy = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW));
885             proxy_bypass = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired);
886 
887             pi->dwAccessType = ai->accessType;
888             pi->lpszProxy = NULL;
889             pi->lpszProxyBypass = NULL;
890             if (ai->proxy) {
891                 lstrcpyW(proxy, ai->proxy);
892                 pi->lpszProxy = proxy;
893             }
894 
895             if (ai->proxyBypass) {
896                 lstrcpyW(proxy_bypass, ai->proxyBypass);
897                 pi->lpszProxyBypass = proxy_bypass;
898             }
899 
900             *size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
901             return ERROR_SUCCESS;
902         }else {
903             INTERNET_PROXY_INFOA *pi = (INTERNET_PROXY_INFOA *)buffer;
904             DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
905             LPSTR proxy, proxy_bypass;
906 
907             if (ai->proxy)
908                 proxyBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, NULL, 0, NULL, NULL);
909             if (ai->proxyBypass)
910                 proxyBypassBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1,
911                         NULL, 0, NULL, NULL);
912             if (!pi || *size < sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired)
913             {
914                 *size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
915                 return ERROR_INSUFFICIENT_BUFFER;
916             }
917             proxy = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA));
918             proxy_bypass = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired);
919 
920             pi->dwAccessType = ai->accessType;
921             pi->lpszProxy = NULL;
922             pi->lpszProxyBypass = NULL;
923             if (ai->proxy) {
924                 WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, proxy, proxyBytesRequired, NULL, NULL);
925                 pi->lpszProxy = proxy;
926             }
927 
928             if (ai->proxyBypass) {
929                 WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1, proxy_bypass,
930                         proxyBypassBytesRequired, NULL, NULL);
931                 pi->lpszProxyBypass = proxy_bypass;
932             }
933 
934             *size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
935             return ERROR_SUCCESS;
936         }
937 
938     case INTERNET_OPTION_CONNECT_TIMEOUT:
939         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
940 
941         if (*size < sizeof(ULONG))
942             return ERROR_INSUFFICIENT_BUFFER;
943 
944         *(ULONG*)buffer = ai->connect_timeout;
945         *size = sizeof(ULONG);
946 
947         return ERROR_SUCCESS;
948     }
949 
950     return INET_QueryOption(hdr, option, buffer, size, unicode);
951 }
952 
953 static DWORD APPINFO_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
954 {
955     appinfo_t *ai = (appinfo_t*)hdr;
956 
957     switch(option) {
958     case INTERNET_OPTION_CONNECT_TIMEOUT:
959         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
960 
961         if(size != sizeof(connect_timeout))
962             return ERROR_INTERNET_BAD_OPTION_LENGTH;
963         if(!*(ULONG*)buf)
964             return ERROR_BAD_ARGUMENTS;
965 
966         ai->connect_timeout = *(ULONG*)buf;
967         return ERROR_SUCCESS;
968     case INTERNET_OPTION_USER_AGENT:
969         heap_free(ai->agent);
970         if (!(ai->agent = heap_strdupW(buf))) return ERROR_OUTOFMEMORY;
971         return ERROR_SUCCESS;
972     case INTERNET_OPTION_REFRESH:
973         FIXME("INTERNET_OPTION_REFRESH\n");
974         return ERROR_SUCCESS;
975     }
976 
977     return INET_SetOption(hdr, option, buf, size);
978 }
979 
980 static const object_vtbl_t APPINFOVtbl = {
981     APPINFO_Destroy,
982     NULL,
983     APPINFO_QueryOption,
984     APPINFO_SetOption,
985     NULL,
986     NULL,
987     NULL
988 };
989 
990 
991 /***********************************************************************
992  *           InternetOpenW   (WININET.@)
993  *
994  * Per-application initialization of wininet
995  *
996  * RETURNS
997  *    HINTERNET on success
998  *    NULL on failure
999  *
1000  */
1001 HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
1002     LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
1003 {
1004     appinfo_t *lpwai = NULL;
1005 
1006 #ifdef __REACTOS__
1007     init_winsock();
1008 #endif
1009     if (TRACE_ON(wininet)) {
1010 #define FE(x) { x, #x }
1011 	static const wininet_flag_info access_type[] = {
1012 	    FE(INTERNET_OPEN_TYPE_PRECONFIG),
1013 	    FE(INTERNET_OPEN_TYPE_DIRECT),
1014 	    FE(INTERNET_OPEN_TYPE_PROXY),
1015 	    FE(INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
1016 	};
1017 #undef FE
1018 	DWORD i;
1019 	const char *access_type_str = "Unknown";
1020 
1021 	TRACE("(%s, %i, %s, %s, %i)\n", debugstr_w(lpszAgent), dwAccessType,
1022 	      debugstr_w(lpszProxy), debugstr_w(lpszProxyBypass), dwFlags);
1023         for (i = 0; i < ARRAY_SIZE(access_type); i++) {
1024 	    if (access_type[i].val == dwAccessType) {
1025 		access_type_str = access_type[i].name;
1026 		break;
1027 	    }
1028 	}
1029 	TRACE("  access type : %s\n", access_type_str);
1030 	TRACE("  flags       :");
1031 	dump_INTERNET_FLAGS(dwFlags);
1032     }
1033 
1034     /* Clear any error information */
1035     INTERNET_SetLastError(0);
1036 
1037     if((dwAccessType == INTERNET_OPEN_TYPE_PROXY) && !lpszProxy) {
1038         SetLastError(ERROR_INVALID_PARAMETER);
1039         return NULL;
1040     }
1041 
1042     lpwai = alloc_object(NULL, &APPINFOVtbl, sizeof(appinfo_t));
1043     if (!lpwai) {
1044         SetLastError(ERROR_OUTOFMEMORY);
1045         return NULL;
1046     }
1047 
1048     lpwai->hdr.htype = WH_HINIT;
1049     lpwai->hdr.dwFlags = dwFlags;
1050     lpwai->accessType = dwAccessType;
1051     lpwai->proxyUsername = NULL;
1052     lpwai->proxyPassword = NULL;
1053     lpwai->connect_timeout = connect_timeout;
1054 
1055     lpwai->agent = heap_strdupW(lpszAgent);
1056     if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
1057         INTERNET_ConfigureProxy( lpwai );
1058     else if(dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1059         lpwai->proxy = heap_strdupW(lpszProxy);
1060         lpwai->proxyBypass = heap_strdupW(lpszProxyBypass);
1061     }
1062 
1063     TRACE("returning %p\n", lpwai);
1064 
1065     return lpwai->hdr.hInternet;
1066 }
1067 
1068 
1069 /***********************************************************************
1070  *           InternetOpenA   (WININET.@)
1071  *
1072  * Per-application initialization of wininet
1073  *
1074  * RETURNS
1075  *    HINTERNET on success
1076  *    NULL on failure
1077  *
1078  */
1079 HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
1080     LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
1081 {
1082     WCHAR *szAgent, *szProxy, *szBypass;
1083     HINTERNET rc;
1084 
1085     TRACE("(%s, 0x%08x, %s, %s, 0x%08x)\n", debugstr_a(lpszAgent),
1086        dwAccessType, debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
1087 
1088     szAgent = heap_strdupAtoW(lpszAgent);
1089     szProxy = heap_strdupAtoW(lpszProxy);
1090     szBypass = heap_strdupAtoW(lpszProxyBypass);
1091 
1092     rc = InternetOpenW(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
1093 
1094     heap_free(szAgent);
1095     heap_free(szProxy);
1096     heap_free(szBypass);
1097     return rc;
1098 }
1099 
1100 /***********************************************************************
1101  *           InternetGetLastResponseInfoA (WININET.@)
1102  *
1103  * Return last wininet error description on the calling thread
1104  *
1105  * RETURNS
1106  *    TRUE on success of writing to buffer
1107  *    FALSE on failure
1108  *
1109  */
1110 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
1111     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
1112 {
1113     LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
1114 
1115     TRACE("\n");
1116 
1117     if (lpwite)
1118     {
1119         *lpdwError = lpwite->dwError;
1120         if (lpwite->dwError)
1121         {
1122             memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
1123             *lpdwBufferLength = strlen(lpszBuffer);
1124         }
1125         else
1126             *lpdwBufferLength = 0;
1127     }
1128     else
1129     {
1130         *lpdwError = 0;
1131         *lpdwBufferLength = 0;
1132     }
1133 
1134     return TRUE;
1135 }
1136 
1137 /***********************************************************************
1138  *           InternetGetLastResponseInfoW (WININET.@)
1139  *
1140  * Return last wininet error description on the calling thread
1141  *
1142  * RETURNS
1143  *    TRUE on success of writing to buffer
1144  *    FALSE on failure
1145  *
1146  */
1147 BOOL WINAPI InternetGetLastResponseInfoW(LPDWORD lpdwError,
1148     LPWSTR lpszBuffer, LPDWORD lpdwBufferLength)
1149 {
1150     LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
1151 
1152     TRACE("\n");
1153 
1154     if (lpwite)
1155     {
1156         *lpdwError = lpwite->dwError;
1157         if (lpwite->dwError)
1158         {
1159             memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
1160             *lpdwBufferLength = lstrlenW(lpszBuffer);
1161         }
1162         else
1163             *lpdwBufferLength = 0;
1164     }
1165     else
1166     {
1167         *lpdwError = 0;
1168         *lpdwBufferLength = 0;
1169     }
1170 
1171     return TRUE;
1172 }
1173 
1174 /***********************************************************************
1175  *           InternetGetConnectedState (WININET.@)
1176  *
1177  * Return connected state
1178  *
1179  * RETURNS
1180  *    TRUE if connected
1181  *    if lpdwStatus is not null, return the status (off line,
1182  *    modem, lan...) in it.
1183  *    FALSE if not connected
1184  */
1185 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
1186 {
1187     TRACE("(%p, 0x%08x)\n", lpdwStatus, dwReserved);
1188 
1189     return InternetGetConnectedStateExW(lpdwStatus, NULL, 0, dwReserved);
1190 }
1191 
1192 
1193 /***********************************************************************
1194  *           InternetGetConnectedStateExW (WININET.@)
1195  *
1196  * Return connected state
1197  *
1198  * PARAMS
1199  *
1200  * lpdwStatus         [O] Flags specifying the status of the internet connection.
1201  * lpszConnectionName [O] Pointer to buffer to receive the friendly name of the internet connection.
1202  * dwNameLen          [I] Size of the buffer, in characters.
1203  * dwReserved         [I] Reserved. Must be set to 0.
1204  *
1205  * RETURNS
1206  *    TRUE if connected
1207  *    if lpdwStatus is not null, return the status (off line,
1208  *    modem, lan...) in it.
1209  *    FALSE if not connected
1210  *
1211  * NOTES
1212  *   If the system has no available network connections, an empty string is
1213  *   stored in lpszConnectionName. If there is a LAN connection, a localized
1214  *   "LAN Connection" string is stored. Presumably, if only a dial-up
1215  *   connection is available then the name of the dial-up connection is
1216  *   returned. Why any application, other than the "Internet Settings" CPL,
1217  *   would want to use this function instead of the simpler InternetGetConnectedStateW
1218  *   function is beyond me.
1219  */
1220 BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
1221                                          DWORD dwNameLen, DWORD dwReserved)
1222 {
1223     TRACE("(%p, %p, %d, 0x%08x)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
1224 
1225     /* Must be zero */
1226     if(dwReserved)
1227         return FALSE;
1228 
1229     if (lpdwStatus) {
1230         WARN("always returning LAN connection.\n");
1231         *lpdwStatus = INTERNET_CONNECTION_LAN;
1232     }
1233 
1234     /* When the buffer size is zero LoadStringW fills the buffer with a pointer to
1235      * the resource, avoid it as we must not change the buffer in this case */
1236     if(lpszConnectionName && dwNameLen) {
1237         *lpszConnectionName = '\0';
1238         LoadStringW(WININET_hModule, IDS_LANCONNECTION, lpszConnectionName, dwNameLen);
1239     }
1240 
1241     return TRUE;
1242 }
1243 
1244 
1245 /***********************************************************************
1246  *           InternetGetConnectedStateExA (WININET.@)
1247  */
1248 BOOL WINAPI InternetGetConnectedStateExA(LPDWORD lpdwStatus, LPSTR lpszConnectionName,
1249                                          DWORD dwNameLen, DWORD dwReserved)
1250 {
1251     LPWSTR lpwszConnectionName = NULL;
1252     BOOL rc;
1253 
1254     TRACE("(%p, %p, %d, 0x%08x)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
1255 
1256     if (lpszConnectionName && dwNameLen > 0)
1257         lpwszConnectionName = heap_alloc(dwNameLen * sizeof(WCHAR));
1258 
1259     rc = InternetGetConnectedStateExW(lpdwStatus,lpwszConnectionName, dwNameLen,
1260                                       dwReserved);
1261     if (rc && lpwszConnectionName)
1262         WideCharToMultiByte(CP_ACP,0,lpwszConnectionName,-1,lpszConnectionName,
1263                             dwNameLen, NULL, NULL);
1264 
1265     heap_free(lpwszConnectionName);
1266     return rc;
1267 }
1268 
1269 
1270 /***********************************************************************
1271  *           InternetConnectW (WININET.@)
1272  *
1273  * Open a ftp, gopher or http session
1274  *
1275  * RETURNS
1276  *    HINTERNET a session handle on success
1277  *    NULL on failure
1278  *
1279  */
1280 HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
1281     LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
1282     LPCWSTR lpszUserName, LPCWSTR lpszPassword,
1283     DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
1284 {
1285     appinfo_t *hIC;
1286     HINTERNET rc = NULL;
1287     DWORD res = ERROR_SUCCESS;
1288 
1289     TRACE("(%p, %s, %u, %s, %p, %u, %x, %lx)\n", hInternet, debugstr_w(lpszServerName),
1290           nServerPort, debugstr_w(lpszUserName), lpszPassword, dwService, dwFlags, dwContext);
1291 
1292     if (!lpszServerName)
1293     {
1294         SetLastError(ERROR_INVALID_PARAMETER);
1295         return NULL;
1296     }
1297 
1298     hIC = (appinfo_t*)get_handle_object( hInternet );
1299     if ( (hIC == NULL) || (hIC->hdr.htype != WH_HINIT) )
1300     {
1301         res = ERROR_INVALID_HANDLE;
1302         goto lend;
1303     }
1304 
1305     switch (dwService)
1306     {
1307         case INTERNET_SERVICE_FTP:
1308             rc = FTP_Connect(hIC, lpszServerName, nServerPort,
1309             lpszUserName, lpszPassword, dwFlags, dwContext, 0);
1310             if(!rc)
1311                 res = INTERNET_GetLastError();
1312             break;
1313 
1314         case INTERNET_SERVICE_HTTP:
1315 	    res = HTTP_Connect(hIC, lpszServerName, nServerPort,
1316                     lpszUserName, lpszPassword, dwFlags, dwContext, 0, &rc);
1317             break;
1318 
1319         case INTERNET_SERVICE_GOPHER:
1320         default:
1321             break;
1322     }
1323 lend:
1324     if( hIC )
1325         WININET_Release( &hIC->hdr );
1326 
1327     TRACE("returning %p\n", rc);
1328     SetLastError(res);
1329     return rc;
1330 }
1331 
1332 
1333 /***********************************************************************
1334  *           InternetConnectA (WININET.@)
1335  *
1336  * Open a ftp, gopher or http session
1337  *
1338  * RETURNS
1339  *    HINTERNET a session handle on success
1340  *    NULL on failure
1341  *
1342  */
1343 HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
1344     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
1345     LPCSTR lpszUserName, LPCSTR lpszPassword,
1346     DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
1347 {
1348     HINTERNET rc = NULL;
1349     LPWSTR szServerName;
1350     LPWSTR szUserName;
1351     LPWSTR szPassword;
1352 
1353     szServerName = heap_strdupAtoW(lpszServerName);
1354     szUserName = heap_strdupAtoW(lpszUserName);
1355     szPassword = heap_strdupAtoW(lpszPassword);
1356 
1357     rc = InternetConnectW(hInternet, szServerName, nServerPort,
1358         szUserName, szPassword, dwService, dwFlags, dwContext);
1359 
1360     heap_free(szServerName);
1361     heap_free(szUserName);
1362     heap_free(szPassword);
1363     return rc;
1364 }
1365 
1366 
1367 /***********************************************************************
1368  *           InternetFindNextFileA (WININET.@)
1369  *
1370  * Continues a file search from a previous call to FindFirstFile
1371  *
1372  * RETURNS
1373  *    TRUE on success
1374  *    FALSE on failure
1375  *
1376  */
1377 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
1378 {
1379     BOOL ret;
1380     WIN32_FIND_DATAW fd;
1381 
1382     ret = InternetFindNextFileW(hFind, lpvFindData?&fd:NULL);
1383     if(lpvFindData)
1384         WININET_find_data_WtoA(&fd, (LPWIN32_FIND_DATAA)lpvFindData);
1385     return ret;
1386 }
1387 
1388 /***********************************************************************
1389  *           InternetFindNextFileW (WININET.@)
1390  *
1391  * Continues a file search from a previous call to FindFirstFile
1392  *
1393  * RETURNS
1394  *    TRUE on success
1395  *    FALSE on failure
1396  *
1397  */
1398 BOOL WINAPI InternetFindNextFileW(HINTERNET hFind, LPVOID lpvFindData)
1399 {
1400     object_header_t *hdr;
1401     DWORD res;
1402 
1403     TRACE("\n");
1404 
1405     hdr = get_handle_object(hFind);
1406     if(!hdr) {
1407         WARN("Invalid handle\n");
1408         SetLastError(ERROR_INVALID_HANDLE);
1409         return FALSE;
1410     }
1411 
1412     if(hdr->vtbl->FindNextFileW) {
1413         res = hdr->vtbl->FindNextFileW(hdr, lpvFindData);
1414     }else {
1415         WARN("Handle doesn't support NextFile\n");
1416         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
1417     }
1418 
1419     WININET_Release(hdr);
1420 
1421     if(res != ERROR_SUCCESS)
1422         SetLastError(res);
1423     return res == ERROR_SUCCESS;
1424 }
1425 
1426 /***********************************************************************
1427  *           InternetCloseHandle (WININET.@)
1428  *
1429  * Generic close handle function
1430  *
1431  * RETURNS
1432  *    TRUE on success
1433  *    FALSE on failure
1434  *
1435  */
1436 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
1437 {
1438     object_header_t *obj;
1439 
1440     TRACE("%p\n", hInternet);
1441 
1442     obj = get_handle_object( hInternet );
1443     if (!obj) {
1444         SetLastError(ERROR_INVALID_HANDLE);
1445         return FALSE;
1446     }
1447 
1448     invalidate_handle(obj);
1449     WININET_Release(obj);
1450 
1451     return TRUE;
1452 }
1453 
1454 static BOOL set_url_component(WCHAR **component, DWORD *component_length, const WCHAR *value, DWORD len)
1455 {
1456     TRACE("%s (%d)\n", debugstr_wn(value, len), len);
1457 
1458     if (!*component_length)
1459         return TRUE;
1460 
1461     if (!*component) {
1462         *(const WCHAR**)component = value;
1463         *component_length = len;
1464         return TRUE;
1465     }
1466 
1467     if (*component_length < len+1) {
1468         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1469         return FALSE;
1470     }
1471 
1472     *component_length = len;
1473     if(len)
1474         memcpy(*component, value, len*sizeof(WCHAR));
1475     (*component)[len] = 0;
1476     return TRUE;
1477 }
1478 
1479 static BOOL set_url_component_WtoA(const WCHAR *comp_w, DWORD length, const WCHAR *url_w, char **comp, DWORD *ret_length,
1480                                    const char *url_a)
1481 {
1482     size_t size, ret_size = *ret_length;
1483 
1484     if (!*ret_length)
1485         return TRUE;
1486     size = WideCharToMultiByte(CP_ACP, 0, comp_w, length, NULL, 0, NULL, NULL);
1487 
1488     if (!*comp) {
1489         *comp = comp_w ? (char*)url_a + WideCharToMultiByte(CP_ACP, 0, url_w, comp_w-url_w, NULL, 0, NULL, NULL) : NULL;
1490         *ret_length = size;
1491         return TRUE;
1492     }
1493 
1494     if (size+1 > ret_size) {
1495         SetLastError(ERROR_INSUFFICIENT_BUFFER);
1496         *ret_length = size+1;
1497         return FALSE;
1498     }
1499 
1500     *ret_length = size;
1501     WideCharToMultiByte(CP_ACP, 0, comp_w, length, *comp, ret_size-1, NULL, NULL);
1502     (*comp)[size] = 0;
1503     return TRUE;
1504 }
1505 
1506 static BOOL set_url_component_AtoW(const char *comp_a, DWORD len_a, WCHAR **comp_w, DWORD *len_w, WCHAR **buf)
1507 {
1508     *len_w = len_a;
1509 
1510     if(!comp_a) {
1511         *comp_w = NULL;
1512         return TRUE;
1513     }
1514 
1515     if(!(*comp_w = *buf = heap_alloc(len_a*sizeof(WCHAR)))) {
1516         SetLastError(ERROR_OUTOFMEMORY);
1517         return FALSE;
1518     }
1519 
1520     return TRUE;
1521 }
1522 
1523 /***********************************************************************
1524  *           InternetCrackUrlA (WININET.@)
1525  *
1526  * See InternetCrackUrlW.
1527  */
1528 BOOL WINAPI InternetCrackUrlA(const char *url, DWORD url_length, DWORD flags, URL_COMPONENTSA *ret_comp)
1529 {
1530     WCHAR *host = NULL, *user = NULL, *pass = NULL, *path = NULL, *scheme = NULL, *extra = NULL;
1531     URL_COMPONENTSW comp;
1532     WCHAR *url_w = NULL;
1533     BOOL ret;
1534 
1535     TRACE("(%s %u %x %p)\n", url_length ? debugstr_an(url, url_length) : debugstr_a(url), url_length, flags, ret_comp);
1536 
1537     if (!url || !*url || !ret_comp || ret_comp->dwStructSize != sizeof(URL_COMPONENTSA)) {
1538         SetLastError(ERROR_INVALID_PARAMETER);
1539         return FALSE;
1540     }
1541 
1542     comp.dwStructSize = sizeof(comp);
1543 
1544     ret = set_url_component_AtoW(ret_comp->lpszHostName, ret_comp->dwHostNameLength,
1545                                  &comp.lpszHostName, &comp.dwHostNameLength, &host)
1546         && set_url_component_AtoW(ret_comp->lpszUserName, ret_comp->dwUserNameLength,
1547                                   &comp.lpszUserName, &comp.dwUserNameLength, &user)
1548         && set_url_component_AtoW(ret_comp->lpszPassword, ret_comp->dwPasswordLength,
1549                                   &comp.lpszPassword, &comp.dwPasswordLength, &pass)
1550         && set_url_component_AtoW(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength,
1551                                   &comp.lpszUrlPath, &comp.dwUrlPathLength, &path)
1552         && set_url_component_AtoW(ret_comp->lpszScheme, ret_comp->dwSchemeLength,
1553                                   &comp.lpszScheme, &comp.dwSchemeLength, &scheme)
1554         && set_url_component_AtoW(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength,
1555                                   &comp.lpszExtraInfo, &comp.dwExtraInfoLength, &extra);
1556 
1557     if(ret && !(url_w = heap_strndupAtoW(url, url_length ? url_length : -1, &url_length))) {
1558         SetLastError(ERROR_OUTOFMEMORY);
1559         ret = FALSE;
1560     }
1561 
1562     if (ret && (ret = InternetCrackUrlW(url_w, url_length, flags, &comp))) {
1563         ret_comp->nScheme = comp.nScheme;
1564         ret_comp->nPort = comp.nPort;
1565 
1566         ret = set_url_component_WtoA(comp.lpszHostName, comp.dwHostNameLength, url_w,
1567                                      &ret_comp->lpszHostName, &ret_comp->dwHostNameLength, url)
1568             && set_url_component_WtoA(comp.lpszUserName, comp.dwUserNameLength, url_w,
1569                                       &ret_comp->lpszUserName, &ret_comp->dwUserNameLength, url)
1570             && set_url_component_WtoA(comp.lpszPassword, comp.dwPasswordLength, url_w,
1571                                       &ret_comp->lpszPassword, &ret_comp->dwPasswordLength, url)
1572             && set_url_component_WtoA(comp.lpszUrlPath, comp.dwUrlPathLength, url_w,
1573                                       &ret_comp->lpszUrlPath, &ret_comp->dwUrlPathLength, url)
1574             && set_url_component_WtoA(comp.lpszScheme, comp.dwSchemeLength, url_w,
1575                                       &ret_comp->lpszScheme, &ret_comp->dwSchemeLength, url)
1576             && set_url_component_WtoA(comp.lpszExtraInfo, comp.dwExtraInfoLength, url_w,
1577                                       &ret_comp->lpszExtraInfo, &ret_comp->dwExtraInfoLength, url);
1578 
1579         if(ret)
1580             TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_a(url),
1581                   debugstr_an(ret_comp->lpszScheme, ret_comp->dwSchemeLength),
1582                   debugstr_an(ret_comp->lpszHostName, ret_comp->dwHostNameLength),
1583                   debugstr_an(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength),
1584                   debugstr_an(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength));
1585     }
1586 
1587     heap_free(host);
1588     heap_free(user);
1589     heap_free(pass);
1590     heap_free(path);
1591     heap_free(scheme);
1592     heap_free(extra);
1593     heap_free(url_w);
1594     return ret;
1595 }
1596 
1597 static const WCHAR url_schemes[][7] =
1598 {
1599     {'f','t','p',0},
1600     {'g','o','p','h','e','r',0},
1601     {'h','t','t','p',0},
1602     {'h','t','t','p','s',0},
1603     {'f','i','l','e',0},
1604     {'n','e','w','s',0},
1605     {'m','a','i','l','t','o',0},
1606     {'r','e','s',0},
1607 };
1608 
1609 /***********************************************************************
1610  *           GetInternetSchemeW (internal)
1611  *
1612  * Get scheme of url
1613  *
1614  * RETURNS
1615  *    scheme on success
1616  *    INTERNET_SCHEME_UNKNOWN on failure
1617  *
1618  */
1619 static INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, DWORD nMaxCmp)
1620 {
1621     int i;
1622 
1623     TRACE("%s %d\n",debugstr_wn(lpszScheme, nMaxCmp), nMaxCmp);
1624 
1625     if(lpszScheme==NULL)
1626         return INTERNET_SCHEME_UNKNOWN;
1627 
1628     for (i = 0; i < ARRAY_SIZE(url_schemes); i++)
1629         if (!strncmpiW(lpszScheme, url_schemes[i], nMaxCmp))
1630             return INTERNET_SCHEME_FIRST + i;
1631 
1632     return INTERNET_SCHEME_UNKNOWN;
1633 }
1634 
1635 /***********************************************************************
1636  *           InternetCrackUrlW   (WININET.@)
1637  *
1638  * Break up URL into its components
1639  *
1640  * RETURNS
1641  *    TRUE on success
1642  *    FALSE on failure
1643  */
1644 BOOL WINAPI InternetCrackUrlW(const WCHAR *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSW *lpUC)
1645 {
1646   /*
1647    * RFC 1808
1648    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1649    *
1650    */
1651     LPCWSTR lpszParam    = NULL;
1652     BOOL  found_colon = FALSE;
1653     LPCWSTR lpszap;
1654     LPCWSTR lpszcp = NULL, lpszNetLoc;
1655 
1656     TRACE("(%s %u %x %p)\n",
1657           lpszUrl ? debugstr_wn(lpszUrl, dwUrlLength ? dwUrlLength : strlenW(lpszUrl)) : "(null)",
1658           dwUrlLength, dwFlags, lpUC);
1659 
1660     if (!lpszUrl || !*lpszUrl || !lpUC)
1661     {
1662         SetLastError(ERROR_INVALID_PARAMETER);
1663         return FALSE;
1664     }
1665     if (!dwUrlLength) dwUrlLength = strlenW(lpszUrl);
1666 
1667     if (dwFlags & ICU_DECODE)
1668     {
1669         WCHAR *url_tmp, *buffer;
1670         DWORD len = dwUrlLength + 1;
1671         BOOL ret;
1672 
1673         if (!(url_tmp = heap_strndupW(lpszUrl, dwUrlLength)))
1674         {
1675             SetLastError(ERROR_OUTOFMEMORY);
1676             return FALSE;
1677         }
1678 
1679         buffer = url_tmp;
1680         ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
1681         if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1682         {
1683             buffer = heap_alloc(len * sizeof(WCHAR));
1684             if (!buffer)
1685             {
1686                 SetLastError(ERROR_OUTOFMEMORY);
1687                 heap_free(url_tmp);
1688                 return FALSE;
1689             }
1690             ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
1691         }
1692         if (ret)
1693             ret = InternetCrackUrlW(buffer, len, dwFlags & ~ICU_DECODE, lpUC);
1694 
1695         if (buffer != url_tmp) heap_free(buffer);
1696         heap_free(url_tmp);
1697         return ret;
1698     }
1699     lpszap = lpszUrl;
1700 
1701     /* Determine if the URI is absolute. */
1702     while (lpszap - lpszUrl < dwUrlLength)
1703     {
1704         if (isalnumW(*lpszap) || *lpszap == '+' || *lpszap == '.' || *lpszap == '-')
1705         {
1706             lpszap++;
1707             continue;
1708         }
1709         if (*lpszap == ':')
1710         {
1711             found_colon = TRUE;
1712             lpszcp = lpszap;
1713         }
1714         else
1715         {
1716             lpszcp = lpszUrl; /* Relative url */
1717         }
1718 
1719         break;
1720     }
1721 
1722     if(!found_colon){
1723         SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
1724         return FALSE;
1725     }
1726 
1727     lpUC->nScheme = INTERNET_SCHEME_UNKNOWN;
1728     lpUC->nPort = INTERNET_INVALID_PORT_NUMBER;
1729 
1730     /* Parse <params> */
1731     lpszParam = memchrW(lpszap, '?', dwUrlLength - (lpszap - lpszUrl));
1732     if(!lpszParam)
1733         lpszParam = memchrW(lpszap, '#', dwUrlLength - (lpszap - lpszUrl));
1734 
1735     if(!set_url_component(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
1736                           lpszParam, lpszParam ? dwUrlLength-(lpszParam-lpszUrl) : 0))
1737         return FALSE;
1738 
1739 
1740     /* Get scheme first. */
1741     lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1742     if(!set_url_component(&lpUC->lpszScheme, &lpUC->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
1743         return FALSE;
1744 
1745     /* Eat ':' in protocol. */
1746     lpszcp++;
1747 
1748     /* double slash indicates the net_loc portion is present */
1749     if ((lpszcp[0] == '/') && (lpszcp[1] == '/'))
1750     {
1751         lpszcp += 2;
1752 
1753         lpszNetLoc = memchrW(lpszcp, '/', dwUrlLength - (lpszcp - lpszUrl));
1754         if (lpszParam)
1755         {
1756             if (lpszNetLoc)
1757                 lpszNetLoc = min(lpszNetLoc, lpszParam);
1758             else
1759                 lpszNetLoc = lpszParam;
1760         }
1761         else if (!lpszNetLoc)
1762             lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1763 
1764         /* Parse net-loc */
1765         if (lpszNetLoc)
1766         {
1767             LPCWSTR lpszHost;
1768             LPCWSTR lpszPort;
1769 
1770             /* [<user>[<:password>]@]<host>[:<port>] */
1771             /* First find the user and password if they exist */
1772 
1773             lpszHost = memchrW(lpszcp, '@', dwUrlLength - (lpszcp - lpszUrl));
1774             if (lpszHost == NULL || lpszHost > lpszNetLoc)
1775             {
1776                 /* username and password not specified. */
1777                 set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1778                 set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1779             }
1780             else /* Parse out username and password */
1781             {
1782                 LPCWSTR lpszUser = lpszcp;
1783                 LPCWSTR lpszPasswd = lpszHost;
1784 
1785                 while (lpszcp < lpszHost)
1786                 {
1787                     if (*lpszcp == ':')
1788                         lpszPasswd = lpszcp;
1789 
1790                     lpszcp++;
1791                 }
1792 
1793                 if(!set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, lpszUser, lpszPasswd - lpszUser))
1794                     return FALSE;
1795 
1796                 if (lpszPasswd != lpszHost)
1797                     lpszPasswd++;
1798                 if(!set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
1799                                       lpszPasswd == lpszHost ? NULL : lpszPasswd, lpszHost - lpszPasswd))
1800                     return FALSE;
1801 
1802                 lpszcp++; /* Advance to beginning of host */
1803             }
1804 
1805             /* Parse <host><:port> */
1806 
1807             lpszHost = lpszcp;
1808             lpszPort = lpszNetLoc;
1809 
1810             /* special case for res:// URLs: there is no port here, so the host is the
1811                entire string up to the first '/' */
1812             if(lpUC->nScheme==INTERNET_SCHEME_RES)
1813             {
1814                 if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
1815                     return FALSE;
1816                 lpszcp=lpszNetLoc;
1817             }
1818             else
1819             {
1820                 while (lpszcp < lpszNetLoc)
1821                 {
1822                     if (*lpszcp == ':')
1823                         lpszPort = lpszcp;
1824 
1825                     lpszcp++;
1826                 }
1827 
1828                 /* If the scheme is "file" and the host is just one letter, it's not a host */
1829                 if(lpUC->nScheme==INTERNET_SCHEME_FILE && lpszPort <= lpszHost+1)
1830                 {
1831                     lpszcp=lpszHost;
1832                     set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1833                 }
1834                 else
1835                 {
1836                     if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
1837                         return FALSE;
1838                     if (lpszPort != lpszNetLoc)
1839                         lpUC->nPort = atoiW(++lpszPort);
1840                     else switch (lpUC->nScheme)
1841                     {
1842                     case INTERNET_SCHEME_HTTP:
1843                         lpUC->nPort = INTERNET_DEFAULT_HTTP_PORT;
1844                         break;
1845                     case INTERNET_SCHEME_HTTPS:
1846                         lpUC->nPort = INTERNET_DEFAULT_HTTPS_PORT;
1847                         break;
1848                     case INTERNET_SCHEME_FTP:
1849                         lpUC->nPort = INTERNET_DEFAULT_FTP_PORT;
1850                         break;
1851                     case INTERNET_SCHEME_GOPHER:
1852                         lpUC->nPort = INTERNET_DEFAULT_GOPHER_PORT;
1853                         break;
1854                     default:
1855                         break;
1856                     }
1857                 }
1858             }
1859         }
1860     }
1861     else
1862     {
1863         set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1864         set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1865         set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1866     }
1867 
1868     /* Here lpszcp points to:
1869      *
1870      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1871      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1872      */
1873     if (lpszcp != 0 && lpszcp - lpszUrl < dwUrlLength && (!lpszParam || lpszcp <= lpszParam))
1874     {
1875         DWORD len;
1876 
1877         /* Only truncate the parameter list if it's already been saved
1878          * in lpUC->lpszExtraInfo.
1879          */
1880         if (lpszParam && lpUC->dwExtraInfoLength && lpUC->lpszExtraInfo)
1881             len = lpszParam - lpszcp;
1882         else
1883         {
1884             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
1885              * newlines if necessary.
1886              */
1887             LPWSTR lpsznewline = memchrW(lpszcp, '\n', dwUrlLength - (lpszcp - lpszUrl));
1888             if (lpsznewline != NULL)
1889                 len = lpsznewline - lpszcp;
1890             else
1891                 len = dwUrlLength-(lpszcp-lpszUrl);
1892         }
1893         if (lpUC->dwUrlPathLength && lpUC->lpszUrlPath &&
1894                 lpUC->nScheme == INTERNET_SCHEME_FILE)
1895         {
1896             WCHAR tmppath[MAX_PATH];
1897             if (*lpszcp == '/')
1898             {
1899                 len = MAX_PATH;
1900                 PathCreateFromUrlW(lpszUrl, tmppath, &len, 0);
1901             }
1902             else
1903             {
1904                 WCHAR *iter;
1905                 memcpy(tmppath, lpszcp, len * sizeof(WCHAR));
1906                 tmppath[len] = '\0';
1907 
1908                 iter = tmppath;
1909                 while (*iter) {
1910                     if (*iter == '/')
1911                         *iter = '\\';
1912                     ++iter;
1913                 }
1914             }
1915             /* if ends in \. or \.. append a backslash */
1916             if (tmppath[len - 1] == '.' &&
1917                     (tmppath[len - 2] == '\\' ||
1918                      (tmppath[len - 2] == '.' && tmppath[len - 3] == '\\')))
1919             {
1920                 if (len < MAX_PATH - 1)
1921                 {
1922                     tmppath[len] = '\\';
1923                     tmppath[len+1] = '\0';
1924                     ++len;
1925                 }
1926             }
1927             if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, tmppath, len))
1928                 return FALSE;
1929         }
1930         else if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, len))
1931             return FALSE;
1932     }
1933     else
1934     {
1935         set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, 0);
1936     }
1937 
1938     TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
1939              debugstr_wn(lpUC->lpszScheme,lpUC->dwSchemeLength),
1940              debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
1941              debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
1942              debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1943 
1944     return TRUE;
1945 }
1946 
1947 /***********************************************************************
1948  *           InternetAttemptConnect (WININET.@)
1949  *
1950  * Attempt to make a connection to the internet
1951  *
1952  * RETURNS
1953  *    ERROR_SUCCESS on success
1954  *    Error value   on failure
1955  *
1956  */
1957 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
1958 {
1959     FIXME("Stub\n");
1960     return ERROR_SUCCESS;
1961 }
1962 
1963 
1964 /***********************************************************************
1965  *           convert_url_canonicalization_flags
1966  *
1967  * Helper for InternetCanonicalizeUrl
1968  *
1969  * PARAMS
1970  *     dwFlags [I] Flags suitable for InternetCanonicalizeUrl
1971  *
1972  * RETURNS
1973  *     Flags suitable for UrlCanonicalize
1974  */
1975 static DWORD convert_url_canonicalization_flags(DWORD dwFlags)
1976 {
1977     DWORD dwUrlFlags = URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE;
1978 
1979     if (dwFlags & ICU_BROWSER_MODE)        dwUrlFlags |= URL_BROWSER_MODE;
1980     if (dwFlags & ICU_DECODE)              dwUrlFlags |= URL_UNESCAPE;
1981     if (dwFlags & ICU_ENCODE_PERCENT)      dwUrlFlags |= URL_ESCAPE_PERCENT;
1982     if (dwFlags & ICU_ENCODE_SPACES_ONLY)  dwUrlFlags |= URL_ESCAPE_SPACES_ONLY;
1983     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1984     if (dwFlags & ICU_NO_ENCODE)           dwUrlFlags ^= URL_ESCAPE_UNSAFE;
1985     if (dwFlags & ICU_NO_META)             dwUrlFlags |= URL_NO_META;
1986 
1987     return dwUrlFlags;
1988 }
1989 
1990 /***********************************************************************
1991  *           InternetCanonicalizeUrlA (WININET.@)
1992  *
1993  * Escape unsafe characters and spaces
1994  *
1995  * RETURNS
1996  *    TRUE on success
1997  *    FALSE on failure
1998  *
1999  */
2000 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
2001 	LPDWORD lpdwBufferLength, DWORD dwFlags)
2002 {
2003     HRESULT hr;
2004 
2005     TRACE("(%s, %p, %p, 0x%08x) buffer length: %d\n", debugstr_a(lpszUrl), lpszBuffer,
2006         lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
2007 
2008     dwFlags = convert_url_canonicalization_flags(dwFlags);
2009     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
2010     if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
2011     if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
2012 
2013     return hr == S_OK;
2014 }
2015 
2016 /***********************************************************************
2017  *           InternetCanonicalizeUrlW (WININET.@)
2018  *
2019  * Escape unsafe characters and spaces
2020  *
2021  * RETURNS
2022  *    TRUE on success
2023  *    FALSE on failure
2024  *
2025  */
2026 BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
2027     LPDWORD lpdwBufferLength, DWORD dwFlags)
2028 {
2029     HRESULT hr;
2030 
2031     TRACE("(%s, %p, %p, 0x%08x) buffer length: %d\n", debugstr_w(lpszUrl), lpszBuffer,
2032           lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
2033 
2034     dwFlags = convert_url_canonicalization_flags(dwFlags);
2035     hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
2036     if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
2037     if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
2038 
2039     return hr == S_OK;
2040 }
2041 
2042 /* #################################################### */
2043 
2044 static INTERNET_STATUS_CALLBACK set_status_callback(
2045     object_header_t *lpwh, INTERNET_STATUS_CALLBACK callback, BOOL unicode)
2046 {
2047     INTERNET_STATUS_CALLBACK ret;
2048 
2049     if (unicode) lpwh->dwInternalFlags |= INET_CALLBACKW;
2050     else lpwh->dwInternalFlags &= ~INET_CALLBACKW;
2051 
2052     ret = lpwh->lpfnStatusCB;
2053     lpwh->lpfnStatusCB = callback;
2054 
2055     return ret;
2056 }
2057 
2058 /***********************************************************************
2059  *           InternetSetStatusCallbackA (WININET.@)
2060  *
2061  * Sets up a callback function which is called as progress is made
2062  * during an operation.
2063  *
2064  * RETURNS
2065  *    Previous callback or NULL 	on success
2066  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
2067  *
2068  */
2069 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
2070 	HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
2071 {
2072     INTERNET_STATUS_CALLBACK retVal;
2073     object_header_t *lpwh;
2074 
2075     TRACE("%p\n", hInternet);
2076 
2077     if (!(lpwh = get_handle_object(hInternet)))
2078         return INTERNET_INVALID_STATUS_CALLBACK;
2079 
2080     retVal = set_status_callback(lpwh, lpfnIntCB, FALSE);
2081 
2082     WININET_Release( lpwh );
2083     return retVal;
2084 }
2085 
2086 /***********************************************************************
2087  *           InternetSetStatusCallbackW (WININET.@)
2088  *
2089  * Sets up a callback function which is called as progress is made
2090  * during an operation.
2091  *
2092  * RETURNS
2093  *    Previous callback or NULL 	on success
2094  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
2095  *
2096  */
2097 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(
2098 	HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
2099 {
2100     INTERNET_STATUS_CALLBACK retVal;
2101     object_header_t *lpwh;
2102 
2103     TRACE("%p\n", hInternet);
2104 
2105     if (!(lpwh = get_handle_object(hInternet)))
2106         return INTERNET_INVALID_STATUS_CALLBACK;
2107 
2108     retVal = set_status_callback(lpwh, lpfnIntCB, TRUE);
2109 
2110     WININET_Release( lpwh );
2111     return retVal;
2112 }
2113 
2114 /***********************************************************************
2115  *           InternetSetFilePointer (WININET.@)
2116  */
2117 DWORD WINAPI InternetSetFilePointer(HINTERNET hFile, LONG lDistanceToMove,
2118     PVOID pReserved, DWORD dwMoveContext, DWORD_PTR dwContext)
2119 {
2120     FIXME("(%p %d %p %d %lx): stub\n", hFile, lDistanceToMove, pReserved, dwMoveContext, dwContext);
2121     return FALSE;
2122 }
2123 
2124 /***********************************************************************
2125  *           InternetWriteFile (WININET.@)
2126  *
2127  * Write data to an open internet file
2128  *
2129  * RETURNS
2130  *    TRUE  on success
2131  *    FALSE on failure
2132  *
2133  */
2134 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer,
2135 	DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
2136 {
2137     object_header_t *lpwh;
2138     BOOL res;
2139 
2140     TRACE("(%p %p %d %p)\n", hFile, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
2141 
2142     lpwh = get_handle_object( hFile );
2143     if (!lpwh) {
2144         WARN("Invalid handle\n");
2145         SetLastError(ERROR_INVALID_HANDLE);
2146         return FALSE;
2147     }
2148 
2149     if(lpwh->vtbl->WriteFile) {
2150         res = lpwh->vtbl->WriteFile(lpwh, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
2151     }else {
2152         WARN("No Writefile method.\n");
2153         res = ERROR_INVALID_HANDLE;
2154     }
2155 
2156     WININET_Release( lpwh );
2157 
2158     if(res != ERROR_SUCCESS)
2159         SetLastError(res);
2160     return res == ERROR_SUCCESS;
2161 }
2162 
2163 
2164 /***********************************************************************
2165  *           InternetReadFile (WININET.@)
2166  *
2167  * Read data from an open internet file
2168  *
2169  * RETURNS
2170  *    TRUE  on success
2171  *    FALSE on failure
2172  *
2173  */
2174 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
2175         DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
2176 {
2177     object_header_t *hdr;
2178     DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2179 
2180     TRACE("%p %p %d %p\n", hFile, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead);
2181 
2182     hdr = get_handle_object(hFile);
2183     if (!hdr) {
2184         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2185         return FALSE;
2186     }
2187 
2188     if(hdr->vtbl->ReadFile) {
2189         res = hdr->vtbl->ReadFile(hdr, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead, 0, 0);
2190         if(res == ERROR_IO_PENDING)
2191             *pdwNumOfBytesRead = 0;
2192     }
2193 
2194     WININET_Release(hdr);
2195 
2196     TRACE("-- %s (%u) (bytes read: %d)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE", res,
2197           pdwNumOfBytesRead ? *pdwNumOfBytesRead : -1);
2198 
2199     SetLastError(res);
2200     return res == ERROR_SUCCESS;
2201 }
2202 
2203 /***********************************************************************
2204  *           InternetReadFileExA (WININET.@)
2205  *
2206  * Read data from an open internet file
2207  *
2208  * PARAMS
2209  *  hFile         [I] Handle returned by InternetOpenUrl or HttpOpenRequest.
2210  *  lpBuffersOut  [I/O] Buffer.
2211  *  dwFlags       [I] Flags. See notes.
2212  *  dwContext     [I] Context for callbacks.
2213  *
2214  * RETURNS
2215  *    TRUE  on success
2216  *    FALSE on failure
2217  *
2218  * NOTES
2219  *  The parameter dwFlags include zero or more of the following flags:
2220  *|IRF_ASYNC - Makes the call asynchronous.
2221  *|IRF_SYNC - Makes the call synchronous.
2222  *|IRF_USE_CONTEXT - Forces dwContext to be used.
2223  *|IRF_NO_WAIT - Don't block if the data is not available, just return what is available.
2224  *
2225  * However, in testing IRF_USE_CONTEXT seems to have no effect - dwContext isn't used.
2226  *
2227  * SEE
2228  *  InternetOpenUrlA(), HttpOpenRequestA()
2229  */
2230 BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffersOut,
2231 	DWORD dwFlags, DWORD_PTR dwContext)
2232 {
2233     object_header_t *hdr;
2234     DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2235 
2236     TRACE("(%p %p 0x%x 0x%lx)\n", hFile, lpBuffersOut, dwFlags, dwContext);
2237 
2238     if (lpBuffersOut->dwStructSize != sizeof(*lpBuffersOut)) {
2239         SetLastError(ERROR_INVALID_PARAMETER);
2240         return FALSE;
2241     }
2242 
2243     hdr = get_handle_object(hFile);
2244     if (!hdr) {
2245         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2246         return FALSE;
2247     }
2248 
2249     if(hdr->vtbl->ReadFile)
2250         res = hdr->vtbl->ReadFile(hdr, lpBuffersOut->lpvBuffer, lpBuffersOut->dwBufferLength,
2251                 &lpBuffersOut->dwBufferLength, dwFlags, dwContext);
2252 
2253     WININET_Release(hdr);
2254 
2255     TRACE("-- %s (%u, bytes read: %d)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
2256           res, lpBuffersOut->dwBufferLength);
2257 
2258     if(res != ERROR_SUCCESS)
2259         SetLastError(res);
2260     return res == ERROR_SUCCESS;
2261 }
2262 
2263 /***********************************************************************
2264  *           InternetReadFileExW (WININET.@)
2265  * SEE
2266  *  InternetReadFileExA()
2267  */
2268 BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
2269 	DWORD dwFlags, DWORD_PTR dwContext)
2270 {
2271     object_header_t *hdr;
2272     DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2273 
2274     TRACE("(%p %p 0x%x 0x%lx)\n", hFile, lpBuffer, dwFlags, dwContext);
2275 
2276     if (!lpBuffer || lpBuffer->dwStructSize != sizeof(*lpBuffer)) {
2277         SetLastError(ERROR_INVALID_PARAMETER);
2278         return FALSE;
2279     }
2280 
2281     hdr = get_handle_object(hFile);
2282     if (!hdr) {
2283         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2284         return FALSE;
2285     }
2286 
2287     if(hdr->vtbl->ReadFile)
2288         res = hdr->vtbl->ReadFile(hdr, lpBuffer->lpvBuffer, lpBuffer->dwBufferLength, &lpBuffer->dwBufferLength,
2289                 dwFlags, dwContext);
2290 
2291     WININET_Release(hdr);
2292 
2293     TRACE("-- %s (%u, bytes read: %d)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
2294           res, lpBuffer->dwBufferLength);
2295 
2296     if(res != ERROR_SUCCESS)
2297         SetLastError(res);
2298     return res == ERROR_SUCCESS;
2299 }
2300 
2301 static WCHAR *get_proxy_autoconfig_url(void)
2302 {
2303 #if defined(MAC_OS_X_VERSION_10_6) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
2304 
2305     CFDictionaryRef settings = CFNetworkCopySystemProxySettings();
2306     WCHAR *ret = NULL;
2307     SIZE_T len;
2308     const void *ref;
2309 
2310     if (!settings) return NULL;
2311 
2312     if (!(ref = CFDictionaryGetValue( settings, kCFNetworkProxiesProxyAutoConfigURLString )))
2313     {
2314         CFRelease( settings );
2315         return NULL;
2316     }
2317     len = CFStringGetLength( ref );
2318     if (len)
2319         ret = heap_alloc( (len+1) * sizeof(WCHAR) );
2320     if (ret)
2321     {
2322         CFStringGetCharacters( ref, CFRangeMake(0, len), ret );
2323         ret[len] = 0;
2324     }
2325     TRACE( "returning %s\n", debugstr_w(ret) );
2326     CFRelease( settings );
2327     return ret;
2328 #else
2329     static int once;
2330     if (!once++) FIXME( "no support on this platform\n" );
2331     return NULL;
2332 #endif
2333 }
2334 
2335 static DWORD query_global_option(DWORD option, void *buffer, DWORD *size, BOOL unicode)
2336 {
2337     /* FIXME: This function currently handles more options than it should. Options requiring
2338      * proper handles should be moved to proper functions */
2339     switch(option) {
2340     case INTERNET_OPTION_HTTP_VERSION:
2341         if (*size < sizeof(HTTP_VERSION_INFO))
2342             return ERROR_INSUFFICIENT_BUFFER;
2343 
2344         /*
2345          * Presently hardcoded to 1.1
2346          */
2347         ((HTTP_VERSION_INFO*)buffer)->dwMajorVersion = 1;
2348         ((HTTP_VERSION_INFO*)buffer)->dwMinorVersion = 1;
2349         *size = sizeof(HTTP_VERSION_INFO);
2350 
2351         return ERROR_SUCCESS;
2352 
2353     case INTERNET_OPTION_CONNECTED_STATE:
2354         FIXME("INTERNET_OPTION_CONNECTED_STATE: semi-stub\n");
2355 
2356         if (*size < sizeof(ULONG))
2357             return ERROR_INSUFFICIENT_BUFFER;
2358 
2359         *(ULONG*)buffer = INTERNET_STATE_CONNECTED;
2360         *size = sizeof(ULONG);
2361 
2362         return ERROR_SUCCESS;
2363 
2364     case INTERNET_OPTION_PROXY: {
2365         appinfo_t ai;
2366         BOOL ret;
2367 
2368         TRACE("Getting global proxy info\n");
2369         memset(&ai, 0, sizeof(appinfo_t));
2370         INTERNET_ConfigureProxy(&ai);
2371 
2372         ret = APPINFO_QueryOption(&ai.hdr, INTERNET_OPTION_PROXY, buffer, size, unicode); /* FIXME */
2373         APPINFO_Destroy(&ai.hdr);
2374         return ret;
2375     }
2376 
2377     case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2378         TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");
2379 
2380         if (*size < sizeof(ULONG))
2381             return ERROR_INSUFFICIENT_BUFFER;
2382 
2383         *(ULONG*)buffer = max_conns;
2384         *size = sizeof(ULONG);
2385 
2386         return ERROR_SUCCESS;
2387 
2388     case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2389             TRACE("INTERNET_OPTION_MAX_CONNS_1_0_SERVER\n");
2390 
2391             if (*size < sizeof(ULONG))
2392                 return ERROR_INSUFFICIENT_BUFFER;
2393 
2394             *(ULONG*)buffer = max_1_0_conns;
2395             *size = sizeof(ULONG);
2396 
2397             return ERROR_SUCCESS;
2398 
2399     case INTERNET_OPTION_SECURITY_FLAGS:
2400         FIXME("INTERNET_OPTION_SECURITY_FLAGS: Stub\n");
2401         return ERROR_SUCCESS;
2402 
2403     case INTERNET_OPTION_VERSION: {
2404         static const INTERNET_VERSION_INFO info = { 1, 2 };
2405 
2406         TRACE("INTERNET_OPTION_VERSION\n");
2407 
2408         if (*size < sizeof(INTERNET_VERSION_INFO))
2409             return ERROR_INSUFFICIENT_BUFFER;
2410 
2411         memcpy(buffer, &info, sizeof(info));
2412         *size = sizeof(info);
2413 
2414         return ERROR_SUCCESS;
2415     }
2416 
2417     case INTERNET_OPTION_PER_CONNECTION_OPTION: {
2418         WCHAR *url;
2419         INTERNET_PER_CONN_OPTION_LISTW *con = buffer;
2420         INTERNET_PER_CONN_OPTION_LISTA *conA = buffer;
2421         DWORD res = ERROR_SUCCESS, i;
2422         proxyinfo_t pi;
2423         LONG ret;
2424 
2425         TRACE("Getting global proxy info\n");
2426         if((ret = INTERNET_LoadProxySettings(&pi)))
2427             return ret;
2428 
2429 #ifdef __REACTOS__
2430         WARN("INTERNET_OPTION_PER_CONNECTION_OPTION stub\n");
2431 #else
2432         FIXME("INTERNET_OPTION_PER_CONNECTION_OPTION stub\n");
2433 #endif
2434 
2435         if (*size < sizeof(INTERNET_PER_CONN_OPTION_LISTW)) {
2436             FreeProxyInfo(&pi);
2437             return ERROR_INSUFFICIENT_BUFFER;
2438         }
2439 
2440         url = get_proxy_autoconfig_url();
2441 
2442         for (i = 0; i < con->dwOptionCount; i++) {
2443             INTERNET_PER_CONN_OPTIONW *optionW = con->pOptions + i;
2444             INTERNET_PER_CONN_OPTIONA *optionA = conA->pOptions + i;
2445 
2446             switch (optionW->dwOption) {
2447             case INTERNET_PER_CONN_FLAGS:
2448                 if(pi.proxyEnabled)
2449                     optionW->Value.dwValue = PROXY_TYPE_PROXY;
2450                 else
2451                     optionW->Value.dwValue = PROXY_TYPE_DIRECT;
2452                 if (url)
2453                     /* native includes PROXY_TYPE_DIRECT even if PROXY_TYPE_PROXY is set */
2454                     optionW->Value.dwValue |= PROXY_TYPE_DIRECT|PROXY_TYPE_AUTO_PROXY_URL;
2455                 break;
2456 
2457             case INTERNET_PER_CONN_PROXY_SERVER:
2458                 if (unicode)
2459                     optionW->Value.pszValue = heap_strdupW(pi.proxy);
2460                 else
2461                     optionA->Value.pszValue = heap_strdupWtoA(pi.proxy);
2462                 break;
2463 
2464             case INTERNET_PER_CONN_PROXY_BYPASS:
2465                 if (unicode)
2466                     optionW->Value.pszValue = heap_strdupW(pi.proxyBypass);
2467                 else
2468                     optionA->Value.pszValue = heap_strdupWtoA(pi.proxyBypass);
2469                 break;
2470 
2471             case INTERNET_PER_CONN_AUTOCONFIG_URL:
2472                 if (!url)
2473                     optionW->Value.pszValue = NULL;
2474                 else if (unicode)
2475                     optionW->Value.pszValue = heap_strdupW(url);
2476                 else
2477                     optionA->Value.pszValue = heap_strdupWtoA(url);
2478                 break;
2479 
2480             case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
2481                 optionW->Value.dwValue = AUTO_PROXY_FLAG_ALWAYS_DETECT;
2482                 break;
2483 
2484             case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
2485             case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
2486             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
2487             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
2488                 FIXME("Unhandled dwOption %d\n", optionW->dwOption);
2489                 memset(&optionW->Value, 0, sizeof(optionW->Value));
2490                 break;
2491 
2492 #ifdef __REACTOS__
2493             case INTERNET_PER_CONN_FLAGS_UI:
2494                 WARN("Unhandled dwOption %d\n", optionW->dwOption);
2495                 break;
2496 
2497 #endif
2498             default:
2499                 FIXME("Unknown dwOption %d\n", optionW->dwOption);
2500                 res = ERROR_INVALID_PARAMETER;
2501                 break;
2502             }
2503         }
2504         heap_free(url);
2505         FreeProxyInfo(&pi);
2506 
2507         return res;
2508     }
2509     case INTERNET_OPTION_REQUEST_FLAGS:
2510     case INTERNET_OPTION_USER_AGENT:
2511         *size = 0;
2512         return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2513     case INTERNET_OPTION_POLICY:
2514         return ERROR_INVALID_PARAMETER;
2515     case INTERNET_OPTION_CONNECT_TIMEOUT:
2516         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
2517 
2518         if (*size < sizeof(ULONG))
2519             return ERROR_INSUFFICIENT_BUFFER;
2520 
2521         *(ULONG*)buffer = connect_timeout;
2522         *size = sizeof(ULONG);
2523 
2524         return ERROR_SUCCESS;
2525     }
2526 
2527     FIXME("Stub for %d\n", option);
2528     return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2529 }
2530 
2531 DWORD INET_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
2532 {
2533     switch(option) {
2534     case INTERNET_OPTION_CONTEXT_VALUE:
2535         if (!size)
2536             return ERROR_INVALID_PARAMETER;
2537 
2538         if (*size < sizeof(DWORD_PTR)) {
2539             *size = sizeof(DWORD_PTR);
2540             return ERROR_INSUFFICIENT_BUFFER;
2541         }
2542         if (!buffer)
2543             return ERROR_INVALID_PARAMETER;
2544 
2545         *(DWORD_PTR *)buffer = hdr->dwContext;
2546         *size = sizeof(DWORD_PTR);
2547         return ERROR_SUCCESS;
2548 
2549     case INTERNET_OPTION_REQUEST_FLAGS:
2550         WARN("INTERNET_OPTION_REQUEST_FLAGS\n");
2551         *size = sizeof(DWORD);
2552         return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2553 
2554     case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2555     case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2556         WARN("Called on global option %u\n", option);
2557         return ERROR_INTERNET_INVALID_OPERATION;
2558     }
2559 
2560     /* FIXME: we shouldn't call it here */
2561     return query_global_option(option, buffer, size, unicode);
2562 }
2563 
2564 /***********************************************************************
2565  *           InternetQueryOptionW (WININET.@)
2566  *
2567  * Queries an options on the specified handle
2568  *
2569  * RETURNS
2570  *    TRUE  on success
2571  *    FALSE on failure
2572  *
2573  */
2574 BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
2575                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
2576 {
2577     object_header_t *hdr;
2578     DWORD res = ERROR_INVALID_HANDLE;
2579 
2580     TRACE("%p %d %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
2581 
2582     if(hInternet) {
2583         hdr = get_handle_object(hInternet);
2584         if (hdr) {
2585             res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, TRUE);
2586             WININET_Release(hdr);
2587         }
2588     }else {
2589         res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, TRUE);
2590     }
2591 
2592     if(res != ERROR_SUCCESS)
2593         SetLastError(res);
2594     return res == ERROR_SUCCESS;
2595 }
2596 
2597 /***********************************************************************
2598  *           InternetQueryOptionA (WININET.@)
2599  *
2600  * Queries an options on the specified handle
2601  *
2602  * RETURNS
2603  *    TRUE  on success
2604  *    FALSE on failure
2605  *
2606  */
2607 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
2608                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
2609 {
2610     object_header_t *hdr;
2611     DWORD res = ERROR_INVALID_HANDLE;
2612 
2613     TRACE("%p %d %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
2614 
2615     if(hInternet) {
2616         hdr = get_handle_object(hInternet);
2617         if (hdr) {
2618             res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, FALSE);
2619             WININET_Release(hdr);
2620         }
2621     }else {
2622         res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, FALSE);
2623     }
2624 
2625     if(res != ERROR_SUCCESS)
2626         SetLastError(res);
2627     return res == ERROR_SUCCESS;
2628 }
2629 
2630 DWORD INET_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
2631 {
2632     switch(option) {
2633     case INTERNET_OPTION_SETTINGS_CHANGED:
2634         FIXME("INTERNETOPTION_SETTINGS_CHANGED semi-stub\n");
2635         collect_connections(COLLECT_CONNECTIONS);
2636         return ERROR_SUCCESS;
2637     case INTERNET_OPTION_CALLBACK:
2638         WARN("Not settable option %u\n", option);
2639         return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2640     case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2641     case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2642         WARN("Called on global option %u\n", option);
2643         return ERROR_INTERNET_INVALID_OPERATION;
2644     case INTERNET_OPTION_REFRESH:
2645         return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2646     }
2647 
2648     return ERROR_INTERNET_INVALID_OPTION;
2649 }
2650 
2651 static DWORD set_global_option(DWORD option, void *buf, DWORD size)
2652 {
2653     switch(option) {
2654     case INTERNET_OPTION_CALLBACK:
2655         WARN("Not global option %u\n", option);
2656         return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2657 
2658     case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2659         TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");
2660 
2661         if(size != sizeof(max_conns))
2662             return ERROR_INTERNET_BAD_OPTION_LENGTH;
2663         if(!*(ULONG*)buf)
2664             return ERROR_BAD_ARGUMENTS;
2665 
2666         max_conns = *(ULONG*)buf;
2667         return ERROR_SUCCESS;
2668 
2669     case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2670         TRACE("INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER\n");
2671 
2672         if(size != sizeof(max_1_0_conns))
2673             return ERROR_INTERNET_BAD_OPTION_LENGTH;
2674         if(!*(ULONG*)buf)
2675             return ERROR_BAD_ARGUMENTS;
2676 
2677         max_1_0_conns = *(ULONG*)buf;
2678         return ERROR_SUCCESS;
2679 
2680     case INTERNET_OPTION_CONNECT_TIMEOUT:
2681         TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");
2682 
2683         if(size != sizeof(connect_timeout))
2684             return ERROR_INTERNET_BAD_OPTION_LENGTH;
2685         if(!*(ULONG*)buf)
2686             return ERROR_BAD_ARGUMENTS;
2687 
2688         connect_timeout = *(ULONG*)buf;
2689         return ERROR_SUCCESS;
2690 
2691     case INTERNET_OPTION_SUPPRESS_BEHAVIOR:
2692         FIXME("INTERNET_OPTION_SUPPRESS_BEHAVIOR stub\n");
2693 
2694         if(size != sizeof(ULONG))
2695             return ERROR_INTERNET_BAD_OPTION_LENGTH;
2696 
2697         FIXME("%08x\n", *(ULONG*)buf);
2698         return ERROR_SUCCESS;
2699     }
2700 
2701     return INET_SetOption(NULL, option, buf, size);
2702 }
2703 
2704 /***********************************************************************
2705  *           InternetSetOptionW (WININET.@)
2706  *
2707  * Sets an options on the specified handle
2708  *
2709  * RETURNS
2710  *    TRUE  on success
2711  *    FALSE on failure
2712  *
2713  */
2714 BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
2715                            LPVOID lpBuffer, DWORD dwBufferLength)
2716 {
2717     object_header_t *lpwhh;
2718     BOOL ret = TRUE;
2719     DWORD res;
2720 
2721     TRACE("(%p %d %p %d)\n", hInternet, dwOption, lpBuffer, dwBufferLength);
2722 
2723     lpwhh = (object_header_t*) get_handle_object( hInternet );
2724     if(lpwhh)
2725         res = lpwhh->vtbl->SetOption(lpwhh, dwOption, lpBuffer, dwBufferLength);
2726     else
2727         res = set_global_option(dwOption, lpBuffer, dwBufferLength);
2728 
2729     if(res != ERROR_INTERNET_INVALID_OPTION) {
2730         if(lpwhh)
2731             WININET_Release(lpwhh);
2732 
2733         if(res != ERROR_SUCCESS)
2734             SetLastError(res);
2735 
2736         return res == ERROR_SUCCESS;
2737     }
2738 
2739     switch (dwOption)
2740     {
2741     case INTERNET_OPTION_HTTP_VERSION:
2742       {
2743         HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
2744         FIXME("Option INTERNET_OPTION_HTTP_VERSION(%d,%d): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
2745       }
2746       break;
2747     case INTERNET_OPTION_ERROR_MASK:
2748       {
2749         if(!lpwhh) {
2750             SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2751             return FALSE;
2752         } else if(*(ULONG*)lpBuffer & (~(INTERNET_ERROR_MASK_INSERT_CDROM|
2753                         INTERNET_ERROR_MASK_COMBINED_SEC_CERT|
2754                         INTERNET_ERROR_MASK_LOGIN_FAILURE_DISPLAY_ENTITY_BODY))) {
2755             SetLastError(ERROR_INVALID_PARAMETER);
2756             ret = FALSE;
2757         } else if(dwBufferLength != sizeof(ULONG)) {
2758             SetLastError(ERROR_INTERNET_BAD_OPTION_LENGTH);
2759             ret = FALSE;
2760         } else
2761             TRACE("INTERNET_OPTION_ERROR_MASK: %x\n", *(ULONG*)lpBuffer);
2762             lpwhh->ErrorMask = *(ULONG*)lpBuffer;
2763       }
2764       break;
2765     case INTERNET_OPTION_PROXY:
2766     {
2767         INTERNET_PROXY_INFOW *info = lpBuffer;
2768 
2769         if (!lpBuffer || dwBufferLength < sizeof(INTERNET_PROXY_INFOW))
2770         {
2771             SetLastError(ERROR_INVALID_PARAMETER);
2772             return FALSE;
2773         }
2774         if (!hInternet)
2775         {
2776             EnterCriticalSection( &WININET_cs );
2777             free_global_proxy();
2778             global_proxy = heap_alloc( sizeof(proxyinfo_t) );
2779             if (global_proxy)
2780             {
2781                 if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY)
2782                 {
2783                     global_proxy->proxyEnabled = 1;
2784                     global_proxy->proxy = heap_strdupW( info->lpszProxy );
2785                     global_proxy->proxyBypass = heap_strdupW( info->lpszProxyBypass );
2786                 }
2787                 else
2788                 {
2789                     global_proxy->proxyEnabled = 0;
2790                     global_proxy->proxy = global_proxy->proxyBypass = NULL;
2791                 }
2792             }
2793             LeaveCriticalSection( &WININET_cs );
2794         }
2795         else
2796         {
2797             /* In general, each type of object should handle
2798              * INTERNET_OPTION_PROXY directly.  This FIXME ensures it doesn't
2799              * get silently dropped.
2800              */
2801             FIXME("INTERNET_OPTION_PROXY unimplemented\n");
2802             SetLastError(ERROR_INTERNET_INVALID_OPTION);
2803             ret = FALSE;
2804         }
2805         break;
2806     }
2807     case INTERNET_OPTION_CODEPAGE:
2808       {
2809         ULONG codepage = *(ULONG *)lpBuffer;
2810         FIXME("Option INTERNET_OPTION_CODEPAGE (%d): STUB\n", codepage);
2811       }
2812       break;
2813     case INTERNET_OPTION_REQUEST_PRIORITY:
2814       {
2815         ULONG priority = *(ULONG *)lpBuffer;
2816         FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%d): STUB\n", priority);
2817       }
2818       break;
2819     case INTERNET_OPTION_CONNECT_TIMEOUT:
2820       {
2821         ULONG connecttimeout = *(ULONG *)lpBuffer;
2822         FIXME("Option INTERNET_OPTION_CONNECT_TIMEOUT (%d): STUB\n", connecttimeout);
2823       }
2824       break;
2825     case INTERNET_OPTION_DATA_RECEIVE_TIMEOUT:
2826       {
2827         ULONG receivetimeout = *(ULONG *)lpBuffer;
2828         FIXME("Option INTERNET_OPTION_DATA_RECEIVE_TIMEOUT (%d): STUB\n", receivetimeout);
2829       }
2830       break;
2831     case INTERNET_OPTION_RESET_URLCACHE_SESSION:
2832         FIXME("Option INTERNET_OPTION_RESET_URLCACHE_SESSION: STUB\n");
2833         break;
2834     case INTERNET_OPTION_END_BROWSER_SESSION:
2835         FIXME("Option INTERNET_OPTION_END_BROWSER_SESSION: semi-stub\n");
2836         free_cookie();
2837         free_authorization_cache();
2838         break;
2839     case INTERNET_OPTION_CONNECTED_STATE:
2840         FIXME("Option INTERNET_OPTION_CONNECTED_STATE: STUB\n");
2841         break;
2842     case INTERNET_OPTION_DISABLE_PASSPORT_AUTH:
2843 	TRACE("Option INTERNET_OPTION_DISABLE_PASSPORT_AUTH: harmless stub, since not enabled\n");
2844 	break;
2845     case INTERNET_OPTION_SEND_TIMEOUT:
2846     case INTERNET_OPTION_RECEIVE_TIMEOUT:
2847     case INTERNET_OPTION_DATA_SEND_TIMEOUT:
2848     {
2849         ULONG timeout = *(ULONG *)lpBuffer;
2850         FIXME("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT/DATA_SEND_TIMEOUT %d\n", timeout);
2851         break;
2852     }
2853     case INTERNET_OPTION_CONNECT_RETRIES:
2854     {
2855         ULONG retries = *(ULONG *)lpBuffer;
2856         FIXME("INTERNET_OPTION_CONNECT_RETRIES %d\n", retries);
2857         break;
2858     }
2859     case INTERNET_OPTION_CONTEXT_VALUE:
2860     {
2861         if (!lpwhh)
2862         {
2863             SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2864             return FALSE;
2865         }
2866         if (!lpBuffer || dwBufferLength != sizeof(DWORD_PTR))
2867         {
2868             SetLastError(ERROR_INVALID_PARAMETER);
2869             ret = FALSE;
2870         }
2871         else
2872             lpwhh->dwContext = *(DWORD_PTR *)lpBuffer;
2873         break;
2874     }
2875     case INTERNET_OPTION_SECURITY_FLAGS:
2876 	 FIXME("Option INTERNET_OPTION_SECURITY_FLAGS; STUB\n");
2877 	 break;
2878     case INTERNET_OPTION_DISABLE_AUTODIAL:
2879 	 FIXME("Option INTERNET_OPTION_DISABLE_AUTODIAL; STUB\n");
2880 	 break;
2881     case INTERNET_OPTION_HTTP_DECODING:
2882         if (!lpwhh)
2883         {
2884             SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2885             return FALSE;
2886         }
2887         if (!lpBuffer || dwBufferLength != sizeof(BOOL))
2888         {
2889             SetLastError(ERROR_INVALID_PARAMETER);
2890             ret = FALSE;
2891         }
2892         else
2893             lpwhh->decoding = *(BOOL *)lpBuffer;
2894         break;
2895     case INTERNET_OPTION_COOKIES_3RD_PARTY:
2896         FIXME("INTERNET_OPTION_COOKIES_3RD_PARTY; STUB\n");
2897         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2898         ret = FALSE;
2899         break;
2900     case INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY:
2901         FIXME("INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY; STUB\n");
2902         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2903         ret = FALSE;
2904         break;
2905     case INTERNET_OPTION_CODEPAGE_PATH:
2906         FIXME("INTERNET_OPTION_CODEPAGE_PATH; STUB\n");
2907         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2908         ret = FALSE;
2909         break;
2910     case INTERNET_OPTION_CODEPAGE_EXTRA:
2911         FIXME("INTERNET_OPTION_CODEPAGE_EXTRA; STUB\n");
2912         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2913         ret = FALSE;
2914         break;
2915     case INTERNET_OPTION_IDN:
2916         FIXME("INTERNET_OPTION_IDN; STUB\n");
2917         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2918         ret = FALSE;
2919         break;
2920     case INTERNET_OPTION_POLICY:
2921         SetLastError(ERROR_INVALID_PARAMETER);
2922         ret = FALSE;
2923         break;
2924     case INTERNET_OPTION_PER_CONNECTION_OPTION: {
2925         INTERNET_PER_CONN_OPTION_LISTW *con = lpBuffer;
2926         LONG res;
2927         unsigned int i;
2928         proxyinfo_t pi;
2929 
2930         if (INTERNET_LoadProxySettings(&pi)) return FALSE;
2931 
2932         for (i = 0; i < con->dwOptionCount; i++) {
2933             INTERNET_PER_CONN_OPTIONW *option = con->pOptions + i;
2934 
2935             switch (option->dwOption) {
2936             case INTERNET_PER_CONN_PROXY_SERVER:
2937                 heap_free(pi.proxy);
2938                 pi.proxy = heap_strdupW(option->Value.pszValue);
2939                 break;
2940 
2941             case INTERNET_PER_CONN_FLAGS:
2942                 if(option->Value.dwValue & PROXY_TYPE_PROXY)
2943                     pi.proxyEnabled = 1;
2944                 else
2945                 {
2946                     if(option->Value.dwValue != PROXY_TYPE_DIRECT)
2947                         FIXME("Unhandled flags: 0x%x\n", option->Value.dwValue);
2948                     pi.proxyEnabled = 0;
2949                 }
2950                 break;
2951 
2952             case INTERNET_PER_CONN_PROXY_BYPASS:
2953                 heap_free(pi.proxyBypass);
2954                 pi.proxyBypass = heap_strdupW(option->Value.pszValue);
2955                 break;
2956 
2957             case INTERNET_PER_CONN_AUTOCONFIG_URL:
2958             case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
2959             case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
2960             case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
2961             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
2962             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
2963                 FIXME("Unhandled dwOption %d\n", option->dwOption);
2964                 break;
2965 
2966             default:
2967                 FIXME("Unknown dwOption %d\n", option->dwOption);
2968                 SetLastError(ERROR_INVALID_PARAMETER);
2969                 break;
2970             }
2971         }
2972 
2973         if ((res = INTERNET_SaveProxySettings(&pi)))
2974             SetLastError(res);
2975 
2976         FreeProxyInfo(&pi);
2977 
2978         ret = (res == ERROR_SUCCESS);
2979         break;
2980         }
2981     default:
2982         FIXME("Option %d STUB\n",dwOption);
2983         SetLastError(ERROR_INTERNET_INVALID_OPTION);
2984         ret = FALSE;
2985         break;
2986     }
2987 
2988     if(lpwhh)
2989         WININET_Release( lpwhh );
2990 
2991     return ret;
2992 }
2993 
2994 
2995 /***********************************************************************
2996  *           InternetSetOptionA (WININET.@)
2997  *
2998  * Sets an options on the specified handle.
2999  *
3000  * RETURNS
3001  *    TRUE  on success
3002  *    FALSE on failure
3003  *
3004  */
3005 BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
3006                            LPVOID lpBuffer, DWORD dwBufferLength)
3007 {
3008     LPVOID wbuffer;
3009     DWORD wlen;
3010     BOOL r;
3011 
3012     switch( dwOption )
3013     {
3014     case INTERNET_OPTION_PROXY:
3015         {
3016         LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
3017         LPINTERNET_PROXY_INFOW piw;
3018         DWORD proxlen, prbylen;
3019         LPWSTR prox, prby;
3020 
3021         proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
3022         prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
3023         wlen = sizeof(*piw) + proxlen + prbylen;
3024         wbuffer = heap_alloc(wlen*sizeof(WCHAR) );
3025         piw = (LPINTERNET_PROXY_INFOW) wbuffer;
3026         piw->dwAccessType = pi->dwAccessType;
3027         prox = (LPWSTR) &piw[1];
3028         prby = &prox[proxlen+1];
3029         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
3030         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
3031         piw->lpszProxy = prox;
3032         piw->lpszProxyBypass = prby;
3033         }
3034         break;
3035     case INTERNET_OPTION_USER_AGENT:
3036     case INTERNET_OPTION_USERNAME:
3037     case INTERNET_OPTION_PASSWORD:
3038     case INTERNET_OPTION_PROXY_USERNAME:
3039     case INTERNET_OPTION_PROXY_PASSWORD:
3040         wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 );
3041         if (!(wbuffer = heap_alloc( wlen * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
3042         MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, wbuffer, wlen );
3043         break;
3044     case INTERNET_OPTION_PER_CONNECTION_OPTION: {
3045         unsigned int i;
3046         INTERNET_PER_CONN_OPTION_LISTW *listW;
3047         INTERNET_PER_CONN_OPTION_LISTA *listA = lpBuffer;
3048         wlen = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
3049         wbuffer = heap_alloc(wlen);
3050         listW = wbuffer;
3051 
3052         listW->dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
3053         if (listA->pszConnection)
3054         {
3055             wlen = MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, NULL, 0 );
3056             listW->pszConnection = heap_alloc(wlen*sizeof(WCHAR));
3057             MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, listW->pszConnection, wlen );
3058         }
3059         else
3060             listW->pszConnection = NULL;
3061         listW->dwOptionCount = listA->dwOptionCount;
3062         listW->dwOptionError = listA->dwOptionError;
3063         listW->pOptions = heap_alloc(sizeof(INTERNET_PER_CONN_OPTIONW) * listA->dwOptionCount);
3064 
3065         for (i = 0; i < listA->dwOptionCount; ++i) {
3066             INTERNET_PER_CONN_OPTIONA *optA = listA->pOptions + i;
3067             INTERNET_PER_CONN_OPTIONW *optW = listW->pOptions + i;
3068 
3069             optW->dwOption = optA->dwOption;
3070 
3071             switch (optA->dwOption) {
3072             case INTERNET_PER_CONN_AUTOCONFIG_URL:
3073             case INTERNET_PER_CONN_PROXY_BYPASS:
3074             case INTERNET_PER_CONN_PROXY_SERVER:
3075             case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
3076             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
3077                 if (optA->Value.pszValue)
3078                 {
3079                     wlen = MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, NULL, 0 );
3080                     optW->Value.pszValue = heap_alloc(wlen*sizeof(WCHAR));
3081                     MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, optW->Value.pszValue, wlen );
3082                 }
3083                 else
3084                     optW->Value.pszValue = NULL;
3085                 break;
3086             case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
3087             case INTERNET_PER_CONN_FLAGS:
3088             case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
3089                 optW->Value.dwValue = optA->Value.dwValue;
3090                 break;
3091             case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
3092                 optW->Value.ftValue = optA->Value.ftValue;
3093                 break;
3094             default:
3095                 WARN("Unknown PER_CONN dwOption: %d, guessing at conversion to Wide\n", optA->dwOption);
3096                 optW->Value.dwValue = optA->Value.dwValue;
3097                 break;
3098             }
3099         }
3100         }
3101         break;
3102     default:
3103         wbuffer = lpBuffer;
3104         wlen = dwBufferLength;
3105     }
3106 
3107     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
3108 
3109     if( lpBuffer != wbuffer )
3110     {
3111         if (dwOption == INTERNET_OPTION_PER_CONNECTION_OPTION)
3112         {
3113             INTERNET_PER_CONN_OPTION_LISTW *list = wbuffer;
3114             unsigned int i;
3115             for (i = 0; i < list->dwOptionCount; ++i) {
3116                 INTERNET_PER_CONN_OPTIONW *opt = list->pOptions + i;
3117                 switch (opt->dwOption) {
3118                 case INTERNET_PER_CONN_AUTOCONFIG_URL:
3119                 case INTERNET_PER_CONN_PROXY_BYPASS:
3120                 case INTERNET_PER_CONN_PROXY_SERVER:
3121                 case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
3122                 case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
3123                     heap_free( opt->Value.pszValue );
3124                     break;
3125                 default:
3126                     break;
3127                 }
3128             }
3129             heap_free( list->pOptions );
3130         }
3131         heap_free( wbuffer );
3132     }
3133 
3134     return r;
3135 }
3136 
3137 
3138 /***********************************************************************
3139  *           InternetSetOptionExA (WININET.@)
3140  */
3141 BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
3142                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
3143 {
3144     FIXME("Flags %08x ignored\n", dwFlags);
3145     return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
3146 }
3147 
3148 /***********************************************************************
3149  *           InternetSetOptionExW (WININET.@)
3150  */
3151 BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
3152                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
3153 {
3154     FIXME("Flags %08x ignored\n", dwFlags);
3155     if( dwFlags & ~ISO_VALID_FLAGS )
3156     {
3157         SetLastError( ERROR_INVALID_PARAMETER );
3158         return FALSE;
3159     }
3160     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
3161 }
3162 
3163 static const WCHAR WININET_wkday[7][4] =
3164     { { 'S','u','n', 0 }, { 'M','o','n', 0 }, { 'T','u','e', 0 }, { 'W','e','d', 0 },
3165       { 'T','h','u', 0 }, { 'F','r','i', 0 }, { 'S','a','t', 0 } };
3166 static const WCHAR WININET_month[12][4] =
3167     { { 'J','a','n', 0 }, { 'F','e','b', 0 }, { 'M','a','r', 0 }, { 'A','p','r', 0 },
3168       { 'M','a','y', 0 }, { 'J','u','n', 0 }, { 'J','u','l', 0 }, { 'A','u','g', 0 },
3169       { 'S','e','p', 0 }, { 'O','c','t', 0 }, { 'N','o','v', 0 }, { 'D','e','c', 0 } };
3170 
3171 /***********************************************************************
3172  *           InternetTimeFromSystemTimeA (WININET.@)
3173  */
3174 BOOL WINAPI InternetTimeFromSystemTimeA( const SYSTEMTIME* time, DWORD format, LPSTR string, DWORD size )
3175 {
3176     BOOL ret;
3177     WCHAR stringW[INTERNET_RFC1123_BUFSIZE];
3178 
3179     TRACE( "%p 0x%08x %p 0x%08x\n", time, format, string, size );
3180 
3181     if (!time || !string || format != INTERNET_RFC1123_FORMAT)
3182     {
3183         SetLastError(ERROR_INVALID_PARAMETER);
3184         return FALSE;
3185     }
3186 
3187     if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
3188     {
3189         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3190         return FALSE;
3191     }
3192 
3193     ret = InternetTimeFromSystemTimeW( time, format, stringW, sizeof(stringW) );
3194     if (ret) WideCharToMultiByte( CP_ACP, 0, stringW, -1, string, size, NULL, NULL );
3195 
3196     return ret;
3197 }
3198 
3199 /***********************************************************************
3200  *           InternetTimeFromSystemTimeW (WININET.@)
3201  */
3202 BOOL WINAPI InternetTimeFromSystemTimeW( const SYSTEMTIME* time, DWORD format, LPWSTR string, DWORD size )
3203 {
3204     static const WCHAR date[] =
3205         { '%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0',
3206           '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0 };
3207 
3208     TRACE( "%p 0x%08x %p 0x%08x\n", time, format, string, size );
3209 
3210     if (!time || !string || format != INTERNET_RFC1123_FORMAT)
3211     {
3212         SetLastError(ERROR_INVALID_PARAMETER);
3213         return FALSE;
3214     }
3215 
3216     if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
3217     {
3218         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3219         return FALSE;
3220     }
3221 
3222     sprintfW( string, date,
3223               WININET_wkday[time->wDayOfWeek],
3224               time->wDay,
3225               WININET_month[time->wMonth - 1],
3226               time->wYear,
3227               time->wHour,
3228               time->wMinute,
3229               time->wSecond );
3230 
3231     return TRUE;
3232 }
3233 
3234 /***********************************************************************
3235  *           InternetTimeToSystemTimeA (WININET.@)
3236  */
3237 BOOL WINAPI InternetTimeToSystemTimeA( LPCSTR string, SYSTEMTIME* time, DWORD reserved )
3238 {
3239     BOOL ret = FALSE;
3240     WCHAR *stringW;
3241 
3242     TRACE( "%s %p 0x%08x\n", debugstr_a(string), time, reserved );
3243 
3244     stringW = heap_strdupAtoW(string);
3245     if (stringW)
3246     {
3247         ret = InternetTimeToSystemTimeW( stringW, time, reserved );
3248         heap_free( stringW );
3249     }
3250     return ret;
3251 }
3252 
3253 /***********************************************************************
3254  *           InternetTimeToSystemTimeW (WININET.@)
3255  */
3256 BOOL WINAPI InternetTimeToSystemTimeW( LPCWSTR string, SYSTEMTIME* time, DWORD reserved )
3257 {
3258     unsigned int i;
3259     const WCHAR *s = string;
3260     WCHAR       *end;
3261 
3262     TRACE( "%s %p 0x%08x\n", debugstr_w(string), time, reserved );
3263 
3264     if (!string || !time) return FALSE;
3265 
3266     /* Windows does this too */
3267     GetSystemTime( time );
3268 
3269     /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
3270      *  a SYSTEMTIME structure.
3271      */
3272 
3273     while (*s && !isalphaW( *s )) s++;
3274     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
3275     time->wDayOfWeek = 7;
3276 
3277     for (i = 0; i < 7; i++)
3278     {
3279         if (toupperW( WININET_wkday[i][0] ) == toupperW( s[0] ) &&
3280             toupperW( WININET_wkday[i][1] ) == toupperW( s[1] ) &&
3281             toupperW( WININET_wkday[i][2] ) == toupperW( s[2] ) )
3282         {
3283             time->wDayOfWeek = i;
3284             break;
3285         }
3286     }
3287 
3288     if (time->wDayOfWeek > 6) return TRUE;
3289     while (*s && !isdigitW( *s )) s++;
3290     time->wDay = strtolW( s, &end, 10 );
3291     s = end;
3292 
3293     while (*s && !isalphaW( *s )) s++;
3294     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
3295     time->wMonth = 0;
3296 
3297     for (i = 0; i < 12; i++)
3298     {
3299         if (toupperW( WININET_month[i][0]) == toupperW( s[0] ) &&
3300             toupperW( WININET_month[i][1]) == toupperW( s[1] ) &&
3301             toupperW( WININET_month[i][2]) == toupperW( s[2] ) )
3302         {
3303             time->wMonth = i + 1;
3304             break;
3305         }
3306     }
3307     if (time->wMonth == 0) return TRUE;
3308 
3309     while (*s && !isdigitW( *s )) s++;
3310     if (*s == '\0') return TRUE;
3311     time->wYear = strtolW( s, &end, 10 );
3312     s = end;
3313 
3314     while (*s && !isdigitW( *s )) s++;
3315     if (*s == '\0') return TRUE;
3316     time->wHour = strtolW( s, &end, 10 );
3317     s = end;
3318 
3319     while (*s && !isdigitW( *s )) s++;
3320     if (*s == '\0') return TRUE;
3321     time->wMinute = strtolW( s, &end, 10 );
3322     s = end;
3323 
3324     while (*s && !isdigitW( *s )) s++;
3325     if (*s == '\0') return TRUE;
3326     time->wSecond = strtolW( s, &end, 10 );
3327     s = end;
3328 
3329     time->wMilliseconds = 0;
3330     return TRUE;
3331 }
3332 
3333 /***********************************************************************
3334  *	InternetCheckConnectionW (WININET.@)
3335  *
3336  * Pings a requested host to check internet connection
3337  *
3338  * RETURNS
3339  *   TRUE on success and FALSE on failure. If a failure then
3340  *   ERROR_NOT_CONNECTED is placed into GetLastError
3341  *
3342  */
3343 BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
3344 {
3345 /*
3346  * this is a kludge which runs the resident ping program and reads the output.
3347  *
3348  * Anyone have a better idea?
3349  */
3350 
3351   BOOL   rc = FALSE;
3352   static const CHAR ping[] = "ping -c 1 ";
3353   static const CHAR redirect[] = " >/dev/null 2>/dev/null";
3354   WCHAR *host;
3355   DWORD len, host_len;
3356   INTERNET_PORT port;
3357   int status = -1;
3358 
3359   FIXME("(%s %x %x)\n", debugstr_w(lpszUrl), dwFlags, dwReserved);
3360 
3361   /*
3362    * Crack or set the Address
3363    */
3364   if (lpszUrl == NULL)
3365   {
3366      /*
3367       * According to the doc we are supposed to use the ip for the next
3368       * server in the WnInet internal server database. I have
3369       * no idea what that is or how to get it.
3370       *
3371       * So someone needs to implement this.
3372       */
3373      FIXME("Unimplemented with URL of NULL\n");
3374      return TRUE;
3375   }
3376   else
3377   {
3378      URL_COMPONENTSW components = {sizeof(components)};
3379 
3380      components.dwHostNameLength = 1;
3381 
3382      if (!InternetCrackUrlW(lpszUrl,0,0,&components))
3383        goto End;
3384 
3385      host = components.lpszHostName;
3386      host_len = components.dwHostNameLength;
3387      port = components.nPort;
3388      TRACE("host name: %s port: %d\n",debugstr_wn(host, host_len), port);
3389   }
3390 
3391   if (dwFlags & FLAG_ICC_FORCE_CONNECTION)
3392   {
3393       struct sockaddr_storage saddr;
3394       int sa_len = sizeof(saddr);
3395       WCHAR *host_z;
3396       int fd;
3397       BOOL b;
3398 
3399       host_z = heap_strndupW(host, host_len);
3400       if (!host_z)
3401           return FALSE;
3402 
3403       b = GetAddress(host_z, port, (struct sockaddr *)&saddr, &sa_len, NULL);
3404       heap_free(host_z);
3405       if(!b)
3406           goto End;
3407       init_winsock();
3408       fd = socket(saddr.ss_family, SOCK_STREAM, 0);
3409       if (fd != -1)
3410       {
3411           if (connect(fd, (struct sockaddr *)&saddr, sa_len) == 0)
3412               rc = TRUE;
3413           closesocket(fd);
3414       }
3415   }
3416   else
3417   {
3418       /*
3419        * Build our ping command
3420        */
3421       char *command;
3422 
3423       len = WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, NULL, 0, NULL, NULL);
3424       command = heap_alloc(strlen(ping)+len+strlen(redirect)+1);
3425       strcpy(command, ping);
3426       WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, command+sizeof(ping)-1, len, NULL, NULL);
3427       strcpy(command+sizeof(ping)-1+len, redirect);
3428 
3429       TRACE("Ping command is : %s\n",command);
3430 
3431       status = system(command);
3432       heap_free( command );
3433 
3434       TRACE("Ping returned a code of %i\n",status);
3435 
3436       /* Ping return code of 0 indicates success */
3437       if (status == 0)
3438          rc = TRUE;
3439   }
3440 
3441 End:
3442   if (rc == FALSE)
3443     INTERNET_SetLastError(ERROR_NOT_CONNECTED);
3444 
3445   return rc;
3446 }
3447 
3448 
3449 /***********************************************************************
3450  *	InternetCheckConnectionA (WININET.@)
3451  *
3452  * Pings a requested host to check internet connection
3453  *
3454  * RETURNS
3455  *   TRUE on success and FALSE on failure. If a failure then
3456  *   ERROR_NOT_CONNECTED is placed into GetLastError
3457  *
3458  */
3459 BOOL WINAPI InternetCheckConnectionA(LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
3460 {
3461     WCHAR *url = NULL;
3462     BOOL rc;
3463 
3464     if(lpszUrl) {
3465         url = heap_strdupAtoW(lpszUrl);
3466         if(!url)
3467             return FALSE;
3468     }
3469 
3470     rc = InternetCheckConnectionW(url, dwFlags, dwReserved);
3471 
3472     heap_free(url);
3473     return rc;
3474 }
3475 
3476 
3477 /**********************************************************
3478  *	INTERNET_InternetOpenUrlW (internal)
3479  *
3480  * Opens an URL
3481  *
3482  * RETURNS
3483  *   handle of connection or NULL on failure
3484  */
3485 static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
3486     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3487 {
3488     URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
3489     WCHAR *host, *user = NULL, *pass = NULL, *path;
3490     HINTERNET client = NULL, client1 = NULL;
3491     DWORD res;
3492 
3493     TRACE("(%p, %s, %s, %08x, %08x, %08lx)\n", hIC, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
3494 	  dwHeadersLength, dwFlags, dwContext);
3495 
3496     urlComponents.dwHostNameLength = 1;
3497     urlComponents.dwUserNameLength = 1;
3498     urlComponents.dwPasswordLength = 1;
3499     urlComponents.dwUrlPathLength = 1;
3500     urlComponents.dwExtraInfoLength = 1;
3501     if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
3502 	return NULL;
3503 
3504     if ((urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) &&
3505         urlComponents.dwExtraInfoLength)
3506     {
3507         assert(urlComponents.lpszUrlPath + urlComponents.dwUrlPathLength == urlComponents.lpszExtraInfo);
3508         urlComponents.dwUrlPathLength += urlComponents.dwExtraInfoLength;
3509     }
3510 
3511     host = heap_strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
3512     path = heap_strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength);
3513     if(urlComponents.dwUserNameLength)
3514         user = heap_strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength);
3515     if(urlComponents.dwPasswordLength)
3516         pass = heap_strndupW(urlComponents.lpszPassword, urlComponents.dwPasswordLength);
3517 
3518     switch(urlComponents.nScheme) {
3519     case INTERNET_SCHEME_FTP:
3520 	client = FTP_Connect(hIC, host, urlComponents.nPort,
3521 			     user, pass, dwFlags, dwContext, INET_OPENURL);
3522 	if(client == NULL)
3523 	    break;
3524 	client1 = FtpOpenFileW(client, path, GENERIC_READ, dwFlags, dwContext);
3525 	if(client1 == NULL) {
3526 	    InternetCloseHandle(client);
3527 	    break;
3528 	}
3529 	break;
3530 
3531     case INTERNET_SCHEME_HTTP:
3532     case INTERNET_SCHEME_HTTPS: {
3533 	static const WCHAR szStars[] = { '*','/','*', 0 };
3534 	LPCWSTR accept[2] = { szStars, NULL };
3535 
3536         if (urlComponents.nScheme == INTERNET_SCHEME_HTTPS) dwFlags |= INTERNET_FLAG_SECURE;
3537 
3538         /* FIXME: should use pointers, not handles, as handles are not thread-safe */
3539 	res = HTTP_Connect(hIC, host, urlComponents.nPort,
3540                            user, pass, dwFlags, dwContext, INET_OPENURL, &client);
3541         if(res != ERROR_SUCCESS) {
3542             INTERNET_SetLastError(res);
3543 	    break;
3544         }
3545 
3546         client1 = HttpOpenRequestW(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
3547 	if(client1 == NULL) {
3548 	    InternetCloseHandle(client);
3549 	    break;
3550 	}
3551 	HttpAddRequestHeadersW(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
3552 	if (!HttpSendRequestW(client1, NULL, 0, NULL, 0) &&
3553             GetLastError() != ERROR_IO_PENDING) {
3554 	    InternetCloseHandle(client1);
3555 	    client1 = NULL;
3556 	    break;
3557 	}
3558     }
3559     case INTERNET_SCHEME_GOPHER:
3560 	/* gopher doesn't seem to be implemented in wine, but it's supposed
3561 	 * to be supported by InternetOpenUrlA. */
3562     default:
3563         SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
3564 	break;
3565     }
3566 
3567     TRACE(" %p <--\n", client1);
3568 
3569     heap_free(host);
3570     heap_free(path);
3571     heap_free(user);
3572     heap_free(pass);
3573     return client1;
3574 }
3575 
3576 /**********************************************************
3577  *	InternetOpenUrlW (WININET.@)
3578  *
3579  * Opens an URL
3580  *
3581  * RETURNS
3582  *   handle of connection or NULL on failure
3583  */
3584 typedef struct {
3585     task_header_t hdr;
3586     WCHAR *url;
3587     WCHAR *headers;
3588     DWORD headers_len;
3589     DWORD flags;
3590     DWORD_PTR context;
3591 } open_url_task_t;
3592 
3593 static void AsyncInternetOpenUrlProc(task_header_t *hdr)
3594 {
3595     open_url_task_t *task = (open_url_task_t*)hdr;
3596 
3597     TRACE("%p\n", task->hdr.hdr);
3598 
3599     INTERNET_InternetOpenUrlW((appinfo_t*)task->hdr.hdr, task->url, task->headers,
3600             task->headers_len, task->flags, task->context);
3601     heap_free(task->url);
3602     heap_free(task->headers);
3603 }
3604 
3605 HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
3606     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3607 {
3608     HINTERNET ret = NULL;
3609     appinfo_t *hIC = NULL;
3610 
3611     if (TRACE_ON(wininet)) {
3612 	TRACE("(%p, %s, %s, %08x, %08x, %08lx)\n", hInternet, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
3613 	      dwHeadersLength, dwFlags, dwContext);
3614 	TRACE("  flags :");
3615 	dump_INTERNET_FLAGS(dwFlags);
3616     }
3617 
3618     if (!lpszUrl)
3619     {
3620         SetLastError(ERROR_INVALID_PARAMETER);
3621         goto lend;
3622     }
3623 
3624     hIC = (appinfo_t*)get_handle_object( hInternet );
3625     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT) {
3626 	SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3627  	goto lend;
3628     }
3629 
3630     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) {
3631 	open_url_task_t *task;
3632 
3633         task = alloc_async_task(&hIC->hdr, AsyncInternetOpenUrlProc, sizeof(*task));
3634         task->url = heap_strdupW(lpszUrl);
3635         task->headers = heap_strdupW(lpszHeaders);
3636         task->headers_len = dwHeadersLength;
3637         task->flags = dwFlags;
3638         task->context = dwContext;
3639 
3640         INTERNET_AsyncCall(&task->hdr);
3641         SetLastError(ERROR_IO_PENDING);
3642     } else {
3643 	ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
3644     }
3645 
3646   lend:
3647     if( hIC )
3648         WININET_Release( &hIC->hdr );
3649     TRACE(" %p <--\n", ret);
3650 
3651     return ret;
3652 }
3653 
3654 /**********************************************************
3655  *	InternetOpenUrlA (WININET.@)
3656  *
3657  * Opens an URL
3658  *
3659  * RETURNS
3660  *   handle of connection or NULL on failure
3661  */
3662 HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
3663     LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3664 {
3665     HINTERNET rc = NULL;
3666     LPWSTR szUrl = NULL;
3667     WCHAR *headers = NULL;
3668 
3669     TRACE("\n");
3670 
3671     if(lpszUrl) {
3672         szUrl = heap_strdupAtoW(lpszUrl);
3673         if(!szUrl)
3674             return NULL;
3675     }
3676 
3677     if(lpszHeaders) {
3678         headers = heap_strndupAtoW(lpszHeaders, dwHeadersLength, &dwHeadersLength);
3679         if(!headers) {
3680             heap_free(szUrl);
3681             return NULL;
3682         }
3683     }
3684 
3685     rc = InternetOpenUrlW(hInternet, szUrl, headers, dwHeadersLength, dwFlags, dwContext);
3686 
3687     heap_free(szUrl);
3688     heap_free(headers);
3689     return rc;
3690 }
3691 
3692 
3693 static LPWITHREADERROR INTERNET_AllocThreadError(void)
3694 {
3695     LPWITHREADERROR lpwite = heap_alloc(sizeof(*lpwite));
3696 
3697     if (lpwite)
3698     {
3699         lpwite->dwError = 0;
3700         lpwite->response[0] = '\0';
3701     }
3702 
3703     if (!TlsSetValue(g_dwTlsErrIndex, lpwite))
3704     {
3705         heap_free(lpwite);
3706         return NULL;
3707     }
3708     return lpwite;
3709 }
3710 
3711 
3712 /***********************************************************************
3713  *           INTERNET_SetLastError (internal)
3714  *
3715  * Set last thread specific error
3716  *
3717  * RETURNS
3718  *
3719  */
3720 void INTERNET_SetLastError(DWORD dwError)
3721 {
3722     LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
3723 
3724     if (!lpwite)
3725         lpwite = INTERNET_AllocThreadError();
3726 
3727     SetLastError(dwError);
3728     if(lpwite)
3729         lpwite->dwError = dwError;
3730 }
3731 
3732 
3733 /***********************************************************************
3734  *           INTERNET_GetLastError (internal)
3735  *
3736  * Get last thread specific error
3737  *
3738  * RETURNS
3739  *
3740  */
3741 DWORD INTERNET_GetLastError(void)
3742 {
3743     LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
3744     if (!lpwite) return 0;
3745     /* TlsGetValue clears last error, so set it again here */
3746     SetLastError(lpwite->dwError);
3747     return lpwite->dwError;
3748 }
3749 
3750 
3751 /***********************************************************************
3752  *           INTERNET_WorkerThreadFunc (internal)
3753  *
3754  * Worker thread execution function
3755  *
3756  * RETURNS
3757  *
3758  */
3759 static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
3760 {
3761     task_header_t *task = lpvParam;
3762 
3763     TRACE("\n");
3764 
3765     task->proc(task);
3766     WININET_Release(task->hdr);
3767     heap_free(task);
3768 
3769     if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
3770     {
3771         heap_free(TlsGetValue(g_dwTlsErrIndex));
3772         TlsSetValue(g_dwTlsErrIndex, NULL);
3773     }
3774     return TRUE;
3775 }
3776 
3777 void *alloc_async_task(object_header_t *hdr, async_task_proc_t proc, size_t size)
3778 {
3779     task_header_t *task;
3780 
3781     task = heap_alloc(size);
3782     if(!task)
3783         return NULL;
3784 
3785     task->hdr = WININET_AddRef(hdr);
3786     task->proc = proc;
3787     return task;
3788 }
3789 
3790 /***********************************************************************
3791  *           INTERNET_AsyncCall (internal)
3792  *
3793  * Retrieves work request from queue
3794  *
3795  * RETURNS
3796  *
3797  */
3798 DWORD INTERNET_AsyncCall(task_header_t *task)
3799 {
3800     BOOL bSuccess;
3801 
3802     TRACE("\n");
3803 
3804     bSuccess = QueueUserWorkItem(INTERNET_WorkerThreadFunc, task, WT_EXECUTELONGFUNCTION);
3805     if (!bSuccess)
3806     {
3807         heap_free(task);
3808         return ERROR_INTERNET_ASYNC_THREAD_FAILED;
3809     }
3810     return ERROR_SUCCESS;
3811 }
3812 
3813 
3814 /***********************************************************************
3815  *          INTERNET_GetResponseBuffer  (internal)
3816  *
3817  * RETURNS
3818  *
3819  */
3820 LPSTR INTERNET_GetResponseBuffer(void)
3821 {
3822     LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
3823     if (!lpwite)
3824         lpwite = INTERNET_AllocThreadError();
3825     TRACE("\n");
3826     return lpwite->response;
3827 }
3828 
3829 /**********************************************************
3830  *	InternetQueryDataAvailable (WININET.@)
3831  *
3832  * Determines how much data is available to be read.
3833  *
3834  * RETURNS
3835  *   TRUE on success, FALSE if an error occurred. If
3836  *   INTERNET_FLAG_ASYNC was specified in InternetOpen, and
3837  *   no data is presently available, FALSE is returned with
3838  *   the last error ERROR_IO_PENDING; a callback with status
3839  *   INTERNET_STATUS_REQUEST_COMPLETE will be sent when more
3840  *   data is available.
3841  */
3842 BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
3843                                 LPDWORD lpdwNumberOfBytesAvailable,
3844                                 DWORD dwFlags, DWORD_PTR dwContext)
3845 {
3846     object_header_t *hdr;
3847     DWORD res;
3848 
3849     TRACE("(%p %p %x %lx)\n", hFile, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
3850 
3851     hdr = get_handle_object( hFile );
3852     if (!hdr) {
3853         SetLastError(ERROR_INVALID_HANDLE);
3854         return FALSE;
3855     }
3856 
3857     if(hdr->vtbl->QueryDataAvailable) {
3858         res = hdr->vtbl->QueryDataAvailable(hdr, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
3859     }else {
3860         WARN("wrong handle\n");
3861         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3862     }
3863 
3864     WININET_Release(hdr);
3865 
3866     if(res != ERROR_SUCCESS)
3867         SetLastError(res);
3868     return res == ERROR_SUCCESS;
3869 }
3870 
3871 DWORD create_req_file(const WCHAR *file_name, req_file_t **ret)
3872 {
3873     req_file_t *req_file;
3874 
3875     req_file = heap_alloc_zero(sizeof(*req_file));
3876     if(!req_file)
3877         return ERROR_NOT_ENOUGH_MEMORY;
3878 
3879     req_file->ref = 1;
3880 
3881     req_file->file_name = heap_strdupW(file_name);
3882     if(!req_file->file_name) {
3883         heap_free(req_file);
3884         return ERROR_NOT_ENOUGH_MEMORY;
3885     }
3886 
3887     req_file->file_handle = CreateFileW(req_file->file_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
3888               NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
3889     if(req_file->file_handle == INVALID_HANDLE_VALUE) {
3890         req_file_release(req_file);
3891         return GetLastError();
3892     }
3893 
3894     *ret = req_file;
3895     return ERROR_SUCCESS;
3896 }
3897 
3898 void req_file_release(req_file_t *req_file)
3899 {
3900     if(InterlockedDecrement(&req_file->ref))
3901         return;
3902 
3903     if(!req_file->is_committed)
3904         DeleteFileW(req_file->file_name);
3905     if(req_file->file_handle && req_file->file_handle != INVALID_HANDLE_VALUE)
3906         CloseHandle(req_file->file_handle);
3907     heap_free(req_file->file_name);
3908     heap_free(req_file->url);
3909     heap_free(req_file);
3910 }
3911 
3912 /***********************************************************************
3913  *      InternetLockRequestFile (WININET.@)
3914  */
3915 BOOL WINAPI InternetLockRequestFile(HINTERNET hInternet, HANDLE *lphLockReqHandle)
3916 {
3917     req_file_t *req_file = NULL;
3918     object_header_t *hdr;
3919     DWORD res;
3920 
3921     TRACE("(%p %p)\n", hInternet, lphLockReqHandle);
3922 
3923     hdr = get_handle_object(hInternet);
3924     if (!hdr) {
3925         SetLastError(ERROR_INVALID_HANDLE);
3926         return FALSE;
3927     }
3928 
3929     if(hdr->vtbl->LockRequestFile) {
3930         res = hdr->vtbl->LockRequestFile(hdr, &req_file);
3931     }else {
3932         WARN("wrong handle\n");
3933         res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
3934     }
3935 
3936     WININET_Release(hdr);
3937 
3938     *lphLockReqHandle = req_file;
3939     if(res != ERROR_SUCCESS)
3940         SetLastError(res);
3941     return res == ERROR_SUCCESS;
3942 }
3943 
3944 BOOL WINAPI InternetUnlockRequestFile(HANDLE hLockHandle)
3945 {
3946     TRACE("(%p)\n", hLockHandle);
3947 
3948     req_file_release(hLockHandle);
3949     return TRUE;
3950 }
3951 
3952 
3953 /***********************************************************************
3954  *      InternetAutodial (WININET.@)
3955  *
3956  * On windows this function is supposed to dial the default internet
3957  * connection. We don't want to have Wine dial out to the internet so
3958  * we return TRUE by default. It might be nice to check if we are connected.
3959  *
3960  * RETURNS
3961  *   TRUE on success
3962  *   FALSE on failure
3963  *
3964  */
3965 BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
3966 {
3967     FIXME("STUB\n");
3968 
3969     /* Tell that we are connected to the internet. */
3970     return TRUE;
3971 }
3972 
3973 /***********************************************************************
3974  *      InternetAutodialHangup (WININET.@)
3975  *
3976  * Hangs up a connection made with InternetAutodial
3977  *
3978  * PARAM
3979  *    dwReserved
3980  * RETURNS
3981  *   TRUE on success
3982  *   FALSE on failure
3983  *
3984  */
3985 BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
3986 {
3987     FIXME("STUB\n");
3988 
3989     /* we didn't dial, we don't disconnect */
3990     return TRUE;
3991 }
3992 
3993 /***********************************************************************
3994  *      InternetCombineUrlA (WININET.@)
3995  *
3996  * Combine a base URL with a relative URL
3997  *
3998  * RETURNS
3999  *   TRUE on success
4000  *   FALSE on failure
4001  *
4002  */
4003 
4004 BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
4005                                 LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
4006                                 DWORD dwFlags)
4007 {
4008     HRESULT hr=S_OK;
4009 
4010     TRACE("(%s, %s, %p, %p, 0x%08x)\n", debugstr_a(lpszBaseUrl), debugstr_a(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
4011 
4012     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
4013     dwFlags ^= ICU_NO_ENCODE;
4014     hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
4015 
4016     return (hr==S_OK);
4017 }
4018 
4019 /***********************************************************************
4020  *      InternetCombineUrlW (WININET.@)
4021  *
4022  * Combine a base URL with a relative URL
4023  *
4024  * RETURNS
4025  *   TRUE on success
4026  *   FALSE on failure
4027  *
4028  */
4029 
4030 BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
4031                                 LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
4032                                 DWORD dwFlags)
4033 {
4034     HRESULT hr=S_OK;
4035 
4036     TRACE("(%s, %s, %p, %p, 0x%08x)\n", debugstr_w(lpszBaseUrl), debugstr_w(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
4037 
4038     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
4039     dwFlags ^= ICU_NO_ENCODE;
4040     hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
4041 
4042     return (hr==S_OK);
4043 }
4044 
4045 /* max port num is 65535 => 5 digits */
4046 #define MAX_WORD_DIGITS 5
4047 
4048 #define URL_GET_COMP_LENGTH(url, component) ((url)->dw##component##Length ? \
4049     (url)->dw##component##Length : strlenW((url)->lpsz##component))
4050 #define URL_GET_COMP_LENGTHA(url, component) ((url)->dw##component##Length ? \
4051     (url)->dw##component##Length : strlen((url)->lpsz##component))
4052 
4053 static BOOL url_uses_default_port(INTERNET_SCHEME nScheme, INTERNET_PORT nPort)
4054 {
4055     if ((nScheme == INTERNET_SCHEME_HTTP) &&
4056         (nPort == INTERNET_DEFAULT_HTTP_PORT))
4057         return TRUE;
4058     if ((nScheme == INTERNET_SCHEME_HTTPS) &&
4059         (nPort == INTERNET_DEFAULT_HTTPS_PORT))
4060         return TRUE;
4061     if ((nScheme == INTERNET_SCHEME_FTP) &&
4062         (nPort == INTERNET_DEFAULT_FTP_PORT))
4063         return TRUE;
4064     if ((nScheme == INTERNET_SCHEME_GOPHER) &&
4065         (nPort == INTERNET_DEFAULT_GOPHER_PORT))
4066         return TRUE;
4067 
4068     if (nPort == INTERNET_INVALID_PORT_NUMBER)
4069         return TRUE;
4070 
4071     return FALSE;
4072 }
4073 
4074 /* opaque urls do not fit into the standard url hierarchy and don't have
4075  * two following slashes */
4076 static inline BOOL scheme_is_opaque(INTERNET_SCHEME nScheme)
4077 {
4078     return (nScheme != INTERNET_SCHEME_FTP) &&
4079            (nScheme != INTERNET_SCHEME_GOPHER) &&
4080            (nScheme != INTERNET_SCHEME_HTTP) &&
4081            (nScheme != INTERNET_SCHEME_HTTPS) &&
4082            (nScheme != INTERNET_SCHEME_FILE);
4083 }
4084 
4085 static LPCWSTR INTERNET_GetSchemeString(INTERNET_SCHEME scheme)
4086 {
4087     int index;
4088     if (scheme < INTERNET_SCHEME_FIRST)
4089         return NULL;
4090     index = scheme - INTERNET_SCHEME_FIRST;
4091     if (index >= ARRAY_SIZE(url_schemes))
4092         return NULL;
4093     return (LPCWSTR)url_schemes[index];
4094 }
4095 
4096 /* we can calculate using ansi strings because we're just
4097  * calculating string length, not size
4098  */
4099 static BOOL calc_url_length(LPURL_COMPONENTSW lpUrlComponents,
4100                             LPDWORD lpdwUrlLength)
4101 {
4102     INTERNET_SCHEME nScheme;
4103 
4104     *lpdwUrlLength = 0;
4105 
4106     if (lpUrlComponents->lpszScheme)
4107     {
4108         DWORD dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
4109         *lpdwUrlLength += dwLen;
4110         nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
4111     }
4112     else
4113     {
4114         LPCWSTR scheme;
4115 
4116         nScheme = lpUrlComponents->nScheme;
4117 
4118         if (nScheme == INTERNET_SCHEME_DEFAULT)
4119             nScheme = INTERNET_SCHEME_HTTP;
4120         scheme = INTERNET_GetSchemeString(nScheme);
4121         *lpdwUrlLength += strlenW(scheme);
4122     }
4123 
4124     (*lpdwUrlLength)++; /* ':' */
4125     if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
4126         *lpdwUrlLength += strlen("//");
4127 
4128     if (lpUrlComponents->lpszUserName)
4129     {
4130         *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
4131         *lpdwUrlLength += strlen("@");
4132     }
4133     else
4134     {
4135         if (lpUrlComponents->lpszPassword)
4136         {
4137             INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4138             return FALSE;
4139         }
4140     }
4141 
4142     if (lpUrlComponents->lpszPassword)
4143     {
4144         *lpdwUrlLength += strlen(":");
4145         *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, Password);
4146     }
4147 
4148     if (lpUrlComponents->lpszHostName)
4149     {
4150         *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
4151 
4152         if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
4153         {
4154             char szPort[MAX_WORD_DIGITS+1];
4155 
4156             *lpdwUrlLength += sprintf(szPort, "%d", lpUrlComponents->nPort);
4157             *lpdwUrlLength += strlen(":");
4158         }
4159 
4160         if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
4161             (*lpdwUrlLength)++; /* '/' */
4162     }
4163 
4164     if (lpUrlComponents->lpszUrlPath)
4165         *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
4166 
4167     if (lpUrlComponents->lpszExtraInfo)
4168         *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);
4169 
4170     return TRUE;
4171 }
4172 
4173 static void convert_urlcomp_atow(LPURL_COMPONENTSA lpUrlComponents, LPURL_COMPONENTSW urlCompW)
4174 {
4175     INT len;
4176 
4177     ZeroMemory(urlCompW, sizeof(URL_COMPONENTSW));
4178 
4179     urlCompW->dwStructSize = sizeof(URL_COMPONENTSW);
4180     urlCompW->dwSchemeLength = lpUrlComponents->dwSchemeLength;
4181     urlCompW->nScheme = lpUrlComponents->nScheme;
4182     urlCompW->dwHostNameLength = lpUrlComponents->dwHostNameLength;
4183     urlCompW->nPort = lpUrlComponents->nPort;
4184     urlCompW->dwUserNameLength = lpUrlComponents->dwUserNameLength;
4185     urlCompW->dwPasswordLength = lpUrlComponents->dwPasswordLength;
4186     urlCompW->dwUrlPathLength = lpUrlComponents->dwUrlPathLength;
4187     urlCompW->dwExtraInfoLength = lpUrlComponents->dwExtraInfoLength;
4188 
4189     if (lpUrlComponents->lpszScheme)
4190     {
4191         len = URL_GET_COMP_LENGTHA(lpUrlComponents, Scheme) + 1;
4192         urlCompW->lpszScheme = heap_alloc(len * sizeof(WCHAR));
4193         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszScheme,
4194                             -1, urlCompW->lpszScheme, len);
4195     }
4196 
4197     if (lpUrlComponents->lpszHostName)
4198     {
4199         len = URL_GET_COMP_LENGTHA(lpUrlComponents, HostName) + 1;
4200         urlCompW->lpszHostName = heap_alloc(len * sizeof(WCHAR));
4201         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszHostName,
4202                             -1, urlCompW->lpszHostName, len);
4203     }
4204 
4205     if (lpUrlComponents->lpszUserName)
4206     {
4207         len = URL_GET_COMP_LENGTHA(lpUrlComponents, UserName) + 1;
4208         urlCompW->lpszUserName = heap_alloc(len * sizeof(WCHAR));
4209         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUserName,
4210                             -1, urlCompW->lpszUserName, len);
4211     }
4212 
4213     if (lpUrlComponents->lpszPassword)
4214     {
4215         len = URL_GET_COMP_LENGTHA(lpUrlComponents, Password) + 1;
4216         urlCompW->lpszPassword = heap_alloc(len * sizeof(WCHAR));
4217         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszPassword,
4218                             -1, urlCompW->lpszPassword, len);
4219     }
4220 
4221     if (lpUrlComponents->lpszUrlPath)
4222     {
4223         len = URL_GET_COMP_LENGTHA(lpUrlComponents, UrlPath) + 1;
4224         urlCompW->lpszUrlPath = heap_alloc(len * sizeof(WCHAR));
4225         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUrlPath,
4226                             -1, urlCompW->lpszUrlPath, len);
4227     }
4228 
4229     if (lpUrlComponents->lpszExtraInfo)
4230     {
4231         len = URL_GET_COMP_LENGTHA(lpUrlComponents, ExtraInfo) + 1;
4232         urlCompW->lpszExtraInfo = heap_alloc(len * sizeof(WCHAR));
4233         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszExtraInfo,
4234                             -1, urlCompW->lpszExtraInfo, len);
4235     }
4236 }
4237 
4238 /***********************************************************************
4239  *      InternetCreateUrlA (WININET.@)
4240  *
4241  * See InternetCreateUrlW.
4242  */
4243 BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
4244                                LPSTR lpszUrl, LPDWORD lpdwUrlLength)
4245 {
4246     BOOL ret;
4247     LPWSTR urlW = NULL;
4248     URL_COMPONENTSW urlCompW;
4249 
4250     TRACE("(%p,%d,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
4251 
4252     if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
4253     {
4254         SetLastError(ERROR_INVALID_PARAMETER);
4255         return FALSE;
4256     }
4257 
4258     convert_urlcomp_atow(lpUrlComponents, &urlCompW);
4259 
4260     if (lpszUrl)
4261         urlW = heap_alloc(*lpdwUrlLength * sizeof(WCHAR));
4262 
4263     ret = InternetCreateUrlW(&urlCompW, dwFlags, urlW, lpdwUrlLength);
4264 
4265     if (!ret && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
4266         *lpdwUrlLength /= sizeof(WCHAR);
4267 
4268     /* on success, lpdwUrlLength points to the size of urlW in WCHARS
4269     * minus one, so add one to leave room for NULL terminator
4270     */
4271     if (ret)
4272         WideCharToMultiByte(CP_ACP, 0, urlW, -1, lpszUrl, *lpdwUrlLength + 1, NULL, NULL);
4273 
4274     heap_free(urlCompW.lpszScheme);
4275     heap_free(urlCompW.lpszHostName);
4276     heap_free(urlCompW.lpszUserName);
4277     heap_free(urlCompW.lpszPassword);
4278     heap_free(urlCompW.lpszUrlPath);
4279     heap_free(urlCompW.lpszExtraInfo);
4280     heap_free(urlW);
4281     return ret;
4282 }
4283 
4284 /***********************************************************************
4285  *      InternetCreateUrlW (WININET.@)
4286  *
4287  * Creates a URL from its component parts.
4288  *
4289  * PARAMS
4290  *  lpUrlComponents [I] URL Components.
4291  *  dwFlags         [I] Flags. See notes.
4292  *  lpszUrl         [I] Buffer in which to store the created URL.
4293  *  lpdwUrlLength   [I/O] On input, the length of the buffer pointed to by
4294  *                        lpszUrl in characters. On output, the number of bytes
4295  *                        required to store the URL including terminator.
4296  *
4297  * NOTES
4298  *
4299  * The dwFlags parameter can be zero or more of the following:
4300  *|ICU_ESCAPE - Generates escape sequences for unsafe characters in the path and extra info of the URL.
4301  *
4302  * RETURNS
4303  *   TRUE on success
4304  *   FALSE on failure
4305  *
4306  */
4307 BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
4308                                LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
4309 {
4310     DWORD dwLen;
4311     INTERNET_SCHEME nScheme;
4312 
4313     static const WCHAR slashSlashW[] = {'/','/'};
4314     static const WCHAR fmtW[] = {'%','u',0};
4315 
4316     TRACE("(%p,%d,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
4317 
4318     if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
4319     {
4320         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4321         return FALSE;
4322     }
4323 
4324     if (!calc_url_length(lpUrlComponents, &dwLen))
4325         return FALSE;
4326 
4327     if (!lpszUrl || *lpdwUrlLength < dwLen)
4328     {
4329         *lpdwUrlLength = (dwLen + 1) * sizeof(WCHAR);
4330         INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
4331         return FALSE;
4332     }
4333 
4334     *lpdwUrlLength = dwLen;
4335     lpszUrl[0] = 0x00;
4336 
4337     dwLen = 0;
4338 
4339     if (lpUrlComponents->lpszScheme)
4340     {
4341         dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
4342         memcpy(lpszUrl, lpUrlComponents->lpszScheme, dwLen * sizeof(WCHAR));
4343         lpszUrl += dwLen;
4344 
4345         nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
4346     }
4347     else
4348     {
4349         LPCWSTR scheme;
4350         nScheme = lpUrlComponents->nScheme;
4351 
4352         if (nScheme == INTERNET_SCHEME_DEFAULT)
4353             nScheme = INTERNET_SCHEME_HTTP;
4354 
4355         scheme = INTERNET_GetSchemeString(nScheme);
4356         dwLen = strlenW(scheme);
4357         memcpy(lpszUrl, scheme, dwLen * sizeof(WCHAR));
4358         lpszUrl += dwLen;
4359     }
4360 
4361     /* all schemes are followed by at least a colon */
4362     *lpszUrl = ':';
4363     lpszUrl++;
4364 
4365     if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
4366     {
4367         memcpy(lpszUrl, slashSlashW, sizeof(slashSlashW));
4368         lpszUrl += ARRAY_SIZE(slashSlashW);
4369     }
4370 
4371     if (lpUrlComponents->lpszUserName)
4372     {
4373         dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
4374         memcpy(lpszUrl, lpUrlComponents->lpszUserName, dwLen * sizeof(WCHAR));
4375         lpszUrl += dwLen;
4376 
4377         if (lpUrlComponents->lpszPassword)
4378         {
4379             *lpszUrl = ':';
4380             lpszUrl++;
4381 
4382             dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Password);
4383             memcpy(lpszUrl, lpUrlComponents->lpszPassword, dwLen * sizeof(WCHAR));
4384             lpszUrl += dwLen;
4385         }
4386 
4387         *lpszUrl = '@';
4388         lpszUrl++;
4389     }
4390 
4391     if (lpUrlComponents->lpszHostName)
4392     {
4393         dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
4394         memcpy(lpszUrl, lpUrlComponents->lpszHostName, dwLen * sizeof(WCHAR));
4395         lpszUrl += dwLen;
4396 
4397         if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
4398         {
4399             *lpszUrl = ':';
4400             lpszUrl++;
4401             lpszUrl += sprintfW(lpszUrl, fmtW, lpUrlComponents->nPort);
4402         }
4403 
4404         /* add slash between hostname and path if necessary */
4405         if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
4406         {
4407             *lpszUrl = '/';
4408             lpszUrl++;
4409         }
4410     }
4411 
4412     if (lpUrlComponents->lpszUrlPath)
4413     {
4414         dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
4415         memcpy(lpszUrl, lpUrlComponents->lpszUrlPath, dwLen * sizeof(WCHAR));
4416         lpszUrl += dwLen;
4417     }
4418 
4419     if (lpUrlComponents->lpszExtraInfo)
4420     {
4421         dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);
4422         memcpy(lpszUrl, lpUrlComponents->lpszExtraInfo, dwLen * sizeof(WCHAR));
4423         lpszUrl += dwLen;
4424     }
4425 
4426     *lpszUrl = '\0';
4427 
4428     return TRUE;
4429 }
4430 
4431 /***********************************************************************
4432  *      InternetConfirmZoneCrossingA (WININET.@)
4433  *
4434  */
4435 DWORD WINAPI InternetConfirmZoneCrossingA( HWND hWnd, LPSTR szUrlPrev, LPSTR szUrlNew, BOOL bPost )
4436 {
4437     FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_a(szUrlPrev), debugstr_a(szUrlNew), bPost);
4438     return ERROR_SUCCESS;
4439 }
4440 
4441 /***********************************************************************
4442  *      InternetConfirmZoneCrossingW (WININET.@)
4443  *
4444  */
4445 DWORD WINAPI InternetConfirmZoneCrossingW( HWND hWnd, LPWSTR szUrlPrev, LPWSTR szUrlNew, BOOL bPost )
4446 {
4447     FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_w(szUrlPrev), debugstr_w(szUrlNew), bPost);
4448     return ERROR_SUCCESS;
4449 }
4450 
4451 static DWORD zone_preference = 3;
4452 
4453 /***********************************************************************
4454  *      PrivacySetZonePreferenceW (WININET.@)
4455  */
4456 DWORD WINAPI PrivacySetZonePreferenceW( DWORD zone, DWORD type, DWORD template, LPCWSTR preference )
4457 {
4458     FIXME( "%x %x %x %s: stub\n", zone, type, template, debugstr_w(preference) );
4459 
4460     zone_preference = template;
4461     return 0;
4462 }
4463 
4464 /***********************************************************************
4465  *      PrivacyGetZonePreferenceW (WININET.@)
4466  */
4467 DWORD WINAPI PrivacyGetZonePreferenceW( DWORD zone, DWORD type, LPDWORD template,
4468                                         LPWSTR preference, LPDWORD length )
4469 {
4470     FIXME( "%x %x %p %p %p: stub\n", zone, type, template, preference, length );
4471 
4472     if (template) *template = zone_preference;
4473     return 0;
4474 }
4475 
4476 /***********************************************************************
4477  *      InternetGetSecurityInfoByURLA (WININET.@)
4478  */
4479 BOOL WINAPI InternetGetSecurityInfoByURLA(LPSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
4480 {
4481     WCHAR *url;
4482     BOOL res;
4483 
4484     TRACE("(%s %p %p)\n", debugstr_a(lpszURL), ppCertChain, pdwSecureFlags);
4485 
4486     url = heap_strdupAtoW(lpszURL);
4487     if(!url)
4488         return FALSE;
4489 
4490     res = InternetGetSecurityInfoByURLW(url, ppCertChain, pdwSecureFlags);
4491     heap_free(url);
4492     return res;
4493 }
4494 
4495 /***********************************************************************
4496  *      InternetGetSecurityInfoByURLW (WININET.@)
4497  */
4498 BOOL WINAPI InternetGetSecurityInfoByURLW(LPCWSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
4499 {
4500     URL_COMPONENTSW url = {sizeof(url)};
4501     server_t *server;
4502     BOOL res;
4503 
4504     TRACE("(%s %p %p)\n", debugstr_w(lpszURL), ppCertChain, pdwSecureFlags);
4505 
4506     if (!ppCertChain && !pdwSecureFlags) {
4507         SetLastError(ERROR_INVALID_PARAMETER);
4508         return FALSE;
4509     }
4510 
4511     url.dwHostNameLength = 1;
4512     res = InternetCrackUrlW(lpszURL, 0, 0, &url);
4513     if(!res || url.nScheme != INTERNET_SCHEME_HTTPS) {
4514         SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
4515         return FALSE;
4516     }
4517 
4518     server = get_server(substr(url.lpszHostName, url.dwHostNameLength), url.nPort, TRUE, FALSE);
4519     if(!server) {
4520         SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
4521         return FALSE;
4522     }
4523 
4524     if(server->cert_chain) {
4525         if(pdwSecureFlags)
4526             *pdwSecureFlags = server->security_flags & _SECURITY_ERROR_FLAGS_MASK;
4527 
4528         if(ppCertChain && !(*ppCertChain = CertDuplicateCertificateChain(server->cert_chain)))
4529             res = FALSE;
4530     }else {
4531         SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
4532         res = FALSE;
4533     }
4534 
4535     server_release(server);
4536     return res;
4537 }
4538 
4539 DWORD WINAPI InternetDialA( HWND hwndParent, LPSTR lpszConnectoid, DWORD dwFlags,
4540                             DWORD_PTR* lpdwConnection, DWORD dwReserved )
4541 {
4542     FIXME("(%p, %p, 0x%08x, %p, 0x%08x) stub\n", hwndParent, lpszConnectoid, dwFlags,
4543           lpdwConnection, dwReserved);
4544     return ERROR_SUCCESS;
4545 }
4546 
4547 DWORD WINAPI InternetDialW( HWND hwndParent, LPWSTR lpszConnectoid, DWORD dwFlags,
4548                             DWORD_PTR* lpdwConnection, DWORD dwReserved )
4549 {
4550     FIXME("(%p, %p, 0x%08x, %p, 0x%08x) stub\n", hwndParent, lpszConnectoid, dwFlags,
4551           lpdwConnection, dwReserved);
4552     return ERROR_SUCCESS;
4553 }
4554 
4555 BOOL WINAPI InternetGoOnlineA( LPSTR lpszURL, HWND hwndParent, DWORD dwReserved )
4556 {
4557     FIXME("(%s, %p, 0x%08x) stub\n", debugstr_a(lpszURL), hwndParent, dwReserved);
4558     return TRUE;
4559 }
4560 
4561 BOOL WINAPI InternetGoOnlineW( LPWSTR lpszURL, HWND hwndParent, DWORD dwReserved )
4562 {
4563     FIXME("(%s, %p, 0x%08x) stub\n", debugstr_w(lpszURL), hwndParent, dwReserved);
4564     return TRUE;
4565 }
4566 
4567 DWORD WINAPI InternetHangUp( DWORD_PTR dwConnection, DWORD dwReserved )
4568 {
4569     FIXME("(0x%08lx, 0x%08x) stub\n", dwConnection, dwReserved);
4570     return ERROR_SUCCESS;
4571 }
4572 
4573 BOOL WINAPI CreateMD5SSOHash( PWSTR pszChallengeInfo, PWSTR pwszRealm, PWSTR pwszTarget,
4574                               PBYTE pbHexHash )
4575 {
4576     FIXME("(%s, %s, %s, %p) stub\n", debugstr_w(pszChallengeInfo), debugstr_w(pwszRealm),
4577           debugstr_w(pwszTarget), pbHexHash);
4578     return FALSE;
4579 }
4580 
4581 BOOL WINAPI ResumeSuspendedDownload( HINTERNET hInternet, DWORD dwError )
4582 {
4583     FIXME("(%p, 0x%08x) stub\n", hInternet, dwError);
4584     return FALSE;
4585 }
4586 
4587 BOOL WINAPI InternetQueryFortezzaStatus(DWORD *a, DWORD_PTR b)
4588 {
4589     FIXME("(%p, %08lx) stub\n", a, b);
4590     return FALSE;
4591 }
4592 
4593 DWORD WINAPI ShowClientAuthCerts(HWND parent)
4594 {
4595     FIXME("%p: stub\n", parent);
4596     return 0;
4597 }
4598