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