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