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