Fixed some issues found by winapi_check.
[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 #include <ctype.h>
19
20 #include "windows.h"
21 #include "wininet.h"
22 #include "debugtools.h"
23 #include "winerror.h"
24 #include "winsock.h"
25 #include "tchar.h"
26 #include "heap.h"
27
28 #include "internet.h"
29
30 DEFAULT_DEBUG_CHANNEL(wininet);
31
32 #define MAX_IDLE_WORKER 1000*60*1
33 #define MAX_WORKER_THREADS 10
34 #define RESPONSE_TIMEOUT        30
35
36 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
37 (LPWININETAPPINFOA)(((LPWININETFTPSESSIONA)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
38
39 typedef struct
40 {
41     DWORD  dwError;
42     CHAR   response[MAX_REPLY_LEN];
43 } WITHREADERROR, *LPWITHREADERROR;
44
45 INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp);
46 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData);
47 VOID INTERNET_ExecuteWork();
48
49 DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
50 DWORD dwNumThreads;
51 DWORD dwNumIdleThreads;
52 HANDLE hEventArray[2];
53 #define hQuitEvent hEventArray[0]
54 #define hWorkEvent hEventArray[1]
55 CRITICAL_SECTION csQueue;
56 LPWORKREQUEST lpHeadWorkQueue;
57 LPWORKREQUEST lpWorkQueueTail;
58
59 /***********************************************************************
60  * WININET_LibMain [Internal] Initializes the internal 'WININET.DLL'.
61  *
62  * PARAMS
63  *     hinstDLL    [I] handle to the 'dlls' instance
64  *     fdwReason   [I]
65  *     lpvReserved [I] reserverd, must be NULL
66  *
67  * RETURNS
68  *     Success: TRUE
69  *     Failure: FALSE
70  */
71
72 BOOL WINAPI
73 WININET_LibMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
74 {
75     TRACE("%x,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
76
77     switch (fdwReason) {
78         case DLL_PROCESS_ATTACH:
79
80             g_dwTlsErrIndex = TlsAlloc();
81
82             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
83                 return FALSE;
84
85             hQuitEvent = CreateEventA(0, TRUE, FALSE, NULL);
86             hWorkEvent = CreateEventA(0, FALSE, FALSE, NULL);
87             InitializeCriticalSection(&csQueue);
88
89             dwNumThreads = 0;
90             dwNumIdleThreads = 0;
91
92         case DLL_THREAD_ATTACH:
93             {
94                 LPWITHREADERROR lpwite = HeapAlloc(GetProcessHeap(), 0, sizeof(WITHREADERROR));
95                 if (NULL == lpwite) 
96                     return FALSE;
97
98                 TlsSetValue(g_dwTlsErrIndex, (LPVOID)lpwite);
99             }
100             break;
101
102         case DLL_THREAD_DETACH:
103             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
104                         {
105                                 LPVOID lpwite = TlsGetValue(g_dwTlsErrIndex);
106                                 if (lpwite)
107                    HeapFree(GetProcessHeap(), 0, lpwite);
108                         }
109             break;
110
111         case DLL_PROCESS_DETACH:
112
113             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
114             {
115                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
116                 TlsFree(g_dwTlsErrIndex);
117             }
118
119             SetEvent(hQuitEvent);
120
121             CloseHandle(hQuitEvent);
122             CloseHandle(hWorkEvent);
123             DeleteCriticalSection(&csQueue);
124             break;
125     }
126
127     return TRUE;
128 }
129
130
131 /***********************************************************************
132  *           InternetOpenA   (WININET.113)
133  *
134  * Per-application initialization of wininet
135  *
136  * RETURNS
137  *    HINTERNET on success
138  *    NULL on failure
139  *
140  */
141 INTERNETAPI HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, 
142         DWORD dwAccessType, LPCSTR lpszProxy,
143         LPCSTR lpszProxyBypass, DWORD dwFlags)
144 {
145     LPWININETAPPINFOA lpwai = NULL;
146
147     TRACE("\n");
148
149     /* Clear any error information */
150     INTERNET_SetLastError(0);
151
152     lpwai = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETAPPINFOA));
153     if (NULL == lpwai)
154         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
155     else
156     {
157         memset(lpwai, 0, sizeof(WININETAPPINFOA));
158         lpwai->hdr.htype = WH_HINIT;
159         lpwai->hdr.lpwhparent = NULL;
160         lpwai->hdr.dwFlags = dwFlags;
161         if (NULL != lpszAgent)
162             lpwai->lpszAgent = HEAP_strdupA(GetProcessHeap(),0,lpszAgent);
163         if (NULL != lpszProxy)
164             lpwai->lpszProxy = HEAP_strdupA(GetProcessHeap(),0,lpszProxy);
165         if (NULL != lpszProxyBypass)
166             lpwai->lpszProxyBypass = HEAP_strdupA(GetProcessHeap(),0,lpszProxyBypass);
167         lpwai->dwAccessType = dwAccessType;
168     }
169
170     return (HINTERNET)lpwai;
171 }
172
173
174 /***********************************************************************
175  *           InternetGetLastResponseInfoA (WININET.108)
176  *
177  * Return last wininet error description on the calling thread
178  *
179  * RETURNS
180  *    TRUE on success of writting to buffer
181  *    FALSE on failure
182  *
183  */
184 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
185     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
186 {
187     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
188
189     TRACE("\n");
190
191     *lpdwError = lpwite->dwError;
192     if (lpwite->dwError)
193     {
194         strncpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
195         *lpdwBufferLength = strlen(lpszBuffer);
196     }
197     else
198         *lpdwBufferLength = 0;
199
200     return TRUE;
201 }
202
203
204 /***********************************************************************
205  *           InternetGetConnectedState (WININET.103)
206  *
207  * Return connected state
208  *
209  * RETURNS
210  *    TRUE if connected
211  *    if lpdwStatus is not null, return the status (off line, 
212  *    modem, lan...) in it.
213  *    FALSE if not connected
214  */
215 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
216 {
217     FIXME("Stub\n");
218     return FALSE;
219 }
220
221
222 /***********************************************************************
223  *           InternetConnectA (WININET.93)
224  *
225  * Open a ftp, gopher or http session
226  *
227  * RETURNS
228  *    HINTERNET a session handle on success
229  *    NULL on failure
230  *
231  */
232 INTERNETAPI HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
233     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
234     LPCSTR lpszUserName, LPCSTR lpszPassword,
235     DWORD dwService, DWORD dwFlags, DWORD dwContext)
236 {
237     HINTERNET rc = (HINTERNET) NULL;
238
239     TRACE("\n");
240
241     /* Clear any error information */
242     INTERNET_SetLastError(0);
243
244     switch (dwService)
245     {
246         case INTERNET_SERVICE_FTP:
247             rc = FTP_Connect(hInternet, lpszServerName, nServerPort,
248             lpszUserName, lpszPassword, dwFlags, dwContext);
249             break;
250
251         case INTERNET_SERVICE_HTTP:
252             rc = HTTP_Connect(hInternet, lpszServerName, nServerPort,
253             lpszUserName, lpszPassword, dwFlags, dwContext);
254             break;
255
256         case INTERNET_SERVICE_GOPHER:
257         default:
258             break;
259     }
260
261     return rc;
262 }
263
264 /***********************************************************************
265  *           InternetFindNextFileA (WININET.102)
266  *
267  * Continues a file search from a previous call to FindFirstFile
268  *
269  * RETURNS
270  *    TRUE on success
271  *    FALSE on failure
272  *
273  */
274 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
275 {
276     LPWININETAPPINFOA hIC = NULL;
277     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
278
279     TRACE("\n");
280
281     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
282     {
283         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
284         return FALSE;
285     }
286
287     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
288     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
289     {
290         WORKREQUEST workRequest;
291
292         workRequest.asyncall = INTERNETFINDNEXTA;
293         workRequest.HFTPSESSION = (DWORD)hFind;
294         workRequest.LPFINDFILEDATA = (DWORD)lpvFindData;
295
296         return INTERNET_AsyncCall(&workRequest);
297     }
298     else
299     {
300         return INTERNET_FindNextFileA(hFind, lpvFindData);
301     }
302 }
303
304 /***********************************************************************
305  *           INTERNET_FindNextFileA (Internal)
306  *
307  * Continues a file search from a previous call to FindFirstFile
308  *
309  * RETURNS
310  *    TRUE on success
311  *    FALSE on failure
312  *
313  */
314 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
315 {
316     BOOL bSuccess = TRUE;
317     LPWININETAPPINFOA hIC = NULL;
318     LPWIN32_FIND_DATAA lpFindFileData;
319     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
320
321     TRACE("\n");
322
323     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
324     {
325         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
326         return FALSE;
327     }
328
329     /* Clear any error information */
330     INTERNET_SetLastError(0);
331
332     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
333     {
334         FIXME("Only FTP find next supported\n");
335         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
336         return FALSE;
337     }
338
339     TRACE("index(%d) size(%ld)\n", lpwh->index, lpwh->size);
340
341     lpFindFileData = (LPWIN32_FIND_DATAA) lpvFindData;
342     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
343
344     if (lpwh->index >= lpwh->size)
345     {
346         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
347         bSuccess = FALSE;
348         goto lend;
349     }
350
351     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
352     lpwh->index++;
353
354     TRACE("\nName: %s\nSize: %ld\n", lpFindFileData->cFileName, lpFindFileData->nFileSizeLow);
355
356 lend:
357
358     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
359     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC && hIC->lpfnStatusCB)
360     {   
361         INTERNET_ASYNC_RESULT iar;
362
363         iar.dwResult = (DWORD)bSuccess;
364         iar.dwError = bSuccess ? ERROR_SUCCESS : INTERNET_GetLastError();
365
366         hIC->lpfnStatusCB(hFind, lpwh->hdr.dwContext, INTERNET_STATUS_REQUEST_COMPLETE,
367         &iar, sizeof(INTERNET_ASYNC_RESULT));
368     }
369
370     return bSuccess;
371 }
372
373
374 /***********************************************************************
375  *           INTERNET_CloseHandle (internal)
376  *
377  * Close internet handle
378  *
379  * RETURNS
380  *    Void
381  *
382  */
383 VOID INTERNET_CloseHandle(LPWININETAPPINFOA lpwai)
384 {
385     if (lpwai->lpszAgent)
386         HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
387
388     if (lpwai->lpszProxy)
389         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
390
391     if (lpwai->lpszProxyBypass)
392         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
393
394     HeapFree(GetProcessHeap(), 0, lpwai);
395 }
396
397
398 /***********************************************************************
399  *           InternetCloseHandle (WININET.89)
400  *
401  * Generic close handle function
402  *
403  * RETURNS
404  *    TRUE on success
405  *    FALSE on failure
406  *
407  */
408 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
409 {
410     BOOL retval = FALSE;
411     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hInternet;
412
413     TRACE("\n");
414     if (NULL == lpwh)
415         return FALSE;
416
417     /* Clear any error information */
418     INTERNET_SetLastError(0);
419
420     switch (lpwh->htype)
421     {
422         case WH_HINIT:
423             INTERNET_CloseHandle((LPWININETAPPINFOA) lpwh);
424             break; 
425
426         case WH_HHTTPSESSION:
427             HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
428             break;
429
430         case WH_HHTTPREQ:
431             HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
432             break;
433
434         case WH_HFTPSESSION:
435             retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
436             break;
437
438         case WH_HFINDNEXT:
439             retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
440             break;
441                 
442         default:
443             break;
444     }
445
446     return retval;
447 }
448
449
450 /***********************************************************************
451  *           SetUrlComponentValue (Internal)
452  *
453  * Helper function for InternetCrackUrlA
454  *
455  * RETURNS
456  *    TRUE on success
457  *    FALSE on failure
458  *
459  */
460 BOOL SetUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen, LPCSTR lpszStart, INT len)
461 {
462     TRACE("%s (%d)\n", lpszStart, len);
463
464     if (*dwComponentLen != 0)
465     {
466         if (*lppszComponent == NULL)
467         {
468             *lppszComponent = (LPSTR)lpszStart;
469             *dwComponentLen = len;
470         }
471         else
472         {
473             INT ncpylen = min((*dwComponentLen)-1, len);
474             strncpy(*lppszComponent, lpszStart, ncpylen);
475             (*lppszComponent)[ncpylen] = '\0';
476             *dwComponentLen = ncpylen;
477         }
478     }
479
480     return TRUE;
481 }
482
483
484 /***********************************************************************
485  *           InternetCrackUrlA (WININET.95)
486  *
487  * Break up URL into its components
488  *
489  * TODO: Hadnle dwFlags
490  *
491  * RETURNS
492  *    TRUE on success
493  *    FALSE on failure
494  *
495  */
496 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags, 
497                 LPURL_COMPONENTSA lpUrlComponents)
498 {
499   /*
500    * RFC 1808
501    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
502    *
503    */
504     LPSTR lpszParam    = NULL;
505     BOOL  bIsAbsolute = FALSE;
506     LPSTR lpszap = (char*)lpszUrl;
507     LPSTR lpszcp = NULL;
508
509     TRACE("\n");
510
511     /* Determine if the URI is absolute. */
512     while (*lpszap != '\0')
513     {
514         if (isalnum(*lpszap))
515         {
516             lpszap++;
517             continue;
518         }
519         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
520         {
521             bIsAbsolute = TRUE;
522             lpszcp = lpszap;
523         }
524         else
525         {
526             lpszcp = (LPSTR)lpszUrl; /* Relative url */
527         }
528
529         break;
530     }
531
532     /* Parse <params> */
533     lpszParam = strpbrk(lpszap, ";?");
534     if (lpszParam != NULL)
535     {
536         if (!SetUrlComponentValue(&lpUrlComponents->lpszExtraInfo, 
537              &lpUrlComponents->dwExtraInfoLength, lpszParam+1, strlen(lpszParam+1)))
538         {
539             return FALSE;
540         }
541         }
542
543     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
544         {
545         LPSTR lpszNetLoc;
546
547         /* Get scheme first. */
548         lpUrlComponents->nScheme = GetInternetScheme(lpszUrl, lpszcp - lpszUrl);
549         if (!SetUrlComponentValue(&lpUrlComponents->lpszScheme, 
550                     &lpUrlComponents->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
551             return FALSE;
552
553         /* Eat ':' in protocol. */
554         lpszcp++;
555
556         /* Skip over slashes. */
557         if (*lpszcp == '/')
558         {
559             lpszcp++;
560             if (*lpszcp == '/')
561             {
562                 lpszcp++;
563                 if (*lpszcp == '/')
564                     lpszcp++;
565             }
566         }
567
568         lpszNetLoc = strpbrk(lpszcp, "/");
569         if (lpszParam)
570         {
571             if (lpszNetLoc)
572                lpszNetLoc = min(lpszNetLoc, lpszParam);
573         else
574                lpszNetLoc = lpszParam;
575         }
576         else if (!lpszNetLoc)
577             lpszNetLoc = lpszcp + strlen(lpszcp);
578
579         /* Parse net-loc */
580         if (lpszNetLoc)
581         {
582             LPSTR lpszHost;
583             LPSTR lpszPort;
584
585                 /* [<user>[<:password>]@]<host>[:<port>] */
586             /* First find the user and password if they exist */
587                         
588             lpszHost = strchr(lpszcp, '@');
589             if (lpszHost == NULL || lpszHost > lpszNetLoc)
590                 {
591                 /* username and password not specified. */
592                 SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
593                         &lpUrlComponents->dwUserNameLength, NULL, 0);
594                 SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
595                         &lpUrlComponents->dwPasswordLength, NULL, 0);
596                 }
597             else /* Parse out username and password */
598                 {
599                 LPSTR lpszUser = lpszcp;
600                 LPSTR lpszPasswd = lpszHost;
601
602                 while (lpszcp < lpszHost)
603                         {
604                    if (*lpszcp == ':')
605                        lpszPasswd = lpszcp;
606
607                    lpszcp++;
608                     }        
609                     
610                 SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
611                         &lpUrlComponents->dwUserNameLength, lpszUser, lpszPasswd - lpszUser);
612
613                 SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
614                         &lpUrlComponents->dwPasswordLength, 
615                         lpszPasswd == lpszHost ? NULL : ++lpszPasswd, 
616                         lpszHost - lpszPasswd);
617
618                 lpszcp++; /* Advance to beginning of host */
619                 }
620
621             /* Parse <host><:port> */
622
623             lpszHost = lpszcp;
624             lpszPort = lpszNetLoc;
625
626             while (lpszcp < lpszNetLoc)
627                     {
628                 if (*lpszcp == ':')
629                     lpszPort = lpszcp;
630
631                 lpszcp++;
632                 }
633
634             SetUrlComponentValue(&lpUrlComponents->lpszHostName, 
635                &lpUrlComponents->dwHostNameLength, lpszHost, lpszPort - lpszHost);
636
637             if (lpszPort != lpszNetLoc)
638                 lpUrlComponents->nPort = atoi(++lpszPort);
639             }
640         }
641
642     /* Here lpszcp points to:
643      *
644      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
645      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
646      */
647     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
648     {
649         if (!SetUrlComponentValue(&lpUrlComponents->lpszUrlPath, 
650          &lpUrlComponents->dwUrlPathLength, lpszcp, 
651          lpszParam ? lpszParam - lpszcp : strlen(lpszcp)))
652          return FALSE;
653     }
654     else
655     {
656         lpUrlComponents->dwUrlPathLength = 0;
657     }
658
659     TRACE("%s: host(%s) path(%s)\n", lpszUrl, lpUrlComponents->lpszHostName, lpUrlComponents->lpszUrlPath);
660
661     return TRUE;
662 }
663
664
665 /***********************************************************************
666  *           InternetAttemptConnect (WININET.81)
667  *
668  * Attempt to make a connection to the internet
669  *
670  * RETURNS
671  *    ERROR_SUCCESS on success
672  *    Error value   on failure
673  *
674  */
675 INTERNETAPI DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
676 {
677     FIXME("Stub\n");
678     return ERROR_SUCCESS;
679 }
680
681
682 /***********************************************************************
683  *           InternetCanonicalizeUrlA (WININET.85)
684  *
685  * Escape unsafe characters and spaces
686  *
687  * RETURNS
688  *    TRUE on success
689  *    FALSE on failure
690  *
691  */
692 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
693         LPDWORD lpdwBufferLength, DWORD dwFlags)
694 {
695     BOOL bSuccess = FALSE;
696
697     FIXME("Stub!\n");
698
699     if (lpszUrl)
700     {
701         strncpy(lpszBuffer, lpszUrl, *lpdwBufferLength);
702         *lpdwBufferLength = strlen(lpszBuffer);
703         bSuccess = TRUE;
704     }
705
706     return bSuccess;
707 }
708
709
710 /***********************************************************************
711  *           InternetSetStatusCallback (WININET.133)
712  *
713  * Sets up a callback function which is called as progress is made
714  * during an operation. 
715  *
716  * RETURNS
717  *    Previous callback or NULL         on success
718  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
719  *
720  */
721 INTERNETAPI INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallback(
722         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
723 {
724     INTERNET_STATUS_CALLBACK retVal;
725     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
726
727     TRACE("0x%08lx\n", (ULONG)hInternet);
728     if (lpwai->hdr.htype != WH_HINIT)
729         return INTERNET_INVALID_STATUS_CALLBACK; 
730
731     retVal = lpwai->lpfnStatusCB;
732     lpwai->lpfnStatusCB = lpfnIntCB;
733
734     return retVal;
735 }
736
737
738 /***********************************************************************
739  *           InternetWriteFile (WININET.138)
740  *
741  * Write data to an open internet file 
742  *
743  * RETURNS
744  *    TRUE  on success
745  *    FALSE on failure
746  *
747  */
748 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
749         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
750 {
751     BOOL retval = FALSE;
752     int nSocket = INVALID_SOCKET;
753     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
754
755     TRACE("\n");
756     if (NULL == lpwh)
757         return FALSE;
758
759     switch (lpwh->htype)
760     {
761         case WH_HHTTPREQ:
762             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
763             break;
764
765         case WH_HFILE:
766             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
767             break;
768
769         default:
770             break;
771     }
772
773     if (INVALID_SOCKET != nSocket)
774     {
775         *lpdwNumOfBytesWritten = INTERNET_WriteDataToStream(nSocket, lpBuffer, dwNumOfBytesToWrite);
776         if (*lpdwNumOfBytesWritten < 0)
777             *lpdwNumOfBytesWritten = 0;
778         else
779             retval = TRUE;
780     }
781
782     return retval;
783 }
784
785
786 /***********************************************************************
787  *           InternetReadFile (WININET.121)
788  *
789  * Read data from an open internet file 
790  *
791  * RETURNS
792  *    TRUE  on success
793  *    FALSE on failure
794  *
795  */
796 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, 
797         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
798 {
799     BOOL retval = FALSE;
800     int nSocket = INVALID_SOCKET;
801     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
802
803     TRACE("\n");
804     if (NULL == lpwh)
805         return FALSE;
806
807     switch (lpwh->htype)
808     {
809         case WH_HHTTPREQ:
810             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
811             break;
812
813         case WH_HFILE:
814             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
815             break;
816
817         default:
818             break;
819     }
820
821     if (INVALID_SOCKET != nSocket)
822     {
823         *dwNumOfBytesRead = INTERNET_ReadDataFromStream(nSocket, lpBuffer, dwNumOfBytesToRead);
824         if (*dwNumOfBytesRead < 0)
825             *dwNumOfBytesRead = 0;
826         else
827             retval = TRUE;
828     }
829
830     return retval;
831 }
832
833
834 /***********************************************************************
835  *           InternetQueryOptionA
836  *
837  * Queries an options on the specified handle
838  *
839  * RETURNS
840  *    TRUE  on success
841  *    FALSE on failure
842  *
843  */
844 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption, 
845         LPVOID lpBuffer, LPDWORD lpdwBufferLength)
846 {
847     LPWININETHANDLEHEADER lpwhh;
848     BOOL bSuccess = FALSE;
849
850     TRACE("0x%08lx\n", dwOption);
851
852     if (NULL == hInternet)
853     {
854         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
855         return FALSE;
856     }
857
858     lpwhh = (LPWININETHANDLEHEADER) hInternet;
859
860     switch (dwOption)
861     {
862        case INTERNET_OPTION_HANDLE_TYPE:
863        {
864          ULONG type = lpwhh->htype;
865          TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
866
867          if (*lpdwBufferLength < sizeof(ULONG))
868              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
869          else
870          {
871              memcpy(lpBuffer, &type, sizeof(ULONG));
872              *lpdwBufferLength = sizeof(ULONG);
873              bSuccess = TRUE;
874          }
875
876          break;
877        }
878
879        default:
880          FIXME("Stub!");
881          break;
882     }
883
884     return bSuccess;
885 }
886
887
888 /***********************************************************************
889  *           GetInternetScheme (internal)
890  *
891  * Get scheme of url
892  *
893  * RETURNS
894  *    scheme on success
895  *    INTERNET_SCHEME_UNKNOWN on failure
896  *
897  */
898 INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp)
899 {
900     if(lpszScheme==NULL)
901         return INTERNET_SCHEME_UNKNOWN;
902
903     if (!_strnicmp("ftp", lpszScheme, nMaxCmp))
904         return INTERNET_SCHEME_FTP;
905     else if (!_strnicmp("gopher", lpszScheme, nMaxCmp))
906         return INTERNET_SCHEME_GOPHER;
907     else if (!_strnicmp("http", lpszScheme, nMaxCmp))
908         return INTERNET_SCHEME_HTTP;
909     else if (!_strnicmp("https", lpszScheme, nMaxCmp))
910         return INTERNET_SCHEME_HTTPS;
911     else if (!_strnicmp("file", lpszScheme, nMaxCmp))
912         return INTERNET_SCHEME_FILE;
913     else if (!_strnicmp("news", lpszScheme, nMaxCmp))
914         return INTERNET_SCHEME_NEWS;
915     else if (!_strnicmp("mailto", lpszScheme, nMaxCmp))
916         return INTERNET_SCHEME_MAILTO;
917     else
918         return INTERNET_SCHEME_UNKNOWN;
919 }
920
921
922 /***********************************************************************
923  *           INTERNET_WriteDataToStream (internal)
924  *
925  * Send data to server
926  *
927  * RETURNS
928  *
929  *   number of characters sent on success
930  *   -1 on error
931  */
932 int INTERNET_WriteDataToStream(int nDataSocket, LPCVOID Buffer, DWORD BytesToWrite)
933 {
934     if (INVALID_SOCKET == nDataSocket)
935         return SOCKET_ERROR;
936
937     return send(nDataSocket, Buffer, BytesToWrite, 0);
938 }
939
940
941 /***********************************************************************
942  *           INTERNET_ReadDataFromStream (internal)
943  *
944  * Read data from http server
945  *
946  * RETURNS
947  *
948  *   number of characters sent on success
949  *   -1 on error
950  */
951 int INTERNET_ReadDataFromStream(int nDataSocket, LPVOID Buffer, DWORD BytesToRead)
952 {
953     if (INVALID_SOCKET == nDataSocket)
954         return SOCKET_ERROR;
955
956     return recv(nDataSocket, Buffer, BytesToRead, 0);
957 }
958
959
960 /***********************************************************************
961  *           INTERNET_SetLastError (internal)
962  *
963  * Set last thread specific error
964  *
965  * RETURNS
966  *
967  */
968 void INTERNET_SetLastError(DWORD dwError)
969 {
970     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
971
972     SetLastError(dwError);
973     lpwite->dwError = dwError;
974 }
975
976
977 /***********************************************************************
978  *           INTERNET_GetLastError (internal)
979  *
980  * Get last thread specific error
981  *
982  * RETURNS
983  *
984  */
985 DWORD INTERNET_GetLastError()
986 {
987     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
988     return lpwite->dwError;
989 }
990
991
992 /***********************************************************************
993  *           INTERNET_WorkerThreadFunc (internal)
994  *
995  * Worker thread execution function
996  *
997  * RETURNS
998  *
999  */
1000 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1001 {
1002     DWORD dwWaitRes;
1003
1004     while (1)
1005     {
1006         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
1007
1008         if (dwWaitRes == WAIT_OBJECT_0 + 1)
1009             INTERNET_ExecuteWork();
1010         else
1011             break;
1012
1013         InterlockedIncrement(&dwNumIdleThreads);
1014     }
1015
1016     InterlockedDecrement(&dwNumIdleThreads);
1017     InterlockedDecrement(&dwNumThreads);
1018     TRACE("Worker thread exiting\n");
1019     return TRUE;
1020 }
1021
1022
1023 /***********************************************************************
1024  *           INTERNET_InsertWorkRequest (internal)
1025  *
1026  * Insert work request into queue
1027  *
1028  * RETURNS
1029  *
1030  */
1031 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
1032 {
1033     BOOL bSuccess = FALSE;
1034     LPWORKREQUEST lpNewRequest;
1035
1036     TRACE("\n");
1037
1038     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
1039     if (lpNewRequest)
1040     {
1041         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
1042         lpNewRequest->prev = NULL;
1043
1044         EnterCriticalSection(&csQueue);
1045
1046         lpNewRequest->next = lpWorkQueueTail;
1047         if (lpWorkQueueTail)
1048             lpWorkQueueTail->prev = lpNewRequest;
1049         lpWorkQueueTail = lpNewRequest;
1050         if (!lpHeadWorkQueue)
1051             lpHeadWorkQueue = lpWorkQueueTail;
1052
1053         LeaveCriticalSection(&csQueue);
1054
1055         bSuccess = TRUE;
1056     }
1057
1058     return bSuccess;
1059 }
1060
1061
1062 /***********************************************************************
1063  *           INTERNET_GetWorkRequest (internal)
1064  *
1065  * Retrieves work request from queue
1066  *
1067  * RETURNS
1068  *
1069  */
1070 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
1071 {
1072     BOOL bSuccess = FALSE;
1073     LPWORKREQUEST lpRequest = NULL;
1074
1075     TRACE("\n");
1076
1077     EnterCriticalSection(&csQueue);
1078
1079     if (lpHeadWorkQueue)
1080     {
1081         lpRequest = lpHeadWorkQueue;
1082         lpHeadWorkQueue = lpHeadWorkQueue->prev;
1083         if (lpRequest == lpWorkQueueTail)
1084             lpWorkQueueTail = lpHeadWorkQueue;
1085     }
1086
1087     LeaveCriticalSection(&csQueue);
1088
1089     if (lpRequest)
1090     {
1091         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
1092         HeapFree(GetProcessHeap(), 0, lpRequest);
1093         bSuccess = TRUE;
1094     }
1095
1096     return bSuccess;
1097 }
1098
1099
1100 /***********************************************************************
1101  *           INTERNET_AsyncCall (internal)
1102  *
1103  * Retrieves work request from queue
1104  *
1105  * RETURNS
1106  *
1107  */
1108 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
1109 {
1110     HANDLE hThread;
1111     DWORD dwTID;
1112     BOOL bSuccess = FALSE;
1113
1114     TRACE("\n");
1115
1116     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
1117     {
1118         InterlockedIncrement(&dwNumIdleThreads);
1119
1120         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS || 
1121             !(hThread = CreateThread(NULL, 0, 
1122             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
1123         {
1124             InterlockedDecrement(&dwNumThreads);
1125             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
1126             goto lerror;
1127         }
1128
1129         TRACE("Created new thread\n");
1130     }
1131
1132     bSuccess = TRUE;
1133     INTERNET_InsertWorkRequest(lpWorkRequest);
1134     SetEvent(hWorkEvent);
1135
1136 lerror:
1137
1138     return bSuccess;
1139 }
1140
1141
1142 /***********************************************************************
1143  *           INTERNET_ExecuteWork (internal)
1144  *
1145  * RETURNS
1146  *
1147  */
1148 VOID INTERNET_ExecuteWork()
1149 {
1150     WORKREQUEST workRequest;
1151
1152     TRACE("\n");
1153
1154     if (INTERNET_GetWorkRequest(&workRequest))
1155     {
1156         switch (workRequest.asyncall)
1157         {
1158             case FTPPUTFILEA:
1159                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
1160                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
1161                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
1162                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
1163                 break;
1164
1165             case FTPSETCURRENTDIRECTORYA:
1166                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1167                         (LPCSTR)workRequest.LPSZDIRECTORY);
1168                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1169                 break;
1170
1171             case FTPCREATEDIRECTORYA:
1172                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1173                         (LPCSTR)workRequest.LPSZDIRECTORY);
1174                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1175                 break;
1176
1177             case FTPFINDFIRSTFILEA:
1178                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION, 
1179                         (LPCSTR)workRequest.LPSZSEARCHFILE, 
1180                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS, 
1181                    workRequest.DWCONTEXT);
1182                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
1183                 break;
1184
1185             case FTPGETCURRENTDIRECTORYA:
1186                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1187                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
1188                 break;
1189
1190             case FTPOPENFILEA:
1191                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
1192                     (LPCSTR)workRequest.LPSZFILENAME,
1193                     workRequest.FDWACCESS,
1194                     workRequest.DWFLAGS,
1195                     workRequest.DWCONTEXT);
1196                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1197                  break;
1198
1199             case FTPGETFILEA:
1200                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
1201                     (LPCSTR)workRequest.LPSZREMOTEFILE,
1202                     (LPCSTR)workRequest.LPSZNEWFILE,
1203                     (BOOL)workRequest.FFAILIFEXISTS,
1204                     workRequest.DWLOCALFLAGSATTRIBUTE,
1205                     workRequest.DWFLAGS,
1206                     workRequest.DWCONTEXT);
1207                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
1208                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
1209                 break;
1210
1211             case FTPDELETEFILEA:
1212                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
1213                         (LPCSTR)workRequest.LPSZFILENAME);
1214                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1215                 break;
1216
1217             case FTPREMOVEDIRECTORYA:
1218                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
1219                         (LPCSTR)workRequest.LPSZDIRECTORY);
1220                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1221                 break;
1222
1223             case FTPRENAMEFILEA:
1224                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
1225                         (LPCSTR)workRequest.LPSZSRCFILE,
1226                         (LPCSTR)workRequest.LPSZDESTFILE);
1227                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
1228                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
1229                 break;
1230
1231             case INTERNETFINDNEXTA:
1232                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
1233                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
1234                 break;
1235
1236             case HTTPSENDREQUESTA:
1237                HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION, 
1238                        (LPCSTR)workRequest.LPSZHEADER,
1239                        workRequest.DWHEADERLENGTH,     
1240                        (LPVOID)workRequest.LPOPTIONAL, 
1241                        workRequest.DWOPTIONALLENGTH);
1242                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
1243                break;
1244
1245             case HTTPOPENREQUESTA:
1246                HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION, 
1247                        (LPCSTR)workRequest.LPSZVERB,
1248                        (LPCSTR)workRequest.LPSZOBJECTNAME,     
1249                        (LPCSTR)workRequest.LPSZVERSION, 
1250                        (LPCSTR)workRequest.LPSZREFERRER, 
1251                        (LPCSTR*)workRequest.LPSZACCEPTTYPES, 
1252                        workRequest.DWFLAGS,
1253                        workRequest.DWCONTEXT);
1254                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
1255                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
1256                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
1257                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
1258                 break;
1259
1260         }
1261     }
1262 }
1263
1264
1265 /***********************************************************************
1266  *          INTERNET_GetResponseBuffer
1267  *
1268  * RETURNS
1269  *
1270  */
1271 LPSTR INTERNET_GetResponseBuffer()
1272 {
1273     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1274     return lpwite->response;
1275 }
1276
1277
1278 /***********************************************************************
1279  *           INTERNET_GetNextLine  (internal)
1280  *
1281  * Parse next line in directory string listing
1282  *
1283  * RETURNS
1284  *   Pointer to begining of next line
1285  *   NULL on failure
1286  *
1287  */
1288
1289 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
1290 {
1291     struct timeval tv;
1292     fd_set infd;
1293     BOOL bSuccess = FALSE;
1294     INT nRecv = 0;
1295
1296     TRACE("\n");
1297
1298     FD_ZERO(&infd);
1299     FD_SET(nSocket, &infd);
1300     tv.tv_sec=RESPONSE_TIMEOUT;
1301     tv.tv_usec=0;
1302
1303     while (nRecv < *dwBuffer)
1304     {
1305         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
1306         {
1307             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
1308             {
1309                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1310                 goto lend;
1311             }
1312
1313             if (lpszBuffer[nRecv] == '\n')
1314             {
1315                 bSuccess = TRUE;
1316                 break;
1317             }
1318             if (lpszBuffer[nRecv] != '\r')
1319                 nRecv++;
1320         }
1321         else
1322         {
1323             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
1324             goto lend;
1325 }
1326     }
1327
1328 lend:
1329     if (bSuccess)
1330     {
1331         lpszBuffer[nRecv] = '\0';
1332         *dwBuffer = nRecv - 1;
1333         TRACE(":%d %s\n", nRecv, lpszBuffer);
1334         return lpszBuffer;
1335     }
1336     else
1337     {
1338         return NULL;
1339     }
1340 }
1341