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