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