Like the AUTORADIOBUTTON, the parent of a RADIOBUTTON style button
[wine] / dlls / wininet / internet.c
1 /*
2  * Wininet
3  *
4  * Copyright 1999 Corel Corporation
5  *
6  * Ulrich Czekalla
7  *
8  */
9
10 #include "config.h"
11
12 #include <string.h>
13 #include <sys/types.h>
14 #ifdef HAVE_SYS_SOCKET_H
15 # include <sys/socket.h>
16 #endif
17 #include <stdlib.h>
18
19 #include "windows.h"
20 #include "wininet.h"
21 #include "debugtools.h"
22 #include "winerror.h"
23 #include "winsock.h"
24
25 #include "internet.h"
26
27 DEFAULT_DEBUG_CHANNEL(wininet);
28
29 #define MAX_IDLE_WORKER 1000*60*1
30 #define MAX_WORKER_THREADS 10
31
32 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
33 (LPWININETAPPINFOA)(((LPWININETFTPSESSIONA)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
34
35 typedef struct
36 {
37     DWORD  dwError;
38     CHAR   response[MAX_REPLY_LEN];
39 } WITHREADERROR, *LPWITHREADERROR;
40
41 INTERNET_SCHEME GetInternetScheme(LPSTR lpszScheme);
42 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData);
43 VOID INTERNET_ExecuteWork();
44
45 DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
46 DWORD dwNumThreads;
47 DWORD dwNumIdleThreads;
48 HANDLE hEventArray[2];
49 #define hQuitEvent hEventArray[0]
50 #define hWorkEvent hEventArray[1]
51 CRITICAL_SECTION csQueue;
52 LPWORKREQUEST lpHeadWorkQueue;
53 LPWORKREQUEST lpWorkQueueTail;
54
55 /***********************************************************************
56  * WININET_LibMain [Internal] Initializes the internal 'WININET.DLL'.
57  *
58  * PARAMS
59  *     hinstDLL    [I] handle to the 'dlls' instance
60  *     fdwReason   [I]
61  *     lpvReserved [I] reserverd, must be NULL
62  *
63  * RETURNS
64  *     Success: TRUE
65  *     Failure: FALSE
66  */
67
68 BOOL WINAPI
69 WININET_LibMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
70 {
71     TRACE("%x,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
72
73     switch (fdwReason) {
74         case DLL_PROCESS_ATTACH:
75
76             g_dwTlsErrIndex = TlsAlloc();
77
78             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
79                 return FALSE;
80
81             hQuitEvent = CreateEventA(0, TRUE, FALSE, NULL);
82             hWorkEvent = CreateEventA(0, FALSE, FALSE, NULL);
83             InitializeCriticalSection(&csQueue);
84
85             dwNumThreads = 0;
86             dwNumIdleThreads = 0;
87
88         case DLL_THREAD_ATTACH:
89             {
90                 LPWITHREADERROR lpwite = HeapAlloc(GetProcessHeap(), 0, sizeof(WITHREADERROR));
91                 if (NULL == lpwite) 
92                     return FALSE;
93
94                 TlsSetValue(g_dwTlsErrIndex, (LPVOID)lpwite);
95             }
96             break;
97
98         case DLL_THREAD_DETACH:
99             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
100                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
101             break;
102
103         case DLL_PROCESS_DETACH:
104
105             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
106             {
107                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
108                 TlsFree(g_dwTlsErrIndex);
109             }
110
111             SetEvent(hQuitEvent);
112
113             CloseHandle(hQuitEvent);
114             CloseHandle(hWorkEvent);
115
116             DeleteCriticalSection(&csQueue);
117             break;
118     }
119
120     return TRUE;
121 }
122
123
124 /***********************************************************************
125  *           InternetOpenA   (WININET.113)
126  *
127  * Per-application initialization of wininet
128  *
129  * RETURNS
130  *    HINTERNET on success
131  *    NULL on failure
132  *
133  */
134 INTERNETAPI HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, 
135         DWORD dwAccessType, LPCSTR lpszProxy,
136         LPCSTR lpszProxyBypass, DWORD dwFlags)
137 {
138     LPWININETAPPINFOA lpwai = NULL;
139
140     TRACE("\n");
141
142     /* Clear any error information */
143     INTERNET_SetLastError(0);
144
145     lpwai = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETAPPINFOA));
146     if (NULL == lpwai)
147         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
148     else
149     {
150         memset(lpwai, 0, sizeof(WININETAPPINFOA));
151         lpwai->hdr.htype = WH_HINIT;
152         lpwai->hdr.lpwhparent = NULL;
153         lpwai->hdr.dwFlags = dwFlags;
154         if (NULL != lpszAgent)
155             lpwai->lpszAgent = strdup(lpszAgent);
156         if (NULL != lpszProxy)
157             lpwai->lpszProxy = strdup(lpszProxy);
158         if (NULL != lpszProxyBypass)
159             lpwai->lpszProxyBypass = strdup(lpszProxyBypass);
160         lpwai->dwAccessType = dwAccessType;
161     }
162
163     return (HINTERNET)lpwai;
164 }
165
166
167 /***********************************************************************
168  *           InternetGetLastResponseInfoA (WININET.108)
169  *
170  * Return last wininet error description on the calling thread
171  *
172  * RETURNS
173  *    TRUE on success of writting to buffer
174  *    FALSE on failure
175  *
176  */
177 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
178     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
179 {
180     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
181
182     TRACE("\n");
183
184     *lpdwError = lpwite->dwError;
185     if (lpwite->dwError)
186     {
187         strncpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
188         *lpdwBufferLength = strlen(lpszBuffer);
189     }
190     else
191         *lpdwBufferLength = 0;
192
193     return TRUE;
194 }
195
196
197 /***********************************************************************
198  *           InternetGetConnectedState (WININET.103)
199  *
200  * Return connected state
201  *
202  * RETURNS
203  *    TRUE if connected
204  *    if lpdwStatus is not null, return the status (off line, 
205  *    modem, lan...) in it.
206  *    FALSE if not connected
207  */
208 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
209 {
210     FIXME("Stub\n");
211     return FALSE;
212 }
213
214
215 /***********************************************************************
216  *           InternetConnectA (WININET.93)
217  *
218  * Open a ftp, gopher or http session
219  *
220  * RETURNS
221  *    HINTERNET a session handle on success
222  *    NULL on failure
223  *
224  */
225 INTERNETAPI HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
226     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
227     LPCSTR lpszUserName, LPCSTR lpszPassword,
228     DWORD dwService, DWORD dwFlags, DWORD dwContext)
229 {
230     HINTERNET rc = (HINTERNET) NULL;
231
232     TRACE("\n");
233
234     /* Clear any error information */
235     INTERNET_SetLastError(0);
236
237     switch (dwService)
238     {
239         case INTERNET_SERVICE_FTP:
240             rc = FTP_Connect(hInternet, lpszServerName, nServerPort,
241             lpszUserName, lpszPassword, dwFlags, dwContext);
242             break;
243
244         case INTERNET_SERVICE_HTTP:
245             break;
246
247         case INTERNET_SERVICE_GOPHER:
248         default:
249             break;
250     }
251
252     return rc;
253 }
254
255 /***********************************************************************
256  *           InternetFindNextFileA (WININET.102)
257  *
258  * Continues a file search from a previous call to FindFirstFile
259  *
260  * RETURNS
261  *    TRUE on success
262  *    FALSE on failure
263  *
264  */
265 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
266 {
267     LPWININETAPPINFOA hIC = NULL;
268     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
269
270     TRACE("\n");
271
272     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
273     {
274         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
275         return FALSE;
276     }
277
278     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
279     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
280     {
281         WORKREQUEST workRequest;
282
283         workRequest.asyncall = INTERNETFINDNEXTA;
284         workRequest.HFTPSESSION = (DWORD)hFind;
285         workRequest.LPFINDFILEDATA = (DWORD)lpvFindData;
286
287         return INTERNET_AsyncCall(&workRequest);
288     }
289     else
290     {
291         return INTERNET_FindNextFileA(hFind, lpvFindData);
292     }
293 }
294
295 /***********************************************************************
296  *           INTERNET_FindNextFileA (Internal)
297  *
298  * Continues a file search from a previous call to FindFirstFile
299  *
300  * RETURNS
301  *    TRUE on success
302  *    FALSE on failure
303  *
304  */
305 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
306 {
307     BOOL bSuccess = TRUE;
308     LPWININETAPPINFOA hIC = NULL;
309     LPWIN32_FIND_DATAA lpFindFileData;
310     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
311
312     TRACE("\n");
313
314     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
315     {
316         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
317         return FALSE;
318     }
319
320     /* Clear any error information */
321     INTERNET_SetLastError(0);
322
323     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
324     {
325         FIXME("Only FTP find next supported\n");
326         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
327         return FALSE;
328     }
329
330     TRACE("index(%d) size(%ld)\n", lpwh->index, lpwh->size);
331
332     lpFindFileData = (LPWIN32_FIND_DATAA) lpvFindData;
333     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
334
335     if (lpwh->index >= lpwh->size)
336     {
337         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
338         bSuccess = FALSE;
339         goto lend;
340     }
341
342     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
343     lpwh->index++;
344
345     TRACE("\nName: %s\nSize: %ld\n", lpFindFileData->cFileName, lpFindFileData->nFileSizeLow);
346
347 lend:
348
349     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
350     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
351     {   
352         INTERNET_ASYNC_RESULT iar;
353
354         iar.dwResult = (DWORD)bSuccess;
355         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
356
357         hIC->lpfnStatusCB(hFind, lpwh->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
358         &iar, sizeof(INTERNET_ASYNC_RESULT));
359     }
360
361     return bSuccess;
362 }
363
364
365 /***********************************************************************
366  *           InternetCloseHandle (WININET.89)
367  *
368  * Continues a file search from a previous call to FindFirstFile
369  *
370  * RETURNS
371  *    TRUE on success
372  *    FALSE on failure
373  *
374  */
375 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
376 {
377     BOOL retval = FALSE;
378     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hInternet;
379
380     TRACE("\n");
381     if (NULL == lpwh)
382         return FALSE;
383
384     /* Clear any error information */
385     INTERNET_SetLastError(0);
386
387     switch (lpwh->htype)
388     {
389         case WH_HINIT:
390         case WH_HHTTPSESSION:
391         case WH_HHTTPREQ:
392             break;
393         case WH_HFTPSESSION:
394             retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
395             break;
396
397         case WH_HFINDNEXT:
398             retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
399             break;
400                 
401         default:
402             break;
403     }
404
405     return retval;
406 }
407
408
409 /***********************************************************************
410  *           InternetCrackUrlA (WININET.95)
411  *
412  * Break up URL into its components
413  *
414  * RETURNS
415  *    TRUE on success
416  *    FALSE on failure
417  *
418  */
419 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags, 
420                 LPURL_COMPONENTSA lpUrlComponents)
421 {
422   /*
423    * RFC 1808
424    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
425    *
426    */
427     char* szScheme   = NULL;
428     char* szUser     = NULL;
429     char* szPass     = NULL;
430     char* szHost     = NULL;
431     char* szUrlPath  = NULL;
432     char* szParam    = NULL;
433     char* szNetLoc   = NULL;
434     int   nPort      = 80;
435     int   nSchemeLen = 0;
436     int   nUserLen   = 0;
437     int   nPassLen   = 0;
438     int   nHostLen   = 0;
439     int   nUrlLen    = 0;
440
441     /* Find out if the URI is absolute... */
442     BOOL  bIsAbsolute = FALSE;
443     char  cAlphanum;
444     char* ap = (char*)lpszUrl;
445     char* cp = NULL;
446
447     TRACE("\n");
448     while( (cAlphanum = *ap) != '\0' )
449     {
450         if( ((cAlphanum >= 'a') && (cAlphanum <= 'z')) ||
451             ((cAlphanum >= 'A') && (cAlphanum <= 'Z')) ||
452             ((cAlphanum >= '0') && (cAlphanum <= '9')) )
453         {
454             ap++;
455             continue;
456         }
457         if( (cAlphanum == ':') && (ap - lpszUrl >= 2) )
458         {
459             bIsAbsolute = TRUE;
460             cp = ap;
461             break;
462         }
463         break;
464     }
465
466     /* Absolute URI...
467        FIXME!!!! This should work on relative urls too!*/
468     if( bIsAbsolute )
469     {
470         /* Get scheme first... */
471         nSchemeLen = cp - lpszUrl;
472         szScheme   = strdup( lpszUrl );
473         szScheme[ nSchemeLen ] = '\0';
474
475         /* Eat ':' in protocol... */
476         cp++;
477
478         /* Parse <params>... */
479         szParam = strpbrk( lpszUrl, ";" );
480         if( szParam != NULL )
481         {
482             char* sParam;
483             /* Eat ';' in Params... */
484             szParam++;
485             sParam    = strdup( szParam );
486             *szParam = '\0';
487         }
488
489         /* Skip over slashes...*/
490         if( *cp == '/' )
491         {
492             cp++;
493             if( *cp == '/' )
494             {
495                 cp++;
496                 if( *cp == '/' )
497                     cp++;
498             }
499         }
500
501         /* Parse the <net-loc>...*/
502         if( GetInternetScheme( szScheme ) == INTERNET_SCHEME_FILE )
503         {
504             szUrlPath = strdup( cp );
505             nUrlLen   = strlen( szUrlPath );
506             if( nUrlLen >= 2 && szUrlPath[ 1 ] == '|' )
507                 szUrlPath[ 1 ] = ':';
508         }
509         else
510         {
511             size_t nNetLocLen;
512             szUrlPath = strpbrk(cp, "/");
513             if( szUrlPath != NULL )
514                 nUrlLen = strlen( szUrlPath );
515
516             /* Find the end of our net-loc... */
517             nNetLocLen = strcspn( cp, "/" );
518             szNetLoc   = strdup( cp );
519             szNetLoc[ nNetLocLen ] = '\0';
520             if( szNetLoc != NULL )
521             {
522                 char* lpszPort;
523                 int   nPortLen;
524                 /* [<user>[<:password>]@]<host>[:<port>] */
525                 /* First find the user and password if they exist...*/
526                         
527                 szHost = strchr( szNetLoc, '@' );
528                 if( szHost == NULL )
529                 {
530                     /* username and password not specified... */
531                     szHost   = szNetLoc;
532                     nHostLen = nNetLocLen;
533                 }
534                 else
535                 {
536                     int   nUserPassLen = nNetLocLen - nHostLen - 1;
537                     char* szUserPass         = strdup( szNetLoc );
538                     /* Get username and/or password... */
539                     /* Eat '@' in domain... */
540                     ++szHost;
541                     nHostLen = strlen( szHost );
542
543                     szUserPass[ nUserPassLen ] = '\0';
544                     if( szUserPass != NULL )
545                     {
546                         szPass = strpbrk( szUserPass, ":" );
547                         if( szPass != NULL )
548                         {
549                             /* Eat ':' in UserPass... */
550                             ++szPass;
551                             nPassLen = strlen( szPass );
552                             nUserLen = nUserPassLen - nPassLen - 1;
553                             szUser   = strdup( szUserPass );
554                             szUser[ nUserLen ] = '\0';
555                         }
556                         else
557                         {
558                             /* password not specified... */
559                             szUser = strdup( szUserPass );
560                             nUserLen = strlen( szUser );
561                         }
562                     }        
563                 }
564
565                 /* <host><:port>...*/
566                 /* Then get the port if it exists... */
567                 lpszPort = strpbrk( szHost, ":" );
568                 nPortLen = 0;
569                 if( lpszPort != NULL )
570                 {
571                     char* szPort = lpszPort + 1;
572                     if( szPort != NULL )
573                     {
574                         nPortLen = strlen( szPort );
575                         nPort    = atoi( szPort );
576                     }
577                     *lpszPort = '\0';
578                     nHostLen = strlen(szHost);
579                 }
580             }
581         }
582     }
583     /* Relative URI... */
584     else
585         return FALSE;
586
587     return TRUE;
588 }
589
590
591 /***********************************************************************
592  *           InternetAttemptConnect (WININET.81)
593  *
594  * Attempt to make a connection to the internet
595  *
596  * RETURNS
597  *    ERROR_SUCCESS on success
598  *    Error value   on failure
599  *
600  */
601 INTERNETAPI DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
602 {
603     FIXME("Stub\n");
604     return ERROR_SUCCESS;
605 }
606
607
608 /***********************************************************************
609  *           InternetCanonicalizeUrlA (WININET.85)
610  *
611  * Escape unsafe characters and spaces
612  *
613  * RETURNS
614  *    TRUE on success
615  *    FALSE on failure
616  *
617  */
618 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
619         LPDWORD lpdwBufferLength, DWORD dwFlags)
620 {
621     BOOL bSuccess = FALSE;
622
623     FIXME("Stub!\n");
624
625     if (lpszUrl)
626     {
627         strncpy(lpszBuffer, lpszUrl, *lpdwBufferLength);
628         *lpdwBufferLength = strlen(lpszBuffer);
629         bSuccess = TRUE;
630     }
631
632     return bSuccess;
633 }
634
635
636 /***********************************************************************
637  *           InternetSetStatusCallback (WININET.133)
638  *
639  * Sets up a callback function which is called as progress is made
640  * during an operation. 
641  *
642  * RETURNS
643  *    Previous callback or NULL         on success
644  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
645  *
646  */
647 INTERNETAPI INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallback(
648         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
649 {
650     INTERNET_STATUS_CALLBACK retVal;
651     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
652
653     TRACE("0x%08lx\n", (ULONG)hInternet);
654     if (lpwai->hdr.htype != WH_HINIT)
655         return INTERNET_INVALID_STATUS_CALLBACK; 
656
657     retVal = lpwai->lpfnStatusCB;
658     lpwai->lpfnStatusCB = lpfnIntCB;
659
660     return retVal;
661 }
662
663
664 /***********************************************************************
665  *           InternetWriteFile (WININET.138)
666  *
667  * Write data to an open internet file 
668  *
669  * RETURNS
670  *    TRUE  on success
671  *    FALSE on failure
672  *
673  */
674 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
675         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
676 {
677     BOOL retval = FALSE;
678     int nSocket = INVALID_SOCKET;
679     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
680
681     TRACE("\n");
682     if (NULL == lpwh)
683         return FALSE;
684
685     switch (lpwh->htype)
686     {
687         case WH_HHTTPREQ:
688             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
689             break;
690
691         case WH_HFILE:
692             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
693             break;
694
695         default:
696             break;
697     }
698
699     if (INVALID_SOCKET != nSocket)
700     {
701         *lpdwNumOfBytesWritten = INTERNET_WriteDataToStream(nSocket, lpBuffer, dwNumOfBytesToWrite);
702         if (*lpdwNumOfBytesWritten < 0)
703             *lpdwNumOfBytesWritten = 0;
704         else
705             retval = TRUE;
706     }
707
708     return retval;
709 }
710
711
712 /***********************************************************************
713  *           InternetReadFile (WININET.121)
714  *
715  * Read data from an open internet file 
716  *
717  * RETURNS
718  *    TRUE  on success
719  *    FALSE on failure
720  *
721  */
722 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, 
723         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
724 {
725     BOOL retval = FALSE;
726     int nSocket = INVALID_SOCKET;
727     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
728
729     TRACE("\n");
730     if (NULL == lpwh)
731         return FALSE;
732
733     switch (lpwh->htype)
734     {
735         case WH_HHTTPREQ:
736             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
737             break;
738
739         case WH_HFILE:
740             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
741             break;
742
743         default:
744             break;
745     }
746
747     if (INVALID_SOCKET != nSocket)
748     {
749         *dwNumOfBytesRead = INTERNET_ReadDataFromStream(nSocket, lpBuffer, dwNumOfBytesToRead);
750         if (*dwNumOfBytesRead < 0)
751             *dwNumOfBytesRead = 0;
752         else
753             retval = TRUE;
754     }
755
756     return retval;
757 }
758
759
760 /***********************************************************************
761  *           GetInternetScheme (internal)
762  *
763  * Get scheme of url
764  *
765  * RETURNS
766  *    scheme on success
767  *    INTERNET_SCHEME_UNKNOWN on failure
768  *
769  */
770 INTERNET_SCHEME GetInternetScheme(LPSTR lpszScheme)
771 {
772     if(lpszScheme==NULL)
773         return INTERNET_SCHEME_UNKNOWN;
774
775     if( (strcmp("ftp", lpszScheme) == 0) ||
776         (strcmp("FTP", lpszScheme) == 0) )
777         return INTERNET_SCHEME_FTP;
778     else if( (strcmp("gopher", lpszScheme) == 0) ||
779         (strcmp("GOPHER", lpszScheme) == 0) )
780         return INTERNET_SCHEME_GOPHER;
781     else if( (strcmp("http", lpszScheme) == 0) ||
782         (strcmp("HTTP", lpszScheme) == 0) )
783         return INTERNET_SCHEME_HTTP;
784     else if( (strcmp("https", lpszScheme) == 0) ||
785         (strcmp("HTTPS", lpszScheme) == 0) )
786         return INTERNET_SCHEME_HTTPS;
787     else if( (strcmp("file", lpszScheme) == 0) ||
788         (strcmp("FILE", lpszScheme) == 0) )
789         return INTERNET_SCHEME_FILE;
790     else if( (strcmp("news", lpszScheme) == 0) ||
791         (strcmp("NEWS", lpszScheme) == 0) )
792         return INTERNET_SCHEME_NEWS;
793     else if( (strcmp("mailto", lpszScheme) == 0) ||
794         (strcmp("MAILTO", lpszScheme) == 0) )
795         return INTERNET_SCHEME_MAILTO;
796     else
797         return INTERNET_SCHEME_UNKNOWN;
798 }
799
800
801 /***********************************************************************
802  *           INTERNET_WriteDataToStream (internal)
803  *
804  * Send data to server
805  *
806  * RETURNS
807  *
808  *   number of characters sent on success
809  *   -1 on error
810  */
811 int INTERNET_WriteDataToStream(int nDataSocket, LPCVOID Buffer, DWORD BytesToWrite)
812 {
813     if (INVALID_SOCKET == nDataSocket)
814         return SOCKET_ERROR;
815
816     return send(nDataSocket, Buffer, BytesToWrite, 0);
817 }
818
819
820 /***********************************************************************
821  *           INTERNET_ReadDataFromStream (internal)
822  *
823  * Read data from http server
824  *
825  * RETURNS
826  *
827  *   number of characters sent on success
828  *   -1 on error
829  */
830 int INTERNET_ReadDataFromStream(int nDataSocket, LPVOID Buffer, DWORD BytesToRead)
831 {
832     if (INVALID_SOCKET == nDataSocket)
833         return SOCKET_ERROR;
834
835     return recv(nDataSocket, Buffer, BytesToRead, 0);
836 }
837
838
839 /***********************************************************************
840  *           INTERNET_SetLastError (internal)
841  *
842  * Set last thread specific error
843  *
844  * RETURNS
845  *
846  */
847 void INTERNET_SetLastError(DWORD dwError)
848 {
849     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
850
851     SetLastError(dwError);
852     lpwite->dwError = dwError;
853 }
854
855
856 /***********************************************************************
857  *           INTERNET_GetLastError (internal)
858  *
859  * Get last thread specific error
860  *
861  * RETURNS
862  *
863  */
864 DWORD INTERNET_GetLastError()
865 {
866     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
867     return lpwite->dwError;
868 }
869
870
871 /***********************************************************************
872  *           INTERNET_WorkerThreadFunc (internal)
873  *
874  * Worker thread execution function
875  *
876  * RETURNS
877  *
878  */
879 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
880 {
881     DWORD dwWaitRes;
882
883     while (1)
884     {
885         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
886
887         if (dwWaitRes == WAIT_OBJECT_0 + 1)
888             INTERNET_ExecuteWork();
889         else
890             break;
891
892         InterlockedIncrement(&dwNumIdleThreads);
893     }
894
895     InterlockedDecrement(&dwNumIdleThreads);
896     InterlockedDecrement(&dwNumThreads);
897     TRACE("Worker thread exiting\n");
898     return TRUE;
899 }
900
901
902 /***********************************************************************
903  *           INTERNET_InsertWorkRequest (internal)
904  *
905  * Insert work request into queue
906  *
907  * RETURNS
908  *
909  */
910 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
911 {
912     BOOL bSuccess = FALSE;
913     LPWORKREQUEST lpNewRequest;
914
915     TRACE("\n");
916
917     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
918     if (lpNewRequest)
919     {
920         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
921         lpNewRequest->prev = NULL;
922
923         EnterCriticalSection(&csQueue);
924
925         lpNewRequest->next = lpWorkQueueTail;
926         if (lpWorkQueueTail)
927             lpWorkQueueTail->prev = lpNewRequest;
928         lpWorkQueueTail = lpNewRequest;
929         if (!lpHeadWorkQueue)
930             lpHeadWorkQueue = lpWorkQueueTail;
931
932         LeaveCriticalSection(&csQueue);
933
934         bSuccess = TRUE;
935     }
936
937     return bSuccess;
938 }
939
940
941 /***********************************************************************
942  *           INTERNET_GetWorkRequest (internal)
943  *
944  * Retrieves work request from queue
945  *
946  * RETURNS
947  *
948  */
949 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
950 {
951     BOOL bSuccess = FALSE;
952     LPWORKREQUEST lpRequest = NULL;
953
954     TRACE("\n");
955
956     EnterCriticalSection(&csQueue);
957
958     if (lpHeadWorkQueue)
959     {
960         lpRequest = lpHeadWorkQueue;
961         lpHeadWorkQueue = lpHeadWorkQueue->prev;
962         if (lpRequest == lpWorkQueueTail)
963             lpWorkQueueTail = lpHeadWorkQueue;
964     }
965
966     LeaveCriticalSection(&csQueue);
967
968     if (lpRequest)
969     {
970         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
971         HeapFree(GetProcessHeap(), 0, lpRequest);
972         bSuccess = TRUE;
973     }
974
975     return bSuccess;
976 }
977
978
979 /***********************************************************************
980  *           INTERNET_AsyncCall (internal)
981  *
982  * Retrieves work request from queue
983  *
984  * RETURNS
985  *
986  */
987 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
988 {
989     HANDLE hThread;
990     DWORD dwTID;
991     BOOL bSuccess = FALSE;
992
993     TRACE("\n");
994
995     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
996     {
997         InterlockedIncrement(&dwNumIdleThreads);
998
999         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS || 
1000             !(hThread = CreateThread(NULL, 0, 
1001             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
1002         {
1003             InterlockedDecrement(&dwNumThreads);
1004             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
1005             goto lerror;
1006         }
1007
1008         TRACE("Created new thread\n");
1009     }
1010
1011     bSuccess = TRUE;
1012     INTERNET_InsertWorkRequest(lpWorkRequest);
1013     SetEvent(hWorkEvent);
1014
1015 lerror:
1016
1017     return bSuccess;
1018 }
1019
1020
1021 /***********************************************************************
1022  *           INTERNET_ExecuteWork (internal)
1023  *
1024  * RETURNS
1025  *
1026  */
1027 VOID INTERNET_ExecuteWork()
1028 {
1029     WORKREQUEST workRequest;
1030
1031     TRACE("\n");
1032
1033     if (INTERNET_GetWorkRequest(&workRequest))
1034     {
1035         switch (workRequest.asyncall)
1036         {
1037             case FTPPUTFILEA:
1038                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
1039                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
1040                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
1041                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
1042                 break;
1043
1044             case FTPSETCURRENTDIRECTORYA:
1045                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1046                         (LPCSTR)workRequest.LPSZDIRECTORY);
1047                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1048                 break;
1049
1050             case FTPCREATEDIRECTORYA:
1051                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1052                         (LPCSTR)workRequest.LPSZDIRECTORY);
1053                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1054                 break;
1055
1056             case FTPFINDFIRSTFILEA:
1057                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION, 
1058                         (LPCSTR)workRequest.LPSZSEARCHFILE, 
1059                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS, 
1060                    workRequest.DWCONTEXT);
1061                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
1062                 break;
1063
1064             case FTPGETCURRENTDIRECTORYA:
1065                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1066                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
1067                 break;
1068
1069             case FTPOPENFILEA:
1070                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
1071                     (LPCSTR)workRequest.LPSZFILENAME,
1072                     workRequest.FDWACCESS,
1073                     workRequest.DWFLAGS,
1074                     workRequest.DWCONTEXT);
1075                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1076                  break;
1077
1078             case FTPGETFILEA:
1079                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
1080                     (LPCSTR)workRequest.LPSZREMOTEFILE,
1081                     (LPCSTR)workRequest.LPSZNEWFILE,
1082                     (BOOL)workRequest.FFAILIFEXISTS,
1083                     workRequest.DWLOCALFLAGSATTRIBUTE,
1084                     workRequest.DWFLAGS,
1085                     workRequest.DWCONTEXT);
1086                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
1087                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
1088                 break;
1089
1090             case FTPDELETEFILEA:
1091                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
1092                         (LPCSTR)workRequest.LPSZFILENAME);
1093                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1094                 break;
1095
1096             case FTPREMOVEDIRECTORYA:
1097                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
1098                         (LPCSTR)workRequest.LPSZDIRECTORY);
1099                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1100                 break;
1101
1102             case FTPRENAMEFILEA:
1103                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
1104                         (LPCSTR)workRequest.LPSZSRCFILE,
1105                         (LPCSTR)workRequest.LPSZDESTFILE);
1106                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
1107                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
1108                 break;
1109
1110             case INTERNETFINDNEXTA:
1111                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
1112                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
1113                 break;
1114         }
1115     }
1116 }
1117
1118
1119 /***********************************************************************
1120  *          INTERNET_GetResponseBuffer
1121  *
1122  * RETURNS
1123  *
1124  */
1125 LPSTR INTERNET_GetResponseBuffer()
1126 {
1127     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1128     return lpwite->response;
1129 }