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