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