internet.c 142 KB
Newer Older
1 2 3 4
/*
 * Wininet
 *
 * Copyright 1999 Corel Corporation
5
 * Copyright 2002 CodeWeavers Inc.
6 7
 * Copyright 2002 Jaco Greeff
 * Copyright 2002 TransGaming Technologies Inc.
8
 * Copyright 2004 Mike McCormack for CodeWeavers
9 10
 *
 * Ulrich Czekalla
11
 * Aric Stewart
12
 * David Hammerton
13
 *
14 15 16 17 18 19 20 21 22 23 24 25
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
26
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 28
 */

29 30 31 32 33 34 35 36 37
#include "winsock2.h"
#include "ws2ipdef.h"

#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <assert.h>
38
#include <wchar.h>
39

40 41
#include "windef.h"
#include "winbase.h"
42
#include "winreg.h"
43
#include "winuser.h"
44
#include "wininet.h"
45
#include "winnls.h"
46
#include "wine/debug.h"
47
#include "winerror.h"
48
#define NO_SHLWAPI_STREAM
49
#include "shlwapi.h"
50 51 52 53
#include "ws2tcpip.h"
#include "winternl.h"
#include "iphlpapi.h"
#include "dhcpcsdk.h"
54 55

#include "internet.h"
56
#include "resource.h"
57

58
WINE_DEFAULT_DEBUG_CHANNEL(wininet);
59 60 61 62 63 64 65

typedef struct
{
    DWORD  dwError;
    CHAR   response[MAX_REPLY_LEN];
} WITHREADERROR, *LPWITHREADERROR;

66
static DWORD g_dwTlsErrIndex = TLS_OUT_OF_INDEXES;
67
HMODULE WININET_hModule;
68

69 70 71 72 73
static CRITICAL_SECTION WININET_cs;
static CRITICAL_SECTION_DEBUG WININET_cs_debug = 
{
    0, 0, &WININET_cs,
    { &WININET_cs_debug.ProcessLocksList, &WININET_cs_debug.ProcessLocksList },
74
      0, 0, { (DWORD_PTR)(__FILE__ ": WININET_cs") }
75 76 77
};
static CRITICAL_SECTION WININET_cs = { &WININET_cs_debug, -1, 0, 0, 0, 0 };

78 79 80
static object_header_t **handle_table;
static UINT_PTR next_handle;
static UINT_PTR handle_table_size;
81

82 83
typedef struct
{
84 85 86
    DWORD  proxyEnabled;
    LPWSTR proxy;
    LPWSTR proxyBypass;
87 88
    LPWSTR proxyUsername;
    LPWSTR proxyPassword;
89 90
} proxyinfo_t;

91
static ULONG max_conns = 2, max_1_0_conns = 4;
92
static ULONG connect_timeout = 60000;
93

94
static const WCHAR szInternetSettings[] =
95
    L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
96

97
void *alloc_object(object_header_t *parent, const object_vtbl_t *vtbl, size_t size)
98
{
99
    UINT_PTR handle = 0, num;
100 101 102
    object_header_t *ret;
    object_header_t **p;
    BOOL res = TRUE;
103

104
    ret = calloc(1, size);
105 106 107 108
    if(!ret)
        return NULL;

    list_init(&ret->children);
109

110
    EnterCriticalSection( &WININET_cs );
111 112 113

    if(!handle_table_size) {
        num = 16;
114
        p = calloc(num, sizeof(handle_table[0]));
115 116 117 118 119
        if(p) {
            handle_table = p;
            handle_table_size = num;
            next_handle = 1;
        }else {
120
            res = FALSE;
121 122 123
        }
    }else if(next_handle == handle_table_size) {
        num = handle_table_size * 2;
124
        p = realloc(handle_table, sizeof(handle_table[0]) * num);
125
        if(p) {
126
            memset(p + handle_table_size, 0, sizeof(handle_table[0]) * handle_table_size);
127 128 129
            handle_table = p;
            handle_table_size = num;
        }else {
130
            res = FALSE;
131 132 133
        }
    }

134
    if(res) {
135 136 137
        handle = next_handle;
        if(handle_table[handle])
            ERR("handle isn't free but should be\n");
138 139
        handle_table[handle] = ret;
        ret->valid_handle = TRUE;
140

141
        while(next_handle < handle_table_size && handle_table[next_handle])
142 143
            next_handle++;
    }
144

145 146
    LeaveCriticalSection( &WININET_cs );

147
    if(!res) {
148
        free(ret);
149 150 151 152 153 154 155 156 157 158 159 160 161
        return NULL;
    }

    ret->vtbl = vtbl;
    ret->refs = 1;
    ret->hInternet = (HINTERNET)handle;

    if(parent) {
        ret->lpfnStatusCB = parent->lpfnStatusCB;
        ret->dwInternalFlags = parent->dwInternalFlags & INET_CALLBACKW;
    }

    return ret;
162 163
}

164
object_header_t *WININET_AddRef( object_header_t *info )
165
{
166
    ULONG refs = InterlockedIncrement(&info->refs);
167
    TRACE("%p -> refcount = %ld\n", info, refs );
168 169 170
    return info;
}

171
object_header_t *get_handle_object( HINTERNET hinternet )
172
{
173
    object_header_t *info = NULL;
174
    UINT_PTR handle = (UINT_PTR) hinternet;
175 176 177

    EnterCriticalSection( &WININET_cs );

178
    if(handle > 0 && handle < handle_table_size && handle_table[handle] && handle_table[handle]->valid_handle)
179
        info = WININET_AddRef(handle_table[handle]);
180 181 182

    LeaveCriticalSection( &WININET_cs );

183
    TRACE("handle %Id -> %p\n", handle, info);
184 185 186 187

    return info;
}

188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
static void invalidate_handle(object_header_t *info)
{
    object_header_t *child, *next;

    if(!info->valid_handle)
        return;
    info->valid_handle = FALSE;

    /* Free all children as native does */
    LIST_FOR_EACH_ENTRY_SAFE( child, next, &info->children, object_header_t, entry )
    {
        TRACE("invalidating child handle %p for parent %p\n", child->hInternet, info);
        invalidate_handle( child );
    }

    WININET_Release(info);
}

206
BOOL WININET_Release( object_header_t *info )
207
{
208
    ULONG refs = InterlockedDecrement(&info->refs);
209
    TRACE( "object %p refcount = %ld\n", info, refs );
210
    if( !refs )
211
    {
212
        invalidate_handle(info);
213
        if ( info->vtbl->CloseConnection )
214 215
        {
            TRACE( "closing connection %p\n", info);
216
            info->vtbl->CloseConnection( info );
217
        }
218
        /* Don't send a callback if this is a session handle created with InternetOpenUrl */
219 220
        if ((info->htype != WH_HHTTPSESSION && info->htype != WH_HFTPSESSION)
            || !(info->dwInternalFlags & INET_OPENURL))
221 222 223 224 225
        {
            INTERNET_SendCallback(info, info->dwContext,
                                  INTERNET_STATUS_HANDLE_CLOSING, &info->hInternet,
                                  sizeof(HINTERNET));
        }
226
        TRACE( "destroying object %p\n", info);
227 228
        if ( info->htype != WH_HINIT )
            list_remove( &info->entry );
229
        info->vtbl->Destroy( info );
230

231 232
        if(info->hInternet) {
            UINT_PTR handle = (UINT_PTR)info->hInternet;
233

234
            EnterCriticalSection( &WININET_cs );
235

236
            handle_table[handle] = NULL;
237 238
            if(next_handle > handle)
                next_handle = handle;
239

240
            LeaveCriticalSection( &WININET_cs );
241
        }
242

243
        free(info);
244
    }
245
    return TRUE;
246 247
}

248
/***********************************************************************
249
 * DllMain [Internal] Initializes the internal 'WININET.DLL'.
250 251
 *
 * PARAMS
Andreas Mohr's avatar
Andreas Mohr committed
252
 *     hinstDLL    [I] handle to the DLL's instance
253
 *     fdwReason   [I]
Andreas Mohr's avatar
Andreas Mohr committed
254
 *     lpvReserved [I] reserved, must be NULL
255 256 257 258 259 260
 *
 * RETURNS
 *     Success: TRUE
 *     Failure: FALSE
 */

261
BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
262
{
263
    TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved);
264 265 266 267 268 269

    switch (fdwReason) {
        case DLL_PROCESS_ATTACH:

            g_dwTlsErrIndex = TlsAlloc();

270 271
            if (g_dwTlsErrIndex == TLS_OUT_OF_INDEXES)
                return FALSE;
272

273 274 275 276 277
            if(!init_urlcache())
            {
                TlsFree(g_dwTlsErrIndex);
                return FALSE;
            }
278

279
            WININET_hModule = hinstDLL;
280
            break;
281

282
        case DLL_THREAD_ATTACH:
283
            break;
284 285

        case DLL_THREAD_DETACH:
286 287
            if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
            {
288
                free(TlsGetValue(g_dwTlsErrIndex));
289 290
            }
            break;
291 292

        case DLL_PROCESS_DETACH:
293
            if (lpvReserved) break;
294
            collect_connections(COLLECT_CLEANUP);
295
            NETCON_unload();
296
            free_urlcache();
297
            free_cookie();
298

299 300
            if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
            {
301
                free(TlsGetValue(g_dwTlsErrIndex));
302 303
                TlsFree(g_dwTlsErrIndex);
            }
304 305 306 307 308
            break;
    }
    return TRUE;
}

309 310 311 312 313 314 315 316 317
/***********************************************************************
 *		DllInstall (WININET.@)
 */
HRESULT WINAPI DllInstall(BOOL bInstall, LPCWSTR cmdline)
{
    FIXME("(%x %s): stub\n", bInstall, debugstr_w(cmdline));
    return S_OK;
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
/***********************************************************************
 *           INTERNET_SaveProxySettings
 *
 * Stores the proxy settings given by lpwai into the registry
 *
 * RETURNS
 *     ERROR_SUCCESS if no error, or error code on fail
 */
static LONG INTERNET_SaveProxySettings( proxyinfo_t *lpwpi )
{
    HKEY key;
    LONG ret;

    if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
        return ret;

334
    if ((ret = RegSetValueExW( key, L"ProxyEnable", 0, REG_DWORD, (BYTE*)&lpwpi->proxyEnabled, sizeof(DWORD))))
335 336 337 338 339
    {
        RegCloseKey( key );
        return ret;
    }

340
    if (lpwpi->proxy)
341
    {
342
        if ((ret = RegSetValueExW( key, L"ProxyServer", 0, REG_SZ, (BYTE*)lpwpi->proxy, sizeof(WCHAR) * (lstrlenW(lpwpi->proxy) + 1))))
343 344 345 346 347 348 349
        {
            RegCloseKey( key );
            return ret;
        }
    }
    else
    {
350
        if ((ret = RegDeleteValueW( key, L"ProxyServer" )) && ret != ERROR_FILE_NOT_FOUND)
351 352 353 354 355 356 357 358 359
        {
            RegCloseKey( key );
            return ret;
        }
    }

    RegCloseKey(key);
    return ERROR_SUCCESS;
}
360

361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
/***********************************************************************
 *           INTERNET_FindProxyForProtocol
 *
 * Searches the proxy string for a proxy of the given protocol.
 * Returns the found proxy, or the default proxy if none of the given
 * protocol is found.
 *
 * PARAMETERS
 *     szProxy       [In]     proxy string to search
 *     proto         [In]     protocol to search for, e.g. "http"
 *     foundProxy    [Out]    found proxy
 *     foundProxyLen [In/Out] length of foundProxy buffer, in WCHARs
 *
 * RETURNS
 *     TRUE if a proxy is found, FALSE if not.  If foundProxy is too short,
 *     *foundProxyLen is set to the required size in WCHARs, including the
 *     NULL terminator, and the last error is set to ERROR_INSUFFICIENT_BUFFER.
 */
379
WCHAR *INTERNET_FindProxyForProtocol(LPCWSTR szProxy, LPCWSTR proto)
380
{
381 382
    WCHAR *ret = NULL;
    const WCHAR *ptr;
383 384 385 386

    TRACE("(%s, %s)\n", debugstr_w(szProxy), debugstr_w(proto));

    /* First, look for the specified protocol (proto=scheme://host:port) */
387
    for (ptr = szProxy; ptr && *ptr; )
388 389 390
    {
        LPCWSTR end, equal;

391 392 393 394 395
        if (!(end = wcschr(ptr, ' ')))
            end = ptr + lstrlenW(ptr);
        if ((equal = wcschr(ptr, '=')) && equal < end &&
             equal - ptr == lstrlenW(proto) &&
             !wcsnicmp(proto, ptr, lstrlenW(proto)))
396
        {
397
            ret = strndupW(equal + 1, end - equal - 1);
398 399
            TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
            return ret;
400 401 402 403 404 405
        }
        if (*end == ' ')
            ptr = end + 1;
        else
            ptr = end;
    }
406 407 408

    /* It wasn't found: look for no protocol */
    for (ptr = szProxy; ptr && *ptr; )
409
    {
410
        LPCWSTR end;
411

412 413 414
        if (!(end = wcschr(ptr, ' ')))
            end = ptr + lstrlenW(ptr);
        if (!wcschr(ptr, '='))
415
        {
416
            ret = strndupW(ptr, end - ptr);
417 418
            TRACE("found proxy for %s: %s\n", debugstr_w(proto), debugstr_w(ret));
            return ret;
419
        }
420 421 422 423
        if (*end == ' ')
            ptr = end + 1;
        else
            ptr = end;
424
    }
425 426

    return NULL;
427 428
}

429 430 431 432 433 434 435 436 437 438 439 440
/***********************************************************************
 *           InternetInitializeAutoProxyDll   (WININET.@)
 *
 * Setup the internal proxy
 *
 * PARAMETERS
 *     dwReserved
 *
 * RETURNS
 *     FALSE on failure
 *
 */
441
BOOL WINAPI InternetInitializeAutoProxyDll(DWORD dwReserved)
442 443
{
    FIXME("STUB\n");
444
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
445 446 447
    return FALSE;
}

448 449 450 451 452 453 454 455 456 457 458 459 460
/***********************************************************************
 *           DetectAutoProxyUrl   (WININET.@)
 *
 * Auto detect the proxy url
 *
 * RETURNS
 *     FALSE on failure
 *
 */
BOOL WINAPI DetectAutoProxyUrl(LPSTR lpszAutoProxyUrl,
	DWORD dwAutoProxyUrlLength, DWORD dwDetectFlags)
{
    FIXME("STUB\n");
461
    SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
462 463 464
    return FALSE;
}

465 466
static void FreeProxyInfo( proxyinfo_t *lpwpi )
{
467 468 469 470
    free(lpwpi->proxy);
    free(lpwpi->proxyBypass);
    free(lpwpi->proxyUsername);
    free(lpwpi->proxyPassword);
471
}
472

473 474 475 476 477 478 479 480
static proxyinfo_t *global_proxy;

static void free_global_proxy( void )
{
    EnterCriticalSection( &WININET_cs );
    if (global_proxy)
    {
        FreeProxyInfo( global_proxy );
481
        free( global_proxy );
482 483 484 485
    }
    LeaveCriticalSection( &WININET_cs );
}

486 487
static BOOL parse_proxy_url( proxyinfo_t *info, const WCHAR *url )
{
488 489 490 491 492
    URL_COMPONENTSW uc = {sizeof(uc)};

    uc.dwHostNameLength = 1;
    uc.dwUserNameLength = 1;
    uc.dwPasswordLength = 1;
493 494

    if (!InternetCrackUrlW( url, 0, 0, &uc )) return FALSE;
495
    if (!uc.dwHostNameLength)
496
    {
497
        if (!(info->proxy = wcsdup( url ))) return FALSE;
498 499 500 501
        info->proxyUsername = NULL;
        info->proxyPassword = NULL;
        return TRUE;
    }
502
    if (!(info->proxy = malloc( (uc.dwHostNameLength + 12) * sizeof(WCHAR) ))) return FALSE;
503
    swprintf( info->proxy, uc.dwHostNameLength + 12, L"%.*s:%u", uc.dwHostNameLength, uc.lpszHostName, uc.nPort );
504

505
    if (!uc.dwUserNameLength) info->proxyUsername = NULL;
506
    else if (!(info->proxyUsername = strndupW( uc.lpszUserName, uc.dwUserNameLength )))
507
    {
508
        free( info->proxy );
509 510
        return FALSE;
    }
511
    if (!uc.dwPasswordLength) info->proxyPassword = NULL;
512
    else if (!(info->proxyPassword = strndupW( uc.lpszPassword, uc.dwPasswordLength )))
513
    {
514 515
        free( info->proxyUsername );
        free( info->proxy );
516 517 518 519 520
        return FALSE;
    }
    return TRUE;
}

521
/***********************************************************************
522 523
 *          INTERNET_LoadProxySettings
 *
524 525
 * Loads proxy information from process-wide global settings, the registry,
 * or the environment into lpwpi.
526 527
 *
 * The caller should call FreeProxyInfo when done with lpwpi.
528 529 530 531 532
 *
 * FIXME:
 * The proxy may be specified in the form 'http=proxy.my.org'
 * Presumably that means there can be ftp=ftpproxy.my.org too.
 */
533
static LONG INTERNET_LoadProxySettings( proxyinfo_t *lpwpi )
534 535
{
    HKEY key;
536
    DWORD type, len;
537
    const WCHAR *envproxy;
538
    LONG ret;
539

540 541
    memset( lpwpi, 0, sizeof(*lpwpi) );

542 543 544 545
    EnterCriticalSection( &WININET_cs );
    if (global_proxy)
    {
        lpwpi->proxyEnabled = global_proxy->proxyEnabled;
546 547
        lpwpi->proxy = wcsdup( global_proxy->proxy );
        lpwpi->proxyBypass = wcsdup( global_proxy->proxyBypass );
548 549 550
    }
    LeaveCriticalSection( &WININET_cs );

551
    if ((ret = RegOpenKeyW( HKEY_CURRENT_USER, szInternetSettings, &key )))
552 553
    {
        FreeProxyInfo( lpwpi );
554
        return ret;
555
    }
556

557
    len = sizeof(DWORD);
558
    if (RegQueryValueExW( key, L"ProxyEnable", NULL, &type, (BYTE *)&lpwpi->proxyEnabled, &len ) || type != REG_DWORD)
559
    {
560
        lpwpi->proxyEnabled = 0;
561
        if((ret = RegSetValueExW( key, L"ProxyEnable", 0, REG_DWORD, (BYTE *)&lpwpi->proxyEnabled, sizeof(DWORD) )))
562
        {
563
            FreeProxyInfo( lpwpi );
564 565 566 567
            RegCloseKey( key );
            return ret;
        }
    }
568

569
    if (!(envproxy = _wgetenv( L"http_proxy" )) || lpwpi->proxyEnabled)
570
    {
571
        /* figure out how much memory the proxy setting takes */
572
        if (!RegQueryValueExW( key, L"ProxyServer", NULL, &type, NULL, &len ) && len && (type == REG_SZ))
573
        {
574
            LPWSTR szProxy, p;
575

576
            if (!(szProxy = malloc( len )))
577
            {
578
                RegCloseKey( key );
579
                FreeProxyInfo( lpwpi );
580
                return ERROR_OUTOFMEMORY;
581
            }
582
            RegQueryValueExW( key, L"ProxyServer", NULL, &type, (BYTE*)szProxy, &len );
583 584

            /* find the http proxy, and strip away everything else */
585
            p = wcsstr( szProxy, L"http=" );
586 587
            if (p)
            {
588
                p += lstrlenW( L"http=" );
589 590
                lstrcpyW( szProxy, p );
            }
591
            p = wcschr( szProxy, ';' );
592 593
            if (p) *p = 0;

594
            FreeProxyInfo( lpwpi );
595
            lpwpi->proxy = szProxy;
596
            lpwpi->proxyBypass = NULL;
597

598
            TRACE("http proxy (from registry) = %s\n", debugstr_w(lpwpi->proxy));
599
        }
600
        else
601 602
        {
            TRACE("No proxy server settings in registry.\n");
603
            FreeProxyInfo( lpwpi );
604
            lpwpi->proxy = NULL;
605
            lpwpi->proxyBypass = NULL;
606
        }
607
    }
608
    else if (envproxy)
609
    {
610
        FreeProxyInfo( lpwpi );
611
        if (parse_proxy_url( lpwpi, envproxy ))
612 613 614 615 616 617 618
        {
            TRACE("http proxy (from environment) = %s\n", debugstr_w(lpwpi->proxy));
            lpwpi->proxyEnabled = 1;
            lpwpi->proxyBypass = NULL;
        }
        else
        {
619
            WARN("failed to parse http_proxy value %s\n", debugstr_w(envproxy));
620 621 622 623
            lpwpi->proxyEnabled = 0;
            lpwpi->proxy = NULL;
            lpwpi->proxyBypass = NULL;
        }
624
    }
625

626 627
    if (lpwpi->proxyEnabled)
    {
628 629
        TRACE("Proxy is enabled.\n");

630
        if (!(envproxy = _wgetenv( L"no_proxy" )))
631 632
        {
            /* figure out how much memory the proxy setting takes */
633
            if (!RegQueryValueExW( key, L"ProxyOverride", NULL, &type, NULL, &len ) && len && (type == REG_SZ))
634 635 636
            {
                LPWSTR szProxy;

637
                if (!(szProxy = malloc( len )))
638 639
                {
                    RegCloseKey( key );
640
                    FreeProxyInfo( lpwpi );
641 642
                    return ERROR_OUTOFMEMORY;
                }
643
                RegQueryValueExW( key, L"ProxyOverride", NULL, &type, (BYTE*)szProxy, &len );
644

645
                free( lpwpi->proxyBypass );
646 647
                lpwpi->proxyBypass = szProxy;

648
                TRACE("http proxy bypass (from registry) = %s\n", debugstr_w(lpwpi->proxyBypass));
649 650 651
            }
            else
            {
652
                free( lpwpi->proxyBypass );
653 654
                lpwpi->proxyBypass = NULL;

655 656 657
                TRACE("No proxy bypass server settings in registry.\n");
            }
        }
658
        else
659 660 661
        {
            WCHAR *envproxyW;

662
            if (!(envproxyW = malloc( wcslen(envproxy) * sizeof(WCHAR) )))
663 664
            {
                RegCloseKey( key );
665
                FreeProxyInfo( lpwpi );
666
                return ERROR_OUTOFMEMORY;
667
            }
668
            lstrcpyW( envproxyW, envproxy );
669

670
            free( lpwpi->proxyBypass );
671 672 673 674 675
            lpwpi->proxyBypass = envproxyW;

            TRACE("http proxy bypass (from environment) = %s\n", debugstr_w(lpwpi->proxyBypass));
        }
    }
676
    else TRACE("Proxy is disabled.\n");
677 678

    RegCloseKey( key );
679 680 681 682 683 684 685 686
    return ERROR_SUCCESS;
}

/***********************************************************************
 *           INTERNET_ConfigureProxy
 */
static BOOL INTERNET_ConfigureProxy( appinfo_t *lpwai )
{
687
    proxyinfo_t wpi;
688 689 690 691

    if (INTERNET_LoadProxySettings( &wpi ))
        return FALSE;

692
    if (wpi.proxyEnabled)
693
    {
694
        TRACE("http proxy = %s bypass = %s\n", debugstr_w(wpi.proxy), debugstr_w(wpi.proxyBypass));
695 696 697 698 699 700 701

        lpwai->accessType    = INTERNET_OPEN_TYPE_PROXY;
        lpwai->proxy         = wpi.proxy;
        lpwai->proxyBypass   = wpi.proxyBypass;
        lpwai->proxyUsername = wpi.proxyUsername;
        lpwai->proxyPassword = wpi.proxyPassword;
        return TRUE;
702
    }
703

704
    lpwai->accessType = INTERNET_OPEN_TYPE_DIRECT;
705
    FreeProxyInfo(&wpi);
706
    return FALSE;
707 708
}

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751
/***********************************************************************
 *           dump_INTERNET_FLAGS
 *
 * Helper function to TRACE the internet flags.
 *
 * RETURNS
 *    None
 *
 */
static void dump_INTERNET_FLAGS(DWORD dwFlags) 
{
#define FE(x) { x, #x }
    static const wininet_flag_info flag[] = {
        FE(INTERNET_FLAG_RELOAD),
        FE(INTERNET_FLAG_RAW_DATA),
        FE(INTERNET_FLAG_EXISTING_CONNECT),
        FE(INTERNET_FLAG_ASYNC),
        FE(INTERNET_FLAG_PASSIVE),
        FE(INTERNET_FLAG_NO_CACHE_WRITE),
        FE(INTERNET_FLAG_MAKE_PERSISTENT),
        FE(INTERNET_FLAG_FROM_CACHE),
        FE(INTERNET_FLAG_SECURE),
        FE(INTERNET_FLAG_KEEP_CONNECTION),
        FE(INTERNET_FLAG_NO_AUTO_REDIRECT),
        FE(INTERNET_FLAG_READ_PREFETCH),
        FE(INTERNET_FLAG_NO_COOKIES),
        FE(INTERNET_FLAG_NO_AUTH),
        FE(INTERNET_FLAG_CACHE_IF_NET_FAIL),
        FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP),
        FE(INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS),
        FE(INTERNET_FLAG_IGNORE_CERT_DATE_INVALID),
        FE(INTERNET_FLAG_IGNORE_CERT_CN_INVALID),
        FE(INTERNET_FLAG_RESYNCHRONIZE),
        FE(INTERNET_FLAG_HYPERLINK),
        FE(INTERNET_FLAG_NO_UI),
        FE(INTERNET_FLAG_PRAGMA_NOCACHE),
        FE(INTERNET_FLAG_CACHE_ASYNC),
        FE(INTERNET_FLAG_FORMS_SUBMIT),
        FE(INTERNET_FLAG_NEED_FILE),
        FE(INTERNET_FLAG_TRANSFER_ASCII),
        FE(INTERNET_FLAG_TRANSFER_BINARY)
    };
#undef FE
752 753
    unsigned int i;

754
    for (i = 0; i < ARRAY_SIZE(flag); i++) {
755 756 757 758 759 760
	if (flag[i].val & dwFlags) {
	    TRACE(" %s", flag[i].name);
	    dwFlags &= ~flag[i].val;
	}
    }	
    if (dwFlags)
761
        TRACE(" Unknown flags (%08lx)\n", dwFlags);
762 763 764 765
    else
        TRACE("\n");
}

