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