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