766 767 768 769 770 771
/***********************************************************************
 *           INTERNET_CloseHandle (internal)
 *
 * Close internet handle
 *
 */
772
static VOID APPINFO_Destroy(object_header_t *hdr)
773
{
774
    appinfo_t *lpwai = (appinfo_t*)hdr;
775 776 777

    TRACE("%p\n",lpwai);

778 779 780 781 782
    free(lpwai->agent);
    free(lpwai->proxy);
    free(lpwai->proxyBypass);
    free(lpwai->proxyUsername);
    free(lpwai->proxyPassword);
783 784
}

785
static DWORD APPINFO_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
786
{
787
    appinfo_t *ai = (appinfo_t*)hdr;
788

789 790 791 792 793 794 795 796 797 798
    switch(option) {
    case INTERNET_OPTION_HANDLE_TYPE:
        TRACE("INTERNET_OPTION_HANDLE_TYPE\n");

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *size = sizeof(DWORD);
        *(DWORD*)buffer = INTERNET_HANDLE_TYPE_INTERNET;
        return ERROR_SUCCESS;
799 800 801 802 803 804 805 806 807

    case INTERNET_OPTION_USER_AGENT: {
        DWORD bufsize;

        TRACE("INTERNET_OPTION_USER_AGENT\n");

        bufsize = *size;

        if (unicode) {
808
            DWORD len = ai->agent ? lstrlenW(ai->agent) : 0;
809 810

            *size = (len + 1) * sizeof(WCHAR);
811 812 813
            if(!buffer || bufsize < *size)
                return ERROR_INSUFFICIENT_BUFFER;

814
            if (ai->agent)
815
                lstrcpyW(buffer, ai->agent);
816 817 818 819 820
            else
                *(WCHAR *)buffer = 0;
            /* If the buffer is copied, the returned length doesn't include
             * the NULL terminator.
             */
821
            *size = len;
822
        }else {
823 824
            if (ai->agent)
                *size = WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, NULL, 0, NULL, NULL);
825 826
            else
                *size = 1;
827 828 829
            if(!buffer || bufsize < *size)
                return ERROR_INSUFFICIENT_BUFFER;

830 831
            if (ai->agent)
                WideCharToMultiByte(CP_ACP, 0, ai->agent, -1, buffer, *size, NULL, NULL);
832 833 834 835 836 837
            else
                *(char *)buffer = 0;
            /* If the buffer is copied, the returned length doesn't include
             * the NULL terminator.
             */
            *size -= 1;
838 839 840 841
        }

        return ERROR_SUCCESS;
    }
842 843

    case INTERNET_OPTION_PROXY:
844
        if(!size) return ERROR_INVALID_PARAMETER;
845 846 847 848 849
        if (unicode) {
            INTERNET_PROXY_INFOW *pi = (INTERNET_PROXY_INFOW *)buffer;
            DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
            LPWSTR proxy, proxy_bypass;

850 851 852 853
            if (ai->proxy)
                proxyBytesRequired = (lstrlenW(ai->proxy) + 1) * sizeof(WCHAR);
            if (ai->proxyBypass)
                proxyBypassBytesRequired = (lstrlenW(ai->proxyBypass) + 1) * sizeof(WCHAR);
854
            if (!pi || *size < sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired)
855 856 857 858
            {
                *size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
                return ERROR_INSUFFICIENT_BUFFER;
            }
859 860 861
            proxy = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW));
            proxy_bypass = (LPWSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired);

862
            pi->dwAccessType = ai->accessType;
863 864
            pi->lpszProxy = NULL;
            pi->lpszProxyBypass = NULL;
865 866
            if (ai->proxy) {
                lstrcpyW(proxy, ai->proxy);
867 868 869
                pi->lpszProxy = proxy;
            }

870 871
            if (ai->proxyBypass) {
                lstrcpyW(proxy_bypass, ai->proxyBypass);
872 873 874 875 876 877 878 879 880 881
                pi->lpszProxyBypass = proxy_bypass;
            }

            *size = sizeof(INTERNET_PROXY_INFOW) + proxyBytesRequired + proxyBypassBytesRequired;
            return ERROR_SUCCESS;
        }else {
            INTERNET_PROXY_INFOA *pi = (INTERNET_PROXY_INFOA *)buffer;
            DWORD proxyBytesRequired = 0, proxyBypassBytesRequired = 0;
            LPSTR proxy, proxy_bypass;

882 883 884 885
            if (ai->proxy)
                proxyBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, NULL, 0, NULL, NULL);
            if (ai->proxyBypass)
                proxyBypassBytesRequired = WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1,
886
                        NULL, 0, NULL, NULL);
887
            if (!pi || *size < sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired)
888 889
            {
                *size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
890
                return ERROR_INSUFFICIENT_BUFFER;
891
            }
892 893 894
            proxy = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA));
            proxy_bypass = (LPSTR)((LPBYTE)buffer + sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired);

895
            pi->dwAccessType = ai->accessType;
896 897
            pi->lpszProxy = NULL;
            pi->lpszProxyBypass = NULL;
898 899
            if (ai->proxy) {
                WideCharToMultiByte(CP_ACP, 0, ai->proxy, -1, proxy, proxyBytesRequired, NULL, NULL);
900 901 902
                pi->lpszProxy = proxy;
            }

903 904
            if (ai->proxyBypass) {
                WideCharToMultiByte(CP_ACP, 0, ai->proxyBypass, -1, proxy_bypass,
905 906 907 908 909 910 911
                        proxyBypassBytesRequired, NULL, NULL);
                pi->lpszProxyBypass = proxy_bypass;
            }

            *size = sizeof(INTERNET_PROXY_INFOA) + proxyBytesRequired + proxyBypassBytesRequired;
            return ERROR_SUCCESS;
        }
912 913 914 915 916 917 918 919 920 921 922

    case INTERNET_OPTION_CONNECT_TIMEOUT:
        TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *(ULONG*)buffer = ai->connect_timeout;
        *size = sizeof(ULONG);

        return ERROR_SUCCESS;
923 924
    }

925
    return INET_QueryOption(hdr, option, buffer, size, unicode);
926 927
}

928 929 930 931 932 933 934 935 936 937 938 939 940 941 942
static DWORD APPINFO_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
{
    appinfo_t *ai = (appinfo_t*)hdr;

    switch(option) {
    case INTERNET_OPTION_CONNECT_TIMEOUT:
        TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");

        if(size != sizeof(connect_timeout))
            return ERROR_INTERNET_BAD_OPTION_LENGTH;
        if(!*(ULONG*)buf)
            return ERROR_BAD_ARGUMENTS;

        ai->connect_timeout = *(ULONG*)buf;
        return ERROR_SUCCESS;
943
    case INTERNET_OPTION_USER_AGENT:
944 945
        free(ai->agent);
        if (!(ai->agent = wcsdup(buf))) return ERROR_OUTOFMEMORY;
946
        return ERROR_SUCCESS;
947 948 949
    case INTERNET_OPTION_REFRESH:
        FIXME("INTERNET_OPTION_REFRESH\n");
        return ERROR_SUCCESS;
950 951 952 953 954
    }

    return INET_SetOption(hdr, option, buf, size);
}

955
static const object_vtbl_t APPINFOVtbl = {
956
    APPINFO_Destroy,
957
    NULL,
958
    APPINFO_QueryOption,
959
    APPINFO_SetOption,
960
    NULL,
961
    NULL,
962
    NULL,
963
    NULL
964 965 966
};


967
/***********************************************************************
968
 *           InternetOpenW   (WININET.@)
969 970 971 972 973 974 975 976
 *
 * Per-application initialization of wininet
 *
 * RETURNS
 *    HINTERNET on success
 *    NULL on failure
 *
 */
977 978
HINTERNET WINAPI InternetOpenW(LPCWSTR lpszAgent, DWORD dwAccessType,
    LPCWSTR lpszProxy, LPCWSTR lpszProxyBypass, DWORD dwFlags)
979
{
980
    appinfo_t *lpwai = NULL;
981 982 983 984 985 986 987 988 989 990

    if (TRACE_ON(wininet)) {
#define FE(x) { x, #x }
	static const wininet_flag_info access_type[] = {
	    FE(INTERNET_OPEN_TYPE_PRECONFIG),
	    FE(INTERNET_OPEN_TYPE_DIRECT),
	    FE(INTERNET_OPEN_TYPE_PROXY),
	    FE(INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY)
	};
#undef FE
991
	DWORD i;
992 993
	const char *access_type_str = "Unknown";
	
994
	TRACE("(%s, %li, %s, %s, %li)\n", debugstr_w(lpszAgent), dwAccessType,
995
	      debugstr_w(lpszProxy), debugstr_w(lpszProxyBypass), dwFlags);
996
        for (i = 0; i < ARRAY_SIZE(access_type); i++) {
997 998 999 1000 1001 1002 1003
	    if (access_type[i].val == dwAccessType) {
		access_type_str = access_type[i].name;
		break;
	    }
	}
	TRACE("  access type : %s\n", access_type_str);
	TRACE("  flags       :");
Lionel Ulmer's avatar
Lionel Ulmer committed
1004
	dump_INTERNET_FLAGS(dwFlags);
1005
    }
1006 1007 1008 1009

    /* Clear any error information */
    INTERNET_SetLastError(0);

1010 1011 1012 1013 1014
    if((dwAccessType == INTERNET_OPEN_TYPE_PROXY) && !lpszProxy) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return NULL;
    }

1015 1016 1017 1018
    lpwai = alloc_object(NULL, &APPINFOVtbl, sizeof(appinfo_t));
    if (!lpwai) {
        SetLastError(ERROR_OUTOFMEMORY);
        return NULL;
1019
    }
1020

1021 1022
    lpwai->hdr.htype = WH_HINIT;
    lpwai->hdr.dwFlags = dwFlags;
1023 1024 1025
    lpwai->accessType = dwAccessType;
    lpwai->proxyUsername = NULL;
    lpwai->proxyPassword = NULL;
1026
    lpwai->connect_timeout = connect_timeout;
1027

1028
    lpwai->agent = wcsdup(lpszAgent);
1029
    if(dwAccessType == INTERNET_OPEN_TYPE_PRECONFIG)
1030
        INTERNET_ConfigureProxy( lpwai );
1031
    else if(dwAccessType == INTERNET_OPEN_TYPE_PROXY) {
1032 1033
        lpwai->proxy = wcsdup(lpszProxy);
        lpwai->proxyBypass = wcsdup(lpszProxyBypass);
1034
    }
1035

1036
    TRACE("returning %p\n", lpwai);
1037

1038
    return lpwai->hdr.hInternet;
1039 1040 1041
}


1042
/***********************************************************************
1043
 *           InternetOpenA   (WININET.@)
1044 1045 1046 1047 1048 1049 1050 1051
 *
 * Per-application initialization of wininet
 *
 * RETURNS
 *    HINTERNET on success
 *    NULL on failure
 *
 */
1052 1053
HINTERNET WINAPI InternetOpenA(LPCSTR lpszAgent, DWORD dwAccessType,
    LPCSTR lpszProxy, LPCSTR lpszProxyBypass, DWORD dwFlags)
1054
{
1055 1056
    WCHAR *szAgent, *szProxy, *szBypass;
    HINTERNET rc;
1057

1058
    TRACE("(%s, 0x%08lx, %s, %s, 0x%08lx)\n", debugstr_a(lpszAgent),
1059
       dwAccessType, debugstr_a(lpszProxy), debugstr_a(lpszProxyBypass), dwFlags);
1060

1061 1062 1063
    szAgent = strdupAtoW(lpszAgent);
    szProxy = strdupAtoW(lpszProxy);
    szBypass = strdupAtoW(lpszProxyBypass);
1064

1065
    rc = InternetOpenW(szAgent, dwAccessType, szProxy, szBypass, dwFlags);
1066

1067 1068 1069
    free(szAgent);
    free(szProxy);
    free(szBypass);
1070 1071 1072
    return rc;
}

1073
/***********************************************************************
1074
 *           InternetGetLastResponseInfoA (WININET.@)
1075 1076 1077 1078
 *
 * Return last wininet error description on the calling thread
 *
 * RETURNS
1079
 *    TRUE on success of writing to buffer
1080 1081 1082
 *    FALSE on failure
 *
 */
1083
BOOL WINAPI InternetGetLastResponseInfoA(LPDWORD lpdwError,
1084 1085
    LPSTR lpszBuffer, LPDWORD lpdwBufferLength)
{
1086
    LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
1087

1088
    TRACE("(%p, %p, %p)\n", lpdwError, lpszBuffer, lpdwBufferLength);
1089

1090 1091 1092 1093 1094
    if (!lpdwError || !lpdwBufferLength)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
1095
    if (lpwite)
1096
    {
1097 1098 1099 1100 1101 1102
        if (lpszBuffer == NULL || *lpdwBufferLength < strlen(lpwite->response))
        {
            *lpdwBufferLength = strlen(lpwite->response);
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return FALSE;
        }
1103
        *lpdwError = lpwite->dwError;
1104
        if (*lpdwBufferLength)
1105 1106
        {
            memcpy(lpszBuffer, lpwite->response, *lpdwBufferLength);
1107
            lpszBuffer[*lpdwBufferLength - 1] = 0;
1108 1109
            *lpdwBufferLength = strlen(lpszBuffer);
        }
1110 1111
    }
    else
1112 1113
    {
        *lpdwError = 0;
1114
        *lpdwBufferLength = 0;
1115
    }
1116 1117 1118 1119

    return TRUE;
}

1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132
/***********************************************************************
 *           InternetGetLastResponseInfoW (WININET.@)
 *
 * Return last wininet error description on the calling thread
 *
 * RETURNS
 *    TRUE on success of writing to buffer
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetGetLastResponseInfoW(LPDWORD lpdwError,
    LPWSTR lpszBuffer, LPDWORD lpdwBufferLength)
{
1133
    LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
1134

1135
    TRACE("(%p, %p, %p)\n", lpdwError, lpszBuffer, lpdwBufferLength);
1136

1137 1138 1139 1140 1141
    if (!lpdwError || !lpdwBufferLength)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }
1142
    if (lpwite)
1143
    {
1144 1145 1146 1147 1148 1149 1150
        int required_size = MultiByteToWideChar(CP_ACP, 0, lpwite->response, -1, NULL, 0) - 1;
        if (lpszBuffer == NULL || *lpdwBufferLength < required_size)
        {
            *lpdwBufferLength = required_size;
            SetLastError(ERROR_INSUFFICIENT_BUFFER);
            return FALSE;
        }
1151
        *lpdwError = lpwite->dwError;
1152
        if (*lpdwBufferLength)
1153
            *lpdwBufferLength = MultiByteToWideChar(CP_ACP, 0, lpwite->response, -1, lpszBuffer, *lpdwBufferLength);
1154 1155
    }
    else
1156 1157
    {
        *lpdwError = 0;
1158
        *lpdwBufferLength = 0;
1159
    }
1160 1161 1162

    return TRUE;
}
1163

1164
/***********************************************************************
1165
 *           InternetGetConnectedState (WININET.@)
1166 1167 1168 1169 1170
 *
 * Return connected state
 *
 * RETURNS
 *    TRUE if connected
1171
 *    if lpdwStatus is not null, return the status (off line,
1172 1173 1174 1175 1176
 *    modem, lan...) in it.
 *    FALSE if not connected
 */
BOOL WINAPI InternetGetConnectedState(LPDWORD lpdwStatus, DWORD dwReserved)
{
1177
    TRACE("(%p, 0x%08lx)\n", lpdwStatus, dwReserved);
1178

1179
    return InternetGetConnectedStateExW(lpdwStatus, NULL, 0, dwReserved);
1180 1181
}

1182

1183
/***********************************************************************
1184
 *           InternetGetConnectedStateExW (WININET.@)
1185 1186 1187
 *
 * Return connected state
 *
1188 1189 1190 1191 1192 1193 1194
 * PARAMS
 *
 * lpdwStatus         [O] Flags specifying the status of the internet connection.
 * lpszConnectionName [O] Pointer to buffer to receive the friendly name of the internet connection.
 * dwNameLen          [I] Size of the buffer, in characters.
 * dwReserved         [I] Reserved. Must be set to 0.
 *
1195 1196 1197 1198 1199
 * RETURNS
 *    TRUE if connected
 *    if lpdwStatus is not null, return the status (off line,
 *    modem, lan...) in it.
 *    FALSE if not connected
1200 1201 1202 1203 1204 1205 1206 1207 1208
 *
 * NOTES
 *   If the system has no available network connections, an empty string is
 *   stored in lpszConnectionName. If there is a LAN connection, a localized
 *   "LAN Connection" string is stored. Presumably, if only a dial-up
 *   connection is available then the name of the dial-up connection is
 *   returned. Why any application, other than the "Internet Settings" CPL,
 *   would want to use this function instead of the simpler InternetGetConnectedStateW
 *   function is beyond me.
1209 1210 1211 1212
 */
BOOL WINAPI InternetGetConnectedStateExW(LPDWORD lpdwStatus, LPWSTR lpszConnectionName,
                                         DWORD dwNameLen, DWORD dwReserved)
{
1213 1214 1215 1216
    IP_ADAPTER_ADDRESSES *buf = NULL, *aa;
    ULONG size = 0;
    DWORD status;

1217
    TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
1218

1219 1220
    /* Must be zero */
    if(dwReserved)
1221 1222
    {
        SetLastError(ERROR_INVALID_PARAMETER);
1223
        return FALSE;
1224
    }
1225

1226 1227 1228
    for (;;)
    {
        ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER |
1229
                      GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_INCLUDE_GATEWAYS;
1230 1231 1232 1233
        ULONG errcode = GetAdaptersAddresses(AF_UNSPEC, flags, NULL, buf, &size);

        if (errcode == ERROR_SUCCESS)
            break;
1234 1235
        free(buf);
        if (errcode == ERROR_BUFFER_OVERFLOW && !(buf = malloc(size)))
1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263
            errcode = ERROR_NOT_ENOUGH_MEMORY;
        if (errcode != ERROR_BUFFER_OVERFLOW)
        {
            if (errcode != ERROR_NO_DATA)
            {
                SetLastError(errcode);
                return FALSE;
            }
            buf = NULL;
            break;
        }
    }

    status = INTERNET_RAS_INSTALLED;
    for (aa = buf; aa; aa = aa->Next)
    {
        /* Connected, but not necessarily to internet */
        if (aa->FirstUnicastAddress)
            status |= INTERNET_CONNECTION_OFFLINE;

        /* Connected to internet */
        if (aa->FirstGatewayAddress)
        {
            WARN("always returning LAN connection.\n");
            status &= ~INTERNET_CONNECTION_OFFLINE;
            status |= INTERNET_CONNECTION_LAN;
            break;
        }
1264
    }
1265
    free(buf);
1266 1267

    if (lpdwStatus) *lpdwStatus = status;
1268 1269 1270

    /* When the buffer size is zero LoadStringW fills the buffer with a pointer to
     * the resource, avoid it as we must not change the buffer in this case */
1271 1272
    if (lpszConnectionName && dwNameLen)
    {
1273
        *lpszConnectionName = '\0';
1274 1275
        if (status & INTERNET_CONNECTION_LAN)
            LoadStringW(WININET_hModule, IDS_LANCONNECTION, lpszConnectionName, dwNameLen);
1276 1277
    }

1278 1279 1280 1281 1282
    if (!(status & (INTERNET_CONNECTION_LAN | INTERNET_CONNECTION_MODEM | INTERNET_CONNECTION_PROXY)))
    {
        SetLastError(ERROR_SUCCESS);
        return FALSE;
    }
1283
    return TRUE;
1284
}
1285

