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