Porting fixes.
[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  *
9  * Ulrich Czekalla
10  * Aric Stewart
11  * David Hammerton
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 #include "config.h"
29 #include "wine/port.h"
30
31 #define MAXHOSTNAME 100 /* from http.c */
32
33 #include <string.h>
34 #include <stdarg.h>
35 #include <stdio.h>
36 #include <sys/types.h>
37 #ifdef HAVE_SYS_SOCKET_H
38 # include <sys/socket.h>
39 #endif
40 #ifdef HAVE_SYS_TIME_H
41 # include <sys/time.h>
42 #endif
43 #include <stdlib.h>
44 #include <ctype.h>
45 #ifdef HAVE_UNISTD_H
46 # include <unistd.h>
47 #endif
48
49 #include "ntstatus.h"
50 #include "windef.h"
51 #include "winbase.h"
52 #include "winreg.h"
53 #include "wininet.h"
54 #include "winnls.h"
55 #include "wine/debug.h"
56 #include "winerror.h"
57 #define NO_SHLWAPI_STREAM
58 #include "shlwapi.h"
59
60 #include "wine/exception.h"
61 #include "excpt.h"
62
63 #include "internet.h"
64
65 #include "wine/unicode.h"
66
67 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
68
69 #define MAX_IDLE_WORKER 1000*60*1
70 #define MAX_WORKER_THREADS 10
71 #define RESPONSE_TIMEOUT        30
72
73 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
74 (LPWININETAPPINFOA)(((LPWININETFTPSESSIONA)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
75
76 /* filter for page-fault exceptions */
77 static WINE_EXCEPTION_FILTER(page_fault)
78 {
79     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
80         GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION)
81         return EXCEPTION_EXECUTE_HANDLER;
82     return EXCEPTION_CONTINUE_SEARCH;
83 }
84
85 typedef struct
86 {
87     DWORD  dwError;
88     CHAR   response[MAX_REPLY_LEN];
89 } WITHREADERROR, *LPWITHREADERROR;
90
91 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData);
92 VOID INTERNET_ExecuteWork();
93
94 DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
95 DWORD dwNumThreads;
96 DWORD dwNumIdleThreads;
97 DWORD dwNumJobs;
98 HANDLE hEventArray[2];
99 #define hQuitEvent hEventArray[0]
100 #define hWorkEvent hEventArray[1]
101 CRITICAL_SECTION csQueue;
102 LPWORKREQUEST lpHeadWorkQueue;
103 LPWORKREQUEST lpWorkQueueTail;
104
105 /***********************************************************************
106  * DllMain [Internal] Initializes the internal 'WININET.DLL'.
107  *
108  * PARAMS
109  *     hinstDLL    [I] handle to the DLL's instance
110  *     fdwReason   [I]
111  *     lpvReserved [I] reserved, must be NULL
112  *
113  * RETURNS
114  *     Success: TRUE
115  *     Failure: FALSE
116  */
117
118 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
119 {
120     TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
121
122     switch (fdwReason) {
123         case DLL_PROCESS_ATTACH:
124
125             g_dwTlsErrIndex = TlsAlloc();
126
127             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
128                 return FALSE;
129
130             hQuitEvent = CreateEventA(0, TRUE, FALSE, NULL);
131             hWorkEvent = CreateEventA(0, FALSE, FALSE, NULL);
132             InitializeCriticalSection(&csQueue);
133
134             dwNumThreads = 0;
135             dwNumIdleThreads = 0;
136             dwNumJobs = 0;
137
138         case DLL_THREAD_ATTACH:
139             {
140                 LPWITHREADERROR lpwite = HeapAlloc(GetProcessHeap(), 0, sizeof(WITHREADERROR));
141                 if (NULL == lpwite)
142                     return FALSE;
143
144                 TlsSetValue(g_dwTlsErrIndex, (LPVOID)lpwite);
145             }
146             break;
147
148         case DLL_THREAD_DETACH:
149             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
150                         {
151                                 LPVOID lpwite = TlsGetValue(g_dwTlsErrIndex);
152                                 if (lpwite)
153                    HeapFree(GetProcessHeap(), 0, lpwite);
154                         }
155             break;
156
157         case DLL_PROCESS_DETACH:
158
159             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
160             {
161                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
162                 TlsFree(g_dwTlsErrIndex);
163             }
164
165             SetEvent(hQuitEvent);
166
167             CloseHandle(hQuitEvent);
168             CloseHandle(hWorkEvent);
169             DeleteCriticalSection(&csQueue);
170             break;
171     }
172
173     return TRUE;
174 }
175
176
177 /***********************************************************************
178  *           InternetInitializeAutoProxyDll   (WININET.@)
179  *
180  * Setup the internal proxy
181  *
182  * PARAMETERS
183  *     dwReserved
184  *
185  * RETURNS
186  *     FALSE on failure
187  *
188  */
189 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
190 {
191     FIXME("STUB\n");
192     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
193     return FALSE;
194 }
195
196 /***********************************************************************
197  *           DetectAutoProxyUrl   (WININET.@)
198  *
199  * Auto detect the proxy url
200  *
201  * RETURNS
202  *     FALSE on failure
203  *
204  */
205 BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
206         DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
207 {
208     FIXME("STUB\n");
209     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
210     return FALSE;
211 }
212
213
214 /***********************************************************************
215  *           INTERNET_ConfigureProxyFromReg
216  *
217  * FIXME:
218  * The proxy may be specified in the form 'http=proxy.my.org'
219  * Presumably that means there can be ftp=ftpproxy.my.org too.
220  */
221 static BOOL INTERNET_ConfigureProxyFromReg( LPWININETAPPINFOA lpwai )
222 {
223     HKEY key;
224     DWORD r, keytype, len, enabled;
225     LPSTR lpszInternetSettings =
226         "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
227
228     r = RegOpenKeyA(HKEY_CURRENT_USER, lpszInternetSettings, &key);
229     if ( r != ERROR_SUCCESS )
230         return FALSE;
231
232     len = sizeof enabled;
233     r = RegQueryValueExA( key, "ProxyEnable", NULL, &keytype,
234                           (BYTE*)&enabled, &len);
235     if( (r == ERROR_SUCCESS) && enabled )
236     {
237         TRACE("Proxy is enabled.\n");
238
239         /* figure out how much memory the proxy setting takes */
240         r = RegQueryValueExA( key, "ProxyServer", NULL, &keytype, 
241                               NULL, &len);
242         if( (r == ERROR_SUCCESS) && len && (keytype == REG_SZ) )
243         {
244             LPSTR szProxy, p, szHttp = "http=";
245
246             szProxy=HeapAlloc( GetProcessHeap(), 0, len+1 );
247             RegQueryValueExA( key, "ProxyServer", NULL, &keytype,
248                               (BYTE*)szProxy, &len);
249
250             /* find the http proxy, and strip away everything else */
251             p = strstr( szProxy, szHttp );
252             if( p )
253             {
254                  p += strlen(szHttp);
255                  strcpy( szProxy, p );
256             }
257             p = strchr( szProxy, ' ' );
258             if( p )
259                 *p = 0;
260
261             lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
262             lpwai->lpszProxy = szProxy;
263
264             TRACE("http proxy = %s\n", lpwai->lpszProxy);
265         }
266         else
267             ERR("Couldn't read proxy server settings.\n");
268     }
269     else
270         TRACE("Proxy is not enabled.\n");
271     RegCloseKey(key);
272
273     return enabled;
274 }
275
276 /***********************************************************************
277  *           InternetOpenA   (WININET.@)
278  *
279  * Per-application initialization of wininet
280  *
281  * RETURNS
282  *    HINTERNET on success
283  *    NULL on failure
284  *
285  */
286 HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
287     LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
288 {
289     LPWININETAPPINFOA lpwai = NULL;
290
291     TRACE("(%s, %li, %s, %s, %li)\n", debugstr_a(lpszAgent), dwAccessType,
292          debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
293
294     /* Clear any error information */
295     INTERNET_SetLastError(0);
296
297     lpwai = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETAPPINFOA));
298     if (NULL == lpwai)
299     {
300         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
301         return NULL;
302     }
303  
304     memset(lpwai, 0, sizeof(WININETAPPINFOA));
305     lpwai->hdr.htype = WH_HINIT;
306     lpwai->hdr.lpwhparent = NULL;
307     lpwai->hdr.dwFlags = dwFlags;
308     lpwai->dwAccessType = dwAccessType;
309     lpwai->lpszProxyUsername = NULL;
310     lpwai->lpszProxyPassword = NULL;
311
312     if (NULL != lpszAgent)
313     {
314         lpwai->lpszAgent = HeapAlloc( GetProcessHeap(),0,
315                                       strlen(lpszAgent)+1);
316         if (lpwai->lpszAgent)
317             strcpy( lpwai->lpszAgent, lpszAgent );
318     }
319     if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
320         INTERNET_ConfigureProxyFromReg( lpwai );
321     else if (NULL != lpszProxy)
322     {
323         lpwai->lpszProxy = HeapAlloc( GetProcessHeap(), 0,
324                                       strlen(lpszProxy)+1);
325         if (lpwai->lpszProxy)
326             strcpy( lpwai->lpszProxy, lpszProxy );
327     }
328
329     if (NULL != lpszProxyBypass)
330     {
331         lpwai->lpszProxyBypass = HeapAlloc( GetProcessHeap(), 0,
332                                             strlen(lpszProxyBypass)+1);
333         if (lpwai->lpszProxyBypass)
334             strcpy( lpwai->lpszProxyBypass, lpszProxyBypass );
335     }
336
337     TRACE("returning %p\n", (HINTERNET)lpwai);
338     return (HINTERNET)lpwai;
339 }
340
341
342 /***********************************************************************
343  *           InternetOpenW   (WININET.@)
344  *
345  * Per-application initialization of wininet
346  *
347  * RETURNS
348  *    HINTERNET on success
349  *    NULL on failure
350  *
351  */
352 HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
353     LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
354 {
355     HINTERNET rc = (HINTERNET)NULL;
356     INT lenAgent = WideCharToMultiByte(CP_ACP, 0, lpszAgent, -1, NULL, 0, NULL, NULL);
357     INT lenProxy = WideCharToMultiByte(CP_ACP, 0, lpszProxy, -1, NULL, 0, NULL, NULL);
358     INT lenBypass = WideCharToMultiByte(CP_ACP, 0, lpszProxyBypass, -1, NULL, 0, NULL, NULL);
359     CHAR *szAgent = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenAgent*sizeof(CHAR));
360     CHAR *szProxy = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenProxy*sizeof(CHAR));
361     CHAR *szBypass = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenBypass*sizeof(CHAR));
362
363     if (!szAgent || !szProxy || !szBypass)
364     {
365         if (szAgent)
366             HeapFree(GetProcessHeap(), 0, szAgent);
367         if (szProxy)
368             HeapFree(GetProcessHeap(), 0, szProxy);
369         if (szBypass)
370             HeapFree(GetProcessHeap(), 0, szBypass);
371         return (HINTERNET)NULL;
372     }
373
374     WideCharToMultiByte(CP_ACP, 0, lpszAgent, -1, szAgent, lenAgent,
375         NULL, NULL);
376     WideCharToMultiByte(CP_ACP, 0, lpszProxy, -1, szProxy, lenProxy,
377         NULL, NULL);
378     WideCharToMultiByte(CP_ACP, 0, lpszProxyBypass, -1, szBypass, lenBypass,
379         NULL, NULL);
380
381     rc = InternetOpenA(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
382
383     HeapFree(GetProcessHeap(), 0, szAgent);
384     HeapFree(GetProcessHeap(), 0, szProxy);
385     HeapFree(GetProcessHeap(), 0, szBypass);
386
387     return rc;
388 }
389
390 /***********************************************************************
391  *           InternetGetLastResponseInfoA (WININET.@)
392  *
393  * Return last wininet error description on the calling thread
394  *
395  * RETURNS
396  *    TRUE on success of writing to buffer
397  *    FALSE on failure
398  *
399  */
400 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
401     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
402 {
403     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
404
405     TRACE("\n");
406
407     *lpdwError = lpwite->dwError;
408     if (lpwite->dwError)
409     {
410         strncpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
411         *lpdwBufferLength = strlen(lpszBuffer);
412     }
413     else
414         *lpdwBufferLength = 0;
415
416     return TRUE;
417 }
418
419
420 /***********************************************************************
421  *           InternetGetConnectedState (WININET.@)
422  *
423  * Return connected state
424  *
425  * RETURNS
426  *    TRUE if connected
427  *    if lpdwStatus is not null, return the status (off line,
428  *    modem, lan...) in it.
429  *    FALSE if not connected
430  */
431 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
432 {
433     if (lpdwStatus) {
434         FIXME("always returning LAN connection.\n");
435         *lpdwStatus = INTERNET_CONNECTION_LAN;
436     }
437     return TRUE;
438 }
439
440 /***********************************************************************
441  *           InternetGetConnectedStateEx (WININET.@)
442  *
443  * Return connected state
444  *
445  * RETURNS
446  *    TRUE if connected
447  *    if lpdwStatus is not null, return the status (off line,
448  *    modem, lan...) in it.
449  *    FALSE if not connected
450  */
451 BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
452                                          DWORD dwNameLen, DWORD dwReserved)
453 {
454     /* Must be zero */
455     if(dwReserved)
456         return FALSE;
457
458     if (lpdwStatus) {
459         FIXME("always returning LAN connection.\n");
460         *lpdwStatus = INTERNET_CONNECTION_LAN;
461     }
462     return TRUE;
463 }
464
465 /***********************************************************************
466  *           InternetConnectA (WININET.@)
467  *
468  * Open a ftp, gopher or http session
469  *
470  * RETURNS
471  *    HINTERNET a session handle on success
472  *    NULL on failure
473  *
474  */
475 HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
476     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
477     LPCSTR lpszUserName, LPCSTR lpszPassword,
478     DWORD dwService, DWORD dwFlags, DWORD dwContext)
479 {
480     HINTERNET rc = (HINTERNET) NULL;
481
482     TRACE("(%p, %s, %i, %s, %s, %li, %li, %li)\n", hInternet, debugstr_a(lpszServerName),
483           nServerPort, debugstr_a(lpszUserName), debugstr_a(lpszPassword),
484           dwService, dwFlags, dwContext);
485
486     /* Clear any error information */
487     INTERNET_SetLastError(0);
488
489     switch (dwService)
490     {
491         case INTERNET_SERVICE_FTP:
492             rc = FTP_Connect(hInternet, lpszServerName, nServerPort,
493             lpszUserName, lpszPassword, dwFlags, dwContext);
494             break;
495
496         case INTERNET_SERVICE_HTTP:
497             rc = HTTP_Connect(hInternet, lpszServerName, nServerPort,
498             lpszUserName, lpszPassword, dwFlags, dwContext);
499             break;
500
501         case INTERNET_SERVICE_GOPHER:
502         default:
503             break;
504     }
505
506     TRACE("returning %p\n", rc);
507     return rc;
508 }
509
510
511 /***********************************************************************
512  *           InternetConnectW (WININET.@)
513  *
514  * Open a ftp, gopher or http session
515  *
516  * RETURNS
517  *    HINTERNET a session handle on success
518  *    NULL on failure
519  *
520  */
521 HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
522     LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
523     LPCWSTR lpszUserName, LPCWSTR lpszPassword,
524     DWORD dwService, DWORD dwFlags, DWORD dwContext)
525 {
526     HINTERNET rc = (HINTERNET)NULL;
527     INT lenServer = 0;
528     INT lenUser = 0;
529     INT lenPass = 0;
530     CHAR *szServerName = NULL;
531     CHAR *szUserName = NULL;
532     CHAR *szPassword = NULL;
533
534     if (lpszServerName)
535     {
536         lenServer = WideCharToMultiByte(CP_ACP, 0, lpszServerName, -1, NULL, 0,
537                             NULL, NULL);
538         szServerName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenServer*sizeof(CHAR));
539         WideCharToMultiByte(CP_ACP, 0, lpszServerName, -1, szServerName, lenServer,
540                             NULL, NULL);
541     }
542     if (lpszUserName)
543     {
544         lenUser = WideCharToMultiByte(CP_ACP, 0, lpszUserName, -1, NULL, 0,
545                             NULL, NULL);
546         szUserName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUser*sizeof(CHAR));
547         WideCharToMultiByte(CP_ACP, 0, lpszUserName, -1, szUserName, lenUser,
548                             NULL, NULL);
549     }
550     if (lpszPassword)
551     {
552         lenPass = WideCharToMultiByte(CP_ACP, 0, lpszPassword, -1, NULL, 0,
553                             NULL, NULL);
554         szPassword = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenPass*sizeof(CHAR));
555         WideCharToMultiByte(CP_ACP, 0, lpszPassword, -1, szPassword, lenPass,
556                             NULL, NULL);
557     }
558
559
560     rc = InternetConnectA(hInternet, szServerName, nServerPort,
561         szUserName, szPassword, dwService, dwFlags, dwContext);
562
563     if (szServerName) HeapFree(GetProcessHeap(), 0, szServerName);
564     if (szUserName) HeapFree(GetProcessHeap(), 0, szUserName);
565     if (szPassword) HeapFree(GetProcessHeap(), 0, szPassword);
566     return rc;
567 }
568
569
570 /***********************************************************************
571  *           InternetFindNextFileA (WININET.@)
572  *
573  * Continues a file search from a previous call to FindFirstFile
574  *
575  * RETURNS
576  *    TRUE on success
577  *    FALSE on failure
578  *
579  */
580 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
581 {
582     LPWININETAPPINFOA hIC = NULL;
583     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
584
585     TRACE("\n");
586
587     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
588     {
589         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
590         return FALSE;
591     }
592
593     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
594     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
595     {
596         WORKREQUEST workRequest;
597         struct WORKREQ_INTERNETFINDNEXTA *req;
598
599         workRequest.asyncall = INTERNETFINDNEXTA;
600         workRequest.handle = hFind;
601         req = &workRequest.u.InternetFindNextA;
602         req->lpFindFileData = lpvFindData;
603
604         return INTERNET_AsyncCall(&workRequest);
605     }
606     else
607     {
608         return INTERNET_FindNextFileA(hFind, lpvFindData);
609     }
610 }
611
612 /***********************************************************************
613  *           INTERNET_FindNextFileA (Internal)
614  *
615  * Continues a file search from a previous call to FindFirstFile
616  *
617  * RETURNS
618  *    TRUE on success
619  *    FALSE on failure
620  *
621  */
622 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
623 {
624     BOOL bSuccess = TRUE;
625     LPWININETAPPINFOA hIC = NULL;
626     LPWIN32_FIND_DATAA lpFindFileData;
627     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
628
629     TRACE("\n");
630
631     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
632     {
633         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
634         return FALSE;
635     }
636
637     /* Clear any error information */
638     INTERNET_SetLastError(0);
639
640     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
641     {
642         FIXME("Only FTP find next supported\n");
643         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
644         return FALSE;
645     }
646
647     TRACE("index(%d) size(%ld)\n", lpwh->index, lpwh->size);
648
649     lpFindFileData = (LPWIN32_FIND_DATAA) lpvFindData;
650     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
651
652     if (lpwh->index >= lpwh->size)
653     {
654         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
655         bSuccess = FALSE;
656         goto lend;
657     }
658
659     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
660     lpwh->index++;
661
662     TRACE("\nName: %s\nSize: %ld\n", lpFindFileData->cFileName, lpFindFileData->nFileSizeLow);
663
664 lend:
665
666     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
667     if (hIC->lpfnStatusCB)
668     {
669         INTERNET_ASYNC_RESULT iar;
670
671         iar.dwResult = (DWORD)bSuccess;
672         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
673                                                INTERNET_GetLastError();
674
675         SendAsyncCallback(hIC, hFind, lpwh->hdr.dwContext,
676                       INTERNET_STATUS_REQUEST_COMPLETE, &iar,
677                        sizeof(INTERNET_ASYNC_RESULT));
678     }
679
680     return bSuccess;
681 }
682
683
684 /***********************************************************************
685  *           INTERNET_CloseHandle (internal)
686  *
687  * Close internet handle
688  *
689  * RETURNS
690  *    Void
691  *
692  */
693 VOID INTERNET_CloseHandle(LPWININETAPPINFOA lpwai)
694 {
695     TRACE("%p\n",lpwai);
696
697     SendAsyncCallback(lpwai, lpwai, lpwai->hdr.dwContext,
698                       INTERNET_STATUS_HANDLE_CLOSING, lpwai,
699                       sizeof(HINTERNET));
700
701     if (lpwai->lpszAgent)
702         HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
703
704     if (lpwai->lpszProxy)
705         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
706
707     if (lpwai->lpszProxyBypass)
708         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
709
710     if (lpwai->lpszProxyUsername)
711         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyUsername);
712
713     if (lpwai->lpszProxyPassword)
714         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyPassword);
715
716     HeapFree(GetProcessHeap(), 0, lpwai);
717 }
718
719
720 /***********************************************************************
721  *           InternetCloseHandle (WININET.@)
722  *
723  * Generic close handle function
724  *
725  * RETURNS
726  *    TRUE on success
727  *    FALSE on failure
728  *
729  */
730 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
731 {
732     BOOL retval;
733     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hInternet;
734
735     TRACE("%p\n",hInternet);
736     if (NULL == lpwh)
737         return FALSE;
738
739     __TRY {
740         /* Clear any error information */
741         INTERNET_SetLastError(0);
742         retval = FALSE;
743
744         switch (lpwh->htype)
745         {
746             case WH_HINIT:
747                 INTERNET_CloseHandle((LPWININETAPPINFOA) lpwh);
748                 retval = TRUE;
749                 break;
750
751             case WH_HHTTPSESSION:
752                 HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
753                 retval = TRUE;
754                 break;
755
756             case WH_HHTTPREQ:
757                 HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
758                 retval = TRUE;
759                 break;
760
761             case WH_HFTPSESSION:
762                 retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
763                 break;
764
765             case WH_HFINDNEXT:
766                 retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
767                 break;
768
769             case WH_HFILE:
770                 retval = FTP_CloseFileTransferHandle((LPWININETFILE) lpwh);
771                 break;
772                 
773             default:
774                 break;
775         }
776     } __EXCEPT(page_fault) {
777         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
778         return FALSE;
779     }
780     __ENDTRY
781
782     return retval;
783 }
784
785
786 /***********************************************************************
787  *           ConvertUrlComponentValue (Internal)
788  *
789  * Helper function for InternetCrackUrlW
790  *
791  */
792 void ConvertUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen,
793                               LPWSTR lpwszComponent, DWORD dwwComponentLen,
794                               LPCSTR lpszStart,
795                               LPCWSTR lpwszStart)
796 {
797     if (*dwComponentLen != 0)
798     {
799         int nASCIILength=WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,NULL,0,NULL,NULL);
800         if (*lppszComponent == NULL)
801         {
802             int nASCIIOffset=WideCharToMultiByte(CP_ACP,0,lpwszStart,lpwszComponent-lpwszStart,NULL,0,NULL,NULL);
803             *lppszComponent = (LPSTR)lpszStart+nASCIIOffset;
804             *dwComponentLen = nASCIILength;
805         }
806         else
807         {
808             INT ncpylen = min((*dwComponentLen)-1, nASCIILength);
809             WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,*lppszComponent,ncpylen+1,NULL,NULL);
810             (*lppszComponent)[ncpylen]=0;
811             *dwComponentLen = ncpylen;
812         }
813     }
814 }
815
816
817 /***********************************************************************
818  *           InternetCrackUrlA (WININET.@)
819  *
820  * Break up URL into its components
821  *
822  * TODO: Handle dwFlags
823  *
824  * RETURNS
825  *    TRUE on success
826  *    FALSE on failure
827  *
828  */
829 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
830     LPURL_COMPONENTSA lpUrlComponents)
831 {
832   DWORD nLength;
833   URL_COMPONENTSW UCW;
834   WCHAR* lpwszUrl;
835   if(dwUrlLength==0)
836       dwUrlLength=strlen(lpszUrl);
837   lpwszUrl=HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(dwUrlLength+1));
838   memset(lpwszUrl,0,sizeof(WCHAR)*(dwUrlLength+1));
839   nLength=MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,lpwszUrl,dwUrlLength+1);
840   memset(&UCW,0,sizeof(UCW));
841   if(lpUrlComponents->dwHostNameLength!=0)
842       UCW.dwHostNameLength=1;
843   if(lpUrlComponents->dwUserNameLength!=0)
844       UCW.dwUserNameLength=1;
845   if(lpUrlComponents->dwPasswordLength!=0)
846       UCW.dwPasswordLength=1;
847   if(lpUrlComponents->dwUrlPathLength!=0)
848       UCW.dwUrlPathLength=1;
849   if(lpUrlComponents->dwSchemeLength!=0)
850       UCW.dwSchemeLength=1;
851   if(lpUrlComponents->dwExtraInfoLength!=0)
852       UCW.dwExtraInfoLength=1;
853   if(!InternetCrackUrlW(lpwszUrl,nLength,dwFlags,&UCW))
854   {
855       HeapFree(GetProcessHeap(), 0, lpwszUrl);
856       return FALSE;
857   }
858   ConvertUrlComponentValue(&lpUrlComponents->lpszHostName, &lpUrlComponents->dwHostNameLength,
859                            UCW.lpszHostName, UCW.dwHostNameLength,
860                            lpszUrl, lpwszUrl);
861   ConvertUrlComponentValue(&lpUrlComponents->lpszUserName, &lpUrlComponents->dwUserNameLength,
862                            UCW.lpszUserName, UCW.dwUserNameLength,
863                            lpszUrl, lpwszUrl);
864   ConvertUrlComponentValue(&lpUrlComponents->lpszPassword, &lpUrlComponents->dwPasswordLength,
865                            UCW.lpszPassword, UCW.dwPasswordLength,
866                            lpszUrl, lpwszUrl);
867   ConvertUrlComponentValue(&lpUrlComponents->lpszUrlPath, &lpUrlComponents->dwUrlPathLength,
868                            UCW.lpszUrlPath, UCW.dwUrlPathLength,
869                            lpszUrl, lpwszUrl);
870   ConvertUrlComponentValue(&lpUrlComponents->lpszScheme, &lpUrlComponents->dwSchemeLength,
871                            UCW.lpszScheme, UCW.dwSchemeLength,
872                            lpszUrl, lpwszUrl);
873   ConvertUrlComponentValue(&lpUrlComponents->lpszExtraInfo, &lpUrlComponents->dwExtraInfoLength,
874                            UCW.lpszExtraInfo, UCW.dwExtraInfoLength,
875                            lpszUrl, lpwszUrl);
876   lpUrlComponents->nScheme=UCW.nScheme;
877   lpUrlComponents->nPort=UCW.nPort;
878   HeapFree(GetProcessHeap(), 0, lpwszUrl);
879   
880   TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", lpszUrl,
881           debugstr_an(lpUrlComponents->lpszScheme,lpUrlComponents->dwSchemeLength),
882           debugstr_an(lpUrlComponents->lpszHostName,lpUrlComponents->dwHostNameLength),
883           debugstr_an(lpUrlComponents->lpszUrlPath,lpUrlComponents->dwUrlPathLength),
884           debugstr_an(lpUrlComponents->lpszExtraInfo,lpUrlComponents->dwExtraInfoLength));
885
886   return TRUE;
887 }
888
889 /***********************************************************************
890  *           GetInternetSchemeW (internal)
891  *
892  * Get scheme of url
893  *
894  * RETURNS
895  *    scheme on success
896  *    INTERNET_SCHEME_UNKNOWN on failure
897  *
898  */
899 INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, INT nMaxCmp)
900 {
901     INTERNET_SCHEME iScheme=INTERNET_SCHEME_UNKNOWN;
902     WCHAR lpszFtp[]={'f','t','p',0};
903     WCHAR lpszGopher[]={'g','o','p','h','e','r',0};
904     WCHAR lpszHttp[]={'h','t','t','p',0};
905     WCHAR lpszHttps[]={'h','t','t','p','s',0};
906     WCHAR lpszFile[]={'f','i','l','e',0};
907     WCHAR lpszNews[]={'n','e','w','s',0};
908     WCHAR lpszMailto[]={'m','a','i','l','t','o',0};
909     WCHAR lpszRes[]={'r','e','s',0};
910     WCHAR* tempBuffer=NULL;
911     TRACE("\n");
912     if(lpszScheme==NULL)
913         return INTERNET_SCHEME_UNKNOWN;
914
915     tempBuffer=HeapAlloc(GetProcessHeap(),0,(nMaxCmp+1)*sizeof(WCHAR));
916     strncpyW(tempBuffer,lpszScheme,nMaxCmp);
917     tempBuffer[nMaxCmp]=0;
918     strlwrW(tempBuffer);
919     if (nMaxCmp==strlenW(lpszFtp) && !strncmpW(lpszFtp, tempBuffer, nMaxCmp))
920         iScheme=INTERNET_SCHEME_FTP;
921     else if (nMaxCmp==strlenW(lpszGopher) && !strncmpW(lpszGopher, tempBuffer, nMaxCmp))
922         iScheme=INTERNET_SCHEME_GOPHER;
923     else if (nMaxCmp==strlenW(lpszHttp) && !strncmpW(lpszHttp, tempBuffer, nMaxCmp))
924         iScheme=INTERNET_SCHEME_HTTP;
925     else if (nMaxCmp==strlenW(lpszHttps) && !strncmpW(lpszHttps, tempBuffer, nMaxCmp))
926         iScheme=INTERNET_SCHEME_HTTPS;
927     else if (nMaxCmp==strlenW(lpszFile) && !strncmpW(lpszFile, tempBuffer, nMaxCmp))
928         iScheme=INTERNET_SCHEME_FILE;
929     else if (nMaxCmp==strlenW(lpszNews) && !strncmpW(lpszNews, tempBuffer, nMaxCmp))
930         iScheme=INTERNET_SCHEME_NEWS;
931     else if (nMaxCmp==strlenW(lpszMailto) && !strncmpW(lpszMailto, tempBuffer, nMaxCmp))
932         iScheme=INTERNET_SCHEME_MAILTO;
933     else if (nMaxCmp==strlenW(lpszRes) && !strncmpW(lpszRes, tempBuffer, nMaxCmp))
934         iScheme=INTERNET_SCHEME_RES;
935     HeapFree(GetProcessHeap(),0,tempBuffer);
936     return iScheme;
937 }
938
939 /***********************************************************************
940  *           SetUrlComponentValueW (Internal)
941  *
942  * Helper function for InternetCrackUrlW
943  *
944  * RETURNS
945  *    TRUE on success
946  *    FALSE on failure
947  *
948  */
949 BOOL SetUrlComponentValueW(LPWSTR* lppszComponent, LPDWORD dwComponentLen, LPCWSTR lpszStart, INT len)
950 {
951     TRACE("%s (%d)\n", debugstr_wn(lpszStart,len), len);
952
953     if (*dwComponentLen != 0 || *lppszComponent == NULL)
954     {
955         if (*lppszComponent == NULL)
956         {
957             *lppszComponent = (LPWSTR)lpszStart;
958             *dwComponentLen = len;
959         }
960         else
961         {
962             INT ncpylen = min((*dwComponentLen)-1, len);
963             strncpyW(*lppszComponent, lpszStart, ncpylen);
964             (*lppszComponent)[ncpylen] = '\0';
965             *dwComponentLen = ncpylen;
966         }
967     }
968
969     return TRUE;
970 }
971
972 /***********************************************************************
973  *           InternetCrackUrlW   (WININET.@)
974  */
975 BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
976                               LPURL_COMPONENTSW lpUC)
977 {
978   /*
979    * RFC 1808
980    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
981    *
982    */
983     LPWSTR lpszParam    = NULL;
984     BOOL  bIsAbsolute = FALSE;
985     LPWSTR lpszap = (WCHAR*)lpszUrl;
986     LPWSTR lpszcp = NULL;
987     WCHAR lpszSeparators[3]={';','?',0};
988     WCHAR lpszSlash[2]={'/',0};
989     if(dwUrlLength==0)
990         dwUrlLength=strlenW(lpszUrl);
991
992     TRACE("\n");
993
994     /* Determine if the URI is absolute. */
995     while (*lpszap != '\0')
996     {
997         if (isalnumW(*lpszap))
998         {
999             lpszap++;
1000             continue;
1001         }
1002         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
1003         {
1004             bIsAbsolute = TRUE;
1005             lpszcp = lpszap;
1006         }
1007         else
1008         {
1009             lpszcp = (LPWSTR)lpszUrl; /* Relative url */
1010         }
1011
1012         break;
1013     }
1014
1015     /* Parse <params> */
1016     lpszParam = strpbrkW(lpszap, lpszSeparators);
1017     if (lpszParam != NULL)
1018     {
1019         if (!SetUrlComponentValueW(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
1020                                    lpszParam, dwUrlLength-(lpszParam-lpszUrl)))
1021         {
1022             return FALSE;
1023         }
1024     }
1025
1026     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
1027     {
1028         LPWSTR lpszNetLoc;
1029         WCHAR wszAbout[]={'a','b','o','u','t',':',0};
1030
1031         /* Get scheme first. */
1032         lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1033         if (!SetUrlComponentValueW(&lpUC->lpszScheme, &lpUC->dwSchemeLength,
1034                                    lpszUrl, lpszcp - lpszUrl))
1035             return FALSE;
1036
1037         /* Eat ':' in protocol. */
1038         lpszcp++;
1039
1040         /* if the scheme is "about", there is no host */
1041         if(strncmpW(wszAbout,lpszUrl, lpszcp - lpszUrl)==0)
1042         {
1043             SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1044             SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1045             SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1046             lpUC->nPort = 0;
1047         }
1048         else
1049         {
1050             /* Skip over slashes. */
1051             if (*lpszcp == '/')
1052             {
1053                 lpszcp++;
1054                 if (*lpszcp == '/')
1055                 {
1056                     lpszcp++;
1057                     if (*lpszcp == '/')
1058                         lpszcp++;
1059                 }
1060             }
1061
1062             lpszNetLoc = strpbrkW(lpszcp, lpszSlash);
1063             if (lpszParam)
1064             {
1065                 if (lpszNetLoc)
1066                     lpszNetLoc = min(lpszNetLoc, lpszParam);
1067                 else
1068                     lpszNetLoc = lpszParam;
1069             }
1070             else if (!lpszNetLoc)
1071                 lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1072
1073             /* Parse net-loc */
1074             if (lpszNetLoc)
1075             {
1076                 LPWSTR lpszHost;
1077                 LPWSTR lpszPort;
1078
1079                 /* [<user>[<:password>]@]<host>[:<port>] */
1080                 /* First find the user and password if they exist */
1081
1082                 lpszHost = strchrW(lpszcp, '@');
1083                 if (lpszHost == NULL || lpszHost > lpszNetLoc)
1084                 {
1085                     /* username and password not specified. */
1086                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1087                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1088                 }
1089                 else /* Parse out username and password */
1090                 {
1091                     LPWSTR lpszUser = lpszcp;
1092                     LPWSTR lpszPasswd = lpszHost;
1093
1094                     while (lpszcp < lpszHost)
1095                     {
1096                         if (*lpszcp == ':')
1097                             lpszPasswd = lpszcp;
1098
1099                         lpszcp++;
1100                     }
1101
1102                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength,
1103                                           lpszUser, lpszPasswd - lpszUser);
1104
1105                     if (lpszPasswd != lpszHost)
1106                         lpszPasswd++;
1107                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
1108                                           lpszPasswd == lpszHost ? NULL : lpszPasswd,
1109                                           lpszHost - lpszPasswd);
1110
1111                     lpszcp++; /* Advance to beginning of host */
1112                 }
1113
1114                 /* Parse <host><:port> */
1115
1116                 lpszHost = lpszcp;
1117                 lpszPort = lpszNetLoc;
1118
1119                 /* special case for res:// URLs: there is no port here, so the host is the
1120                    entire string up to the first '/' */
1121                 if(lpUC->nScheme==INTERNET_SCHEME_RES)
1122                 {
1123                     SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1124                                           lpszHost, lpszPort - lpszHost);
1125                     lpUC->nPort = 0;
1126                     lpszcp=lpszNetLoc;
1127                 }
1128                 else
1129                 {
1130                     while (lpszcp < lpszNetLoc)
1131                     {
1132                         if (*lpszcp == ':')
1133                             lpszPort = lpszcp;
1134
1135                         lpszcp++;
1136                     }
1137
1138                     /* If the scheme is "file" and the host is just one letter, it's not a host */
1139                     if(lpUC->nScheme==INTERNET_SCHEME_FILE && (lpszPort-lpszHost)==1)
1140                     {
1141                         lpszcp=lpszHost;
1142                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1143                                               NULL, 0);
1144                         lpUC->nPort = 0;
1145                     }
1146                     else
1147                     {
1148                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1149                                               lpszHost, lpszPort - lpszHost);
1150                         if (lpszPort != lpszNetLoc)
1151                             lpUC->nPort = atoiW(++lpszPort);
1152                         else
1153                             lpUC->nPort = 0;
1154                     }
1155                 }
1156             }
1157         }
1158     }
1159
1160     /* Here lpszcp points to:
1161      *
1162      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1163      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1164      */
1165     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
1166     {
1167         INT len;
1168
1169         /* Only truncate the parameter list if it's already been saved
1170          * in lpUC->lpszExtraInfo.
1171          */
1172         if (lpszParam && lpUC->dwExtraInfoLength)
1173             len = lpszParam - lpszcp;
1174         else
1175         {
1176             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
1177              * newlines if necessary.
1178              */
1179             LPWSTR lpsznewline = strchrW(lpszcp, '\n');
1180             if (lpsznewline != NULL)
1181                 len = lpsznewline - lpszcp;
1182             else
1183                 len = dwUrlLength-(lpszcp-lpszUrl);
1184         }
1185
1186         if (!SetUrlComponentValueW(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength,
1187                                    lpszcp, len))
1188             return FALSE;
1189     }
1190     else
1191     {
1192         lpUC->dwUrlPathLength = 0;
1193     }
1194
1195     TRACE("%s: host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
1196              debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
1197              debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
1198              debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1199
1200     return TRUE;
1201 }
1202
1203 /***********************************************************************
1204  *           InternetAttemptConnect (WININET.@)
1205  *
1206  * Attempt to make a connection to the internet
1207  *
1208  * RETURNS
1209  *    ERROR_SUCCESS on success
1210  *    Error value   on failure
1211  *
1212  */
1213 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
1214 {
1215     FIXME("Stub\n");
1216     return ERROR_SUCCESS;
1217 }
1218
1219
1220 /***********************************************************************
1221  *           InternetCanonicalizeUrlA (WININET.@)
1222  *
1223  * Escape unsafe characters and spaces
1224  *
1225  * RETURNS
1226  *    TRUE on success
1227  *    FALSE on failure
1228  *
1229  */
1230 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
1231         LPDWORD lpdwBufferLength, DWORD dwFlags)
1232 {
1233     HRESULT hr;
1234     TRACE("%s %p %p %08lx\n",debugstr_a(lpszUrl), lpszBuffer,
1235           lpdwBufferLength, dwFlags);
1236
1237     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1238     dwFlags ^= ICU_NO_ENCODE;
1239
1240     dwFlags |= 0x80000000; /* Don't know what this means */
1241
1242     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1243
1244     return (hr == S_OK) ? TRUE : FALSE;
1245 }
1246
1247 /***********************************************************************
1248  *           InternetCanonicalizeUrlW (WININET.@)
1249  *
1250  * Escape unsafe characters and spaces
1251  *
1252  * RETURNS
1253  *    TRUE on success
1254  *    FALSE on failure
1255  *
1256  */
1257 BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
1258     LPDWORD lpdwBufferLength, DWORD dwFlags)
1259 {
1260     HRESULT hr;
1261     TRACE("%s %p %p %08lx\n", debugstr_w(lpszUrl), lpszBuffer,
1262         lpdwBufferLength, dwFlags);
1263
1264     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1265     dwFlags ^= ICU_NO_ENCODE;
1266
1267     dwFlags |= 0x80000000; /* Don't know what this means */
1268
1269     hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1270
1271     return (hr == S_OK) ? TRUE : FALSE;
1272 }
1273
1274
1275 /***********************************************************************
1276  *           InternetSetStatusCallbackA (WININET.@)
1277  *
1278  * Sets up a callback function which is called as progress is made
1279  * during an operation.
1280  *
1281  * RETURNS
1282  *    Previous callback or NULL         on success
1283  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
1284  *
1285  */
1286 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
1287         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
1288 {
1289     INTERNET_STATUS_CALLBACK retVal;
1290     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
1291
1292     TRACE("0x%08lx\n", (ULONG)hInternet);
1293     if (lpwai->hdr.htype != WH_HINIT)
1294         return INTERNET_INVALID_STATUS_CALLBACK;
1295
1296     retVal = lpwai->lpfnStatusCB;
1297     lpwai->lpfnStatusCB = lpfnIntCB;
1298
1299     return retVal;
1300 }
1301
1302 /***********************************************************************
1303  *           InternetSetFilePointer (WININET.@)
1304  */
1305 DWORD WINAPI InternetSetFilePointer(HINTERNET f1, LONG f2, PVOID f3, DWORD f4, DWORD f5)
1306 {
1307     FIXME("stub\n");
1308     return FALSE;
1309 }
1310
1311 /***********************************************************************
1312  *           InternetWriteFile (WININET.@)
1313  *
1314  * Write data to an open internet file
1315  *
1316  * RETURNS
1317  *    TRUE  on success
1318  *    FALSE on failure
1319  *
1320  */
1321 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
1322         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
1323 {
1324     BOOL retval = FALSE;
1325     int nSocket = -1;
1326     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1327
1328     TRACE("\n");
1329     if (NULL == lpwh)
1330         return FALSE;
1331
1332     switch (lpwh->htype)
1333     {
1334         case WH_HHTTPREQ:
1335             FIXME("This shouldn't be here! We don't support this kind"
1336                   " of connection anymore. Must use NETCON functions,"
1337                   " especially if using SSL\n");
1338             nSocket = ((LPWININETHTTPREQA)hFile)->netConnection.socketFD;
1339             break;
1340
1341         case WH_HFILE:
1342             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1343             break;
1344
1345         default:
1346             break;
1347     }
1348
1349     if (nSocket != -1)
1350     {
1351         int res = send(nSocket, lpBuffer, dwNumOfBytesToWrite, 0);
1352         retval = (res >= 0);
1353         *lpdwNumOfBytesWritten = retval ? res : 0;
1354     }
1355
1356     return retval;
1357 }
1358
1359
1360 /***********************************************************************
1361  *           InternetReadFile (WININET.@)
1362  *
1363  * Read data from an open internet file
1364  *
1365  * RETURNS
1366  *    TRUE  on success
1367  *    FALSE on failure
1368  *
1369  */
1370 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
1371         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
1372 {
1373     BOOL retval = FALSE;
1374     int nSocket = -1;
1375     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1376
1377     TRACE("\n");
1378
1379     if (NULL == lpwh)
1380         return FALSE;
1381
1382     /* FIXME: this should use NETCON functions! */
1383     switch (lpwh->htype)
1384     {
1385         case WH_HHTTPREQ:
1386             if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, lpBuffer,
1387                              dwNumOfBytesToRead, 0, (int *)dwNumOfBytesRead))
1388             {
1389                 *dwNumOfBytesRead = 0;
1390                 retval = TRUE; /* Under windows, it seems to return 0 even if nothing was read... */
1391             }
1392             else
1393                 retval = TRUE;
1394             break;
1395
1396         case WH_HFILE:
1397             /* FIXME: FTP should use NETCON_ stuff */
1398             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1399             if (nSocket != -1)
1400             {
1401                 int res = recv(nSocket, lpBuffer, dwNumOfBytesToRead, 0);
1402                 retval = (res >= 0);
1403                 *dwNumOfBytesRead = retval ? res : 0;
1404             }
1405             break;
1406
1407         default:
1408             break;
1409     }
1410
1411     return retval;
1412 }
1413
1414 /***********************************************************************
1415  *           InternetReadFileExA (WININET.@)
1416  *
1417  * Read data from an open internet file
1418  *
1419  * RETURNS
1420  *    TRUE  on success
1421  *    FALSE on failure
1422  *
1423  */
1424 BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffer,
1425         DWORD dwFlags, DWORD dwContext)
1426 {
1427   FIXME("stub\n");
1428   return FALSE;
1429 }
1430
1431 /***********************************************************************
1432  *           InternetReadFileExW (WININET.@)
1433  *
1434  * Read data from an open internet file
1435  *
1436  * RETURNS
1437  *    TRUE  on success
1438  *    FALSE on failure
1439  *
1440  */
1441 BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
1442         DWORD dwFlags, DWORD dwContext)
1443 {
1444   FIXME("stub\n");
1445
1446   INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1447   return FALSE;
1448 }
1449
1450 /***********************************************************************
1451  *           INET_QueryOptionHelper (internal)
1452  */
1453 static BOOL INET_QueryOptionHelper(BOOL bIsUnicode, HINTERNET hInternet, DWORD dwOption,
1454                                    LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1455 {
1456     LPWININETHANDLEHEADER lpwhh;
1457     BOOL bSuccess = FALSE;
1458
1459     TRACE("(%p, 0x%08lx, %p, %p)\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
1460
1461     if (NULL == hInternet)
1462     {
1463         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1464         return FALSE;
1465     }
1466
1467     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1468
1469     switch (dwOption)
1470     {
1471         case INTERNET_OPTION_HANDLE_TYPE:
1472         {
1473             ULONG type = lpwhh->htype;
1474             TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
1475
1476             if (*lpdwBufferLength < sizeof(ULONG))
1477                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1478             else
1479             {
1480                 memcpy(lpBuffer, &type, sizeof(ULONG));
1481                     *lpdwBufferLength = sizeof(ULONG);
1482                 bSuccess = TRUE;
1483             }
1484             break;
1485         }
1486
1487         case INTERNET_OPTION_REQUEST_FLAGS:
1488         {
1489             ULONG flags = 4;
1490             TRACE("INTERNET_OPTION_REQUEST_FLAGS: %ld\n", flags);
1491             if (*lpdwBufferLength < sizeof(ULONG))
1492                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1493             else
1494             {
1495                 memcpy(lpBuffer, &flags, sizeof(ULONG));
1496                     *lpdwBufferLength = sizeof(ULONG);
1497                 bSuccess = TRUE;
1498             }
1499             break;
1500         }
1501
1502         case INTERNET_OPTION_URL:
1503         case INTERNET_OPTION_DATAFILE_NAME:
1504         {
1505             ULONG type = lpwhh->htype;
1506             if (type == WH_HHTTPREQ)
1507             {
1508                 LPWININETHTTPREQA lpreq = hInternet;
1509                 char url[1023];
1510
1511                 sprintf(url,"http://%s%s",lpreq->lpszHostName,lpreq->lpszPath);
1512                 TRACE("INTERNET_OPTION_URL: %s\n",url);
1513                 if (*lpdwBufferLength < strlen(url)+1)
1514                     INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1515                 else
1516                 {
1517                     if(bIsUnicode)
1518                     {
1519                         *lpdwBufferLength=MultiByteToWideChar(CP_ACP,0,url,-1,lpBuffer,*lpdwBufferLength);
1520                     }
1521                     else
1522                     {
1523                         memcpy(lpBuffer, url, strlen(url)+1);
1524                         *lpdwBufferLength = strlen(url)+1;
1525                     }
1526                     bSuccess = TRUE;
1527                 }
1528             }
1529             break;
1530         }
1531        case INTERNET_OPTION_HTTP_VERSION:
1532        {
1533             /*
1534              * Presently hardcoded to 1.1
1535              */
1536             ((HTTP_VERSION_INFO*)lpBuffer)->dwMajorVersion = 1;
1537             ((HTTP_VERSION_INFO*)lpBuffer)->dwMinorVersion = 1;
1538             bSuccess = TRUE;
1539         break;
1540        }
1541
1542        default:
1543          FIXME("Stub! %ld \n",dwOption);
1544          break;
1545     }
1546
1547     return bSuccess;
1548 }
1549
1550 /***********************************************************************
1551  *           InternetQueryOptionW (WININET.@)
1552  *
1553  * Queries an options on the specified handle
1554  *
1555  * RETURNS
1556  *    TRUE  on success
1557  *    FALSE on failure
1558  *
1559  */
1560 BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
1561                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1562 {
1563     return INET_QueryOptionHelper(TRUE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1564 }
1565
1566 /***********************************************************************
1567  *           InternetQueryOptionA (WININET.@)
1568  *
1569  * Queries an options on the specified handle
1570  *
1571  * RETURNS
1572  *    TRUE  on success
1573  *    FALSE on failure
1574  *
1575  */
1576 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
1577                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1578 {
1579     return INET_QueryOptionHelper(FALSE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1580 }
1581
1582
1583 /***********************************************************************
1584  *           InternetSetOptionW (WININET.@)
1585  *
1586  * Sets an options on the specified handle
1587  *
1588  * RETURNS
1589  *    TRUE  on success
1590  *    FALSE on failure
1591  *
1592  */
1593 BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
1594                            LPVOID lpBuffer, DWORD dwBufferLength)
1595 {
1596     LPWININETHANDLEHEADER lpwhh;
1597
1598     TRACE("0x%08lx\n", dwOption);
1599
1600     if (NULL == hInternet)
1601     {
1602         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1603         return FALSE;
1604     }
1605
1606     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1607
1608     switch (dwOption)
1609     {
1610     case INTERNET_OPTION_HTTP_VERSION:
1611       {
1612         HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
1613         FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
1614       }
1615       break;
1616     case INTERNET_OPTION_ERROR_MASK:
1617       {
1618         unsigned long flags=*(unsigned long*)lpBuffer;
1619         FIXME("Option INTERNET_OPTION_ERROR_MASK(%ld): STUB\n",flags);
1620       }
1621       break;
1622     case INTERNET_OPTION_CODEPAGE:
1623       {
1624         unsigned long codepage=*(unsigned long*)lpBuffer;
1625         FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n",codepage);
1626       }
1627       break;
1628     case INTERNET_OPTION_REQUEST_PRIORITY:
1629       {
1630         unsigned long priority=*(unsigned long*)lpBuffer;
1631         FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n",priority);
1632       }
1633       break;
1634     default:
1635         FIXME("Option %ld STUB\n",dwOption);
1636         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1637         return FALSE;
1638     }
1639
1640     return TRUE;
1641 }
1642
1643
1644 /***********************************************************************
1645  *           InternetSetOptionA (WININET.@)
1646  *
1647  * Sets an options on the specified handle.
1648  *
1649  * RETURNS
1650  *    TRUE  on success
1651  *    FALSE on failure
1652  *
1653  */
1654 BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
1655                            LPVOID lpBuffer, DWORD dwBufferLength)
1656 {
1657     LPVOID wbuffer;
1658     DWORD wlen;
1659     BOOL r;
1660
1661     switch( dwOption )
1662     {
1663     case INTERNET_OPTION_PROXY:
1664         {
1665         LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
1666         LPINTERNET_PROXY_INFOW piw;
1667         DWORD proxlen, prbylen;
1668         LPWSTR prox, prby;
1669
1670         proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
1671         prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
1672         wlen = sizeof(*piw) + proxlen + prbylen;
1673         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1674         piw = (LPINTERNET_PROXY_INFOW) wbuffer;
1675         piw->dwAccessType = pi->dwAccessType;
1676         prox = (LPWSTR) &piw[1];
1677         prby = &prox[proxlen+1];
1678         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
1679         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
1680         piw->lpszProxy = prox;
1681         piw->lpszProxyBypass = prby;
1682         }
1683         break;
1684     case INTERNET_OPTION_USER_AGENT:
1685     case INTERNET_OPTION_USERNAME:
1686     case INTERNET_OPTION_PASSWORD:
1687         wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1688                                    NULL, 0 );
1689         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1690         MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1691                                    wbuffer, wlen );
1692         break;
1693     default:
1694         wbuffer = lpBuffer;
1695         wlen = dwBufferLength;
1696     }
1697
1698     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
1699
1700     if( lpBuffer != wbuffer )
1701         HeapFree( GetProcessHeap(), 0, wbuffer );
1702
1703     return r;
1704 }
1705
1706
1707 /***********************************************************************
1708  *           InternetSetOptionExA (WININET.@)
1709  */
1710 BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
1711                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1712 {
1713     FIXME("Flags %08lx ignored\n", dwFlags);
1714     return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
1715 }
1716
1717 /***********************************************************************
1718  *           InternetSetOptionExW (WININET.@)
1719  */
1720 BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
1721                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1722 {
1723     FIXME("Flags %08lx ignored\n", dwFlags);
1724     if( dwFlags & ~ISO_VALID_FLAGS )
1725     {
1726         SetLastError( ERROR_INVALID_PARAMETER );
1727         return FALSE;
1728     }
1729     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
1730 }
1731
1732
1733 /***********************************************************************
1734  *      InternetCheckConnectionA (WININET.@)
1735  *
1736  * Pings a requested host to check internet connection
1737  *
1738  * RETURNS
1739  *   TRUE on success and FALSE on failure. If a failure then
1740  *   ERROR_NOT_CONNECTED is placesd into GetLastError
1741  *
1742  */
1743 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
1744 {
1745 /*
1746  * this is a kludge which runs the resident ping program and reads the output.
1747  *
1748  * Anyone have a better idea?
1749  */
1750
1751   BOOL   rc = FALSE;
1752   char command[1024];
1753   char host[1024];
1754   int status = -1;
1755
1756   FIXME("\n");
1757
1758   /*
1759    * Crack or set the Address
1760    */
1761   if (lpszUrl == NULL)
1762   {
1763      /*
1764       * According to the doc we are supost to use the ip for the next
1765       * server in the WnInet internal server database. I have
1766       * no idea what that is or how to get it.
1767       *
1768       * So someone needs to implement this.
1769       */
1770      FIXME("Unimplemented with URL of NULL\n");
1771      return TRUE;
1772   }
1773   else
1774   {
1775      URL_COMPONENTSA componets;
1776
1777      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
1778      componets.lpszHostName = (LPSTR)&host;
1779      componets.dwHostNameLength = 1024;
1780
1781      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
1782        goto End;
1783
1784      TRACE("host name : %s\n",componets.lpszHostName);
1785   }
1786
1787   /*
1788    * Build our ping command
1789    */
1790   strcpy(command,"ping -w 1 ");
1791   strcat(command,host);
1792   strcat(command," >/dev/null 2>/dev/null");
1793
1794   TRACE("Ping command is : %s\n",command);
1795
1796   status = system(command);
1797
1798   TRACE("Ping returned a code of %i \n",status);
1799
1800   /* Ping return code of 0 indicates success */
1801   if (status == 0)
1802      rc = TRUE;
1803
1804 End:
1805
1806   if (rc == FALSE)
1807     SetLastError(ERROR_NOT_CONNECTED);
1808
1809   return rc;
1810 }
1811
1812
1813 /***********************************************************************
1814  *      InternetCheckConnectionW (WININET.@)
1815  *
1816  * Pings a requested host to check internet connection
1817  *
1818  * RETURNS
1819  *   TRUE on success and FALSE on failure. If a failure then
1820  *   ERROR_NOT_CONNECTED is placed into GetLastError
1821  *
1822  */
1823 BOOL WINAPI InternetCheckConnectionW(LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
1824 {
1825     CHAR *szUrl;
1826     INT len;
1827     BOOL rc;
1828
1829     len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1830     if (!(szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
1831         return FALSE;
1832     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, szUrl, len, NULL, NULL);
1833     rc = InternetCheckConnectionA((LPCSTR)szUrl, dwFlags, dwReserved);
1834     HeapFree(GetProcessHeap(), 0, szUrl);
1835     
1836     return rc;
1837 }
1838
1839
1840 /**********************************************************
1841  *      InternetOpenUrlA (WININET.@)
1842  *
1843  * Opens an URL
1844  *
1845  * RETURNS
1846  *   handle of connection or NULL on failure
1847  */
1848 HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
1849     LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1850 {
1851   URL_COMPONENTSA urlComponents;
1852   char protocol[32], hostName[MAXHOSTNAME], userName[1024];
1853   char password[1024], path[2048], extra[1024];
1854   HINTERNET client = NULL, client1 = NULL;
1855
1856   TRACE("(%p, %s, %s, %08lx, %08lx, %08lx\n", hInternet, debugstr_a(lpszUrl), debugstr_a(lpszHeaders),
1857        dwHeadersLength, dwFlags, dwContext);
1858
1859   urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
1860   urlComponents.lpszScheme = protocol;
1861   urlComponents.dwSchemeLength = 32;
1862   urlComponents.lpszHostName = hostName;
1863   urlComponents.dwHostNameLength = MAXHOSTNAME;
1864   urlComponents.lpszUserName = userName;
1865   urlComponents.dwUserNameLength = 1024;
1866   urlComponents.lpszPassword = password;
1867   urlComponents.dwPasswordLength = 1024;
1868   urlComponents.lpszUrlPath = path;
1869   urlComponents.dwUrlPathLength = 2048;
1870   urlComponents.lpszExtraInfo = extra;
1871   urlComponents.dwExtraInfoLength = 1024;
1872   if(!InternetCrackUrlA(lpszUrl, strlen(lpszUrl), 0, &urlComponents))
1873     return NULL;
1874   switch(urlComponents.nScheme) {
1875   case INTERNET_SCHEME_FTP:
1876     if(urlComponents.nPort == 0)
1877       urlComponents.nPort = INTERNET_DEFAULT_FTP_PORT;
1878     client = InternetConnectA(hInternet, hostName, urlComponents.nPort,
1879         userName, password, INTERNET_SERVICE_FTP, dwFlags, dwContext);
1880     return FtpOpenFileA(client, path, GENERIC_READ, dwFlags, dwContext);
1881   case INTERNET_SCHEME_HTTP:
1882   case INTERNET_SCHEME_HTTPS:
1883   {
1884     LPCSTR accept[2] = { "*/*", NULL };
1885     if(urlComponents.nPort == 0) {
1886       if(urlComponents.nScheme == INTERNET_SCHEME_HTTP)
1887         urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1888       else
1889         urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
1890     }
1891     client = InternetConnectA(hInternet, hostName, urlComponents.nPort, userName,
1892         password, INTERNET_SERVICE_HTTP, dwFlags, dwContext);
1893     if(client == NULL)
1894       return NULL;
1895     client1 = HttpOpenRequestA(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
1896     if(client1 == NULL) {
1897       InternetCloseHandle(client);
1898       return NULL;
1899     }
1900     HttpAddRequestHeadersA(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
1901     if(!HttpSendRequestA(client1, NULL, 0, NULL, 0)) {
1902       InternetCloseHandle(client1);
1903       InternetCloseHandle(client);
1904       return NULL;
1905     }
1906     return client1;
1907   }
1908   case INTERNET_SCHEME_GOPHER:
1909     /* gopher doesn't seem to be implemented in wine, but it's supposed
1910      * to be supported by InternetOpenUrlA. */
1911   default:
1912     return NULL;
1913   }
1914 }
1915
1916
1917 /**********************************************************
1918  *      InternetOpenUrlW (WININET.@)
1919  *
1920  * Opens an URL
1921  *
1922  * RETURNS
1923  *   handle of connection or NULL on failure
1924  */
1925 HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
1926     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1927 {
1928     HINTERNET rc = (HINTERNET)NULL;
1929
1930     INT lenUrl = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1931     INT lenHeaders = WideCharToMultiByte(CP_ACP, 0, lpszHeaders, -1, NULL, 0, NULL, NULL);
1932     CHAR *szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUrl*sizeof(CHAR));
1933     CHAR *szHeaders = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenHeaders*sizeof(CHAR));
1934
1935     if (!szUrl || !szHeaders)
1936     {
1937         if (szUrl)
1938             HeapFree(GetProcessHeap(), 0, szUrl);
1939         if (szHeaders)
1940             HeapFree(GetProcessHeap(), 0, szHeaders);
1941         return (HINTERNET)NULL;
1942     }
1943
1944     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, szUrl, lenUrl,
1945         NULL, NULL);
1946     WideCharToMultiByte(CP_ACP, 0, lpszHeaders, -1, szHeaders, lenHeaders,
1947         NULL, NULL);
1948
1949     rc = InternetOpenUrlA(hInternet, szUrl, szHeaders,
1950         dwHeadersLength, dwFlags, dwContext);
1951
1952     HeapFree(GetProcessHeap(), 0, szUrl);
1953     HeapFree(GetProcessHeap(), 0, szHeaders);
1954
1955     return rc;
1956 }
1957
1958
1959 /***********************************************************************
1960  *           INTERNET_SetLastError (internal)
1961  *
1962  * Set last thread specific error
1963  *
1964  * RETURNS
1965  *
1966  */
1967 void INTERNET_SetLastError(DWORD dwError)
1968 {
1969     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1970
1971     SetLastError(dwError);
1972     if(lpwite)
1973         lpwite->dwError = dwError;
1974 }
1975
1976
1977 /***********************************************************************
1978  *           INTERNET_GetLastError (internal)
1979  *
1980  * Get last thread specific error
1981  *
1982  * RETURNS
1983  *
1984  */
1985 DWORD INTERNET_GetLastError()
1986 {
1987     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1988     return lpwite->dwError;
1989 }
1990
1991
1992 /***********************************************************************
1993  *           INTERNET_WorkerThreadFunc (internal)
1994  *
1995  * Worker thread execution function
1996  *
1997  * RETURNS
1998  *
1999  */
2000 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
2001 {
2002     DWORD dwWaitRes;
2003
2004     while (1)
2005     {
2006         if(dwNumJobs > 0) {
2007             INTERNET_ExecuteWork();
2008             continue;
2009         }
2010         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
2011
2012         if (dwWaitRes == WAIT_OBJECT_0 + 1)
2013             INTERNET_ExecuteWork();
2014         else
2015             break;
2016
2017         InterlockedIncrement(&dwNumIdleThreads);
2018     }
2019
2020     InterlockedDecrement(&dwNumIdleThreads);
2021     InterlockedDecrement(&dwNumThreads);
2022     TRACE("Worker thread exiting\n");
2023     return TRUE;
2024 }
2025
2026
2027 /***********************************************************************
2028  *           INTERNET_InsertWorkRequest (internal)
2029  *
2030  * Insert work request into queue
2031  *
2032  * RETURNS
2033  *
2034  */
2035 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
2036 {
2037     BOOL bSuccess = FALSE;
2038     LPWORKREQUEST lpNewRequest;
2039
2040     TRACE("\n");
2041
2042     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
2043     if (lpNewRequest)
2044     {
2045         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
2046         lpNewRequest->prev = NULL;
2047
2048         EnterCriticalSection(&csQueue);
2049
2050         lpNewRequest->next = lpWorkQueueTail;
2051         if (lpWorkQueueTail)
2052             lpWorkQueueTail->prev = lpNewRequest;
2053         lpWorkQueueTail = lpNewRequest;
2054         if (!lpHeadWorkQueue)
2055             lpHeadWorkQueue = lpWorkQueueTail;
2056
2057         LeaveCriticalSection(&csQueue);
2058
2059         bSuccess = TRUE;
2060         InterlockedIncrement(&dwNumJobs);
2061     }
2062
2063     return bSuccess;
2064 }
2065
2066
2067 /***********************************************************************
2068  *           INTERNET_GetWorkRequest (internal)
2069  *
2070  * Retrieves work request from queue
2071  *
2072  * RETURNS
2073  *
2074  */
2075 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
2076 {
2077     BOOL bSuccess = FALSE;
2078     LPWORKREQUEST lpRequest = NULL;
2079
2080     TRACE("\n");
2081
2082     EnterCriticalSection(&csQueue);
2083
2084     if (lpHeadWorkQueue)
2085     {
2086         lpRequest = lpHeadWorkQueue;
2087         lpHeadWorkQueue = lpHeadWorkQueue->prev;
2088         if (lpRequest == lpWorkQueueTail)
2089             lpWorkQueueTail = lpHeadWorkQueue;
2090     }
2091
2092     LeaveCriticalSection(&csQueue);
2093
2094     if (lpRequest)
2095     {
2096         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
2097         HeapFree(GetProcessHeap(), 0, lpRequest);
2098         bSuccess = TRUE;
2099         InterlockedDecrement(&dwNumJobs);
2100     }
2101
2102     return bSuccess;
2103 }
2104
2105
2106 /***********************************************************************
2107  *           INTERNET_AsyncCall (internal)
2108  *
2109  * Retrieves work request from queue
2110  *
2111  * RETURNS
2112  *
2113  */
2114 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
2115 {
2116     HANDLE hThread;
2117     DWORD dwTID;
2118     BOOL bSuccess = FALSE;
2119
2120     TRACE("\n");
2121
2122     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
2123     {
2124         InterlockedIncrement(&dwNumIdleThreads);
2125
2126         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS ||
2127             !(hThread = CreateThread(NULL, 0,
2128             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
2129         {
2130             InterlockedDecrement(&dwNumThreads);
2131             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
2132             goto lerror;
2133         }
2134
2135         TRACE("Created new thread\n");
2136     }
2137
2138     bSuccess = TRUE;
2139     INTERNET_InsertWorkRequest(lpWorkRequest);
2140     SetEvent(hWorkEvent);
2141
2142 lerror:
2143
2144     return bSuccess;
2145 }
2146
2147
2148 /***********************************************************************
2149  *           INTERNET_ExecuteWork (internal)
2150  *
2151  * RETURNS
2152  *
2153  */
2154 VOID INTERNET_ExecuteWork()
2155 {
2156     WORKREQUEST workRequest;
2157
2158     TRACE("\n");
2159
2160     if (!INTERNET_GetWorkRequest(&workRequest))
2161         return;
2162     TRACE("Got work %d\n", workRequest.asyncall);
2163     switch (workRequest.asyncall)
2164     {
2165     case FTPPUTFILEA:
2166         {
2167         struct WORKREQ_FTPPUTFILEA *req = &workRequest.u.FtpPutFileA;
2168
2169         FTP_FtpPutFileA(workRequest.handle, req->lpszLocalFile,
2170                    req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
2171
2172         HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
2173         HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
2174         }
2175         break;
2176
2177     case FTPSETCURRENTDIRECTORYA:
2178         {
2179         struct WORKREQ_FTPSETCURRENTDIRECTORYA *req;
2180
2181         req = &workRequest.u.FtpSetCurrentDirectoryA;
2182         FTP_FtpSetCurrentDirectoryA(workRequest.handle, req->lpszDirectory);
2183         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2184         }
2185         break;
2186
2187     case FTPCREATEDIRECTORYA:
2188         {
2189         struct WORKREQ_FTPCREATEDIRECTORYA *req;
2190
2191         req = &workRequest.u.FtpCreateDirectoryA;
2192         FTP_FtpCreateDirectoryA(workRequest.handle, req->lpszDirectory);
2193         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2194         }
2195         break;
2196
2197     case FTPFINDFIRSTFILEA:
2198         {
2199         struct WORKREQ_FTPFINDFIRSTFILEA *req;
2200
2201         req = &workRequest.u.FtpFindFirstFileA;
2202         FTP_FtpFindFirstFileA(workRequest.handle, req->lpszSearchFile,
2203            req->lpFindFileData, req->dwFlags, req->dwContext);
2204         HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
2205         }
2206         break;
2207
2208     case FTPGETCURRENTDIRECTORYA:
2209         {
2210         struct WORKREQ_FTPGETCURRENTDIRECTORYA *req;
2211
2212         req = &workRequest.u.FtpGetCurrentDirectoryA;
2213         FTP_FtpGetCurrentDirectoryA(workRequest.handle,
2214                 req->lpszDirectory, req->lpdwDirectory);
2215         }
2216         break;
2217
2218     case FTPOPENFILEA:
2219         {
2220         struct WORKREQ_FTPOPENFILEA *req = &workRequest.u.FtpOpenFileA;
2221
2222         FTP_FtpOpenFileA(workRequest.handle, req->lpszFilename,
2223             req->dwAccess, req->dwFlags, req->dwContext);
2224         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
2225         }
2226         break;
2227
2228     case FTPGETFILEA:
2229         {
2230         struct WORKREQ_FTPGETFILEA *req = &workRequest.u.FtpGetFileA;
2231
2232         FTP_FtpGetFileA(workRequest.handle, req->lpszRemoteFile,
2233                  req->lpszNewFile, req->fFailIfExists,
2234                  req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
2235         HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
2236         HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
2237         }
2238         break;
2239
2240     case FTPDELETEFILEA:
2241         {
2242         struct WORKREQ_FTPDELETEFILEA *req = &workRequest.u.FtpDeleteFileA;
2243
2244         FTP_FtpDeleteFileA(workRequest.handle, req->lpszFilename);
2245         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
2246         }
2247         break;
2248
2249     case FTPREMOVEDIRECTORYA:
2250         {
2251         struct WORKREQ_FTPREMOVEDIRECTORYA *req;
2252
2253         req = &workRequest.u.FtpRemoveDirectoryA;
2254         FTP_FtpRemoveDirectoryA(workRequest.handle, req->lpszDirectory);
2255         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2256         }
2257         break;
2258
2259     case FTPRENAMEFILEA:
2260         {
2261         struct WORKREQ_FTPRENAMEFILEA *req = &workRequest.u.FtpRenameFileA;
2262
2263         FTP_FtpRenameFileA(workRequest.handle, req->lpszSrcFile, req->lpszDestFile);
2264         HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2265         HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2266         }
2267         break;
2268
2269     case INTERNETFINDNEXTA:
2270         {
2271         struct WORKREQ_INTERNETFINDNEXTA *req;
2272
2273         req = &workRequest.u.InternetFindNextA;
2274         INTERNET_FindNextFileA(workRequest.handle, req->lpFindFileData);
2275         }
2276         break;
2277
2278     case HTTPSENDREQUESTA:
2279         {
2280         struct WORKREQ_HTTPSENDREQUESTA *req = &workRequest.u.HttpSendRequestA;
2281
2282         HTTP_HttpSendRequestA(workRequest.handle, req->lpszHeader,
2283                 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength);
2284
2285         HeapFree(GetProcessHeap(), 0, req->lpszHeader);
2286         }
2287         break;
2288
2289     case HTTPOPENREQUESTA:
2290         {
2291         struct WORKREQ_HTTPOPENREQUESTA *req = &workRequest.u.HttpOpenRequestA;
2292
2293         HTTP_HttpOpenRequestA(workRequest.handle, req->lpszVerb,
2294             req->lpszObjectName, req->lpszVersion, req->lpszReferrer,
2295             req->lpszAcceptTypes, req->dwFlags, req->dwContext);
2296
2297         HeapFree(GetProcessHeap(), 0, req->lpszVerb);
2298         HeapFree(GetProcessHeap(), 0, req->lpszObjectName);
2299         HeapFree(GetProcessHeap(), 0, req->lpszVersion);
2300         HeapFree(GetProcessHeap(), 0, req->lpszReferrer);
2301         }
2302         break;
2303
2304     case SENDCALLBACK:
2305         {
2306         struct WORKREQ_SENDCALLBACK *req = &workRequest.u.SendCallback;
2307
2308         SendAsyncCallbackInt(workRequest.handle, req->hHttpSession,
2309                 req->dwContext, req->dwInternetStatus, req->lpvStatusInfo,
2310                 req->dwStatusInfoLength);
2311         }
2312         break;
2313     }
2314 }
2315
2316
2317 /***********************************************************************
2318  *          INTERNET_GetResponseBuffer
2319  *
2320  * RETURNS
2321  *
2322  */
2323 LPSTR INTERNET_GetResponseBuffer()
2324 {
2325     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
2326     TRACE("\n");
2327     return lpwite->response;
2328 }
2329
2330 /***********************************************************************
2331  *           INTERNET_GetNextLine  (internal)
2332  *
2333  * Parse next line in directory string listing
2334  *
2335  * RETURNS
2336  *   Pointer to beginning of next line
2337  *   NULL on failure
2338  *
2339  */
2340
2341 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
2342 {
2343     struct timeval tv;
2344     fd_set infd;
2345     BOOL bSuccess = FALSE;
2346     INT nRecv = 0;
2347
2348     TRACE("\n");
2349
2350     FD_ZERO(&infd);
2351     FD_SET(nSocket, &infd);
2352     tv.tv_sec=RESPONSE_TIMEOUT;
2353     tv.tv_usec=0;
2354
2355     while (nRecv < *dwBuffer)
2356     {
2357         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
2358         {
2359             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2360             {
2361                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2362                 goto lend;
2363             }
2364
2365             if (lpszBuffer[nRecv] == '\n')
2366             {
2367                 bSuccess = TRUE;
2368                 break;
2369             }
2370             if (lpszBuffer[nRecv] != '\r')
2371                 nRecv++;
2372         }
2373         else
2374         {
2375             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2376             goto lend;
2377         }
2378     }
2379
2380 lend:
2381     if (bSuccess)
2382     {
2383         lpszBuffer[nRecv] = '\0';
2384         *dwBuffer = nRecv - 1;
2385         TRACE(":%d %s\n", nRecv, lpszBuffer);
2386         return lpszBuffer;
2387     }
2388     else
2389     {
2390         return NULL;
2391     }
2392 }
2393
2394 /***********************************************************************
2395  *
2396  */
2397 BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
2398                                 LPDWORD lpdwNumberOfBytesAvailble,
2399                                 DWORD dwFlags, DWORD dwConext)
2400 {
2401     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hFile;
2402     INT retval = -1;
2403     char buffer[4048];
2404
2405
2406     if (NULL == lpwhr)
2407     {
2408         SetLastError(ERROR_NO_MORE_FILES);
2409         return FALSE;
2410     }
2411
2412     TRACE("-->  %p %i\n",lpwhr,lpwhr->hdr.htype);
2413
2414     switch (lpwhr->hdr.htype)
2415     {
2416     case WH_HHTTPREQ:
2417         if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, buffer,
2418                          4048, MSG_PEEK, (int *)lpdwNumberOfBytesAvailble))
2419         {
2420             SetLastError(ERROR_NO_MORE_FILES);
2421             retval = FALSE;
2422         }
2423         else
2424             retval = TRUE;
2425         break;
2426
2427     default:
2428         FIXME("unsupported file type\n");
2429         break;
2430     }
2431
2432     TRACE("<-- %i\n",retval);
2433     return (retval+1);
2434 }
2435
2436
2437 /***********************************************************************
2438  *
2439  */
2440 BOOL WINAPI InternetLockRequestFile( HINTERNET hInternet, HANDLE
2441 *lphLockReqHandle)
2442 {
2443     FIXME("STUB\n");
2444     return FALSE;
2445 }
2446
2447 BOOL WINAPI InternetUnlockRequestFile( HANDLE hLockHandle)
2448 {
2449     FIXME("STUB\n");
2450     return FALSE;
2451 }
2452
2453
2454 /***********************************************************************
2455  *           InternetAutodial
2456  *
2457  * On windows this function is supposed to dial the default internet
2458  * connection. We don't want to have Wine dial out to the internet so
2459  * we return TRUE by default. It might be nice to check if we are connected.
2460  *
2461  * RETURNS
2462  *   TRUE on success
2463  *   FALSE on failure
2464  *
2465  */
2466 BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
2467 {
2468     FIXME("STUB\n");
2469
2470     /* Tell that we are connected to the internet. */
2471     return TRUE;
2472 }
2473
2474 /***********************************************************************
2475  *           InternetAutodialHangup
2476  *
2477  * Hangs up an connection made with InternetAutodial
2478  *
2479  * PARAM
2480  *    dwReserved
2481  * RETURNS
2482  *   TRUE on success
2483  *   FALSE on failure
2484  *
2485  */
2486 BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
2487 {
2488     FIXME("STUB\n");
2489
2490     /* we didn't dial, we don't disconnect */
2491     return TRUE;
2492 }
2493
2494 /***********************************************************************
2495  *
2496  *         InternetCombineUrlA
2497  *
2498  * Combine a base URL with a relative URL
2499  *
2500  * RETURNS
2501  *   TRUE on success
2502  *   FALSE on failure
2503  *
2504  */
2505
2506 BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
2507                                 LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
2508                                 DWORD dwFlags)
2509 {
2510     HRESULT hr=S_OK;
2511     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2512     dwFlags ^= ICU_NO_ENCODE;
2513     hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2514
2515     return (hr==S_OK);
2516 }
2517
2518 /***********************************************************************
2519  *
2520  *         InternetCombineUrlW
2521  *
2522  * Combine a base URL with a relative URL
2523  *
2524  * RETURNS
2525  *   TRUE on success
2526  *   FALSE on failure
2527  *
2528  */
2529
2530 BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
2531                                 LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
2532                                 DWORD dwFlags)
2533 {
2534     HRESULT hr=S_OK;
2535     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2536     dwFlags ^= ICU_NO_ENCODE;
2537     hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2538
2539     return (hr==S_OK);
2540 }
2541
2542 /***********************************************************************
2543  *
2544  *         InternetCreateUrlA
2545  *
2546  * RETURNS
2547  *   TRUE on success
2548  *   FALSE on failure
2549  *
2550  */
2551 BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
2552                                LPSTR lpszUrl, LPDWORD lpdwUrlLength)
2553 {
2554     FIXME("\n");
2555     return FALSE;
2556 }
2557
2558 /***********************************************************************
2559  *
2560  *         InternetCreateUrlW
2561  *
2562  * RETURNS
2563  *   TRUE on success
2564  *   FALSE on failure
2565  *
2566  */
2567 BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
2568                                LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
2569 {
2570     FIXME("\n");
2571     return FALSE;
2572 }