1286 1287 1288 1289 1290 1291 1292 1293 1294 1295

/***********************************************************************
 *           InternetGetConnectedStateExA (WININET.@)
 */
BOOL WINAPI InternetGetConnectedStateExA(LPDWORD lpdwStatus, LPSTR lpszConnectionName,
                                         DWORD dwNameLen, DWORD dwReserved)
{
    LPWSTR lpwszConnectionName = NULL;
    BOOL rc;

1296
    TRACE("(%p, %p, %ld, 0x%08lx)\n", lpdwStatus, lpszConnectionName, dwNameLen, dwReserved);
1297 1298

    if (lpszConnectionName && dwNameLen > 0)
1299
        lpwszConnectionName = malloc(dwNameLen * sizeof(WCHAR));
1300 1301 1302 1303

    rc = InternetGetConnectedStateExW(lpdwStatus,lpwszConnectionName, dwNameLen,
                                      dwReserved);
    if (rc && lpwszConnectionName)
1304
    {
1305 1306
        WideCharToMultiByte(CP_ACP,0,lpwszConnectionName,-1,lpszConnectionName,
                            dwNameLen, NULL, NULL);
1307 1308 1309
        /* Yes, blindly truncate double-byte characters */
        lpszConnectionName[dwNameLen - 1] = '\0';
    }
1310

1311
    free(lpwszConnectionName);
1312 1313 1314 1315
    return rc;
}


1316
/***********************************************************************
1317
 *           InternetConnectW (WININET.@)
1318 1319 1320 1321 1322 1323 1324 1325
 *
 * Open a ftp, gopher or http session
 *
 * RETURNS
 *    HINTERNET a session handle on success
 *    NULL on failure
 *
 */
1326 1327 1328
HINTERNET WINAPI InternetConnectW(HINTERNET hInternet,
    LPCWSTR lpszServerName, INTERNET_PORT nServerPort,
    LPCWSTR lpszUserName, LPCWSTR lpszPassword,
1329
    DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
1330
{
1331
    appinfo_t *hIC;
1332
    HINTERNET rc = NULL;
1333
    DWORD res = ERROR_SUCCESS;
1334

1335
    TRACE("(%p, %s, %u, %s, %p, %lu, %lx, %Ix)\n", hInternet, debugstr_w(lpszServerName),
1336
          nServerPort, debugstr_w(lpszUserName), lpszPassword, dwService, dwFlags, dwContext);
1337

1338 1339
    if (!lpszServerName)
    {
1340
        SetLastError(ERROR_INVALID_PARAMETER);
1341 1342 1343
        return NULL;
    }

1344
    hIC = (appinfo_t*)get_handle_object( hInternet );
1345
    if ( (hIC == NULL) || (hIC->hdr.htype != WH_HINIT) )
1346
    {
1347
        res = ERROR_INVALID_HANDLE;
1348
        goto lend;
1349
    }
1350 1351 1352 1353

    switch (dwService)
    {
        case INTERNET_SERVICE_FTP:
1354
            rc = FTP_Connect(hIC, lpszServerName, nServerPort,
1355
            lpszUserName, lpszPassword, dwFlags, dwContext, 0);
1356 1357
            if(!rc)
                res = INTERNET_GetLastError();
1358 1359 1360
            break;

        case INTERNET_SERVICE_HTTP:
1361 1362
	    res = HTTP_Connect(hIC, lpszServerName, nServerPort,
                    lpszUserName, lpszPassword, dwFlags, dwContext, 0, &rc);
1363 1364 1365 1366 1367 1368
            break;

        case INTERNET_SERVICE_GOPHER:
        default:
            break;
    }
1369 1370 1371
lend:
    if( hIC )
        WININET_Release( &hIC->hdr );
1372

1373
    TRACE("returning %p\n", rc);
1374
    SetLastError(res);
1375 1376 1377
    return rc;
}

1378 1379

/***********************************************************************
1380
 *           InternetConnectA (WININET.@)
1381 1382 1383 1384 1385 1386 1387 1388
 *
 * Open a ftp, gopher or http session
 *
 * RETURNS
 *    HINTERNET a session handle on success
 *    NULL on failure
 *
 */
1389 1390 1391
HINTERNET WINAPI InternetConnectA(HINTERNET hInternet,
    LPCSTR lpszServerName, INTERNET_PORT nServerPort,
    LPCSTR lpszUserName, LPCSTR lpszPassword,
1392
    DWORD dwService, DWORD dwFlags, DWORD_PTR dwContext)
1393
{
1394
    HINTERNET rc = NULL;
1395 1396 1397
    LPWSTR szServerName;
    LPWSTR szUserName;
    LPWSTR szPassword;
1398

1399 1400 1401
    szServerName = strdupAtoW(lpszServerName);
    szUserName = strdupAtoW(lpszUserName);
    szPassword = strdupAtoW(lpszPassword);
1402

1403
    rc = InternetConnectW(hInternet, szServerName, nServerPort,
1404 1405
        szUserName, szPassword, dwService, dwFlags, dwContext);

1406 1407 1408
    free(szServerName);
    free(szUserName);
    free(szPassword);
1409 1410 1411 1412
    return rc;
}


1413
/***********************************************************************
1414
 *           InternetFindNextFileA (WININET.@)
1415 1416 1417 1418 1419 1420 1421 1422
 *
 * Continues a file search from a previous call to FindFirstFile
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
 *
 */
1423
BOOL WINAPI InternetFindNextFileA(HINTERNET hFind, LPVOID lpvFindData)
1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444
{
    BOOL ret;
    WIN32_FIND_DATAW fd;
    
    ret = InternetFindNextFileW(hFind, lpvFindData?&fd:NULL);
    if(lpvFindData)
        WININET_find_data_WtoA(&fd, (LPWIN32_FIND_DATAA)lpvFindData);
    return ret;
}

/***********************************************************************
 *           InternetFindNextFileW (WININET.@)
 *
 * Continues a file search from a previous call to FindFirstFile
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetFindNextFileW(HINTERNET hFind, LPVOID lpvFindData)
1445
{
1446
    object_header_t *hdr;
1447
    DWORD res;
1448 1449 1450

    TRACE("\n");

1451
    hdr = get_handle_object(hFind);
1452 1453 1454 1455
    if(!hdr) {
        WARN("Invalid handle\n");
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
1456 1457
    }

1458 1459 1460 1461 1462 1463
    if(hdr->vtbl->FindNextFileW) {
        res = hdr->vtbl->FindNextFileW(hdr, lpvFindData);
    }else {
        WARN("Handle doesn't support NextFile\n");
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
    }
1464

1465
    WININET_Release(hdr);
1466

1467 1468 1469
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
1470 1471 1472
}

/***********************************************************************
1473
 *           InternetCloseHandle (WININET.@)
1474
 *
1475
 * Generic close handle function
1476 1477 1478 1479 1480 1481
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
 *
 */
1482
BOOL WINAPI InternetCloseHandle(HINTERNET hInternet)
1483
{
1484
    object_header_t *obj;
1485
    
1486
    TRACE("%p\n", hInternet);
1487

1488 1489 1490
    obj = get_handle_object( hInternet );
    if (!obj) {
        SetLastError(ERROR_INVALID_HANDLE);
1491
        return FALSE;
1492
    }
1493

1494 1495
    invalidate_handle(obj);
    WININET_Release(obj);
1496

1497
    return TRUE;
1498 1499
}

1500 1501
static BOOL set_url_component(WCHAR **component, DWORD *component_length, const WCHAR *value, DWORD len)
{
1502
    TRACE("%s (%ld)\n", debugstr_wn(value, len), len);
1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523

    if (!*component_length)
        return TRUE;

    if (!*component) {
        *(const WCHAR**)component = value;
        *component_length = len;
        return TRUE;
    }

    if (*component_length < len+1) {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

    *component_length = len;
    if(len)
        memcpy(*component, value, len*sizeof(WCHAR));
    (*component)[len] = 0;
    return TRUE;
}
1524

1525 1526
static BOOL set_url_component_WtoA(const WCHAR *comp_w, DWORD length, const WCHAR *url_w, char **comp, DWORD *ret_length,
                                   const char *url_a)
1527
{
1528
    size_t size, ret_size = *ret_length;
1529

1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543
    if (!*ret_length)
        return TRUE;
    size = WideCharToMultiByte(CP_ACP, 0, comp_w, length, NULL, 0, NULL, NULL);

    if (!*comp) {
        *comp = comp_w ? (char*)url_a + WideCharToMultiByte(CP_ACP, 0, url_w, comp_w-url_w, NULL, 0, NULL, NULL) : NULL;
        *ret_length = size;
        return TRUE;
    }

    if (size+1 > ret_size) {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        *ret_length = size+1;
        return FALSE;
1544
    }
1545 1546 1547 1548 1549

    *ret_length = size;
    WideCharToMultiByte(CP_ACP, 0, comp_w, length, *comp, ret_size-1, NULL, NULL);
    (*comp)[size] = 0;
    return TRUE;
1550 1551
}

1552 1553 1554 1555 1556 1557 1558 1559 1560
static BOOL set_url_component_AtoW(const char *comp_a, DWORD len_a, WCHAR **comp_w, DWORD *len_w, WCHAR **buf)
{
    *len_w = len_a;

    if(!comp_a) {
        *comp_w = NULL;
        return TRUE;
    }

1561
    if(!(*comp_w = *buf = malloc(len_a * sizeof(WCHAR)))) {
1562 1563 1564 1565 1566 1567
        SetLastError(ERROR_OUTOFMEMORY);
        return FALSE;
    }

    return TRUE;
}
1568

1569
/***********************************************************************
1570
 *           InternetCrackUrlA (WININET.@)
1571
 *
1572
 * See InternetCrackUrlW.
1573
 */
1574
BOOL WINAPI InternetCrackUrlA(const char *url, DWORD url_length, DWORD flags, URL_COMPONENTSA *ret_comp)
1575
{
1576 1577
    WCHAR *host = NULL, *user = NULL, *pass = NULL, *path = NULL, *scheme = NULL, *extra = NULL;
    URL_COMPONENTSW comp;
1578
    WCHAR *url_w = NULL;
1579 1580
    BOOL ret;

1581
    TRACE("(%s %lu %lx %p)\n", url_length ? debugstr_an(url, url_length) : debugstr_a(url), url_length, flags, ret_comp);
1582 1583 1584 1585

    if (!url || !*url || !ret_comp || ret_comp->dwStructSize != sizeof(URL_COMPONENTSA)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
1586
    }
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602

    comp.dwStructSize = sizeof(comp);

    ret = set_url_component_AtoW(ret_comp->lpszHostName, ret_comp->dwHostNameLength,
                                 &comp.lpszHostName, &comp.dwHostNameLength, &host)
        && set_url_component_AtoW(ret_comp->lpszUserName, ret_comp->dwUserNameLength,
                                  &comp.lpszUserName, &comp.dwUserNameLength, &user)
        && set_url_component_AtoW(ret_comp->lpszPassword, ret_comp->dwPasswordLength,
                                  &comp.lpszPassword, &comp.dwPasswordLength, &pass)
        && set_url_component_AtoW(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength,
                                  &comp.lpszUrlPath, &comp.dwUrlPathLength, &path)
        && set_url_component_AtoW(ret_comp->lpszScheme, ret_comp->dwSchemeLength,
                                  &comp.lpszScheme, &comp.dwSchemeLength, &scheme)
        && set_url_component_AtoW(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength,
                                  &comp.lpszExtraInfo, &comp.dwExtraInfoLength, &extra);

1603
    if(ret && !(url_w = strndupAtoW(url, url_length ? url_length : -1, &url_length))) {
1604 1605
        SetLastError(ERROR_OUTOFMEMORY);
        ret = FALSE;
1606
    }
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630

    if (ret && (ret = InternetCrackUrlW(url_w, url_length, flags, &comp))) {
        ret_comp->nScheme = comp.nScheme;
        ret_comp->nPort = comp.nPort;

        ret = set_url_component_WtoA(comp.lpszHostName, comp.dwHostNameLength, url_w,
                                     &ret_comp->lpszHostName, &ret_comp->dwHostNameLength, url)
            && set_url_component_WtoA(comp.lpszUserName, comp.dwUserNameLength, url_w,
                                      &ret_comp->lpszUserName, &ret_comp->dwUserNameLength, url)
            && set_url_component_WtoA(comp.lpszPassword, comp.dwPasswordLength, url_w,
                                      &ret_comp->lpszPassword, &ret_comp->dwPasswordLength, url)
            && set_url_component_WtoA(comp.lpszUrlPath, comp.dwUrlPathLength, url_w,
                                      &ret_comp->lpszUrlPath, &ret_comp->dwUrlPathLength, url)
            && set_url_component_WtoA(comp.lpszScheme, comp.dwSchemeLength, url_w,
                                      &ret_comp->lpszScheme, &ret_comp->dwSchemeLength, url)
            && set_url_component_WtoA(comp.lpszExtraInfo, comp.dwExtraInfoLength, url_w,
                                      &ret_comp->lpszExtraInfo, &ret_comp->dwExtraInfoLength, url);

        if(ret)
            TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_a(url),
                  debugstr_an(ret_comp->lpszScheme, ret_comp->dwSchemeLength),
                  debugstr_an(ret_comp->lpszHostName, ret_comp->dwHostNameLength),
                  debugstr_an(ret_comp->lpszUrlPath, ret_comp->dwUrlPathLength),
                  debugstr_an(ret_comp->lpszExtraInfo, ret_comp->dwExtraInfoLength));
1631
    }
1632

1633 1634 1635 1636 1637 1638 1639
    free(host);
    free(user);
    free(pass);
    free(path);
    free(scheme);
    free(extra);
    free(url_w);
1640
    return ret;
1641 1642
}

1643
static const WCHAR *url_schemes[] =
1644
{
1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655
    L"ftp",
    L"gopher",
    L"http",
    L"https",
    L"file",
    L"news",
    L"mailto",
    L"socks",
    L"javascript",
    L"vbscript",
    L"res"
1656 1657
};

1658 1659 1660 1661 1662 1663 1664 1665 1666 1667
/***********************************************************************
 *           GetInternetSchemeW (internal)
 *
 * Get scheme of url
 *
 * RETURNS
 *    scheme on success
 *    INTERNET_SCHEME_UNKNOWN on failure
 *
 */
1668
static INTERNET_SCHEME GetInternetSchemeW(LPCWSTR lpszScheme, DWORD nMaxCmp)
1669
{
1670 1671
    int i;

1672
    TRACE("%s %ld\n",debugstr_wn(lpszScheme, nMaxCmp), nMaxCmp);
1673

1674 1675 1676
    if(lpszScheme==NULL)
        return INTERNET_SCHEME_UNKNOWN;

1677
    for (i = 0; i < ARRAY_SIZE(url_schemes); i++)
1678
        if (!wcsnicmp(lpszScheme, url_schemes[i], nMaxCmp))
1679 1680 1681
            return INTERNET_SCHEME_FIRST + i;

    return INTERNET_SCHEME_UNKNOWN;
1682 1683 1684 1685
}

/***********************************************************************
 *           InternetCrackUrlW   (WININET.@)
1686 1687 1688 1689 1690 1691
 *
 * Break up URL into its components
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
1692
 */
1693
BOOL WINAPI InternetCrackUrlW(const WCHAR *lpszUrl, DWORD dwUrlLength, DWORD dwFlags, URL_COMPONENTSW *lpUC)
1694 1695 1696 1697 1698 1699
{
  /*
   * RFC 1808
   * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
   *
   */
1700
    LPCWSTR lpszParam    = NULL;
1701
    BOOL  found_colon = FALSE;
1702
    LPCWSTR lpszap;
1703
    LPCWSTR lpszcp = NULL, lpszNetLoc;
1704

1705
    TRACE("(%s %lu %lx %p)\n",
1706
          lpszUrl ? debugstr_wn(lpszUrl, dwUrlLength ? dwUrlLength : lstrlenW(lpszUrl)) : "(null)",
1707
          dwUrlLength, dwFlags, lpUC);
1708

1709
    if (!lpszUrl || !*lpszUrl || !lpUC)
1710
    {
1711
        SetLastError(ERROR_INVALID_PARAMETER);
1712 1713
        return FALSE;
    }
1714 1715 1716 1717 1718 1719 1720

    if (!dwUrlLength)
        dwUrlLength = lstrlenW(lpszUrl);
    else {
        /* Windows stops at a null, regardless of what dwUrlLength says. */
        dwUrlLength = wcsnlen(lpszUrl, dwUrlLength);
    }
1721

1722 1723
    if (dwFlags & ICU_DECODE)
    {
1724
        WCHAR *url_tmp, *buffer;
1725
        DWORD len = dwUrlLength + 1;
1726
        BOOL ret;
1727

1728
        if (!(url_tmp = strndupW(lpszUrl, dwUrlLength)))
1729
        {
1730
            SetLastError(ERROR_OUTOFMEMORY);
1731 1732
            return FALSE;
        }
1733 1734 1735 1736 1737

        buffer = url_tmp;
        ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
        if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
        {
1738
            buffer = malloc(len * sizeof(WCHAR));
1739 1740 1741
            if (!buffer)
            {
                SetLastError(ERROR_OUTOFMEMORY);
1742
                free(url_tmp);
1743 1744 1745 1746
                return FALSE;
            }
            ret = InternetCanonicalizeUrlW(url_tmp, buffer, &len, ICU_DECODE | ICU_NO_ENCODE);
        }
1747
        if (ret)
1748 1749
            ret = InternetCrackUrlW(buffer, len, dwFlags & ~ICU_DECODE, lpUC);

1750 1751
        if (buffer != url_tmp) free(buffer);
        free(url_tmp);
1752
        return ret;
1753 1754 1755
    }
    lpszap = lpszUrl;
    
1756
    /* Determine if the URI is absolute. */
1757
    while (lpszap - lpszUrl < dwUrlLength)
1758
    {
1759
        if (iswalnum(*lpszap) || *lpszap == '+' || *lpszap == '.' || *lpszap == '-')
1760
        {
1761
            lpszap++;
1762 1763
            continue;
        }
1764
        if (*lpszap == ':')
1765
        {
1766
            found_colon = TRUE;
1767 1768
            lpszcp = lpszap;
        }
1769 1770
        else
        {
1771
            lpszcp = lpszUrl; /* Relative url */
1772
        }
1773

1774 1775 1776
        break;
    }

1777 1778
    if(!found_colon){
        SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
1779
        return FALSE;
1780 1781
    }

1782 1783 1784
    lpUC->nScheme = INTERNET_SCHEME_UNKNOWN;
    lpUC->nPort = INTERNET_INVALID_PORT_NUMBER;

1785
    /* Parse <params> */
1786
    lpszParam = wmemchr(lpszap, '?', dwUrlLength - (lpszap - lpszUrl));
1787
    if(!lpszParam)
1788
        lpszParam = wmemchr(lpszap, '#', dwUrlLength - (lpszap - lpszUrl));
1789

1790 1791 1792
    if(!set_url_component(&lpUC->lpszExtraInfo, &lpUC->dwExtraInfoLength,
                          lpszParam, lpszParam ? dwUrlLength-(lpszParam-lpszUrl) : 0))
        return FALSE;
1793

1794

1795 1796
    /* Get scheme first. */
    lpUC->nScheme = GetInternetSchemeW(lpszUrl, lpszcp - lpszUrl);
1797 1798
    if(!set_url_component(&lpUC->lpszScheme, &lpUC->dwSchemeLength, lpszUrl, lpszcp - lpszUrl))
        return FALSE;
1799 1800 1801 1802 1803 1804 1805 1806

    /* Eat ':' in protocol. */
    lpszcp++;

    /* double slash indicates the net_loc portion is present */
    if ((lpszcp[0] == '/') && (lpszcp[1] == '/'))
    {
        lpszcp += 2;
1807

1808
        lpszNetLoc = wmemchr(lpszcp, '/', dwUrlLength - (lpszcp - lpszUrl));
1809 1810 1811 1812 1813 1814 1815 1816 1817
        if (lpszParam)
        {
            if (lpszNetLoc)
                lpszNetLoc = min(lpszNetLoc, lpszParam);
            else
                lpszNetLoc = lpszParam;
        }
        else if (!lpszNetLoc)
            lpszNetLoc = lpszcp + dwUrlLength-(lpszcp-lpszUrl);
1818

1819 1820
        /* Parse net-loc */
        if (lpszNetLoc)
1821
        {
1822 1823
            LPCWSTR lpszHost;
            LPCWSTR lpszPort;
1824

1825 1826 1827
            /* [<user>[<:password>]@]<host>[:<port>] */
            /* First find the user and password if they exist */

1828
            lpszHost = wmemchr(lpszcp, '@', dwUrlLength - (lpszcp - lpszUrl));
1829
            if (lpszHost == NULL || lpszHost > lpszNetLoc)
1830
            {
1831
                /* username and password not specified. */
1832 1833
                set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
                set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
1834
            }
1835
            else /* Parse out username and password */
1836
            {
1837 1838
                LPCWSTR lpszUser = lpszcp;
                LPCWSTR lpszPasswd = lpszHost;
1839

1840
                while (lpszcp < lpszHost)
1841
                {
1842 1843 1844 1845
                    if (*lpszcp == ':')
                        lpszPasswd = lpszcp;

                    lpszcp++;
1846
                }
1847

1848 1849
                if(!set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, lpszUser, lpszPasswd - lpszUser))
                    return FALSE;
1850

1851 1852
                if (lpszPasswd != lpszHost)
                    lpszPasswd++;
1853 1854 1855
                if(!set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength,
                                      lpszPasswd == lpszHost ? NULL : lpszPasswd, lpszHost - lpszPasswd))
                    return FALSE;
1856

1857 1858
                lpszcp++; /* Advance to beginning of host */
            }
1859

1860
            /* Parse <host><:port> */
1861

1862 1863
            lpszHost = lpszcp;
            lpszPort = lpszNetLoc;
1864

1865 1866 1867 1868
            /* special case for res:// URLs: there is no port here, so the host is the
               entire string up to the first '/' */
            if(lpUC->nScheme==INTERNET_SCHEME_RES)
            {
1869 1870
                if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
                    return FALSE;
1871 1872 1873 1874 1875 1876 1877 1878
                lpszcp=lpszNetLoc;
            }
            else
            {
                while (lpszcp < lpszNetLoc)
                {
                    if (*lpszcp == ':')
                        lpszPort = lpszcp;
1879

1880 1881
                    lpszcp++;
                }
1882

1883 1884
                /* If the scheme is "file" and the host is just one letter, it's not a host */
                if(lpUC->nScheme==INTERNET_SCHEME_FILE && lpszPort <= lpszHost+1)
1885
                {
1886
                    lpszcp=lpszHost;
1887
                    set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1888
                }
1889 1890
                else
                {
1891 1892
                    if(!set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, lpszHost, lpszPort - lpszHost))
                        return FALSE;
1893
                    if (lpszPort != lpszNetLoc)
1894
                        lpUC->nPort = wcstol(++lpszPort, NULL, 10);
1895
                    else switch (lpUC->nScheme)
1896
                    {
1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907
                    case INTERNET_SCHEME_HTTP:
                        lpUC->nPort = INTERNET_DEFAULT_HTTP_PORT;
                        break;
                    case INTERNET_SCHEME_HTTPS:
                        lpUC->nPort = INTERNET_DEFAULT_HTTPS_PORT;
                        break;
                    case INTERNET_SCHEME_FTP:
                        lpUC->nPort = INTERNET_DEFAULT_FTP_PORT;
                        break;
                    default:
                        break;
1908 1909
                    }
                }
1910 1911
            }
        }
