Fixed some issues found by winapi_check.
[wine] / dlls / wininet / internet.c
1 /*
2  * Wininet
3  *
4  * Copyright 1999 Corel Corporation
5  *
6  * Ulrich Czekalla
7  *
8  */
9
10 #include "config.h"
11
12 #include <string.h>
13 #include <sys/types.h>
14 #ifdef HAVE_SYS_SOCKET_H
15 # include <sys/socket.h>
16 #endif
17 #include <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             retval = TRUE;
435             break; 
436
437         case WH_HHTTPSESSION:
438             HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
439             retval = TRUE;
440             break;
441
442         case WH_HHTTPREQ:
443             HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
444             retval = TRUE;
445             break;
446
447         case WH_HFTPSESSION:
448             retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
449             break;
450
451         case WH_HFINDNEXT:
452             retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
453             break;
454                 
455         default:
456             break;
457     }
458
459     return retval;
460 }
461
462
463 /***********************************************************************
464  *           SetUrlComponentValue (Internal)
465  *
466  * Helper function for InternetCrackUrlA
467  *
468  * RETURNS
469  *    TRUE on success
470  *    FALSE on failure
471  *
472  */
473 BOOL SetUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen, LPCSTR lpszStart, INT len)
474 {
475     TRACE("%s (%d)\n", lpszStart, len);
476
477     if (*dwComponentLen != 0)
478     {
479         if (*lppszComponent == NULL)
480         {
481             *lppszComponent = (LPSTR)lpszStart;
482             *dwComponentLen = len;
483         }
484         else
485         {
486             INT ncpylen = min((*dwComponentLen)-1, len);
487             strncpy(*lppszComponent, lpszStart, ncpylen);
488             (*lppszComponent)[ncpylen] = '\0';
489             *dwComponentLen = ncpylen;
490         }
491     }
492
493     return TRUE;
494 }
495
496
497 /***********************************************************************
498  *           InternetCrackUrlA (WININET.95)
499  *
500  * Break up URL into its components
501  *
502  * TODO: Hadnle dwFlags
503  *
504  * RETURNS
505  *    TRUE on success
506  *    FALSE on failure
507  *
508  */
509 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags, 
510                 LPURL_COMPONENTSA lpUrlComponents)
511 {
512   /*
513    * RFC 1808
514    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
515    *
516    */
517     LPSTR lpszParam    = NULL;
518     BOOL  bIsAbsolute = FALSE;
519     LPSTR lpszap = (char*)lpszUrl;
520     LPSTR lpszcp = NULL;
521
522     TRACE("\n");
523
524     /* Determine if the URI is absolute. */
525     while (*lpszap != '\0')
526     {
527         if (isalnum(*lpszap))
528         {
529             lpszap++;
530             continue;
531         }
532         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
533         {
534             bIsAbsolute = TRUE;
535             lpszcp = lpszap;
536         }
537         else
538         {
539             lpszcp = (LPSTR)lpszUrl; /* Relative url */
540         }
541
542         break;
543     }
544
545     /* Parse <params> */
546     lpszParam = strpbrk(lpszap, ";?");
547     if (lpszParam != NULL)
548     {
549         if (!SetUrlComponentValue(&lpUrlComponents->lpszExtraInfo, 
550              &lpUrlComponents->dwExtraInfoLength, lpszParam+1, strlen(lpszParam+1)))
551         {
552             return FALSE;
553         }
554         }
555
556     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
557         {
558         LPSTR lpszNetLoc;
559
560         /* Get scheme first. */
561         lpUrlComponents->nScheme = GetInternetScheme(lpszUrl, lpszcp - lpszUrl);
562         if (!SetUrlComponentValue(&lpUrlComponents->lpszScheme, 
563                     &lpUrlComponents->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
564             return FALSE;
565
566         /* Eat ':' in protocol. */
567         lpszcp++;
568
569         /* Skip over slashes. */
570         if (*lpszcp == '/')
571         {
572             lpszcp++;
573             if (*lpszcp == '/')
574             {
575                 lpszcp++;
576                 if (*lpszcp == '/')
577                     lpszcp++;
578             }
579         }
580
581         lpszNetLoc = strpbrk(lpszcp, "/");
582         if (lpszParam)
583         {
584             if (lpszNetLoc)
585                lpszNetLoc = min(lpszNetLoc, lpszParam);
586         else
587                lpszNetLoc = lpszParam;
588         }
589         else if (!lpszNetLoc)
590             lpszNetLoc = lpszcp + strlen(lpszcp);
591
592         /* Parse net-loc */
593         if (lpszNetLoc)
594         {
595             LPSTR lpszHost;
596             LPSTR lpszPort;
597
598                 /* [<user>[<:password>]@]<host>[:<port>] */
599             /* First find the user and password if they exist */
600                         
601             lpszHost = strchr(lpszcp, '@');
602             if (lpszHost == NULL || lpszHost > lpszNetLoc)
603                 {
604                 /* username and password not specified. */
605                 SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
606                         &lpUrlComponents->dwUserNameLength, NULL, 0);
607                 SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
608                         &lpUrlComponents->dwPasswordLength, NULL, 0);
609                 }
610             else /* Parse out username and password */
611                 {
612                 LPSTR lpszUser = lpszcp;
613                 LPSTR lpszPasswd = lpszHost;
614
615                 while (lpszcp < lpszHost)
616                         {
617                    if (*lpszcp == ':')
618                        lpszPasswd = lpszcp;
619
620                    lpszcp++;
621                     }        
622                     
623                 SetUrlComponentValue(&lpUrlComponents->lpszUserName, 
624                         &lpUrlComponents->dwUserNameLength, lpszUser, lpszPasswd - lpszUser);
625
626                 SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
627                         &lpUrlComponents->dwPasswordLength, 
628                         lpszPasswd == lpszHost ? NULL : ++lpszPasswd, 
629                         lpszHost - lpszPasswd);
630
631                 lpszcp++; /* Advance to beginning of host */
632                 }
633
634             /* Parse <host><:port> */
635
636             lpszHost = lpszcp;
637             lpszPort = lpszNetLoc;
638
639             while (lpszcp < lpszNetLoc)
640                     {
641                 if (*lpszcp == ':')
642                     lpszPort = lpszcp;
643
644                 lpszcp++;
645                 }
646
647             SetUrlComponentValue(&lpUrlComponents->lpszHostName, 
648                &lpUrlComponents->dwHostNameLength, lpszHost, lpszPort - lpszHost);
649
650             if (lpszPort != lpszNetLoc)
651                 lpUrlComponents->nPort = atoi(++lpszPort);
652             }
653         }
654
655     /* Here lpszcp points to:
656      *
657      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
658      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
659      */
660     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
661     {
662         INT len;
663
664         /* Only truncate the parameter list if it's already been saved
665          * in lpUrlComponents->lpszExtraInfo.
666          */
667         if (lpszParam && lpUrlComponents->dwExtraInfoLength)
668             len = lpszParam - lpszcp;
669         else
670         {
671             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
672              * newlines if necessary.
673              */
674             LPSTR lpsznewline = strchr (lpszcp, '\n');
675             if (lpsznewline != NULL)
676                 len = lpsznewline - lpszcp;
677             else
678                 len = strlen(lpszcp);
679         }
680
681         if (!SetUrlComponentValue(&lpUrlComponents->lpszUrlPath, 
682          &lpUrlComponents->dwUrlPathLength, lpszcp, len))
683          return FALSE;
684     }
685     else
686     {
687         lpUrlComponents->dwUrlPathLength = 0;
688     }
689
690     TRACE("%s: host(%s) path(%s) extra(%s)\n", lpszUrl, lpUrlComponents->lpszHostName,
691           lpUrlComponents->lpszUrlPath, lpUrlComponents->lpszExtraInfo);
692
693     return TRUE;
694 }
695
696
697 /***********************************************************************
698  *           InternetAttemptConnect (WININET.81)
699  *
700  * Attempt to make a connection to the internet
701  *
702  * RETURNS
703  *    ERROR_SUCCESS on success
704  *    Error value   on failure
705  *
706  */
707 INTERNETAPI DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
708 {
709     FIXME("Stub\n");
710     return ERROR_SUCCESS;
711 }
712
713
714 /***********************************************************************
715  *           InternetCanonicalizeUrlA (WININET.85)
716  *
717  * Escape unsafe characters and spaces
718  *
719  * RETURNS
720  *    TRUE on success
721  *    FALSE on failure
722  *
723  */
724 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
725         LPDWORD lpdwBufferLength, DWORD dwFlags)
726 {
727     BOOL bSuccess = FALSE;
728
729     FIXME("Stub!\n");
730
731     if (lpszUrl)
732     {
733         strncpy(lpszBuffer, lpszUrl, *lpdwBufferLength);
734         *lpdwBufferLength = strlen(lpszBuffer);
735         bSuccess = TRUE;
736     }
737
738     return bSuccess;
739 }
740
741
742 /***********************************************************************
743  *           InternetSetStatusCallback (WININET.133)
744  *
745  * Sets up a callback function which is called as progress is made
746  * during an operation. 
747  *
748  * RETURNS
749  *    Previous callback or NULL         on success
750  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
751  *
752  */
753 INTERNETAPI INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallback(
754         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
755 {
756     INTERNET_STATUS_CALLBACK retVal;
757     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
758
759     TRACE("0x%08lx\n", (ULONG)hInternet);
760     if (lpwai->hdr.htype != WH_HINIT)
761         return INTERNET_INVALID_STATUS_CALLBACK; 
762
763     retVal = lpwai->lpfnStatusCB;
764     lpwai->lpfnStatusCB = lpfnIntCB;
765
766     return retVal;
767 }
768
769
770 /***********************************************************************
771  *           InternetWriteFile (WININET.138)
772  *
773  * Write data to an open internet file 
774  *
775  * RETURNS
776  *    TRUE  on success
777  *    FALSE on failure
778  *
779  */
780 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
781         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
782 {
783     BOOL retval = FALSE;
784     int nSocket = INVALID_SOCKET;
785     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
786
787     TRACE("\n");
788     if (NULL == lpwh)
789         return FALSE;
790
791     switch (lpwh->htype)
792     {
793         case WH_HHTTPREQ:
794             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
795             break;
796
797         case WH_HFILE:
798             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
799             break;
800
801         default:
802             break;
803     }
804
805     if (INVALID_SOCKET != nSocket)
806     {
807         *lpdwNumOfBytesWritten = INTERNET_WriteDataToStream(nSocket, lpBuffer, dwNumOfBytesToWrite);
808         if (*lpdwNumOfBytesWritten < 0)
809             *lpdwNumOfBytesWritten = 0;
810         else
811             retval = TRUE;
812     }
813
814     return retval;
815 }
816
817
818 /***********************************************************************
819  *           InternetReadFile (WININET.121)
820  *
821  * Read data from an open internet file 
822  *
823  * RETURNS
824  *    TRUE  on success
825  *    FALSE on failure
826  *
827  */
828 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, 
829         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
830 {
831     BOOL retval = FALSE;
832     int nSocket = INVALID_SOCKET;
833     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
834
835     TRACE("\n");
836     if (NULL == lpwh)
837         return FALSE;
838
839     switch (lpwh->htype)
840     {
841         case WH_HHTTPREQ:
842             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
843             break;
844
845         case WH_HFILE:
846             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
847             break;
848
849         default:
850             break;
851     }
852
853     if (INVALID_SOCKET != nSocket)
854     {
855         *dwNumOfBytesRead = INTERNET_ReadDataFromStream(nSocket, lpBuffer, dwNumOfBytesToRead);
856         if (*dwNumOfBytesRead < 0)
857             *dwNumOfBytesRead = 0;
858         else
859             retval = TRUE;
860     }
861
862     return retval;
863 }
864
865
866 /***********************************************************************
867  *           InternetQueryOptionA
868  *
869  * Queries an options on the specified handle
870  *
871  * RETURNS
872  *    TRUE  on success
873  *    FALSE on failure
874  *
875  */
876 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption, 
877         LPVOID lpBuffer, LPDWORD lpdwBufferLength)
878 {
879     LPWININETHANDLEHEADER lpwhh;
880     BOOL bSuccess = FALSE;
881
882     TRACE("0x%08lx\n", dwOption);
883
884     if (NULL == hInternet)
885     {
886         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
887         return FALSE;
888     }
889
890     lpwhh = (LPWININETHANDLEHEADER) hInternet;
891
892     switch (dwOption)
893     {
894        case INTERNET_OPTION_HANDLE_TYPE:
895        {
896          ULONG type = lpwhh->htype;
897          TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
898
899          if (*lpdwBufferLength < sizeof(ULONG))
900              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
901          else
902          {
903              memcpy(lpBuffer, &type, sizeof(ULONG));
904              *lpdwBufferLength = sizeof(ULONG);
905              bSuccess = TRUE;
906          }
907
908          break;
909        }
910
911        default:
912          FIXME("Stub!");
913          break;
914     }
915
916     return bSuccess;
917 }
918
919
920 /***********************************************************************
921  *           GetInternetScheme (internal)
922  *
923  * Get scheme of url
924  *
925  * RETURNS
926  *    scheme on success
927  *    INTERNET_SCHEME_UNKNOWN on failure
928  *
929  */
930 INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp)
931 {
932     if(lpszScheme==NULL)
933         return INTERNET_SCHEME_UNKNOWN;
934
935     if (!strncasecmp("ftp", lpszScheme, nMaxCmp))
936         return INTERNET_SCHEME_FTP;
937     else if (!strncasecmp("gopher", lpszScheme, nMaxCmp))
938         return INTERNET_SCHEME_GOPHER;
939     else if (!strncasecmp("http", lpszScheme, nMaxCmp))
940         return INTERNET_SCHEME_HTTP;
941     else if (!strncasecmp("https", lpszScheme, nMaxCmp))
942         return INTERNET_SCHEME_HTTPS;
943     else if (!strncasecmp("file", lpszScheme, nMaxCmp))
944         return INTERNET_SCHEME_FILE;
945     else if (!strncasecmp("news", lpszScheme, nMaxCmp))
946         return INTERNET_SCHEME_NEWS;
947     else if (!strncasecmp("mailto", lpszScheme, nMaxCmp))
948         return INTERNET_SCHEME_MAILTO;
949     else
950         return INTERNET_SCHEME_UNKNOWN;
951 }
952
953 /***********************************************************************
954  *      InternetCheckConnectionA
955  *
956  * Pings a requested host to check internet connection
957  *
958  * RETURNS
959  *
960  *  TRUE on success and FALSE on failure. if a failures then 
961  *   ERROR_NOT_CONNECTED is places into GetLastError
962  *
963  */
964 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
965 {
966 /*
967  * this is a kludge which runs the resident ping program and reads the output.
968  *
969  * Anyone have a better idea?
970  */
971
972   BOOL   rc = FALSE;
973   char command[1024];
974   char host[1024];
975   int status = -1;
976
977   /* 
978    * Crack or set the Address
979    */
980   if (lpszUrl == NULL)
981   {
982      /* 
983       * According to the doc we are supost to use the ip for the next
984       * server in the WnInet internal server database. I have 
985       * no idea what that is or how to get it.
986       *
987       * So someone needs to implement this.
988       */
989      FIXME("Unimplemented with URL of NULL");
990      return TRUE;
991   }
992   else
993   {
994      URL_COMPONENTSA componets;
995
996      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
997      componets.lpszHostName = (LPSTR)&host;
998      componets.dwHostNameLength = 1024;
999      
1000      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
1001        goto End;
1002  
1003      TRACE("host name : %s\n",componets.lpszHostName);
1004   }
1005
1006   /*
1007    * Build our ping command
1008    */
1009   strcpy(command,"ping -w 1 ");
1010   strcat(command,host);
1011   strcat(command," >/dev/null 2>/dev/null");
1012
1013   TRACE("Ping command is : %s\n",command);
1014
1015   status = system(command);
1016
1017   TRACE("Ping returned a code of %i \n",status);
1018  
1019   /* Ping return code of 0 indicates success */ 
1020   if (status == 0)
1021      rc = TRUE;
1022
1023 End:
1024  
1025   if (rc == FALSE)
1026     SetLastError(ERROR_NOT_CONNECTED);
1027
1028   return rc;
1029 }
1030
1031
1032
1033 /***********************************************************************
1034  *           INTERNET_WriteDataToStream (internal)
1035  *
1036  * Send data to server
1037  *
1038  * RETURNS
1039  *
1040  *   number of characters sent on success
1041  *   -1 on error
1042  */
1043 int INTERNET_WriteDataToStream(int nDataSocket, LPCVOID Buffer, DWORD BytesToWrite)
1044 {
1045     if (INVALID_SOCKET == nDataSocket)
1046         return SOCKET_ERROR;
1047
1048     return send(nDataSocket, Buffer, BytesToWrite, 0);
1049 }
1050
1051
1052 /***********************************************************************
1053  *           INTERNET_ReadDataFromStream (internal)
1054  *
1055  * Read data from http server
1056  *
1057  * RETURNS
1058  *
1059  *   number of characters sent on success
1060  *   -1 on error
1061  */
1062 int INTERNET_ReadDataFromStream(int nDataSocket, LPVOID Buffer, DWORD BytesToRead)
1063 {
1064     if (INVALID_SOCKET == nDataSocket)
1065         return SOCKET_ERROR;
1066
1067     return recv(nDataSocket, Buffer, BytesToRead, 0);
1068 }
1069
1070
1071 /***********************************************************************
1072  *           INTERNET_SetLastError (internal)
1073  *
1074  * Set last thread specific error
1075  *
1076  * RETURNS
1077  *
1078  */
1079 void INTERNET_SetLastError(DWORD dwError)
1080 {
1081     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1082
1083     SetLastError(dwError);
1084     lpwite->dwError = dwError;
1085 }
1086
1087
1088 /***********************************************************************
1089  *           INTERNET_GetLastError (internal)
1090  *
1091  * Get last thread specific error
1092  *
1093  * RETURNS
1094  *
1095  */
1096 DWORD INTERNET_GetLastError()
1097 {
1098     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1099     return lpwite->dwError;
1100 }
1101
1102
1103 /***********************************************************************
1104  *           INTERNET_WorkerThreadFunc (internal)
1105  *
1106  * Worker thread execution function
1107  *
1108  * RETURNS
1109  *
1110  */
1111 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1112 {
1113     DWORD dwWaitRes;
1114
1115     while (1)
1116     {
1117         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
1118
1119         if (dwWaitRes == WAIT_OBJECT_0 + 1)
1120             INTERNET_ExecuteWork();
1121         else
1122             break;
1123
1124         InterlockedIncrement(&dwNumIdleThreads);
1125     }
1126
1127     InterlockedDecrement(&dwNumIdleThreads);
1128     InterlockedDecrement(&dwNumThreads);
1129     TRACE("Worker thread exiting\n");
1130     return TRUE;
1131 }
1132
1133
1134 /***********************************************************************
1135  *           INTERNET_InsertWorkRequest (internal)
1136  *
1137  * Insert work request into queue
1138  *
1139  * RETURNS
1140  *
1141  */
1142 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
1143 {
1144     BOOL bSuccess = FALSE;
1145     LPWORKREQUEST lpNewRequest;
1146
1147     TRACE("\n");
1148
1149     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
1150     if (lpNewRequest)
1151     {
1152         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
1153         lpNewRequest->prev = NULL;
1154
1155         EnterCriticalSection(&csQueue);
1156
1157         lpNewRequest->next = lpWorkQueueTail;
1158         if (lpWorkQueueTail)
1159             lpWorkQueueTail->prev = lpNewRequest;
1160         lpWorkQueueTail = lpNewRequest;
1161         if (!lpHeadWorkQueue)
1162             lpHeadWorkQueue = lpWorkQueueTail;
1163
1164         LeaveCriticalSection(&csQueue);
1165
1166         bSuccess = TRUE;
1167     }
1168
1169     return bSuccess;
1170 }
1171
1172
1173 /***********************************************************************
1174  *           INTERNET_GetWorkRequest (internal)
1175  *
1176  * Retrieves work request from queue
1177  *
1178  * RETURNS
1179  *
1180  */
1181 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
1182 {
1183     BOOL bSuccess = FALSE;
1184     LPWORKREQUEST lpRequest = NULL;
1185
1186     TRACE("\n");
1187
1188     EnterCriticalSection(&csQueue);
1189
1190     if (lpHeadWorkQueue)
1191     {
1192         lpRequest = lpHeadWorkQueue;
1193         lpHeadWorkQueue = lpHeadWorkQueue->prev;
1194         if (lpRequest == lpWorkQueueTail)
1195             lpWorkQueueTail = lpHeadWorkQueue;
1196     }
1197
1198     LeaveCriticalSection(&csQueue);
1199
1200     if (lpRequest)
1201     {
1202         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
1203         HeapFree(GetProcessHeap(), 0, lpRequest);
1204         bSuccess = TRUE;
1205     }
1206
1207     return bSuccess;
1208 }
1209
1210
1211 /***********************************************************************
1212  *           INTERNET_AsyncCall (internal)
1213  *
1214  * Retrieves work request from queue
1215  *
1216  * RETURNS
1217  *
1218  */
1219 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
1220 {
1221     HANDLE hThread;
1222     DWORD dwTID;
1223     BOOL bSuccess = FALSE;
1224
1225     TRACE("\n");
1226
1227     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
1228     {
1229         InterlockedIncrement(&dwNumIdleThreads);
1230
1231         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS || 
1232             !(hThread = CreateThread(NULL, 0, 
1233             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
1234         {
1235             InterlockedDecrement(&dwNumThreads);
1236             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
1237             goto lerror;
1238         }
1239
1240         TRACE("Created new thread\n");
1241     }
1242
1243     bSuccess = TRUE;
1244     INTERNET_InsertWorkRequest(lpWorkRequest);
1245     SetEvent(hWorkEvent);
1246
1247 lerror:
1248
1249     return bSuccess;
1250 }
1251
1252
1253 /***********************************************************************
1254  *           INTERNET_ExecuteWork (internal)
1255  *
1256  * RETURNS
1257  *
1258  */
1259 VOID INTERNET_ExecuteWork()
1260 {
1261     WORKREQUEST workRequest;
1262
1263     TRACE("\n");
1264
1265     if (INTERNET_GetWorkRequest(&workRequest))
1266     {
1267         switch (workRequest.asyncall)
1268         {
1269             case FTPPUTFILEA:
1270                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
1271                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
1272                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
1273                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
1274                 break;
1275
1276             case FTPSETCURRENTDIRECTORYA:
1277                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1278                         (LPCSTR)workRequest.LPSZDIRECTORY);
1279                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1280                 break;
1281
1282             case FTPCREATEDIRECTORYA:
1283                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1284                         (LPCSTR)workRequest.LPSZDIRECTORY);
1285                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1286                 break;
1287
1288             case FTPFINDFIRSTFILEA:
1289                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION, 
1290                         (LPCSTR)workRequest.LPSZSEARCHFILE, 
1291                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS, 
1292                    workRequest.DWCONTEXT);
1293                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
1294                 break;
1295
1296             case FTPGETCURRENTDIRECTORYA:
1297                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1298                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
1299                 break;
1300
1301             case FTPOPENFILEA:
1302                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
1303                     (LPCSTR)workRequest.LPSZFILENAME,
1304                     workRequest.FDWACCESS,
1305                     workRequest.DWFLAGS,
1306                     workRequest.DWCONTEXT);
1307                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1308                  break;
1309
1310             case FTPGETFILEA:
1311                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
1312                     (LPCSTR)workRequest.LPSZREMOTEFILE,
1313                     (LPCSTR)workRequest.LPSZNEWFILE,
1314                     (BOOL)workRequest.FFAILIFEXISTS,
1315                     workRequest.DWLOCALFLAGSATTRIBUTE,
1316                     workRequest.DWFLAGS,
1317                     workRequest.DWCONTEXT);
1318                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
1319                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
1320                 break;
1321
1322             case FTPDELETEFILEA:
1323                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
1324                         (LPCSTR)workRequest.LPSZFILENAME);
1325                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1326                 break;
1327
1328             case FTPREMOVEDIRECTORYA:
1329                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
1330                         (LPCSTR)workRequest.LPSZDIRECTORY);
1331                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1332                 break;
1333
1334             case FTPRENAMEFILEA:
1335                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
1336                         (LPCSTR)workRequest.LPSZSRCFILE,
1337                         (LPCSTR)workRequest.LPSZDESTFILE);
1338                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
1339                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
1340                 break;
1341
1342             case INTERNETFINDNEXTA:
1343                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
1344                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
1345                 break;
1346
1347             case HTTPSENDREQUESTA:
1348                HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION, 
1349                        (LPCSTR)workRequest.LPSZHEADER,
1350                        workRequest.DWHEADERLENGTH,     
1351                        (LPVOID)workRequest.LPOPTIONAL, 
1352                        workRequest.DWOPTIONALLENGTH);
1353                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
1354                break;
1355
1356             case HTTPOPENREQUESTA:
1357                HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION, 
1358                        (LPCSTR)workRequest.LPSZVERB,
1359                        (LPCSTR)workRequest.LPSZOBJECTNAME,     
1360                        (LPCSTR)workRequest.LPSZVERSION, 
1361                        (LPCSTR)workRequest.LPSZREFERRER, 
1362                        (LPCSTR*)workRequest.LPSZACCEPTTYPES, 
1363                        workRequest.DWFLAGS,
1364                        workRequest.DWCONTEXT);
1365                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
1366                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
1367                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
1368                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
1369                 break;
1370
1371         }
1372     }
1373 }
1374
1375
1376 /***********************************************************************
1377  *          INTERNET_GetResponseBuffer
1378  *
1379  * RETURNS
1380  *
1381  */
1382 LPSTR INTERNET_GetResponseBuffer()
1383 {
1384     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1385     return lpwite->response;
1386 }
1387
1388
1389 /***********************************************************************
1390  *           INTERNET_GetNextLine  (internal)
1391  *
1392  * Parse next line in directory string listing
1393  *
1394  * RETURNS
1395  *   Pointer to begining of next line
1396  *   NULL on failure
1397  *
1398  */
1399
1400 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
1401 {
1402     struct timeval tv;
1403     fd_set infd;
1404     BOOL bSuccess = FALSE;
1405     INT nRecv = 0;
1406
1407     TRACE("\n");
1408
1409     FD_ZERO(&infd);
1410     FD_SET(nSocket, &infd);
1411     tv.tv_sec=RESPONSE_TIMEOUT;
1412     tv.tv_usec=0;
1413
1414     while (nRecv < *dwBuffer)
1415     {
1416         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
1417         {
1418             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
1419             {
1420                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1421                 goto lend;
1422             }
1423
1424             if (lpszBuffer[nRecv] == '\n')
1425             {
1426                 bSuccess = TRUE;
1427                 break;
1428             }
1429             if (lpszBuffer[nRecv] != '\r')
1430                 nRecv++;
1431         }
1432         else
1433         {
1434             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
1435             goto lend;
1436 }
1437     }
1438
1439 lend:
1440     if (bSuccess)
1441     {
1442         lpszBuffer[nRecv] = '\0';
1443         *dwBuffer = nRecv - 1;
1444         TRACE(":%d %s\n", nRecv, lpszBuffer);
1445         return lpszBuffer;
1446     }
1447     else
1448     {
1449         return NULL;
1450     }
1451 }
1452