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