1912
    }
1913 1914
    else
    {
1915 1916 1917
        set_url_component(&lpUC->lpszUserName, &lpUC->dwUserNameLength, NULL, 0);
        set_url_component(&lpUC->lpszPassword, &lpUC->dwPasswordLength, NULL, 0);
        set_url_component(&lpUC->lpszHostName, &lpUC->dwHostNameLength, NULL, 0);
1918
    }
1919 1920 1921 1922 1923 1924

    /* Here lpszcp points to:
     *
     * <protocol>:[//<net_loc>][/path][;<params>][?<query>][#<fragment>]
     *                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
     */
1925
    if (lpszcp != 0 && lpszcp - lpszUrl < dwUrlLength && (!lpszParam || lpszcp <= lpszParam))
1926
    {
1927
        DWORD len;
1928 1929

        /* Only truncate the parameter list if it's already been saved
1930
         * in lpUC->lpszExtraInfo.
1931
         */
1932
        if (lpszParam && lpUC->dwExtraInfoLength && lpUC->lpszExtraInfo)
1933 1934 1935 1936 1937 1938
            len = lpszParam - lpszcp;
        else
        {
            /* Leave the parameter list in lpszUrlPath.  Strip off any trailing
             * newlines if necessary.
             */
1939
            LPWSTR lpsznewline = wmemchr(lpszcp, '\n', dwUrlLength - (lpszcp - lpszUrl));
1940 1941 1942
            if (lpsznewline != NULL)
                len = lpsznewline - lpszcp;
            else
1943
                len = dwUrlLength-(lpszcp-lpszUrl);
1944
        }
1945 1946 1947 1948 1949 1950 1951
        if (lpUC->dwUrlPathLength && lpUC->lpszUrlPath &&
                lpUC->nScheme == INTERNET_SCHEME_FILE)
        {
            WCHAR tmppath[MAX_PATH];
            if (*lpszcp == '/')
            {
                len = MAX_PATH;
1952
                PathCreateFromUrlW(lpszUrl, tmppath, &len, 0);
1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978
            }
            else
            {
                WCHAR *iter;
                memcpy(tmppath, lpszcp, len * sizeof(WCHAR));
                tmppath[len] = '\0';

                iter = tmppath;
                while (*iter) {
                    if (*iter == '/')
                        *iter = '\\';
                    ++iter;
                }
            }
            /* if ends in \. or \.. append a backslash */
            if (tmppath[len - 1] == '.' &&
                    (tmppath[len - 2] == '\\' ||
                     (tmppath[len - 2] == '.' && tmppath[len - 3] == '\\')))
            {
                if (len < MAX_PATH - 1)
                {
                    tmppath[len] = '\\';
                    tmppath[len+1] = '\0';
                    ++len;
                }
            }
1979 1980
            if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, tmppath, len))
                return FALSE;
1981
        }
1982 1983
        else if(!set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, len))
            return FALSE;
1984 1985
    }
    else
1986
    {
1987
        set_url_component(&lpUC->lpszUrlPath, &lpUC->dwUrlPathLength, lpszcp, 0);
1988 1989
    }

1990 1991
    TRACE("%s: scheme(%s) host(%s) path(%s) extra(%s)\n", debugstr_wn(lpszUrl,dwUrlLength),
             debugstr_wn(lpUC->lpszScheme,lpUC->dwSchemeLength),
1992 1993 1994
             debugstr_wn(lpUC->lpszHostName,lpUC->dwHostNameLength),
             debugstr_wn(lpUC->lpszUrlPath,lpUC->dwUrlPathLength),
             debugstr_wn(lpUC->lpszExtraInfo,lpUC->dwExtraInfoLength));
1995 1996 1997 1998 1999

    return TRUE;
}

/***********************************************************************
2000
 *           InternetAttemptConnect (WININET.@)
2001 2002 2003 2004 2005 2006 2007 2008
 *
 * Attempt to make a connection to the internet
 *
 * RETURNS
 *    ERROR_SUCCESS on success
 *    Error value   on failure
 *
 */
2009
DWORD WINAPI InternetAttemptConnect(DWORD dwReserved)
2010 2011 2012 2013 2014 2015
{
    FIXME("Stub\n");
    return ERROR_SUCCESS;
}


2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041
/***********************************************************************
 *           convert_url_canonicalization_flags
 *
 * Helper for InternetCanonicalizeUrl
 *
 * PARAMS
 *     dwFlags [I] Flags suitable for InternetCanonicalizeUrl
 *
 * RETURNS
 *     Flags suitable for UrlCanonicalize
 */
static DWORD convert_url_canonicalization_flags(DWORD dwFlags)
{
    DWORD dwUrlFlags = URL_WININET_COMPATIBILITY | URL_ESCAPE_UNSAFE;

    if (dwFlags & ICU_BROWSER_MODE)        dwUrlFlags |= URL_BROWSER_MODE;
    if (dwFlags & ICU_DECODE)              dwUrlFlags |= URL_UNESCAPE;
    if (dwFlags & ICU_ENCODE_PERCENT)      dwUrlFlags |= URL_ESCAPE_PERCENT;
    if (dwFlags & ICU_ENCODE_SPACES_ONLY)  dwUrlFlags |= URL_ESCAPE_SPACES_ONLY;
    /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
    if (dwFlags & ICU_NO_ENCODE)           dwUrlFlags ^= URL_ESCAPE_UNSAFE;
    if (dwFlags & ICU_NO_META)             dwUrlFlags |= URL_NO_META;

    return dwUrlFlags;
}

2042
/***********************************************************************
2043
 *           InternetCanonicalizeUrlA (WININET.@)
2044 2045 2046 2047 2048 2049 2050 2051
 *
 * Escape unsafe characters and spaces
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
 *
 */
