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