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