2052
BOOL WINAPI InternetCanonicalizeUrlA(LPCSTR lpszUrl, LPSTR lpszBuffer,
2053 2054
	LPDWORD lpdwBufferLength, DWORD dwFlags)
{
2055
    HRESULT hr;
2056

2057
    TRACE("(%s, %p, %p, 0x%08lx) buffer length: %ld\n", debugstr_a(lpszUrl), lpszBuffer,
2058
        lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
2059

2060 2061
    dwFlags = convert_url_canonicalization_flags(dwFlags);
    hr = UrlCanonicalizeA(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
2062 2063
    if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
    if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
2064

2065
    return hr == S_OK;
2066
}
2067

2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081
/***********************************************************************
 *           InternetCanonicalizeUrlW (WININET.@)
 *
 * Escape unsafe characters and spaces
 *
 * RETURNS
 *    TRUE on success
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetCanonicalizeUrlW(LPCWSTR lpszUrl, LPWSTR lpszBuffer,
    LPDWORD lpdwBufferLength, DWORD dwFlags)
{
    HRESULT hr;
2082

2083
    TRACE("(%s, %p, %p, 0x%08lx) buffer length: %ld\n", debugstr_w(lpszUrl), lpszBuffer,
2084
          lpdwBufferLength, dwFlags, lpdwBufferLength ? *lpdwBufferLength : -1);
2085

2086 2087
    dwFlags = convert_url_canonicalization_flags(dwFlags);
    hr = UrlCanonicalizeW(lpszUrl, lpszBuffer, lpdwBufferLength, dwFlags);
2088 2089
    if (hr == E_POINTER) SetLastError(ERROR_INSUFFICIENT_BUFFER);
    if (hr == E_INVALIDARG) SetLastError(ERROR_INVALID_PARAMETER);
2090

2091
    return hr == S_OK;
2092 2093
}

2094 2095
/* #################################################### */

2096
static INTERNET_STATUS_CALLBACK set_status_callback(
2097
    object_header_t *lpwh, INTERNET_STATUS_CALLBACK callback, BOOL unicode)
2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108
{
    INTERNET_STATUS_CALLBACK ret;

    if (unicode) lpwh->dwInternalFlags |= INET_CALLBACKW;
    else lpwh->dwInternalFlags &= ~INET_CALLBACKW;

    ret = lpwh->lpfnStatusCB;
    lpwh->lpfnStatusCB = callback;

    return ret;
}
2109

2110
/***********************************************************************
2111
 *           InternetSetStatusCallbackA (WININET.@)
2112 2113
 *
 * Sets up a callback function which is called as progress is made
2114
 * during an operation.
2115 2116 2117 2118 2119 2120
 *
 * RETURNS
 *    Previous callback or NULL 	on success
 *    INTERNET_INVALID_STATUS_CALLBACK  on failure
 *
 */
2121
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackA(
2122 2123
	HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
{
2124
    INTERNET_STATUS_CALLBACK retVal;
2125
    object_header_t *lpwh;
2126

2127 2128
    TRACE("%p\n", hInternet);

2129
    if (!(lpwh = get_handle_object(hInternet)))
2130
        return INTERNET_INVALID_STATUS_CALLBACK;
2131

2132
    retVal = set_status_callback(lpwh, lpfnIntCB, FALSE);
2133 2134

    WININET_Release( lpwh );
2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151
    return retVal;
}

/***********************************************************************
 *           InternetSetStatusCallbackW (WININET.@)
 *
 * Sets up a callback function which is called as progress is made
 * during an operation.
 *
 * RETURNS
 *    Previous callback or NULL 	on success
 *    INTERNET_INVALID_STATUS_CALLBACK  on failure
 *
 */
INTERNET_STATUS_CALLBACK WINAPI InternetSetStatusCallbackW(
	HINTERNET hInternet ,INTERNET_STATUS_CALLBACK lpfnIntCB)
{
2152
    INTERNET_STATUS_CALLBACK retVal;
2153
    object_header_t *lpwh;
2154

2155
    TRACE("%p\n", hInternet);
2156

2157
    if (!(lpwh = get_handle_object(hInternet)))
2158
        return INTERNET_INVALID_STATUS_CALLBACK;
2159

2160
    retVal = set_status_callback(lpwh, lpfnIntCB, TRUE);
2161

2162
    WININET_Release( lpwh );
2163 2164 2165
    return retVal;
}

Kirill Smelkov's avatar
Kirill Smelkov committed
2166 2167
/***********************************************************************
 *           InternetSetFilePointer (WININET.@)
2168 2169 2170 2171 2172 2173
 *
 * Sets read position for an open internet file.
 *
 * RETURNS
 *    Current position of the file on success
 *    INVALID_SET_FILE_POINTER on failure
Kirill Smelkov's avatar
Kirill Smelkov committed
2174
 */
2175
DWORD WINAPI InternetSetFilePointer(HINTERNET hFile, LONG lDistanceToMove,
2176
    PVOID pReserved, DWORD dwMoveContext, DWORD_PTR dwContext)
Kirill Smelkov's avatar
Kirill Smelkov committed
2177
{
2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194
    object_header_t *hdr;
    DWORD res;

    TRACE("(%p %ld %p %ld %Ix)\n", hFile, lDistanceToMove, pReserved, dwMoveContext, dwContext);

    hdr = get_handle_object(hFile);
    if(!hdr) {
        SetLastError(ERROR_INVALID_HANDLE);
        return INVALID_SET_FILE_POINTER;
    }

    if(hdr->vtbl->SetFilePointer) {
        res = hdr->vtbl->SetFilePointer(hdr, lDistanceToMove, dwMoveContext);
    }else {
        SetLastError(ERROR_INVALID_HANDLE);
        res = INVALID_SET_FILE_POINTER;
    }
2195

2196 2197
    WININET_Release(hdr);
    return res;
Kirill Smelkov's avatar
Kirill Smelkov committed
2198
}
2199 2200

/***********************************************************************
2201
 *           InternetWriteFile (WININET.@)
2202
 *
2203
 * Write data to an open internet file
2204 2205 2206 2207 2208 2209
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
2210
BOOL WINAPI InternetWriteFile(HINTERNET hFile, LPCVOID lpBuffer,
2211 2212
	DWORD dwNumOfBytesToWrite, LPDWORD lpdwNumOfBytesWritten)
{
2213
    object_header_t *lpwh;
2214
    BOOL res;
2215

2216
    TRACE("(%p %p %ld %p)\n", hFile, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
2217

2218
    lpwh = get_handle_object( hFile );
2219 2220 2221 2222 2223
    if (!lpwh) {
        WARN("Invalid handle\n");
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }
2224

2225
    if(lpwh->vtbl->WriteFile) {
2226
        res = lpwh->vtbl->WriteFile(lpwh, lpBuffer, dwNumOfBytesToWrite, lpdwNumOfBytesWritten);
2227
    }else {
2228
        WARN("No Writefile method.\n");
2229
        res = ERROR_INVALID_HANDLE;
2230 2231
    }

2232
    WININET_Release( lpwh );
2233

2234 2235 2236
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
2237 2238 2239
}


2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250
/***********************************************************************
 *           InternetReadFile (WININET.@)
 *
 * Read data from an open internet file
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetReadFile(HINTERNET hFile, LPVOID lpBuffer,
2251
        DWORD dwNumOfBytesToRead, LPDWORD pdwNumOfBytesRead)
2252
{
2253
    object_header_t *hdr;
2254
    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2255

2256
    TRACE("%p %p %ld %p\n", hFile, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead);
2257

2258
    hdr = get_handle_object(hFile);
2259
    if (!hdr) {
2260
        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2261 2262 2263
        return FALSE;
    }

2264 2265 2266 2267 2268
    if(hdr->vtbl->ReadFile) {
        res = hdr->vtbl->ReadFile(hdr, lpBuffer, dwNumOfBytesToRead, pdwNumOfBytesRead, 0, 0);
        if(res == ERROR_IO_PENDING)
            *pdwNumOfBytesRead = 0;
    }
2269

2270 2271
    WININET_Release(hdr);

2272
    TRACE("-- %s (%lu) (bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE", res,
2273 2274
          pdwNumOfBytesRead ? *pdwNumOfBytesRead : -1);

2275
    SetLastError(res);
2276
    return res == ERROR_SUCCESS;
2277 2278
}

2279 2280 2281 2282 2283
/***********************************************************************
 *           InternetReadFileExA (WININET.@)
 *
 * Read data from an open internet file
 *
2284 2285 2286 2287 2288 2289
 * PARAMS
 *  hFile         [I] Handle returned by InternetOpenUrl or HttpOpenRequest.
 *  lpBuffersOut  [I/O] Buffer.
 *  dwFlags       [I] Flags. See notes.
 *  dwContext     [I] Context for callbacks.
 *
2290 2291 2292 2293
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304
 * NOTES
 *  The parameter dwFlags include zero or more of the following flags:
 *|IRF_ASYNC - Makes the call asynchronous.
 *|IRF_SYNC - Makes the call synchronous.
 *|IRF_USE_CONTEXT - Forces dwContext to be used.
 *|IRF_NO_WAIT - Don't block if the data is not available, just return what is available.
 *
 * However, in testing IRF_USE_CONTEXT seems to have no effect - dwContext isn't used.
 *
 * SEE
 *  InternetOpenUrlA(), HttpOpenRequestA()
2305
 */
2306
BOOL WINAPI InternetReadFileExA(HINTERNET hFile, LPINTERNET_BUFFERSA lpBuffersOut,
2307
	DWORD dwFlags, DWORD_PTR dwContext)
2308
{
2309
    object_header_t *hdr;
2310
    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2311

2312
    TRACE("(%p %p 0x%lx 0x%Ix)\n", hFile, lpBuffersOut, dwFlags, dwContext);
2313

2314 2315 2316 2317 2318
    if (lpBuffersOut->dwStructSize != sizeof(*lpBuffersOut)) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

2319
    hdr = get_handle_object(hFile);
2320
    if (!hdr) {
2321
        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
2322 2323 2324
        return FALSE;
    }

2325 2326
    if(hdr->vtbl->ReadFile)
        res = hdr->vtbl->ReadFile(hdr, lpBuffersOut->lpvBuffer, lpBuffersOut->dwBufferLength,
2327
                &lpBuffersOut->dwBufferLength, dwFlags, dwContext);
2328

2329
    WININET_Release(hdr);
2330

2331
    TRACE("-- %s (%lu, bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
2332
          res, lpBuffersOut->dwBufferLength);
2333

2334 2335 2336
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
2337 2338 2339 2340
}

/***********************************************************************
 *           InternetReadFileExW (WININET.@)
2341 2342
 * SEE
 *  InternetReadFileExA()
2343 2344
 */
BOOL WINAPI InternetReadFileExW(HINTERNET hFile, LPINTERNET_BUFFERSW lpBuffer,
2345
	DWORD dwFlags, DWORD_PTR dwContext)
2346
{
2347
    object_header_t *hdr;
2348 2349
    DWORD res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;

2350
    TRACE("(%p %p 0x%lx 0x%Ix)\n", hFile, lpBuffer, dwFlags, dwContext);
2351

2352
    if (!lpBuffer || lpBuffer->dwStructSize != sizeof(*lpBuffer)) {
2353 2354 2355 2356
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

2357
    hdr = get_handle_object(hFile);
2358 2359 2360 2361 2362
    if (!hdr) {
        INTERNET_SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

2363 2364
    if(hdr->vtbl->ReadFile)
        res = hdr->vtbl->ReadFile(hdr, lpBuffer->lpvBuffer, lpBuffer->dwBufferLength, &lpBuffer->dwBufferLength,
2365
                dwFlags, dwContext);
2366 2367 2368

    WININET_Release(hdr);

2369
    TRACE("-- %s (%lu, bytes read: %ld)\n", res == ERROR_SUCCESS ? "TRUE": "FALSE",
2370 2371 2372 2373 2374
          res, lpBuffer->dwBufferLength);

    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
2375
}
2376

2377
static IP_ADAPTER_ADDRESSES *get_adapters(void)
2378
{
2379 2380 2381
    ULONG err, size = 1024, flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
                                    GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
    IP_ADAPTER_ADDRESSES *tmp, *ret;
2382

2383
    if (!(ret = malloc( size ))) return NULL;
2384 2385 2386
    err = GetAdaptersAddresses( AF_UNSPEC, flags, NULL, ret, &size );
    while (err == ERROR_BUFFER_OVERFLOW)
    {
2387
        if (!(tmp = realloc( ret, size ))) break;
2388 2389 2390 2391
        ret = tmp;
        err = GetAdaptersAddresses( AF_UNSPEC, flags, NULL, ret, &size );
    }
    if (err == ERROR_SUCCESS) return ret;
2392
    free( ret );
2393 2394
    return NULL;
}
2395

2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415
static WCHAR *detect_proxy_autoconfig_url_dhcp(void)
{
    IP_ADAPTER_ADDRESSES *adapters, *ptr;
    DHCPCAPI_PARAMS_ARRAY send_params, recv_params;
    DHCPCAPI_PARAMS param;
    WCHAR name[MAX_ADAPTER_NAME_LENGTH + 1], *ret = NULL;
    DWORD err, size;
    BYTE *tmp, *buf = NULL;

    if (!(adapters = get_adapters())) return NULL;

    memset( &send_params, 0, sizeof(send_params) );
    memset( &param, 0, sizeof(param) );
    param.OptionId = OPTION_MSFT_IE_PROXY;
    recv_params.nParams = 1;
    recv_params.Params  = &param;

    for (ptr = adapters; ptr; ptr = ptr->Next)
    {
        MultiByteToWideChar( CP_ACP, 0, ptr->AdapterName, -1, name, ARRAY_SIZE(name) );
2416
        TRACE( "adapter '%s' type %lu dhcpv4 enabled %d\n", wine_dbgstr_w(name), ptr->IfType, ptr->Dhcpv4Enabled );
2417 2418 2419

        if (ptr->IfType == IF_TYPE_SOFTWARE_LOOPBACK) continue;
        /* FIXME: also skip adapters where DHCP is disabled */
2420

2421
        size = 256;
2422
        if (!(buf = malloc( size ))) goto done;
2423 2424 2425 2426
        err = DhcpRequestParams( DHCPCAPI_REQUEST_SYNCHRONOUS, NULL, name, NULL, send_params, recv_params,
                                 buf, &size, NULL );
        while (err == ERROR_MORE_DATA)
        {
2427
            if (!(tmp = realloc( buf, size ))) goto done;
2428 2429 2430 2431 2432 2433 2434
            buf = tmp;
            err = DhcpRequestParams( DHCPCAPI_REQUEST_SYNCHRONOUS, NULL, name, NULL, send_params, recv_params,
                                     buf, &size, NULL );
        }
        if (err == ERROR_SUCCESS && param.nBytesData)
        {
            int len = MultiByteToWideChar( CP_ACP, 0, (const char *)param.Data, param.nBytesData, NULL, 0 );
2435
            if ((ret = malloc( (len + 1) * sizeof(WCHAR) )))
2436 2437 2438 2439 2440 2441 2442
            {
                MultiByteToWideChar( CP_ACP, 0,  (const char *)param.Data, param.nBytesData, ret, len );
                ret[len] = 0;
            }
            TRACE("returning %s\n", debugstr_w(ret));
            break;
        }
2443 2444
        free( buf );
        buf = NULL;
2445 2446 2447
    }

done:
2448 2449
    free( buf );
    free( adapters );
2450 2451 2452 2453 2454 2455 2456 2457 2458 2459
    return ret;
}

static char *get_computer_name( COMPUTER_NAME_FORMAT format )
{
    char *ret;
    DWORD size = 0;

    GetComputerNameExA( format, NULL, &size );
    if (GetLastError() != ERROR_MORE_DATA) return NULL;
2460
    if (!(ret = malloc( size ))) return NULL;
2461
    if (!GetComputerNameExA( format, ret, &size ))
2462
    {
2463
        free( ret );
2464
        return NULL;
2465
    }
2466 2467 2468 2469 2470 2471 2472 2473
    return ret;
}

static BOOL is_domain_suffix( const char *domain, const char *suffix )
{
    int len_domain = strlen( domain ), len_suffix = strlen( suffix );

    if (len_suffix > len_domain) return FALSE;
2474
    if (!stricmp( domain + len_domain - len_suffix, suffix )) return TRUE;
2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493
    return FALSE;
}

static int reverse_lookup( const struct addrinfo *ai, char *hostname, size_t len )
{
    return getnameinfo( ai->ai_addr, ai->ai_addrlen, hostname, len, NULL, 0, 0 );
}

static WCHAR *build_wpad_url( const char *hostname, const struct addrinfo *ai )
{
    char name[NI_MAXHOST];
    WCHAR *ret, *p;
    int len;

    while (ai && ai->ai_family != AF_INET && ai->ai_family != AF_INET6) ai = ai->ai_next;
    if (!ai) return NULL;

    if (!reverse_lookup( ai, name, sizeof(name) )) hostname = name;

2494
    len = lstrlenW( L"http://" ) + strlen( hostname ) + lstrlenW( L"/wpad.dat" );
2495
    if (!(ret = p = GlobalAlloc( 0, (len + 1) * sizeof(WCHAR) ))) return NULL;
2496 2497
    lstrcpyW( p, L"http://" );
    p += lstrlenW( L"http://" );
2498
    while (*hostname) { *p++ = *hostname++; }
2499
    lstrcpyW( p, L"/wpad.dat" );
2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510
    return ret;
}

static WCHAR *detect_proxy_autoconfig_url_dns(void)
{
    char *fqdn, *domain, *p;
    WCHAR *ret;

    if (!(fqdn = get_computer_name( ComputerNamePhysicalDnsFullyQualified ))) return NULL;
    if (!(domain = get_computer_name( ComputerNamePhysicalDnsDomain )))
    {
2511
        free( fqdn );
2512 2513 2514 2515
        return NULL;
    }
    p = fqdn;
    while ((p = strchr( p, '.' )) && is_domain_suffix( p + 1, domain ))
2516
    {
2517
        char *name;
2518
        struct addrinfo *ai, hints;
2519 2520
        int res;

2521
        if (!(name = malloc( sizeof("wpad") + strlen(p) )))
2522
        {
2523 2524
            free( fqdn );
            free( domain );
2525 2526 2527 2528
            return NULL;
        }
        strcpy( name, "wpad" );
        strcat( name, p );
2529 2530 2531 2532
        memset( &hints, 0, sizeof(hints) );
        hints.ai_flags  = AI_ALL | AI_DNS_ONLY;
        hints.ai_family = AF_UNSPEC;
        res = getaddrinfo( name, NULL, &hints, &ai );
2533 2534 2535 2536 2537 2538 2539
        if (!res)
        {
            ret = build_wpad_url( name, ai );
            freeaddrinfo( ai );
            if (ret)
            {
                TRACE("returning %s\n", debugstr_w(ret));
2540
                free( name );
2541 2542 2543
                break;
            }
        }
2544
       free( name );
2545
       p++;
2546
    }
2547 2548
    free( domain );
    free( fqdn );
2549 2550 2551 2552 2553 2554 2555
    return ret;
}

static WCHAR *get_proxy_autoconfig_url(void)
{
    WCHAR *ret = detect_proxy_autoconfig_url_dhcp();
    if (!ret) ret = detect_proxy_autoconfig_url_dns();
2556 2557 2558
    return ret;
}

2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589
static WCHAR *copy_optionW(WCHAR *value)
{
    DWORD len;
    void *tmp;

    if (!value)
        return NULL;

    len = (wcslen(value) + 1) * sizeof(WCHAR);
    if (!(tmp = HeapAlloc(GetProcessHeap(), 0, len)))
        return NULL;

    return memcpy(tmp, value, len);
}

static char *copy_optionA(WCHAR *value)
{
    DWORD len;
    void *tmp;

    if (!value)
        return NULL;

    len = wcslen(value) * 3 + 1;
    if (!(tmp = HeapAlloc(GetProcessHeap(), 0, len)))
        return NULL;

    WideCharToMultiByte(CP_ACP, 0, value, -1, tmp, len, NULL, NULL);
    return tmp;
}

2590
static DWORD query_global_option(DWORD option, void *buffer, DWORD *size, BOOL unicode)
2591
{
2592 2593
    /* FIXME: This function currently handles more options than it should. Options requiring
     * proper handles should be moved to proper functions */
2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608
    switch(option) {
    case INTERNET_OPTION_HTTP_VERSION:
        if (*size < sizeof(HTTP_VERSION_INFO))
            return ERROR_INSUFFICIENT_BUFFER;

        /*
         * Presently hardcoded to 1.1
         */
        ((HTTP_VERSION_INFO*)buffer)->dwMajorVersion = 1;
        ((HTTP_VERSION_INFO*)buffer)->dwMinorVersion = 1;
        *size = sizeof(HTTP_VERSION_INFO);

        return ERROR_SUCCESS;

    case INTERNET_OPTION_CONNECTED_STATE:
2609 2610
        FIXME("INTERNET_OPTION_CONNECTED_STATE: semi-stub\n");

2611 2612 2613 2614 2615 2616 2617
        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *(ULONG*)buffer = INTERNET_STATE_CONNECTED;
        *size = sizeof(ULONG);

        return ERROR_SUCCESS;
2618 2619

    case INTERNET_OPTION_PROXY: {
2620
        appinfo_t ai;
2621
        BOOL ret;
2622 2623

        TRACE("Getting global proxy info\n");
2624
        memset(&ai, 0, sizeof(appinfo_t));
2625 2626
        INTERNET_ConfigureProxy(&ai);

2627 2628 2629
        ret = APPINFO_QueryOption(&ai.hdr, INTERNET_OPTION_PROXY, buffer, size, unicode); /* FIXME */
        APPINFO_Destroy(&ai.hdr);
        return ret;
2630
    }
2631

2632 2633
    case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
        TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");
2634

2635 2636
        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;
2637

2638
        *(ULONG*)buffer = max_conns;
2639
        *size = sizeof(ULONG);
2640

2641
        return ERROR_SUCCESS;
2642

2643 2644
    case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
            TRACE("INTERNET_OPTION_MAX_CONNS_1_0_SERVER\n");
2645

2646 2647
            if (*size < sizeof(ULONG))
                return ERROR_INSUFFICIENT_BUFFER;
2648

2649
            *(ULONG*)buffer = max_1_0_conns;
2650
            *size = sizeof(ULONG);
2651

2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672
            return ERROR_SUCCESS;

    case INTERNET_OPTION_SECURITY_FLAGS:
        FIXME("INTERNET_OPTION_SECURITY_FLAGS: Stub\n");
        return ERROR_SUCCESS;

    case INTERNET_OPTION_VERSION: {
        static const INTERNET_VERSION_INFO info = { 1, 2 };

        TRACE("INTERNET_OPTION_VERSION\n");

        if (*size < sizeof(INTERNET_VERSION_INFO))
            return ERROR_INSUFFICIENT_BUFFER;

        memcpy(buffer, &info, sizeof(info));
        *size = sizeof(info);

        return ERROR_SUCCESS;
    }

    case INTERNET_OPTION_PER_CONNECTION_OPTION: {
2673
        WCHAR *url;
2674
        INTERNET_PER_CONN_OPTION_LISTW *con = buffer;
2675
        INTERNET_PER_CONN_OPTION_LISTA *conA = buffer;
2676
        DWORD res = ERROR_SUCCESS, i;
2677 2678
        proxyinfo_t pi;
        LONG ret;
2679 2680

        TRACE("Getting global proxy info\n");
2681 2682
        if((ret = INTERNET_LoadProxySettings(&pi)))
            return ret;
2683 2684 2685

        FIXME("INTERNET_OPTION_PER_CONNECTION_OPTION stub\n");

2686
        if (*size < sizeof(INTERNET_PER_CONN_OPTION_LISTW)) {
2687
            FreeProxyInfo(&pi);
2688
            return ERROR_INSUFFICIENT_BUFFER;
2689
        }
2690

2691 2692
        url = get_proxy_autoconfig_url();

2693
        for (i = 0; i < con->dwOptionCount; i++) {
2694
            INTERNET_PER_CONN_OPTIONW *optionW = con->pOptions + i;
2695
            INTERNET_PER_CONN_OPTIONA *optionA = conA->pOptions + i;
2696

2697
            switch (optionW->dwOption) {
2698
            case INTERNET_PER_CONN_FLAGS:
2699
                if(pi.proxyEnabled)
2700
                    optionW->Value.dwValue = PROXY_TYPE_PROXY;
2701
                else
2702
                    optionW->Value.dwValue = PROXY_TYPE_DIRECT;
2703
                if (url)
2704 2705
                    /* native includes PROXY_TYPE_DIRECT even if PROXY_TYPE_PROXY is set */
                    optionW->Value.dwValue |= PROXY_TYPE_DIRECT|PROXY_TYPE_AUTO_PROXY_URL;
2706 2707 2708
                break;

            case INTERNET_PER_CONN_PROXY_SERVER:
2709
                if (unicode)
2710
                    optionW->Value.pszValue = copy_optionW(pi.proxy);
2711
                else
2712
                    optionA->Value.pszValue = copy_optionA(pi.proxy);
2713 2714
                break;

2715
            case INTERNET_PER_CONN_PROXY_BYPASS:
2716
                if (unicode)
2717
                    optionW->Value.pszValue = copy_optionW(pi.proxyBypass);
2718
                else
2719
                    optionA->Value.pszValue = copy_optionA(pi.proxyBypass);
2720 2721
                break;

2722
            case INTERNET_PER_CONN_AUTOCONFIG_URL:
2723
                if (!url)
2724 2725
                    optionW->Value.pszValue = NULL;
                else if (unicode)
2726
                    optionW->Value.pszValue = copy_optionW(url);
2727
                else
2728
                    optionA->Value.pszValue = copy_optionA(url);
2729 2730
                break;

2731
            case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
2732 2733 2734
                optionW->Value.dwValue = AUTO_PROXY_FLAG_ALWAYS_DETECT;
                break;

2735 2736 2737 2738
            case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
            case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
2739
                FIXME("Unhandled dwOption %ld\n", optionW->dwOption);
2740
                memset(&optionW->Value, 0, sizeof(optionW->Value));
2741 2742 2743
                break;

            default:
2744
                FIXME("Unknown dwOption %ld\n", optionW->dwOption);
2745 2746 2747
                res = ERROR_INVALID_PARAMETER;
                break;
            }
2748
        }
2749
        free(url);
2750
        FreeProxyInfo(&pi);
2751 2752 2753

        return res;
    }
2754
    case INTERNET_OPTION_REQUEST_FLAGS:
Juan Lang's avatar
Juan Lang committed
2755
    case INTERNET_OPTION_USER_AGENT:
2756
        *size = 0;
Juan Lang's avatar
Juan Lang committed
2757
        return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2758 2759
    case INTERNET_OPTION_POLICY:
        return ERROR_INVALID_PARAMETER;
2760 2761 2762 2763 2764 2765 2766 2767 2768 2769
    case INTERNET_OPTION_CONNECT_TIMEOUT:
        TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");

        if (*size < sizeof(ULONG))
            return ERROR_INSUFFICIENT_BUFFER;

        *(ULONG*)buffer = connect_timeout;
        *size = sizeof(ULONG);

        return ERROR_SUCCESS;
2770 2771
    }

2772
    FIXME("Stub for %ld\n", option);
2773 2774 2775 2776 2777 2778
    return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
}

DWORD INET_QueryOption(object_header_t *hdr, DWORD option, void *buffer, DWORD *size, BOOL unicode)
{
    switch(option) {
2779 2780 2781 2782
    case INTERNET_OPTION_CONTEXT_VALUE:
        if (!size)
            return ERROR_INVALID_PARAMETER;

2783
        if (*size < sizeof(DWORD_PTR)) {
2784 2785 2786 2787 2788 2789 2790 2791 2792
            *size = sizeof(DWORD_PTR);
            return ERROR_INSUFFICIENT_BUFFER;
        }
        if (!buffer)
            return ERROR_INVALID_PARAMETER;

        *(DWORD_PTR *)buffer = hdr->dwContext;
        *size = sizeof(DWORD_PTR);
        return ERROR_SUCCESS;
2793

2794 2795 2796 2797 2798
    case INTERNET_OPTION_REQUEST_FLAGS:
        WARN("INTERNET_OPTION_REQUEST_FLAGS\n");
        *size = sizeof(DWORD);
        return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;

2799 2800
    case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
    case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2801
        WARN("Called on global option %lu\n", option);
2802
        return ERROR_INTERNET_INVALID_OPERATION;
2803 2804
    }

2805 2806
    /* FIXME: we shouldn't call it here */
    return query_global_option(option, buffer, size, unicode);
2807 2808
}

2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821
/***********************************************************************
 *           InternetQueryOptionW (WININET.@)
 *
 * Queries an options on the specified handle
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetQueryOptionW(HINTERNET hInternet, DWORD dwOption,
                                 LPVOID lpBuffer, LPDWORD lpdwBufferLength)
{
2822
    object_header_t *hdr;
2823 2824
    DWORD res = ERROR_INVALID_HANDLE;

2825
    TRACE("%p %ld %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
2826 2827

    if(hInternet) {
2828
        hdr = get_handle_object(hInternet);
2829 2830 2831 2832 2833
        if (hdr) {
            res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, TRUE);
            WININET_Release(hdr);
        }
    }else {
2834
        res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, TRUE);
2835 2836 2837 2838 2839
    }

    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854
}

/***********************************************************************
 *           InternetQueryOptionA (WININET.@)
 *
 * Queries an options on the specified handle
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
BOOL WINAPI InternetQueryOptionA(HINTERNET hInternet, DWORD dwOption,
                                 LPVOID lpBuffer, LPDWORD lpdwBufferLength)
{
2855
    object_header_t *hdr;
2856 2857
    DWORD res = ERROR_INVALID_HANDLE;

2858
    TRACE("%p %ld %p %p\n", hInternet, dwOption, lpBuffer, lpdwBufferLength);
2859 2860

    if(hInternet) {
2861
        hdr = get_handle_object(hInternet);
2862 2863 2864 2865 2866
        if (hdr) {
            res = hdr->vtbl->QueryOption(hdr, dwOption, lpBuffer, lpdwBufferLength, FALSE);
            WININET_Release(hdr);
        }
    }else {
2867
        res = query_global_option(dwOption, lpBuffer, lpdwBufferLength, FALSE);
2868 2869 2870 2871 2872
    }

    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
2873 2874
}

2875 2876 2877
DWORD INET_SetOption(object_header_t *hdr, DWORD option, void *buf, DWORD size)
{
    switch(option) {
2878 2879 2880 2881
    case INTERNET_OPTION_SETTINGS_CHANGED:
        FIXME("INTERNETOPTION_SETTINGS_CHANGED semi-stub\n");
        collect_connections(COLLECT_CONNECTIONS);
        return ERROR_SUCCESS;
2882
    case INTERNET_OPTION_CALLBACK:
2883
        WARN("Not settable option %lu\n", option);
2884
        return ERROR_INTERNET_OPTION_NOT_SETTABLE;
2885 2886
    case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
    case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
2887
        WARN("Called on global option %lu\n", option);
2888
        return ERROR_INTERNET_INVALID_OPERATION;
2889 2890
    case INTERNET_OPTION_REFRESH:
        return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2891 2892 2893 2894
    }

    return ERROR_INTERNET_INVALID_OPTION;
}
2895

2896 2897 2898 2899
static DWORD set_global_option(DWORD option, void *buf, DWORD size)
{
    switch(option) {
    case INTERNET_OPTION_CALLBACK:
2900
        WARN("Not global option %lu\n", option);
2901
        return ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923

    case INTERNET_OPTION_MAX_CONNS_PER_SERVER:
        TRACE("INTERNET_OPTION_MAX_CONNS_PER_SERVER\n");

        if(size != sizeof(max_conns))
            return ERROR_INTERNET_BAD_OPTION_LENGTH;
        if(!*(ULONG*)buf)
            return ERROR_BAD_ARGUMENTS;

        max_conns = *(ULONG*)buf;
        return ERROR_SUCCESS;

    case INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER:
        TRACE("INTERNET_OPTION_MAX_CONNS_PER_1_0_SERVER\n");

        if(size != sizeof(max_1_0_conns))
            return ERROR_INTERNET_BAD_OPTION_LENGTH;
        if(!*(ULONG*)buf)
            return ERROR_BAD_ARGUMENTS;

        max_1_0_conns = *(ULONG*)buf;
        return ERROR_SUCCESS;
2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934

    case INTERNET_OPTION_CONNECT_TIMEOUT:
        TRACE("INTERNET_OPTION_CONNECT_TIMEOUT\n");

        if(size != sizeof(connect_timeout))
            return ERROR_INTERNET_BAD_OPTION_LENGTH;
        if(!*(ULONG*)buf)
            return ERROR_BAD_ARGUMENTS;

        connect_timeout = *(ULONG*)buf;
        return ERROR_SUCCESS;
2935

2936 2937 2938 2939 2940 2941
    case INTERNET_OPTION_SUPPRESS_BEHAVIOR:
        FIXME("INTERNET_OPTION_SUPPRESS_BEHAVIOR stub\n");

        if(size != sizeof(ULONG))
            return ERROR_INTERNET_BAD_OPTION_LENGTH;

2942
        FIXME("%08lx\n", *(ULONG*)buf);
2943
        return ERROR_SUCCESS;
2944 2945
    }

2946
    return INET_SetOption(NULL, option, buf, size);
2947 2948
}

2949
/***********************************************************************
2950
 *           InternetSetOptionW (WININET.@)
2951 2952 2953 2954 2955 2956 2957 2958
 *
 * Sets an options on the specified handle
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
2959
BOOL WINAPI InternetSetOptionW(HINTERNET hInternet, DWORD dwOption,
2960 2961
                           LPVOID lpBuffer, DWORD dwBufferLength)
{
2962
    object_header_t *lpwhh;
2963
    BOOL ret = TRUE;
2964
    DWORD res;
2965

2966
    TRACE("(%p %ld %p %ld)\n", hInternet, dwOption, lpBuffer, dwBufferLength);
2967

2968
    lpwhh = (object_header_t*) get_handle_object( hInternet );
2969
    if(lpwhh)
2970
        res = lpwhh->vtbl->SetOption(lpwhh, dwOption, lpBuffer, dwBufferLength);
2971 2972
    else
        res = set_global_option(dwOption, lpBuffer, dwBufferLength);
2973

2974 2975 2976
    if(res != ERROR_INTERNET_INVALID_OPTION) {
        if(lpwhh)
            WININET_Release(lpwhh);
2977

2978 2979 2980 2981
        if(res != ERROR_SUCCESS)
            SetLastError(res);

        return res == ERROR_SUCCESS;
2982
    }
2983 2984 2985

    switch (dwOption)
    {
2986 2987 2988
    case INTERNET_OPTION_HTTP_VERSION:
      {
        HTTP_VERSION_INFO* pVersion=(HTTP_VERSION_INFO*)lpBuffer;
2989
        FIXME("Option INTERNET_OPTION_HTTP_VERSION(%ld,%ld): STUB\n",pVersion->dwMajorVersion,pVersion->dwMinorVersion);
2990 2991 2992 2993
      }
      break;
    case INTERNET_OPTION_ERROR_MASK:
      {
2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004
        if(!lpwhh) {
            SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
            return FALSE;
        } else if(*(ULONG*)lpBuffer & (~(INTERNET_ERROR_MASK_INSERT_CDROM|
                        INTERNET_ERROR_MASK_COMBINED_SEC_CERT|
                        INTERNET_ERROR_MASK_LOGIN_FAILURE_DISPLAY_ENTITY_BODY))) {
            SetLastError(ERROR_INVALID_PARAMETER);
            ret = FALSE;
        } else if(dwBufferLength != sizeof(ULONG)) {
            SetLastError(ERROR_INTERNET_BAD_OPTION_LENGTH);
            ret = FALSE;
3005
        } else {
3006
            TRACE("INTERNET_OPTION_ERROR_MASK: %lx\n", *(ULONG*)lpBuffer);
3007
            lpwhh->ErrorMask = *(ULONG*)lpBuffer;
3008
        }
3009 3010
      }
      break;
3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023
    case INTERNET_OPTION_PROXY:
    {
        INTERNET_PROXY_INFOW *info = lpBuffer;

        if (!lpBuffer || dwBufferLength < sizeof(INTERNET_PROXY_INFOW))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            return FALSE;
        }
        if (!hInternet)
        {
            EnterCriticalSection( &WININET_cs );
            free_global_proxy();
3024
            global_proxy = malloc(sizeof(proxyinfo_t));
3025 3026 3027 3028 3029
            if (global_proxy)
            {
                if (info->dwAccessType == INTERNET_OPEN_TYPE_PROXY)
                {
                    global_proxy->proxyEnabled = 1;
3030 3031
                    global_proxy->proxy = wcsdup(info->lpszProxy);
                    global_proxy->proxyBypass = wcsdup(info->lpszProxyBypass);
3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052
                }
                else
                {
                    global_proxy->proxyEnabled = 0;
                    global_proxy->proxy = global_proxy->proxyBypass = NULL;
                }
            }
            LeaveCriticalSection( &WININET_cs );
        }
        else
        {
            /* In general, each type of object should handle
             * INTERNET_OPTION_PROXY directly.  This FIXME ensures it doesn't
             * get silently dropped.
             */
            FIXME("INTERNET_OPTION_PROXY unimplemented\n");
            SetLastError(ERROR_INTERNET_INVALID_OPTION);
            ret = FALSE;
        }
        break;
    }
3053 3054
    case INTERNET_OPTION_CODEPAGE:
      {
3055
        ULONG codepage = *(ULONG *)lpBuffer;
3056
        FIXME("Option INTERNET_OPTION_CODEPAGE (%ld): STUB\n", codepage);
3057 3058 3059 3060
      }
      break;
    case INTERNET_OPTION_REQUEST_PRIORITY:
      {
3061
        ULONG priority = *(ULONG *)lpBuffer;
3062
        FIXME("Option INTERNET_OPTION_REQUEST_PRIORITY (%ld): STUB\n", priority);
3063 3064
      }
      break;
3065 3066
    case INTERNET_OPTION_CONNECT_TIMEOUT:
      {
3067
        ULONG connecttimeout = *(ULONG *)lpBuffer;
3068
        FIXME("Option INTERNET_OPTION_CONNECT_TIMEOUT (%ld): STUB\n", connecttimeout);
3069 3070 3071 3072
      }
      break;
    case INTERNET_OPTION_DATA_RECEIVE_TIMEOUT:
      {
3073
        ULONG receivetimeout = *(ULONG *)lpBuffer;
3074
        FIXME("Option INTERNET_OPTION_DATA_RECEIVE_TIMEOUT (%ld): STUB\n", receivetimeout);
3075 3076
      }
      break;
3077 3078 3079 3080
    case INTERNET_OPTION_RESET_URLCACHE_SESSION:
        FIXME("Option INTERNET_OPTION_RESET_URLCACHE_SESSION: STUB\n");
        break;
    case INTERNET_OPTION_END_BROWSER_SESSION:
3081 3082
        FIXME("Option INTERNET_OPTION_END_BROWSER_SESSION: semi-stub\n");
        free_cookie();
3083
        free_authorization_cache();
3084 3085 3086 3087
        break;
    case INTERNET_OPTION_CONNECTED_STATE:
        FIXME("Option INTERNET_OPTION_CONNECTED_STATE: STUB\n");
        break;
3088 3089 3090
    case INTERNET_OPTION_DISABLE_PASSPORT_AUTH:
	TRACE("Option INTERNET_OPTION_DISABLE_PASSPORT_AUTH: harmless stub, since not enabled\n");
	break;
3091 3092 3093
    case INTERNET_OPTION_IGNORE_OFFLINE:
        FIXME("Option INTERNET_OPTION_IGNORE_OFFLINE: STUB\n");
        break;
3094
    case INTERNET_OPTION_SEND_TIMEOUT:
3095
    case INTERNET_OPTION_RECEIVE_TIMEOUT:
3096
    case INTERNET_OPTION_DATA_SEND_TIMEOUT:
3097 3098
    {
        ULONG timeout = *(ULONG *)lpBuffer;
3099
        FIXME("INTERNET_OPTION_SEND/RECEIVE_TIMEOUT/DATA_SEND_TIMEOUT %ld\n", timeout);
3100
        break;
3101
    }
3102
    case INTERNET_OPTION_CONNECT_RETRIES:
3103 3104
    {
        ULONG retries = *(ULONG *)lpBuffer;
3105
        FIXME("INTERNET_OPTION_CONNECT_RETRIES %ld\n", retries);
3106
        break;
3107
    }
3108
    case INTERNET_OPTION_CONTEXT_VALUE:
3109
    {
3110 3111 3112 3113 3114
        if (!lpwhh)
        {
            SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
            return FALSE;
        }
3115 3116 3117 3118 3119 3120 3121 3122 3123
        if (!lpBuffer || dwBufferLength != sizeof(DWORD_PTR))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            ret = FALSE;
        }
        else
            lpwhh->dwContext = *(DWORD_PTR *)lpBuffer;
        break;
    }
3124 3125 3126
    case INTERNET_OPTION_SECURITY_FLAGS:
	 FIXME("Option INTERNET_OPTION_SECURITY_FLAGS; STUB\n");
	 break;
3127 3128 3129
    case INTERNET_OPTION_DISABLE_AUTODIAL:
	 FIXME("Option INTERNET_OPTION_DISABLE_AUTODIAL; STUB\n");
	 break;
3130
    case INTERNET_OPTION_HTTP_DECODING:
3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142
        if (!lpwhh)
        {
            SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
            return FALSE;
        }
        if (!lpBuffer || dwBufferLength != sizeof(BOOL))
        {
            SetLastError(ERROR_INVALID_PARAMETER);
            ret = FALSE;
        }
        else
            lpwhh->decoding = *(BOOL *)lpBuffer;
3143 3144 3145
        break;
    case INTERNET_OPTION_COOKIES_3RD_PARTY:
        FIXME("INTERNET_OPTION_COOKIES_3RD_PARTY; STUB\n");
3146
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3147 3148 3149 3150
        ret = FALSE;
        break;
    case INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY:
        FIXME("INTERNET_OPTION_SEND_UTF8_SERVERNAME_TO_PROXY; STUB\n");
3151
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3152 3153 3154 3155
        ret = FALSE;
        break;
    case INTERNET_OPTION_CODEPAGE_PATH:
        FIXME("INTERNET_OPTION_CODEPAGE_PATH; STUB\n");
3156
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3157 3158 3159 3160
        ret = FALSE;
        break;
    case INTERNET_OPTION_CODEPAGE_EXTRA:
        FIXME("INTERNET_OPTION_CODEPAGE_EXTRA; STUB\n");
3161
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3162 3163 3164 3165
        ret = FALSE;
        break;
    case INTERNET_OPTION_IDN:
        FIXME("INTERNET_OPTION_IDN; STUB\n");
3166
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3167
        ret = FALSE;
3168
        break;
3169 3170 3171 3172
    case INTERNET_OPTION_POLICY:
        SetLastError(ERROR_INVALID_PARAMETER);
        ret = FALSE;
        break;
3173 3174 3175
    case INTERNET_OPTION_PER_CONNECTION_OPTION: {
        INTERNET_PER_CONN_OPTION_LISTW *con = lpBuffer;
        LONG res;
3176
        unsigned int i;
3177 3178
        proxyinfo_t pi;

3179
        if (INTERNET_LoadProxySettings(&pi)) return FALSE;
3180 3181 3182 3183 3184 3185

        for (i = 0; i < con->dwOptionCount; i++) {
            INTERNET_PER_CONN_OPTIONW *option = con->pOptions + i;

            switch (option->dwOption) {
            case INTERNET_PER_CONN_PROXY_SERVER:
3186 3187
                free(pi.proxy);
                pi.proxy = wcsdup(option->Value.pszValue);
3188 3189 3190
                break;

            case INTERNET_PER_CONN_FLAGS:
3191
                if(option->Value.dwValue & PROXY_TYPE_PROXY)
3192
                    pi.proxyEnabled = 1;
3193 3194 3195
                else
                {
                    if(option->Value.dwValue != PROXY_TYPE_DIRECT)
3196
                        FIXME("Unhandled flags: 0x%lx\n", option->Value.dwValue);
3197
                    pi.proxyEnabled = 0;
3198 3199 3200
                }
                break;

3201
            case INTERNET_PER_CONN_PROXY_BYPASS:
3202 3203
                free(pi.proxyBypass);
                pi.proxyBypass = wcsdup(option->Value.pszValue);
3204 3205
                break;

3206 3207 3208 3209 3210 3211
            case INTERNET_PER_CONN_AUTOCONFIG_URL:
            case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
            case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
            case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
3212
                FIXME("Unhandled dwOption %ld\n", option->dwOption);
3213 3214 3215
                break;

            default:
3216
                FIXME("Unknown dwOption %ld\n", option->dwOption);
3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229
                SetLastError(ERROR_INVALID_PARAMETER);
                break;
            }
        }

        if ((res = INTERNET_SaveProxySettings(&pi)))
            SetLastError(res);

        FreeProxyInfo(&pi);

        ret = (res == ERROR_SUCCESS);
        break;
        }
3230
    default:
3231
        FIXME("Option %ld STUB\n",dwOption);
3232
        SetLastError(ERROR_INTERNET_INVALID_OPTION);
3233 3234
        ret = FALSE;
        break;
3235
    }
3236 3237 3238

    if(lpwhh)
        WININET_Release( lpwhh );
3239

3240
    return ret;
3241 3242 3243 3244
}


