wininet: HTTP headers reworking.
[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  * Copyright 2004 Mike McCormack for CodeWeavers
9  *
10  * Ulrich Czekalla
11  * Aric Stewart
12  * David Hammerton
13  *
14  * This library is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU Lesser General Public
16  * License as published by the Free Software Foundation; either
17  * version 2.1 of the License, or (at your option) any later version.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27  */
28
29 #include "config.h"
30 #include "wine/port.h"
31
32 #define MAXHOSTNAME 100 /* from http.c */
33
34 #include <string.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <sys/types.h>
38 #ifdef HAVE_SYS_SOCKET_H
39 # include <sys/socket.h>
40 #endif
41 #ifdef HAVE_SYS_TIME_H
42 # include <sys/time.h>
43 #endif
44 #include <stdlib.h>
45 #include <ctype.h>
46 #ifdef HAVE_UNISTD_H
47 # include <unistd.h>
48 #endif
49 #include <assert.h>
50
51 #include "windef.h"
52 #include "winbase.h"
53 #include "winreg.h"
54 #include "winuser.h"
55 #include "wininet.h"
56 #include "winnls.h"
57 #include "wine/debug.h"
58 #include "winerror.h"
59 #define NO_SHLWAPI_STREAM
60 #include "shlwapi.h"
61
62 #include "wine/exception.h"
63 #include "excpt.h"
64
65 #include "internet.h"
66 #include "resource.h"
67
68 #include "wine/unicode.h"
69
70 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
71
72 #define MAX_IDLE_WORKER 1000*60*1
73 #define MAX_WORKER_THREADS 10
74 #define RESPONSE_TIMEOUT        30
75
76 #define GET_HWININET_FROM_LPWININETFINDNEXT(lpwh) \
77 (LPWININETAPPINFOW)(((LPWININETFTPSESSIONW)(lpwh->hdr.lpwhparent))->hdr.lpwhparent)
78
79
80 typedef struct
81 {
82     DWORD  dwError;
83     CHAR   response[MAX_REPLY_LEN];
84 } WITHREADERROR, *LPWITHREADERROR;
85
86 static VOID INTERNET_CloseHandle(LPWININETHANDLEHEADER hdr);
87 BOOL WINAPI INTERNET_FindNextFileW(LPWININETFINDNEXTW lpwh, LPVOID lpvFindData);
88 HINTERNET WINAPI INTERNET_InternetOpenUrlW(LPWININETAPPINFOW hIC, LPCWSTR lpszUrl,
89               LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext);
90 static VOID INTERNET_ExecuteWork(void);
91
92 static DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
93 static LONG dwNumThreads;
94 static LONG dwNumIdleThreads;
95 static LONG dwNumJobs;
96 static HANDLE hEventArray[2];
97 #define hQuitEvent hEventArray[0]
98 #define hWorkEvent hEventArray[1]
99 static CRITICAL_SECTION csQueue;
100 static LPWORKREQUEST lpHeadWorkQueue;
101 static LPWORKREQUEST lpWorkQueueTail;
102 static HMODULE WININET_hModule;
103
104 #define HANDLE_CHUNK_SIZE 0x10
105
106 static CRITICAL_SECTION WININET_cs;
107 static CRITICAL_SECTION_DEBUG WININET_cs_debug = 
108 {
109     0, 0, &WININET_cs,
110     { &WININET_cs_debug.ProcessLocksList, &WININET_cs_debug.ProcessLocksList },
111       0, 0, { (DWORD_PTR)(__FILE__ ": WININET_cs") }
112 };
113 static CRITICAL_SECTION WININET_cs = { &WININET_cs_debug, -1, 0, 0, 0, 0 };
114
115 static LPWININETHANDLEHEADER *WININET_Handles;
116 static UINT WININET_dwNextHandle;
117 static UINT WININET_dwMaxHandles;
118
119 HINTERNET WININET_AllocHandle( LPWININETHANDLEHEADER info )
120 {
121     LPWININETHANDLEHEADER *p;
122     UINT handle = 0, num;
123
124     EnterCriticalSection( &WININET_cs );
125     if( !WININET_dwMaxHandles )
126     {
127         num = HANDLE_CHUNK_SIZE;
128         p = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
129                    sizeof (UINT)* num);
130         if( !p )
131             goto end;
132         WININET_Handles = p;
133         WININET_dwMaxHandles = num;
134     }
135     if( WININET_dwMaxHandles == WININET_dwNextHandle )
136     {
137         num = WININET_dwMaxHandles + HANDLE_CHUNK_SIZE;
138         p = HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
139                    WININET_Handles, sizeof (UINT)* num);
140         if( !p )
141             goto end;
142         WININET_Handles = p;
143         WININET_dwMaxHandles = num;
144     }
145
146     handle = WININET_dwNextHandle;
147     if( WININET_Handles[handle] )
148         ERR("handle isn't free but should be\n");
149     WININET_Handles[handle] = WININET_AddRef( info );
150
151     while( WININET_Handles[WININET_dwNextHandle] && 
152            (WININET_dwNextHandle < WININET_dwMaxHandles ) )
153         WININET_dwNextHandle++;
154     
155 end:
156     LeaveCriticalSection( &WININET_cs );
157
158     return (HINTERNET) (handle+1);
159 }
160
161 HINTERNET WININET_FindHandle( LPWININETHANDLEHEADER info )
162 {
163     UINT i, handle = 0;
164
165     EnterCriticalSection( &WININET_cs );
166     for( i=0; i<WININET_dwMaxHandles; i++ )
167     {
168         if( info == WININET_Handles[i] )
169         {
170             WININET_AddRef( info );
171             handle = i+1;
172             break;
173         }
174     }
175     LeaveCriticalSection( &WININET_cs );
176
177     return (HINTERNET) handle;
178 }
179
180 LPWININETHANDLEHEADER WININET_AddRef( LPWININETHANDLEHEADER info )
181 {
182     info->dwRefCount++;
183     TRACE("%p -> refcount = %ld\n", info, info->dwRefCount );
184     return info;
185 }
186
187 LPWININETHANDLEHEADER WININET_GetObject( HINTERNET hinternet )
188 {
189     LPWININETHANDLEHEADER info = NULL;
190     UINT handle = (UINT) hinternet;
191
192     EnterCriticalSection( &WININET_cs );
193
194     if( (handle > 0) && ( handle <= WININET_dwMaxHandles ) && 
195         WININET_Handles[handle-1] )
196         info = WININET_AddRef( WININET_Handles[handle-1] );
197
198     LeaveCriticalSection( &WININET_cs );
199
200     TRACE("handle %d -> %p\n", handle, info);
201
202     return info;
203 }
204
205 BOOL WININET_Release( LPWININETHANDLEHEADER info )
206 {
207     info->dwRefCount--;
208     TRACE( "object %p refcount = %ld\n", info, info->dwRefCount );
209     if( !info->dwRefCount )
210     {
211         TRACE( "destroying object %p\n", info);
212         info->destroy( info );
213     }
214     return TRUE;
215 }
216
217 BOOL WININET_FreeHandle( HINTERNET hinternet )
218 {
219     BOOL ret = FALSE;
220     UINT handle = (UINT) hinternet;
221     LPWININETHANDLEHEADER info = NULL;
222
223     EnterCriticalSection( &WININET_cs );
224
225     if( (handle > 0) && ( handle <= WININET_dwMaxHandles ) )
226     {
227         handle--;
228         if( WININET_Handles[handle] )
229         {
230             info = WININET_Handles[handle];
231             TRACE( "destroying handle %d for object %p\n", handle+1, info);
232             WININET_Handles[handle] = NULL;
233             ret = TRUE;
234             if( WININET_dwNextHandle > handle )
235                 WININET_dwNextHandle = handle;
236         }
237     }
238
239     LeaveCriticalSection( &WININET_cs );
240
241     if( info )
242         WININET_Release( info );
243
244     return ret;
245 }
246
247 /***********************************************************************
248  * DllMain [Internal] Initializes the internal 'WININET.DLL'.
249  *
250  * PARAMS
251  *     hinstDLL    [I] handle to the DLL's instance
252  *     fdwReason   [I]
253  *     lpvReserved [I] reserved, must be NULL
254  *
255  * RETURNS
256  *     Success: TRUE
257  *     Failure: FALSE
258  */
259
260 BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
261 {
262     TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
263
264     switch (fdwReason) {
265         case DLL_PROCESS_ATTACH:
266
267             g_dwTlsErrIndex = TlsAlloc();
268
269             if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
270                 return FALSE;
271
272             hQuitEvent = CreateEventW(0, TRUE, FALSE, NULL);
273             hWorkEvent = CreateEventW(0, FALSE, FALSE, NULL);
274             InitializeCriticalSection(&csQueue);
275
276             URLCacheContainers_CreateDefaults();
277
278             dwNumThreads = 0;
279             dwNumIdleThreads = 0;
280             dwNumJobs = 0;
281
282             WININET_hModule = (HMODULE)hinstDLL;
283
284         case DLL_THREAD_ATTACH:
285             {
286                 LPWITHREADERROR lpwite = HeapAlloc(GetProcessHeap(), 0, sizeof(WITHREADERROR));
287                 if (NULL == lpwite)
288                     return FALSE;
289
290                 TlsSetValue(g_dwTlsErrIndex, (LPVOID)lpwite);
291             }
292             break;
293
294         case DLL_THREAD_DETACH:
295             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
296                         {
297                                 LPVOID lpwite = TlsGetValue(g_dwTlsErrIndex);
298                                 HeapFree(GetProcessHeap(), 0, lpwite);
299                         }
300             break;
301
302         case DLL_PROCESS_DETACH:
303
304             URLCacheContainers_DeleteAll();
305
306             if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
307             {
308                 HeapFree(GetProcessHeap(), 0, TlsGetValue(g_dwTlsErrIndex));
309                 TlsFree(g_dwTlsErrIndex);
310             }
311
312             SetEvent(hQuitEvent);
313
314             CloseHandle(hQuitEvent);
315             CloseHandle(hWorkEvent);
316             DeleteCriticalSection(&csQueue);
317             break;
318     }
319
320     return TRUE;
321 }
322
323
324 /***********************************************************************
325  *           InternetInitializeAutoProxyDll   (WININET.@)
326  *
327  * Setup the internal proxy
328  *
329  * PARAMETERS
330  *     dwReserved
331  *
332  * RETURNS
333  *     FALSE on failure
334  *
335  */
336 BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
337 {
338     FIXME("STUB\n");
339     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
340     return FALSE;
341 }
342
343 /***********************************************************************
344  *           DetectAutoProxyUrl   (WININET.@)
345  *
346  * Auto detect the proxy url
347  *
348  * RETURNS
349  *     FALSE on failure
350  *
351  */
352 BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
353         DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
354 {
355     FIXME("STUB\n");
356     INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
357     return FALSE;
358 }
359
360
361 /***********************************************************************
362  *           INTERNET_ConfigureProxyFromReg
363  *
364  * FIXME:
365  * The proxy may be specified in the form 'http=proxy.my.org'
366  * Presumably that means there can be ftp=ftpproxy.my.org too.
367  */
368 static BOOL INTERNET_ConfigureProxyFromReg( LPWININETAPPINFOW lpwai )
369 {
370     HKEY key;
371     DWORD r, keytype, len, enabled;
372     LPCSTR lpszInternetSettings =
373         "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
374     static const WCHAR szProxyServer[] = { 'P','r','o','x','y','S','e','r','v','e','r', 0 };
375
376     r = RegOpenKeyA(HKEY_CURRENT_USER, lpszInternetSettings, &key);
377     if ( r != ERROR_SUCCESS )
378         return FALSE;
379
380     len = sizeof enabled;
381     r = RegQueryValueExA( key, "ProxyEnable", NULL, &keytype,
382                           (BYTE*)&enabled, &len);
383     if( (r == ERROR_SUCCESS) && enabled )
384     {
385         TRACE("Proxy is enabled.\n");
386
387         /* figure out how much memory the proxy setting takes */
388         r = RegQueryValueExW( key, szProxyServer, NULL, &keytype, 
389                               NULL, &len);
390         if( (r == ERROR_SUCCESS) && len && (keytype == REG_SZ) )
391         {
392             LPWSTR szProxy, p;
393             static const WCHAR szHttp[] = {'h','t','t','p','=',0};
394
395             szProxy=HeapAlloc( GetProcessHeap(), 0, len );
396             RegQueryValueExW( key, szProxyServer, NULL, &keytype,
397                               (BYTE*)szProxy, &len);
398
399             /* find the http proxy, and strip away everything else */
400             p = strstrW( szProxy, szHttp );
401             if( p )
402             {
403                  p += lstrlenW(szHttp);
404                  lstrcpyW( szProxy, p );
405             }
406             p = strchrW( szProxy, ' ' );
407             if( p )
408                 *p = 0;
409
410             lpwai->dwAccessType = INTERNET_OPEN_TYPE_PROXY;
411             lpwai->lpszProxy = szProxy;
412
413             TRACE("http proxy = %s\n", debugstr_w(lpwai->lpszProxy));
414         }
415         else
416             ERR("Couldn't read proxy server settings.\n");
417     }
418     else
419         TRACE("Proxy is not enabled.\n");
420     RegCloseKey(key);
421
422     return enabled;
423 }
424
425 /***********************************************************************
426  *           dump_INTERNET_FLAGS
427  *
428  * Helper function to TRACE the internet flags.
429  *
430  * RETURNS
431  *    None
432  *
433  */
434 static void dump_INTERNET_FLAGS(DWORD dwFlags) 
435 {
436 #define FE(x) { x, #x }
437     static const wininet_flag_info flag[] = {
438         FE(INTERNET_FLAG_RELOAD),
439         FE(INTERNET_FLAG_RAW_DATA),
440         FE(INTERNET_FLAG_EXISTING_CONNECT),
441         FE(INTERNET_FLAG_ASYNC),
442         FE(INTERNET_FLAG_PASSIVE),
443         FE(INTERNET_FLAG_NO_CACHE_WRITE),
444         FE(INTERNET_FLAG_MAKE_PERSISTENT),
445         FE(INTERNET_FLAG_FROM_CACHE),
446         FE(INTERNET_FLAG_SECURE),
447         FE(INTERNET_FLAG_KEEP_CONNECTION),
448         FE(INTERNET_FLAG_NO_AUTO_REDIRECT),
449         FE(INTERNET_FLAG_READ_PREFETCH),
450         FE(INTERNET_FLAG_NO_COOKIES),
451         FE(INTERNET_FLAG_NO_AUTH),
452         FE(INTERNET_FLAG_CACHE_IF_NET_FAIL),
453         FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP),
454         FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS),
455         FE(INTERNET_FLAG_IGNORE_CERT_DATE_INVALID),
456         FE(INTERNET_FLAG_IGNORE_CERT_CN_INVALID),
457         FE(INTERNET_FLAG_RESYNCHRONIZE),
458         FE(INTERNET_FLAG_HYPERLINK),
459         FE(INTERNET_FLAG_NO_UI),
460         FE(INTERNET_FLAG_PRAGMA_NOCACHE),
461         FE(INTERNET_FLAG_CACHE_ASYNC),
462         FE(INTERNET_FLAG_FORMS_SUBMIT),
463         FE(INTERNET_FLAG_NEED_FILE),
464         FE(INTERNET_FLAG_TRANSFER_ASCII),
465         FE(INTERNET_FLAG_TRANSFER_BINARY)
466     };
467 #undef FE
468     int i;
469     
470     for (i = 0; i < (sizeof(flag) / sizeof(flag[0])); i++) {
471         if (flag[i].val & dwFlags) {
472             TRACE(" %s", flag[i].name);
473             dwFlags &= ~flag[i].val;
474         }
475     }   
476     if (dwFlags)
477         TRACE(" Unknown flags (%08lx)\n", dwFlags);
478     else
479         TRACE("\n");
480 }
481
482 /***********************************************************************
483  *           InternetOpenW   (WININET.@)
484  *
485  * Per-application initialization of wininet
486  *
487  * RETURNS
488  *    HINTERNET on success
489  *    NULL on failure
490  *
491  */
492 HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
493     LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
494 {
495     LPWININETAPPINFOW lpwai = NULL;
496     HINTERNET handle = NULL;
497
498     if (TRACE_ON(wininet)) {
499 #define FE(x) { x, #x }
500         static const wininet_flag_info access_type[] = {
501             FE(INTERNET_OPEN_TYPE_PRECONFIG),
502             FE(INTERNET_OPEN_TYPE_DIRECT),
503             FE(INTERNET_OPEN_TYPE_PROXY),
504             FE(INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
505         };
506 #undef FE
507         DWORD i;
508         const char *access_type_str = "Unknown";
509         
510         TRACE("(%s, %li, %s, %s, %li)\n", debugstr_w(lpszAgent), dwAccessType,
511               debugstr_w(lpszProxy), debugstr_w(lpszProxyBypass), dwFlags);
512         for (i = 0; i < (sizeof(access_type) / sizeof(access_type[0])); i++) {
513             if (access_type[i].val == dwAccessType) {
514                 access_type_str = access_type[i].name;
515                 break;
516             }
517         }
518         TRACE("  access type : %s\n", access_type_str);
519         TRACE("  flags       :");
520         dump_INTERNET_FLAGS(dwFlags);
521     }
522
523     /* Clear any error information */
524     INTERNET_SetLastError(0);
525
526     lpwai = HeapAlloc(GetProcessHeap(), 0, sizeof(WININETAPPINFOW));
527     if (NULL == lpwai)
528     {
529         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
530         goto lend;
531     }
532  
533     memset(lpwai, 0, sizeof(WININETAPPINFOW));
534     lpwai->hdr.htype = WH_HINIT;
535     lpwai->hdr.lpwhparent = NULL;
536     lpwai->hdr.dwFlags = dwFlags;
537     lpwai->hdr.dwRefCount = 1;
538     lpwai->hdr.destroy = INTERNET_CloseHandle;
539     lpwai->dwAccessType = dwAccessType;
540     lpwai->lpszProxyUsername = NULL;
541     lpwai->lpszProxyPassword = NULL;
542
543     handle = WININET_AllocHandle( &lpwai->hdr );
544     if( !handle )
545     {
546         HeapFree( GetProcessHeap(), 0, lpwai );
547         INTERNET_SetLastError(ERROR_OUTOFMEMORY);
548         goto lend;
549     }
550
551     if (NULL != lpszAgent)
552     {
553         lpwai->lpszAgent = HeapAlloc( GetProcessHeap(),0,
554                                       (strlenW(lpszAgent)+1)*sizeof(WCHAR));
555         if (lpwai->lpszAgent)
556             lstrcpyW( lpwai->lpszAgent, lpszAgent );
557     }
558     if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
559         INTERNET_ConfigureProxyFromReg( lpwai );
560     else if (NULL != lpszProxy)
561     {
562         lpwai->lpszProxy = HeapAlloc( GetProcessHeap(), 0,
563                                       (strlenW(lpszProxy)+1)*sizeof(WCHAR));
564         if (lpwai->lpszProxy)
565             lstrcpyW( lpwai->lpszProxy, lpszProxy );
566     }
567
568     if (NULL != lpszProxyBypass)
569     {
570         lpwai->lpszProxyBypass = HeapAlloc( GetProcessHeap(), 0,
571                                      (strlenW(lpszProxyBypass)+1)*sizeof(WCHAR));
572         if (lpwai->lpszProxyBypass)
573             lstrcpyW( lpwai->lpszProxyBypass, lpszProxyBypass );
574     }
575
576 lend:
577     if( lpwai )
578         WININET_Release( &lpwai->hdr );
579
580     TRACE("returning %p\n", lpwai);
581
582     return handle;
583 }
584
585
586 /***********************************************************************
587  *           InternetOpenA   (WININET.@)
588  *
589  * Per-application initialization of wininet
590  *
591  * RETURNS
592  *    HINTERNET on success
593  *    NULL on failure
594  *
595  */
596 HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
597     LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
598 {
599     HINTERNET rc = (HINTERNET)NULL;
600     INT len;
601     WCHAR *szAgent = NULL, *szProxy = NULL, *szBypass = NULL;
602
603     TRACE("(%s, 0x%08lx, %s, %s, 0x%08lx)\n", debugstr_a(lpszAgent),
604        dwAccessType, debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
605
606     if( lpszAgent )
607     {
608         len = MultiByteToWideChar(CP_ACP, 0, lpszAgent, -1, NULL, 0);
609         szAgent = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
610         MultiByteToWideChar(CP_ACP, 0, lpszAgent, -1, szAgent, len);
611     }
612
613     if( lpszProxy )
614     {
615         len = MultiByteToWideChar(CP_ACP, 0, lpszProxy, -1, NULL, 0);
616         szProxy = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
617         MultiByteToWideChar(CP_ACP, 0, lpszProxy, -1, szProxy, len);
618     }
619
620     if( lpszProxyBypass )
621     {
622         len = MultiByteToWideChar(CP_ACP, 0, lpszProxyBypass, -1, NULL, 0);
623         szBypass = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
624         MultiByteToWideChar(CP_ACP, 0, lpszProxyBypass, -1, szBypass, len);
625     }
626
627     rc = InternetOpenW(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
628
629     HeapFree(GetProcessHeap(), 0, szAgent);
630     HeapFree(GetProcessHeap(), 0, szProxy);
631     HeapFree(GetProcessHeap(), 0, szBypass);
632
633     return rc;
634 }
635
636 /***********************************************************************
637  *           InternetGetLastResponseInfoA (WININET.@)
638  *
639  * Return last wininet error description on the calling thread
640  *
641  * RETURNS
642  *    TRUE on success of writing to buffer
643  *    FALSE on failure
644  *
645  */
646 BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
647     LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
648 {
649     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
650
651     TRACE("\n");
652
653     *lpdwError = lpwite->dwError;
654     if (lpwite->dwError)
655     {
656         memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
657         *lpdwBufferLength = strlen(lpszBuffer);
658     }
659     else
660         *lpdwBufferLength = 0;
661
662     return TRUE;
663 }
664
665 /***********************************************************************
666  *           InternetGetLastResponseInfoW (WININET.@)
667  *
668  * Return last wininet error description on the calling thread
669  *
670  * RETURNS
671  *    TRUE on success of writing to buffer
672  *    FALSE on failure
673  *
674  */
675 BOOL WINAPI InternetGetLastResponseInfoW(LPDWORD lpdwError,
676     LPWSTR lpszBuffer, LPDWORD lpdwBufferLength)
677 {
678     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
679
680     TRACE("\n");
681
682     *lpdwError = lpwite->dwError;
683     if (lpwite->dwError)
684     {
685         memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
686         *lpdwBufferLength = lstrlenW(lpszBuffer);
687     }
688     else
689         *lpdwBufferLength = 0;
690
691     return TRUE;
692 }
693
694 /***********************************************************************
695  *           InternetGetConnectedState (WININET.@)
696  *
697  * Return connected state
698  *
699  * RETURNS
700  *    TRUE if connected
701  *    if lpdwStatus is not null, return the status (off line,
702  *    modem, lan...) in it.
703  *    FALSE if not connected
704  */
705 BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
706 {
707     TRACE("(%p, 0x%08lx)\n", lpdwStatus, dwReserved);
708
709     if (lpdwStatus) {
710         FIXME("always returning LAN connection.\n");
711         *lpdwStatus = INTERNET_CONNECTION_LAN;
712     }
713     return TRUE;
714 }
715
716
717 /***********************************************************************
718  *           InternetGetConnectedStateExW (WININET.@)
719  *
720  * Return connected state
721  *
722  * PARAMS
723  *
724  * lpdwStatus         [O] Flags specifying the status of the internet connection.
725  * lpszConnectionName [O] Pointer to buffer to receive the friendly name of the internet connection.
726  * dwNameLen          [I] Size of the buffer, in characters.
727  * dwReserved         [I] Reserved. Must be set to 0.
728  *
729  * RETURNS
730  *    TRUE if connected
731  *    if lpdwStatus is not null, return the status (off line,
732  *    modem, lan...) in it.
733  *    FALSE if not connected
734  *
735  * NOTES
736  *   If the system has no available network connections, an empty string is
737  *   stored in lpszConnectionName. If there is a LAN connection, a localized
738  *   "LAN Connection" string is stored. Presumably, if only a dial-up
739  *   connection is available then the name of the dial-up connection is
740  *   returned. Why any application, other than the "Internet Settings" CPL,
741  *   would want to use this function instead of the simpler InternetGetConnectedStateW
742  *   function is beyond me.
743  */
744 BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
745                                          DWORD dwNameLen, DWORD dwReserved)
746 {
747     TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
748
749     /* Must be zero */
750     if(dwReserved)
751         return FALSE;
752
753     if (lpdwStatus) {
754         FIXME("always returning LAN connection.\n");
755         *lpdwStatus = INTERNET_CONNECTION_LAN;
756     }
757     return LoadStringW(WININET_hModule, IDS_LANCONNECTION, lpszConnectionName, dwNameLen);
758 }
759
760
761 /***********************************************************************
762  *           InternetGetConnectedStateExA (WININET.@)
763  */
764 BOOL WINAPI InternetGetConnectedStateExA(LPDWORD lpdwStatus, LPSTR lpszConnectionName,
765                                          DWORD dwNameLen, DWORD dwReserved)
766 {
767     LPWSTR lpwszConnectionName = NULL;
768     BOOL rc;
769
770     TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
771
772     if (lpszConnectionName && dwNameLen > 0)
773         lpwszConnectionName= HeapAlloc(GetProcessHeap(), 0, dwNameLen * sizeof(WCHAR));
774
775     rc = InternetGetConnectedStateExW(lpdwStatus,lpwszConnectionName, dwNameLen,
776                                       dwReserved);
777     if (rc && lpwszConnectionName)
778     {
779         WideCharToMultiByte(CP_ACP,0,lpwszConnectionName,-1,lpszConnectionName,
780                             dwNameLen, NULL, NULL);
781
782         HeapFree(GetProcessHeap(),0,lpwszConnectionName);
783     }
784
785     return rc;
786 }
787
788
789 /***********************************************************************
790  *           InternetConnectW (WININET.@)
791  *
792  * Open a ftp, gopher or http session
793  *
794  * RETURNS
795  *    HINTERNET a session handle on success
796  *    NULL on failure
797  *
798  */
799 HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
800     LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
801     LPCWSTR lpszUserName, LPCWSTR lpszPassword,
802     DWORD dwService, DWORD dwFlags, DWORD dwContext)
803 {
804     LPWININETAPPINFOW hIC;
805     HINTERNET rc = (HINTERNET) NULL;
806
807     TRACE("(%p, %s, %i, %s, %s, %li, %li, %li)\n", hInternet, debugstr_w(lpszServerName),
808           nServerPort, debugstr_w(lpszUserName), debugstr_w(lpszPassword),
809           dwService, dwFlags, dwContext);
810
811     /* Clear any error information */
812     INTERNET_SetLastError(0);
813     hIC = (LPWININETAPPINFOW) WININET_GetObject( hInternet );
814     if ( (hIC == NULL) || (hIC->hdr.htype != WH_HINIT) )
815         goto lend;
816
817     switch (dwService)
818     {
819         case INTERNET_SERVICE_FTP:
820             rc = FTP_Connect(hIC, lpszServerName, nServerPort,
821             lpszUserName, lpszPassword, dwFlags, dwContext, 0);
822             break;
823
824         case INTERNET_SERVICE_HTTP:
825             rc = HTTP_Connect(hIC, lpszServerName, nServerPort,
826             lpszUserName, lpszPassword, dwFlags, dwContext, 0);
827             break;
828
829         case INTERNET_SERVICE_GOPHER:
830         default:
831             break;
832     }
833 lend:
834     if( hIC )
835         WININET_Release( &hIC->hdr );
836
837     TRACE("returning %p\n", rc);
838     return rc;
839 }
840
841
842 /***********************************************************************
843  *           InternetConnectA (WININET.@)
844  *
845  * Open a ftp, gopher or http session
846  *
847  * RETURNS
848  *    HINTERNET a session handle on success
849  *    NULL on failure
850  *
851  */
852 HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
853     LPCSTR lpszServerName, INTERNET_PORT nServerPort,
854     LPCSTR lpszUserName, LPCSTR lpszPassword,
855     DWORD dwService, DWORD dwFlags, DWORD dwContext)
856 {
857     HINTERNET rc = (HINTERNET)NULL;
858     INT len = 0;
859     LPWSTR szServerName = NULL;
860     LPWSTR szUserName = NULL;
861     LPWSTR szPassword = NULL;
862
863     if (lpszServerName)
864     {
865         len = MultiByteToWideChar(CP_ACP, 0, lpszServerName, -1, NULL, 0);
866         szServerName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
867         MultiByteToWideChar(CP_ACP, 0, lpszServerName, -1, szServerName, len);
868     }
869     if (lpszUserName)
870     {
871         len = MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, NULL, 0);
872         szUserName = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
873         MultiByteToWideChar(CP_ACP, 0, lpszUserName, -1, szUserName, len);
874     }
875     if (lpszPassword)
876     {
877         len = MultiByteToWideChar(CP_ACP, 0, lpszPassword, -1, NULL, 0);
878         szPassword = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR));
879         MultiByteToWideChar(CP_ACP, 0, lpszPassword, -1, szPassword, len);
880     }
881
882
883     rc = InternetConnectW(hInternet, szServerName, nServerPort,
884         szUserName, szPassword, dwService, dwFlags, dwContext);
885
886     HeapFree(GetProcessHeap(), 0, szServerName);
887     HeapFree(GetProcessHeap(), 0, szUserName);
888     HeapFree(GetProcessHeap(), 0, szPassword);
889     return rc;
890 }
891
892
893 /***********************************************************************
894  *           InternetFindNextFileA (WININET.@)
895  *
896  * Continues a file search from a previous call to FindFirstFile
897  *
898  * RETURNS
899  *    TRUE on success
900  *    FALSE on failure
901  *
902  */
903 BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
904 {
905     BOOL ret;
906     WIN32_FIND_DATAW fd;
907     
908     ret = InternetFindNextFileW(hFind, lpvFindData?&fd:NULL);
909     if(lpvFindData)
910         WININET_find_data_WtoA(&fd, (LPWIN32_FIND_DATAA)lpvFindData);
911     return ret;
912 }
913
914 /***********************************************************************
915  *           InternetFindNextFileW (WININET.@)
916  *
917  * Continues a file search from a previous call to FindFirstFile
918  *
919  * RETURNS
920  *    TRUE on success
921  *    FALSE on failure
922  *
923  */
924 BOOL WINAPI InternetFindNextFileW(HINTERNET hFind, LPVOID lpvFindData)
925 {
926     LPWININETAPPINFOW hIC = NULL;
927     LPWININETFINDNEXTW lpwh;
928     BOOL bSuccess = FALSE;
929
930     TRACE("\n");
931
932     lpwh = (LPWININETFINDNEXTW) WININET_GetObject( hFind );
933     if (NULL == lpwh || lpwh->hdr.htype != WH_HFINDNEXT)
934     {
935         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
936         goto lend;
937     }
938
939     hIC = GET_HWININET_FROM_LPWININETFINDNEXT(lpwh);
940     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC)
941     {
942         WORKREQUEST workRequest;
943         struct WORKREQ_INTERNETFINDNEXTW *req;
944
945         workRequest.asyncall = INTERNETFINDNEXTW;
946         workRequest.hdr = WININET_AddRef( &lpwh->hdr );
947         req = &workRequest.u.InternetFindNextW;
948         req->lpFindFileData = lpvFindData;
949
950         bSuccess = INTERNET_AsyncCall(&workRequest);
951     }
952     else
953     {
954         bSuccess = INTERNET_FindNextFileW(lpwh, lpvFindData);
955     }
956 lend:
957     if( lpwh )
958         WININET_Release( &lpwh->hdr );
959     return bSuccess;
960 }
961
962 /***********************************************************************
963  *           INTERNET_FindNextFileW (Internal)
964  *
965  * Continues a file search from a previous call to FindFirstFile
966  *
967  * RETURNS
968  *    TRUE on success
969  *    FALSE on failure
970  *
971  */
972 BOOL WINAPI INTERNET_FindNextFileW(LPWININETFINDNEXTW lpwh, LPVOID lpvFindData)
973 {
974     BOOL bSuccess = TRUE;
975     LPWIN32_FIND_DATAW lpFindFileData;
976
977     TRACE("\n");
978
979     assert (lpwh->hdr.htype == WH_HFINDNEXT);
980
981     /* Clear any error information */
982     INTERNET_SetLastError(0);
983
984     if (lpwh->hdr.lpwhparent->htype != WH_HFTPSESSION)
985     {
986         FIXME("Only FTP find next supported\n");
987         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
988         return FALSE;
989     }
990
991     TRACE("index(%ld) size(%ld)\n", lpwh->index, lpwh->size);
992
993     lpFindFileData = (LPWIN32_FIND_DATAW) lpvFindData;
994     ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
995
996     if (lpwh->index >= lpwh->size)
997     {
998         INTERNET_SetLastError(ERROR_NO_MORE_FILES);
999         bSuccess = FALSE;
1000         goto lend;
1001     }
1002
1003     FTP_ConvertFileProp(&lpwh->lpafp[lpwh->index], lpFindFileData);
1004     lpwh->index++;
1005
1006     TRACE("\nName: %s\nSize: %ld\n", debugstr_w(lpFindFileData->cFileName), lpFindFileData->nFileSizeLow);
1007
1008 lend:
1009
1010     if (lpwh->hdr.dwFlags & INTERNET_FLAG_ASYNC)
1011     {
1012         INTERNET_ASYNC_RESULT iar;
1013
1014         iar.dwResult = (DWORD)bSuccess;
1015         iar.dwError = iar.dwError = bSuccess ? ERROR_SUCCESS :
1016                                                INTERNET_GetLastError();
1017
1018         INTERNET_SendCallback(&lpwh->hdr, lpwh->hdr.dwContext,
1019                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1020                               sizeof(INTERNET_ASYNC_RESULT));
1021     }
1022
1023     return bSuccess;
1024 }
1025
1026
1027 /***********************************************************************
1028  *           INTERNET_CloseHandle (internal)
1029  *
1030  * Close internet handle
1031  *
1032  * RETURNS
1033  *    Void
1034  *
1035  */
1036 static VOID INTERNET_CloseHandle(LPWININETHANDLEHEADER hdr)
1037 {
1038     LPWININETAPPINFOW lpwai = (LPWININETAPPINFOW) hdr;
1039
1040     TRACE("%p\n",lpwai);
1041
1042     HeapFree(GetProcessHeap(), 0, lpwai->lpszAgent);
1043     HeapFree(GetProcessHeap(), 0, lpwai->lpszProxy);
1044     HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyBypass);
1045     HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyUsername);
1046     HeapFree(GetProcessHeap(), 0, lpwai->lpszProxyPassword);
1047     HeapFree(GetProcessHeap(), 0, lpwai);
1048 }
1049
1050
1051 /***********************************************************************
1052  *           InternetCloseHandle (WININET.@)
1053  *
1054  * Generic close handle function
1055  *
1056  * RETURNS
1057  *    TRUE on success
1058  *    FALSE on failure
1059  *
1060  */
1061 BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
1062 {
1063     LPWININETHANDLEHEADER lpwh;
1064     
1065     TRACE("%p\n",hInternet);
1066
1067     lpwh = WININET_GetObject( hInternet );
1068     if (NULL == lpwh)
1069     {
1070         INTERNET_SetLastError(ERROR_INVALID_HANDLE);
1071         return FALSE;
1072     }
1073
1074     /* FIXME: native appears to send this from the equivalent of
1075      * WININET_Release */
1076     INTERNET_SendCallback(lpwh, lpwh->dwContext,
1077                           INTERNET_STATUS_HANDLE_CLOSING, &hInternet,
1078                           sizeof(HINTERNET));
1079
1080     if( lpwh->lpwhparent )
1081         WININET_Release( lpwh->lpwhparent );
1082     WININET_FreeHandle( hInternet );
1083     WININET_Release( lpwh );
1084
1085     return TRUE;
1086 }
1087
1088
1089 /***********************************************************************
1090  *           ConvertUrlComponentValue (Internal)
1091  *
1092  * Helper function for InternetCrackUrlW
1093  *
1094  */
1095 static void ConvertUrlComponentValue(LPSTR* lppszComponent, LPDWORD dwComponentLen,
1096                                      LPWSTR lpwszComponent, DWORD dwwComponentLen,
1097                                      LPCSTR lpszStart, LPCWSTR lpwszStart)
1098 {
1099     TRACE("%p %p %p %ld %p %p\n", lppszComponent, dwComponentLen, lpwszComponent, dwwComponentLen, lpszStart, lpwszStart);
1100     if (*dwComponentLen != 0)
1101     {
1102         DWORD nASCIILength=WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,NULL,0,NULL,NULL);
1103         if (*lppszComponent == NULL)
1104         {
1105             int nASCIIOffset=WideCharToMultiByte(CP_ACP,0,lpwszStart,lpwszComponent-lpwszStart,NULL,0,NULL,NULL);
1106             *lppszComponent = (LPSTR)lpszStart+nASCIIOffset;
1107             *dwComponentLen = nASCIILength;
1108         }
1109         else
1110         {
1111             DWORD ncpylen = min((*dwComponentLen)-1, nASCIILength);
1112             WideCharToMultiByte(CP_ACP,0,lpwszComponent,dwwComponentLen,*lppszComponent,ncpylen+1,NULL,NULL);
1113             (*lppszComponent)[ncpylen]=0;
1114             *dwComponentLen = ncpylen;
1115         }
1116     }
1117 }
1118
1119
1120 /***********************************************************************
1121  *           InternetCrackUrlA (WININET.@)
1122  *
1123  * Break up URL into its components
1124  *
1125  * TODO: Handle dwFlags
1126  *
1127  * RETURNS
1128  *    TRUE on success
1129  *    FALSE on failure
1130  *
1131  */
1132 BOOL WINAPI InternetCrackUrlA(LPCSTR lpszUrl, DWORD dwUrlLength, DWORD dwFlags,
1133     LPURL_COMPONENTSA lpUrlComponents)
1134 {
1135   DWORD nLength;
1136   URL_COMPONENTSW UCW;
1137   WCHAR* lpwszUrl;
1138
1139   TRACE("(%s %lu %lx %p)\n", debugstr_a(lpszUrl), dwUrlLength, dwFlags, lpUrlComponents);
1140   if(dwUrlLength<=0)
1141       dwUrlLength=-1;
1142   nLength=MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,NULL,0);
1143
1144   /* if dwUrlLength=-1 then nLength includes null but length to 
1145        InternetCrackUrlW should not include it                  */
1146   if (dwUrlLength == -1) nLength--;
1147
1148   lpwszUrl=HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*nLength);
1149   MultiByteToWideChar(CP_ACP,0,lpszUrl,dwUrlLength,lpwszUrl,nLength);
1150
1151   memset(&UCW,0,sizeof(UCW));
1152   if(lpUrlComponents->dwHostNameLength!=0)
1153       UCW.dwHostNameLength= lpUrlComponents->dwHostNameLength;
1154   if(lpUrlComponents->dwUserNameLength!=0)
1155       UCW.dwUserNameLength=lpUrlComponents->dwUserNameLength;
1156   if(lpUrlComponents->dwPasswordLength!=0)
1157       UCW.dwPasswordLength=lpUrlComponents->dwPasswordLength;
1158   if(lpUrlComponents->dwUrlPathLength!=0)
1159       UCW.dwUrlPathLength=lpUrlComponents->dwUrlPathLength;
1160   if(lpUrlComponents->dwSchemeLength!=0)
1161       UCW.dwSchemeLength=lpUrlComponents->dwSchemeLength;
1162   if(lpUrlComponents->dwExtraInfoLength!=0)
1163       UCW.dwExtraInfoLength=lpUrlComponents->dwExtraInfoLength;
1164   if(!InternetCrackUrlW(lpwszUrl,nLength,dwFlags,&UCW))
1165   {
1166       HeapFree(GetProcessHeap(), 0, lpwszUrl);
1167       return FALSE;
1168   }
1169
1170   ConvertUrlComponentValue(&lpUrlComponents->lpszHostName, &lpUrlComponents->dwHostNameLength,
1171                            UCW.lpszHostName, UCW.dwHostNameLength,
1172                            lpszUrl, lpwszUrl);
1173   ConvertUrlComponentValue(&lpUrlComponents->lpszUserName, &lpUrlComponents->dwUserNameLength,
1174                            UCW.lpszUserName, UCW.dwUserNameLength,
1175                            lpszUrl, lpwszUrl);
1176   ConvertUrlComponentValue(&lpUrlComponents->lpszPassword, &lpUrlComponents->dwPasswordLength,
1177                            UCW.lpszPassword, UCW.dwPasswordLength,
1178                            lpszUrl, lpwszUrl);
1179   ConvertUrlComponentValue(&lpUrlComponents->lpszUrlPath, &lpUrlComponents->dwUrlPathLength,
1180                            UCW.lpszUrlPath, UCW.dwUrlPathLength,
1181                            lpszUrl, lpwszUrl);
1182   ConvertUrlComponentValue(&lpUrlComponents->lpszScheme, &lpUrlComponents->dwSchemeLength,
1183                            UCW.lpszScheme, UCW.dwSchemeLength,
1184                            lpszUrl, lpwszUrl);
1185   ConvertUrlComponentValue(&lpUrlComponents->lpszExtraInfo, &lpUrlComponents->dwExtraInfoLength,
1186                            UCW.lpszExtraInfo, UCW.dwExtraInfoLength,
1187                            lpszUrl, lpwszUrl);
1188   lpUrlComponents->nScheme=UCW.nScheme;
1189   lpUrlComponents->nPort=UCW.nPort;
1190   HeapFree(GetProcessHeap(), 0, lpwszUrl);
1191   
1192   TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", lpszUrl,
1193           debugstr_an(lpUrlComponents->lpszScheme,lpUrlComponents->dwSchemeLength),
1194           debugstr_an(lpUrlComponents->lpszHostName,lpUrlComponents->dwHostNameLength),
1195           debugstr_an(lpUrlComponents->lpszUrlPath,lpUrlComponents->dwUrlPathLength),
1196           debugstr_an(lpUrlComponents->lpszExtraInfo,lpUrlComponents->dwExtraInfoLength));
1197
1198   return TRUE;
1199 }
1200
1201 /***********************************************************************
1202  *           GetInternetSchemeW (internal)
1203  *
1204  * Get scheme of url
1205  *
1206  * RETURNS
1207  *    scheme on success
1208  *    INTERNET_SCHEME_UNKNOWN on failure
1209  *
1210  */
1211 static INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, DWORD nMaxCmp)
1212 {
1213     INTERNET_SCHEME iScheme=INTERNET_SCHEME_UNKNOWN;
1214     static const WCHAR lpszFtp[]={'f','t','p',0};
1215     static const WCHAR lpszGopher[]={'g','o','p','h','e','r',0};
1216     static const WCHAR lpszHttp[]={'h','t','t','p',0};
1217     static const WCHAR lpszHttps[]={'h','t','t','p','s',0};
1218     static const WCHAR lpszFile[]={'f','i','l','e',0};
1219     static const WCHAR lpszNews[]={'n','e','w','s',0};
1220     static const WCHAR lpszMailto[]={'m','a','i','l','t','o',0};
1221     static const WCHAR lpszRes[]={'r','e','s',0};
1222     WCHAR* tempBuffer=NULL;
1223     TRACE("%s %ld\n",debugstr_wn(lpszScheme, nMaxCmp), nMaxCmp);
1224     if(lpszScheme==NULL)
1225         return INTERNET_SCHEME_UNKNOWN;
1226
1227     tempBuffer=HeapAlloc(GetProcessHeap(),0,(nMaxCmp+1)*sizeof(WCHAR));
1228     lstrcpynW(tempBuffer,lpszScheme,nMaxCmp+1);
1229     strlwrW(tempBuffer);
1230     if (nMaxCmp==strlenW(lpszFtp) && !strncmpW(lpszFtp, tempBuffer, nMaxCmp))
1231         iScheme=INTERNET_SCHEME_FTP;
1232     else if (nMaxCmp==strlenW(lpszGopher) && !strncmpW(lpszGopher, tempBuffer, nMaxCmp))
1233         iScheme=INTERNET_SCHEME_GOPHER;
1234     else if (nMaxCmp==strlenW(lpszHttp) && !strncmpW(lpszHttp, tempBuffer, nMaxCmp))
1235         iScheme=INTERNET_SCHEME_HTTP;
1236     else if (nMaxCmp==strlenW(lpszHttps) && !strncmpW(lpszHttps, tempBuffer, nMaxCmp))
1237         iScheme=INTERNET_SCHEME_HTTPS;
1238     else if (nMaxCmp==strlenW(lpszFile) && !strncmpW(lpszFile, tempBuffer, nMaxCmp))
1239         iScheme=INTERNET_SCHEME_FILE;
1240     else if (nMaxCmp==strlenW(lpszNews) && !strncmpW(lpszNews, tempBuffer, nMaxCmp))
1241         iScheme=INTERNET_SCHEME_NEWS;
1242     else if (nMaxCmp==strlenW(lpszMailto) && !strncmpW(lpszMailto, tempBuffer, nMaxCmp))
1243         iScheme=INTERNET_SCHEME_MAILTO;
1244     else if (nMaxCmp==strlenW(lpszRes) && !strncmpW(lpszRes, tempBuffer, nMaxCmp))
1245         iScheme=INTERNET_SCHEME_RES;
1246     HeapFree(GetProcessHeap(),0,tempBuffer);
1247     return iScheme;
1248 }
1249
1250 /***********************************************************************
1251  *           SetUrlComponentValueW (Internal)
1252  *
1253  * Helper function for InternetCrackUrlW
1254  *
1255  * PARAMS
1256  *     lppszComponent [O] Holds the returned string
1257  *     dwComponentLen [I] Holds the size of lppszComponent
1258  *                    [O] Holds the length of the string in lppszComponent without '\0'
1259  *     lpszStart      [I] Holds the string to copy from
1260  *     len            [I] Holds the length of lpszStart without '\0'
1261  *
1262  * RETURNS
1263  *    TRUE on success
1264  *    FALSE on failure
1265  *
1266  */
1267 static BOOL SetUrlComponentValueW(LPWSTR* lppszComponent, LPDWORD dwComponentLen, LPCWSTR lpszStart, DWORD len)
1268 {
1269     TRACE("%s (%ld)\n", debugstr_wn(lpszStart,len), len);
1270
1271     if ( (*dwComponentLen == 0) && (*lppszComponent == NULL) )
1272         return FALSE;
1273
1274     if (*dwComponentLen != 0 || *lppszComponent == NULL)
1275     {
1276         if (*lppszComponent == NULL)
1277         {
1278             *lppszComponent = (LPWSTR)lpszStart;
1279             *dwComponentLen = len;
1280         }
1281         else
1282         {
1283             DWORD ncpylen = min((*dwComponentLen)-1, len);
1284             memcpy(*lppszComponent, lpszStart, ncpylen*sizeof(WCHAR));
1285             (*lppszComponent)[ncpylen] = '\0';
1286             *dwComponentLen = ncpylen;
1287         }
1288     }
1289
1290     return TRUE;
1291 }
1292
1293 /***********************************************************************
1294  *           InternetCrackUrlW   (WININET.@)
1295  */
1296 BOOL WINAPI InternetCrackUrlW(LPCWSTR lpszUrl_orig, DWORD dwUrlLength_orig, DWORD dwFlags,
1297                               LPURL_COMPONENTSW lpUC)
1298 {
1299   /*
1300    * RFC 1808
1301    * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1302    *
1303    */
1304     LPCWSTR lpszParam    = NULL;
1305     BOOL  bIsAbsolute = FALSE;
1306     LPCWSTR lpszap, lpszUrl = lpszUrl_orig;
1307     LPCWSTR lpszcp = NULL;
1308     LPWSTR  lpszUrl_decode = NULL;
1309     DWORD dwUrlLength = dwUrlLength_orig;
1310     const WCHAR lpszSeparators[3]={';','?',0};
1311     const WCHAR lpszSlash[2]={'/',0};
1312     if(dwUrlLength==0)
1313         dwUrlLength=strlenW(lpszUrl);
1314
1315     TRACE("(%s %lu %lx %p)\n", debugstr_w(lpszUrl), dwUrlLength, dwFlags, lpUC);
1316     if (dwFlags & ICU_DECODE)
1317     {
1318         lpszUrl_decode=HeapAlloc( GetProcessHeap(), 0,  dwUrlLength * sizeof (WCHAR) );
1319         if( InternetCanonicalizeUrlW(lpszUrl_orig, lpszUrl_decode, &dwUrlLength, dwFlags))
1320         {
1321             lpszUrl =  lpszUrl_decode;
1322         }
1323     }
1324     lpszap = lpszUrl;
1325     
1326     /* Determine if the URI is absolute. */
1327     while (*lpszap != '\0')
1328     {
1329         if (isalnumW(*lpszap))
1330         {
1331             lpszap++;
1332             continue;
1333         }
1334         if ((*lpszap == ':') && (lpszap - lpszUrl >= 2))
1335         {
1336             bIsAbsolute = TRUE;
1337             lpszcp = lpszap;
1338         }
1339         else
1340         {
1341             lpszcp = lpszUrl; /* Relative url */
1342         }
1343
1344         break;
1345     }
1346
1347     /* Parse <params> */
1348     lpszParam = strpbrkW(lpszap, lpszSeparators);
1349     if (lpszParam != NULL)
1350     {
1351         SetUrlComponentValueW(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
1352                                    lpszParam, dwUrlLength-(lpszParam-lpszUrl));
1353     }
1354
1355     if (bIsAbsolute) /* Parse <protocol>:[//<net_loc>] */
1356     {
1357         LPCWSTR lpszNetLoc;
1358         static const WCHAR wszAbout[]={'a','b','o','u','t',':',0};
1359
1360         /* Get scheme first. */
1361         lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1362         SetUrlComponentValueW(&lpUC->lpszScheme, &lpUC->dwSchemeLength,
1363                                    lpszUrl, lpszcp - lpszUrl);
1364
1365         /* Eat ':' in protocol. */
1366         lpszcp++;
1367
1368         /* if the scheme is "about", there is no host */
1369         if(strncmpW(wszAbout,lpszUrl, lpszcp - lpszUrl)==0)
1370         {
1371             SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1372             SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1373             SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1374             lpUC->nPort = 0;
1375         }
1376         else
1377         {
1378             /* Skip over slashes. */
1379             if (*lpszcp == '/')
1380             {
1381                 lpszcp++;
1382                 if (*lpszcp == '/')
1383                 {
1384                     lpszcp++;
1385                     if (*lpszcp == '/')
1386                         lpszcp++;
1387                 }
1388             }
1389
1390             lpszNetLoc = strpbrkW(lpszcp, lpszSlash);
1391             if (lpszParam)
1392             {
1393                 if (lpszNetLoc)
1394                     lpszNetLoc = min(lpszNetLoc, lpszParam);
1395                 else
1396                     lpszNetLoc = lpszParam;
1397             }
1398             else if (!lpszNetLoc)
1399                 lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1400
1401             /* Parse net-loc */
1402             if (lpszNetLoc)
1403             {
1404                 LPCWSTR lpszHost;
1405                 LPCWSTR lpszPort;
1406
1407                 /* [<user>[<:password>]@]<host>[:<port>] */
1408                 /* First find the user and password if they exist */
1409
1410                 lpszHost = strchrW(lpszcp, '@');
1411                 if (lpszHost == NULL || lpszHost > lpszNetLoc)
1412                 {
1413                     /* username and password not specified. */
1414                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
1415                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1416                 }
1417                 else /* Parse out username and password */
1418                 {
1419                     LPCWSTR lpszUser = lpszcp;
1420                     LPCWSTR lpszPasswd = lpszHost;
1421
1422                     while (lpszcp < lpszHost)
1423                     {
1424                         if (*lpszcp == ':')
1425                             lpszPasswd = lpszcp;
1426
1427                         lpszcp++;
1428                     }
1429
1430                     SetUrlComponentValueW(&lpUC->lpszUserName, &lpUC->dwUserNameLength,
1431                                           lpszUser, lpszPasswd - lpszUser);
1432
1433                     if (lpszPasswd != lpszHost)
1434                         lpszPasswd++;
1435                     SetUrlComponentValueW(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
1436                                           lpszPasswd == lpszHost ? NULL : lpszPasswd,
1437                                           lpszHost - lpszPasswd);
1438
1439                     lpszcp++; /* Advance to beginning of host */
1440                 }
1441
1442                 /* Parse <host><:port> */
1443
1444                 lpszHost = lpszcp;
1445                 lpszPort = lpszNetLoc;
1446
1447                 /* special case for res:// URLs: there is no port here, so the host is the
1448                    entire string up to the first '/' */
1449                 if(lpUC->nScheme==INTERNET_SCHEME_RES)
1450                 {
1451                     SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1452                                           lpszHost, lpszPort - lpszHost);
1453                     lpUC->nPort = 0;
1454                     lpszcp=lpszNetLoc;
1455                 }
1456                 else
1457                 {
1458                     while (lpszcp < lpszNetLoc)
1459                     {
1460                         if (*lpszcp == ':')
1461                             lpszPort = lpszcp;
1462
1463                         lpszcp++;
1464                     }
1465
1466                     /* If the scheme is "file" and the host is just one letter, it's not a host */
1467                     if(lpUC->nScheme==INTERNET_SCHEME_FILE && (lpszPort-lpszHost)==1)
1468                     {
1469                         lpszcp=lpszHost;
1470                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1471                                               NULL, 0);
1472                         lpUC->nPort = 0;
1473                     }
1474                     else
1475                     {
1476                         SetUrlComponentValueW(&lpUC->lpszHostName, &lpUC->dwHostNameLength,
1477                                               lpszHost, lpszPort - lpszHost);
1478                         if (lpszPort != lpszNetLoc)
1479                             lpUC->nPort = atoiW(++lpszPort);
1480                         else
1481                             lpUC->nPort = 0;
1482                     }
1483                 }
1484             }
1485         }
1486     }
1487
1488     /* Here lpszcp points to:
1489      *
1490      * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
1491      *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1492      */
1493     if (lpszcp != 0 && *lpszcp != '\0' && (!lpszParam || lpszcp < lpszParam))
1494     {
1495         INT len;
1496
1497         /* Only truncate the parameter list if it's already been saved
1498          * in lpUC->lpszExtraInfo.
1499          */
1500         if (lpszParam && lpUC->dwExtraInfoLength && lpUC->lpszExtraInfo)
1501             len = lpszParam - lpszcp;
1502         else
1503         {
1504             /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
1505              * newlines if necessary.
1506              */
1507             LPWSTR lpsznewline = strchrW(lpszcp, '\n');
1508             if (lpsznewline != NULL)
1509                 len = lpsznewline - lpszcp;
1510             else
1511                 len = dwUrlLength-(lpszcp-lpszUrl);
1512         }
1513         SetUrlComponentValueW(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength,
1514                                    lpszcp, len);
1515     }
1516     else
1517     {
1518         lpUC->dwUrlPathLength = 0;
1519     }
1520
1521     TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
1522              debugstr_wn(lpUC->lpszScheme,lpUC->dwSchemeLength),
1523              debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
1524              debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
1525              debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1526
1527     if (lpszUrl_decode)
1528          HeapFree(GetProcessHeap(), 0, lpszUrl_decode );
1529     return TRUE;
1530 }
1531
1532 /***********************************************************************
1533  *           InternetAttemptConnect (WININET.@)
1534  *
1535  * Attempt to make a connection to the internet
1536  *
1537  * RETURNS
1538  *    ERROR_SUCCESS on success
1539  *    Error value   on failure
1540  *
1541  */
1542 DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
1543 {
1544     FIXME("Stub\n");
1545     return ERROR_SUCCESS;
1546 }
1547
1548
1549 /***********************************************************************
1550  *           InternetCanonicalizeUrlA (WININET.@)
1551  *
1552  * Escape unsafe characters and spaces
1553  *
1554  * RETURNS
1555  *    TRUE on success
1556  *    FALSE on failure
1557  *
1558  */
1559 BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
1560         LPDWORD lpdwBufferLength, DWORD dwFlags)
1561 {
1562     HRESULT hr;
1563     DWORD dwURLFlags= 0x80000000; /* Don't know what this means */
1564     if(dwFlags & ICU_DECODE)
1565     {
1566         dwURLFlags |= URL_UNESCAPE;
1567         dwFlags &= ~ICU_DECODE;
1568     }
1569
1570     if(dwFlags & ICU_ESCAPE)
1571     {
1572         dwURLFlags |= URL_UNESCAPE;
1573         dwFlags &= ~ICU_ESCAPE;
1574     }
1575     if(dwFlags & ICU_BROWSER_MODE)
1576     {
1577         dwURLFlags |= URL_BROWSER_MODE;
1578         dwFlags &= ~ICU_BROWSER_MODE;
1579     }
1580     if(dwFlags)
1581         FIXME("Unhandled flags 0x%08lx\n", dwFlags);
1582     TRACE("%s %p %p %08lx\n", debugstr_a(lpszUrl), lpszBuffer,
1583         lpdwBufferLength, dwURLFlags);
1584
1585     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1586     dwFlags ^= ICU_NO_ENCODE;
1587
1588     hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwURLFlags);
1589
1590     return (hr == S_OK) ? TRUE : FALSE;
1591 }
1592
1593 /***********************************************************************
1594  *           InternetCanonicalizeUrlW (WININET.@)
1595  *
1596  * Escape unsafe characters and spaces
1597  *
1598  * RETURNS
1599  *    TRUE on success
1600  *    FALSE on failure
1601  *
1602  */
1603 BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
1604     LPDWORD lpdwBufferLength, DWORD dwFlags)
1605 {
1606     HRESULT hr;
1607     DWORD dwURLFlags= 0x80000000; /* Don't know what this means */
1608     if(dwFlags & ICU_DECODE)
1609     {
1610         dwURLFlags |= URL_UNESCAPE;
1611         dwFlags &= ~ICU_DECODE;
1612     }
1613
1614     if(dwFlags & ICU_ESCAPE)
1615     {
1616         dwURLFlags |= URL_UNESCAPE;
1617         dwFlags &= ~ICU_ESCAPE;
1618     }
1619     if(dwFlags & ICU_BROWSER_MODE)
1620     {
1621         dwURLFlags |= URL_BROWSER_MODE;
1622         dwFlags &= ~ICU_BROWSER_MODE;
1623     }
1624     if(dwFlags)
1625         FIXME("Unhandled flags 0x%08lx\n", dwFlags);
1626     TRACE("%s %p %p %08lx\n", debugstr_w(lpszUrl), lpszBuffer,
1627         lpdwBufferLength, dwURLFlags);
1628
1629     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
1630     dwFlags ^= ICU_NO_ENCODE;
1631
1632     hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwURLFlags);
1633
1634     return (hr == S_OK) ? TRUE : FALSE;
1635 }
1636
1637
1638 /***********************************************************************
1639  *           InternetSetStatusCallbackA (WININET.@)
1640  *
1641  * Sets up a callback function which is called as progress is made
1642  * during an operation.
1643  *
1644  * RETURNS
1645  *    Previous callback or NULL         on success
1646  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
1647  *
1648  */
1649 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
1650         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
1651 {
1652     INTERNET_STATUS_CALLBACK retVal;
1653     LPWININETHANDLEHEADER lpwh;
1654
1655     TRACE("0x%08lx\n", (ULONG)hInternet);
1656     
1657     lpwh = WININET_GetObject(hInternet);
1658     if (!lpwh)
1659         return INTERNET_INVALID_STATUS_CALLBACK;
1660
1661     lpwh->dwInternalFlags &= ~INET_CALLBACKW;
1662     retVal = lpwh->lpfnStatusCB;
1663     lpwh->lpfnStatusCB = lpfnIntCB;
1664
1665     WININET_Release( lpwh );
1666
1667     return retVal;
1668 }
1669
1670 /***********************************************************************
1671  *           InternetSetStatusCallbackW (WININET.@)
1672  *
1673  * Sets up a callback function which is called as progress is made
1674  * during an operation.
1675  *
1676  * RETURNS
1677  *    Previous callback or NULL         on success
1678  *    INTERNET_INVALID_STATUS_CALLBACK  on failure
1679  *
1680  */
1681 INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(
1682         HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
1683 {
1684     INTERNET_STATUS_CALLBACK retVal;
1685     LPWININETHANDLEHEADER lpwh;
1686
1687     TRACE("0x%08lx\n", (ULONG)hInternet);
1688     
1689     lpwh = WININET_GetObject(hInternet);
1690     if (!lpwh)
1691         return INTERNET_INVALID_STATUS_CALLBACK;
1692
1693     lpwh->dwInternalFlags |= INET_CALLBACKW;
1694     retVal = lpwh->lpfnStatusCB;
1695     lpwh->lpfnStatusCB = lpfnIntCB;
1696
1697     WININET_Release( lpwh );
1698
1699     return retVal;
1700 }
1701
1702 /***********************************************************************
1703  *           InternetSetFilePointer (WININET.@)
1704  */
1705 DWORD WINAPI InternetSetFilePointer(HINTERNET hFile, LONG lDistanceToMove,
1706     PVOID pReserved, DWORD dwMoveContext, DWORD dwContext)
1707 {
1708     FIXME("stub\n");
1709     return FALSE;
1710 }
1711
1712 /***********************************************************************
1713  *           InternetWriteFile (WININET.@)
1714  *
1715  * Write data to an open internet file
1716  *
1717  * RETURNS
1718  *    TRUE  on success
1719  *    FALSE on failure
1720  *
1721  */
1722 BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer ,
1723         DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
1724 {
1725     BOOL retval = FALSE;
1726     int nSocket = -1;
1727     LPWININETHANDLEHEADER lpwh;
1728
1729     TRACE("\n");
1730     lpwh = (LPWININETHANDLEHEADER) WININET_GetObject( hFile );
1731     if (NULL == lpwh)
1732         return FALSE;
1733
1734     switch (lpwh->htype)
1735     {
1736         case WH_HHTTPREQ:
1737             {
1738                 LPWININETHTTPREQW lpwhr;
1739                 lpwhr = (LPWININETHTTPREQW)lpwh;
1740
1741                 TRACE("HTTPREQ %li\n",dwNumOfBytesToWrite);
1742                 retval = NETCON_send(&lpwhr->netConnection, lpBuffer, 
1743                         dwNumOfBytesToWrite, 0, (LPINT)lpdwNumOfBytesWritten);
1744
1745                 WININET_Release( lpwh );
1746                 return retval;
1747             }
1748             break;
1749
1750         case WH_HFILE:
1751             nSocket = ((LPWININETFILE)lpwh)->nDataSocket;
1752             break;
1753
1754         default:
1755             break;
1756     }
1757
1758     if (nSocket != -1)
1759     {
1760         int res = send(nSocket, lpBuffer, dwNumOfBytesToWrite, 0);
1761         retval = (res >= 0);
1762         *lpdwNumOfBytesWritten = retval ? res : 0;
1763     }
1764     WININET_Release( lpwh );
1765
1766     return retval;
1767 }
1768
1769
1770 static BOOL INTERNET_ReadFile(LPWININETHANDLEHEADER lpwh, LPVOID lpBuffer,
1771                               DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead,
1772                               BOOL bWait, BOOL bSendCompletionStatus)
1773 {
1774     BOOL retval = FALSE;
1775     int nSocket = -1;
1776
1777     /* FIXME: this should use NETCON functions! */
1778     switch (lpwh->htype)
1779     {
1780         case WH_HHTTPREQ:
1781             if (!NETCON_recv(&((LPWININETHTTPREQW)lpwh)->netConnection, lpBuffer,
1782                              dwNumOfBytesToRead, bWait ? MSG_WAITALL : 0, (int *)pdwNumOfBytesRead))
1783             {
1784                 *pdwNumOfBytesRead = 0;
1785                 retval = TRUE; /* Under windows, it seems to return 0 even if nothing was read... */
1786             }
1787             else
1788                 retval = TRUE;
1789             break;
1790
1791         case WH_HFILE:
1792             /* FIXME: FTP should use NETCON_ stuff */
1793             nSocket = ((LPWININETFILE)lpwh)->nDataSocket;
1794             if (nSocket != -1)
1795             {
1796                 int res = recv(nSocket, lpBuffer, dwNumOfBytesToRead, bWait ? MSG_WAITALL : 0);
1797                 retval = (res >= 0);
1798                 *pdwNumOfBytesRead = retval ? res : 0;
1799             }
1800             break;
1801
1802         default:
1803             break;
1804     }
1805
1806     if (bSendCompletionStatus)
1807     {
1808         INTERNET_ASYNC_RESULT iar;
1809
1810         iar.dwResult = retval;
1811         iar.dwError = iar.dwError = retval ? ERROR_SUCCESS :
1812                                              INTERNET_GetLastError();
1813
1814         INTERNET_SendCallback(lpwh, lpwh->dwContext,
1815                               INTERNET_STATUS_REQUEST_COMPLETE, &iar,
1816                               sizeof(INTERNET_ASYNC_RESULT));
1817     }
1818     return retval;
1819 }
1820
1821 /***********************************************************************
1822  *           InternetReadFile (WININET.@)
1823  *
1824  * Read data from an open internet file
1825  *
1826  * RETURNS
1827  *    TRUE  on success
1828  *    FALSE on failure
1829  *
1830  */
1831 BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
1832         DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
1833 {
1834     LPWININETHANDLEHEADER lpwh;
1835     BOOL retval;
1836
1837     TRACE("%p %p %ld %p\n", hFile, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead);
1838
1839     lpwh = WININET_GetObject( hFile );
1840     if (!lpwh)
1841     {
1842         SetLastError(ERROR_INVALID_HANDLE);
1843         return FALSE;
1844     }
1845
1846     retval = INTERNET_ReadFile(lpwh, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead, TRUE, FALSE);
1847     WININET_Release( lpwh );
1848
1849     TRACE("-- %s (bytes read: %ld)\n", retval ? "TRUE": "FALSE", pdwNumOfBytesRead ? *pdwNumOfBytesRead : -1);
1850     return retval;
1851 }
1852
1853 /***********************************************************************
1854  *           InternetReadFileExA (WININET.@)
1855  *
1856  * Read data from an open internet file
1857  *
1858  * PARAMS
1859  *  hFile         [I] Handle returned by InternetOpenUrl or HttpOpenRequest.
1860  *  lpBuffersOut  [I/O] Buffer.
1861  *  dwFlags       [I] Flags. See notes.
1862  *  dwContext     [I] Context for callbacks.
1863  *
1864  * RETURNS
1865  *    TRUE  on success
1866  *    FALSE on failure
1867  *
1868  * NOTES
1869  *  The parameter dwFlags include zero or more of the following flags:
1870  *|IRF_ASYNC - Makes the call asynchronous.
1871  *|IRF_SYNC - Makes the call synchronous.
1872  *|IRF_USE_CONTEXT - Forces dwContext to be used.
1873  *|IRF_NO_WAIT - Don't block if the data is not available, just return what is available.
1874  *
1875  * However, in testing IRF_USE_CONTEXT seems to have no effect - dwContext isn't used.
1876  *
1877  * SEE
1878  *  InternetOpenUrlA(), HttpOpenRequestA()
1879  */
1880 BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffersOut,
1881         DWORD dwFlags, DWORD dwContext)
1882 {
1883     BOOL retval = FALSE;
1884     LPWININETHANDLEHEADER lpwh;
1885
1886     TRACE("(%p %p 0x%lx 0x%lx)\n", hFile, lpBuffersOut, dwFlags, dwContext);
1887
1888     if (dwFlags & ~(IRF_ASYNC|IRF_NO_WAIT))
1889         FIXME("these dwFlags aren't implemented: 0x%lx\n", dwFlags & ~(IRF_ASYNC|IRF_NO_WAIT));
1890
1891     if (lpBuffersOut->dwStructSize != sizeof(*lpBuffersOut))
1892     {
1893         SetLastError(ERROR_INVALID_PARAMETER);
1894         return FALSE;
1895     }
1896
1897     lpwh = (LPWININETHANDLEHEADER) WININET_GetObject( hFile );
1898     if (!lpwh)
1899     {
1900         SetLastError(ERROR_INVALID_HANDLE);
1901         return FALSE;
1902     }
1903
1904     /* FIXME: native only does it asynchronously if the amount of data
1905      * requested isn't available. See NtReadFile. */
1906     /* FIXME: IRF_ASYNC may not be the right thing to test here;
1907      * hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC is probably better, but
1908      * we should implement the above first */
1909     if (dwFlags & IRF_ASYNC)
1910     {
1911         WORKREQUEST workRequest;
1912         struct WORKREQ_INTERNETREADFILEEXA *req;
1913
1914         workRequest.asyncall = INTERNETREADFILEEXA;
1915         workRequest.hdr = WININET_AddRef( lpwh );
1916         req = &workRequest.u.InternetReadFileExA;
1917         req->lpBuffersOut = lpBuffersOut;
1918
1919         retval = INTERNET_AsyncCall(&workRequest);
1920         if (!retval) return FALSE;
1921
1922         SetLastError(ERROR_IO_PENDING);
1923         return FALSE;
1924     }
1925
1926     retval = INTERNET_ReadFile(lpwh, lpBuffersOut->lpvBuffer,
1927         lpBuffersOut->dwBufferLength, &lpBuffersOut->dwBufferLength,
1928         !(dwFlags & IRF_NO_WAIT), FALSE);
1929
1930     WININET_Release( lpwh );
1931
1932     TRACE("-- %s (bytes read: %ld)\n", retval ? "TRUE": "FALSE", lpBuffersOut->dwBufferLength);
1933     return retval;
1934 }
1935
1936 /***********************************************************************
1937  *           InternetReadFileExW (WININET.@)
1938  *
1939  * Read data from an open internet file.
1940  *
1941  * PARAMS
1942  *  hFile         [I] Handle returned by InternetOpenUrl() or HttpOpenRequest().
1943  *  lpBuffersOut  [I/O] Buffer.
1944  *  dwFlags       [I] Flags.
1945  *  dwContext     [I] Context for callbacks.
1946  *
1947  * RETURNS
1948  *    FALSE, last error is set to ERROR_CALL_NOT_IMPLEMENTED
1949  *
1950  * NOTES
1951  *  Not implemented in Wine or native either (as of IE6 SP2).
1952  *
1953  */
1954 BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
1955         DWORD dwFlags, DWORD dwContext)
1956 {
1957   ERR("(%p, %p, 0x%lx, 0x%lx): not implemented in native\n", hFile, lpBuffer, dwFlags, dwContext);
1958
1959   INTERNET_SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1960   return FALSE;
1961 }
1962
1963 /***********************************************************************
1964  *           INET_QueryOptionHelper (internal)
1965  */
1966 static BOOL INET_QueryOptionHelper(BOOL bIsUnicode, HINTERNET hInternet, DWORD dwOption,
1967                                    LPVOID lpBuffer, LPDWORD lpdwBufferLength)
1968 {
1969     LPWININETHANDLEHEADER lpwhh;
1970     BOOL bSuccess = FALSE;
1971
1972     TRACE("(%p, 0x%08lx, %p, %p)\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
1973
1974     lpwhh = (LPWININETHANDLEHEADER) WININET_GetObject( hInternet );
1975
1976     switch (dwOption)
1977     {
1978         case INTERNET_OPTION_HANDLE_TYPE:
1979         {
1980             ULONG type;
1981
1982             if (!lpwhh)
1983             {
1984                 WARN("Invalid hInternet handle\n");
1985                 SetLastError(ERROR_INVALID_HANDLE);
1986                 return FALSE;
1987             }
1988
1989             type = lpwhh->htype;
1990
1991             TRACE("INTERNET_OPTION_HANDLE_TYPE: %ld\n", type);
1992
1993             if (*lpdwBufferLength < sizeof(ULONG))
1994                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
1995             else
1996             {
1997                 memcpy(lpBuffer, &type, sizeof(ULONG));
1998                 bSuccess = TRUE;
1999             }
2000             *lpdwBufferLength = sizeof(ULONG);
2001             break;
2002         }
2003
2004         case INTERNET_OPTION_REQUEST_FLAGS:
2005         {
2006             ULONG flags = 4;
2007             TRACE("INTERNET_OPTION_REQUEST_FLAGS: %ld\n", flags);
2008             if (*lpdwBufferLength < sizeof(ULONG))
2009                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2010             else
2011             {
2012                 memcpy(lpBuffer, &flags, sizeof(ULONG));
2013                 bSuccess = TRUE;
2014             }
2015             *lpdwBufferLength = sizeof(ULONG);
2016             break;
2017         }
2018
2019         case INTERNET_OPTION_URL:
2020         case INTERNET_OPTION_DATAFILE_NAME:
2021         {
2022             if (!lpwhh)
2023             {
2024                 WARN("Invalid hInternet handle\n");
2025                 SetLastError(ERROR_INVALID_HANDLE);
2026                 return FALSE;
2027             }
2028             if (lpwhh->htype == WH_HHTTPREQ)
2029             {
2030                 LPWININETHTTPREQW lpreq = (LPWININETHTTPREQW) lpwhh;
2031                 WCHAR url[1023];
2032                 static const WCHAR szFmt[] = {'h','t','t','p',':','/','/','%','s','%','s',0};
2033                 static const WCHAR szHost[] = {'H','o','s','t',0};
2034                 DWORD sizeRequired;
2035                 LPHTTPHEADERW Host;
2036
2037                 Host = HTTP_GetHeader(lpreq,szHost);
2038                 sprintfW(url,szFmt,Host->lpszValue,lpreq->lpszPath);
2039                 TRACE("INTERNET_OPTION_URL: %s\n",debugstr_w(url));
2040                 if(!bIsUnicode)
2041                 {
2042                     sizeRequired = WideCharToMultiByte(CP_ACP,0,url,-1,
2043                      lpBuffer,*lpdwBufferLength,NULL,NULL);
2044                     if (sizeRequired > *lpdwBufferLength)
2045                         INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2046                     else
2047                         bSuccess = TRUE;
2048                     *lpdwBufferLength = sizeRequired;
2049                 }
2050                 else
2051                 {
2052                     sizeRequired = (lstrlenW(url)+1) * sizeof(WCHAR);
2053                     if (*lpdwBufferLength < sizeRequired)
2054                         INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2055                     else
2056                     {
2057                         strcpyW(lpBuffer, url);
2058                         bSuccess = TRUE;
2059                     }
2060                     *lpdwBufferLength = sizeRequired;
2061                 }
2062             }
2063             break;
2064         }
2065         case INTERNET_OPTION_HTTP_VERSION:
2066         {
2067             if (*lpdwBufferLength < sizeof(HTTP_VERSION_INFO))
2068                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2069             else
2070             {
2071                 /*
2072                  * Presently hardcoded to 1.1
2073                  */
2074                 ((HTTP_VERSION_INFO*)lpBuffer)->dwMajorVersion = 1;
2075                 ((HTTP_VERSION_INFO*)lpBuffer)->dwMinorVersion = 1;
2076                 bSuccess = TRUE;
2077             }
2078             *lpdwBufferLength = sizeof(HTTP_VERSION_INFO);
2079             break;
2080         }
2081        case INTERNET_OPTION_CONNECTED_STATE:
2082        {
2083             DWORD *pdwConnectedState = (DWORD *)lpBuffer;
2084             FIXME("INTERNET_OPTION_CONNECTED_STATE: semi-stub\n");
2085
2086             if (*lpdwBufferLength < sizeof(*pdwConnectedState))
2087                  INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2088             else
2089             {
2090                 *pdwConnectedState = INTERNET_STATE_CONNECTED;
2091                 bSuccess = TRUE;
2092             }
2093             *lpdwBufferLength = sizeof(*pdwConnectedState);
2094             break;
2095         }
2096         case INTERNET_OPTION_PROXY:
2097         {
2098             LPWININETAPPINFOW lpwai = (LPWININETAPPINFOW)lpwhh;
2099             WININETAPPINFOW wai;
2100
2101             if (lpwai == NULL)
2102             {
2103                 TRACE("Getting global proxy info\n");
2104                 memset(&wai, 0, sizeof(WININETAPPINFOW));
2105                 INTERNET_ConfigureProxyFromReg( &wai );
2106                 lpwai = &wai;
2107             }
2108
2109             if (bIsUnicode)
2110             {
2111                 INTERNET_PROXY_INFOW *pPI = (INTERNET_PROXY_INFOW *)lpBuffer;
2112                 DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
2113
2114                 if (lpwai->lpszProxy)
2115                     proxyBytesRequired = (lstrlenW(lpwai->lpszProxy) + 1) *
2116                      sizeof(WCHAR);
2117                 if (lpwai->lpszProxyBypass)
2118                     proxyBypassBytesRequired =
2119                      (lstrlenW(lpwai->lpszProxyBypass) + 1) * sizeof(WCHAR);
2120                 if (*lpdwBufferLength < sizeof(INTERNET_PROXY_INFOW) +
2121                  proxyBytesRequired + proxyBypassBytesRequired)
2122                     INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2123                 else
2124                 {
2125                     pPI->dwAccessType = lpwai->dwAccessType;
2126                     if (lpwai->lpszProxy)
2127                     {
2128                         pPI->lpszProxy = (LPWSTR)((LPBYTE)lpBuffer +
2129                                                   sizeof(INTERNET_PROXY_INFOW));
2130                         lstrcpyW((LPWSTR)pPI->lpszProxy, lpwai->lpszProxy);
2131                     }
2132                     else
2133                     {
2134                         pPI->lpszProxy = (LPWSTR)((LPBYTE)lpBuffer +
2135                                                   sizeof(INTERNET_PROXY_INFOW));
2136                         *((LPWSTR)(pPI->lpszProxy)) = 0;
2137                     }
2138
2139                     if (lpwai->lpszProxyBypass)
2140                     {
2141                         pPI->lpszProxyBypass = (LPWSTR)((LPBYTE)lpBuffer +
2142                                                         sizeof(INTERNET_PROXY_INFOW) +
2143                                                         proxyBytesRequired);
2144                         lstrcpyW((LPWSTR)pPI->lpszProxyBypass,
2145                          lpwai->lpszProxyBypass);
2146                     }
2147                     else
2148                     {
2149                         pPI->lpszProxyBypass = (LPWSTR)((LPBYTE)lpBuffer +
2150                                                         sizeof(INTERNET_PROXY_INFOW) +
2151                                                         proxyBytesRequired);
2152                         *((LPWSTR)(pPI->lpszProxyBypass)) = 0;
2153                     }
2154                     bSuccess = TRUE;
2155                 }
2156                 *lpdwBufferLength = sizeof(INTERNET_PROXY_INFOW) +
2157                  proxyBytesRequired + proxyBypassBytesRequired;
2158             }
2159             else
2160             {
2161                 INTERNET_PROXY_INFOA *pPI = (INTERNET_PROXY_INFOA *)lpBuffer;
2162                 DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
2163
2164                 if (lpwai->lpszProxy)
2165                     proxyBytesRequired = WideCharToMultiByte(CP_ACP, 0,
2166                      lpwai->lpszProxy, -1, NULL, 0, NULL, NULL);
2167                 if (lpwai->lpszProxyBypass)
2168                     proxyBypassBytesRequired = WideCharToMultiByte(CP_ACP, 0,
2169                      lpwai->lpszProxyBypass, -1, NULL, 0, NULL, NULL);
2170                 if (*lpdwBufferLength < sizeof(INTERNET_PROXY_INFOA) +
2171                  proxyBytesRequired + proxyBypassBytesRequired)
2172                     INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2173                 else
2174                 {
2175                     pPI->dwAccessType = lpwai->dwAccessType;
2176                     if (lpwai->lpszProxy)
2177                     {
2178                         pPI->lpszProxy = (LPSTR)((LPBYTE)lpBuffer +
2179                                                  sizeof(INTERNET_PROXY_INFOA));
2180                         WideCharToMultiByte(CP_ACP, 0, lpwai->lpszProxy, -1,
2181                          (LPSTR)pPI->lpszProxy, proxyBytesRequired, NULL, NULL);
2182                     }
2183                     else
2184                     {
2185                         pPI->lpszProxy = (LPSTR)((LPBYTE)lpBuffer +
2186                                                  sizeof(INTERNET_PROXY_INFOA));
2187                         *((LPSTR)(pPI->lpszProxy)) = '\0';
2188                     }
2189                     
2190                     if (lpwai->lpszProxyBypass)
2191                     {
2192                         pPI->lpszProxyBypass = (LPSTR)((LPBYTE)lpBuffer +
2193                          sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired);
2194                         WideCharToMultiByte(CP_ACP, 0, lpwai->lpszProxyBypass,
2195                          -1, (LPSTR)pPI->lpszProxyBypass,
2196                          proxyBypassBytesRequired,
2197                          NULL, NULL);
2198                     }
2199                     else
2200                     {
2201                         pPI->lpszProxyBypass = (LPSTR)((LPBYTE)lpBuffer +
2202                                                        sizeof(INTERNET_PROXY_INFOA) +
2203                                                        proxyBytesRequired);
2204                         *((LPSTR)(pPI->lpszProxyBypass)) = '\0';
2205                     }
2206                     bSuccess = TRUE;
2207                 }
2208                 *lpdwBufferLength = sizeof(INTERNET_PROXY_INFOA) +
2209                  proxyBytesRequired + proxyBypassBytesRequired;
2210             }
2211             break;
2212         }
2213         case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2214         {
2215             ULONG conn = 2;
2216             TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER: %ld\n", conn);
2217             if (*lpdwBufferLength < sizeof(ULONG))
2218                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2219             else
2220             {
2221                 memcpy(lpBuffer, &conn, sizeof(ULONG));
2222                 bSuccess = TRUE;
2223             }
2224             *lpdwBufferLength = sizeof(ULONG);
2225             break;
2226         }
2227         case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2228         {
2229             ULONG conn = 4;
2230             TRACE("INTERNET_OPTION_MAX_CONNS_1_0_SERVER: %ld\n", conn);
2231             if (*lpdwBufferLength < sizeof(ULONG))
2232                 INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
2233             else
2234             {
2235                 memcpy(lpBuffer, &conn, sizeof(ULONG));
2236                 bSuccess = TRUE;
2237             }
2238             *lpdwBufferLength = sizeof(ULONG);
2239             break;
2240         }
2241         case INTERNET_OPTION_SECURITY_FLAGS:
2242             FIXME("INTERNET_OPTION_SECURITY_FLAGS: Stub\n");
2243             break;
2244
2245         default:
2246             FIXME("Stub! %ld\n", dwOption);
2247             break;
2248     }
2249     if (lpwhh)
2250         WININET_Release( lpwhh );
2251
2252     return bSuccess;
2253 }
2254
2255 /***********************************************************************
2256  *           InternetQueryOptionW (WININET.@)
2257  *
2258  * Queries an options on the specified handle
2259  *
2260  * RETURNS
2261  *    TRUE  on success
2262  *    FALSE on failure
2263  *
2264  */
2265 BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
2266                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
2267 {
2268     return INET_QueryOptionHelper(TRUE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
2269 }
2270
2271 /***********************************************************************
2272  *           InternetQueryOptionA (WININET.@)
2273  *
2274  * Queries an options on the specified handle
2275  *
2276  * RETURNS
2277  *    TRUE  on success
2278  *    FALSE on failure
2279  *
2280  */
2281 BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
2282                                  LPVOID lpBuffer, LPDWORD lpdwBufferLength)
2283 {
2284     return INET_QueryOptionHelper(FALSE, hInternet, dwOption, lpBuffer, lpdwBufferLength);
2285 }
2286
2287
2288 /***********************************************************************
2289  *           InternetSetOptionW (WININET.@)
2290  *
2291  * Sets an options on the specified handle
2292  *
2293  * RETURNS
2294  *    TRUE  on success
2295  *    FALSE on failure
2296  *
2297  */
2298 BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
2299                            LPVOID lpBuffer, DWORD dwBufferLength)
2300 {
2301     LPWININETHANDLEHEADER lpwhh;
2302     BOOL ret = TRUE;
2303
2304     TRACE("0x%08lx\n", dwOption);
2305
2306     lpwhh = (LPWININETHANDLEHEADER) WININET_GetObject( hInternet );
2307     if( !lpwhh )
2308         return FALSE;
2309
2310     switch (dwOption)
2311     {
2312     case INTERNET_OPTION_HTTP_VERSION:
2313       {
2314         HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
2315         FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
2316       }
2317       break;
2318     case INTERNET_OPTION_ERROR_MASK:
2319       {
2320         unsigned long flags=*(unsigned long*)lpBuffer;
2321         FIXME("Option INTERNET_OPTION_ERROR_MASK(%ld): STUB\n",flags);
2322       }
2323       break;
2324     case INTERNET_OPTION_CODEPAGE:
2325       {
2326         unsigned long codepage=*(unsigned long*)lpBuffer;
2327         FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n",codepage);
2328       }
2329       break;
2330     case INTERNET_OPTION_REQUEST_PRIORITY:
2331       {
2332         unsigned long priority=*(unsigned long*)lpBuffer;
2333         FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n",priority);
2334       }
2335       break;
2336     case INTERNET_OPTION_CONNECT_TIMEOUT:
2337       {
2338         unsigned long connecttimeout=*(unsigned long*)lpBuffer;
2339         FIXME("Option INTERNET_OPTION_CONNECT_TIMEOUT (%ld): STUB\n",connecttimeout);
2340       }
2341       break;
2342     case INTERNET_OPTION_DATA_RECEIVE_TIMEOUT:
2343       {
2344         unsigned long receivetimeout=*(unsigned long*)lpBuffer;
2345         FIXME("Option INTERNET_OPTION_DATA_RECEIVE_TIMEOUT (%ld): STUB\n",receivetimeout);
2346       }
2347       break;
2348     case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
2349       {
2350         unsigned long conns=*(unsigned long*)lpBuffer;
2351         FIXME("Option INTERNET_OPTION_MAX_CONNS_PER_SERVER (%ld): STUB\n",conns);
2352       }
2353       break;
2354     case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2355       {
2356         unsigned long conns=*(unsigned long*)lpBuffer;
2357         FIXME("Option INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER (%ld): STUB\n",conns);
2358       }
2359       break;
2360     case INTERNET_OPTION_RESET_URLCACHE_SESSION:
2361         FIXME("Option INTERNET_OPTION_RESET_URLCACHE_SESSION: STUB\n");
2362         break;
2363     case INTERNET_OPTION_END_BROWSER_SESSION:
2364         FIXME("Option INTERNET_OPTION_END_BROWSER_SESSION: STUB\n");
2365         break;
2366     case INTERNET_OPTION_CONNECTED_STATE:
2367         FIXME("Option INTERNET_OPTION_CONNECTED_STATE: STUB\n");
2368         break;
2369     case INTERNET_OPTION_DISABLE_PASSPORT_AUTH:
2370         TRACE("Option INTERNET_OPTION_DISABLE_PASSPORT_AUTH: harmless stub, since not enabled\n");
2371         break;
2372     case INTERNET_OPTION_RECEIVE_TIMEOUT:
2373         FIXME("Option INTERNET_OPTION_RECEIVE_TIMEOUT: STUB\n");
2374         break;
2375     case INTERNET_OPTION_SEND_TIMEOUT:
2376         FIXME("Option INTERNET_OPTION_SEND_TIMEOUT: STUB\n");
2377         break;
2378     case INTERNET_OPTION_CONNECT_RETRIES:
2379         FIXME("Option INTERNET_OPTION_CONNECT_RETRIES: STUB\n");
2380         break;
2381     default:
2382         FIXME("Option %ld STUB\n",dwOption);
2383         INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
2384         ret = FALSE;
2385         break;
2386     }
2387     WININET_Release( lpwhh );
2388
2389     return ret;
2390 }
2391
2392
2393 /***********************************************************************
2394  *           InternetSetOptionA (WININET.@)
2395  *
2396  * Sets an options on the specified handle.
2397  *
2398  * RETURNS
2399  *    TRUE  on success
2400  *    FALSE on failure
2401  *
2402  */
2403 BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
2404                            LPVOID lpBuffer, DWORD dwBufferLength)
2405 {
2406     LPVOID wbuffer;
2407     DWORD wlen;
2408     BOOL r;
2409
2410     switch( dwOption )
2411     {
2412     case INTERNET_OPTION_PROXY:
2413         {
2414         LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
2415         LPINTERNET_PROXY_INFOW piw;
2416         DWORD proxlen, prbylen;
2417         LPWSTR prox, prby;
2418
2419         proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
2420         prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
2421         wlen = sizeof(*piw) + proxlen + prbylen;
2422         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
2423         piw = (LPINTERNET_PROXY_INFOW) wbuffer;
2424         piw->dwAccessType = pi->dwAccessType;
2425         prox = (LPWSTR) &piw[1];
2426         prby = &prox[proxlen+1];
2427         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
2428         MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
2429         piw->lpszProxy = prox;
2430         piw->lpszProxyBypass = prby;
2431         }
2432         break;
2433     case INTERNET_OPTION_USER_AGENT:
2434     case INTERNET_OPTION_USERNAME:
2435     case INTERNET_OPTION_PASSWORD:
2436         wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
2437                                    NULL, 0 );
2438         wbuffer = HeapAlloc( GetProcessHeap(), 0, wlen*sizeof(WCHAR) );
2439         MultiByteToWideChar( CP_ACP, 0, lpBuffer, dwBufferLength,
2440                                    wbuffer, wlen );
2441         break;
2442     default:
2443         wbuffer = lpBuffer;
2444         wlen = dwBufferLength;
2445     }
2446
2447     r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);
2448
2449     if( lpBuffer != wbuffer )
2450         HeapFree( GetProcessHeap(), 0, wbuffer );
2451
2452     return r;
2453 }
2454
2455
2456 /***********************************************************************
2457  *           InternetSetOptionExA (WININET.@)
2458  */
2459 BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
2460                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
2461 {
2462     FIXME("Flags %08lx ignored\n", dwFlags);
2463     return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
2464 }
2465
2466 /***********************************************************************
2467  *           InternetSetOptionExW (WININET.@)
2468  */
2469 BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
2470                            LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
2471 {
2472     FIXME("Flags %08lx ignored\n", dwFlags);
2473     if( dwFlags & ~ISO_VALID_FLAGS )
2474     {
2475         SetLastError( ERROR_INVALID_PARAMETER );
2476         return FALSE;
2477     }
2478     return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
2479 }
2480
2481 static const WCHAR WININET_wkday[7][4] =
2482     { { 'S','u','n', 0 }, { 'M','o','n', 0 }, { 'T','u','e', 0 }, { 'W','e','d', 0 },
2483       { 'T','h','u', 0 }, { 'F','r','i', 0 }, { 'S','a','t', 0 } };
2484 static const WCHAR WININET_month[12][4] =
2485     { { 'J','a','n', 0 }, { 'F','e','b', 0 }, { 'M','a','r', 0 }, { 'A','p','r', 0 },
2486       { 'M','a','y', 0 }, { 'J','u','n', 0 }, { 'J','u','l', 0 }, { 'A','u','g', 0 },
2487       { 'S','e','p', 0 }, { 'O','c','t', 0 }, { 'N','o','v', 0 }, { 'D','e','c', 0 } };
2488
2489 /***********************************************************************
2490  *           InternetTimeFromSystemTimeA (WININET.@)
2491  */
2492 BOOL WINAPI InternetTimeFromSystemTimeA( const SYSTEMTIME* time, DWORD format, LPSTR string, DWORD size )
2493 {
2494     BOOL ret;
2495     WCHAR stringW[INTERNET_RFC1123_BUFSIZE];
2496
2497     TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
2498
2499     ret = InternetTimeFromSystemTimeW( time, format, stringW, sizeof(stringW) );
2500     if (ret) WideCharToMultiByte( CP_ACP, 0, stringW, -1, string, size, NULL, NULL );
2501
2502     return ret;
2503 }
2504
2505 /***********************************************************************
2506  *           InternetTimeFromSystemTimeW (WININET.@)
2507  */
2508 BOOL WINAPI InternetTimeFromSystemTimeW( const SYSTEMTIME* time, DWORD format, LPWSTR string, DWORD size )
2509 {
2510     static const WCHAR date[] =
2511         { '%','s',',',' ','%','0','2','d',' ','%','s',' ','%','4','d',' ','%','0',
2512           '2','d',':','%','0','2','d',':','%','0','2','d',' ','G','M','T', 0 };
2513
2514     TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
2515
2516     if (!time || !string) return FALSE;
2517
2518     if (format != INTERNET_RFC1123_FORMAT || size < INTERNET_RFC1123_BUFSIZE * sizeof(WCHAR))
2519         return FALSE;
2520
2521     sprintfW( string, date,
2522               WININET_wkday[time->wDayOfWeek],
2523               time->wDay,
2524               WININET_month[time->wMonth - 1],
2525               time->wYear,
2526               time->wHour,
2527               time->wMinute,
2528               time->wSecond );
2529
2530     return TRUE;
2531 }
2532
2533 /***********************************************************************
2534  *           InternetTimeToSystemTimeA (WININET.@)
2535  */
2536 BOOL WINAPI InternetTimeToSystemTimeA( LPCSTR string, SYSTEMTIME* time, DWORD reserved )
2537 {
2538     BOOL ret = FALSE;
2539     WCHAR *stringW;
2540     int len;
2541
2542     TRACE( "%s %p 0x%08lx\n", debugstr_a(string), time, reserved );
2543
2544     len = MultiByteToWideChar( CP_ACP, 0, string, -1, NULL, 0 );
2545     stringW = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
2546
2547     if (stringW)
2548     {
2549         MultiByteToWideChar( CP_ACP, 0, string, -1, stringW, len );
2550         ret = InternetTimeToSystemTimeW( stringW, time, reserved );
2551         HeapFree( GetProcessHeap(), 0, stringW );
2552     }
2553     return ret;
2554 }
2555
2556 /***********************************************************************
2557  *           InternetTimeToSystemTimeW (WININET.@)
2558  */
2559 BOOL WINAPI InternetTimeToSystemTimeW( LPCWSTR string, SYSTEMTIME* time, DWORD reserved )
2560 {
2561     unsigned int i;
2562     WCHAR *s = (LPWSTR)string;
2563
2564     TRACE( "%s %p 0x%08lx\n", debugstr_w(string), time, reserved );
2565
2566     if (!string || !time) return FALSE;
2567
2568     /* Windows does this too */
2569     GetSystemTime( time );
2570
2571     /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
2572      *  a SYSTEMTIME structure.
2573      */
2574
2575     while (*s && !isalphaW( *s )) s++;
2576     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
2577     time->wDayOfWeek = 7;
2578
2579     for (i = 0; i < 7; i++)
2580     {
2581         if (toupperW( WININET_wkday[i][0] ) == toupperW( s[0] ) &&
2582             toupperW( WININET_wkday[i][1] ) == toupperW( s[1] ) &&
2583             toupperW( WININET_wkday[i][2] ) == toupperW( s[2] ) )
2584         {
2585             time->wDayOfWeek = i;
2586             break;
2587         }
2588     }
2589
2590     if (time->wDayOfWeek > 6) return TRUE;
2591     while (*s && !isdigitW( *s )) s++;
2592     time->wDay = strtolW( s, &s, 10 );
2593
2594     while (*s && !isalphaW( *s )) s++;
2595     if (s[0] == '\0' || s[1] == '\0' || s[2] == '\0') return TRUE;
2596     time->wMonth = 0;
2597
2598     for (i = 0; i < 12; i++)
2599     {
2600         if (toupperW( WININET_month[i][0]) == toupperW( s[0] ) &&
2601             toupperW( WININET_month[i][1]) == toupperW( s[1] ) &&
2602             toupperW( WININET_month[i][2]) == toupperW( s[2] ) )
2603         {
2604             time->wMonth = i + 1;
2605             break;
2606         }
2607     }
2608     if (time->wMonth == 0) return TRUE;
2609
2610     while (*s && !isdigitW( *s )) s++;
2611     if (*s == '\0') return TRUE;
2612     time->wYear = strtolW( s, &s, 10 );
2613
2614     while (*s && !isdigitW( *s )) s++;
2615     if (*s == '\0') return TRUE;
2616     time->wHour = strtolW( s, &s, 10 );
2617
2618     while (*s && !isdigitW( *s )) s++;
2619     if (*s == '\0') return TRUE;
2620     time->wMinute = strtolW( s, &s, 10 );
2621
2622     while (*s && !isdigitW( *s )) s++;
2623     if (*s == '\0') return TRUE;
2624     time->wSecond = strtolW( s, &s, 10 );
2625
2626     time->wMilliseconds = 0;
2627     return TRUE;
2628 }
2629
2630 /***********************************************************************
2631  *      InternetCheckConnectionW (WININET.@)
2632  *
2633  * Pings a requested host to check internet connection
2634  *
2635  * RETURNS
2636  *   TRUE on success and FALSE on failure. If a failure then
2637  *   ERROR_NOT_CONNECTED is placed into GetLastError
2638  *
2639  */
2640 BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
2641 {
2642 /*
2643  * this is a kludge which runs the resident ping program and reads the output.
2644  *
2645  * Anyone have a better idea?
2646  */
2647
2648   BOOL   rc = FALSE;
2649   static const CHAR ping[] = "ping -w 1 ";
2650   static const CHAR redirect[] = " >/dev/null 2>/dev/null";
2651   CHAR *command = NULL;
2652   WCHAR hostW[1024];
2653   DWORD len;
2654   int status = -1;
2655
2656   FIXME("\n");
2657
2658   /*
2659    * Crack or set the Address
2660    */
2661   if (lpszUrl == NULL)
2662   {
2663      /*
2664       * According to the doc we are supost to use the ip for the next
2665       * server in the WnInet internal server database. I have
2666       * no idea what that is or how to get it.
2667       *
2668       * So someone needs to implement this.
2669       */
2670      FIXME("Unimplemented with URL of NULL\n");
2671      return TRUE;
2672   }
2673   else
2674   {
2675      URL_COMPONENTSW components;
2676
2677      ZeroMemory(&components,sizeof(URL_COMPONENTSW));
2678      components.lpszHostName = (LPWSTR)&hostW;
2679      components.dwHostNameLength = 1024;
2680
2681      if (!InternetCrackUrlW(lpszUrl,0,0,&components))
2682        goto End;
2683
2684      TRACE("host name : %s\n",debugstr_w(components.lpszHostName));
2685   }
2686
2687   /*
2688    * Build our ping command
2689    */
2690   len = WideCharToMultiByte(CP_UNIXCP, 0, hostW, -1, NULL, 0, NULL, NULL);
2691   command = HeapAlloc( GetProcessHeap(), 0, strlen(ping)+len+strlen(redirect) );
2692   strcpy(command,ping);
2693   WideCharToMultiByte(CP_UNIXCP, 0, hostW, -1, command+strlen(ping), len, NULL, NULL);
2694   strcat(command,redirect);
2695
2696   TRACE("Ping command is : %s\n",command);
2697
2698   status = system(command);
2699
2700   TRACE("Ping returned a code of %i\n",status);
2701
2702   /* Ping return code of 0 indicates success */
2703   if (status == 0)
2704      rc = TRUE;
2705
2706 End:
2707
2708   HeapFree( GetProcessHeap(), 0, command );
2709   if (rc == FALSE)
2710     SetLastError(ERROR_NOT_CONNECTED);
2711
2712   return rc;
2713 }
2714
2715
2716 /***********************************************************************
2717  *      InternetCheckConnectionA (WININET.@)
2718  *
2719  * Pings a requested host to check internet connection
2720  *
2721  * RETURNS
2722  *   TRUE on success and FALSE on failure. If a failure then
2723  *   ERROR_NOT_CONNECTED is placed into GetLastError
2724  *
2725  */
2726 BOOL WINAPI InternetCheckConnectionA(LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
2727 {
2728     WCHAR *szUrl;
2729     INT len;
2730     BOOL rc;
2731
2732     len = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0);
2733     if (!(szUrl = HeapAlloc(GetProcessHeap(), 0, len*sizeof(WCHAR))))
2734         return FALSE;
2735     MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, szUrl, len);
2736     rc = InternetCheckConnectionW(szUrl, dwFlags, dwReserved);
2737     HeapFree(GetProcessHeap(), 0, szUrl);
2738     
2739     return rc;
2740 }
2741
2742
2743 /**********************************************************
2744  *      INTERNET_InternetOpenUrlW (internal)
2745  *
2746  * Opens an URL
2747  *
2748  * RETURNS
2749  *   handle of connection or NULL on failure
2750  */
2751 HINTERNET WINAPI INTERNET_InternetOpenUrlW(LPWININETAPPINFOW hIC, LPCWSTR lpszUrl,
2752     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
2753 {
2754     URL_COMPONENTSW urlComponents;
2755     WCHAR protocol[32], hostName[MAXHOSTNAME], userName[1024];
2756     WCHAR password[1024], path[2048], extra[1024];
2757     HINTERNET client = NULL, client1 = NULL;
2758     
2759     TRACE("(%p, %s, %s, %08lx, %08lx, %08lx)\n", hIC, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
2760           dwHeadersLength, dwFlags, dwContext);
2761     
2762     urlComponents.dwStructSize = sizeof(URL_COMPONENTSW);
2763     urlComponents.lpszScheme = protocol;
2764     urlComponents.dwSchemeLength = 32;
2765     urlComponents.lpszHostName = hostName;
2766     urlComponents.dwHostNameLength = MAXHOSTNAME;
2767     urlComponents.lpszUserName = userName;
2768     urlComponents.dwUserNameLength = 1024;
2769     urlComponents.lpszPassword = password;
2770     urlComponents.dwPasswordLength = 1024;
2771     urlComponents.lpszUrlPath = path;
2772     urlComponents.dwUrlPathLength = 2048;
2773     urlComponents.lpszExtraInfo = extra;
2774     urlComponents.dwExtraInfoLength = 1024;
2775     if(!InternetCrackUrlW(lpszUrl, strlenW(lpszUrl), 0, &urlComponents))
2776         return NULL;
2777     switch(urlComponents.nScheme) {
2778     case INTERNET_SCHEME_FTP:
2779         if(urlComponents.nPort == 0)
2780             urlComponents.nPort = INTERNET_DEFAULT_FTP_PORT;
2781         client = FTP_Connect(hIC, hostName, urlComponents.nPort,
2782                              userName, password, dwFlags, dwContext, INET_OPENURL);
2783         if(client == NULL)
2784             break;
2785         client1 = FtpOpenFileW(client, path, GENERIC_READ, dwFlags, dwContext);
2786         if(client1 == NULL) {
2787             InternetCloseHandle(client);
2788             break;
2789         }
2790         break;
2791         
2792     case INTERNET_SCHEME_HTTP:
2793     case INTERNET_SCHEME_HTTPS: {
2794         static const WCHAR szStars[] = { '*','/','*', 0 };
2795         LPCWSTR accept[2] = { szStars, NULL };
2796         if(urlComponents.nPort == 0) {
2797             if(urlComponents.nScheme == INTERNET_SCHEME_HTTP)
2798                 urlComponents.nPort = INTERNET_DEFAULT_HTTP_PORT;
2799             else
2800                 urlComponents.nPort = INTERNET_DEFAULT_HTTPS_PORT;
2801         }
2802         /* FIXME: should use pointers, not handles, as handles are not thread-safe */
2803         client = HTTP_Connect(hIC, hostName, urlComponents.nPort,
2804                               userName, password, dwFlags, dwContext, INET_OPENURL);
2805         if(client == NULL)
2806             break;
2807         client1 = HttpOpenRequestW(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
2808         if(client1 == NULL) {
2809             InternetCloseHandle(client);
2810             break;
2811         }
2812         HttpAddRequestHeadersW(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
2813         if (!HttpSendRequestW(client1, NULL, 0, NULL, 0)) {
2814             InternetCloseHandle(client1);
2815             client1 = NULL;
2816             break;
2817         }
2818     }
2819     case INTERNET_SCHEME_GOPHER:
2820         /* gopher doesn't seem to be implemented in wine, but it's supposed
2821          * to be supported by InternetOpenUrlA. */
2822     default:
2823         break;
2824     }
2825
2826     TRACE(" %p <--\n", client1);
2827     
2828     return client1;
2829 }
2830
2831 /**********************************************************
2832  *      InternetOpenUrlW (WININET.@)
2833  *
2834  * Opens an URL
2835  *
2836  * RETURNS
2837  *   handle of connection or NULL on failure
2838  */
2839 HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
2840     LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
2841 {
2842     HINTERNET ret = NULL;
2843     LPWININETAPPINFOW hIC = NULL;
2844
2845     if (TRACE_ON(wininet)) {
2846         TRACE("(%p, %s, %s, %08lx, %08lx, %08lx)\n", hInternet, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
2847               dwHeadersLength, dwFlags, dwContext);
2848         TRACE("  flags :");
2849         dump_INTERNET_FLAGS(dwFlags);
2850     }
2851
2852     hIC = (LPWININETAPPINFOW) WININET_GetObject( hInternet );
2853     if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT) {
2854         INTERNET_SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
2855         goto lend;
2856     }
2857     
2858     if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) {
2859         WORKREQUEST workRequest;
2860         struct WORKREQ_INTERNETOPENURLW *req;
2861         
2862         workRequest.asyncall = INTERNETOPENURLW;
2863         workRequest.hdr = WININET_AddRef( &hIC->hdr );
2864         req = &workRequest.u.InternetOpenUrlW;
2865         if (lpszUrl)
2866             req->lpszUrl = WININET_strdupW(lpszUrl);
2867         else
2868             req->lpszUrl = 0;
2869         if (lpszHeaders)
2870             req->lpszHeaders = WININET_strdupW(lpszHeaders);
2871         else
2872             req->lpszHeaders = 0;
2873         req->dwHeadersLength = dwHeadersLength;
2874         req->dwFlags = dwFlags;
2875         req->dwContext = dwContext;
2876         
2877         INTERNET_AsyncCall(&workRequest);
2878         /*
2879          * This is from windows.
2880          */
2881         SetLastError(ERROR_IO_PENDING);
2882     } else {
2883         ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
2884     }
2885     
2886   lend:
2887     if( hIC )
2888         WININET_Release( &hIC->hdr );
2889     TRACE(" %p <--\n", ret);
2890     
2891     return ret;
2892 }
2893
2894 /**********************************************************
2895  *      InternetOpenUrlA (WININET.@)
2896  *
2897  * Opens an URL
2898  *
2899  * RETURNS
2900  *   handle of connection or NULL on failure
2901  */
2902 HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
2903     LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD dwContext)
2904 {
2905     HINTERNET rc = (HINTERNET)NULL;
2906
2907     INT lenUrl;
2908     INT lenHeaders = 0;
2909     LPWSTR szUrl = NULL;
2910     LPWSTR szHeaders = NULL;
2911
2912     TRACE("\n");
2913
2914     if(lpszUrl) {
2915         lenUrl = MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, NULL, 0 );
2916         szUrl = HeapAlloc(GetProcessHeap(), 0, lenUrl*sizeof(WCHAR));
2917         if(!szUrl)
2918             return (HINTERNET)NULL;
2919         MultiByteToWideChar(CP_ACP, 0, lpszUrl, -1, szUrl, lenUrl);
2920     }
2921     
2922     if(lpszHeaders) {
2923         lenHeaders = MultiByteToWideChar(CP_ACP, 0, lpszHeaders, dwHeadersLength, NULL, 0 );
2924         szHeaders = HeapAlloc(GetProcessHeap(), 0, lenHeaders*sizeof(WCHAR));
2925         if(!szHeaders) {
2926             HeapFree(GetProcessHeap(), 0, szUrl);
2927             return (HINTERNET)NULL;
2928         }
2929         MultiByteToWideChar(CP_ACP, 0, lpszHeaders, dwHeadersLength, szHeaders, lenHeaders);
2930     }
2931     
2932     rc = InternetOpenUrlW(hInternet, szUrl, szHeaders,
2933         lenHeaders, dwFlags, dwContext);
2934
2935     HeapFree(GetProcessHeap(), 0, szUrl);
2936     HeapFree(GetProcessHeap(), 0, szHeaders);
2937
2938     return rc;
2939 }
2940
2941
2942 /***********************************************************************
2943  *           INTERNET_SetLastError (internal)
2944  *
2945  * Set last thread specific error
2946  *
2947  * RETURNS
2948  *
2949  */
2950 void INTERNET_SetLastError(DWORD dwError)
2951 {
2952     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
2953
2954     SetLastError(dwError);
2955     if(lpwite)
2956         lpwite->dwError = dwError;
2957 }
2958
2959
2960 /***********************************************************************
2961  *           INTERNET_GetLastError (internal)
2962  *
2963  * Get last thread specific error
2964  *
2965  * RETURNS
2966  *
2967  */
2968 DWORD INTERNET_GetLastError(void)
2969 {
2970     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
2971     /* TlsGetValue clears last error, so set it again here */
2972     SetLastError(lpwite->dwError);
2973     return lpwite->dwError;
2974 }
2975
2976
2977 /***********************************************************************
2978  *           INTERNET_WorkerThreadFunc (internal)
2979  *
2980  * Worker thread execution function
2981  *
2982  * RETURNS
2983  *
2984  */
2985 static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
2986 {
2987     DWORD dwWaitRes;
2988
2989     while (1)
2990     {
2991         if(dwNumJobs > 0) {
2992             INTERNET_ExecuteWork();
2993             continue;
2994         }
2995         dwWaitRes = WaitForMultipleObjects(2, hEventArray, FALSE, MAX_IDLE_WORKER);
2996
2997         if (dwWaitRes == WAIT_OBJECT_0 + 1)
2998             INTERNET_ExecuteWork();
2999         else
3000             break;
3001
3002         InterlockedIncrement(&dwNumIdleThreads);
3003     }
3004
3005     InterlockedDecrement(&dwNumIdleThreads);
3006     InterlockedDecrement(&dwNumThreads);
3007     TRACE("Worker thread exiting\n");
3008     return TRUE;
3009 }
3010
3011
3012 /***********************************************************************
3013  *           INTERNET_InsertWorkRequest (internal)
3014  *
3015  * Insert work request into queue
3016  *
3017  * RETURNS
3018  *
3019  */
3020 static BOOL INTERNET_InsertWorkRequest(LPWORKREQUEST lpWorkRequest)
3021 {
3022     BOOL bSuccess = FALSE;
3023     LPWORKREQUEST lpNewRequest;
3024
3025     TRACE("\n");
3026
3027     lpNewRequest = HeapAlloc(GetProcessHeap(), 0, sizeof(WORKREQUEST));
3028     if (lpNewRequest)
3029     {
3030         memcpy(lpNewRequest, lpWorkRequest, sizeof(WORKREQUEST));
3031         lpNewRequest->prev = NULL;
3032
3033         EnterCriticalSection(&csQueue);
3034
3035         lpNewRequest->next = lpWorkQueueTail;
3036         if (lpWorkQueueTail)
3037             lpWorkQueueTail->prev = lpNewRequest;
3038         lpWorkQueueTail = lpNewRequest;
3039         if (!lpHeadWorkQueue)
3040             lpHeadWorkQueue = lpWorkQueueTail;
3041
3042         LeaveCriticalSection(&csQueue);
3043
3044         bSuccess = TRUE;
3045         InterlockedIncrement(&dwNumJobs);
3046     }
3047
3048     return bSuccess;
3049 }
3050
3051
3052 /***********************************************************************
3053  *           INTERNET_GetWorkRequest (internal)
3054  *
3055  * Retrieves work request from queue
3056  *
3057  * RETURNS
3058  *
3059  */
3060 static BOOL INTERNET_GetWorkRequest(LPWORKREQUEST lpWorkRequest)
3061 {
3062     BOOL bSuccess = FALSE;
3063     LPWORKREQUEST lpRequest = NULL;
3064
3065     TRACE("\n");
3066
3067     EnterCriticalSection(&csQueue);
3068
3069     if (lpHeadWorkQueue)
3070     {
3071         lpRequest = lpHeadWorkQueue;
3072         lpHeadWorkQueue = lpHeadWorkQueue->prev;
3073         if (lpRequest == lpWorkQueueTail)
3074             lpWorkQueueTail = lpHeadWorkQueue;
3075     }
3076
3077     LeaveCriticalSection(&csQueue);
3078
3079     if (lpRequest)
3080     {
3081         memcpy(lpWorkRequest, lpRequest, sizeof(WORKREQUEST));
3082         HeapFree(GetProcessHeap(), 0, lpRequest);
3083         bSuccess = TRUE;
3084         InterlockedDecrement(&dwNumJobs);
3085     }
3086
3087     return bSuccess;
3088 }
3089
3090
3091 /***********************************************************************
3092  *           INTERNET_AsyncCall (internal)
3093  *
3094  * Retrieves work request from queue
3095  *
3096  * RETURNS
3097  *
3098  */
3099 BOOL INTERNET_AsyncCall(LPWORKREQUEST lpWorkRequest)
3100 {
3101     HANDLE hThread;
3102     DWORD dwTID;
3103     BOOL bSuccess = FALSE;
3104
3105     TRACE("\n");
3106
3107     if (InterlockedDecrement(&dwNumIdleThreads) < 0)
3108     {
3109         InterlockedIncrement(&dwNumIdleThreads);
3110
3111         if (InterlockedIncrement(&dwNumThreads) > MAX_WORKER_THREADS ||
3112             !(hThread = CreateThread(NULL, 0,
3113             INTERNET_WorkerThreadFunc, NULL, 0, &dwTID)))
3114         {
3115             InterlockedDecrement(&dwNumThreads);
3116             INTERNET_SetLastError(ERROR_INTERNET_ASYNC_THREAD_FAILED);
3117             goto lerror;
3118         }
3119
3120         TRACE("Created new thread\n");
3121     }
3122
3123     bSuccess = TRUE;
3124     INTERNET_InsertWorkRequest(lpWorkRequest);
3125     SetEvent(hWorkEvent);
3126
3127 lerror:
3128
3129     return bSuccess;
3130 }
3131
3132
3133 /***********************************************************************
3134  *           INTERNET_ExecuteWork (internal)
3135  *
3136  * RETURNS
3137  *
3138  */
3139 static VOID INTERNET_ExecuteWork(void)
3140 {
3141     WORKREQUEST workRequest;
3142
3143     TRACE("\n");
3144
3145     if (!INTERNET_GetWorkRequest(&workRequest))
3146         return;
3147
3148     switch (workRequest.asyncall)
3149     {
3150     case FTPPUTFILEW:
3151         {
3152         struct WORKREQ_FTPPUTFILEW *req = &workRequest.u.FtpPutFileW;
3153         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3154
3155         TRACE("FTPPUTFILEW %p\n", lpwfs);
3156
3157         FTP_FtpPutFileW(lpwfs, req->lpszLocalFile,
3158                    req->lpszNewRemoteFile, req->dwFlags, req->dwContext);
3159
3160         HeapFree(GetProcessHeap(), 0, req->lpszLocalFile);
3161         HeapFree(GetProcessHeap(), 0, req->lpszNewRemoteFile);
3162         }
3163         break;
3164
3165     case FTPSETCURRENTDIRECTORYW:
3166         {
3167         struct WORKREQ_FTPSETCURRENTDIRECTORYW *req;
3168         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3169
3170         TRACE("FTPSETCURRENTDIRECTORYW %p\n", lpwfs);
3171
3172         req = &workRequest.u.FtpSetCurrentDirectoryW;
3173         FTP_FtpSetCurrentDirectoryW(lpwfs, req->lpszDirectory);
3174         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
3175         }
3176         break;
3177
3178     case FTPCREATEDIRECTORYW:
3179         {
3180         struct WORKREQ_FTPCREATEDIRECTORYW *req;
3181         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3182
3183         TRACE("FTPCREATEDIRECTORYW %p\n", lpwfs);
3184
3185         req = &workRequest.u.FtpCreateDirectoryW;
3186         FTP_FtpCreateDirectoryW(lpwfs, req->lpszDirectory);
3187         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
3188         }
3189         break;
3190
3191     case FTPFINDFIRSTFILEW:
3192         {
3193         struct WORKREQ_FTPFINDFIRSTFILEW *req;
3194         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3195
3196         TRACE("FTPFINDFIRSTFILEW %p\n", lpwfs);
3197
3198         req = &workRequest.u.FtpFindFirstFileW;
3199         FTP_FtpFindFirstFileW(lpwfs, req->lpszSearchFile,
3200            req->lpFindFileData, req->dwFlags, req->dwContext);
3201         HeapFree(GetProcessHeap(), 0, req->lpszSearchFile);
3202         }
3203         break;
3204
3205     case FTPGETCURRENTDIRECTORYW:
3206         {
3207         struct WORKREQ_FTPGETCURRENTDIRECTORYW *req;
3208         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3209
3210         TRACE("FTPGETCURRENTDIRECTORYW %p\n", lpwfs);
3211
3212         req = &workRequest.u.FtpGetCurrentDirectoryW;
3213         FTP_FtpGetCurrentDirectoryW(lpwfs,
3214                 req->lpszDirectory, req->lpdwDirectory);
3215         }
3216         break;
3217
3218     case FTPOPENFILEW:
3219         {
3220         struct WORKREQ_FTPOPENFILEW *req = &workRequest.u.FtpOpenFileW;
3221         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3222
3223         TRACE("FTPOPENFILEW %p\n", lpwfs);
3224
3225         FTP_FtpOpenFileW(lpwfs, req->lpszFilename,
3226             req->dwAccess, req->dwFlags, req->dwContext);
3227         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
3228         }
3229         break;
3230
3231     case FTPGETFILEW:
3232         {
3233         struct WORKREQ_FTPGETFILEW *req = &workRequest.u.FtpGetFileW;
3234         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3235
3236         TRACE("FTPGETFILEW %p\n", lpwfs);
3237
3238         FTP_FtpGetFileW(lpwfs, req->lpszRemoteFile,
3239                  req->lpszNewFile, req->fFailIfExists,
3240                  req->dwLocalFlagsAttribute, req->dwFlags, req->dwContext);
3241         HeapFree(GetProcessHeap(), 0, req->lpszRemoteFile);
3242         HeapFree(GetProcessHeap(), 0, req->lpszNewFile);
3243         }
3244         break;
3245
3246     case FTPDELETEFILEW:
3247         {
3248         struct WORKREQ_FTPDELETEFILEW *req = &workRequest.u.FtpDeleteFileW;
3249         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3250
3251         TRACE("FTPDELETEFILEW %p\n", lpwfs);
3252
3253         FTP_FtpDeleteFileW(lpwfs, req->lpszFilename);
3254         HeapFree(GetProcessHeap(), 0, req->lpszFilename);
3255         }
3256         break;
3257
3258     case FTPREMOVEDIRECTORYW:
3259         {
3260         struct WORKREQ_FTPREMOVEDIRECTORYW *req;
3261         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3262
3263         TRACE("FTPREMOVEDIRECTORYW %p\n", lpwfs);
3264
3265         req = &workRequest.u.FtpRemoveDirectoryW;
3266         FTP_FtpRemoveDirectoryW(lpwfs, req->lpszDirectory);
3267         HeapFree(GetProcessHeap(), 0, req->lpszDirectory);
3268         }
3269         break;
3270
3271     case FTPRENAMEFILEW:
3272         {
3273         struct WORKREQ_FTPRENAMEFILEW *req = &workRequest.u.FtpRenameFileW;
3274         LPWININETFTPSESSIONW lpwfs = (LPWININETFTPSESSIONW) workRequest.hdr;
3275
3276         TRACE("FTPRENAMEFILEW %p\n", lpwfs);
3277
3278         FTP_FtpRenameFileW(lpwfs, req->lpszSrcFile, req->lpszDestFile);
3279         HeapFree(GetProcessHeap(), 0, req->lpszSrcFile);
3280         HeapFree(GetProcessHeap(), 0, req->lpszDestFile);
3281         }
3282         break;
3283
3284     case INTERNETFINDNEXTW:
3285         {
3286         struct WORKREQ_INTERNETFINDNEXTW *req;
3287         LPWININETFINDNEXTW lpwh = (LPWININETFINDNEXTW) workRequest.hdr;
3288
3289         TRACE("INTERNETFINDNEXTW %p\n", lpwh);
3290
3291         req = &workRequest.u.InternetFindNextW;
3292         INTERNET_FindNextFileW(lpwh, req->lpFindFileData);
3293         }
3294         break;
3295
3296     case HTTPSENDREQUESTW:
3297         {
3298         struct WORKREQ_HTTPSENDREQUESTW *req = &workRequest.u.HttpSendRequestW;
3299         LPWININETHTTPREQW lpwhr = (LPWININETHTTPREQW) workRequest.hdr;
3300
3301         TRACE("HTTPSENDREQUESTW %p\n", lpwhr);
3302
3303         HTTP_HttpSendRequestW(lpwhr, req->lpszHeader,
3304                 req->dwHeaderLength, req->lpOptional, req->dwOptionalLength,
3305                 req->dwContentLength, req->bEndRequest);
3306
3307         HeapFree(GetProcessHeap(), 0, req->lpszHeader);
3308         }
3309         break;
3310
3311     case HTTPOPENREQUESTW:
3312         {
3313         struct WORKREQ_HTTPOPENREQUESTW *req = &workRequest.u.HttpOpenRequestW;
3314         LPWININETHTTPSESSIONW lpwhs = (LPWININETHTTPSESSIONW) workRequest.hdr;
3315
3316         TRACE("HTTPOPENREQUESTW %p\n", lpwhs);
3317
3318         HTTP_HttpOpenRequestW(lpwhs, req->lpszVerb,
3319             req->lpszObjectName, req->lpszVersion, req->lpszReferrer,
3320             req->lpszAcceptTypes, req->dwFlags, req->dwContext);
3321
3322         HeapFree(GetProcessHeap(), 0, req->lpszVerb);
3323         HeapFree(GetProcessHeap(), 0, req->lpszObjectName);
3324         HeapFree(GetProcessHeap(), 0, req->lpszVersion);
3325         HeapFree(GetProcessHeap(), 0, req->lpszReferrer);
3326         }
3327         break;
3328
3329     case SENDCALLBACK:
3330         {
3331         struct WORKREQ_SENDCALLBACK *req = &workRequest.u.SendCallback;
3332
3333         TRACE("SENDCALLBACK %p\n", workRequest.hdr);
3334
3335         INTERNET_SendCallback(workRequest.hdr,
3336                          req->dwContext, req->dwInternetStatus, req->lpvStatusInfo,
3337                          req->dwStatusInfoLength);
3338
3339         /* And frees the copy of the status info */
3340         HeapFree(GetProcessHeap(), 0, req->lpvStatusInfo);
3341         }
3342         break;
3343
3344     case INTERNETOPENURLW:
3345         {
3346         struct WORKREQ_INTERNETOPENURLW *req = &workRequest.u.InternetOpenUrlW;
3347         LPWININETAPPINFOW hIC = (LPWININETAPPINFOW) workRequest.hdr;
3348         
3349         TRACE("INTERNETOPENURLW %p\n", hIC);
3350
3351         INTERNET_InternetOpenUrlW(hIC, req->lpszUrl,
3352                                   req->lpszHeaders, req->dwHeadersLength, req->dwFlags, req->dwContext);
3353         HeapFree(GetProcessHeap(), 0, req->lpszUrl);
3354         HeapFree(GetProcessHeap(), 0, req->lpszHeaders);
3355         }
3356         break;
3357     case INTERNETREADFILEEXA:
3358         {
3359         struct WORKREQ_INTERNETREADFILEEXA *req = &workRequest.u.InternetReadFileExA;
3360
3361         TRACE("INTERNETREADFILEEXA %p\n", workRequest.hdr);
3362
3363         INTERNET_ReadFile(workRequest.hdr, req->lpBuffersOut->lpvBuffer,
3364             req->lpBuffersOut->dwBufferLength,
3365             &req->lpBuffersOut->dwBufferLength, TRUE, TRUE);
3366         }
3367         break;
3368     }
3369     WININET_Release( workRequest.hdr );
3370 }
3371
3372
3373 /***********************************************************************
3374  *          INTERNET_GetResponseBuffer  (internal)
3375  *
3376  * RETURNS
3377  *
3378  */
3379 LPSTR INTERNET_GetResponseBuffer(void)
3380 {
3381     LPWITHREADERROR lpwite = (LPWITHREADERROR)TlsGetValue(g_dwTlsErrIndex);
3382     TRACE("\n");
3383     return lpwite->response;
3384 }
3385
3386 /***********************************************************************
3387  *           INTERNET_GetNextLine  (internal)
3388  *
3389  * Parse next line in directory string listing
3390  *
3391  * RETURNS
3392  *   Pointer to beginning of next line
3393  *   NULL on failure
3394  *
3395  */
3396
3397 LPSTR INTERNET_GetNextLine(INT nSocket, LPDWORD dwLen)
3398 {
3399     struct timeval tv;
3400     fd_set infd;
3401     BOOL bSuccess = FALSE;
3402     INT nRecv = 0;
3403     LPSTR lpszBuffer = INTERNET_GetResponseBuffer();
3404
3405     TRACE("\n");
3406
3407     FD_ZERO(&infd);
3408     FD_SET(nSocket, &infd);
3409     tv.tv_sec=RESPONSE_TIMEOUT;
3410     tv.tv_usec=0;
3411
3412     while (nRecv < MAX_REPLY_LEN)
3413     {
3414         if (select(nSocket+1,&infd,NULL,NULL,&tv) > 0)
3415         {
3416             if (recv(nSocket, &lpszBuffer[nRecv], 1, 0) <= 0)
3417             {
3418                 INTERNET_SetLastError(ERROR_FTP_TRANSFER_IN_PROGRESS);
3419                 goto lend;
3420             }
3421
3422             if (lpszBuffer[nRecv] == '\n')
3423             {
3424                 bSuccess = TRUE;
3425                 break;
3426             }
3427             if (lpszBuffer[nRecv] != '\r')
3428                 nRecv++;
3429         }
3430         else
3431         {
3432             INTERNET_SetLastError(ERROR_INTERNET_TIMEOUT);
3433             goto lend;
3434         }
3435     }
3436
3437 lend:
3438     if (bSuccess)
3439     {
3440         lpszBuffer[nRecv] = '\0';
3441         *dwLen = nRecv - 1;
3442         TRACE(":%d %s\n", nRecv, lpszBuffer);
3443         return lpszBuffer;
3444     }
3445     else
3446     {
3447         return NULL;
3448     }
3449 }
3450
3451 /**********************************************************
3452  *      InternetQueryDataAvailable (WININET.@)
3453  *
3454  * Determines how much data is available to be read.
3455  *
3456  * RETURNS
3457  *   If there is data available then TRUE, otherwise if there
3458  *   is not or an error occurred then FALSE. Use GetLastError() to
3459  *   check for ERROR_NO_MORE_FILES to see if it was the former.
3460  */
3461 BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
3462                                 LPDWORD lpdwNumberOfBytesAvailble,
3463                                 DWORD dwFlags, DWORD dwConext)
3464 {
3465     LPWININETHTTPREQW lpwhr;
3466     BOOL retval = FALSE;
3467     char buffer[4048];
3468
3469     lpwhr = (LPWININETHTTPREQW) WININET_GetObject( hFile );
3470     if (NULL == lpwhr)
3471     {
3472         SetLastError(ERROR_NO_MORE_FILES);
3473         return FALSE;
3474     }
3475
3476     TRACE("-->  %p %i\n",lpwhr,lpwhr->hdr.htype);
3477
3478     switch (lpwhr->hdr.htype)
3479     {
3480     case WH_HHTTPREQ:
3481         if (!NETCON_recv(&lpwhr->netConnection, buffer,
3482                          4048, MSG_PEEK, (int *)lpdwNumberOfBytesAvailble))
3483         {
3484             SetLastError(ERROR_NO_MORE_FILES);
3485             retval = FALSE;
3486         }
3487         else
3488             retval = TRUE;
3489         break;
3490
3491     default:
3492         FIXME("unsupported file type\n");
3493         break;
3494     }
3495     WININET_Release( &lpwhr->hdr );
3496
3497     TRACE("<-- %i\n",retval);
3498     return retval;
3499 }
3500
3501
3502 /***********************************************************************
3503  *      InternetLockRequestFile (WININET.@)
3504  */
3505 BOOL WINAPI InternetLockRequestFile( HINTERNET hInternet, HANDLE
3506 *lphLockReqHandle)
3507 {
3508     FIXME("STUB\n");
3509     return FALSE;
3510 }
3511
3512 BOOL WINAPI InternetUnlockRequestFile( HANDLE hLockHandle)
3513 {
3514     FIXME("STUB\n");
3515     return FALSE;
3516 }
3517
3518
3519 /***********************************************************************
3520  *      InternetAutodial (WININET.@)
3521  *
3522  * On windows this function is supposed to dial the default internet
3523  * connection. We don't want to have Wine dial out to the internet so
3524  * we return TRUE by default. It might be nice to check if we are connected.
3525  *
3526  * RETURNS
3527  *   TRUE on success
3528  *   FALSE on failure
3529  *
3530  */
3531 BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
3532 {
3533     FIXME("STUB\n");
3534
3535     /* Tell that we are connected to the internet. */
3536     return TRUE;
3537 }
3538
3539 /***********************************************************************
3540  *      InternetAutodialHangup (WININET.@)
3541  *
3542  * Hangs up a connection made with InternetAutodial
3543  *
3544  * PARAM
3545  *    dwReserved
3546  * RETURNS
3547  *   TRUE on success
3548  *   FALSE on failure
3549  *
3550  */
3551 BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
3552 {
3553     FIXME("STUB\n");
3554
3555     /* we didn't dial, we don't disconnect */
3556     return TRUE;
3557 }
3558
3559 /***********************************************************************
3560  *      InternetCombineUrlA (WININET.@)
3561  *
3562  * Combine a base URL with a relative URL
3563  *
3564  * RETURNS
3565  *   TRUE on success
3566  *   FALSE on failure
3567  *
3568  */
3569
3570 BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
3571                                 LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
3572                                 DWORD dwFlags)
3573 {
3574     HRESULT hr=S_OK;
3575
3576     TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_a(lpszBaseUrl), debugstr_a(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
3577
3578     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
3579     dwFlags ^= ICU_NO_ENCODE;
3580     hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
3581
3582     return (hr==S_OK);
3583 }
3584
3585 /***********************************************************************
3586  *      InternetCombineUrlW (WININET.@)
3587  *
3588  * Combine a base URL with a relative URL
3589  *
3590  * RETURNS
3591  *   TRUE on success
3592  *   FALSE on failure
3593  *
3594  */
3595
3596 BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
3597                                 LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
3598                                 DWORD dwFlags)
3599 {
3600     HRESULT hr=S_OK;
3601
3602     TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_w(lpszBaseUrl), debugstr_w(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
3603
3604     /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
3605     dwFlags ^= ICU_NO_ENCODE;
3606     hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);
3607
3608     return (hr==S_OK);
3609 }
3610
3611 /* max port num is 65535 => 5 digits */
3612 #define MAX_WORD_DIGITS 5
3613
3614 /* we can calculate using ansi strings because we're just
3615  * calculating string length, not size
3616  */
3617 static BOOL calc_url_length(LPURL_COMPONENTSW lpUrlComponents,
3618                             LPDWORD lpdwUrlLength, LPDWORD lpdwSchemeLength)
3619 {
3620     static const WCHAR httpW[] = {'h','t','t','p',0};
3621
3622     *lpdwUrlLength = 0;
3623
3624     switch (lpUrlComponents->nScheme)
3625     {
3626         case INTERNET_SCHEME_FTP:
3627         case INTERNET_SCHEME_RES:
3628             *lpdwSchemeLength = 3;
3629             break;
3630         case INTERNET_SCHEME_HTTP:
3631         case INTERNET_SCHEME_FILE:
3632         case INTERNET_SCHEME_NEWS:
3633             *lpdwSchemeLength = 4;
3634             break;
3635
3636         default:
3637             *lpdwSchemeLength = 4;
3638             break;
3639     }
3640
3641     *lpdwUrlLength += *lpdwSchemeLength;
3642     *lpdwUrlLength += strlen("://");
3643
3644     if (lpUrlComponents->lpszUserName)
3645     {
3646         *lpdwUrlLength += lpUrlComponents->dwUserNameLength;
3647         *lpdwUrlLength += strlen("@");
3648     }
3649     else
3650     {
3651         if (lpUrlComponents->lpszPassword)
3652         {
3653             SetLastError(ERROR_INVALID_PARAMETER);
3654             return FALSE;
3655         }
3656     }
3657
3658     if (lpUrlComponents->lpszPassword)
3659     {
3660         *lpdwUrlLength += strlen(":");
3661         *lpdwUrlLength += lpUrlComponents->dwPasswordLength;
3662     }
3663
3664     *lpdwUrlLength += lpUrlComponents->dwHostNameLength;
3665
3666     if (lpUrlComponents->nPort != 80 ||
3667         (lpUrlComponents->lpszScheme && strncmpW(lpUrlComponents->lpszScheme, httpW, lpUrlComponents->dwSchemeLength)))
3668     {
3669         char szPort[MAX_WORD_DIGITS];
3670
3671         sprintf(szPort, "%d", lpUrlComponents->nPort);
3672         *lpdwUrlLength += strlen(szPort);
3673         *lpdwUrlLength += strlen(":");
3674     }
3675
3676     *lpdwUrlLength += lpUrlComponents->dwUrlPathLength;
3677     return TRUE;
3678 }
3679
3680 static void convert_urlcomp_atow(LPURL_COMPONENTSA lpUrlComponents, LPURL_COMPONENTSW urlCompW)
3681 {
3682     INT len;
3683
3684     ZeroMemory(urlCompW, sizeof(URL_COMPONENTSW));
3685
3686     urlCompW->dwStructSize = sizeof(URL_COMPONENTSW);
3687     urlCompW->dwSchemeLength = lpUrlComponents->dwSchemeLength;
3688     urlCompW->nScheme = lpUrlComponents->nScheme;
3689     urlCompW->dwHostNameLength = lpUrlComponents->dwHostNameLength;
3690     urlCompW->nPort = lpUrlComponents->nPort;
3691     urlCompW->dwUserNameLength = lpUrlComponents->dwUserNameLength;
3692     urlCompW->dwPasswordLength = lpUrlComponents->dwPasswordLength;
3693     urlCompW->dwUrlPathLength = lpUrlComponents->dwUrlPathLength;
3694     urlCompW->dwExtraInfoLength = lpUrlComponents->dwExtraInfoLength;
3695
3696     if (lpUrlComponents->lpszScheme)
3697     {
3698         len = lpUrlComponents->dwSchemeLength + 1;
3699         urlCompW->lpszScheme = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3700         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszScheme,
3701                             -1, urlCompW->lpszScheme, len);
3702     }
3703
3704     if (lpUrlComponents->lpszHostName)
3705     {
3706         len = lpUrlComponents->dwHostNameLength + 1;
3707         urlCompW->lpszHostName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3708         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszHostName,
3709                             -1, urlCompW->lpszHostName, len);
3710     }
3711
3712     if (lpUrlComponents->lpszUserName)
3713     {
3714         len = lpUrlComponents->dwUserNameLength + 1;
3715         urlCompW->lpszUserName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3716         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUserName,
3717                             -1, urlCompW->lpszUserName, len);
3718     }
3719
3720     if (lpUrlComponents->lpszPassword)
3721     {
3722         len = lpUrlComponents->dwPasswordLength + 1;
3723         urlCompW->lpszPassword = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3724         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszPassword,
3725                             -1, urlCompW->lpszPassword, len);
3726     }
3727
3728     if (lpUrlComponents->lpszUrlPath)
3729     {
3730         len = lpUrlComponents->dwUrlPathLength + 1;
3731         urlCompW->lpszUrlPath = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3732         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUrlPath,
3733                             -1, urlCompW->lpszUrlPath, len);
3734     }
3735
3736     if (lpUrlComponents->lpszExtraInfo)
3737     {
3738         len = lpUrlComponents->dwExtraInfoLength + 1;
3739         urlCompW->lpszExtraInfo = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
3740         MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszExtraInfo,
3741                             -1, urlCompW->lpszExtraInfo, len);
3742     }
3743 }
3744
3745 /***********************************************************************
3746  *      InternetCreateUrlA (WININET.@)
3747  *
3748  * RETURNS
3749  *   TRUE on success
3750  *   FALSE on failure
3751  *
3752  */
3753 BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
3754                                LPSTR lpszUrl, LPDWORD lpdwUrlLength)
3755 {
3756     BOOL ret;
3757     LPWSTR urlW = NULL;
3758     URL_COMPONENTSW urlCompW;
3759
3760     TRACE("(%p,%ld,%s,%p)\n", lpUrlComponents, dwFlags, debugstr_a(lpszUrl), lpdwUrlLength);
3761
3762     if (!lpUrlComponents)
3763         return FALSE;
3764
3765     if (lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
3766     {
3767         SetLastError(ERROR_INVALID_PARAMETER);
3768         return FALSE;
3769     }
3770
3771     convert_urlcomp_atow(lpUrlComponents, &urlCompW);
3772
3773     if (lpszUrl)
3774         urlW = HeapAlloc(GetProcessHeap(), 0, *lpdwUrlLength * sizeof(WCHAR));
3775
3776     ret = InternetCreateUrlW(&urlCompW, dwFlags, urlW, lpdwUrlLength);
3777
3778     /* on success, lpdwUrlLength points to the size of urlW in WCHARS
3779     * minus one, so add one to leave room for NULL terminator
3780     */
3781     if (ret)
3782         WideCharToMultiByte(CP_ACP, 0, urlW, -1, lpszUrl, *lpdwUrlLength + 1, NULL, NULL);
3783
3784     HeapFree(GetProcessHeap(), 0, urlCompW.lpszScheme);
3785     HeapFree(GetProcessHeap(), 0, urlCompW.lpszHostName);
3786     HeapFree(GetProcessHeap(), 0, urlCompW.lpszUserName);
3787     HeapFree(GetProcessHeap(), 0, urlCompW.lpszPassword);
3788     HeapFree(GetProcessHeap(), 0, urlCompW.lpszUrlPath);
3789     HeapFree(GetProcessHeap(), 0, urlCompW.lpszExtraInfo);
3790     HeapFree(GetProcessHeap(), 0, urlW);
3791
3792     return ret;
3793 }
3794
3795 /***********************************************************************
3796  *      InternetCreateUrlW (WININET.@)
3797  *
3798  * RETURNS
3799  *   TRUE on success
3800  *   FALSE on failure
3801  *
3802  */
3803 BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
3804                                LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
3805 {
3806     DWORD dwLen, dwSchemeLen;
3807
3808     static const WCHAR colonSlashW[] = {':','/','/',0};
3809     static const WCHAR httpW[] = {'h','t','t','p',0};
3810     static const WCHAR colonW[] = {':',0};
3811     static const WCHAR atW[] = {'@',0};
3812     static const WCHAR percentD[] = {'%','d',0};
3813
3814     TRACE("(%p,%ld,%s,%p)\n", lpUrlComponents, dwFlags, debugstr_w(lpszUrl), lpdwUrlLength);
3815
3816     if (!lpUrlComponents)
3817         return FALSE;
3818
3819     if (lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
3820     {
3821         SetLastError(ERROR_INVALID_PARAMETER);
3822         return FALSE;
3823     }
3824
3825     if (!calc_url_length(lpUrlComponents, &dwLen, &dwSchemeLen))
3826         return FALSE;
3827
3828     if (!lpszUrl || *lpdwUrlLength < dwLen)
3829     {
3830         *lpdwUrlLength = dwLen + 1; /* terminating null */
3831         SetLastError(ERROR_INSUFFICIENT_BUFFER);
3832         return FALSE;
3833     }
3834
3835     *lpdwUrlLength = dwLen;
3836     lpszUrl[0] = 0x00;
3837
3838     if (lpUrlComponents->lpszScheme)
3839         lstrcpynW(lpszUrl, lpUrlComponents->lpszScheme,
3840                   min(lpUrlComponents->dwSchemeLength, dwSchemeLen) + 1);
3841
3842     lstrcatW(lpszUrl, colonSlashW);
3843
3844     if (lpUrlComponents->lpszUserName)
3845     {
3846         if (!*lpUrlComponents->lpszUserName)
3847             return TRUE;
3848
3849         lstrcatW(lpszUrl, lpUrlComponents->lpszUserName);
3850
3851         if (lpUrlComponents->lpszPassword)
3852         {
3853             lstrcatW(lpszUrl, colonW);
3854
3855             if (!*lpUrlComponents->lpszPassword)
3856                 return TRUE;
3857             else
3858                 lstrcatW(lpszUrl, lpUrlComponents->lpszPassword);
3859         }
3860
3861         lstrcatW(lpszUrl, atW);
3862     }
3863
3864     lstrcatW(lpszUrl, lpUrlComponents->lpszHostName);
3865
3866     if (lpUrlComponents->nPort != 80 || (lpUrlComponents->lpszScheme &&
3867         strncmpW(lpUrlComponents->lpszScheme, httpW, lpUrlComponents->dwSchemeLength)))
3868     {
3869         WCHAR szPort[MAX_WORD_DIGITS];
3870
3871         sprintfW(szPort, percentD, lpUrlComponents->nPort);
3872         lstrcatW(lpszUrl, colonW);
3873         lstrcatW(lpszUrl, szPort);
3874     }
3875
3876     lstrcatW(lpszUrl, lpUrlComponents->lpszUrlPath);
3877
3878     return TRUE;
3879 }
3880
3881 /***********************************************************************
3882  *      InternetConfirmZoneCrossingA (WININET.@)
3883  *
3884  */
3885 DWORD WINAPI InternetConfirmZoneCrossingA( HWND hWnd, LPSTR szUrlPrev, LPSTR szUrlNew, BOOL bPost )
3886 {
3887     FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_a(szUrlPrev), debugstr_a(szUrlNew), bPost);
3888     return ERROR_SUCCESS;
3889 }
3890
3891 /***********************************************************************
3892  *      InternetConfirmZoneCrossingW (WININET.@)
3893  *
3894  */
3895 DWORD WINAPI InternetConfirmZoneCrossingW( HWND hWnd, LPWSTR szUrlPrev, LPWSTR szUrlNew, BOOL bPost )
3896 {
3897     FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_w(szUrlPrev), debugstr_w(szUrlNew), bPost);
3898     return ERROR_SUCCESS;
3899 }
3900
3901 DWORD WINAPI InternetDialA( HWND hwndParent, LPSTR lpszConnectoid, DWORD dwFlags,
3902                             LPDWORD lpdwConnection, DWORD dwReserved )
3903 {
3904     FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
3905           lpdwConnection, dwReserved);
3906     return ERROR_SUCCESS;
3907 }
3908
3909 DWORD WINAPI InternetDialW( HWND hwndParent, LPWSTR lpszConnectoid, DWORD dwFlags,
3910                             LPDWORD lpdwConnection, DWORD dwReserved )
3911 {
3912     FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
3913           lpdwConnection, dwReserved);
3914     return ERROR_SUCCESS;
3915 }
3916
3917 BOOL WINAPI InternetGoOnlineA( LPSTR lpszURL, HWND hwndParent, DWORD dwReserved )
3918 {
3919     FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_a(lpszURL), hwndParent, dwReserved);
3920     return TRUE;
3921 }
3922
3923 BOOL WINAPI InternetGoOnlineW( LPWSTR lpszURL, HWND hwndParent, DWORD dwReserved )
3924 {
3925     FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_w(lpszURL), hwndParent, dwReserved);
3926     return TRUE;
3927 }
3928
3929 DWORD WINAPI InternetHangUp( DWORD dwConnection, DWORD dwReserved )
3930 {
3931     FIXME("(0x%08lx, 0x%08lx) stub\n", dwConnection, dwReserved);
3932     return ERROR_SUCCESS;
3933 }
3934
3935 BOOL WINAPI CreateMD5SSOHash( PWSTR pszChallengeInfo, PWSTR pwszRealm, PWSTR pwszTarget,
3936                               PBYTE pbHexHash )
3937 {
3938     FIXME("(%s, %s, %s, %p) stub\n", debugstr_w(pszChallengeInfo), debugstr_w(pwszRealm),
3939           debugstr_w(pwszTarget), pbHexHash);
3940     return FALSE;
3941 }
3942
3943 BOOL WINAPI InternetClearAllPerSiteCookieDecisions( VOID )
3944 {
3945     FIXME("stub\n");
3946     return TRUE;
3947 }
3948
3949 BOOL WINAPI InternetEnumPerSiteCookieDecisionA( LPSTR pszSiteName, unsigned long *pcSiteNameSize,
3950                                                 unsigned long *pdwDecision, unsigned long dwIndex )
3951 {
3952     FIXME("(%s, %p, %p, 0x%08lx) stub\n",
3953           debugstr_a(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
3954     return FALSE;
3955 }
3956
3957 BOOL WINAPI InternetEnumPerSiteCookieDecisionW( LPWSTR pszSiteName, unsigned long *pcSiteNameSize,
3958                                                 unsigned long *pdwDecision, unsigned long dwIndex )
3959 {
3960     FIXME("(%s, %p, %p, 0x%08lx) stub\n",
3961           debugstr_w(pszSiteName), pcSiteNameSize, pdwDecision, dwIndex);
3962     return FALSE;
3963 }
3964
3965 BOOL WINAPI InternetGetCookieExA( LPCSTR pchURL, LPCSTR pchCookieName, LPSTR pchCookieData,
3966                                   LPDWORD pcchCookieData, DWORD dwFlags, LPVOID lpReserved)
3967 {
3968     FIXME("(%s, %s, %s, %p, 0x%08lx, %p) stub\n",
3969           debugstr_a(pchURL), debugstr_a(pchCookieName), debugstr_a(pchCookieData),
3970           pcchCookieData, dwFlags, lpReserved);
3971     return FALSE;
3972 }
3973
3974 BOOL WINAPI InternetGetCookieExW( LPCWSTR pchURL, LPCWSTR pchCookieName, LPWSTR pchCookieData,
3975                                   LPDWORD pcchCookieData, DWORD dwFlags, LPVOID lpReserved)
3976 {
3977     FIXME("(%s, %s, %s, %p, 0x%08lx, %p) stub\n",
3978           debugstr_w(pchURL), debugstr_w(pchCookieName), debugstr_w(pchCookieData),
3979           pcchCookieData, dwFlags, lpReserved);
3980     return FALSE;
3981 }
3982
3983 BOOL WINAPI InternetGetPerSiteCookieDecisionA( LPCSTR pwchHostName, unsigned long *pResult )
3984 {
3985     FIXME("(%s, %p) stub\n", debugstr_a(pwchHostName), pResult);
3986     return FALSE;
3987 }
3988
3989 BOOL WINAPI InternetGetPerSiteCookieDecisionW( LPCWSTR pwchHostName, unsigned long *pResult )
3990 {
3991     FIXME("(%s, %p) stub\n", debugstr_w(pwchHostName), pResult);
3992     return FALSE;
3993 }
3994
3995 BOOL WINAPI InternetSetPerSiteCookieDecisionA( LPCSTR pchHostName, DWORD dwDecision )
3996 {
3997     FIXME("(%s, 0x%08lx) stub\n", debugstr_a(pchHostName), dwDecision);
3998     return FALSE;
3999 }
4000
4001 BOOL WINAPI InternetSetPerSiteCookieDecisionW( LPCWSTR pchHostName, DWORD dwDecision )
4002 {
4003     FIXME("(%s, 0x%08lx) stub\n", debugstr_w(pchHostName), dwDecision);
4004     return FALSE;
4005 }
4006
4007 DWORD WINAPI InternetSetCookieExA( LPCSTR lpszURL, LPCSTR lpszCookieName, LPCSTR lpszCookieData,
4008                                    DWORD dwFlags, DWORD_PTR dwReserved)
4009 {
4010     FIXME("(%s, %s, %s, 0x%08lx, 0x%08lx) stub\n",
4011           debugstr_a(lpszURL), debugstr_a(lpszCookieName), debugstr_a(lpszCookieData),
4012           dwFlags, dwReserved);
4013     return TRUE;
4014 }
4015
4016 DWORD WINAPI InternetSetCookieExW( LPCWSTR lpszURL, LPCWSTR lpszCookieName, LPCWSTR lpszCookieData,
4017                                    DWORD dwFlags, DWORD_PTR dwReserved)
4018 {
4019     FIXME("(%s, %s, %s, 0x%08lx, 0x%08lx) stub\n",
4020           debugstr_w(lpszURL), debugstr_w(lpszCookieName), debugstr_w(lpszCookieData),
4021           dwFlags, dwReserved);
4022     return TRUE;
4023 }
4024
4025 BOOL WINAPI ResumeSuspendedDownload( HINTERNET hInternet, DWORD dwError )
4026 {
4027     FIXME("(%p, 0x%08lx) stub\n", hInternet, dwError);
4028     return FALSE;
4029 }