Use DrawFrameControl instead of bitmaps in certain cases.
[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                 SetUrlComponentValue(&lpUrlComponents->lpszPassword, 
632                         &lpUrlComponents->dwPasswordLength, 
633                         lpszPasswd == lpszHost ? NULL : ++lpszPasswd, 
634                         lpszHost - lpszPasswd);
635
636                 lpszcp++; /* Advance to beginning of host */
637                 }
638
639             /* Parse <host><:port> */
640
641             lpszHost = lpszcp;
642             lpszPort = lpszNetLoc;
643
644             while (lpszcp < lpszNetLoc)
645                     {
646                 if (*lpszcp == ':')
647                     lpszPort = lpszcp;
648
649                 lpszcp++;
650                 }
651
652             SetUrlComponentValue(&lpUrlComponents->lpszHostName, 
653                &lpUrlComponents->dwHostNameLength, lpszHost, lpszPort - lpszHost);
654
655             if (lpszPort != lpszNetLoc)
656                 lpUrlComponents->nPort = atoi(++lpszPort);
657             }
658         }
659
660     /* Here lpszcp points to:
661      *
662      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
663      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
664      */
665     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
666     {
667         INT len;
668
669         /* Only truncate the parameter list if it's already been saved
670          * in lpUrlComponents->lpszExtraInfo.
671          */
672         if (lpszParam && lpUrlComponents->dwExtraInfoLength)
673             len = lpszParam - lpszcp;
674         else
675         {
676             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
677              * newlines if necessary.
678              */
679             LPSTR lpsznewline = strchr (lpszcp, '\n');
680             if (lpsznewline != NULL)
681                 len = lpsznewline - lpszcp;
682             else
683                 len = strlen(lpszcp);
684         }
685
686         if (!SetUrlComponentValue(&lpUrlComponents->lpszUrlPath, 
687          &lpUrlComponents->dwUrlPathLength, lpszcp, len))
688          return FALSE;
689     }
690     else
691     {
692         lpUrlComponents->dwUrlPathLength = 0;
693     }
694
695     TRACE("%s: host(%s) path(%s) extra(%s)\n", lpszUrl, lpUrlComponents->lpszHostName,
696           lpUrlComponents->lpszUrlPath, lpUrlComponents->lpszExtraInfo);
697
698     return TRUE;
699 }
700
701
702 /***********************************************************************
703  *           GetUrlCacheEntryInfoA (WININET.@)
704  *
705  */
706 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrl, 
707   LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntry,
708   LPDWORD lpCacheEntrySize)
709 {
710     FIXME("stub\n");
711     return FALSE;
712 }
713
714 /***********************************************************************
715  *           CommitUrlCacheEntryA (WININET.@)
716  *
717  */
718 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrl, LPCSTR lpszLocalName,
719     FILETIME ExpireTime, FILETIME lastModified, DWORD cacheEntryType,
720     LPBYTE lpHeaderInfo, DWORD headerSize, LPCSTR fileExtension,
721     DWORD originalUrl)
722 {
723     FIXME("stub\n");
724     return FALSE;
725 }
726
727 /***********************************************************************
728  *           InternetAttemptConnect (WININET.@)
729  *
730  * Attempt to make a connection to the internet
731  *
732  * RETURNS
733  *    ERROR_SUCCESS on success
734  *    Error value   on failure
735  *
736  */
737 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
738 {
739     FIXME("Stub\n");
740     return ERROR_SUCCESS;
741 }
742
743
744 /***********************************************************************
745  *           InternetCanonicalizeUrlA (WININET.@)
746  *
747  * Escape unsafe characters and spaces
748  *
749  * RETURNS
750  *    TRUE on success
751  *    FALSE on failure
752  *
753  */
754 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
755         LPDWORD lpdwBufferLength, DWORD dwFlags)
756 {
757     HRESULT hr;
758     TRACE("%s %p %p %08lx\n",debugstr_a(lpszUrl), lpszBuffer,
759           lpdwBufferLength, dwFlags);
760
761     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
762     dwFlags ^= ICU_NO_ENCODE;
763
764     dwFlags |= 0x80000000; /* Don't know what this means */
765
766     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
767
768     return (hr == S_OK) ? TRUE : FALSE;
769 }
770
771 /***********************************************************************
772  *           InternetSetStatusCallback (WININET.@)
773  *
774  * Sets up a callback function which is called as progress is made
775  * during an operation. 
776  *
777  * RETURNS
778  *    Previous callback or NULL         on success
779  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
780  *
781  */
782 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallback(
783         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
784 {
785     INTERNET_STATUS_CALLBACK retVal;
786     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
787
788     TRACE("0x%08lx\n", (ULONG)hInternet);
789     if (lpwai->hdr.htype != WH_HINIT)
790         return INTERNET_INVALID_STATUS_CALLBACK; 
791
792     retVal = lpwai->lpfnStatusCB;
793     lpwai->lpfnStatusCB = lpfnIntCB;
794
795     return retVal;
796 }
797
798
799 /***********************************************************************
800  *           InternetWriteFile (WININET.@)
801  *
802  * Write data to an open internet file 
803  *
804  * RETURNS
805  *    TRUE  on success
806  *    FALSE on failure
807  *
808  */
809 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
810         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
811 {
812     BOOL retval = FALSE;
813     int nSocket = -1;
814     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
815
816     TRACE("\n");
817     if (NULL == lpwh)
818         return FALSE;
819
820     switch (lpwh->htype)
821     {
822         case WH_HHTTPREQ:
823             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
824             break;
825
826         case WH_HFILE:
827             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
828             break;
829
830         default:
831             break;
832     }
833
834     if (nSocket != -1)
835     {
836         *lpdwNumOfBytesWritten = INTERNET_WriteDataToStream(nSocket, lpBuffer, dwNumOfBytesToWrite);
837         if (*lpdwNumOfBytesWritten < 0)
838             *lpdwNumOfBytesWritten = 0;
839         else
840             retval = TRUE;
841     }
842
843     return retval;
844 }
845
846
847 /***********************************************************************
848  *           InternetReadFile (WININET.@)
849  *
850  * Read data from an open internet file 
851  *
852  * RETURNS
853  *    TRUE  on success
854  *    FALSE on failure
855  *
856  */
857 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer, 
858         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
859 {
860     BOOL retval = FALSE;
861     int nSocket = -1;
862     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
863
864     TRACE("\n");
865     if (NULL == lpwh)
866         return FALSE;
867
868     switch (lpwh->htype)
869     {
870         case WH_HHTTPREQ:
871             nSocket = ((LPWININETHTTPREQA)hFile)->nSocketFD; 
872             break;
873
874         case WH_HFILE:
875             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
876             break;
877
878         default:
879             break;
880     }
881
882     if (nSocket != -1)
883     {
884         *dwNumOfBytesRead = INTERNET_ReadDataFromStream(nSocket, lpBuffer, dwNumOfBytesToRead);
885         if (*dwNumOfBytesRead < 0)
886             *dwNumOfBytesRead = 0;
887         else
888             retval = TRUE;
889     }
890
891     return retval;
892 }
893
894
895 /***********************************************************************
896  *           InternetQueryOptionA (WININET.@)
897  *
898  * Queries an options on the specified handle
899  *
900  * RETURNS
901  *    TRUE  on success
902  *    FALSE on failure
903  *
904  */
905 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption, 
906         LPVOID lpBuffer, LPDWORD lpdwBufferLength)
907 {
908     LPWININETHANDLEHEADER lpwhh;
909     BOOL bSuccess = FALSE;
910
911     TRACE("0x%08lx\n", dwOption);
912
913     if (NULL == hInternet)
914     {
915         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
916         return FALSE;
917     }
918
919     lpwhh = (LPWININETHANDLEHEADER) hInternet;
920
921     switch (dwOption)
922     {
923        case INTERNET_OPTION_HANDLE_TYPE:
924        {
925          ULONG type = lpwhh->htype;
926          TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
927
928          if (*lpdwBufferLength < sizeof(ULONG))
929              INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
930          else
931          {
932              memcpy(lpBuffer, &type, sizeof(ULONG));
933              *lpdwBufferLength = sizeof(ULONG);
934              bSuccess = TRUE;
935          }
936
937          break;
938        }
939
940        default:
941          FIXME("Stub!\n");
942          break;
943     }
944
945     return bSuccess;
946 }
947
948
949 /***********************************************************************
950  *           InternetGetCookieA (WININET.@)
951  *
952  * Retrieve cookie from the specified url
953  *
954  * RETURNS
955  *    TRUE  on success
956  *    FALSE on failure
957  *
958  */
959 BOOL WINAPI InternetGetCookieA(LPCSTR lpszUrl, LPCSTR lpszCookieName, 
960                 LPSTR lpCookieData, LPDWORD lpdwSize)
961 {
962     FIXME("(%s,%s,%p), stub!\n",debugstr_a(lpszUrl),debugstr_a(lpszCookieName),
963             lpCookieData
964     );
965     return FALSE;
966 }
967 /***********************************************************************
968  *           InternetSetCookieA (WININET.@)
969  *
970  * Sets cookie for the specified url
971  *
972  * RETURNS
973  *    TRUE  on success
974  *    FALSE on failure
975  *
976  */
977 BOOL WINAPI InternetSetCookieA(
978         LPCSTR lpszUrl, LPCSTR lpszCookieName, LPCSTR lpCookieData
979 ) {
980     FIXME("(%s,%s,%s), stub!\n",debugstr_a(lpszUrl),debugstr_a(lpszCookieName),debugstr_a(lpCookieData));
981     return FALSE;
982 }
983
984 /***********************************************************************
985  *           GetInternetScheme (internal)
986  *
987  * Get scheme of url
988  *
989  * RETURNS
990  *    scheme on success
991  *    INTERNET_SCHEME_UNKNOWN on failure
992  *
993  */
994 INTERNET_SCHEME GetInternetScheme(LPCSTR lpszScheme, INT nMaxCmp)
995 {
996     if(lpszScheme==NULL)
997         return INTERNET_SCHEME_UNKNOWN;
998
999     if (!strncasecmp("ftp", lpszScheme, nMaxCmp))
1000         return INTERNET_SCHEME_FTP;
1001     else if (!strncasecmp("gopher", lpszScheme, nMaxCmp))
1002         return INTERNET_SCHEME_GOPHER;
1003     else if (!strncasecmp("http", lpszScheme, nMaxCmp))
1004         return INTERNET_SCHEME_HTTP;
1005     else if (!strncasecmp("https", lpszScheme, nMaxCmp))
1006         return INTERNET_SCHEME_HTTPS;
1007     else if (!strncasecmp("file", lpszScheme, nMaxCmp))
1008         return INTERNET_SCHEME_FILE;
1009     else if (!strncasecmp("news", lpszScheme, nMaxCmp))
1010         return INTERNET_SCHEME_NEWS;
1011     else if (!strncasecmp("mailto", lpszScheme, nMaxCmp))
1012         return INTERNET_SCHEME_MAILTO;
1013     else
1014         return INTERNET_SCHEME_UNKNOWN;
1015 }
1016
1017 /***********************************************************************
1018  *      InternetCheckConnectionA (WININET.@)
1019  *
1020  * Pings a requested host to check internet connection
1021  *
1022  * RETURNS
1023  *
1024  *  TRUE on success and FALSE on failure. if a failures then 
1025  *   ERROR_NOT_CONNECTED is places into GetLastError
1026  *
1027  */
1028 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
1029 {
1030 /*
1031  * this is a kludge which runs the resident ping program and reads the output.
1032  *
1033  * Anyone have a better idea?
1034  */
1035
1036   BOOL   rc = FALSE;
1037   char command[1024];
1038   char host[1024];
1039   int status = -1;
1040
1041   /* 
1042    * Crack or set the Address
1043    */
1044   if (lpszUrl == NULL)
1045   {
1046      /* 
1047       * According to the doc we are supost to use the ip for the next
1048       * server in the WnInet internal server database. I have 
1049       * no idea what that is or how to get it.
1050       *
1051       * So someone needs to implement this.
1052       */
1053      FIXME("Unimplemented with URL of NULL\n");
1054      return TRUE;
1055   }
1056   else
1057   {
1058      URL_COMPONENTSA componets;
1059
1060      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
1061      componets.lpszHostName = (LPSTR)&host;
1062      componets.dwHostNameLength = 1024;
1063      
1064      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
1065        goto End;
1066  
1067      TRACE("host name : %s\n",componets.lpszHostName);
1068   }
1069
1070   /*
1071    * Build our ping command
1072    */
1073   strcpy(command,"ping -w 1 ");
1074   strcat(command,host);
1075   strcat(command," >/dev/null 2>/dev/null");
1076
1077   TRACE("Ping command is : %s\n",command);
1078
1079   status = system(command);
1080
1081   TRACE("Ping returned a code of %i \n",status);
1082  
1083   /* Ping return code of 0 indicates success */ 
1084   if (status == 0)
1085      rc = TRUE;
1086
1087 End:
1088  
1089   if (rc == FALSE)
1090     SetLastError(ERROR_NOT_CONNECTED);
1091
1092   return rc;
1093 }
1094
1095
1096
1097 /***********************************************************************
1098  *           INTERNET_WriteDataToStream (internal)
1099  *
1100  * Send data to server
1101  *
1102  * RETURNS
1103  *
1104  *   number of characters sent on success
1105  *   -1 on error
1106  */
1107 int INTERNET_WriteDataToStream(int nDataSocket, LPCVOID Buffer, DWORD BytesToWrite)
1108 {
1109     if (nDataSocket == -1)
1110         return -1;
1111
1112     return send(nDataSocket, Buffer, BytesToWrite, 0);
1113 }
1114
1115
1116 /***********************************************************************
1117  *           INTERNET_ReadDataFromStream (internal)
1118  *
1119  * Read data from http server
1120  *
1121  * RETURNS
1122  *
1123  *   number of characters sent on success
1124  *   -1 on error
1125  */
1126 int INTERNET_ReadDataFromStream(int nDataSocket, LPVOID Buffer, DWORD BytesToRead)
1127 {
1128     if (nDataSocket == -1)
1129         return -1;
1130
1131     return recv(nDataSocket, Buffer, BytesToRead, 0);
1132 }
1133
1134
1135 /***********************************************************************
1136  *           INTERNET_SetLastError (internal)
1137  *
1138  * Set last thread specific error
1139  *
1140  * RETURNS
1141  *
1142  */
1143 void INTERNET_SetLastError(DWORD dwError)
1144 {
1145     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1146
1147     SetLastError(dwError);
1148     lpwite->dwError = dwError;
1149 }
1150
1151
1152 /***********************************************************************
1153  *           INTERNET_GetLastError (internal)
1154  *
1155  * Get last thread specific error
1156  *
1157  * RETURNS
1158  *
1159  */
1160 DWORD INTERNET_GetLastError()
1161 {
1162     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1163     return lpwite->dwError;
1164 }
1165
1166
1167 /***********************************************************************
1168  *           INTERNET_WorkerThreadFunc (internal)
1169  *
1170  * Worker thread execution function
1171  *
1172  * RETURNS
1173  *
1174  */
1175 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1176 {
1177     DWORD dwWaitRes;
1178
1179     while (1)
1180     {
1181         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
1182
1183         if (dwWaitRes == WAIT_OBJECT_0 + 1)
1184             INTERNET_ExecuteWork();
1185         else
1186             break;
1187
1188         InterlockedIncrement(&dwNumIdleThreads);
1189     }
1190
1191     InterlockedDecrement(&dwNumIdleThreads);
1192     InterlockedDecrement(&dwNumThreads);
1193     TRACE("Worker thread exiting\n");
1194     return TRUE;
1195 }
1196
1197
1198 /***********************************************************************
1199  *           INTERNET_InsertWorkRequest (internal)
1200  *
1201  * Insert work request into queue
1202  *
1203  * RETURNS
1204  *
1205  */
1206 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
1207 {
1208     BOOL bSuccess = FALSE;
1209     LPWORKREQUEST lpNewRequest;
1210
1211     TRACE("\n");
1212
1213     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
1214     if (lpNewRequest)
1215     {
1216         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
1217         lpNewRequest->prev = NULL;
1218
1219         EnterCriticalSection(&csQueue);
1220
1221         lpNewRequest->next = lpWorkQueueTail;
1222         if (lpWorkQueueTail)
1223             lpWorkQueueTail->prev = lpNewRequest;
1224         lpWorkQueueTail = lpNewRequest;
1225         if (!lpHeadWorkQueue)
1226             lpHeadWorkQueue = lpWorkQueueTail;
1227
1228         LeaveCriticalSection(&csQueue);
1229
1230         bSuccess = TRUE;
1231     }
1232
1233     return bSuccess;
1234 }
1235
1236
1237 /***********************************************************************
1238  *           INTERNET_GetWorkRequest (internal)
1239  *
1240  * Retrieves work request from queue
1241  *
1242  * RETURNS
1243  *
1244  */
1245 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
1246 {
1247     BOOL bSuccess = FALSE;
1248     LPWORKREQUEST lpRequest = NULL;
1249
1250     TRACE("\n");
1251
1252     EnterCriticalSection(&csQueue);
1253
1254     if (lpHeadWorkQueue)
1255     {
1256         lpRequest = lpHeadWorkQueue;
1257         lpHeadWorkQueue = lpHeadWorkQueue->prev;
1258         if (lpRequest == lpWorkQueueTail)
1259             lpWorkQueueTail = lpHeadWorkQueue;
1260     }
1261
1262     LeaveCriticalSection(&csQueue);
1263
1264     if (lpRequest)
1265     {
1266         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
1267         HeapFree(GetProcessHeap(), 0, lpRequest);
1268         bSuccess = TRUE;
1269     }
1270
1271     return bSuccess;
1272 }
1273
1274
1275 /***********************************************************************
1276  *           INTERNET_AsyncCall (internal)
1277  *
1278  * Retrieves work request from queue
1279  *
1280  * RETURNS
1281  *
1282  */
1283 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
1284 {
1285     HANDLE hThread;
1286     DWORD dwTID;
1287     BOOL bSuccess = FALSE;
1288
1289     TRACE("\n");
1290
1291     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
1292     {
1293         InterlockedIncrement(&dwNumIdleThreads);
1294
1295         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS || 
1296             !(hThread = CreateThread(NULL, 0, 
1297             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
1298         {
1299             InterlockedDecrement(&dwNumThreads);
1300             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
1301             goto lerror;
1302         }
1303
1304         TRACE("Created new thread\n");
1305     }
1306
1307     bSuccess = TRUE;
1308     INTERNET_InsertWorkRequest(lpWorkRequest);
1309     SetEvent(hWorkEvent);
1310
1311 lerror:
1312
1313     return bSuccess;
1314 }
1315
1316
1317 /***********************************************************************
1318  *           INTERNET_ExecuteWork (internal)
1319  *
1320  * RETURNS
1321  *
1322  */
1323 VOID INTERNET_ExecuteWork()
1324 {
1325     WORKREQUEST workRequest;
1326
1327     TRACE("\n");
1328
1329     if (INTERNET_GetWorkRequest(&workRequest))
1330     {
1331         switch (workRequest.asyncall)
1332         {
1333             case FTPPUTFILEA:
1334                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
1335                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
1336                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
1337                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
1338                 break;
1339
1340             case FTPSETCURRENTDIRECTORYA:
1341                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1342                         (LPCSTR)workRequest.LPSZDIRECTORY);
1343                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1344                 break;
1345
1346             case FTPCREATEDIRECTORYA:
1347                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1348                         (LPCSTR)workRequest.LPSZDIRECTORY);
1349                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1350                 break;
1351
1352             case FTPFINDFIRSTFILEA:
1353                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION, 
1354                         (LPCSTR)workRequest.LPSZSEARCHFILE, 
1355                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS, 
1356                    workRequest.DWCONTEXT);
1357                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
1358                 break;
1359
1360             case FTPGETCURRENTDIRECTORYA:
1361                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION, 
1362                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
1363                 break;
1364
1365             case FTPOPENFILEA:
1366                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
1367                     (LPCSTR)workRequest.LPSZFILENAME,
1368                     workRequest.FDWACCESS,
1369                     workRequest.DWFLAGS,
1370                     workRequest.DWCONTEXT);
1371                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1372                  break;
1373
1374             case FTPGETFILEA:
1375                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
1376                     (LPCSTR)workRequest.LPSZREMOTEFILE,
1377                     (LPCSTR)workRequest.LPSZNEWFILE,
1378                     (BOOL)workRequest.FFAILIFEXISTS,
1379                     workRequest.DWLOCALFLAGSATTRIBUTE,
1380                     workRequest.DWFLAGS,
1381                     workRequest.DWCONTEXT);
1382                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
1383                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
1384                 break;
1385
1386             case FTPDELETEFILEA:
1387                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
1388                         (LPCSTR)workRequest.LPSZFILENAME);
1389                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
1390                 break;
1391
1392             case FTPREMOVEDIRECTORYA:
1393                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
1394                         (LPCSTR)workRequest.LPSZDIRECTORY);
1395                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
1396                 break;
1397
1398             case FTPRENAMEFILEA:
1399                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
1400                         (LPCSTR)workRequest.LPSZSRCFILE,
1401                         (LPCSTR)workRequest.LPSZDESTFILE);
1402                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
1403                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
1404                 break;
1405
1406             case INTERNETFINDNEXTA:
1407                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
1408                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
1409                 break;
1410
1411             case HTTPSENDREQUESTA:
1412                HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION, 
1413                        (LPCSTR)workRequest.LPSZHEADER,
1414                        workRequest.DWHEADERLENGTH,     
1415                        (LPVOID)workRequest.LPOPTIONAL, 
1416                        workRequest.DWOPTIONALLENGTH);
1417                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
1418                break;
1419
1420             case HTTPOPENREQUESTA:
1421                HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION, 
1422                        (LPCSTR)workRequest.LPSZVERB,
1423                        (LPCSTR)workRequest.LPSZOBJECTNAME,     
1424                        (LPCSTR)workRequest.LPSZVERSION, 
1425                        (LPCSTR)workRequest.LPSZREFERRER, 
1426                        (LPCSTR*)workRequest.LPSZACCEPTTYPES, 
1427                        workRequest.DWFLAGS,
1428                        workRequest.DWCONTEXT);
1429                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
1430                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
1431                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
1432                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
1433                 break;
1434
1435         }
1436     }
1437 }
1438
1439
1440 /***********************************************************************
1441  *          INTERNET_GetResponseBuffer
1442  *
1443  * RETURNS
1444  *
1445  */
1446 LPSTR INTERNET_GetResponseBuffer()
1447 {
1448     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1449     return lpwite->response;
1450 }
1451
1452
1453 /***********************************************************************
1454  *           INTERNET_GetNextLine  (internal)
1455  *
1456  * Parse next line in directory string listing
1457  *
1458  * RETURNS
1459  *   Pointer to beginning of next line
1460  *   NULL on failure
1461  *
1462  */
1463
1464 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
1465 {
1466     struct timeval tv;
1467     fd_set infd;
1468     BOOL bSuccess = FALSE;
1469     INT nRecv = 0;
1470
1471     TRACE("\n");
1472
1473     FD_ZERO(&infd);
1474     FD_SET(nSocket, &infd);
1475     tv.tv_sec=RESPONSE_TIMEOUT;
1476     tv.tv_usec=0;
1477
1478     while (nRecv < *dwBuffer)
1479     {
1480         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
1481         {
1482             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
1483             {
1484                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
1485                 goto lend;
1486             }
1487
1488             if (lpszBuffer[nRecv] == '\n')
1489             {
1490                 bSuccess = TRUE;
1491                 break;
1492             }
1493             if (lpszBuffer[nRecv] != '\r')
1494                 nRecv++;
1495         }
1496         else
1497         {
1498             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
1499             goto lend;
1500         }
1501     }
1502
1503 lend:
1504     if (bSuccess)
1505     {
1506         lpszBuffer[nRecv] = '\0';
1507         *dwBuffer = nRecv - 1;
1508         TRACE(":%d %s\n", nRecv, lpszBuffer);
1509         return lpszBuffer;
1510     }
1511     else
1512     {
1513         return NULL;
1514     }
1515 }
1516