/***********************************************************************
3245
 *           InternetSetOptionA (WININET.@)
3246 3247 3248 3249 3250 3251 3252 3253
 *
 * Sets an options on the specified handle.
 *
 * RETURNS
 *    TRUE  on success
 *    FALSE on failure
 *
 */
3254
BOOL WINAPI InternetSetOptionA(HINTERNET hInternet, DWORD dwOption,
3255 3256
                           LPVOID lpBuffer, DWORD dwBufferLength)
{
3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271
    LPVOID wbuffer;
    DWORD wlen;
    BOOL r;

    switch( dwOption )
    {
    case INTERNET_OPTION_PROXY:
        {
        LPINTERNET_PROXY_INFOA pi = (LPINTERNET_PROXY_INFOA) lpBuffer;
        LPINTERNET_PROXY_INFOW piw;
        DWORD proxlen, prbylen;
        LPWSTR prox, prby;

        proxlen = MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, NULL, 0);
        prbylen= MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, NULL, 0);
3272
        wlen = sizeof(*piw) + proxlen + prbylen;
3273
        wbuffer = malloc( wlen * sizeof(WCHAR) );
3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286
        piw = (LPINTERNET_PROXY_INFOW) wbuffer;
        piw->dwAccessType = pi->dwAccessType;
        prox = (LPWSTR) &piw[1];
        prby = &prox[proxlen+1];
        MultiByteToWideChar( CP_ACP, 0, pi->lpszProxy, -1, prox, proxlen);
        MultiByteToWideChar( CP_ACP, 0, pi->lpszProxyBypass, -1, prby, prbylen);
        piw->lpszProxy = prox;
        piw->lpszProxyBypass = prby;
        }
        break;
    case INTERNET_OPTION_USER_AGENT:
    case INTERNET_OPTION_USERNAME:
    case INTERNET_OPTION_PASSWORD:
3287 3288 3289
    case INTERNET_OPTION_PROXY_USERNAME:
    case INTERNET_OPTION_PROXY_PASSWORD:
        wlen = MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, NULL, 0 );
3290
        if (!(wbuffer = malloc( wlen * sizeof(WCHAR) ))) return ERROR_OUTOFMEMORY;
3291
        MultiByteToWideChar( CP_ACP, 0, lpBuffer, -1, wbuffer, wlen );
3292
        break;
3293
    case INTERNET_OPTION_PER_CONNECTION_OPTION: {
3294
        unsigned int i;
3295 3296 3297
        INTERNET_PER_CONN_OPTION_LISTW *listW;
        INTERNET_PER_CONN_OPTION_LISTA *listA = lpBuffer;
        wlen = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
3298
        wbuffer = malloc( wlen );
3299 3300 3301 3302 3303 3304
        listW = wbuffer;

        listW->dwSize = sizeof(INTERNET_PER_CONN_OPTION_LISTW);
        if (listA->pszConnection)
        {
            wlen = MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, NULL, 0 );
3305
            listW->pszConnection = malloc( wlen * sizeof(WCHAR) );
3306 3307 3308 3309 3310 3311
            MultiByteToWideChar( CP_ACP, 0, listA->pszConnection, -1, listW->pszConnection, wlen );
        }
        else
            listW->pszConnection = NULL;
        listW->dwOptionCount = listA->dwOptionCount;
        listW->dwOptionError = listA->dwOptionError;
3312
        listW->pOptions = malloc( sizeof(INTERNET_PER_CONN_OPTIONW) * listA->dwOptionCount );
3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328

        for (i = 0; i < listA->dwOptionCount; ++i) {
            INTERNET_PER_CONN_OPTIONA *optA = listA->pOptions + i;
            INTERNET_PER_CONN_OPTIONW *optW = listW->pOptions + i;

            optW->dwOption = optA->dwOption;

            switch (optA->dwOption) {
            case INTERNET_PER_CONN_AUTOCONFIG_URL:
            case INTERNET_PER_CONN_PROXY_BYPASS:
            case INTERNET_PER_CONN_PROXY_SERVER:
            case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
                if (optA->Value.pszValue)
                {
                    wlen = MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, NULL, 0 );
3329
                    optW->Value.pszValue = malloc( wlen * sizeof(WCHAR) );
3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341
                    MultiByteToWideChar( CP_ACP, 0, optA->Value.pszValue, -1, optW->Value.pszValue, wlen );
                }
                else
                    optW->Value.pszValue = NULL;
                break;
            case INTERNET_PER_CONN_AUTODISCOVERY_FLAGS:
            case INTERNET_PER_CONN_FLAGS:
            case INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS:
                optW->Value.dwValue = optA->Value.dwValue;
                break;
            case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME:
                optW->Value.ftValue = optA->Value.ftValue;
3342
                break;
3343
            default:
3344
                WARN("Unknown PER_CONN dwOption: %ld, guessing at conversion to Wide\n", optA->dwOption);
3345 3346 3347 3348 3349 3350
                optW->Value.dwValue = optA->Value.dwValue;
                break;
            }
        }
        }
        break;
3351 3352 3353 3354 3355 3356 3357 3358
    default:
        wbuffer = lpBuffer;
        wlen = dwBufferLength;
    }

    r = InternetSetOptionW(hInternet,dwOption, wbuffer, wlen);

    if( lpBuffer != wbuffer )
3359 3360 3361 3362
    {
        if (dwOption == INTERNET_OPTION_PER_CONNECTION_OPTION)
        {
            INTERNET_PER_CONN_OPTION_LISTW *list = wbuffer;
3363
            unsigned int i;
3364 3365 3366 3367 3368 3369 3370 3371
            for (i = 0; i < list->dwOptionCount; ++i) {
                INTERNET_PER_CONN_OPTIONW *opt = list->pOptions + i;
                switch (opt->dwOption) {
                case INTERNET_PER_CONN_AUTOCONFIG_URL:
                case INTERNET_PER_CONN_PROXY_BYPASS:
                case INTERNET_PER_CONN_PROXY_SERVER:
                case INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL:
                case INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL:
3372
                    free( opt->Value.pszValue );
3373 3374 3375 3376 3377
                    break;
                default:
                    break;
                }
            }
3378
            free( list->pOptions );
3379
        }
3380
        free( wbuffer );
3381
    }
3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392

    return r;
}


/***********************************************************************
 *           InternetSetOptionExA (WININET.@)
 */
BOOL WINAPI InternetSetOptionExA(HINTERNET hInternet, DWORD dwOption,
                           LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
{
3393
    FIXME("Flags %08lx ignored\n", dwFlags);
3394 3395 3396 3397 3398 3399 3400 3401 3402
    return InternetSetOptionA( hInternet, dwOption, lpBuffer, dwBufferLength );
}

/***********************************************************************
 *           InternetSetOptionExW (WININET.@)
 */
BOOL WINAPI InternetSetOptionExW(HINTERNET hInternet, DWORD dwOption,
                           LPVOID lpBuffer, DWORD dwBufferLength, DWORD dwFlags)
{
3403
    FIXME("Flags %08lx ignored\n", dwFlags);
3404 3405
    if( dwFlags & ~ISO_VALID_FLAGS )
    {
3406
        SetLastError( ERROR_INVALID_PARAMETER );
3407 3408 3409
        return FALSE;
    }
    return InternetSetOptionW( hInternet, dwOption, lpBuffer, dwBufferLength );
3410 3411
}

3412
static const WCHAR WININET_wkday[7][4] =
3413 3414
    { L"Sun", L"Mon", L"Tue", L"Wed",
      L"Thu", L"Fri", L"Sat"};
3415
static const WCHAR WININET_month[12][4] =
3416 3417 3418
    { L"Jan", L"Feb", L"Mar", L"Apr",
      L"May", L"Jun", L"Jul", L"Aug",
      L"Sep", L"Oct", L"Nov", L"Dec"};
3419

3420 3421 3422 3423 3424
static inline BOOL is_time_digit(const WCHAR c)
{
    return c >= '0' && c <= '9';
}

3425 3426 3427 3428 3429 3430 3431 3432
/***********************************************************************
 *           InternetTimeFromSystemTimeA (WININET.@)
 */
BOOL WINAPI InternetTimeFromSystemTimeA( const SYSTEMTIME* time, DWORD format, LPSTR string, DWORD size )
{
    BOOL ret;
    WCHAR stringW[INTERNET_RFC1123_BUFSIZE];

3433
    TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
3434

3435 3436 3437 3438 3439 3440
    if (!time || !string || format != INTERNET_RFC1123_FORMAT)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

3441 3442 3443 3444 3445 3446
    if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
        return FALSE;
    }

3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457
    ret = InternetTimeFromSystemTimeW( time, format, stringW, sizeof(stringW) );
    if (ret) WideCharToMultiByte( CP_ACP, 0, stringW, -1, string, size, NULL, NULL );

    return ret;
}

/***********************************************************************
 *           InternetTimeFromSystemTimeW (WININET.@)
 */
BOOL WINAPI InternetTimeFromSystemTimeW( const SYSTEMTIME* time, DWORD format, LPWSTR string, DWORD size )
{
3458
    TRACE( "%p 0x%08lx %p 0x%08lx\n", time, format, string, size );
3459

3460
    if (!time || !string || format != INTERNET_RFC1123_FORMAT)
3461 3462
    {
        SetLastError(ERROR_INVALID_PARAMETER);
3463
        return FALSE;
3464
    }
3465

3466 3467 3468
    if (size < INTERNET_RFC1123_BUFSIZE * sizeof(*string))
    {
        SetLastError(ERROR_INSUFFICIENT_BUFFER);
3469
        return FALSE;
3470
    }
3471

3472
    swprintf( string, size, L"%s, %02d %s %4d %02d:%02d:%02d GMT",
3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491
              WININET_wkday[time->wDayOfWeek],
              time->wDay,
              WININET_month[time->wMonth - 1],
              time->wYear,
              time->wHour,
              time->wMinute,
              time->wSecond );

    return TRUE;
}

/***********************************************************************
 *           InternetTimeToSystemTimeA (WININET.@)
 */
BOOL WINAPI InternetTimeToSystemTimeA( LPCSTR string, SYSTEMTIME* time, DWORD reserved )
{
    BOOL ret = FALSE;
    WCHAR *stringW;

3492
    TRACE( "%s %p 0x%08lx\n", debugstr_a(string), time, reserved );
3493

3494
    stringW = strdupAtoW( string );
3495 3496 3497
    if (stringW)
    {
        ret = InternetTimeToSystemTimeW( stringW, time, reserved );
3498
        free( stringW );
3499 3500 3501 3502 3503 3504 3505 3506 3507 3508
    }
    return ret;
}

/***********************************************************************
 *           InternetTimeToSystemTimeW (WININET.@)
 */
BOOL WINAPI InternetTimeToSystemTimeW( LPCWSTR string, SYSTEMTIME* time, DWORD reserved )
{
    unsigned int i;
3509 3510
    const WCHAR *s = string;
    WCHAR       *end;
3511

3512
    TRACE( "%s %p 0x%08lx\n", debugstr_w(string), time, reserved );
3513

3514 3515 3516 3517
    if (!string || !time) return FALSE;

    /* Windows does this too */
    GetSystemTime( time );
3518 3519 3520 3521 3522

    /*  Convert an RFC1123 time such as 'Fri, 07 Jan 2005 12:06:35 GMT' into
     *  a SYSTEMTIME structure.
     */

3523 3524
    while (*s && !iswalpha(*s) && !is_time_digit(*s)) s++;
    if (*s == '\0') return TRUE;
3525 3526
    time->wDayOfWeek = 7;

3527
    if (iswalpha(*s))
3528
    {
3529 3530
        if (s[1] == '\0' || s[2] == '\0') return TRUE;
        for (i = 0; i < 7; i++)
3531
        {
3532 3533 3534 3535 3536
            if (!wcsnicmp(WININET_wkday[i], s, 3))
            {
                time->wDayOfWeek = i;
                break;
            }
3537 3538
        }
    }
3539 3540 3541 3542 3543
    else if (is_time_digit(*s))
    {
        time->wDayOfWeek = wcstol(s, &end, 10);
        s = end;
    }
3544
    if (time->wDayOfWeek > 6) return TRUE;
3545 3546

    while (*s && !is_time_digit(*s)) s++;
3547
    time->wDay = wcstol( s, &end, 10 );
3548
    s = end;
3549

3550 3551
    while (*s && !iswalpha(*s) && !is_time_digit(*s)) s++;
    if (*s == '\0') return TRUE;
3552 3553
    time->wMonth = 0;

3554
    if (iswalpha(*s))
3555
    {
3556 3557
        if (s[1] == '\0' || s[2] == '\0') return TRUE;
        for (i = 0; i < 12; i++)
3558
        {
3559 3560 3561 3562 3563
            if (!wcsnicmp(WININET_month[i], s, 3))
            {
                time->wMonth = i + 1;
                break;
            }
3564 3565
        }
    }
3566 3567 3568 3569 3570
    else if (is_time_digit(*s))
    {
        time->wMonth = wcstol(s, &end, 10);
        s = end;
    }
3571
    if (time->wMonth == 0) return TRUE;
3572

3573
    while (*s && !is_time_digit(*s)) s++;
3574
    if (*s == '\0') return TRUE;
3575
    time->wYear = wcstol( s, &end, 10 );
3576
    s = end;
3577

3578
    while (*s && !is_time_digit(*s)) s++;
3579
    if (*s == '\0') return TRUE;
3580
    time->wHour = wcstol( s, &end, 10 );
3581
    s = end;
3582

3583
    while (*s && !is_time_digit(*s)) s++;
3584
    if (*s == '\0') return TRUE;
3585
    time->wMinute = wcstol( s, &end, 10 );
3586
    s = end;
3587

3588
    while (*s && !is_time_digit(*s)) s++;
3589
    if (*s == '\0') return TRUE;
3590
    time->wSecond = wcstol( s, &end, 10 );
3591
    s = end;
3592 3593 3594 3595

    time->wMilliseconds = 0;
    return TRUE;
}
3596

3597
/***********************************************************************
3598
 *	InternetCheckConnectionW (WININET.@)
3599 3600 3601 3602
 *
 * Pings a requested host to check internet connection
 *
 * RETURNS
3603
 *   TRUE on success and FALSE on failure. If a failure then
3604
 *   ERROR_NOT_CONNECTED is placed into GetLastError
3605 3606
 *
 */
3607
BOOL WINAPI InternetCheckConnectionW( LPCWSTR lpszUrl, DWORD dwFlags, DWORD dwReserved )
3608 3609 3610 3611 3612 3613 3614 3615
{
/*
 * this is a kludge which runs the resident ping program and reads the output.
 *
 * Anyone have a better idea?
 */

  BOOL   rc = FALSE;
3616
  static const CHAR ping[] = "ping -c 1 ";
3617
  static const CHAR redirect[] = " >/dev/null 2>/dev/null";
3618 3619
  WCHAR *host;
  DWORD len, host_len;
3620
  INTERNET_PORT port;
3621 3622
  int status = -1;

3623
  FIXME("(%s %lx %lx)\n", debugstr_w(lpszUrl), dwFlags, dwReserved);
3624

3625
  /*
3626 3627 3628 3629
   * Crack or set the Address
   */
  if (lpszUrl == NULL)
  {
3630
     /*
Austin English's avatar
Austin English committed
3631
      * According to the doc we are supposed to use the ip for the next
3632
      * server in the WnInet internal server database. I have
3633 3634 3635 3636
      * no idea what that is or how to get it.
      *
      * So someone needs to implement this.
      */
3637
     FIXME("Unimplemented with URL of NULL\n");
3638 3639 3640 3641
     return TRUE;
  }
  else
  {
3642
     URL_COMPONENTSW components = {sizeof(components)};
3643

3644
     components.dwHostNameLength = 1;
3645

3646
     if (!InternetCrackUrlW(lpszUrl,0,0,&components))
3647
       goto End;
3648

3649 3650
     host = components.lpszHostName;
     host_len = components.dwHostNameLength;
3651
     port = components.nPort;
3652
     TRACE("host name: %s port: %d\n",debugstr_wn(host, host_len), port);
3653 3654
  }

3655 3656
  if (dwFlags & FLAG_ICC_FORCE_CONNECTION)
  {
3657
      struct sockaddr_storage saddr;
3658
      int sa_len = sizeof(saddr);
3659
      WCHAR *host_z;
3660
      int fd;
3661
      BOOL b;
3662

3663
      host_z = strndupW(host, host_len);
3664 3665 3666 3667
      if (!host_z)
          return FALSE;

      b = GetAddress(host_z, port, (struct sockaddr *)&saddr, &sa_len, NULL);
3668
      free(host_z);
3669
      if(!b)
3670
          goto End;
3671
      init_winsock();
3672
      fd = socket(saddr.ss_family, SOCK_STREAM, 0);
3673 3674
      if (fd != -1)
      {
3675
          if (connect(fd, (struct sockaddr *)&saddr, sa_len) == 0)
3676
              rc = TRUE;
3677
          closesocket(fd);
3678 3679 3680 3681 3682 3683 3684
      }
  }
  else
  {
      /*
       * Build our ping command
       */
3685 3686 3687
      char *command;

      len = WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, NULL, 0, NULL, NULL);
3688
      command = malloc(strlen(ping) + len + strlen(redirect) + 1);
3689 3690 3691
      strcpy(command, ping);
      WideCharToMultiByte(CP_UNIXCP, 0, host, host_len, command+sizeof(ping)-1, len, NULL, NULL);
      strcpy(command+sizeof(ping)-1+len, redirect);
3692

3693
      TRACE("Ping command is : %s\n",command);
3694

3695
      status = system(command);
3696
      free(command);
3697

3698
      TRACE("Ping returned a code of %i\n",status);
3699

3700 3701 3702 3703
      /* Ping return code of 0 indicates success */
      if (status == 0)
         rc = TRUE;
  }
