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