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