Avoid excessive heap memory reallocation when generating EMF
[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 = WideCharToMultiByte(CP_ACP, 0, lpszAgent, -1, NULL, 0, NULL, NULL);
356     INT lenProxy = WideCharToMultiByte(CP_ACP, 0, lpszProxy, -1, NULL, 0, NULL, NULL);
357     INT lenBypass = WideCharToMultiByte(CP_ACP, 0, lpszProxyBypass, -1, NULL, 0, NULL, NULL);
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             HeapFree(GetProcessHeap(), 0, szAgent);
366         if (szProxy)
367             HeapFree(GetProcessHeap(), 0, szProxy);
368         if (szBypass)
369             HeapFree(GetProcessHeap(), 0, szBypass);
370         return (HINTERNET)NULL;
371     }
372
373     WideCharToMultiByte(CP_ACP, 0, lpszAgent, -1, szAgent, lenAgent,
374         NULL, NULL);
375     WideCharToMultiByte(CP_ACP, 0, lpszProxy, -1, szProxy, lenProxy,
376         NULL, NULL);
377     WideCharToMultiByte(CP_ACP, 0, 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 = WideCharToMultiByte(CP_ACP, 0, lpszServerName, -1, NULL, 0,
536                             NULL, NULL);
537         szServerName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenServer*sizeof(CHAR));
538         WideCharToMultiByte(CP_ACP, 0, lpszServerName, -1, szServerName, lenServer,
539                             NULL, NULL);
540     }
541     if (lpszUserName)
542     {
543         lenUser = WideCharToMultiByte(CP_ACP, 0, lpszUserName, -1, NULL, 0,
544                             NULL, NULL);
545         szUserName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUser*sizeof(CHAR));
546         WideCharToMultiByte(CP_ACP, 0, lpszUserName, -1, szUserName, lenUser,
547                             NULL, NULL);
548     }
549     if (lpszPassword)
550     {
551         lenPass = WideCharToMultiByte(CP_ACP, 0, lpszPassword, -1, NULL, 0,
552                             NULL, NULL);
553         szPassword = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenPass*sizeof(CHAR));
554         WideCharToMultiByte(CP_ACP, 0, lpszPassword, -1, szPassword, lenPass,
555                             NULL, NULL);
556     }
557
558
559     rc = InternetConnectA(hInternet, szServerName, nServerPort,
560         szUserName, szPassword, dwService, dwFlags, dwContext);
561
562     if (szServerName) HeapFree(GetProcessHeap(), 0, szServerName);
563     if (szUserName) HeapFree(GetProcessHeap(), 0, szUserName);
564     if (szPassword) HeapFree(GetProcessHeap(), 0, szPassword);
565     return rc;
566 }
567
568
569 /***********************************************************************
570  *           InternetFindNextFileA (WININET.@)
571  *
572  * Continues a file search from a previous call to FindFirstFile
573  *
574  * RETURNS
575  *    TRUE on success
576  *    FALSE on failure
577  *
578  */
579 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
580 {
581     LPWININETAPPINFOA hIC = NULL;
582     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
583
584     TRACE("\n");
585
586     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
587     {
588         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
589         return FALSE;
590     }
591
592     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
593     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
594     {
595         WORKREQUEST workRequest;
596         struct WORKREQ_INTERNETFINDNEXTA *req;
597
598         workRequest.asyncall = INTERNETFINDNEXTA;
599         workRequest.handle = hFind;
600         req = &workRequest.u.InternetFindNextA;
601         req->lpFindFileData = lpvFindData;
602
603         return INTERNET_AsyncCall(&workRequest);
604     }
605     else
606     {
607         return INTERNET_FindNextFileA(hFind, lpvFindData);
608     }
609 }
610
611 /***********************************************************************
612  *           INTERNET_FindNextFileA (Internal)
613  *
614  * Continues a file search from a previous call to FindFirstFile
615  *
616  * RETURNS
617  *    TRUE on success
618  *    FALSE on failure
619  *
620  */
621 BOOL WINAPI INTERNET_FindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
622 {
623     BOOL bSuccess = TRUE;
624     LPWININETAPPINFOA hIC = NULL;
625     LPWIN32_FIND_DATAA lpFindFileData;
626     LPWININETFINDNEXTA lpwh = (LPWININETFINDNEXTA) hFind;
627
628     TRACE("\n");
629
630     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
631     {
632         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
633         return FALSE;
634     }
635
636     /* Clear any error information */
637     INTERNET_SetLastError(0);
638
639     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
640     {
641         FIXME("Only FTP find next supported\n");
642         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
643         return FALSE;
644     }
645
646     TRACE("index(%d) size(%ld)\n", lpwh->index, lpwh->size);
647
648     lpFindFileData = (LPWIN32_FIND_DATAA) lpvFindData;
649     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
650
651     if (lpwh->index >= lpwh->size)
652     {
653         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
654         bSuccess = FALSE;
655         goto lend;
656     }
657
658     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
659     lpwh->index++;
660
661     TRACE("\nName: %s\nSize: %ld\n", lpFindFileData->cFileName, lpFindFileData->nFileSizeLow);
662
663 lend:
664
665     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
666     if (hIC->lpfnStatusCB)
667     {
668         INTERNET_ASYNC_RESULT iar;
669
670         iar.dwResult = (DWORD)bSuccess;
671         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
672                                                INTERNET_GetLastError();
673
674         SendAsyncCallback(hIC, hFind, lpwh->hdr.dwContext,
675                       INTERNET_STATUS_REQUEST_COMPLETE, &iar,
676                        sizeof(INTERNET_ASYNC_RESULT));
677     }
678
679     return bSuccess;
680 }
681
682
683 /***********************************************************************
684  *           INTERNET_CloseHandle (internal)
685  *
686  * Close internet handle
687  *
688  * RETURNS
689  *    Void
690  *
691  */
692 VOID INTERNET_CloseHandle(LPWININETAPPINFOA lpwai)
693 {
694     TRACE("%p\n",lpwai);
695
696     SendAsyncCallback(lpwai, lpwai, lpwai->hdr.dwContext,
697                       INTERNET_STATUS_HANDLE_CLOSING, lpwai,
698                       sizeof(HINTERNET));
699
700     if (lpwai->lpszAgent)
701         HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
702
703     if (lpwai->lpszProxy)
704         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
705
706     if (lpwai->lpszProxyBypass)
707         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
708
709     if (lpwai->lpszProxyUsername)
710         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyUsername);
711
712     if (lpwai->lpszProxyPassword)
713         HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyPassword);
714
715     HeapFree(GetProcessHeap(), 0, lpwai);
716 }
717
718
719 /***********************************************************************
720  *           InternetCloseHandle (WININET.@)
721  *
722  * Generic close handle function
723  *
724  * RETURNS
725  *    TRUE on success
726  *    FALSE on failure
727  *
728  */
729 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
730 {
731     BOOL retval;
732     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hInternet;
733
734     TRACE("%p\n",hInternet);
735     if (NULL == lpwh)
736         return FALSE;
737
738     __TRY {
739         /* Clear any error information */
740         INTERNET_SetLastError(0);
741         retval = FALSE;
742
743         switch (lpwh->htype)
744         {
745             case WH_HINIT:
746                 INTERNET_CloseHandle((LPWININETAPPINFOA) lpwh);
747                 retval = TRUE;
748                 break;
749
750             case WH_HHTTPSESSION:
751                 HTTP_CloseHTTPSessionHandle((LPWININETHTTPSESSIONA) lpwh);
752                 retval = TRUE;
753                 break;
754
755             case WH_HHTTPREQ:
756                 HTTP_CloseHTTPRequestHandle((LPWININETHTTPREQA) lpwh);
757                 retval = TRUE;
758                 break;
759
760             case WH_HFTPSESSION:
761                 retval = FTP_CloseSessionHandle((LPWININETFTPSESSIONA) lpwh);
762                 break;
763
764             case WH_HFINDNEXT:
765                 retval = FTP_CloseFindNextHandle((LPWININETFINDNEXTA) lpwh);
766                 break;
767
768             case WH_HFILE:
769                 retval = FTP_CloseFileTransferHandle((LPWININETFILE) lpwh);
770                 break;
771                 
772             default:
773                 break;
774         }
775     } __EXCEPT(page_fault) {
776         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
777         return FALSE;
778     }
779     __ENDTRY
780
781     return retval;
782 }
783
784
785 /***********************************************************************
786  *           ConvertUrlComponentValue (Internal)
787  *
788  * Helper function for InternetCrackUrlW
789  *
790  */
791 void ConvertUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen,
792                               LPWSTR lpwszComponent, DWORD dwwComponentLen,
793                               LPCSTR lpszStart,
794                               LPCWSTR lpwszStart)
795 {
796     if (*dwComponentLen != 0)
797     {
798         int nASCIILength=WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,NULL,0,NULL,NULL);
799         if (*lppszComponent == NULL)
800         {
801             int nASCIIOffset=WideCharToMultiByte(CP_ACP,0,lpwszStart,lpwszComponent-lpwszStart,NULL,0,NULL,NULL);
802             *lppszComponent = (LPSTR)lpszStart+nASCIIOffset;
803             *dwComponentLen = nASCIILength;
804         }
805         else
806         {
807             INT ncpylen = min((*dwComponentLen)-1, nASCIILength);
808             WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,*lppszComponent,ncpylen+1,NULL,NULL);
809             (*lppszComponent)[ncpylen]=0;
810             *dwComponentLen = ncpylen;
811         }
812     }
813 }
814
815
816 /***********************************************************************
817  *           InternetCrackUrlA (WININET.@)
818  *
819  * Break up URL into its components
820  *
821  * TODO: Handle dwFlags
822  *
823  * RETURNS
824  *    TRUE on success
825  *    FALSE on failure
826  *
827  */
828 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
829     LPURL_COMPONENTSA lpUrlComponents)
830 {
831   DWORD nLength;
832   URL_COMPONENTSW UCW;
833   WCHAR* lpwszUrl;
834   if(dwUrlLength==0)
835       dwUrlLength=strlen(lpszUrl);
836   lpwszUrl=HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR)*(dwUrlLength+1));
837   memset(lpwszUrl,0,sizeof(WCHAR)*(dwUrlLength+1));
838   nLength=MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,lpwszUrl,dwUrlLength+1);
839   memset(&UCW,0,sizeof(UCW));
840   if(lpUrlComponents->dwHostNameLength!=0)
841       UCW.dwHostNameLength=1;
842   if(lpUrlComponents->dwUserNameLength!=0)
843       UCW.dwUserNameLength=1;
844   if(lpUrlComponents->dwPasswordLength!=0)
845       UCW.dwPasswordLength=1;
846   if(lpUrlComponents->dwUrlPathLength!=0)
847       UCW.dwUrlPathLength=1;
848   if(lpUrlComponents->dwSchemeLength!=0)
849       UCW.dwSchemeLength=1;
850   if(lpUrlComponents->dwExtraInfoLength!=0)
851       UCW.dwExtraInfoLength=1;
852   if(!InternetCrackUrlW(lpwszUrl,nLength,dwFlags,&UCW))
853   {
854       HeapFree(GetProcessHeap(), 0, lpwszUrl);
855       return FALSE;
856   }
857   ConvertUrlComponentValue(&lpUrlComponents->lpszHostName, &lpUrlComponents->dwHostNameLength,
858                            UCW.lpszHostName, UCW.dwHostNameLength,
859                            lpszUrl, lpwszUrl);
860   ConvertUrlComponentValue(&lpUrlComponents->lpszUserName, &lpUrlComponents->dwUserNameLength,
861                            UCW.lpszUserName, UCW.dwUserNameLength,
862                            lpszUrl, lpwszUrl);
863   ConvertUrlComponentValue(&lpUrlComponents->lpszPassword, &lpUrlComponents->dwPasswordLength,
864                            UCW.lpszPassword, UCW.dwPasswordLength,
865                            lpszUrl, lpwszUrl);
866   ConvertUrlComponentValue(&lpUrlComponents->lpszUrlPath, &lpUrlComponents->dwUrlPathLength,
867                            UCW.lpszUrlPath, UCW.dwUrlPathLength,
868                            lpszUrl, lpwszUrl);
869   ConvertUrlComponentValue(&lpUrlComponents->lpszScheme, &lpUrlComponents->dwSchemeLength,
870                            UCW.lpszScheme, UCW.dwSchemeLength,
871                            lpszUrl, lpwszUrl);
872   ConvertUrlComponentValue(&lpUrlComponents->lpszExtraInfo, &lpUrlComponents->dwExtraInfoLength,
873                            UCW.lpszExtraInfo, UCW.dwExtraInfoLength,
874                            lpszUrl, lpwszUrl);
875   lpUrlComponents->nScheme=UCW.nScheme;
876   lpUrlComponents->nPort=UCW.nPort;
877   HeapFree(GetProcessHeap(), 0, lpwszUrl);
878   
879   TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", lpszUrl,
880           debugstr_an(lpUrlComponents->lpszScheme,lpUrlComponents->dwSchemeLength),
881           debugstr_an(lpUrlComponents->lpszHostName,lpUrlComponents->dwHostNameLength),
882           debugstr_an(lpUrlComponents->lpszUrlPath,lpUrlComponents->dwUrlPathLength),
883           debugstr_an(lpUrlComponents->lpszExtraInfo,lpUrlComponents->dwExtraInfoLength));
884
885   return TRUE;
886 }
887
888 /***********************************************************************
889  *           GetInternetSchemeW (internal)
890  *
891  * Get scheme of url
892  *
893  * RETURNS
894  *    scheme on success
895  *    INTERNET_SCHEME_UNKNOWN on failure
896  *
897  */
898 INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, INT nMaxCmp)
899 {
900     INTERNET_SCHEME iScheme=INTERNET_SCHEME_UNKNOWN;
901     WCHAR lpszFtp[]={'f','t','p',0};
902     WCHAR lpszGopher[]={'g','o','p','h','e','r',0};
903     WCHAR lpszHttp[]={'h','t','t','p',0};
904     WCHAR lpszHttps[]={'h','t','t','p','s',0};
905     WCHAR lpszFile[]={'f','i','l','e',0};
906     WCHAR lpszNews[]={'n','e','w','s',0};
907     WCHAR lpszMailto[]={'m','a','i','l','t','o',0};
908     WCHAR lpszRes[]={'r','e','s',0};
909     WCHAR* tempBuffer=NULL;
910     TRACE("\n");
911     if(lpszScheme==NULL)
912         return INTERNET_SCHEME_UNKNOWN;
913
914     tempBuffer=HeapAlloc(GetProcessHeap(),0,(nMaxCmp+1)*sizeof(WCHAR));
915     strncpyW(tempBuffer,lpszScheme,nMaxCmp);
916     tempBuffer[nMaxCmp]=0;
917     strlwrW(tempBuffer);
918     if (nMaxCmp==strlenW(lpszFtp) && !strncmpW(lpszFtp, tempBuffer, nMaxCmp))
919         iScheme=INTERNET_SCHEME_FTP;
920     else if (nMaxCmp==strlenW(lpszGopher) && !strncmpW(lpszGopher, tempBuffer, nMaxCmp))
921         iScheme=INTERNET_SCHEME_GOPHER;
922     else if (nMaxCmp==strlenW(lpszHttp) && !strncmpW(lpszHttp, tempBuffer, nMaxCmp))
923         iScheme=INTERNET_SCHEME_HTTP;
924     else if (nMaxCmp==strlenW(lpszHttps) && !strncmpW(lpszHttps, tempBuffer, nMaxCmp))
925         iScheme=INTERNET_SCHEME_HTTPS;
926     else if (nMaxCmp==strlenW(lpszFile) && !strncmpW(lpszFile, tempBuffer, nMaxCmp))
927         iScheme=INTERNET_SCHEME_FILE;
928     else if (nMaxCmp==strlenW(lpszNews) && !strncmpW(lpszNews, tempBuffer, nMaxCmp))
929         iScheme=INTERNET_SCHEME_NEWS;
930     else if (nMaxCmp==strlenW(lpszMailto) && !strncmpW(lpszMailto, tempBuffer, nMaxCmp))
931         iScheme=INTERNET_SCHEME_MAILTO;
932     else if (nMaxCmp==strlenW(lpszRes) && !strncmpW(lpszRes, tempBuffer, nMaxCmp))
933         iScheme=INTERNET_SCHEME_RES;
934     HeapFree(GetProcessHeap(),0,tempBuffer);
935     return iScheme;
936 }
937
938 /***********************************************************************
939  *           SetUrlComponentValueW (Internal)
940  *
941  * Helper function for InternetCrackUrlW
942  *
943  * RETURNS
944  *    TRUE on success
945  *    FALSE on failure
946  *
947  */
948 BOOL SetUrlComponentValueW(LPWSTR* lppszComponent, LPDWORD dwComponentLen, LPCWSTR lpszStart, INT len)
949 {
950     TRACE("%s (%d)\n", debugstr_wn(lpszStart,len), len);
951
952     if (*dwComponentLen != 0 || *lppszComponent == NULL)
953     {
954         if (*lppszComponent == NULL)
955         {
956             *lppszComponent = (LPWSTR)lpszStart;
957             *dwComponentLen = len;
958         }
959         else
960         {
961             INT ncpylen = min((*dwComponentLen)-1, len);
962             strncpyW(*lppszComponent, lpszStart, ncpylen);
963             (*lppszComponent)[ncpylen] = '\0';
964             *dwComponentLen = ncpylen;
965         }
966     }
967
968     return TRUE;
969 }
970
971 /***********************************************************************
972  *           InternetCrackUrlW   (WININET.@)
973  */
974 BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
975                               LPURL_COMPONENTSW lpUC)
976 {
977   /*
978    * RFC 1808
979    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
980    *
981    */
982     LPWSTR lpszParam    = NULL;
983     BOOL  bIsAbsolute = FALSE;
984     LPWSTR lpszap = (WCHAR*)lpszUrl;
985     LPWSTR lpszcp = NULL;
986     WCHAR lpszSeparators[3]={';','?',0};
987     WCHAR lpszSlash[2]={'/',0};
988     if(dwUrlLength==0)
989         dwUrlLength=strlenW(lpszUrl);
990
991     TRACE("\n");
992
993     /* Determine if the URI is absolute. */
994     while (*lpszap != '\0')
995     {
996         if (isalnumW(*lpszap))
997         {
998             lpszap++;
999             continue;
1000         }
1001         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
1002         {
1003             bIsAbsolute = TRUE;
1004             lpszcp = lpszap;
1005         }
1006         else
1007         {
1008             lpszcp = (LPWSTR)lpszUrl; /* Relative url */
1009         }
1010
1011         break;
1012     }
1013
1014     /* Parse <params> */
1015     lpszParam = strpbrkW(lpszap, lpszSeparators);
1016     if (lpszParam != NULL)
1017     {
1018         if (!SetUrlComponentValueW(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
1019                                    lpszParam, dwUrlLength-(lpszParam-lpszUrl)))
1020         {
1021             return FALSE;
1022         }
1023     }
1024
1025     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
1026     {
1027         LPWSTR lpszNetLoc;
1028         WCHAR wszAbout[]={'a','b','o','u','t',':',0};
1029
1030         /* Get scheme first. */
1031         lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1032         if (!SetUrlComponentValueW(&lpUC->lpszScheme, &lpUC->dwSchemeLength,
1033                                    lpszUrl, lpszcp - lpszUrl))
1034             return FALSE;
1035
1036         /* Eat ':' in protocol. */
1037         lpszcp++;
1038
1039         /* if the scheme is "about", there is no host */
1040         if(strncmpW(wszAbout,lpszUrl, lpszcp - lpszUrl)==0)
1041         {
1042             SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1043             SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1044             SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1045             lpUC->nPort = 0;
1046         }
1047         else
1048         {
1049             /* Skip over slashes. */
1050             if (*lpszcp == '/')
1051             {
1052                 lpszcp++;
1053                 if (*lpszcp == '/')
1054                 {
1055                     lpszcp++;
1056                     if (*lpszcp == '/')
1057                         lpszcp++;
1058                 }
1059             }
1060
1061             lpszNetLoc = strpbrkW(lpszcp, lpszSlash);
1062             if (lpszParam)
1063             {
1064                 if (lpszNetLoc)
1065                     lpszNetLoc = min(lpszNetLoc, lpszParam);
1066                 else
1067                     lpszNetLoc = lpszParam;
1068             }
1069             else if (!lpszNetLoc)
1070                 lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1071
1072             /* Parse net-loc */
1073             if (lpszNetLoc)
1074             {
1075                 LPWSTR lpszHost;
1076                 LPWSTR lpszPort;
1077
1078                 /* [<user>[<:password>]@]<host>[:<port>] */
1079                 /* First find the user and password if they exist */
1080
1081                 lpszHost = strchrW(lpszcp, '@');
1082                 if (lpszHost == NULL || lpszHost > lpszNetLoc)
1083                 {
1084                     /* username and password not specified. */
1085                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1086                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1087                 }
1088                 else /* Parse out username and password */
1089                 {
1090                     LPWSTR lpszUser = lpszcp;
1091                     LPWSTR lpszPasswd = lpszHost;
1092
1093                     while (lpszcp < lpszHost)
1094                     {
1095                         if (*lpszcp == ':')
1096                             lpszPasswd = lpszcp;
1097
1098                         lpszcp++;
1099                     }
1100
1101                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength,
1102                                           lpszUser, lpszPasswd - lpszUser);
1103
1104                     if (lpszPasswd != lpszHost)
1105                         lpszPasswd++;
1106                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
1107                                           lpszPasswd == lpszHost ? NULL : lpszPasswd,
1108                                           lpszHost - lpszPasswd);
1109
1110                     lpszcp++; /* Advance to beginning of host */
1111                 }
1112
1113                 /* Parse <host><:port> */
1114
1115                 lpszHost = lpszcp;
1116                 lpszPort = lpszNetLoc;
1117
1118                 /* special case for res:// URLs: there is no port here, so the host is the
1119                    entire string up to the first '/' */
1120                 if(lpUC->nScheme==INTERNET_SCHEME_RES)
1121                 {
1122                     SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1123                                           lpszHost, lpszPort - lpszHost);
1124                     lpUC->nPort = 0;
1125                     lpszcp=lpszNetLoc;
1126                 }
1127                 else
1128                 {
1129                     while (lpszcp < lpszNetLoc)
1130                     {
1131                         if (*lpszcp == ':')
1132                             lpszPort = lpszcp;
1133
1134                         lpszcp++;
1135                     }
1136
1137                     /* If the scheme is "file" and the host is just one letter, it's not a host */
1138                     if(lpUC->nScheme==INTERNET_SCHEME_FILE && (lpszPort-lpszHost)==1)
1139                     {
1140                         lpszcp=lpszHost;
1141                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1142                                               NULL, 0);
1143                         lpUC->nPort = 0;
1144                     }
1145                     else
1146                     {
1147                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1148                                               lpszHost, lpszPort - lpszHost);
1149                         if (lpszPort != lpszNetLoc)
1150                             lpUC->nPort = atoiW(++lpszPort);
1151                         else
1152                             lpUC->nPort = 0;
1153                     }
1154                 }
1155             }
1156         }
1157     }
1158
1159     /* Here lpszcp points to:
1160      *
1161      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1162      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1163      */
1164     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
1165     {
1166         INT len;
1167
1168         /* Only truncate the parameter list if it's already been saved
1169          * in lpUC->lpszExtraInfo.
1170          */
1171         if (lpszParam && lpUC->dwExtraInfoLength)
1172             len = lpszParam - lpszcp;
1173         else
1174         {
1175             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
1176              * newlines if necessary.
1177              */
1178             LPWSTR lpsznewline = strchrW(lpszcp, '\n');
1179             if (lpsznewline != NULL)
1180                 len = lpsznewline - lpszcp;
1181             else
1182                 len = dwUrlLength-(lpszcp-lpszUrl);
1183         }
1184
1185         if (!SetUrlComponentValueW(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength,
1186                                    lpszcp, len))
1187             return FALSE;
1188     }
1189     else
1190     {
1191         lpUC->dwUrlPathLength = 0;
1192     }
1193
1194     TRACE("%s: host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
1195              debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
1196              debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
1197              debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1198
1199     return TRUE;
1200 }
1201
1202 /***********************************************************************
1203  *           InternetAttemptConnect (WININET.@)
1204  *
1205  * Attempt to make a connection to the internet
1206  *
1207  * RETURNS
1208  *    ERROR_SUCCESS on success
1209  *    Error value   on failure
1210  *
1211  */
1212 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
1213 {
1214     FIXME("Stub\n");
1215     return ERROR_SUCCESS;
1216 }
1217
1218
1219 /***********************************************************************
1220  *           InternetCanonicalizeUrlA (WININET.@)
1221  *
1222  * Escape unsafe characters and spaces
1223  *
1224  * RETURNS
1225  *    TRUE on success
1226  *    FALSE on failure
1227  *
1228  */
1229 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
1230         LPDWORD lpdwBufferLength, DWORD dwFlags)
1231 {
1232     HRESULT hr;
1233     TRACE("%s %p %p %08lx\n",debugstr_a(lpszUrl), lpszBuffer,
1234           lpdwBufferLength, dwFlags);
1235
1236     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1237     dwFlags ^= ICU_NO_ENCODE;
1238
1239     dwFlags |= 0x80000000; /* Don't know what this means */
1240
1241     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1242
1243     return (hr == S_OK) ? TRUE : FALSE;
1244 }
1245
1246 /***********************************************************************
1247  *           InternetCanonicalizeUrlW (WININET.@)
1248  *
1249  * Escape unsafe characters and spaces
1250  *
1251  * RETURNS
1252  *    TRUE on success
1253  *    FALSE on failure
1254  *
1255  */
1256 BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
1257     LPDWORD lpdwBufferLength, DWORD dwFlags)
1258 {
1259     HRESULT hr;
1260     TRACE("%s %p %p %08lx\n", debugstr_w(lpszUrl), lpszBuffer,
1261         lpdwBufferLength, dwFlags);
1262
1263     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1264     dwFlags ^= ICU_NO_ENCODE;
1265
1266     dwFlags |= 0x80000000; /* Don't know what this means */
1267
1268     hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
1269
1270     return (hr == S_OK) ? TRUE : FALSE;
1271 }
1272
1273
1274 /***********************************************************************
1275  *           InternetSetStatusCallbackA (WININET.@)
1276  *
1277  * Sets up a callback function which is called as progress is made
1278  * during an operation.
1279  *
1280  * RETURNS
1281  *    Previous callback or NULL         on success
1282  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
1283  *
1284  */
1285 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
1286         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
1287 {
1288     INTERNET_STATUS_CALLBACK retVal;
1289     LPWININETAPPINFOA lpwai = (LPWININETAPPINFOA)hInternet;
1290
1291     TRACE("0x%08lx\n", (ULONG)hInternet);
1292     if (lpwai->hdr.htype != WH_HINIT)
1293         return INTERNET_INVALID_STATUS_CALLBACK;
1294
1295     retVal = lpwai->lpfnStatusCB;
1296     lpwai->lpfnStatusCB = lpfnIntCB;
1297
1298     return retVal;
1299 }
1300
1301
1302 /***********************************************************************
1303  *           InternetWriteFile (WININET.@)
1304  *
1305  * Write data to an open internet file
1306  *
1307  * RETURNS
1308  *    TRUE  on success
1309  *    FALSE on failure
1310  *
1311  */
1312 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
1313         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
1314 {
1315     BOOL retval = FALSE;
1316     int nSocket = -1;
1317     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1318
1319     TRACE("\n");
1320     if (NULL == lpwh)
1321         return FALSE;
1322
1323     switch (lpwh->htype)
1324     {
1325         case WH_HHTTPREQ:
1326             FIXME("This shouldn't be here! We don't support this kind"
1327                   " of connection anymore. Must use NETCON functions,"
1328                   " especially if using SSL\n");
1329             nSocket = ((LPWININETHTTPREQA)hFile)->netConnection.socketFD;
1330             break;
1331
1332         case WH_HFILE:
1333             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1334             break;
1335
1336         default:
1337             break;
1338     }
1339
1340     if (nSocket != -1)
1341     {
1342         int res = send(nSocket, lpBuffer, dwNumOfBytesToWrite, 0);
1343         retval = (res >= 0);
1344         *lpdwNumOfBytesWritten = retval ? res : 0;
1345     }
1346
1347     return retval;
1348 }
1349
1350
1351 /***********************************************************************
1352  *           InternetReadFile (WININET.@)
1353  *
1354  * Read data from an open internet file
1355  *
1356  * RETURNS
1357  *    TRUE  on success
1358  *    FALSE on failure
1359  *
1360  */
1361 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
1362         DWORD dwNumOfBytesToRead, LPDWORD dwNumOfBytesRead)
1363 {
1364     BOOL retval = FALSE;
1365     int nSocket = -1;
1366     LPWININETHANDLEHEADER lpwh = (LPWININETHANDLEHEADER) hFile;
1367
1368     TRACE("\n");
1369
1370     if (NULL == lpwh)
1371         return FALSE;
1372
1373     /* FIXME: this should use NETCON functions! */
1374     switch (lpwh->htype)
1375     {
1376         case WH_HHTTPREQ:
1377             if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, lpBuffer,
1378                              dwNumOfBytesToRead, 0, (int *)dwNumOfBytesRead))
1379             {
1380                 *dwNumOfBytesRead = 0;
1381                 retval = TRUE; /* Under windows, it seems to return 0 even if nothing was read... */
1382             }
1383             else
1384                 retval = TRUE;
1385             break;
1386
1387         case WH_HFILE:
1388             /* FIXME: FTP should use NETCON_ stuff */
1389             nSocket = ((LPWININETFILE)hFile)->nDataSocket;
1390             if (nSocket != -1)
1391             {
1392                 int res = recv(nSocket, lpBuffer, dwNumOfBytesToRead, 0);
1393                 retval = (res >= 0);
1394                 *dwNumOfBytesRead = retval ? res : 0;
1395             }
1396             break;
1397
1398         default:
1399             break;
1400     }
1401
1402     return retval;
1403 }
1404
1405 /***********************************************************************
1406  *           InternetReadFileExA (WININET.@)
1407  *
1408  * Read data from an open internet file
1409  *
1410  * RETURNS
1411  *    TRUE  on success
1412  *    FALSE on failure
1413  *
1414  */
1415 BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffer,
1416         DWORD dwFlags, DWORD dwContext)
1417 {
1418   FIXME("stub\n");
1419   return FALSE;
1420 }
1421
1422 /***********************************************************************
1423  *           InternetReadFileExW (WININET.@)
1424  *
1425  * Read data from an open internet file
1426  *
1427  * RETURNS
1428  *    TRUE  on success
1429  *    FALSE on failure
1430  *
1431  */
1432 BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
1433         DWORD dwFlags, DWORD dwContext)
1434 {
1435   FIXME("stub\n");
1436
1437   INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1438   return FALSE;
1439 }
1440
1441 /***********************************************************************
1442  *           INET_QueryOptionHelper (internal)
1443  */
1444 static BOOL INET_QueryOptionHelper(BOOL bIsUnicode, HINTERNET hInternet, DWORD dwOption,
1445                                    LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1446 {
1447     LPWININETHANDLEHEADER lpwhh;
1448     BOOL bSuccess = FALSE;
1449
1450     TRACE("(%p, 0x%08lx, %p, %p)\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
1451
1452     if (NULL == hInternet)
1453     {
1454         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1455         return FALSE;
1456     }
1457
1458     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1459
1460     switch (dwOption)
1461     {
1462         case INTERNET_OPTION_HANDLE_TYPE:
1463         {
1464             ULONG type = lpwhh->htype;
1465             TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
1466
1467             if (*lpdwBufferLength < sizeof(ULONG))
1468                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1469             else
1470             {
1471                 memcpy(lpBuffer, &type, sizeof(ULONG));
1472                     *lpdwBufferLength = sizeof(ULONG);
1473                 bSuccess = TRUE;
1474             }
1475             break;
1476         }
1477
1478         case INTERNET_OPTION_REQUEST_FLAGS:
1479         {
1480             ULONG flags = 4;
1481             TRACE("INTERNET_OPTION_REQUEST_FLAGS: %ld\n", flags);
1482             if (*lpdwBufferLength < sizeof(ULONG))
1483                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1484             else
1485             {
1486                 memcpy(lpBuffer, &flags, sizeof(ULONG));
1487                     *lpdwBufferLength = sizeof(ULONG);
1488                 bSuccess = TRUE;
1489             }
1490             break;
1491         }
1492
1493         case INTERNET_OPTION_URL:
1494         case INTERNET_OPTION_DATAFILE_NAME:
1495         {
1496             ULONG type = lpwhh->htype;
1497             if (type == WH_HHTTPREQ)
1498             {
1499                 LPWININETHTTPREQA lpreq = hInternet;
1500                 char url[1023];
1501
1502                 sprintf(url,"http://%s%s",lpreq->lpszHostName,lpreq->lpszPath);
1503                 TRACE("INTERNET_OPTION_URL: %s\n",url);
1504                 if (*lpdwBufferLength < strlen(url)+1)
1505                     INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1506                 else
1507                 {
1508                     if(bIsUnicode)
1509                     {
1510                         *lpdwBufferLength=MultiByteToWideChar(CP_ACP,0,url,-1,lpBuffer,*lpdwBufferLength);
1511                     }
1512                     else
1513                     {
1514                         memcpy(lpBuffer, url, strlen(url)+1);
1515                         *lpdwBufferLength = strlen(url)+1;
1516                     }
1517                     bSuccess = TRUE;
1518                 }
1519             }
1520             break;
1521         }
1522        case INTERNET_OPTION_HTTP_VERSION:
1523        {
1524             /*
1525              * Presently hardcoded to 1.1
1526              */
1527             ((HTTP_VERSION_INFO*)lpBuffer)->dwMajorVersion = 1;
1528             ((HTTP_VERSION_INFO*)lpBuffer)->dwMinorVersion = 1;
1529             bSuccess = TRUE;
1530         break;
1531        }
1532
1533        default:
1534          FIXME("Stub! %ld \n",dwOption);
1535          break;
1536     }
1537
1538     return bSuccess;
1539 }
1540
1541 /***********************************************************************
1542  *           InternetQueryOptionW (WININET.@)
1543  *
1544  * Queries an options on the specified handle
1545  *
1546  * RETURNS
1547  *    TRUE  on success
1548  *    FALSE on failure
1549  *
1550  */
1551 BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
1552                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1553 {
1554     return INET_QueryOptionHelper(TRUE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1555 }
1556
1557 /***********************************************************************
1558  *           InternetQueryOptionA (WININET.@)
1559  *
1560  * Queries an options on the specified handle
1561  *
1562  * RETURNS
1563  *    TRUE  on success
1564  *    FALSE on failure
1565  *
1566  */
1567 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
1568                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1569 {
1570     return INET_QueryOptionHelper(FALSE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
1571 }
1572
1573
1574 /***********************************************************************
1575  *           InternetSetOptionW (WININET.@)
1576  *
1577  * Sets an options on the specified handle
1578  *
1579  * RETURNS
1580  *    TRUE  on success
1581  *    FALSE on failure
1582  *
1583  */
1584 BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
1585                            LPVOID lpBuffer, DWORD dwBufferLength)
1586 {
1587     LPWININETHANDLEHEADER lpwhh;
1588
1589     TRACE("0x%08lx\n", dwOption);
1590
1591     if (NULL == hInternet)
1592     {
1593         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
1594         return FALSE;
1595     }
1596
1597     lpwhh = (LPWININETHANDLEHEADER) hInternet;
1598
1599     switch (dwOption)
1600     {
1601     case INTERNET_OPTION_HTTP_VERSION:
1602       {
1603         HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
1604         FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
1605       }
1606       break;
1607     case INTERNET_OPTION_ERROR_MASK:
1608       {
1609         unsigned long flags=*(unsigned long*)lpBuffer;
1610         FIXME("Option INTERNET_OPTION_ERROR_MASK(%ld): STUB\n",flags);
1611       }
1612       break;
1613     case INTERNET_OPTION_CODEPAGE:
1614       {
1615         unsigned long codepage=*(unsigned long*)lpBuffer;
1616         FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n",codepage);
1617       }
1618       break;
1619     case INTERNET_OPTION_REQUEST_PRIORITY:
1620       {
1621         unsigned long priority=*(unsigned long*)lpBuffer;
1622         FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n",priority);
1623       }
1624       break;
1625     default:
1626         FIXME("Option %ld STUB\n",dwOption);
1627         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
1628         return FALSE;
1629     }
1630
1631     return TRUE;
1632 }
1633
1634
1635 /***********************************************************************
1636  *           InternetSetOptionA (WININET.@)
1637  *
1638  * Sets an options on the specified handle.
1639  *
1640  * RETURNS
1641  *    TRUE  on success
1642  *    FALSE on failure
1643  *
1644  */
1645 BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
1646                            LPVOID lpBuffer, DWORD dwBufferLength)
1647 {
1648     LPVOID wbuffer;
1649     DWORD wlen;
1650     BOOL r;
1651
1652     switch( dwOption )
1653     {
1654     case INTERNET_OPTION_PROXY:
1655         {
1656         LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
1657         LPINTERNET_PROXY_INFOW piw;
1658         DWORD proxlen, prbylen;
1659         LPWSTR prox, prby;
1660
1661         proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
1662         prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
1663         wlen = sizeof(*piw) + proxlen + prbylen;
1664         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1665         piw = (LPINTERNET_PROXY_INFOW) wbuffer;
1666         piw->dwAccessType = pi->dwAccessType;
1667         prox = (LPWSTR) &piw[1];
1668         prby = &prox[proxlen+1];
1669         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
1670         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
1671         piw->lpszProxy = prox;
1672         piw->lpszProxyBypass = prby;
1673         }
1674         break;
1675     case INTERNET_OPTION_USER_AGENT:
1676     case INTERNET_OPTION_USERNAME:
1677     case INTERNET_OPTION_PASSWORD:
1678         wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1679                                    NULL, 0 );
1680         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
1681         MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
1682                                    wbuffer, wlen );
1683         break;
1684     default:
1685         wbuffer = lpBuffer;
1686         wlen = dwBufferLength;
1687     }
1688
1689     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
1690
1691     if( lpBuffer != wbuffer )
1692         HeapFree( GetProcessHeap(), 0, wbuffer );
1693
1694     return r;
1695 }
1696
1697
1698 /***********************************************************************
1699  *           InternetSetOptionExA (WININET.@)
1700  */
1701 BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
1702                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1703 {
1704     FIXME("Flags %08lx ignored\n", dwFlags);
1705     return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
1706 }
1707
1708 /***********************************************************************
1709  *           InternetSetOptionExW (WININET.@)
1710  */
1711 BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
1712                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
1713 {
1714     FIXME("Flags %08lx ignored\n", dwFlags);
1715     if( dwFlags & ~ISO_VALID_FLAGS )
1716     {
1717         SetLastError( ERROR_INVALID_PARAMETER );
1718         return FALSE;
1719     }
1720     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
1721 }
1722
1723
1724 /***********************************************************************
1725  *      InternetCheckConnectionA (WININET.@)
1726  *
1727  * Pings a requested host to check internet connection
1728  *
1729  * RETURNS
1730  *   TRUE on success and FALSE on failure. If a failure then
1731  *   ERROR_NOT_CONNECTED is placesd into GetLastError
1732  *
1733  */
1734 BOOL WINAPI InternetCheckConnectionA( LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
1735 {
1736 /*
1737  * this is a kludge which runs the resident ping program and reads the output.
1738  *
1739  * Anyone have a better idea?
1740  */
1741
1742   BOOL   rc = FALSE;
1743   char command[1024];
1744   char host[1024];
1745   int status = -1;
1746
1747   FIXME("\n");
1748
1749   /*
1750    * Crack or set the Address
1751    */
1752   if (lpszUrl == NULL)
1753   {
1754      /*
1755       * According to the doc we are supost to use the ip for the next
1756       * server in the WnInet internal server database. I have
1757       * no idea what that is or how to get it.
1758       *
1759       * So someone needs to implement this.
1760       */
1761      FIXME("Unimplemented with URL of NULL\n");
1762      return TRUE;
1763   }
1764   else
1765   {
1766      URL_COMPONENTSA componets;
1767
1768      ZeroMemory(&componets,sizeof(URL_COMPONENTSA));
1769      componets.lpszHostName = (LPSTR)&host;
1770      componets.dwHostNameLength = 1024;
1771
1772      if (!InternetCrackUrlA(lpszUrl,0,0,&componets))
1773        goto End;
1774
1775      TRACE("host name : %s\n",componets.lpszHostName);
1776   }
1777
1778   /*
1779    * Build our ping command
1780    */
1781   strcpy(command,"ping -w 1 ");
1782   strcat(command,host);
1783   strcat(command," >/dev/null 2>/dev/null");
1784
1785   TRACE("Ping command is : %s\n",command);
1786
1787   status = system(command);
1788
1789   TRACE("Ping returned a code of %i \n",status);
1790
1791   /* Ping return code of 0 indicates success */
1792   if (status == 0)
1793      rc = TRUE;
1794
1795 End:
1796
1797   if (rc == FALSE)
1798     SetLastError(ERROR_NOT_CONNECTED);
1799
1800   return rc;
1801 }
1802
1803
1804 /***********************************************************************
1805  *      InternetCheckConnectionW (WININET.@)
1806  *
1807  * Pings a requested host to check internet connection
1808  *
1809  * RETURNS
1810  *   TRUE on success and FALSE on failure. If a failure then
1811  *   ERROR_NOT_CONNECTED is placed into GetLastError
1812  *
1813  */
1814 BOOL WINAPI InternetCheckConnectionW(LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
1815 {
1816     CHAR *szUrl;
1817     INT len;
1818     BOOL rc;
1819
1820     len = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1821     if (!(szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
1822         return FALSE;
1823     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, szUrl, len, NULL, NULL);
1824     rc = InternetCheckConnectionA((LPCSTR)szUrl, dwFlags, dwReserved);
1825     HeapFree(GetProcessHeap(), 0, szUrl);
1826     
1827     return rc;
1828 }
1829
1830
1831 /**********************************************************
1832  *      InternetOpenUrlA (WININET.@)
1833  *
1834  * Opens an URL
1835  *
1836  * RETURNS
1837  *   handle of connection or NULL on failure
1838  */
1839 HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
1840     LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1841 {
1842   URL_COMPONENTSA urlComponents;
1843   char protocol[32], hostName[MAXHOSTNAME], userName[1024];
1844   char password[1024], path[2048], extra[1024];
1845   HINTERNET client = NULL, client1 = NULL;
1846
1847   TRACE("(%p, %s, %s, %08lx, %08lx, %08lx\n", hInternet, debugstr_a(lpszUrl), debugstr_a(lpszHeaders),
1848        dwHeadersLength, dwFlags, dwContext);
1849
1850   urlComponents.dwStructSize = sizeof(URL_COMPONENTSA);
1851   urlComponents.lpszScheme = protocol;
1852   urlComponents.dwSchemeLength = 32;
1853   urlComponents.lpszHostName = hostName;
1854   urlComponents.dwHostNameLength = MAXHOSTNAME;
1855   urlComponents.lpszUserName = userName;
1856   urlComponents.dwUserNameLength = 1024;
1857   urlComponents.lpszPassword = password;
1858   urlComponents.dwPasswordLength = 1024;
1859   urlComponents.lpszUrlPath = path;
1860   urlComponents.dwUrlPathLength = 2048;
1861   urlComponents.lpszExtraInfo = extra;
1862   urlComponents.dwExtraInfoLength = 1024;
1863   if(!InternetCrackUrlA(lpszUrl, strlen(lpszUrl), 0, &urlComponents))
1864     return NULL;
1865   switch(urlComponents.nScheme) {
1866   case INTERNET_SCHEME_FTP:
1867     if(urlComponents.nPort == 0)
1868       urlComponents.nPort = INTERNET_DEFAULT_FTP_PORT;
1869     client = InternetConnectA(hInternet, hostName, urlComponents.nPort,
1870         userName, password, INTERNET_SERVICE_FTP, dwFlags, dwContext);
1871     return FtpOpenFileA(client, path, GENERIC_READ, dwFlags, dwContext);
1872   case INTERNET_SCHEME_HTTP:
1873   case INTERNET_SCHEME_HTTPS:
1874   {
1875     LPCSTR accept[2] = { "*/*", NULL };
1876     if(urlComponents.nPort == 0) {
1877       if(urlComponents.nScheme == INTERNET_SCHEME_HTTP)
1878         urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
1879       else
1880         urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
1881     }
1882     client = InternetConnectA(hInternet, hostName, urlComponents.nPort, userName,
1883         password, INTERNET_SERVICE_HTTP, dwFlags, dwContext);
1884     if(client == NULL)
1885       return NULL;
1886     client1 = HttpOpenRequestA(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
1887     if(client1 == NULL) {
1888       InternetCloseHandle(client);
1889       return NULL;
1890     }
1891     HttpAddRequestHeadersA(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
1892     if(!HttpSendRequestA(client1, NULL, 0, NULL, 0)) {
1893       InternetCloseHandle(client1);
1894       InternetCloseHandle(client);
1895       return NULL;
1896     }
1897     return client1;
1898   }
1899   case INTERNET_SCHEME_GOPHER:
1900     /* gopher doesn't seem to be implemented in wine, but it's supposed
1901      * to be supported by InternetOpenUrlA. */
1902   default:
1903     return NULL;
1904   }
1905 }
1906
1907
1908 /**********************************************************
1909  *      InternetOpenUrlW (WININET.@)
1910  *
1911  * Opens an URL
1912  *
1913  * RETURNS
1914  *   handle of connection or NULL on failure
1915  */
1916 HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
1917     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
1918 {
1919     HINTERNET rc = (HINTERNET)NULL;
1920
1921     INT lenUrl = WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, NULL, 0, NULL, NULL);
1922     INT lenHeaders = WideCharToMultiByte(CP_ACP, 0, lpszHeaders, -1, NULL, 0, NULL, NULL);
1923     CHAR *szUrl = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenUrl*sizeof(CHAR));
1924     CHAR *szHeaders = (CHAR *)HeapAlloc(GetProcessHeap(), 0, lenHeaders*sizeof(CHAR));
1925
1926     if (!szUrl || !szHeaders)
1927     {
1928         if (szUrl)
1929             HeapFree(GetProcessHeap(), 0, szUrl);
1930         if (szHeaders)
1931             HeapFree(GetProcessHeap(), 0, szHeaders);
1932         return (HINTERNET)NULL;
1933     }
1934
1935     WideCharToMultiByte(CP_ACP, 0, lpszUrl, -1, szUrl, lenUrl,
1936         NULL, NULL);
1937     WideCharToMultiByte(CP_ACP, 0, lpszHeaders, -1, szHeaders, lenHeaders,
1938         NULL, NULL);
1939
1940     rc = InternetOpenUrlA(hInternet, szUrl, szHeaders,
1941         dwHeadersLength, dwFlags, dwContext);
1942
1943     HeapFree(GetProcessHeap(), 0, szUrl);
1944     HeapFree(GetProcessHeap(), 0, szHeaders);
1945
1946     return rc;
1947 }
1948
1949
1950 /***********************************************************************
1951  *           INTERNET_SetLastError (internal)
1952  *
1953  * Set last thread specific error
1954  *
1955  * RETURNS
1956  *
1957  */
1958 void INTERNET_SetLastError(DWORD dwError)
1959 {
1960     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1961
1962     SetLastError(dwError);
1963     if(lpwite)
1964         lpwite->dwError = dwError;
1965 }
1966
1967
1968 /***********************************************************************
1969  *           INTERNET_GetLastError (internal)
1970  *
1971  * Get last thread specific error
1972  *
1973  * RETURNS
1974  *
1975  */
1976 DWORD INTERNET_GetLastError()
1977 {
1978     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
1979     return lpwite->dwError;
1980 }
1981
1982
1983 /***********************************************************************
1984  *           INTERNET_WorkerThreadFunc (internal)
1985  *
1986  * Worker thread execution function
1987  *
1988  * RETURNS
1989  *
1990  */
1991 DWORD INTERNET_WorkerThreadFunc(LPVOID *lpvParam)
1992 {
1993     DWORD dwWaitRes;
1994
1995     while (1)
1996     {
1997         if(dwNumJobs > 0) {
1998             INTERNET_ExecuteWork();
1999             continue;
2000         }
2001         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
2002
2003         if (dwWaitRes == WAIT_OBJECT_0 + 1)
2004             INTERNET_ExecuteWork();
2005         else
2006             break;
2007
2008         InterlockedIncrement(&dwNumIdleThreads);
2009     }
2010
2011     InterlockedDecrement(&dwNumIdleThreads);
2012     InterlockedDecrement(&dwNumThreads);
2013     TRACE("Worker thread exiting\n");
2014     return TRUE;
2015 }
2016
2017
2018 /***********************************************************************
2019  *           INTERNET_InsertWorkRequest (internal)
2020  *
2021  * Insert work request into queue
2022  *
2023  * RETURNS
2024  *
2025  */
2026 BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
2027 {
2028     BOOL bSuccess = FALSE;
2029     LPWORKREQUEST lpNewRequest;
2030
2031     TRACE("\n");
2032
2033     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
2034     if (lpNewRequest)
2035     {
2036         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
2037         lpNewRequest->prev = NULL;
2038
2039         EnterCriticalSection(&csQueue);
2040
2041         lpNewRequest->next = lpWorkQueueTail;
2042         if (lpWorkQueueTail)
2043             lpWorkQueueTail->prev = lpNewRequest;
2044         lpWorkQueueTail = lpNewRequest;
2045         if (!lpHeadWorkQueue)
2046             lpHeadWorkQueue = lpWorkQueueTail;
2047
2048         LeaveCriticalSection(&csQueue);
2049
2050         bSuccess = TRUE;
2051         InterlockedIncrement(&dwNumJobs);
2052     }
2053
2054     return bSuccess;
2055 }
2056
2057
2058 /***********************************************************************
2059  *           INTERNET_GetWorkRequest (internal)
2060  *
2061  * Retrieves work request from queue
2062  *
2063  * RETURNS
2064  *
2065  */
2066 BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
2067 {
2068     BOOL bSuccess = FALSE;
2069     LPWORKREQUEST lpRequest = NULL;
2070
2071     TRACE("\n");
2072
2073     EnterCriticalSection(&csQueue);
2074
2075     if (lpHeadWorkQueue)
2076     {
2077         lpRequest = lpHeadWorkQueue;
2078         lpHeadWorkQueue = lpHeadWorkQueue->prev;
2079         if (lpRequest == lpWorkQueueTail)
2080             lpWorkQueueTail = lpHeadWorkQueue;
2081     }
2082
2083     LeaveCriticalSection(&csQueue);
2084
2085     if (lpRequest)
2086     {
2087         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
2088         HeapFree(GetProcessHeap(), 0, lpRequest);
2089         bSuccess = TRUE;
2090         InterlockedDecrement(&dwNumJobs);
2091     }
2092
2093     return bSuccess;
2094 }
2095
2096
2097 /***********************************************************************
2098  *           INTERNET_AsyncCall (internal)
2099  *
2100  * Retrieves work request from queue
2101  *
2102  * RETURNS
2103  *
2104  */
2105 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
2106 {
2107     HANDLE hThread;
2108     DWORD dwTID;
2109     BOOL bSuccess = FALSE;
2110
2111     TRACE("\n");
2112
2113     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
2114     {
2115         InterlockedIncrement(&dwNumIdleThreads);
2116
2117         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS ||
2118             !(hThread = CreateThread(NULL, 0,
2119             (LPTHREAD_START_ROUTINE)INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
2120         {
2121             InterlockedDecrement(&dwNumThreads);
2122             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
2123             goto lerror;
2124         }
2125
2126         TRACE("Created new thread\n");
2127     }
2128
2129     bSuccess = TRUE;
2130     INTERNET_InsertWorkRequest(lpWorkRequest);
2131     SetEvent(hWorkEvent);
2132
2133 lerror:
2134
2135     return bSuccess;
2136 }
2137
2138
2139 /***********************************************************************
2140  *           INTERNET_ExecuteWork (internal)
2141  *
2142  * RETURNS
2143  *
2144  */
2145 VOID INTERNET_ExecuteWork()
2146 {
2147     WORKREQUEST workRequest;
2148
2149     TRACE("\n");
2150
2151     if (!INTERNET_GetWorkRequest(&workRequest))
2152         return;
2153     TRACE("Got work %d\n", workRequest.asyncall);
2154     switch (workRequest.asyncall)
2155     {
2156     case FTPPUTFILEA:
2157         {
2158         struct WORKREQ_FTPPUTFILEA *req = &workRequest.u.FtpPutFileA;
2159
2160         FTP_FtpPutFileA(workRequest.handle, req->lpszLocalFile,
2161                    req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
2162
2163         HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
2164         HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
2165         }
2166         break;
2167
2168     case FTPSETCURRENTDIRECTORYA:
2169         {
2170         struct WORKREQ_FTPSETCURRENTDIRECTORYA *req;
2171
2172         req = &workRequest.u.FtpSetCurrentDirectoryA;
2173         FTP_FtpSetCurrentDirectoryA(workRequest.handle, req->lpszDirectory);
2174         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2175         }
2176         break;
2177
2178     case FTPCREATEDIRECTORYA:
2179         {
2180         struct WORKREQ_FTPCREATEDIRECTORYA *req;
2181
2182         req = &workRequest.u.FtpCreateDirectoryA;
2183         FTP_FtpCreateDirectoryA(workRequest.handle, req->lpszDirectory);
2184         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2185         }
2186         break;
2187
2188     case FTPFINDFIRSTFILEA:
2189         {
2190         struct WORKREQ_FTPFINDFIRSTFILEA *req;
2191
2192         req = &workRequest.u.FtpFindFirstFileA;
2193         FTP_FtpFindFirstFileA(workRequest.handle, req->lpszSearchFile,
2194            req->lpFindFileData, req->dwFlags, req->dwContext);
2195         HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
2196         }
2197         break;
2198
2199     case FTPGETCURRENTDIRECTORYA:
2200         {
2201         struct WORKREQ_FTPGETCURRENTDIRECTORYA *req;
2202
2203         req = &workRequest.u.FtpGetCurrentDirectoryA;
2204         FTP_FtpGetCurrentDirectoryA(workRequest.handle,
2205                 req->lpszDirectory, req->lpdwDirectory);
2206         }
2207         break;
2208
2209     case FTPOPENFILEA:
2210         {
2211         struct WORKREQ_FTPOPENFILEA *req = &workRequest.u.FtpOpenFileA;
2212
2213         FTP_FtpOpenFileA(workRequest.handle, req->lpszFilename,
2214             req->dwAccess, req->dwFlags, req->dwContext);
2215         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
2216         }
2217         break;
2218
2219     case FTPGETFILEA:
2220         {
2221         struct WORKREQ_FTPGETFILEA *req = &workRequest.u.FtpGetFileA;
2222
2223         FTP_FtpGetFileA(workRequest.handle, req->lpszRemoteFile,
2224                  req->lpszNewFile, req->fFailIfExists,
2225                  req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
2226         HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
2227         HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
2228         }
2229         break;
2230
2231     case FTPDELETEFILEA:
2232         {
2233         struct WORKREQ_FTPDELETEFILEA *req = &workRequest.u.FtpDeleteFileA;
2234
2235         FTP_FtpDeleteFileA(workRequest.handle, req->lpszFilename);
2236         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
2237         }
2238         break;
2239
2240     case FTPREMOVEDIRECTORYA:
2241         {
2242         struct WORKREQ_FTPREMOVEDIRECTORYA *req;
2243
2244         req = &workRequest.u.FtpRemoveDirectoryA;
2245         FTP_FtpRemoveDirectoryA(workRequest.handle, req->lpszDirectory);
2246         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
2247         }
2248         break;
2249
2250     case FTPRENAMEFILEA:
2251         {
2252         struct WORKREQ_FTPRENAMEFILEA *req = &workRequest.u.FtpRenameFileA;
2253
2254         FTP_FtpRenameFileA(workRequest.handle, req->lpszSrcFile, req->lpszDestFile);
2255         HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
2256         HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
2257         }
2258         break;
2259
2260     case INTERNETFINDNEXTA:
2261         {
2262         struct WORKREQ_INTERNETFINDNEXTA *req;
2263
2264         req = &workRequest.u.InternetFindNextA;
2265         INTERNET_FindNextFileA(workRequest.handle, req->lpFindFileData);
2266         }
2267         break;
2268
2269     case HTTPSENDREQUESTA:
2270         {
2271         struct WORKREQ_HTTPSENDREQUESTA *req = &workRequest.u.HttpSendRequestA;
2272
2273         HTTP_HttpSendRequestA(workRequest.handle, req->lpszHeader,
2274                 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength);
2275
2276         HeapFree(GetProcessHeap(), 0, req->lpszHeader);
2277         }
2278         break;
2279
2280     case HTTPOPENREQUESTA:
2281         {
2282         struct WORKREQ_HTTPOPENREQUESTA *req = &workRequest.u.HttpOpenRequestA;
2283
2284         HTTP_HttpOpenRequestA(workRequest.handle, req->lpszVerb,
2285             req->lpszObjectName, req->lpszVersion, req->lpszReferrer,
2286             req->lpszAcceptTypes, req->dwFlags, req->dwContext);
2287
2288         HeapFree(GetProcessHeap(), 0, req->lpszVerb);
2289         HeapFree(GetProcessHeap(), 0, req->lpszObjectName);
2290         HeapFree(GetProcessHeap(), 0, req->lpszVersion);
2291         HeapFree(GetProcessHeap(), 0, req->lpszReferrer);
2292         }
2293         break;
2294
2295     case SENDCALLBACK:
2296         {
2297         struct WORKREQ_SENDCALLBACK *req = &workRequest.u.SendCallback;
2298
2299         SendAsyncCallbackInt(workRequest.handle, req->hHttpSession,
2300                 req->dwContext, req->dwInternetStatus, req->lpvStatusInfo,
2301                 req->dwStatusInfoLength);
2302         }
2303         break;
2304     }
2305 }
2306
2307
2308 /***********************************************************************
2309  *          INTERNET_GetResponseBuffer
2310  *
2311  * RETURNS
2312  *
2313  */
2314 LPSTR INTERNET_GetResponseBuffer()
2315 {
2316     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
2317     TRACE("\n");
2318     return lpwite->response;
2319 }
2320
2321 /***********************************************************************
2322  *           INTERNET_GetNextLine  (internal)
2323  *
2324  * Parse next line in directory string listing
2325  *
2326  * RETURNS
2327  *   Pointer to beginning of next line
2328  *   NULL on failure
2329  *
2330  */
2331
2332 LPSTR INTERNET_GetNextLine(INT nSocket, LPSTR lpszBuffer, LPDWORD dwBuffer)
2333 {
2334     struct timeval tv;
2335     fd_set infd;
2336     BOOL bSuccess = FALSE;
2337     INT nRecv = 0;
2338
2339     TRACE("\n");
2340
2341     FD_ZERO(&infd);
2342     FD_SET(nSocket, &infd);
2343     tv.tv_sec=RESPONSE_TIMEOUT;
2344     tv.tv_usec=0;
2345
2346     while (nRecv < *dwBuffer)
2347     {
2348         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
2349         {
2350             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
2351             {
2352                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
2353                 goto lend;
2354             }
2355
2356             if (lpszBuffer[nRecv] == '\n')
2357             {
2358                 bSuccess = TRUE;
2359                 break;
2360             }
2361             if (lpszBuffer[nRecv] != '\r')
2362                 nRecv++;
2363         }
2364         else
2365         {
2366             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
2367             goto lend;
2368         }
2369     }
2370
2371 lend:
2372     if (bSuccess)
2373     {
2374         lpszBuffer[nRecv] = '\0';
2375         *dwBuffer = nRecv - 1;
2376         TRACE(":%d %s\n", nRecv, lpszBuffer);
2377         return lpszBuffer;
2378     }
2379     else
2380     {
2381         return NULL;
2382     }
2383 }
2384
2385 /***********************************************************************
2386  *
2387  */
2388 BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
2389                                 LPDWORD lpdwNumberOfBytesAvailble,
2390                                 DWORD dwFlags, DWORD dwConext)
2391 {
2392     LPWININETHTTPREQA lpwhr = (LPWININETHTTPREQA) hFile;
2393     INT retval = -1;
2394     char buffer[4048];
2395
2396
2397     if (NULL == lpwhr)
2398     {
2399         SetLastError(ERROR_NO_MORE_FILES);
2400         return FALSE;
2401     }
2402
2403     TRACE("-->  %p %i\n",lpwhr,lpwhr->hdr.htype);
2404
2405     switch (lpwhr->hdr.htype)
2406     {
2407     case WH_HHTTPREQ:
2408         if (!NETCON_recv(&((LPWININETHTTPREQA)hFile)->netConnection, buffer,
2409                          4048, MSG_PEEK, (int *)lpdwNumberOfBytesAvailble))
2410         {
2411             SetLastError(ERROR_NO_MORE_FILES);
2412             retval = FALSE;
2413         }
2414         else
2415             retval = TRUE;
2416         break;
2417
2418     default:
2419         FIXME("unsupported file type\n");
2420         break;
2421     }
2422
2423     TRACE("<-- %i\n",retval);
2424     return (retval+1);
2425 }
2426
2427
2428 /***********************************************************************
2429  *
2430  */
2431 BOOL WINAPI InternetLockRequestFile( HINTERNET hInternet, HANDLE
2432 *lphLockReqHandle)
2433 {
2434     FIXME("STUB\n");
2435     return FALSE;
2436 }
2437
2438 BOOL WINAPI InternetUnlockRequestFile( HANDLE hLockHandle)
2439 {
2440     FIXME("STUB\n");
2441     return FALSE;
2442 }
2443
2444
2445 /***********************************************************************
2446  *           InternetAutodial
2447  *
2448  * On windows this function is supposed to dial the default internet
2449  * connection. We don't want to have Wine dial out to the internet so
2450  * we return TRUE by default. It might be nice to check if we are connected.
2451  *
2452  * RETURNS
2453  *   TRUE on success
2454  *   FALSE on failure
2455  *
2456  */
2457 BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
2458 {
2459     FIXME("STUB\n");
2460
2461     /* Tell that we are connected to the internet. */
2462     return TRUE;
2463 }
2464
2465 /***********************************************************************
2466  *           InternetAutodialHangup
2467  *
2468  * Hangs up an connection made with InternetAutodial
2469  *
2470  * PARAM
2471  *    dwReserved
2472  * RETURNS
2473  *   TRUE on success
2474  *   FALSE on failure
2475  *
2476  */
2477 BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
2478 {
2479     FIXME("STUB\n");
2480
2481     /* we didn't dial, we don't disconnect */
2482     return TRUE;
2483 }
2484
2485 /***********************************************************************
2486  *
2487  *         InternetCombineUrlA
2488  *
2489  * Combine a base URL with a relative URL
2490  *
2491  * RETURNS
2492  *   TRUE on success
2493  *   FALSE on failure
2494  *
2495  */
2496
2497 BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
2498                                 LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
2499                                 DWORD dwFlags)
2500 {
2501     HRESULT hr=S_OK;
2502     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2503     dwFlags ^= ICU_NO_ENCODE;
2504     hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2505
2506     return (hr==S_OK);
2507 }
2508
2509 /***********************************************************************
2510  *
2511  *         InternetCombineUrlW
2512  *
2513  * Combine a base URL with a relative URL
2514  *
2515  * RETURNS
2516  *   TRUE on success
2517  *   FALSE on failure
2518  *
2519  */
2520
2521 BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
2522                                 LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
2523                                 DWORD dwFlags)
2524 {
2525     HRESULT hr=S_OK;
2526     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
2527     dwFlags ^= ICU_NO_ENCODE;
2528     hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
2529
2530     return (hr==S_OK);
2531 }
2532
2533 /***********************************************************************
2534  *
2535  *         InternetCreateUrlA
2536  *
2537  * RETURNS
2538  *   TRUE on success
2539  *   FALSE on failure
2540  *
2541  */
2542 BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
2543                                LPSTR lpszUrl, LPDWORD lpdwUrlLength)
2544 {
2545     FIXME("\n");
2546     return FALSE;
2547 }
2548
2549 /***********************************************************************
2550  *
2551  *         InternetCreateUrlW
2552  *
2553  * RETURNS
2554  *   TRUE on success
2555  *   FALSE on failure
2556  *
2557  */
2558 BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
2559                                LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
2560 {
2561     FIXME("\n");
2562     return FALSE;
2563 }