3704 3705 3706

End:
  if (rc == FALSE)
3707
    INTERNET_SetLastError(ERROR_NOT_CONNECTED);
3708 3709 3710 3711

  return rc;
}

3712 3713

/***********************************************************************
3714
 *	InternetCheckConnectionA (WININET.@)
3715 3716 3717 3718 3719 3720 3721 3722
 *
 * Pings a requested host to check internet connection
 *
 * RETURNS
 *   TRUE on success and FALSE on failure. If a failure then
 *   ERROR_NOT_CONNECTED is placed into GetLastError
 *
 */
3723
BOOL WINAPI InternetCheckConnectionA(LPCSTR lpszUrl, DWORD dwFlags, DWORD dwReserved)
3724
{
3725
    WCHAR *url = NULL;
3726 3727
    BOOL rc;

3728
    if(lpszUrl) {
3729
        url = strdupAtoW(lpszUrl);
3730 3731 3732 3733 3734 3735
        if(!url)
            return FALSE;
    }

    rc = InternetCheckConnectionW(url, dwFlags, dwReserved);

3736
    free(url);
3737 3738 3739 3740
    return rc;
}


3741
/**********************************************************
3742
 *	INTERNET_InternetOpenUrlW (internal)
3743 3744
 *
 * Opens an URL
3745
 *
3746 3747 3748
 * RETURNS
 *   handle of connection or NULL on failure
 */
3749
static HINTERNET INTERNET_InternetOpenUrlW(appinfo_t *hIC, LPCWSTR lpszUrl,
3750
    LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3751
{
3752 3753
    URL_COMPONENTSW urlComponents = { sizeof(urlComponents) };
    WCHAR *host, *user = NULL, *pass = NULL, *path;
3754
    HINTERNET client = NULL, client1 = NULL;
3755
    DWORD res;
3756
    
3757
    TRACE("(%p, %s, %s, %08lx, %08lx, %08Ix)\n", hIC, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
3758 3759
	  dwHeadersLength, dwFlags, dwContext);
    
3760 3761 3762 3763 3764
    urlComponents.dwHostNameLength = 1;
    urlComponents.dwUserNameLength = 1;
    urlComponents.dwPasswordLength = 1;
    urlComponents.dwUrlPathLength = 1;
    urlComponents.dwExtraInfoLength = 1;
3765
    if(!InternetCrackUrlW(lpszUrl, lstrlenW(lpszUrl), 0, &urlComponents))
3766
	return NULL;
3767

3768 3769 3770
    if ((urlComponents.nScheme == INTERNET_SCHEME_HTTP || urlComponents.nScheme == INTERNET_SCHEME_HTTPS) &&
        urlComponents.dwExtraInfoLength)
    {
3771 3772 3773 3774
        assert(urlComponents.lpszUrlPath + urlComponents.dwUrlPathLength == urlComponents.lpszExtraInfo);
        urlComponents.dwUrlPathLength += urlComponents.dwExtraInfoLength;
    }

3775 3776
    host = strndupW(urlComponents.lpszHostName, urlComponents.dwHostNameLength);
    path = strndupW(urlComponents.lpszUrlPath, urlComponents.dwUrlPathLength);
3777
    if(urlComponents.dwUserNameLength)
3778
        user = strndupW(urlComponents.lpszUserName, urlComponents.dwUserNameLength);
3779
    if(urlComponents.dwPasswordLength)
3780
        pass = strndupW(urlComponents.lpszPassword, urlComponents.dwPasswordLength);
3781

3782 3783
    switch(urlComponents.nScheme) {
    case INTERNET_SCHEME_FTP:
3784 3785
	client = FTP_Connect(hIC, host, urlComponents.nPort,
			     user, pass, dwFlags, dwContext, INET_OPENURL);
3786 3787
	if(client == NULL)
	    break;
3788
	client1 = FtpOpenFileW(client, path, GENERIC_READ, dwFlags, dwContext);
3789 3790 3791 3792
	if(client1 == NULL) {
	    InternetCloseHandle(client);
	    break;
	}
3793 3794 3795 3796
	break;
	
    case INTERNET_SCHEME_HTTP:
    case INTERNET_SCHEME_HTTPS: {
3797
        LPCWSTR accept[2] = { L"*/*", NULL };
3798

3799 3800
        if (urlComponents.nScheme == INTERNET_SCHEME_HTTPS) dwFlags |= INTERNET_FLAG_SECURE;

3801
        /* FIXME: should use pointers, not handles, as handles are not thread-safe */
3802 3803
	res = HTTP_Connect(hIC, host, urlComponents.nPort,
                           user, pass, dwFlags, dwContext, INET_OPENURL, &client);
3804 3805
        if(res != ERROR_SUCCESS) {
            INTERNET_SetLastError(res);
3806
	    break;
3807
        }
3808

3809
        client1 = HttpOpenRequestW(client, NULL, path, NULL, NULL, accept, dwFlags, dwContext);
3810 3811 3812 3813
	if(client1 == NULL) {
	    InternetCloseHandle(client);
	    break;
	}
3814
	HttpAddRequestHeadersW(client1, lpszHeaders, dwHeadersLength, HTTP_ADDREQ_FLAG_ADD);
3815 3816
	if (!HttpSendRequestW(client1, NULL, 0, NULL, 0) &&
            GetLastError() != ERROR_IO_PENDING) {
3817 3818 3819 3820
	    InternetCloseHandle(client1);
	    client1 = NULL;
	    break;
	}
3821
    }
3822 3823 3824 3825
    case INTERNET_SCHEME_GOPHER:
	/* gopher doesn't seem to be implemented in wine, but it's supposed
	 * to be supported by InternetOpenUrlA. */
    default:
3826
        SetLastError(ERROR_INTERNET_UNRECOGNIZED_SCHEME);
3827
	break;
3828
    }
3829 3830

    TRACE(" %p <--\n", client1);
3831

3832 3833 3834 3835
    free(host);
    free(path);
    free(user);
    free(pass);
3836 3837
    return client1;
}
3838

3839
/**********************************************************
3840
 *	InternetOpenUrlW (WININET.@)
3841 3842 3843 3844 3845 3846
 *
 * Opens an URL
 *
 * RETURNS
 *   handle of connection or NULL on failure
 */
3847 3848 3849 3850 3851 3852 3853 3854 3855 3856
typedef struct {
    task_header_t hdr;
    WCHAR *url;
    WCHAR *headers;
    DWORD headers_len;
    DWORD flags;
    DWORD_PTR context;
} open_url_task_t;

static void AsyncInternetOpenUrlProc(task_header_t *hdr)
3857
{
3858
    open_url_task_t *task = (open_url_task_t*)hdr;
3859

3860
    TRACE("%p\n", task->hdr.hdr);
3861

3862 3863
    INTERNET_InternetOpenUrlW((appinfo_t*)task->hdr.hdr, task->url, task->headers,
            task->headers_len, task->flags, task->context);
3864 3865
    free(task->url);
    free(task->headers);
3866 3867
}

3868
HINTERNET WINAPI InternetOpenUrlW(HINTERNET hInternet, LPCWSTR lpszUrl,
3869
    LPCWSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3870 3871
{
    HINTERNET ret = NULL;
3872
    appinfo_t *hIC = NULL;
3873

Lionel Ulmer's avatar
Lionel Ulmer committed
3874
    if (TRACE_ON(wininet)) {
3875
	TRACE("(%p, %s, %s, %08lx, %08lx, %08Ix)\n", hInternet, debugstr_w(lpszUrl), debugstr_w(lpszHeaders),
Lionel Ulmer's avatar
Lionel Ulmer committed
3876 3877 3878 3879 3880
	      dwHeadersLength, dwFlags, dwContext);
	TRACE("  flags :");
	dump_INTERNET_FLAGS(dwFlags);
    }

3881 3882
    if (!lpszUrl)
    {
3883
        SetLastError(ERROR_INVALID_PARAMETER);
3884 3885 3886
        goto lend;
    }

3887
    hIC = (appinfo_t*)get_handle_object( hInternet );
3888
    if (NULL == hIC ||  hIC->hdr.htype != WH_HINIT) {
3889
	SetLastError(ERROR_INTERNET_INCORRECT_HANDLE_TYPE);
3890 3891 3892 3893
 	goto lend;
    }
    
    if (hIC->hdr.dwFlags & INTERNET_FLAG_ASYNC) {
3894
	open_url_task_t *task;
3895

3896
        task = alloc_async_task(&hIC->hdr, AsyncInternetOpenUrlProc, sizeof(*task));
3897 3898
        task->url = wcsdup(lpszUrl);
        task->headers = wcsdup(lpszHeaders);
3899 3900 3901
        task->headers_len = dwHeadersLength;
        task->flags = dwFlags;
        task->context = dwContext;
3902
	
3903 3904
        INTERNET_AsyncCall(&task->hdr);
        SetLastError(ERROR_IO_PENDING);
3905
    } else {
3906
	ret = INTERNET_InternetOpenUrlW(hIC, lpszUrl, lpszHeaders, dwHeadersLength, dwFlags, dwContext);
3907 3908 3909
    }
    
  lend:
3910 3911
    if( hIC )
        WININET_Release( &hIC->hdr );
3912 3913 3914 3915
    TRACE(" %p <--\n", ret);
    
    return ret;
}
3916

3917
/**********************************************************
3918
 *	InternetOpenUrlA (WININET.@)
3919 3920 3921 3922 3923 3924
 *
 * Opens an URL
 *
 * RETURNS
 *   handle of connection or NULL on failure
 */
3925
HINTERNET WINAPI InternetOpenUrlA(HINTERNET hInternet, LPCSTR lpszUrl,
3926
    LPCSTR lpszHeaders, DWORD dwHeadersLength, DWORD dwFlags, DWORD_PTR dwContext)
3927
{
3928
    HINTERNET rc = NULL;
3929
    LPWSTR szUrl = NULL;
3930
    WCHAR *headers = NULL;
3931

3932 3933
    TRACE("\n");

3934
    if(lpszUrl) {
3935
        szUrl = strdupAtoW(lpszUrl);
3936
        if(!szUrl)
3937
            return NULL;
3938
    }
3939

3940
    if(lpszHeaders) {
3941
        headers = strndupAtoW(lpszHeaders, dwHeadersLength, &dwHeadersLength);
3942
        if(!headers) {
3943
            free(szUrl);
3944
            return NULL;
3945 3946 3947
        }
    }
    
3948
    rc = InternetOpenUrlW(hInternet, szUrl, headers, dwHeadersLength, dwFlags, dwContext);
3949

3950 3951
    free(szUrl);
    free(headers);
3952 3953 3954 3955
    return rc;
}


3956 3957
static LPWITHREADERROR INTERNET_AllocThreadError(void)
{
3958
    WITHREADERROR *lpwite = malloc(sizeof(*lpwite));
3959 3960 3961 3962 3963 3964 3965 3966 3967

    if (lpwite)
    {
        lpwite->dwError = 0;
        lpwite->response[0] = '\0';
    }

    if (!TlsSetValue(g_dwTlsErrIndex, lpwite))
    {
3968
        free(lpwite);
3969 3970 3971 3972 3973 3974
        return NULL;
    }
    return lpwite;
}


3975 3976 3977 3978 3979 3980 3981 3982 3983 3984
/***********************************************************************
 *           INTERNET_SetLastError (internal)
 *
 * Set last thread specific error
 *
 * RETURNS
 *
 */
void INTERNET_SetLastError(DWORD dwError)
{
3985
    LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
3986

3987
    if (!lpwite)
3988
        lpwite = INTERNET_AllocThreadError();
3989

3990
    SetLastError(dwError);
3991 3992
    if(lpwite)
        lpwite->dwError = dwError;
3993 3994 3995 3996 3997 3998 3999 4000 4001 4002 4003
}


/***********************************************************************
 *           INTERNET_GetLastError (internal)
 *
 * Get last thread specific error
 *
 * RETURNS
 *
 */
4004
DWORD INTERNET_GetLastError(void)
4005
{
4006
    LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
4007
    if (!lpwite) return 0;
4008 4009
    /* TlsGetValue clears last error, so set it again here */
    SetLastError(lpwite->dwError);
4010 4011 4012 4013 4014 4015 4016 4017 4018 4019 4020 4021
    return lpwite->dwError;
}


/***********************************************************************
 *           INTERNET_WorkerThreadFunc (internal)
 *
 * Worker thread execution function
 *
 * RETURNS
 *
 */
4022
static DWORD CALLBACK INTERNET_WorkerThreadFunc(LPVOID lpvParam)
4023
{
4024
    task_header_t *task = lpvParam;
4025 4026 4027

    TRACE("\n");

4028 4029
    task->proc(task);
    WININET_Release(task->hdr);
4030
    free(task);
4031 4032 4033

    if (g_dwTlsErrIndex != TLS_OUT_OF_INDEXES)
    {
4034
        free(TlsGetValue(g_dwTlsErrIndex));
4035 4036
        TlsSetValue(g_dwTlsErrIndex, NULL);
    }
4037
    return TRUE;
4038 4039
}

4040 4041 4042 4043
void *alloc_async_task(object_header_t *hdr, async_task_proc_t proc, size_t size)
{
    task_header_t *task;

4044
    task = malloc(size);
4045 4046 4047 4048 4049 4050 4051
    if(!task)
        return NULL;

    task->hdr = WININET_AddRef(hdr);
    task->proc = proc;
    return task;
}
4052 4053 4054 4055 4056 4057 4058 4059 4060

/***********************************************************************
 *           INTERNET_AsyncCall (internal)
 *
 * Retrieves work request from queue
 *
 * RETURNS
 *
 */
4061
DWORD INTERNET_AsyncCall(task_header_t *task)
4062
{
4063
    BOOL bSuccess;
4064 4065 4066

    TRACE("\n");

4067
    bSuccess = QueueUserWorkItem(INTERNET_WorkerThreadFunc, task, WT_EXECUTELONGFUNCTION);
4068 4069
    if (!bSuccess)
    {
4070
        free(task);
4071
        return ERROR_INTERNET_ASYNC_THREAD_FAILED;
4072
    }
4073
    return ERROR_SUCCESS;
4074 4075 4076 4077
}


/***********************************************************************
4078
 *          INTERNET_GetResponseBuffer  (internal)
4079 4080 4081 4082
 *
 * RETURNS
 *
 */
4083
LPSTR INTERNET_GetResponseBuffer(void)
4084
{
4085
    LPWITHREADERROR lpwite = TlsGetValue(g_dwTlsErrIndex);
4086
    if (!lpwite)
4087
        lpwite = INTERNET_AllocThreadError();
4088
    TRACE("\n");
4089 4090
    return lpwite->response;
}
4091

4092 4093 4094 4095
/**********************************************************
 *	InternetQueryDataAvailable (WININET.@)
 *
 * Determines how much data is available to be read.
4096
 *
4097
 * RETURNS
4098 4099 4100 4101 4102 4103
 *   TRUE on success, FALSE if an error occurred. If
 *   INTERNET_FLAG_ASYNC was specified in InternetOpen, and
 *   no data is presently available, FALSE is returned with
 *   the last error ERROR_IO_PENDING; a callback with status
 *   INTERNET_STATUS_REQUEST_COMPLETE will be sent when more
 *   data is available.
4104 4105
 */
BOOL WINAPI InternetQueryDataAvailable( HINTERNET hFile,
4106
                                LPDWORD lpdwNumberOfBytesAvailable,
4107
                                DWORD dwFlags, DWORD_PTR dwContext)
4108
{
4109
    object_header_t *hdr;
4110
    DWORD res;
4111

4112
    TRACE("(%p %p %lx %Ix)\n", hFile, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
4113

4114
    hdr = get_handle_object( hFile );
4115
    if (!hdr) {
4116
        SetLastError(ERROR_INVALID_HANDLE);
4117 4118 4119
        return FALSE;
    }

4120
    if(hdr->vtbl->QueryDataAvailable) {
4121
        res = hdr->vtbl->QueryDataAvailable(hdr, lpdwNumberOfBytesAvailable, dwFlags, dwContext);
4122 4123 4124
    }else {
        WARN("wrong handle\n");
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
4125 4126
    }

4127 4128 4129 4130 4131
    WININET_Release(hdr);

    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
4132 4133
}

4134 4135 4136 4137
DWORD create_req_file(const WCHAR *file_name, req_file_t **ret)
{
    req_file_t *req_file;

4138
    req_file = calloc(1, sizeof(*req_file));
4139 4140 4141 4142 4143
    if(!req_file)
        return ERROR_NOT_ENOUGH_MEMORY;

    req_file->ref = 1;

4144
    req_file->file_name = wcsdup(file_name);
4145
    if(!req_file->file_name) {
4146
        free(req_file);
4147 4148 4149
        return ERROR_NOT_ENOUGH_MEMORY;
    }

4150 4151 4152 4153 4154 4155 4156
    req_file->file_handle = CreateFileW(req_file->file_name, GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE,
              NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(req_file->file_handle == INVALID_HANDLE_VALUE) {
        req_file_release(req_file);
        return GetLastError();
    }

4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167
    *ret = req_file;
    return ERROR_SUCCESS;
}

void req_file_release(req_file_t *req_file)
{
    if(InterlockedDecrement(&req_file->ref))
        return;

    if(!req_file->is_committed)
        DeleteFileW(req_file->file_name);
4168 4169
    if(req_file->file_handle && req_file->file_handle != INVALID_HANDLE_VALUE)
        CloseHandle(req_file->file_handle);
4170 4171 4172
    free(req_file->file_name);
    free(req_file->url);
    free(req_file);
4173
}
4174 4175

/***********************************************************************
4176
 *      InternetLockRequestFile (WININET.@)
4177
 */
4178
BOOL WINAPI InternetLockRequestFile(HINTERNET hInternet, HANDLE *lphLockReqHandle)
4179
{
4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204
    req_file_t *req_file = NULL;
    object_header_t *hdr;
    DWORD res;

    TRACE("(%p %p)\n", hInternet, lphLockReqHandle);

    hdr = get_handle_object(hInternet);
    if (!hdr) {
        SetLastError(ERROR_INVALID_HANDLE);
        return FALSE;
    }

    if(hdr->vtbl->LockRequestFile) {
        res = hdr->vtbl->LockRequestFile(hdr, &req_file);
    }else {
        WARN("wrong handle\n");
        res = ERROR_INTERNET_INCORRECT_HANDLE_TYPE;
    }

    WININET_Release(hdr);

    *lphLockReqHandle = req_file;
    if(res != ERROR_SUCCESS)
        SetLastError(res);
    return res == ERROR_SUCCESS;
4205 4206
}

4207
BOOL WINAPI InternetUnlockRequestFile(HANDLE hLockHandle)
4208
{
4209 4210 4211 4212
    TRACE("(%p)\n", hLockHandle);

    req_file_release(hLockHandle);
    return TRUE;
4213
}
4214 4215 4216


/***********************************************************************
4217
 *      InternetAutodial (WININET.@)
4218 4219 4220 4221 4222 4223 4224 4225 4226 4227
 *
 * On windows this function is supposed to dial the default internet
 * connection. We don't want to have Wine dial out to the internet so
 * we return TRUE by default. It might be nice to check if we are connected.
 *
 * RETURNS
 *   TRUE on success
 *   FALSE on failure
 *
 */
4228
BOOL WINAPI InternetAutodial(DWORD dwFlags, HWND hwndParent)
4229 4230 4231 4232 4233 4234
{
    FIXME("STUB\n");

    /* Tell that we are connected to the internet. */
    return TRUE;
}
4235 4236

/***********************************************************************
4237
 *      InternetAutodialHangup (WININET.@)
4238
 *
4239
 * Hangs up a connection made with InternetAutodial
4240 4241 4242 4243 4244 4245 4246 4247 4248 4249 4250 4251 4252 4253 4254
 *
 * PARAM
 *    dwReserved
 * RETURNS
 *   TRUE on success
 *   FALSE on failure
 *
 */
BOOL WINAPI InternetAutodialHangup(DWORD dwReserved)
{
    FIXME("STUB\n");

    /* we didn't dial, we don't disconnect */
    return TRUE;
}
4255 4256

/***********************************************************************
4257
 *      InternetCombineUrlA (WININET.@)
4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271
 *
 * Combine a base URL with a relative URL
 *
 * RETURNS
 *   TRUE on success
 *   FALSE on failure
 *
 */

BOOL WINAPI InternetCombineUrlA(LPCSTR lpszBaseUrl, LPCSTR lpszRelativeUrl,
                                LPSTR lpszBuffer, LPDWORD lpdwBufferLength,
                                DWORD dwFlags)
{
    HRESULT hr=S_OK;
4272

4273
    TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_a(lpszBaseUrl), debugstr_a(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
4274

4275 4276 4277 4278 4279 4280 4281 4282
    /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
    dwFlags ^= ICU_NO_ENCODE;
    hr=UrlCombineA(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);

    return (hr==S_OK);
}

/***********************************************************************
4283
 *      InternetCombineUrlW (WININET.@)
4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297
 *
 * Combine a base URL with a relative URL
 *
 * RETURNS
 *   TRUE on success
 *   FALSE on failure
 *
 */

BOOL WINAPI InternetCombineUrlW(LPCWSTR lpszBaseUrl, LPCWSTR lpszRelativeUrl,
                                LPWSTR lpszBuffer, LPDWORD lpdwBufferLength,
                                DWORD dwFlags)
{
    HRESULT hr=S_OK;
4298

4299
    TRACE("(%s, %s, %p, %p, 0x%08lx)\n", debugstr_w(lpszBaseUrl), debugstr_w(lpszRelativeUrl), lpszBuffer, lpdwBufferLength, dwFlags);
4300

4301 4302 4303 4304 4305 4306
    /* Flip this bit to correspond to URL_ESCAPE_UNSAFE */
    dwFlags ^= ICU_NO_ENCODE;
    hr=UrlCombineW(lpszBaseUrl,lpszRelativeUrl,lpszBuffer,lpdwBufferLength,dwFlags);

    return (hr==S_OK);
}
4307

4308 4309 4310
/* max port num is 65535 => 5 digits */
#define MAX_WORD_DIGITS 5

4311
#define URL_GET_COMP_LENGTH(url, component) ((url)->dw##component##Length ? \
4312
    (url)->dw##component##Length : lstrlenW((url)->lpsz##component))
4313 4314 4315
#define URL_GET_COMP_LENGTHA(url, component) ((url)->dw##component##Length ? \
    (url)->dw##component##Length : strlen((url)->lpsz##component))

4316
static BOOL url_uses_default_port(INTERNET_SCHEME nScheme, INTERNET_PORT nPort)
4317
{
4318 4319
    if ((nScheme == INTERNET_SCHEME_HTTP) &&
        (nPort == INTERNET_DEFAULT_HTTP_PORT))
4320
        return TRUE;
4321 4322
    if ((nScheme == INTERNET_SCHEME_HTTPS) &&
        (nPort == INTERNET_DEFAULT_HTTPS_PORT))
4323
        return TRUE;
4324 4325
    if ((nScheme == INTERNET_SCHEME_FTP) &&
        (nPort == INTERNET_DEFAULT_FTP_PORT))
4326
        return TRUE;
4327 4328
    if ((nScheme == INTERNET_SCHEME_GOPHER) &&
        (nPort == INTERNET_DEFAULT_GOPHER_PORT))
4329 4330
        return TRUE;

4331 4332 4333
    if (nPort == INTERNET_INVALID_PORT_NUMBER)
        return TRUE;

4334 4335 4336
    return FALSE;
}

4337 4338 4339 4340 4341 4342 4343 4344 4345 4346 4347
/* opaque urls do not fit into the standard url hierarchy and don't have
 * two following slashes */
static inline BOOL scheme_is_opaque(INTERNET_SCHEME nScheme)
{
    return (nScheme != INTERNET_SCHEME_FTP) &&
           (nScheme != INTERNET_SCHEME_GOPHER) &&
           (nScheme != INTERNET_SCHEME_HTTP) &&
           (nScheme != INTERNET_SCHEME_HTTPS) &&
           (nScheme != INTERNET_SCHEME_FILE);
}

4348 4349 4350 4351 4352 4353
static LPCWSTR INTERNET_GetSchemeString(INTERNET_SCHEME scheme)
{
    int index;
    if (scheme < INTERNET_SCHEME_FIRST)
        return NULL;
    index = scheme - INTERNET_SCHEME_FIRST;
4354
    if (index >= ARRAY_SIZE(url_schemes))
4355
        return NULL;
4356
    return url_schemes[index];
4357 4358
}

4359 4360 4361 4362
/* we can calculate using ansi strings because we're just
 * calculating string length, not size
 */
static BOOL calc_url_length(LPURL_COMPONENTSW lpUrlComponents,
4363
                            LPDWORD lpdwUrlLength)
4364
{
4365 4366
    INTERNET_SCHEME nScheme;

4367 4368
    *lpdwUrlLength = 0;

4369
    if (lpUrlComponents->lpszScheme)
4370 4371 4372 4373 4374
    {
        DWORD dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
        *lpdwUrlLength += dwLen;
        nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
    }
4375 4376
    else
    {
4377 4378 4379 4380 4381 4382 4383
        LPCWSTR scheme;

        nScheme = lpUrlComponents->nScheme;

        if (nScheme == INTERNET_SCHEME_DEFAULT)
            nScheme = INTERNET_SCHEME_HTTP;
        scheme = INTERNET_GetSchemeString(nScheme);
4384
        *lpdwUrlLength += lstrlenW(scheme);
4385 4386
    }

4387 4388 4389
    (*lpdwUrlLength)++; /* ':' */
    if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
        *lpdwUrlLength += strlen("//");
4390 4391 4392

    if (lpUrlComponents->lpszUserName)
    {
4393
        *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
4394 4395 4396 4397 4398 4399
        *lpdwUrlLength += strlen("@");
    }
    else
    {
        if (lpUrlComponents->lpszPassword)
        {
4400
            INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4401 4402 4403 4404 4405 4406 4407
            return FALSE;
        }
    }

    if (lpUrlComponents->lpszPassword)
    {
        *lpdwUrlLength += strlen(":");
4408
        *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, Password);
4409 4410
    }

4411
    if (lpUrlComponents->lpszHostName)
4412
    {
4413
        *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
4414 4415 4416

        if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
        {
4417
            WCHAR port[MAX_WORD_DIGITS + 1];
4418

4419 4420
            _ltow(lpUrlComponents->nPort, port, 10);
            *lpdwUrlLength += lstrlenW(port);
4421 4422 4423
            *lpdwUrlLength += strlen(":");
        }

4424 4425 4426
        if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
            (*lpdwUrlLength)++; /* '/' */
    }
4427

4428 4429
    if (lpUrlComponents->lpszUrlPath)
        *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
4430

4431 4432 4433
    if (lpUrlComponents->lpszExtraInfo)
        *lpdwUrlLength += URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);

4434 4435 4436 4437 4438 4439 4440 4441 4442 4443 4444 4445 4446 4447 4448 4449 4450 4451 4452 4453 4454
    return TRUE;
}

static void convert_urlcomp_atow(LPURL_COMPONENTSA lpUrlComponents, LPURL_COMPONENTSW urlCompW)
{
    INT len;

    ZeroMemory(urlCompW, sizeof(URL_COMPONENTSW));

    urlCompW->dwStructSize = sizeof(URL_COMPONENTSW);
    urlCompW->dwSchemeLength = lpUrlComponents->dwSchemeLength;
    urlCompW->nScheme = lpUrlComponents->nScheme;
    urlCompW->dwHostNameLength = lpUrlComponents->dwHostNameLength;
    urlCompW->nPort = lpUrlComponents->nPort;
    urlCompW->dwUserNameLength = lpUrlComponents->dwUserNameLength;
    urlCompW->dwPasswordLength = lpUrlComponents->dwPasswordLength;
    urlCompW->dwUrlPathLength = lpUrlComponents->dwUrlPathLength;
    urlCompW->dwExtraInfoLength = lpUrlComponents->dwExtraInfoLength;

    if (lpUrlComponents->lpszScheme)
    {
4455
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, Scheme) + 1;
4456
        urlCompW->lpszScheme = malloc(len * sizeof(WCHAR));
4457 4458 4459 4460 4461 4462
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszScheme,
                            -1, urlCompW->lpszScheme, len);
    }

    if (lpUrlComponents->lpszHostName)
    {
4463
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, HostName) + 1;
4464
        urlCompW->lpszHostName = malloc(len * sizeof(WCHAR));
4465 4466 4467 4468 4469 4470
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszHostName,
                            -1, urlCompW->lpszHostName, len);
    }

    if (lpUrlComponents->lpszUserName)
    {
4471
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, UserName) + 1;
4472
        urlCompW->lpszUserName = malloc(len * sizeof(WCHAR));
