Fixed definitions of TTTOOLINFOA/W_V1_SIZE and
[wine] / dlls / wininet / internet.c
1 /*
2  * Wininet
3  *
4  * Copyright 1999 Corel Corporation
5  * Copyright 2002 CodeWeavers Inc.
6  * Copyright 2002 Jaco Greeff
7  * Copyright 2002 TransGaming Technologies Inc.
8  *
9  * Ulrich Czekalla
10  * Aric Stewart
11  * David Hammerton
12  *
13  * This library is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU Lesser General Public
15  * License as published by the Free Software Foundation; either
16  * version 2.1 of the License, or (at your option) any later version.
17  *
18  * This library is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21  * Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public
24  * License along with this library; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
26  */
27
28 #include "config.h"
29
30 #define MAXHOSTNAME 100 /* from http.c */
31
32 #include <string.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <sys/types.h>
36 #ifdef HAVE_SYS_SOCKET_H
37 # include <sys/socket.h>
38 #endif
39 #ifdef HAVE_SYS_TIME_H
40 # include <sys/time.h>
41 #endif
42 #include <stdlib.h>
43 #include <ctype.h>
44 #ifdef HAVE_UNISTD_H
45 # include <unistd.h>
46 #endif
47
48 #include "ntstatus.h"
49 #include "windef.h"
50 #include "winbase.h"
51 #include "winreg.h"
52 #include "wininet.h"
53 #include "winnls.h"
54 #include "wine/debug.h"
55 #include "winerror.h"
56 #define NO_SHLWAPI_STREAM
57 #include "shlwapi.h"
58
59 #include "wine/exception.h"
60 #include "excpt.h"
61
62 #include "internet.h"
63
64 #include "wine/unicode.h"
65
66 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
67
68 #define MAX_IDLE_WORKER 1000*60*1
69 #define MAX_WORKER_THREADS 10
70 #define RESPONSE_TIMEOUT        30
71
72 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
73 (LPWININETAPPINFOA)(((LPWININETFTPSESSIONA)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
74
75 /* filter for page-fault exceptions */
76 static WINE_EXCEPTION_FILTER(page_fault)
77 {
78     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION ||
79         GetExceptionCode() == EXCEPTION_PRIV_INSTRUCTION)
80         return EXCEPTION_EXECUTE_HANDLER;
81     return EXCEPTION_CONTINUE_SEARCH;
82 }
83
84 typedef struct
85 {
86     DWORD  dwError;
87     CHAR   response[MAX_REPLY_LEN];
88 } WITHREADERROR, *LPWITHREADERROR;
89
90 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData);
91 VOID INTERNET_ExecuteWork();
92
93 DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
94 DWORD dwNumThreads;
95 DWORD dwNumIdleThreads;
96 DWORD dwNumJobs;
97 HANDLE hEventArray[2];
98 #define hQuitEvent hEventArray[0]
99 #define hWorkEvent hEventArray[1]
100 CRITICAL_SECTION csQueue;
101 LPWORKREQUEST lpHeadWorkQueue;
102 LPWORKREQUEST lpWorkQueueTail;
103
104 /***********************************************************************
105  * DllMain [Internal] Initializes the internal 'WININET.DLL'.
106  *
107  * PARAMS
108  *     hinstDLL    [I] handle to the DLL's instance
109  *     fdwReason   [I]
110  *     lpvReserved [I] reserved, must be NULL
111  *
112  * RETURNS
113  *     Success: TRUE
114  *     Failure: FALSE
115  */
116
117 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
118 {
119     TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
120
121     switch (fdwReason) {
122         case DLL_PROCESS_ATTACH:
123
124             g_dwTlsErrIndex = TlsAlloc();
125
126             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
127                 return FALSE;
128
129             hQuitEvent = CreateEventA(0, TRUE, FALSE, NULL);
130             hWorkEvent = CreateEventA(0, FALSE, FALSE, NULL);
131             InitializeCriticalSection(&csQueue);
132
133             dwNumThreads = 0;
134             dwNumIdleThreads = 0;
135             dwNumJobs = 0;
136
137         case DLL_THREAD_ATTACH:
138             {
139                 LPWITHREADERROR lpwite = HeapAlloc(GetProcessHeap(), 0, sizeof(WITHREADERROR));
140                 if (NULL == lpwite)
141                     return FALSE;
142
143                 TlsSetValue(g_dwTlsErrIndex, (LPVOID)lpwite);
144             }
145             break;
146
147         case DLL_THREAD_DETACH:
148             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
149                         {
150                                 LPVOID lpwite = TlsGetValue(g_dwTlsErrIndex);
151                                 if (lpwite)
152                    HeapFree(GetProcessHeap(), 0, lpwite);
153                         }
154             break;
155
156         case DLL_PROCESS_DETACH:
157
158             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
159             {
160                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
161                 TlsFree(g_dwTlsErrIndex);
162             }
163
164             SetEvent(hQuitEvent);
165
166             CloseHandle(hQuitEvent);
167             CloseHandle(hWorkEvent);
168             DeleteCriticalSection(&csQueue);
169             break;
170     }
171
172     return TRUE;
173 }
174
175
176 /***********************************************************************
177  *           InternetInitializeAutoProxyDll   (WININET.@)
178  *
179  * Setup the internal proxy
180  *
181  * PARAMETERS
182  *     dwReserved
183  *
184  * RETURNS
185  *     FALSE on failure
186  *
187  */
188 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
189 {
190     FIXME("STUB\n");
191     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
192     return FALSE;
193 }
194
195 /***********************************************************************
196  *           DetectAutoProxyUrl   (WININET.@)
197  *
198  * Auto detect the proxy url
199  *
200  * RETURNS
201  *     FALSE on failure
202  *
203  */
204 BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
205         DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
206 {
207     FIXME("STUB\n");
208     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
209     return FALSE;
210 }
211
212
213 /***********************************************************************
214  *           INTERNET_ConfigureProxyFromReg
215  *
216  * FIXME:
217  * The proxy may be specified in the form 'http=proxy.my.org'
218  * Presumably that means there can be ftp=ftpproxy.my.org too.
219  */
220 static BOOL INTERNET_ConfigureProxyFromReg( LPWININETAPPINFOA lpwai )
221 {
222     HKEY key;
223     DWORD r, keytype, len, enabled;
224     LPSTR lpszInternetSettings =
225         "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
226
227     r = RegOpenKeyA(HKEY_CURRENT_USER, lpszInternetSettings, &key);
228     if ( r != ERROR_SUCCESS )
229         return FALSE;
230
231     len = sizeof enabled;
232     r = RegQueryValueExA( key, "ProxyEnable", NULL, &keytype,
233                           (BYTE*)&enabled, &len);
234     if( (r == ERROR_SUCCESS) && enabled )
235     {
236         TRACE("Proxy is enabled.\n");
237
238         /* figure out how much memory the proxy setting takes */
239         r = RegQueryValueExA( key, "ProxyServer", NULL, &keytype, 
240                               NULL, &len);
241         if( (r == ERROR_SUCCESS) && len && (keytype == REG_SZ) )
242         {
243             LPSTR szProxy, p, szHttp = "http=";
244
245             szProxy=HeapAlloc( GetProcessHeap(), 0, len+1 );
246             RegQueryValueExA( key, "ProxyServer", NULL, &keytype,
247                               (BYTE*)szProxy, &len);
248
249             /* find the http proxy, and strip away everything else */
250             p = strstr( szProxy, szHttp );
251             if( p )
252             {
253                  p += strlen(szHttp);
254                  strcpy( szProxy, p );
255             }
256             p = strchr( szProxy, ' ' );
257             if( p )
258                 *p = 0;
259
260             lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
261             lpwai->lpszProxy = szProxy;
262
263             TRACE("http proxy = %s\n", lpwai->lpszProxy);
264         }
265         else
266             ERR("Couldn't read proxy server settings.\n");
267     }
268     else
269         TRACE("Proxy is not enabled.\n");
270     RegCloseKey(key);
271
272     return enabled;
273 }
274
275 /***********************************************************************
276  *           InternetOpenA   (WININET.@)
277  *
278  * Per-application initialization of wininet
279  *
280  * RETURNS
281  *    HINTERNET on success
282  *    NULL on failure
283  *
284  */
285 HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
286     LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
287 {
288     LPWININETAPPINFOA lpwai = NULL;
289
290     TRACE("(%s, %li, %s, %s, %li)\n", debugstr_a(lpszAgent), dwAccessType,
291          debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
292
293     /* Clear any error information */
294     INTERNET_SetLastError(0);
295
296     lpwai = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETAPPINFOA));
297     if (NULL == lpwai)
298     {
299         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
300         return NULL;
301     }
302  
303     memset(lpwai, 0, sizeof(WININETAPPINFOA));
304     lpwai->hdr.htype = WH_HINIT;
305     lpwai->hdr.lpwhparent = NULL;
306     lpwai->hdr.dwFlags = dwFlags;
307     lpwai->dwAccessType = dwAccessType;
308     lpwai->lpszProxyUsername = NULL;
309     lpwai->lpszProxyPassword = NULL;
310
311     if (NULL != lpszAgent)
312     {
313         lpwai->lpszAgent = HeapAlloc( GetProcessHeap(),0,
314                                       strlen(lpszAgent)+1);
315         if (lpwai->lpszAgent)
316             strcpy( lpwai->lpszAgent, lpszAgent );
317     }
318     if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
319         INTERNET_ConfigureProxyFromReg( lpwai );
320     else if (NULL != lpszProxy)
321     {
322         lpwai->lpszProxy = HeapAlloc( GetProcessHeap(), 0,
323                                       strlen(lpszProxy)+1);
324         if (lpwai->lpszProxy)
325             strcpy( lpwai->lpszProxy, lpszProxy );
326     }
327
328     if (NULL != lpszProxyBypass)
329     {
330         lpwai->lpszProxyBypass = HeapAlloc( GetProcessHeap(), 0,
331                                             strlen(lpszProxyBypass)+1);
332         if (lpwai->lpszProxyBypass)
333             strcpy( lpwai->lpszProxyBypass, lpszProxyBypass );
334     }
335
336     TRACE("returning %p\n", (HINTERNET)lpwai);
337     return (HINTERNET)lpwai;
338 }
339
340
341 /***********************************************************************
342  *           InternetOpenW   (WININET.@)
343  *
344  * Per-application initialization of wininet
345  *
346  * RETURNS
347  *    HINTERNET on success
348  *    NULL on failure
349  *
350  */
351 HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
352     LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
353 {
354     HINTERNET rc = (HINTERNET)NULL;
355     INT lenAgent = lstrlenW(lpszAgent)+1;
356     INT lenProxy = lstrlenW(lpszProxy)+1;
357     INT lenBypass = lstrlenW(lpszProxyBypass)+1;
358     CHAR *szAgent = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenAgent*sizeof(CHAR));
359     CHAR *szProxy = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenProxy*sizeof(CHAR));
360     CHAR *szBypass = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenBypass*sizeof(CHAR));
361
362     if (!szAgent || !szProxy || !szBypass)
363     {
364         if (szAgent)
365             free(szAgent);
366         if (szProxy)
367             free(szProxy);
368         if (szBypass)
369             free(szBypass);
370         return (HINTERNET)NULL;
371     }
372
373     WideCharToMultiByte(CP_ACP, -1, lpszAgent, -1, szAgent, lenAgent,
374         NULL, NULL);
375     WideCharToMultiByte(CP_ACP, -1, lpszProxy, -1, szProxy, lenProxy,
376         NULL, NULL);
377     WideCharToMultiByte(CP_ACP, -1, lpszProxyBypass, -1, szBypass, lenBypass,
378         NULL, NULL);
379
380     rc = InternetOpenA(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
381
382     HeapFree(GetProcessHeap(), 0, szAgent);
383     HeapFree(GetProcessHeap(), 0, szProxy);
384     HeapFree(GetProcessHeap(), 0, szBypass);
385
386     return rc;
387 }
388
389 /***********************************************************************
390  *           InternetGetLastResponseInfoA (WININET.@)
391  *
392  * Return last wininet error description on the calling thread
393  *
394  * RETURNS
395  *    TRUE on success of writting to buffer
396  *    FALSE on failure
397  *
398  */
399 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
400     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
401 {
402     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
403
404     TRACE("\n");
405
406     *lpdwError = lpwite->dwError;
407     if (lpwite->dwError)
408     {
409         strncpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
410         *lpdwBufferLength = strlen(lpszBuffer);
411     }
412     else
413         *lpdwBufferLength = 0;
414
415     return TRUE;
416 }
417
418
419 /***********************************************************************
420  *           InternetGetConnectedState (WININET.@)
421  *
422  * Return connected state
423  *
424  * RETURNS
425  *    TRUE if connected
426  *    if lpdwStatus is not null, return the status (off line,
427  *    modem, lan...) in it.
428  *    FALSE if not connected
429  */
430 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
431 {
432     if (lpdwStatus) {
433         FIXME("always returning LAN connection.\n");
434         *lpdwStatus = INTERNET_CONNECTION_LAN;
435     }
436     return TRUE;
437 }
438
439 /***********************************************************************
440  *           InternetGetConnectedStateEx (WININET.@)
441  *
442  * Return connected state
443  *
444  * RETURNS
445  *    TRUE if connected
446  *    if lpdwStatus is not null, return the status (off line,
447  *    modem, lan...) in it.
448  *    FALSE if not connected
449  */
450 BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
451                                          DWORD dwNameLen, DWORD dwReserved)
452 {
453     /* Must be zero */
454     if(dwReserved)
455         return FALSE;
456
457     if (lpdwStatus) {
458         FIXME("always returning LAN connection.\n");
459         *lpdwStatus = INTERNET_CONNECTION_LAN;
460     }
461     return TRUE;
462 }
463
464 /***********************************************************************
465  *           InternetConnectA (WININET.@)
466  *
467  * Open a ftp, gopher or http session
468  *
469  * RETURNS
470  *    HINTERNET a session handle on success
471  *    NULL on failure
472  *
473  */
474 HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
475     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
476     LPCSTR lpszUserName, LPCSTR lpszPassword,
477     DWORD dwService, DWORD dwFlags, DWORD dwContext)
478 {
479     HINTERNET rc = (HINTERNET) NULL;
480
481     TRACE("(%p, %s, %i, %s, %s, %li, %li, %li)\n", hInternet, debugstr_a(lpszServerName),
482           nServerPort, debugstr_a(lpszUserName), debugstr_a(lpszPassword),
483           dwService, dwFlags, dwContext);
484
485     /* Clear any error information */
486     INTERNET_SetLastError(0);
487
488     switch (dwService)
489     {
490         case INTERNET_SERVICE_FTP:
491             rc = FTP_Connect(hInternet, lpszServerName, nServerPort,
492             lpszUserName, lpszPassword, dwFlags, dwContext);
493             break;
494
495         case INTERNET_SERVICE_HTTP:
496             rc = HTTP_Connect(hInternet, lpszServerName, nServerPort,
497             lpszUserName, lpszPassword, dwFlags, dwContext);
498             break;
499
500         case INTERNET_SERVICE_GOPHER:
501         default:
502             break;
503     }
504
505     TRACE("returning %p\n", rc);
506     return rc;
507 }
508
509
510 /***********************************************************************
511  *           InternetConnectW (WININET.@)
512  *
513  * Open a ftp, gopher or http session
514  *
515  * RETURNS
516  *    HINTERNET a session handle on success
517  *    NULL on failure
518  *
519  */
520 HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
521     LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
522     LPCWSTR lpszUserName, LPCWSTR lpszPassword,
523     DWORD dwService, DWORD dwFlags, DWORD dwContext)
524 {
525     HINTERNET rc = (HINTERNET)NULL;
526     INT lenServer = 0;
527     INT lenUser = 0;
528     INT lenPass = 0;
529     CHAR *szServerName = NULL;
530     CHAR *szUserName = NULL;
531     CHAR *szPassword = NULL;
532
533     if (lpszServerName)
534     {
535         lenServer = lstrlenW(lpszServerName)+1;
536         szServerName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenServer*sizeof(CHAR));
537         WideCharToMultiByte(CP_ACP, -1, lpszServerName, -1, szServerName, lenServer,
538                             NULL, NULL);
539     }
540     if (lpszUserName)
541     {
542         lenUser = lstrlenW(lpszUserName)+1;
543         szUserName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUser*sizeof(CHAR));
544         WideCharToMultiByte(CP_ACP, -1, lpszUserName, -1, szUserName, lenUser,
545                             NULL, NULL);
546     }
547     if (lpszPassword)
548     {
549         lenPass = lstrlenW(lpszPassword)+1;
550         szPassword = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenPass*sizeof(CHAR));
551         WideCharToMultiByte(CP_ACP, -1, lpszPassword, -1, szPassword, lenPass,
552                             NULL, NULL);
553     }
554
555
556     rc = InternetConnectA(hInternet, szServerName, nServerPort,
557         szUserName, szPassword, dwService, dwFlags, dwContext);
558
559     if (szServerName) HeapFree(GetProcessHeap(), 0, szServerName);
560     if (szUserName) HeapFree(GetProcessHeap(), 0, szUserName);
561     if (szPassword) HeapFree(GetProcessHeap(), 0, szPassword);
562     return rc;
563 }
564
565
566 /***********************************************************************
567  *           InternetFindNextFileA (WININET.@)
568  *
569  * Continues a file search from a previous call to FindFirstFile
570  *
571  * RETURNS
572  *    TRUE on success
573  *    FALSE on failure
574  *
575  */
576 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
577 {
578     LPWININETAPPINFOA hIC = NULL;
579     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
580
581     TRACE("\n");
582
583     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
584     {
585         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
586         return FALSE;
587     }
588
589     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
590     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
591     {
592         WORKREQUEST workRequest;
593
594         workRequest.asyncall = INTERNETFINDNEXTA;
595         workRequest.HFTPSESSION = (DWORD)hFind;
596         workRequest.LPFINDFILEDATA = (DWORD)lpvFindData;
597
598         return INTERNET_AsyncCall(&workRequest);
599     }
600     else
601     {
602         return INTERNET_FindNextFileA(hFind, lpvFindData);
603     }
604 }
605
606 /***********************************************************************
607  *           INTERNET_FindNextFileA (Internal)
608  *
609  * Continues a file search from a previous call to FindFirstFile
610  *
611  * RETURNS
612  *    TRUE on success
613  *    FALSE on failure
614  *
615  */
616 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
617 {
618     BOOL bSuccess = TRUE;
619     LPWININETAPPINFOA hIC = NULL;
620     LPWIN32_FIND_DATAA lpFindFileData;
621     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
622
623     TRACE("\n");
624
625     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
626     {
627         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
628         return FALSE;
629     }
630
631     /* Clear any error information */
632     INTERNET_SetLastError(0);
633
634     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
635     {
636         FIXME("Only FTP find next supported\n");
637         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
638         return FALSE;
639     }
640
641     TRACE("index(%d) size(%ld)\n", lpwh->index, lpwh->size);
642
643     lpFindFileData = (LPWIN32_FIND_DATAA) lpvFindData;
644     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
645
646     if (lpwh->index >= lpwh->size)
647     {
648         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
649         bSuccess = FALSE;
650         goto lend;
651     }
652
653     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
654     lpwh->index++;
655
656     TRACE("\nName: %s\nSize: %ld\n", lpFindFileData->cFileName, lpFindFileData->nFileSizeLow);
657
658 lend:
659
660     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
661     if (hIC->lpfnStatusCB)
662     {
663         INTERNET_ASYNC_RESULT iar;
664
665         iar.dwResult = (DWORD)bSuccess;
666         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
667                                                INTERNET_GetLastError();
668
669         SendAsyncCallback(hIC, hFind, lpwh->hdr.dwContext,
670                       INTERNET_STATUS_REQUEST_COMPLETE, &iar,
671                        sizeof(INTERNET_ASYNC_RESULT));
672     }
673
674     return bSuccess;
675 }
676
677
678 /***********************************************************************
679  *           INTERNET_CloseHandle (internal)
680  *
681  * Close internet handle
682  *
683  * RETURNS
684  *    Void
685  *
686  */
687 VOID INTERNET_CloseHandle(LPWININETAPPINFOA lpwai)
688 {
689     TRACE("%p\n",lpwai);
690
691     SendAsyncCallback(lpwai, lpwai, lpwai->hdr.dwContext,
692                       INTERNET_STATUS_HANDLE_CLOSING, lpwai,
693                       sizeof(HINTERNET));
694
695     if (lpwai->lpszAgent)
696         HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
697
698     if (lpwai->lpszProxy)
699         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
700
701     if (lpwai->lpszProxyBypass)
702         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
703
704     if (lpwai->lpszProxyUsername)
705         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyUsername);
706
707     if (lpwai->lpszProxyPassword)
708         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyPassword);
709
710     HeapFree(GetProcessHeap(), 0, lpwai);
711 }
712
713
714 /***********************************************************************
715  *           InternetCloseHandle (WININET.@)
716  *
717  * Generic close handle function
718  *
719  * RETURNS
720  *    TRUE on success
721  *    FALSE on failure
722  *
723  */
724 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
725 {
726     BOOL retval;
727     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hInternet;
728
729     TRACE("%p\n",hInternet);
730     if (NULL == lpwh)
731         return FALSE;
732
733     __TRY {
734         /* Clear any error information */
735         INTERNET_SetLastError(0);
736         retval = FALSE;
737
738         switch (lpwh->htype)
739         {
740             case WH_HINIT:
741                 INTERNET_CloseHandle((LPWININETAPPINFOA) lpwh);
742                 retval = TRUE;
743                 break;
744
745             case WH_HHTTPSESSION:
746                 HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
747                 retval = TRUE;
748                 break;
749
750             case WH_HHTTPREQ:
751                 HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
752                 retval = TRUE;
753                 break;
754
755             case WH_HFTPSESSION:
756                 retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
757                 break;
758
759             case WH_HFINDNEXT:
760                 retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
761                 break;
762
763             case WH_HFILE:
764                 retval = FTP_CloseFileTransferHandle((LPWININETFILE) lpwh);
765                 break;
766                 
767             default:
768                 break;
769         }
770     } __EXCEPT(page_fault) {
771         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
772         return FALSE;
773     }
774     __ENDTRY
775
776     return retval;
777 }
778
779
780 /***********************************************************************
781  *           ConvertUrlComponentValue (Internal)
782  *
783  * Helper function for InternetCrackUrlW
784  *
785  */
786 void ConvertUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen,
787                               LPWSTR lpwszComponent, DWORD dwwComponentLen,
788                               LPCSTR lpszStart,
789                               LPCWSTR lpwszStart)
790 {
791     if (*dwComponentLen != 0)
792     {
793         int nASCIILength=WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,NULL,0,NULL,NULL);
794         if (*lppszComponent == NULL)
795         {
796             int nASCIIOffset=WideCharToMultiByte(CP_ACP,0,lpwszStart,lpwszComponent-lpwszStart,NULL,0,NULL,NULL);
797             *lppszComponent = (LPSTR)lpszStart+nASCIIOffset;
798             *dwComponentLen = nASCIILength;
799         }
800         else
801         {
802             INT ncpylen = min((*dwComponentLen)-1, nASCIILength);
803             WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,*lppszComponent,ncpylen+1,NULL,NULL);
804             (*lppszComponent)[ncpylen]=0;
805             *dwComponentLen = ncpylen;
806         }
807     }
808 }
809
810
811 /***********************************************************************
812  *           InternetCrackUrlA (WININET.@)
813  *
814  * Break up URL into its components
815  *
816  * TODO: Handle dwFlags
817  *
818  * RETURNS
819  *    TRUE on success
820  *    FALSE on failure
821  *
822  */
823 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
824     LPURL_COMPONENTSA lpUrlComponents)
825 {
826   DWORD nLength;
827   URL_COMPONENTSW UCW;
828   WCHAR* lpwszUrl;
829   if(dwUrlLength==0)
830       dwUrlLength=strlen(lpszUrl);
831   lpwszUrl=HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(dwUrlLength+1));
832   memset(lpwszUrl,0,sizeof(WCHAR)*(dwUrlLength+1));
833   nLength=MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,lpwszUrl,dwUrlLength+1);
834   memset(&UCW,0,sizeof(UCW));
835   if(lpUrlComponents->dwHostNameLength!=0)
836       UCW.dwHostNameLength=1;
837   if(lpUrlComponents->dwUserNameLength!=0)
838       UCW.dwUserNameLength=1;
839   if(lpUrlComponents->dwPasswordLength!=0)
840       UCW.dwPasswordLength=1;
841   if(lpUrlComponents->dwUrlPathLength!=0)
842       UCW.dwUrlPathLength=1;
843   if(lpUrlComponents->dwSchemeLength!=0)
844       UCW.dwSchemeLength=1;
845   if(lpUrlComponents->dwExtraInfoLength!=0)
846       UCW.dwExtraInfoLength=1;
847   if(!InternetCrackUrlW(lpwszUrl,nLength,dwFlags,&UCW))
848   {
849       HeapFree(GetProcessHeap(), 0, lpwszUrl);
850       return FALSE;
851   }
852   ConvertUrlComponentValue(&lpUrlComponents->lpszHostName, &lpUrlComponents->dwHostNameLength,
853                            UCW.lpszHostName, UCW.dwHostNameLength,
854                            lpszUrl, lpwszUrl);
855   ConvertUrlComponentValue(&lpUrlComponents->lpszUserName, &lpUrlComponents->dwUserNameLength,
856                            UCW.lpszUserName, UCW.dwUserNameLength,
857                            lpszUrl, lpwszUrl);
858   ConvertUrlComponentValue(&lpUrlComponents->lpszPassword, &lpUrlComponents->dwPasswordLength,
859                            UCW.lpszPassword, UCW.dwPasswordLength,
860                            lpszUrl, lpwszUrl);
861   ConvertUrlComponentValue(&lpUrlComponents->lpszUrlPath, &lpUrlComponents->dwUrlPathLength,
862                            UCW.lpszUrlPath, UCW.dwUrlPathLength,
863                            lpszUrl, lpwszUrl);
864   ConvertUrlComponentValue(&lpUrlComponents->lpszScheme, &lpUrlComponents->dwSchemeLength,
865                            UCW.lpszScheme, UCW.dwSchemeLength,
866                            lpszUrl, lpwszUrl);
867   ConvertUrlComponentValue(&lpUrlComponents->lpszExtraInfo, &lpUrlComponents->dwExtraInfoLength,
868                            UCW.lpszExtraInfo, UCW.dwExtraInfoLength,
869                            lpszUrl, lpwszUrl);
870   lpUrlComponents->nScheme=UCW.nScheme;
871   lpUrlComponents->nPort=UCW.nPort;
872   HeapFree(GetProcessHeap(), 0, lpwszUrl);
873   
874   TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", lpszUrl,
875           debugstr_an(lpUrlComponents->lpszScheme,lpUrlComponents->dwSchemeLength),
876           debugstr_an(lpUrlComponents->lpszHostName,lpUrlComponents->dwHostNameLength),
877           debugstr_an(lpUrlComponents->lpszUrlPath,lpUrlComponents->dwUrlPathLength),
878           debugstr_an(lpUrlComponents->lpszExtraInfo,lpUrlComponents->dwExtraInfoLength));
879
880   return TRUE;
881 }
882
883 /***********************************************************************
884  *           GetInternetSchemeW (internal)
885  *
886  * Get scheme of url
887  *
888  * RETURNS
889  *    scheme on success
890  *    INTERNET_SCHEME_UNKNOWN on failure
891  *
892  */
893 INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, INT nMaxCmp)
894 {
895     INTERNET_SCHEME iScheme=INTERNET_SCHEME_UNKNOWN;
896     WCHAR lpszFtp[]={'f','t','p',0};
897     WCHAR lpszGopher[]={'g','o','p','h','e','r',0};
898     WCHAR lpszHttp[]={'h','t','t','p',0};
899     WCHAR lpszHttps[]={'h','t','t','p','s',0};
900     WCHAR lpszFile[]={'f','i','l','e',0};
901     WCHAR lpszNews[]={'n','e','w','s',0};
902     WCHAR lpszMailto[]={'m','a','i','l','t','o',0};
903     WCHAR lpszRes[]={'r','e','s',0};
904     WCHAR* tempBuffer=NULL;
905     TRACE("\n");
906     if(lpszScheme==NULL)
907         return INTERNET_SCHEME_UNKNOWN;
908
909     tempBuffer=malloc(nMaxCmp+1);
910     strncpyW(tempBuffer,lpszScheme,nMaxCmp);
911     tempBuffer[nMaxCmp]=0;
912     strlwrW(tempBuffer);
913     if (nMaxCmp==strlenW(lpszFtp) && !strncmpW(lpszFtp, tempBuffer, nMaxCmp))
914         iScheme=INTERNET_SCHEME_FTP;
915     else if (nMaxCmp==strlenW(lpszGopher) && !strncmpW(lpszGopher, tempBuffer, nMaxCmp))
916         iScheme=INTERNET_SCHEME_GOPHER;
917     else if (nMaxCmp==strlenW(lpszHttp) && !strncmpW(lpszHttp, tempBuffer, nMaxCmp))
918         iScheme=INTERNET_SCHEME_HTTP;
919     else if (nMaxCmp==strlenW(lpszHttps) && !strncmpW(lpszHttps, tempBuffer, nMaxCmp))
920         iScheme=INTERNET_SCHEME_HTTPS;
921     else if (nMaxCmp==strlenW(lpszFile) && !strncmpW(lpszFile, tempBuffer, nMaxCmp))
922         iScheme=INTERNET_SCHEME_FILE;
923     else if (nMaxCmp==strlenW(lpszNews) && !strncmpW(lpszNews, tempBuffer, nMaxCmp))
924         iScheme=INTERNET_SCHEME_NEWS;
925     else if (nMaxCmp==strlenW(lpszMailto) && !strncmpW(lpszMailto, tempBuffer, nMaxCmp))
926         iScheme=INTERNET_SCHEME_MAILTO;
927     else if (nMaxCmp==strlenW(lpszRes) && !strncmpW(lpszRes, tempBuffer, nMaxCmp))
928         iScheme=INTERNET_SCHEME_RES;
929     free(tempBuffer);
930     return iScheme;
931 }
932
933 /***********************************************************************
934  *           SetUrlComponentValueW (Internal)
935  *
936  * Helper function for InternetCrackUrlW
937  *
938  * RETURNS
939  *    TRUE on success
940  *    FALSE on failure
941  *
942  */
943 BOOL SetUrlComponentValueW(LPWSTR* lppszComponent, LPDWORD dwComponentLen, LPCWSTR lpszStart, INT len)
944 {
945     TRACE("%s (%d)\n", debugstr_wn(lpszStart,len), len);
946
947     if (*dwComponentLen != 0 || *lppszComponent == NULL)
948     {
949         if (*lppszComponent == NULL)
950         {
951             *lppszComponent = (LPWSTR)lpszStart;
952             *dwComponentLen = len;
953         }
954         else
955         {
956             INT ncpylen = min((*dwComponentLen)-1, len);
957             strncpyW(*lppszComponent, lpszStart, ncpylen);
958             (*lppszComponent)[ncpylen] = '\0';
959             *dwComponentLen = ncpylen;
960         }
961     }
962
963     return TRUE;
964 }
965
966 /***********************************************************************
967  *           InternetCrackUrlW   (WININET.@)
968  */
969 BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
970                               LPURL_COMPONENTSW lpUC)
971 {
972   /*
973    * RFC 1808
974    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
975    *
976    */
977     LPWSTR lpszParam    = NULL;
978     BOOL  bIsAbsolute = FALSE;
979     LPWSTR lpszap = (WCHAR*)lpszUrl;
980     LPWSTR lpszcp = NULL;
981     WCHAR lpszSeparators[3]={';','?',0};
982     WCHAR lpszSlash[2]={'/',0};
983     if(dwUrlLength==0)
984         dwUrlLength=strlenW(lpszUrl);
985
986     TRACE("\n");
987
988     /* Determine if the URI is absolute. */
989     while (*lpszap != '\0')
990     {
991         if (isalnumW(*lpszap))
992         {
993             lpszap++;
994             continue;
995         }
996         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
997         {
998             bIsAbsolute = TRUE;
999             lpszcp = lpszap;
1000         }
1001         else
1002         {
1003             lpszcp = (LPWSTR)lpszUrl; /* Relative url */
1004         }
1005
1006         break;
1007     }
1008
1009     /* Parse <params> */
1010     lpszParam = strpbrkW(lpszap, lpszSeparators);
1011     if (lpszParam != NULL)
1012     {
1013         if (!SetUrlComponentValueW(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
1014                                    lpszParam, dwUrlLength-(lpszParam-lpszUrl)))
1015         {
1016             return FALSE;
1017         }
1018     }
1019
1020     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
1021     {
1022         LPWSTR lpszNetLoc;
1023         WCHAR wszAbout[]={'a','b','o','u','t',':',0};
1024
1025         /* Get scheme first. */
1026         lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1027         if (!SetUrlComponentValueW(&lpUC->lpszScheme, &lpUC->dwSchemeLength,
1028                                    lpszUrl, lpszcp - lpszUrl))
1029             return FALSE;
1030
1031         /* Eat ':' in protocol. */
1032         lpszcp++;
1033
1034         /* if the scheme is "about", there is no host */
1035         if(strncmpW(wszAbout,lpszUrl, lpszcp - lpszUrl)==0)
1036         {
1037             SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1038             SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1039             SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1040             lpUC->nPort = 0;
1041         }
1042         else
1043         {
1044             /* Skip over slashes. */
1045             if (*lpszcp == '/')
1046             {
1047                 lpszcp++;
1048                 if (*lpszcp == '/')
1049                 {
1050                     lpszcp++;
1051                     if (*lpszcp == '/')
1052                         lpszcp++;
1053                 }
1054             }
1055
1056             lpszNetLoc = strpbrkW(lpszcp, lpszSlash);
1057             if (lpszParam)
1058             {
1059                 if (lpszNetLoc)
1060                     lpszNetLoc = min(lpszNetLoc, lpszParam);
1061                 else
1062                     lpszNetLoc = lpszParam;
1063             }
1064             else if (!lpszNetLoc)
1065                 lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1066
1067             /* Parse net-loc */
1068             if (lpszNetLoc)
1069             {
1070                 LPWSTR lpszHost;
1071                 LPWSTR lpszPort;
1072
1073                 /* [<user>[<:password>]@]<host>[:<port>] */
1074                 /* First find the user and password if they exist */
1075
1076                 lpszHost = strchrW(lpszcp, '@');
1077                 if (lpszHost == NULL || lpszHost > lpszNetLoc)
1078                 {
1079                     /* username and password not specified. */
1080                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1081                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1082                 }
1083                 else /* Parse out username and password */
1084                 {
1085                     LPWSTR lpszUser = lpszcp;
1086                     LPWSTR lpszPasswd = lpszHost;
1087
1088                     while (lpszcp < lpszHost)
1089                     {
1090                         if (*lpszcp == ':')
1091                             lpszPasswd = lpszcp;
1092
1093                         lpszcp++;
1094                     }
1095
1096                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength,
1097                                           lpszUser, lpszPasswd - lpszUser);
1098
1099                     if (lpszPasswd != lpszHost)
1100                         lpszPasswd++;
1101                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
1102                                           lpszPasswd == lpszHost ? NULL : lpszPasswd,
1103                                           lpszHost - lpszPasswd);
1104
1105                     lpszcp++; /* Advance to beginning of host */
1106                 }
1107
1108                 /* Parse <host><:port> */
1109
1110                 lpszHost = lpszcp;
1111                 lpszPort = lpszNetLoc;
1112
1113                 /* special case for res:// URLs: there is no port here, so the host is the
1114                    entire string up to the first '/' */
1115                 if(lpUC->nScheme==INTERNET_SCHEME_RES)
1116                 {
1117                     SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1118                                           lpszHost, lpszPort - lpszHost);
1119                     lpUC->nPort = 0;
1120                     lpszcp=lpszNetLoc;
1121                 }
1122                 else
1123                 {
1124                     while (lpszcp < lpszNetLoc)
1125                     {
1126                         if (*lpszcp == ':')
1127                             lpszPort = lpszcp;
1128
1129                         lpszcp++;
1130                     }
1131
1132                     /* If the scheme is "file" and the host is just one letter, it's not a host */
1133                     if(lpUC->nScheme==INTERNET_SCHEME_FILE && (lpszPort-lpszHost)==1)
1134                     {
1135                         lpszcp=lpszHost;
1136                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1137                                               NULL, 0);
1138                         lpUC->nPort = 0;
1139                     }
1140                     else
1141                     {
1142                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1143                                               lpszHost, lpszPort - lpszHost);
1144                         if (lpszPort != lpszNetLoc)
1145                             lpUC->nPort = atoiW(++lpszPort);
1146                         else
1147                             lpUC->nPort = 0;
1148                     }
1149                 }
1150             }
1151         }
1152     }
1153
1154     /* Here lpszcp points to:
1155      *
1156      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1157      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1158      */
1159     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
1160     {
1161         INT len;
1162
1163         /* Only truncate the parameter list if it's already been saved
1164          * in lpUC->lpszExtraInfo.
1165          */
1166         if (lpszParam && lpUC->dwExtraInfoLength)
1167             len = lpszParam - lpszcp;
1168         else
1169         {
1170             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
1171              * newlines if necessary.
1172              */
1173             LPWSTR lpsznewline = strchrW(lpszcp, '\n');
1174             if (lpsznewline != NULL)
1175                 len = lpsznewline - lpszcp;
1176             else
1177                 len = dwUrlLength-(lpszcp-lpszUrl);
1178         }
1179
1180         if (!SetUrlComponentValueW(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength,
1181                                    lpszcp, len))
1182             return FALSE;
1183     }
1184     else
1185     {
1186         lpUC->dwUrlPathLength = 0;
1187     }
1188
1189     TRACE("%s: host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
1190              debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
1191              debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
1192              debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1193
1194     return TRUE;
1195 }
1196
1197 /***********************************************************************
1198  *           InternetAttemptConnect (WININET.@)
1199  *
1200  * Attempt to make a connection to the internet
1201  *
1202  * RETURNS
1203  *    ERROR_SUCCESS on success
1204  *    Error value   on failure
1205  *
1206  */
1207 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
1208 {
1209     FIXME("Stub\n");
1210     return ERROR_SUCCESS;
1211 }
1212
1213
1214 /***********************************************************************
1215  *           InternetCanonicalizeUrlA (WININET.@)
1216  *
1217  * Escape unsafe characters and spaces
1218  *
1219  * RETURNS
1220  *    TRUE on success
1221  *    FALSE on failure
1222  *
1223  */
1224 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
1225         LPDWORD lpdwBufferLength, DWORD dwFlags)
1226 {
1227     HRESULT hr;
1228     TRACE("%s %p %p %08lx\n",debugstr_a(lpszUrl), lpszBuffer,
1229           lpdwBufferLength, dwFlags);
1230
1231     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1232     dwFlags ^= ICU_NO_ENCODE;
1233
1234     dwFlags |= 0x80000000; /* Don't know what this means */
1235
1236     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1237
1238     return (hr == S_OK) ? TRUE : FALSE;
1239 }
1240
1241 /***********************************************************************
1242  *           InternetCanonicalizeUrlW (WININET.@)
1243  *
1244  * Escape unsafe characters and spaces
1245  *
1246  * RETURNS
1247  *    TRUE on success
1248  *    FALSE on failure
1249  *
1250  */
1251 BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
1252     LPDWORD lpdwBufferLength, DWORD dwFlags)
1253 {
1254     HRESULT hr;
1255     TRACE("%s %p %p %08lx\n", debugstr_w(lpszUrl), lpszBuffer,
1256         lpdwBufferLength, dwFlags);
1257
1258     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1259     dwFlags ^= ICU_NO_ENCODE;
1260
1261     dwFlags |= 0x80000000; /* Don't know what this means */
1262
1263     hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1264
1265     return (hr == S_OK) ? TRUE : FALSE;
1266 }
1267
1268
1269 /***********************************************************************
1270  *           InternetSetStatusCallbackA (WININET.@)
1271  *
1272  * Sets up a callback function which is called as progress is made
1273  * during an operation.
1274  *
1275  * RETURNS
1276  *    Previous callback or NULL         on success
1277  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
1278  *
1279  */
1280 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
1281         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
1282 {
1283     INTERNET_STATUS_CALLBACK retVal;
1284     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
1285
1286     TRACE("0x%08lx\n", (ULONG)hInternet);
1287     if (lpwai->hdr.htype != WH_HINIT)
1288         return INTERNET_INVALID_STATUS_CALLBACK;
1289
1290     retVal = lpwai->lpfnStatusCB;
1291     lpwai->lpfnStatusCB = lpfnIntCB;
1292
1293     return retVal;
1294 }
1295
1296
1297 /***********************************************************************
1298  *           InternetWriteFile (WININET.@)
1299  *
1300  * Write data to an open internet file
1301  *
1302  * RETURNS
1303  *    TRUE  on success
1304  *    FALSE on failure
1305  *
1306  */
1307 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
1308         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
1309 {
1310     BOOL retval = FALSE;
1311     int nSocket = -1;
1312     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1313
1314     TRACE("\n");
1315     if (NULL == lpwh)
1316         return FALSE;
1317
1318     switch (lpwh->htype)
1319     {
1320         case WH_HHTTPREQ:
1321             FIXME("This shouldn't be here! We don't support this kind"
1322                   " of connection anymore. Must use NETCON functions,"
1323                   " especially if using SSL\n");
1324             nSocket = ((LPWININETHTTPREQA)hFile)->netConnection.socketFD;
1325             break;
1326
1327         case WH_HFILE:
1328             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1329             break;
1330
1331         default:
1332             break;
1333     }
1334
1335     if (nSocket != -1)
1336     {
1337         int res = send(nSocket, lpBuffer, dwNumOfBytesToWrite, 0);
1338         retval = (res >= 0);
1339         *lpdwNumOfBytesWritten = retval ? res : 0;
1340     }
1341
1342     return retval;
1343 }
1344
1345
1346 /***********************************************************************
1347  *           InternetReadFile (WININET.@)
1348  *
1349  * Read data from an open internet file
1350  *
1351  * RETURNS
1352  *    TRUE  on success
1353  *    FALSE on failure
1354  *
1355  */
1356 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
1357         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
1358 {
1359     BOOL retval = FALSE;
1360     int nSocket = -1;
1361     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1362
1363     TRACE("\n");
1364
1365     if (NULL == lpwh)
1366         return FALSE;
1367
1368     /* FIXME: this should use NETCON functions! */
1369     switch (lpwh->htype)
1370     {
1371         case WH_HHTTPREQ:
1372             if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, lpBuffer,
1373                              dwNumOfBytesToRead, 0, (int *)dwNumOfBytesRead))
1374             {
1375                 *dwNumOfBytesRead = 0;
1376                 retval = TRUE; /* Under windows, it seems to return 0 even if nothing was read... */
1377             }
1378             else
1379                 retval = TRUE;
1380             break;
1381
1382         case WH_HFILE:
1383             /* FIXME: FTP should use NETCON_ stuff */
1384             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1385             if (nSocket != -1)
1386             {
1387                 int res = recv(nSocket, lpBuffer, dwNumOfBytesToRead, 0);
1388                 retval = (res >= 0);
1389                 *dwNumOfBytesRead = retval ? res : 0;
1390             }
1391             break;
1392
1393         default:
1394             break;
1395     }
1396
1397     return retval;
1398 }
1399
1400 /***********************************************************************
1401  *           InternetReadFileExA (WININET.@)
1402  *
1403  * Read data from an open internet file
1404  *
1405  * RETURNS
1406  *    TRUE  on success
1407  *    FALSE on failure
1408  *
1409  */
1410 BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffer,
1411         DWORD dwFlags, DWORD dwContext)
1412 {
1413   FIXME("stub\n");
1414   return FALSE;
1415 }
1416
1417 /***********************************************************************
1418  *           InternetReadFileExW (WININET.@)
1419  *
1420  * Read data from an open internet file
1421  *
1422  * RETURNS
1423  *    TRUE  on success
1424  *    FALSE on failure
1425  *
1426  */
1427 BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
1428         DWORD dwFlags, DWORD dwContext)
1429 {
1430   FIXME("stub\n");
1431
1432   INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1433   return FALSE;
1434 }
1435
1436 /***********************************************************************
1437  *           INET_QueryOptionHelper (internal)
1438  */
1439 static BOOL INET_QueryOptionHelper(BOOL bIsUnicode, HINTERNET hInternet, DWORD dwOption,
1440                                    LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1441 {
1442     LPWININETHANDLEHEADER lpwhh;
1443     BOOL bSuccess = FALSE;
1444
1445     TRACE("(%p, 0x%08lx, %p, %p)\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
1446
1447     if (NULL == hInternet)
1448     {
1449         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1450         return FALSE;
1451     }
1452
1453     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1454
1455     switch (dwOption)
1456     {
1457         case INTERNET_OPTION_HANDLE_TYPE:
1458         {
1459             ULONG type = lpwhh->htype;
1460             TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
1461
1462             if (*lpdwBufferLength < sizeof(ULONG))
1463                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1464             else
1465             {
1466                 memcpy(lpBuffer, &type, sizeof(ULONG));
1467                     *lpdwBufferLength = sizeof(ULONG);
1468                 bSuccess = TRUE;
1469             }
1470             break;
1471         }
1472
1473         case INTERNET_OPTION_REQUEST_FLAGS:
1474         {
1475             ULONG flags = 4;
1476             TRACE("INTERNET_OPTION_REQUEST_FLAGS: %ld\n", flags);
1477             if (*lpdwBufferLength < sizeof(ULONG))
1478                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1479             else
1480             {
1481                 memcpy(lpBuffer, &flags, sizeof(ULONG));
1482                     *lpdwBufferLength = sizeof(ULONG);
1483                 bSuccess = TRUE;
1484             }
1485             break;
1486         }
1487
1488         case INTERNET_OPTION_URL:
1489         case INTERNET_OPTION_DATAFILE_NAME:
1490         {
1491             ULONG type = lpwhh->htype;
1492             if (type == WH_HHTTPREQ)
1493             {
1494                 LPWININETHTTPREQA lpreq = hInternet;
1495                 char url[1023];
1496
1497                 sprintf(url,"http://%s%s",lpreq->lpszHostName,lpreq->lpszPath);
1498                 TRACE("INTERNET_OPTION_URL: %s\n",url);
1499                 if (*lpdwBufferLength < strlen(url)+1)
1500                     INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1501                 else
1502                 {
1503                     if(bIsUnicode)
1504                     {
1505                         *lpdwBufferLength=MultiByteToWideChar(CP_ACP,0,url,-1,lpBuffer,*lpdwBufferLength);
1506                     }
1507                     else
1508                     {
1509                         memcpy(lpBuffer, url, strlen(url)+1);
1510                         *lpdwBufferLength = strlen(url)+1;
1511                     }
1512                     bSuccess = TRUE;
1513                 }
1514             }
1515             break;
1516         }
1517        case INTERNET_OPTION_HTTP_VERSION:
1518        {
1519             /*
1520              * Presently hardcoded to 1.1
1521              */
1522             ((HTTP_VERSION_INFO*)lpBuffer)->dwMajorVersion = 1;
1523             ((HTTP_VERSION_INFO*)lpBuffer)->dwMinorVersion = 1;
1524             bSuccess = TRUE;
1525         break;
1526        }
1527
1528        default:
1529          FIXME("Stub! %ld \n",dwOption);
1530          break;
1531     }
1532
1533     return bSuccess;
1534 }
1535
1536 /***********************************************************************
1537  *           InternetQueryOptionW (WININET.@)
1538  *
1539  * Queries an options on the specified handle
1540  *
1541  * RETURNS
1542  *    TRUE  on success
1543  *    FALSE on failure
1544  *
1545  */
1546 BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
1547                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1548 {
1549     return INET_QueryOptionHelper(TRUE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1550 }
1551
1552 /***********************************************************************
1553  *           InternetQueryOptionA (WININET.@)
1554  *
1555  * Queries an options on the specified handle
1556  *
1557  * RETURNS
1558  *    TRUE  on success
1559  *    FALSE on failure
1560  *
1561  */
1562 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
1563                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1564 {
1565     return INET_QueryOptionHelper(FALSE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1566 }
1567
1568
1569 /***********************************************************************
1570  *           InternetSetOptionW (WININET.@)
1571  *
1572  * Sets an options on the specified handle
1573  *
1574  * RETURNS
1575  *    TRUE  on success
1576  *    FALSE on failure
1577  *
1578  */
1579 BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
1580                            LPVOID lpBuffer, DWORD dwBufferLength)
1581 {
1582     LPWININETHANDLEHEADER lpwhh;
1583
1584     TRACE("0x%08lx\n", dwOption);
1585
1586     if (NULL == hInternet)
1587     {
1588         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1589         return FALSE;
1590     }
1591
1592     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1593
1594     switch (dwOption)
1595     {
1596     case INTERNET_OPTION_HTTP_VERSION:
1597       {
1598         HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
1599         FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
1600       }
1601       break;
1602     case INTERNET_OPTION_ERROR_MASK:
1603       {
1604         unsigned long flags=*(unsigned long*)lpBuffer;
1605         FIXME("Option INTERNET_OPTION_ERROR_MASK(%ld): STUB\n",flags);
1606       }
1607       break;
1608     case INTERNET_OPTION_CODEPAGE:
1609       {
1610         unsigned long codepage=*(unsigned long*)lpBuffer;
1611         FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n",codepage);
1612       }
1613       break;
1614     case INTERNET_OPTION_REQUEST_PRIORITY:
1615       {
1616         unsigned long priority=*(unsigned long*)lpBuffer;
1617         FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n",priority);
1618       }
1619       break;
1620     default:
1621         FIXME("Option %ld STUB\n",dwOption);
1622         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1623         return FALSE;
1624     }
1625
1626     return TRUE;
1627 }
1628
1629
1630 /***********************************************************************
1631  *           InternetSetOptionA (WININET.@)
1632  *
1633  * Sets an options on the specified handle.
1634  *
1635  * RETURNS
1636  *    TRUE  on success
1637  *    FALSE on failure
1638  *
1639  */
1640 BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
1641                            LPVOID lpBuffer, DWORD dwBufferLength)
1642 {
1643     LPVOID wbuffer;
1644     DWORD wlen;
1645     BOOL r;
1646
1647     switch( dwOption )
1648     {
1649     case INTERNET_OPTION_PROXY:
1650         {
1651         LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
1652         LPINTERNET_PROXY_INFOW piw;
1653         DWORD proxlen, prbylen;
1654         LPWSTR prox, prby;
1655
1656         proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
1657         prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
1658         wlen = sizeof(*piw) + proxlen + prbylen;
1659         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1660         piw = (LPINTERNET_PROXY_INFOW) wbuffer;
1661         piw->dwAccessType = pi->dwAccessType;
1662         prox = (LPWSTR) &piw[1];
1663         prby = &prox[proxlen+1];
1664         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
1665         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
1666         piw->lpszProxy = prox;
1667         piw->lpszProxyBypass = prby;
1668         }
1669         break;
1670     case INTERNET_OPTION_USER_AGENT:
1671     case INTERNET_OPTION_USERNAME:
1672     case INTERNET_OPTION_PASSWORD:
1673         wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1674                                    NULL, 0 );
1675         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1676         MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1677                                    wbuffer, wlen );
1678         break;
1679     default:
1680         wbuffer = lpBuffer;
1681         wlen = dwBufferLength;
1682     }
1683
1684     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
1685
1686     if( lpBuffer != wbuffer )
1687         HeapFree( GetProcessHeap(), 0, wbuffer );
1688
1689     return r;
1690 }
1691
1692
1693 /***********************************************************************
1694  *           InternetSetOptionExA (WININET.@)
1695  */
1696 BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
1697                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1698 {
1699     FIXME("Flags %08lx ignored\n", dwFlags);
1700     return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
1701 }
1702
1703 /***********************************************************************
1704  *           InternetSetOptionExW (WININET.@)
1705  */
1706 BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
1707                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1708 {
1709     FIXME("Flags %08lx ignored\n", dwFlags);
1710     if( dwFlags & ~ISO_VALID_FLAGS )
1711     {
1712         SetLastError( ERROR_INVALID_PARAMETER );
1713         return FALSE;
1714     }
1715     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
1716 }
1717
1718
1719 /***********************************************************************
1720  *      InternetCheckConnectionA (WININET.@)
1721  *
1722  * Pings a requested host to check internet connection
1723  *
1724  * RETURNS
1725  *   TRUE on success and FALSE on failure. If a failure then
1726  *   ERROR_NOT_CONNECTED is placesd into GetLastError
1727  *
1728  */
1729 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
1730 {
1731 /*
1732  * this is a kludge which runs the resident ping program and reads the output.
1733  *
1734  * Anyone have a better idea?
1735  */
1736
1737   BOOL   rc = FALSE;
1738   char command[1024];
1739   char host[1024];
1740   int status = -1;
1741
1742   FIXME("\n");
1743
1744   /*
1745    * Crack or set the Address
1746    */
1747   if (lpszUrl == NULL)
1748   {
1749      /*
1750       * According to the doc we are supost to use the ip for the next
1751       * server in the WnInet internal server database. I have
1752       * no idea what that is or how to get it.
1753       *
1754       * So someone needs to implement this.
1755       */
1756      FIXME("Unimplemented with URL of NULL\n");
1757      return TRUE;
1758   }
1759   else
1760   {
1761      URL_COMPONENTSA componets;
1762
1763      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
1764      componets.lpszHostName = (LPSTR)&host;
1765      componets.dwHostNameLength = 1024;
1766
1767      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
1768        goto End;
1769
1770      TRACE("host name : %s\n",componets.lpszHostName);
1771   }
1772
1773   /*
1774    * Build our ping command
1775    */
1776   strcpy(command,"ping -w 1 ");
1777   strcat(command,host);
1778   strcat(command," >/dev/null 2>/dev/null");
1779
1780   TRACE("Ping command is : %s\n",command);
1781
1782   status = system(command);
1783
1784   TRACE("Ping returned a code of %i \n",status);
1785
1786   /* Ping return code of 0 indicates success */
1787   if (status == 0)
1788      rc = TRUE;
1789
1790 End:
1791
1792   if (rc == FALSE)
1793     SetLastError(ERROR_NOT_CONNECTED);
1794
1795   return rc;
1796 }
1797
1798
1799 /***********************************************************************
1800  *      InternetCheckConnectionW (WININET.@)
1801  *
1802  * Pings a requested host to check internet connection
1803  *
1804  * RETURNS
1805  *   TRUE on success and FALSE on failure. If a failure then
1806  *   ERROR_NOT_CONNECTED is placed into GetLastError
1807  *
1808  */
1809 BOOL WINAPI InternetCheckConnectionW(LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
1810 {
1811     CHAR *szUrl;
1812     INT len;
1813     BOOL rc;
1814
1815     len = lstrlenW(lpszUrl)+1;
1816     if (!(szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
1817         return FALSE;
1818     WideCharToMultiByte(CP_ACP, -1, lpszUrl, -1, szUrl, len, NULL, NULL);
1819     rc = InternetCheckConnectionA((LPCSTR)szUrl, dwFlags, dwReserved);
1820     HeapFree(GetProcessHeap(), 0, szUrl);
1821     
1822     return rc;
1823 }
1824
1825
1826 /**********************************************************
1827  *      InternetOpenUrlA (WININET.@)
1828  *
1829  * Opens an URL
1830  *
1831  * RETURNS
1832  *   handle of connection or NULL on failure
1833  */
1834 HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
1835     LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1836 {
1837   URL_COMPONENTSA urlComponents;
1838   char protocol[32], hostName[MAXHOSTNAME], userName[1024];
1839   char password[1024], path[2048], extra[1024];
1840   HINTERNET client = NULL, client1 = NULL;
1841
1842   TRACE("(%p, %s, %s, %08lx, %08lx, %08lx\n", hInternet, debugstr_a(lpszUrl), debugstr_a(lpszHeaders),
1843        dwHeadersLength, dwFlags, dwContext);
1844
1845   urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
1846   urlComponents.lpszScheme = protocol;
1847   urlComponents.dwSchemeLength = 32;
1848   urlComponents.lpszHostName = hostName;
1849   urlComponents.dwHostNameLength = MAXHOSTNAME;
1850   urlComponents.lpszUserName = userName;
1851   urlComponents.dwUserNameLength = 1024;
1852   urlComponents.lpszPassword = password;
1853   urlComponents.dwPasswordLength = 1024;
1854   urlComponents.lpszUrlPath = path;
1855   urlComponents.dwUrlPathLength = 2048;
1856   urlComponents.lpszExtraInfo = extra;
1857   urlComponents.dwExtraInfoLength = 1024;
1858   if(!InternetCrackUrlA(lpszUrl, strlen(lpszUrl), 0, &urlComponents))
1859     return NULL;
1860   switch(urlComponents.nScheme) {
1861   case INTERNET_SCHEME_FTP:
1862     if(urlComponents.nPort == 0)
1863       urlComponents.nPort = INTERNET_DEFAULT_FTP_PORT;
1864     client = InternetConnectA(hInternet, hostName, urlComponents.nPort,
1865         userName, password, INTERNET_SERVICE_FTP, dwFlags, dwContext);
1866     return FtpOpenFileA(client, path, GENERIC_READ, dwFlags, dwContext);
1867   case INTERNET_SCHEME_HTTP:
1868   case INTERNET_SCHEME_HTTPS:
1869   {
1870     LPCSTR accept[2] = { "*/*", NULL };
1871     char *hostreq=(char*)malloc(strlen(hostName)+9);
1872     sprintf(hostreq, "Host: %s\r\n", hostName);
1873     if(urlComponents.nPort == 0) {
1874       if(urlComponents.nScheme == INTERNET_SCHEME_HTTP)
1875         urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1876       else
1877         urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
1878     }
1879     client = InternetConnectA(hInternet, hostName, urlComponents.nPort, userName,
1880         password, INTERNET_SERVICE_HTTP, dwFlags, dwContext);
1881     if(client == NULL)
1882       return NULL;
1883     client1 = HttpOpenRequestA(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
1884     if(client1 == NULL) {
1885       InternetCloseHandle(client);
1886       return NULL;
1887     }
1888     HttpAddRequestHeadersA(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
1889     if(!HttpSendRequestA(client1, NULL, 0, NULL, 0)) {
1890       InternetCloseHandle(client1);
1891       InternetCloseHandle(client);
1892       return NULL;
1893     }
1894     return client1;
1895   }
1896   case INTERNET_SCHEME_GOPHER:
1897     /* gopher doesn't seem to be implemented in wine, but it's supposed
1898      * to be supported by InternetOpenUrlA. */
1899   default:
1900     return NULL;
1901   }
1902 }
1903
1904
1905 /**********************************************************
1906  *      InternetOpenUrlW (WININET.@)
1907  *
1908  * Opens an URL
1909  *
1910  * RETURNS
1911  *   handle of connection or NULL on failure
1912  */
1913 HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
1914     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1915 {
1916     HINTERNET rc = (HINTERNET)NULL;
1917
1918     INT lenUrl = lstrlenW(lpszUrl)+1;
1919     INT lenHeaders = lstrlenW(lpszHeaders)+1;
1920     CHAR *szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUrl*sizeof(CHAR));
1921     CHAR *szHeaders = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenHeaders*sizeof(CHAR));
1922
1923     if (!szUrl || !szHeaders)
1924     {
1925         if (szUrl)
1926             HeapFree(GetProcessHeap(), 0, szUrl);
1927         if (szHeaders)
1928             HeapFree(GetProcessHeap(), 0, szHeaders);
1929         return (HINTERNET)NULL;
1930     }
1931
1932     WideCharToMultiByte(CP_ACP, -1, lpszUrl, -1, szUrl, lenUrl,
1933         NULL, NULL);
1934     WideCharToMultiByte(CP_ACP, -1, lpszHeaders, -1, szHeaders, lenHeaders,
1935         NULL, NULL);
1936
1937     rc = InternetOpenUrlA(hInternet, szUrl, szHeaders,
1938         dwHeadersLength, dwFlags, dwContext);
1939
1940     HeapFree(GetProcessHeap(), 0, szUrl);
1941     HeapFree(GetProcessHeap(), 0, szHeaders);
1942
1943     return rc;
1944 }
1945
1946
1947 /***********************************************************************
1948  *           INTERNET_SetLastError (internal)
1949  *
1950  * Set last thread specific error
1951  *
1952  * RETURNS
1953  *
1954  */
1955 void INTERNET_SetLastError(DWORD dwError)
1956 {
1957     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1958
1959     SetLastError(dwError);
1960     if(lpwite)
1961         lpwite->dwError = dwError;
1962 }
1963
1964
1965 /***********************************************************************
1966  *           INTERNET_GetLastError (internal)
1967  *
1968  * Get last thread specific error
1969  *
1970  * RETURNS
1971  *
1972  */
1973 DWORD INTERNET_GetLastError()
1974 {
1975     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1976     return lpwite->dwError;
1977 }
1978
1979
1980 /***********************************************************************
1981  *           INTERNET_WorkerThreadFunc (internal)
1982  *
1983  * Worker thread execution function
1984  *
1985  * RETURNS
1986  *
1987  */
1988 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1989 {
1990     DWORD dwWaitRes;
1991
1992     while (1)
1993     {
1994         if(dwNumJobs > 0) {
1995             INTERNET_ExecuteWork();
1996             continue;
1997         }
1998         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
1999
2000         if (dwWaitRes == WAIT_OBJECT_0 + 1)
2001             INTERNET_ExecuteWork();
2002         else
2003             break;
2004
2005         InterlockedIncrement(&dwNumIdleThreads);
2006     }
2007
2008     InterlockedDecrement(&dwNumIdleThreads);
2009     InterlockedDecrement(&dwNumThreads);
2010     TRACE("Worker thread exiting\n");
2011     return TRUE;
2012 }
2013
2014
2015 /***********************************************************************
2016  *           INTERNET_InsertWorkRequest (internal)
2017  *
2018  * Insert work request into queue
2019  *
2020  * RETURNS
2021  *
2022  */
2023 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
2024 {
2025     BOOL bSuccess = FALSE;
2026     LPWORKREQUEST lpNewRequest;
2027
2028     TRACE("\n");
2029
2030     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
2031     if (lpNewRequest)
2032     {
2033         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
2034         lpNewRequest->prev = NULL;
2035
2036         EnterCriticalSection(&csQueue);
2037
2038         lpNewRequest->next = lpWorkQueueTail;
2039         if (lpWorkQueueTail)
2040             lpWorkQueueTail->prev = lpNewRequest;
2041         lpWorkQueueTail = lpNewRequest;
2042         if (!lpHeadWorkQueue)
2043             lpHeadWorkQueue = lpWorkQueueTail;
2044
2045         LeaveCriticalSection(&csQueue);
2046
2047         bSuccess = TRUE;
2048         InterlockedIncrement(&dwNumJobs);
2049     }
2050
2051     return bSuccess;
2052 }
2053
2054
2055 /***********************************************************************
2056  *           INTERNET_GetWorkRequest (internal)
2057  *
2058  * Retrieves work request from queue
2059  *
2060  * RETURNS
2061  *
2062  */
2063 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
2064 {
2065     BOOL bSuccess = FALSE;
2066     LPWORKREQUEST lpRequest = NULL;
2067
2068     TRACE("\n");
2069
2070     EnterCriticalSection(&csQueue);
2071
2072     if (lpHeadWorkQueue)
2073     {
2074         lpRequest = lpHeadWorkQueue;
2075         lpHeadWorkQueue = lpHeadWorkQueue->prev;
2076         if (lpRequest == lpWorkQueueTail)
2077             lpWorkQueueTail = lpHeadWorkQueue;
2078     }
2079
2080     LeaveCriticalSection(&csQueue);
2081
2082     if (lpRequest)
2083     {
2084         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
2085         HeapFree(GetProcessHeap(), 0, lpRequest);
2086         bSuccess = TRUE;
2087         InterlockedDecrement(&dwNumJobs);
2088     }
2089
2090     return bSuccess;
2091 }
2092
2093
2094 /***********************************************************************
2095  *           INTERNET_AsyncCall (internal)
2096  *
2097  * Retrieves work request from queue
2098  *
2099  * RETURNS
2100  *
2101  */
2102 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
2103 {
2104     HANDLE hThread;
2105     DWORD dwTID;
2106     BOOL bSuccess = FALSE;
2107
2108     TRACE("\n");
2109
2110     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
2111     {
2112         InterlockedIncrement(&dwNumIdleThreads);
2113
2114         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS ||
2115             !(hThread = CreateThread(NULL, 0,
2116             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
2117         {
2118             InterlockedDecrement(&dwNumThreads);
2119             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
2120             goto lerror;
2121         }
2122
2123         TRACE("Created new thread\n");
2124     }
2125
2126     bSuccess = TRUE;
2127     INTERNET_InsertWorkRequest(lpWorkRequest);
2128     SetEvent(hWorkEvent);
2129
2130 lerror:
2131
2132     return bSuccess;
2133 }
2134
2135
2136 /***********************************************************************
2137  *           INTERNET_ExecuteWork (internal)
2138  *
2139  * RETURNS
2140  *
2141  */
2142 VOID INTERNET_ExecuteWork()
2143 {
2144     WORKREQUEST workRequest;
2145
2146     TRACE("\n");
2147
2148     if (INTERNET_GetWorkRequest(&workRequest))
2149     {
2150         TRACE("Got work %d\n", workRequest.asyncall);
2151         switch (workRequest.asyncall)
2152         {
2153             case FTPPUTFILEA:
2154                 FTP_FtpPutFileA((HINTERNET)workRequest.HFTPSESSION, (LPCSTR)workRequest.LPSZLOCALFILE,
2155                     (LPCSTR)workRequest.LPSZNEWREMOTEFILE, workRequest.DWFLAGS, workRequest.DWCONTEXT);
2156                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZLOCALFILE);
2157                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWREMOTEFILE);
2158                 break;
2159
2160             case FTPSETCURRENTDIRECTORYA:
2161                 FTP_FtpSetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION,
2162                         (LPCSTR)workRequest.LPSZDIRECTORY);
2163                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
2164                 break;
2165
2166             case FTPCREATEDIRECTORYA:
2167                 FTP_FtpCreateDirectoryA((HINTERNET)workRequest.HFTPSESSION,
2168                         (LPCSTR)workRequest.LPSZDIRECTORY);
2169                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
2170                 break;
2171
2172             case FTPFINDFIRSTFILEA:
2173                 FTP_FtpFindFirstFileA((HINTERNET)workRequest.HFTPSESSION,
2174                         (LPCSTR)workRequest.LPSZSEARCHFILE,
2175                    (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA, workRequest.DWFLAGS,
2176                    workRequest.DWCONTEXT);
2177                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSEARCHFILE);
2178                 break;
2179
2180             case FTPGETCURRENTDIRECTORYA:
2181                 FTP_FtpGetCurrentDirectoryA((HINTERNET)workRequest.HFTPSESSION,
2182                         (LPSTR)workRequest.LPSZDIRECTORY, (LPDWORD)workRequest.LPDWDIRECTORY);
2183                 break;
2184
2185             case FTPOPENFILEA:
2186                  FTP_FtpOpenFileA((HINTERNET)workRequest.HFTPSESSION,
2187                     (LPCSTR)workRequest.LPSZFILENAME,
2188                     workRequest.FDWACCESS,
2189                     workRequest.DWFLAGS,
2190                     workRequest.DWCONTEXT);
2191                  HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
2192                  break;
2193
2194             case FTPGETFILEA:
2195                 FTP_FtpGetFileA((HINTERNET)workRequest.HFTPSESSION,
2196                     (LPCSTR)workRequest.LPSZREMOTEFILE,
2197                     (LPCSTR)workRequest.LPSZNEWFILE,
2198                     (BOOL)workRequest.FFAILIFEXISTS,
2199                     workRequest.DWLOCALFLAGSATTRIBUTE,
2200                     workRequest.DWFLAGS,
2201                     workRequest.DWCONTEXT);
2202                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREMOTEFILE);
2203                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZNEWFILE);
2204                 break;
2205
2206             case FTPDELETEFILEA:
2207                 FTP_FtpDeleteFileA((HINTERNET)workRequest.HFTPSESSION,
2208                         (LPCSTR)workRequest.LPSZFILENAME);
2209                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZFILENAME);
2210                 break;
2211
2212             case FTPREMOVEDIRECTORYA:
2213                 FTP_FtpRemoveDirectoryA((HINTERNET)workRequest.HFTPSESSION,
2214                         (LPCSTR)workRequest.LPSZDIRECTORY);
2215                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDIRECTORY);
2216                 break;
2217
2218             case FTPRENAMEFILEA:
2219                 FTP_FtpRenameFileA((HINTERNET)workRequest.HFTPSESSION,
2220                         (LPCSTR)workRequest.LPSZSRCFILE,
2221                         (LPCSTR)workRequest.LPSZDESTFILE);
2222                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZSRCFILE);
2223                 HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZDESTFILE);
2224                 break;
2225
2226             case INTERNETFINDNEXTA:
2227                 INTERNET_FindNextFileA((HINTERNET)workRequest.HFTPSESSION,
2228                     (LPWIN32_FIND_DATAA)workRequest.LPFINDFILEDATA);
2229                 break;
2230
2231             case HTTPSENDREQUESTA:
2232                HTTP_HttpSendRequestA((HINTERNET)workRequest.HFTPSESSION,
2233                        (LPCSTR)workRequest.LPSZHEADER,
2234                        workRequest.DWHEADERLENGTH,
2235                        (LPVOID)workRequest.LPOPTIONAL,
2236                        workRequest.DWOPTIONALLENGTH);
2237                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZHEADER);
2238                break;
2239
2240             case HTTPOPENREQUESTA:
2241                HTTP_HttpOpenRequestA((HINTERNET)workRequest.HFTPSESSION,
2242                        (LPCSTR)workRequest.LPSZVERB,
2243                        (LPCSTR)workRequest.LPSZOBJECTNAME,
2244                        (LPCSTR)workRequest.LPSZVERSION,
2245                        (LPCSTR)workRequest.LPSZREFERRER,
2246                        (LPCSTR*)workRequest.LPSZACCEPTTYPES,
2247                        workRequest.DWFLAGS,
2248                        workRequest.DWCONTEXT);
2249                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERB);
2250                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZOBJECTNAME);
2251                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZVERSION);
2252                HeapFree(GetProcessHeap(), 0, (LPVOID)workRequest.LPSZREFERRER);
2253                 break;
2254
2255             case SENDCALLBACK:
2256                SendAsyncCallbackInt((LPWININETAPPINFOA)workRequest.param1,
2257                        (HINTERNET)workRequest.param2, workRequest.param3,
2258                         workRequest.param4, (LPVOID)workRequest.param5,
2259                         workRequest.param6);
2260                break;
2261         }
2262     }
2263 }
2264
2265
2266 /***********************************************************************
2267  *          INTERNET_GetResponseBuffer
2268  *
2269  * RETURNS
2270  *
2271  */
2272 LPSTR INTERNET_GetResponseBuffer()
2273 {
2274     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
2275     TRACE("\n");
2276     return lpwite->response;
2277 }
2278
2279 /***********************************************************************
2280  *           INTERNET_GetNextLine  (internal)
2281  *
2282  * Parse next line in directory string listing
2283  *
2284  * RETURNS
2285  *   Pointer to beginning of next line
2286  *   NULL on failure
2287  *
2288  */
2289
2290 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
2291 {
2292     struct timeval tv;
2293     fd_set infd;
2294     BOOL bSuccess = FALSE;
2295     INT nRecv = 0;
2296
2297     TRACE("\n");
2298
2299     FD_ZERO(&infd);
2300     FD_SET(nSocket, &infd);
2301     tv.tv_sec=RESPONSE_TIMEOUT;
2302     tv.tv_usec=0;
2303
2304     while (nRecv < *dwBuffer)
2305     {
2306         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
2307         {
2308             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2309             {
2310                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2311                 goto lend;
2312             }
2313
2314             if (lpszBuffer[nRecv] == '\n')
2315             {
2316                 bSuccess = TRUE;
2317                 break;
2318             }
2319             if (lpszBuffer[nRecv] != '\r')
2320                 nRecv++;
2321         }
2322         else
2323         {
2324             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2325             goto lend;
2326         }
2327     }
2328
2329 lend:
2330     if (bSuccess)
2331     {
2332         lpszBuffer[nRecv] = '\0';
2333         *dwBuffer = nRecv - 1;
2334         TRACE(":%d %s\n", nRecv, lpszBuffer);
2335         return lpszBuffer;
2336     }
2337     else
2338     {
2339         return NULL;
2340     }
2341 }
2342
2343 /***********************************************************************
2344  *
2345  */
2346 BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
2347                                 LPDWORD lpdwNumberOfBytesAvailble,
2348                                 DWORD dwFlags, DWORD dwConext)
2349 {
2350     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hFile;
2351     INT retval = -1;
2352     char buffer[4048];
2353
2354
2355     if (NULL == lpwhr)
2356     {
2357         SetLastError(ERROR_NO_MORE_FILES);
2358         return FALSE;
2359     }
2360
2361     TRACE("-->  %p %i\n",lpwhr,lpwhr->hdr.htype);
2362
2363     switch (lpwhr->hdr.htype)
2364     {
2365     case WH_HHTTPREQ:
2366         if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, buffer,
2367                          4048, MSG_PEEK, (int *)lpdwNumberOfBytesAvailble))
2368         {
2369             SetLastError(ERROR_NO_MORE_FILES);
2370             retval = FALSE;
2371         }
2372         else
2373             retval = TRUE;
2374         break;
2375
2376     default:
2377         FIXME("unsupported file type\n");
2378         break;
2379     }
2380
2381     TRACE("<-- %i\n",retval);
2382     return (retval+1);
2383 }
2384
2385
2386 /***********************************************************************
2387  *
2388  */
2389 BOOL WINAPI InternetLockRequestFile( HINTERNET hInternet, HANDLE
2390 *lphLockReqHandle)
2391 {
2392     FIXME("STUB\n");
2393     return FALSE;
2394 }
2395
2396 BOOL WINAPI InternetUnlockRequestFile( HANDLE hLockHandle)
2397 {
2398     FIXME("STUB\n");
2399     return FALSE;
2400 }
2401
2402
2403 /***********************************************************************
2404  *           InternetAutodial
2405  *
2406  * On windows this function is supposed to dial the default internet
2407  * connection. We don't want to have Wine dial out to the internet so
2408  * we return TRUE by default. It might be nice to check if we are connected.
2409  *
2410  * RETURNS
2411  *   TRUE on success
2412  *   FALSE on failure
2413  *
2414  */
2415 BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
2416 {
2417     FIXME("STUB\n");
2418
2419     /* Tell that we are connected to the internet. */
2420     return TRUE;
2421 }
2422
2423 /***********************************************************************
2424  *           InternetAutodialHangup
2425  *
2426  * Hangs up an connection made with InternetAutodial
2427  *
2428  * PARAM
2429  *    dwReserved
2430  * RETURNS
2431  *   TRUE on success
2432  *   FALSE on failure
2433  *
2434  */
2435 BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
2436 {
2437     FIXME("STUB\n");
2438
2439     /* we didn't dial, we don't disconnect */
2440     return TRUE;
2441 }
2442
2443 /***********************************************************************
2444  *
2445  *         InternetCombineUrlA
2446  *
2447  * Combine a base URL with a relative URL
2448  *
2449  * RETURNS
2450  *   TRUE on success
2451  *   FALSE on failure
2452  *
2453  */
2454
2455 BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
2456                                 LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
2457                                 DWORD dwFlags)
2458 {
2459     HRESULT hr=S_OK;
2460     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2461     dwFlags ^= ICU_NO_ENCODE;
2462     hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2463
2464     return (hr==S_OK);
2465 }
2466
2467 /***********************************************************************
2468  *
2469  *         InternetCombineUrlW
2470  *
2471  * Combine a base URL with a relative URL
2472  *
2473  * RETURNS
2474  *   TRUE on success
2475  *   FALSE on failure
2476  *
2477  */
2478
2479 BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
2480                                 LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
2481                                 DWORD dwFlags)
2482 {
2483     HRESULT hr=S_OK;
2484     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2485     dwFlags ^= ICU_NO_ENCODE;
2486     hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2487
2488     return (hr==S_OK);
2489 }
2490
2491 /***********************************************************************
2492  *
2493  *         InternetCreateUrlA
2494  *
2495  * RETURNS
2496  *   TRUE on success
2497  *   FALSE on failure
2498  *
2499  */
2500 BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
2501                                LPSTR lpszUrl, LPDWORD lpdwUrlLength)
2502 {
2503     FIXME("\n");
2504     return FALSE;
2505 }
2506
2507 /***********************************************************************
2508  *
2509  *         InternetCreateUrlW
2510  *
2511  * RETURNS
2512  *   TRUE on success
2513  *   FALSE on failure
2514  *
2515  */
2516 BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
2517                                LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
2518 {
2519     FIXME("\n");
2520     return FALSE;
2521 }