Fixed the displaying of the FOURCC codes in _dump_pixelformat.
[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
21 #include "windows.h"
22 #include "wininet.h"
23 #include "debugtools.h"
24 #include "winerror.h"
25 #include "winsock.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 DLL's instance
64  *     fdwReason   [I]
65  *     lpvReserved [I] reserved, 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         INT len;
650
651         /* Only truncate the parameter list if it's already been saved
652          * in lpUrlComponents->lpszExtraInfo.
653          */
654         if (lpszParam && lpUrlComponents->dwExtraInfoLength)
655             len = lpszParam - lpszcp;
656         else
657         {
658             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
659              * newlines if necessary.
660              */
661             LPSTR lpsznewline = strchr (lpszcp, '\n');
662             if (lpsznewline != NULL)
663                 len = lpsznewline - lpszcp;
664             else
665                 len = strlen(lpszcp);
666         }
667
668         if (!SetUrlComponentValue(&lpUrlComponents->lpszUrlPath, 
669          &lpUrlComponents->dwUrlPathLength, lpszcp, len))
670          return FALSE;
671     }
672     else
673     {
674         lpUrlComponents->dwUrlPathLength = 0;
675     }
676
677     TRACE("%s: host(%s) path(%s) extra(%s)\n", lpszUrl, lpUrlComponents->lpszHostName,
678           lpUrlComponents->lpszUrlPath, lpUrlComponents->lpszExtraInfo);
679
680     return TRUE;
681 }
682
683
684 /***********************************************************************
685  *           InternetAttemptConnect (WININET.81)
686  *
687  * Attempt to make a connection to the internet
688  *
689  * RETURNS
690  *    ERROR_SUCCESS on success
691  *    Error value   on failure
692  *
693  */
694 INTERNETAPI DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
695 {
696     FIXME("Stub\n");
697     return ERROR_SUCCESS;
698 }
699
700
701 /***********************************************************************
702  *           InternetCanonicalizeUrlA (WININET.85)
703  *
704  * Escape unsafe characters and spaces
705  *
706  * RETURNS
707  *    TRUE on success
708  *    FALSE on failure
709  *
710  */
711 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
712         LPDWORD lpdwBufferLength, DWORD dwFlags)
713 {
714     BOOL bSuccess = FALSE;
715
716     FIXME("Stub!\n");
717
718     if (lpszUrl)
719     {
720         strncpy(lpszBuffer, lpszUrl, *lpdwBufferLength);
721         *lpdwBufferLength = strlen(lpszBuffer);
722         bSuccess = TRUE;
723     }
724
725     return bSuccess;
726 }
727
728
729 /***********************************************************************
730  *           InternetSetStatusCallback (WININET.133)
731  *
732  * Sets up a callback function which is called as progress is made
733  * during an operation. 
734  *
735  * RETURNS
736  *    Previous callback or NULL         on success
737  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
738  *
739  */
740 INTERNETAPI INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallback(
741         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
742 {
743     INTERNET_STATUS_CALLBACK retVal;
744     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
745
746     TRACE("0x%08lx\n", (ULONG)hInternet);
747     if (lpwai->hdr.htype != WH_HINIT)
748         return INTERNET_INVALID_STATUS_CALLBACK; 
749
750     retVal = lpwai->lpfnStatusCB;
751     lpwai->lpfnStatusCB = lpfnIntCB;
752
753     return retVal;
754 }
755
756
757 /***********************************************************************
758  *           InternetWriteFile (WININET.138)
759  *
760  * Write data to an open internet file 
761  *
762  * RETURNS
763  *    TRUE  on success
764  *    FALSE on failure
765  *
766  */
767 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
768         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
769 {
770     BOOL retval = FALSE;
771     int nSocket = INVALID_SOCKET;
772     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
773
774     TRACE("\n");
775     if (NULL == lpwh)
776         return FALSE;
777
778     switch (lpwh->htype)
779     {
780         case WH_HHTTPREQ:
781             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
782             break;
783
784         case WH_HFILE:
785             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
786             break;
787
788         default:
789             break;
790     }
791
792     if (INVALID_SOCKET != nSocket)
793     {
794         *lpdwNumOfBytesWritten = INTERNET_WriteDataToStream(nSocket, lpBuffer, dwNumOfBytesToWrite);
795         if (*lpdwNumOfBytesWritten < 0)
796             *lpdwNumOfBytesWritten = 0;
797         else
798             retval = TRUE;
799     }
800
801     return retval;
802 }
803
804
805 /***********************************************************************
806  *           InternetReadFile (WININET.121)
807  *
808  * Read data from an open internet file 
809  *
810  * RETURNS
811  *    TRUE  on success
812  *    FALSE on failure
813  *
814  */
815 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, 
816         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
817 {
818     BOOL retval = FALSE;
819     int nSocket = INVALID_SOCKET;
820     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
821
822     TRACE("\n");
823     if (NULL == lpwh)
824         return FALSE;
825
826     switch (lpwh->htype)
827     {
828         case WH_HHTTPREQ:
829             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
830             break;
831
832         case WH_HFILE:
833             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
834             break;
835
836         default:
837             break;
838     }
839
840     if (INVALID_SOCKET != nSocket)
841     {
842         *dwNumOfBytesRead = INTERNET_ReadDataFromStream(nSocket, lpBuffer, dwNumOfBytesToRead);
843         if (*dwNumOfBytesRead < 0)
844             *dwNumOfBytesRead = 0;
845         else
846             retval = TRUE;
847     }
848
849     return retval;
850 }
851
852
853 /***********************************************************************
854  *           InternetQueryOptionA
855  *
856  * Queries an options on the specified handle
857  *
858  * RETURNS
859  *    TRUE  on success
860  *    FALSE on failure
861  *
862  */
863 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption, 
864         LPVOID lpBuffer, LPDWORD lpdwBufferLength)
865 {
866     LPWININETHANDLEHEADER lpwhh;
867     BOOL bSuccess = FALSE;
868
869     TRACE("0x%08lx\n", dwOption);
870
871     if (NULL == hInternet)
872     {
873         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
874         return FALSE;
875     }
876
877     lpwhh = (LPWININETHANDLEHEADER) hInternet;
878
879     switch (dwOption)
880     {
881        case INTERNET_OPTION_HANDLE_TYPE:
882        {
883          ULONG type = lpwhh->htype;
884          TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
885
886          if (*lpdwBufferLength < sizeof(ULONG))
887              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
888          else
889          {
890              memcpy(lpBuffer, &type, sizeof(ULONG));
891              *lpdwBufferLength = sizeof(ULONG);
892              bSuccess = TRUE;
893          }
894
895          break;
896        }
897
898        default:
899          FIXME("Stub!");
900          break;
901     }
902
903     return bSuccess;
904 }
905
906
907 /***********************************************************************
908  *           GetInternetScheme (internal)
909  *
910  * Get scheme of url
911  *
912  * RETURNS
913  *    scheme on success
914  *    INTERNET_SCHEME_UNKNOWN on failure
915  *
916  */
917 INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp)
918 {
919     if(lpszScheme==NULL)
920         return INTERNET_SCHEME_UNKNOWN;
921
922     if (!strncasecmp("ftp", lpszScheme, nMaxCmp))
923         return INTERNET_SCHEME_FTP;
924     else if (!strncasecmp("gopher", lpszScheme, nMaxCmp))
925         return INTERNET_SCHEME_GOPHER;
926     else if (!strncasecmp("http", lpszScheme, nMaxCmp))
927         return INTERNET_SCHEME_HTTP;
928     else if (!strncasecmp("https", lpszScheme, nMaxCmp))
929         return INTERNET_SCHEME_HTTPS;
930     else if (!strncasecmp("file", lpszScheme, nMaxCmp))
931         return INTERNET_SCHEME_FILE;
932     else if (!strncasecmp("news", lpszScheme, nMaxCmp))
933         return INTERNET_SCHEME_NEWS;
934     else if (!strncasecmp("mailto", lpszScheme, nMaxCmp))
935         return INTERNET_SCHEME_MAILTO;
936     else
937         return INTERNET_SCHEME_UNKNOWN;
938 }
939
940 /***********************************************************************
941  *      InternetCheckConnectionA
942  *
943  * Pings a requested host to check internet connection
944  *
945  * RETURNS
946  *
947  *  TRUE on success and FALSE on failure. if a failures then 
948  *   ERROR_NOT_CONNECTED is places into GetLastError
949  *
950  */
951 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
952 {
953 /*
954  * this is a kludge which runs the resident ping program and reads the output.
955  *
956  * Anyone have a better idea?
957  */
958
959   BOOL   rc = FALSE;
960   char command[1024];
961   char host[1024];
962   int status = -1;
963
964   /* 
965    * Crack or set the Address
966    */
967   if (lpszUrl == NULL)
968   {
969      /* 
970       * According to the doc we are supost to use the ip for the next
971       * server in the WnInet internal server database. I have 
972       * no idea what that is or how to get it.
973       *
974       * So someone needs to implement this.
975       */
976      FIXME("Unimplemented with URL of NULL");
977      return TRUE;
978   }
979   else
980   {
981      URL_COMPONENTSA componets;
982
983      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
984      componets.lpszHostName = (LPSTR)&host;
985      componets.dwHostNameLength = 1024;
986      
987      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
988        goto End;
989  
990      TRACE("host name : %s\n",componets.lpszHostName);
991   }
992
993   /*
994    * Build our ping command
995    */
996   strcpy(command,"ping -w 1 ");
997   strcat(command,host);
998   strcat(command," >/dev/null 2>/dev/null");
999
1000   TRACE("Ping command is : %s\n",command);
1001
1002   status = system(command);
1003
1004   TRACE("Ping returned a code of %i \n",status);
1005  
1006   /* Ping return code of 0 indicates success */ 
1007   if (status == 0)
1008      rc = TRUE;
1009
1010 End:
1011  
1012   if (rc == FALSE)
1013     SetLastError(ERROR_NOT_CONNECTED);
1014
1015   return rc;
1016 }
1017
1018
1019
1020 /***********************************************************************
1021  *           INTERNET_WriteDataToStream (internal)
1022  *
1023  * Send data to server
1024  *
1025  * RETURNS
1026  *
1027  *   number of characters sent on success
1028  *   -1 on error
1029  */
1030 int INTERNET_WriteDataToStream(int nDataSocket, LPCVOID Buffer, DWORD BytesToWrite)
1031 {
1032     if (INVALID_SOCKET == nDataSocket)
1033         return SOCKET_ERROR;
1034
1035     return send(nDataSocket, Buffer, BytesToWrite, 0);
1036 }
1037
1038
1039 /***********************************************************************
1040  *           INTERNET_ReadDataFromStream (internal)
1041  *
1042  * Read data from http server
1043  *
1044  * RETURNS
1045  *
1046  *   number of characters sent on success
1047  *   -1 on error
1048  */
1049 int INTERNET_ReadDataFromStream(int nDataSocket, LPVOID Buffer, DWORD BytesToRead)
1050 {
1051     if (INVALID_SOCKET == nDataSocket)
1052         return SOCKET_ERROR;
1053
1054     return recv(nDataSocket, Buffer, BytesToRead, 0);
1055 }
1056
1057
1058 /***********************************************************************
1059  *           INTERNET_SetLastError (internal)
1060  *
1061  * Set last thread specific error
1062  *
1063  * RETURNS
1064  *
1065  */
1066 void INTERNET_SetLastError(DWORD dwError)
1067 {
1068     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1069
1070     SetLastError(dwError);
1071     lpwite->dwError = dwError;
1072 }
1073
1074
1075 /***********************************************************************
1076  *           INTERNET_GetLastError (internal)
1077  *
1078  * Get last thread specific error
1079  *
1080  * RETURNS
1081  *
1082  */
1083 DWORD INTERNET_GetLastError()
1084 {
1085     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1086     return lpwite->dwError;
1087 }
1088
1089
1090 /***********************************************************************
1091  *           INTERNET_WorkerThreadFunc (internal)
1092  *
1093  * Worker thread execution function
1094  *
1095  * RETURNS
1096  *
1097  */
1098 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1099 {
1100     DWORD dwWaitRes;
1101
1102     while (1)
1103     {
1104         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
1105
1106         if (dwWaitRes == WAIT_OBJECT_0 + 1)
1107             INTERNET_ExecuteWork();
1108         else
1109             break;
1110
1111         InterlockedIncrement(&dwNumIdleThreads);
1112     }
1113
1114     InterlockedDecrement(&dwNumIdleThreads);
1115     InterlockedDecrement(&dwNumThreads);
1116     TRACE("Worker thread exiting\n");
1117     return TRUE;
1118 }
1119
1120
1121 /***********************************************************************
1122  *           INTERNET_InsertWorkRequest (internal)
1123  *
1124  * Insert work request into queue
1125  *
1126  * RETURNS
1127  *
1128  */
1129 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
1130 {
1131     BOOL bSuccess = FALSE;
1132     LPWORKREQUEST lpNewRequest;
1133
1134     TRACE("\n");
1135
1136     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
1137     if (lpNewRequest)
1138     {
1139         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
1140         lpNewRequest->prev = NULL;
1141
1142         EnterCriticalSection(&csQueue);
1143
1144         lpNewRequest->next = lpWorkQueueTail;
1145         if (lpWorkQueueTail)
1146             lpWorkQueueTail->prev = lpNewRequest;
1147         lpWorkQueueTail = lpNewRequest;
1148         if (!lpHeadWorkQueue)
1149             lpHeadWorkQueue = lpWorkQueueTail;
1150
1151         LeaveCriticalSection(&csQueue);
1152
1153         bSuccess = TRUE;
1154     }
1155
1156     return bSuccess;
1157 }
1158
1159
1160 /***********************************************************************
1161  *           INTERNET_GetWorkRequest (internal)
1162  *
1163  * Retrieves work request from queue
1164  *
1165  * RETURNS
1166  *
1167  */
1168 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
1169 {
1170     BOOL bSuccess = FALSE;
1171     LPWORKREQUEST lpRequest = NULL;
1172
1173     TRACE("\n");
1174
1175     EnterCriticalSection(&csQueue);
1176
1177     if (lpHeadWorkQueue)
1178     {
1179         lpRequest = lpHeadWorkQueue;
1180         lpHeadWorkQueue = lpHeadWorkQueue->prev;
1181         if (lpRequest == lpWorkQueueTail)
1182             lpWorkQueueTail = lpHeadWorkQueue;
1183     }
1184
1185     LeaveCriticalSection(&csQueue);
1186
1187     if (lpRequest)
1188     {
1189         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
1190         HeapFree(GetProcessHeap(), 0, lpRequest);
1191         bSuccess = TRUE;
1192     }
1193
1194     return bSuccess;
1195 }
1196
1197
1198 /***********************************************************************
1199  *           INTERNET_AsyncCall (internal)
1200  *
1201  * Retrieves work request from queue
1202  *
1203  * RETURNS
1204  *
1205  */
1206 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
1207 {
1208     HANDLE hThread;
1209     DWORD dwTID;
1210     BOOL bSuccess = FALSE;
1211
1212     TRACE("\n");
1213
1214     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
1215     {
1216         InterlockedIncrement(&dwNumIdleThreads);
1217
1218         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS || 
1219             !(hThread = CreateThread(NULL, 0, 
1220             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
1221         {
1222             InterlockedDecrement(&dwNumThreads);
1223             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
1224             goto lerror;
1225         }
1226
1227         TRACE("Created new thread\n");
1228     }
1229
1230     bSuccess = TRUE;
1231     INTERNET_InsertWorkRequest(lpWorkRequest);
1232     SetEvent(hWorkEvent);
1233
1234 lerror:
1235
1236     return bSuccess;
1237 }
1238
1239
1240 /***********************************************************************
1241  *           INTERNET_ExecuteWork (internal)
1242  *
1243  * RETURNS
1244  *
1245  */
1246 VOID INTERNET_ExecuteWork()
1247 {
1248     WORKREQUEST workRequest;
1249
1250     TRACE("\n");
1251
1252     if (INTERNET_GetWorkRequest(&workRequest))
1253     {
1254         switch (workRequest.asyncall)
1255         {
1256             case FTPPUTFILEA:
1257                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
1258                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
1259                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
1260                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
1261                 break;
1262
1263             case FTPSETCURRENTDIRECTORYA:
1264                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1265                         (LPCSTR)workRequest.LPSZDIRECTORY);
1266                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1267                 break;
1268
1269             case FTPCREATEDIRECTORYA:
1270                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1271                         (LPCSTR)workRequest.LPSZDIRECTORY);
1272                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1273                 break;
1274
1275             case FTPFINDFIRSTFILEA:
1276                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION, 
1277                         (LPCSTR)workRequest.LPSZSEARCHFILE, 
1278                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS, 
1279                    workRequest.DWCONTEXT);
1280                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
1281                 break;
1282
1283             case FTPGETCURRENTDIRECTORYA:
1284                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1285                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
1286                 break;
1287
1288             case FTPOPENFILEA:
1289                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
1290                     (LPCSTR)workRequest.LPSZFILENAME,
1291                     workRequest.FDWACCESS,
1292                     workRequest.DWFLAGS,
1293                     workRequest.DWCONTEXT);
1294                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1295                  break;
1296
1297             case FTPGETFILEA:
1298                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
1299                     (LPCSTR)workRequest.LPSZREMOTEFILE,
1300                     (LPCSTR)workRequest.LPSZNEWFILE,
1301                     (BOOL)workRequest.FFAILIFEXISTS,
1302                     workRequest.DWLOCALFLAGSATTRIBUTE,
1303                     workRequest.DWFLAGS,
1304                     workRequest.DWCONTEXT);
1305                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
1306                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
1307                 break;
1308
1309             case FTPDELETEFILEA:
1310                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
1311                         (LPCSTR)workRequest.LPSZFILENAME);
1312                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1313                 break;
1314
1315             case FTPREMOVEDIRECTORYA:
1316                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
1317                         (LPCSTR)workRequest.LPSZDIRECTORY);
1318                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1319                 break;
1320
1321             case FTPRENAMEFILEA:
1322                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
1323                         (LPCSTR)workRequest.LPSZSRCFILE,
1324                         (LPCSTR)workRequest.LPSZDESTFILE);
1325                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
1326                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
1327                 break;
1328
1329             case INTERNETFINDNEXTA:
1330                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
1331                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
1332                 break;
1333
1334             case HTTPSENDREQUESTA:
1335                HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION, 
1336                        (LPCSTR)workRequest.LPSZHEADER,
1337                        workRequest.DWHEADERLENGTH,     
1338                        (LPVOID)workRequest.LPOPTIONAL, 
1339                        workRequest.DWOPTIONALLENGTH);
1340                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
1341                break;
1342
1343             case HTTPOPENREQUESTA:
1344                HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION, 
1345                        (LPCSTR)workRequest.LPSZVERB,
1346                        (LPCSTR)workRequest.LPSZOBJECTNAME,     
1347                        (LPCSTR)workRequest.LPSZVERSION, 
1348                        (LPCSTR)workRequest.LPSZREFERRER, 
1349                        (LPCSTR*)workRequest.LPSZACCEPTTYPES, 
1350                        workRequest.DWFLAGS,
1351                        workRequest.DWCONTEXT);
1352                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
1353                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
1354                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
1355                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
1356                 break;
1357
1358         }
1359     }
1360 }
1361
1362
1363 /***********************************************************************
1364  *          INTERNET_GetResponseBuffer
1365  *
1366  * RETURNS
1367  *
1368  */
1369 LPSTR INTERNET_GetResponseBuffer()
1370 {
1371     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1372     return lpwite->response;
1373 }
1374
1375
1376 /***********************************************************************
1377  *           INTERNET_GetNextLine  (internal)
1378  *
1379  * Parse next line in directory string listing
1380  *
1381  * RETURNS
1382  *   Pointer to begining of next line
1383  *   NULL on failure
1384  *
1385  */
1386
1387 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
1388 {
1389     struct timeval tv;
1390     fd_set infd;
1391     BOOL bSuccess = FALSE;
1392     INT nRecv = 0;
1393
1394     TRACE("\n");
1395
1396     FD_ZERO(&infd);
1397     FD_SET(nSocket, &infd);
1398     tv.tv_sec=RESPONSE_TIMEOUT;
1399     tv.tv_usec=0;
1400
1401     while (nRecv < *dwBuffer)
1402     {
1403         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
1404         {
1405             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
1406             {
1407                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1408                 goto lend;
1409             }
1410
1411             if (lpszBuffer[nRecv] == '\n')
1412             {
1413                 bSuccess = TRUE;
1414                 break;
1415             }
1416             if (lpszBuffer[nRecv] != '\r')
1417                 nRecv++;
1418         }
1419         else
1420         {
1421             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
1422             goto lend;
1423 }
1424     }
1425
1426 lend:
1427     if (bSuccess)
1428     {
1429         lpszBuffer[nRecv] = '\0';
1430         *dwBuffer = nRecv - 1;
1431         TRACE(":%d %s\n", nRecv, lpszBuffer);
1432         return lpszBuffer;
1433     }
1434     else
1435     {
1436         return NULL;
1437     }
1438 }
1439