4473 4474 4475 4476 4477 4478
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUserName,
                            -1, urlCompW->lpszUserName, len);
    }

    if (lpUrlComponents->lpszPassword)
    {
4479
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, Password) + 1;
4480
        urlCompW->lpszPassword = malloc(len * sizeof(WCHAR));
4481 4482 4483 4484 4485 4486
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszPassword,
                            -1, urlCompW->lpszPassword, len);
    }

    if (lpUrlComponents->lpszUrlPath)
    {
4487
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, UrlPath) + 1;
4488
        urlCompW->lpszUrlPath = malloc(len * sizeof(WCHAR));
4489 4490 4491 4492 4493 4494
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszUrlPath,
                            -1, urlCompW->lpszUrlPath, len);
    }

    if (lpUrlComponents->lpszExtraInfo)
    {
4495
        len = URL_GET_COMP_LENGTHA(lpUrlComponents, ExtraInfo) + 1;
4496
        urlCompW->lpszExtraInfo = malloc(len * sizeof(WCHAR));
4497 4498 4499 4500 4501
        MultiByteToWideChar(CP_ACP, 0, lpUrlComponents->lpszExtraInfo,
                            -1, urlCompW->lpszExtraInfo, len);
    }
}

4502
/***********************************************************************
4503
 *      InternetCreateUrlA (WININET.@)
4504
 *
4505
 * See InternetCreateUrlW.
4506
 */
4507 4508 4509
BOOL WINAPI InternetCreateUrlA(LPURL_COMPONENTSA lpUrlComponents, DWORD dwFlags,
                               LPSTR lpszUrl, LPDWORD lpdwUrlLength)
{
4510 4511 4512 4513
    BOOL ret;
    LPWSTR urlW = NULL;
    URL_COMPONENTSW urlCompW;

4514
    TRACE("(%p,%ld,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
4515

4516
    if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
4517
    {
4518
        SetLastError(ERROR_INVALID_PARAMETER);
4519 4520 4521 4522 4523 4524
        return FALSE;
    }

    convert_urlcomp_atow(lpUrlComponents, &urlCompW);

    if (lpszUrl)
4525
        urlW = malloc(*lpdwUrlLength * sizeof(WCHAR));
4526 4527 4528

    ret = InternetCreateUrlW(&urlCompW, dwFlags, urlW, lpdwUrlLength);

4529 4530 4531
    if (!ret && (GetLastError() == ERROR_INSUFFICIENT_BUFFER))
        *lpdwUrlLength /= sizeof(WCHAR);

4532 4533 4534 4535 4536 4537
    /* on success, lpdwUrlLength points to the size of urlW in WCHARS
    * minus one, so add one to leave room for NULL terminator
    */
    if (ret)
        WideCharToMultiByte(CP_ACP, 0, urlW, -1, lpszUrl, *lpdwUrlLength + 1, NULL, NULL);

4538 4539 4540 4541 4542 4543 4544
    free(urlCompW.lpszScheme);
    free(urlCompW.lpszHostName);
    free(urlCompW.lpszUserName);
    free(urlCompW.lpszPassword);
    free(urlCompW.lpszUrlPath);
    free(urlCompW.lpszExtraInfo);
    free(urlW);
4545 4546 4547 4548
    return ret;
}

/***********************************************************************
4549
 *      InternetCreateUrlW (WININET.@)
4550
 *
4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 4565
 * Creates a URL from its component parts.
 *
 * PARAMS
 *  lpUrlComponents [I] URL Components.
 *  dwFlags         [I] Flags. See notes.
 *  lpszUrl         [I] Buffer in which to store the created URL.
 *  lpdwUrlLength   [I/O] On input, the length of the buffer pointed to by
 *                        lpszUrl in characters. On output, the number of bytes
 *                        required to store the URL including terminator.
 *
 * NOTES
 *
 * The dwFlags parameter can be zero or more of the following:
 *|ICU_ESCAPE - Generates escape sequences for unsafe characters in the path and extra info of the URL.
 *
4566 4567 4568 4569 4570 4571 4572 4573
 * RETURNS
 *   TRUE on success
 *   FALSE on failure
 *
 */
BOOL WINAPI InternetCreateUrlW(LPURL_COMPONENTSW lpUrlComponents, DWORD dwFlags,
                               LPWSTR lpszUrl, LPDWORD lpdwUrlLength)
{
4574
    DWORD dwLen;
4575
    INTERNET_SCHEME nScheme;
4576

4577
    static const WCHAR slashSlashW[] = {'/','/'};
4578

4579
    TRACE("(%p,%ld,%p,%p)\n", lpUrlComponents, dwFlags, lpszUrl, lpdwUrlLength);
4580

4581
    if (!lpUrlComponents || lpUrlComponents->dwStructSize != sizeof(URL_COMPONENTSW) || !lpdwUrlLength)
4582
    {
4583
        INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4584 4585 4586
        return FALSE;
    }

4587
    if (!calc_url_length(lpUrlComponents, &dwLen))
4588 4589 4590 4591
        return FALSE;

    if (!lpszUrl || *lpdwUrlLength < dwLen)
    {
4592
        *lpdwUrlLength = (dwLen + 1) * sizeof(WCHAR);
4593
        INTERNET_SetLastError(ERROR_INSUFFICIENT_BUFFER);
4594 4595 4596 4597 4598 4599
        return FALSE;
    }

    *lpdwUrlLength = dwLen;
    lpszUrl[0] = 0x00;

4600 4601
    dwLen = 0;

4602
    if (lpUrlComponents->lpszScheme)
4603 4604 4605 4606
    {
        dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Scheme);
        memcpy(lpszUrl, lpUrlComponents->lpszScheme, dwLen * sizeof(WCHAR));
        lpszUrl += dwLen;
4607 4608

        nScheme = GetInternetSchemeW(lpUrlComponents->lpszScheme, dwLen);
4609
    }
4610 4611
    else
    {
4612 4613 4614 4615 4616 4617 4618
        LPCWSTR scheme;
        nScheme = lpUrlComponents->nScheme;

        if (nScheme == INTERNET_SCHEME_DEFAULT)
            nScheme = INTERNET_SCHEME_HTTP;

        scheme = INTERNET_GetSchemeString(nScheme);
4619
        dwLen = lstrlenW(scheme);
4620 4621 4622
        memcpy(lpszUrl, scheme, dwLen * sizeof(WCHAR));
        lpszUrl += dwLen;
    }
4623

4624 4625 4626 4627 4628 4629 4630
    /* all schemes are followed by at least a colon */
    *lpszUrl = ':';
    lpszUrl++;

    if (!scheme_is_opaque(nScheme) || lpUrlComponents->lpszHostName)
    {
        memcpy(lpszUrl, slashSlashW, sizeof(slashSlashW));
4631
        lpszUrl += ARRAY_SIZE(slashSlashW);
4632
    }
4633 4634 4635

    if (lpUrlComponents->lpszUserName)
    {
4636 4637 4638
        dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UserName);
        memcpy(lpszUrl, lpUrlComponents->lpszUserName, dwLen * sizeof(WCHAR));
        lpszUrl += dwLen;
4639 4640 4641

        if (lpUrlComponents->lpszPassword)
        {
4642 4643
            *lpszUrl = ':';
            lpszUrl++;
4644

4645 4646 4647
            dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, Password);
            memcpy(lpszUrl, lpUrlComponents->lpszPassword, dwLen * sizeof(WCHAR));
            lpszUrl += dwLen;
4648 4649
        }

4650 4651
        *lpszUrl = '@';
        lpszUrl++;
4652 4653
    }

4654 4655 4656 4657 4658
    if (lpUrlComponents->lpszHostName)
    {
        dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, HostName);
        memcpy(lpszUrl, lpUrlComponents->lpszHostName, dwLen * sizeof(WCHAR));
        lpszUrl += dwLen;
4659

4660 4661
        if (!url_uses_default_port(nScheme, lpUrlComponents->nPort))
        {
4662 4663 4664
            *lpszUrl++ = ':';
            _ltow(lpUrlComponents->nPort, lpszUrl, 10);
            lpszUrl += lstrlenW(lpszUrl);
4665 4666
        }

4667 4668 4669 4670 4671 4672
        /* add slash between hostname and path if necessary */
        if (lpUrlComponents->lpszUrlPath && *lpUrlComponents->lpszUrlPath != '/')
        {
            *lpszUrl = '/';
            lpszUrl++;
        }
4673
    }
4674

4675 4676 4677 4678
    if (lpUrlComponents->lpszUrlPath)
    {
        dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, UrlPath);
        memcpy(lpszUrl, lpUrlComponents->lpszUrlPath, dwLen * sizeof(WCHAR));
4679
        lpszUrl += dwLen;
4680 4681
    }

4682 4683 4684 4685 4686 4687 4688
    if (lpUrlComponents->lpszExtraInfo)
    {
        dwLen = URL_GET_COMP_LENGTH(lpUrlComponents, ExtraInfo);
        memcpy(lpszUrl, lpUrlComponents->lpszExtraInfo, dwLen * sizeof(WCHAR));
        lpszUrl += dwLen;
    }

4689
    *lpszUrl = '\0';
4690 4691

    return TRUE;
4692 4693
}

4694 4695 4696 4697
/***********************************************************************
 *      InternetConfirmZoneCrossingA (WININET.@)
 *
 */
4698 4699 4700 4701 4702 4703
DWORD WINAPI InternetConfirmZoneCrossingA( HWND hWnd, LPSTR szUrlPrev, LPSTR szUrlNew, BOOL bPost )
{
    FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_a(szUrlPrev), debugstr_a(szUrlNew), bPost);
    return ERROR_SUCCESS;
}

4704 4705 4706 4707
/***********************************************************************
 *      InternetConfirmZoneCrossingW (WININET.@)
 *
 */
4708 4709 4710 4711 4712 4713
DWORD WINAPI InternetConfirmZoneCrossingW( HWND hWnd, LPWSTR szUrlPrev, LPWSTR szUrlNew, BOOL bPost )
{
    FIXME("(%p, %s, %s, %x) stub\n", hWnd, debugstr_w(szUrlPrev), debugstr_w(szUrlNew), bPost);
    return ERROR_SUCCESS;
}

4714 4715
static DWORD zone_preference = 3;

4716 4717 4718 4719 4720
/***********************************************************************
 *      PrivacySetZonePreferenceW (WININET.@)
 */
DWORD WINAPI PrivacySetZonePreferenceW( DWORD zone, DWORD type, DWORD template, LPCWSTR preference )
{
4721
    FIXME( "%lx %lx %lx %s: stub\n", zone, type, template, debugstr_w(preference) );
4722 4723

    zone_preference = template;
4724 4725 4726 4727 4728 4729 4730 4731 4732
    return 0;
}

/***********************************************************************
 *      PrivacyGetZonePreferenceW (WININET.@)
 */
DWORD WINAPI PrivacyGetZonePreferenceW( DWORD zone, DWORD type, LPDWORD template,
                                        LPWSTR preference, LPDWORD length )
{
4733
    FIXME( "%lx %lx %p %p %p: stub\n", zone, type, template, preference, length );
4734 4735

    if (template) *template = zone_preference;
4736 4737 4738
    return 0;
}

4739 4740 4741 4742 4743
/***********************************************************************
 *      InternetGetSecurityInfoByURLA (WININET.@)
 */
BOOL WINAPI InternetGetSecurityInfoByURLA(LPSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
{
4744 4745 4746 4747 4748
    WCHAR *url;
    BOOL res;

    TRACE("(%s %p %p)\n", debugstr_a(lpszURL), ppCertChain, pdwSecureFlags);

4749
    url = strdupAtoW(lpszURL);
4750 4751 4752 4753
    if(!url)
        return FALSE;

    res = InternetGetSecurityInfoByURLW(url, ppCertChain, pdwSecureFlags);
4754
    free(url);
4755
    return res;
4756 4757 4758 4759 4760 4761 4762
}

/***********************************************************************
 *      InternetGetSecurityInfoByURLW (WININET.@)
 */
BOOL WINAPI InternetGetSecurityInfoByURLW(LPCWSTR lpszURL, PCCERT_CHAIN_CONTEXT *ppCertChain, DWORD *pdwSecureFlags)
{
4763 4764
    URL_COMPONENTSW url = {sizeof(url)};
    server_t *server;
4765
    BOOL res;
4766 4767 4768

    TRACE("(%s %p %p)\n", debugstr_w(lpszURL), ppCertChain, pdwSecureFlags);

4769 4770 4771 4772 4773
    if (!ppCertChain && !pdwSecureFlags) {
        SetLastError(ERROR_INVALID_PARAMETER);
        return FALSE;
    }

4774
    url.dwHostNameLength = 1;
4775 4776 4777 4778 4779 4780
    res = InternetCrackUrlW(lpszURL, 0, 0, &url);
    if(!res || url.nScheme != INTERNET_SCHEME_HTTPS) {
        SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
        return FALSE;
    }

4781
    server = get_server(substr(url.lpszHostName, url.dwHostNameLength), url.nPort, TRUE, FALSE);
4782 4783 4784 4785 4786 4787
    if(!server) {
        SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
        return FALSE;
    }

    if(server->cert_chain) {
4788
        if(pdwSecureFlags)
4789
            *pdwSecureFlags = server->security_flags & _SECURITY_ERROR_FLAGS_MASK;
4790 4791

        if(ppCertChain && !(*ppCertChain = CertDuplicateCertificateChain(server->cert_chain)))
4792 4793 4794 4795 4796 4797 4798 4799
            res = FALSE;
    }else {
        SetLastError(ERROR_INTERNET_ITEM_NOT_FOUND);
        res = FALSE;
    }

    server_release(server);
    return res;
4800 4801
}

4802
DWORD WINAPI InternetDialA( HWND hwndParent, LPSTR lpszConnectoid, DWORD dwFlags,
4803
                            DWORD_PTR* lpdwConnection, DWORD dwReserved )
4804
{
4805
    FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
4806 4807 4808 4809 4810
          lpdwConnection, dwReserved);
    return ERROR_SUCCESS;
}

DWORD WINAPI InternetDialW( HWND hwndParent, LPWSTR lpszConnectoid, DWORD dwFlags,
4811
                            DWORD_PTR* lpdwConnection, DWORD dwReserved )
4812
{
4813
    FIXME("(%p, %p, 0x%08lx, %p, 0x%08lx) stub\n", hwndParent, lpszConnectoid, dwFlags,
4814 4815 4816 4817 4818 4819
          lpdwConnection, dwReserved);
    return ERROR_SUCCESS;
}

BOOL WINAPI InternetGoOnlineA( LPSTR lpszURL, HWND hwndParent, DWORD dwReserved )
{
4820
    FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_a(lpszURL), hwndParent, dwReserved);
4821 4822 4823 4824 4825
    return TRUE;
}

BOOL WINAPI InternetGoOnlineW( LPWSTR lpszURL, HWND hwndParent, DWORD dwReserved )
{
4826
    FIXME("(%s, %p, 0x%08lx) stub\n", debugstr_w(lpszURL), hwndParent, dwReserved);
4827 4828 4829
    return TRUE;
}

4830
DWORD WINAPI InternetHangUp( DWORD_PTR dwConnection, DWORD dwReserved )
4831
{
4832
    FIXME("(0x%08Ix, 0x%08lx) stub\n", dwConnection, dwReserved);
4833 4834 4835 4836 4837 4838 4839 4840 4841 4842 4843 4844 4845
    return ERROR_SUCCESS;
}

BOOL WINAPI CreateMD5SSOHash( PWSTR pszChallengeInfo, PWSTR pwszRealm, PWSTR pwszTarget,
                              PBYTE pbHexHash )
{
    FIXME("(%s, %s, %s, %p) stub\n", debugstr_w(pszChallengeInfo), debugstr_w(pwszRealm),
          debugstr_w(pwszTarget), pbHexHash);
    return FALSE;
}

BOOL WINAPI ResumeSuspendedDownload( HINTERNET hInternet, DWORD dwError )
{
4846
    FIXME("(%p, 0x%08lx) stub\n", hInternet, dwError);
4847 4848
    return FALSE;
}
4849

4850
BOOL WINAPI InternetQueryFortezzaStatus(DWORD *a, DWORD_PTR b)
4851
{
4852
    FIXME("(%p, %08Ix) stub\n", a, b);
4853
    return FALSE;
4854
}
4855 4856 4857 4858 4859 4860

DWORD WINAPI ShowClientAuthCerts(HWND parent)
{
    FIXME("%p: stub\n", parent);
    return 0;
}