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