http.c 31 KB
Newer Older
1 2
/*
 * Copyright 2005 Jacek Caban
3
 * Copyright 2007 Misha Koshelev
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * 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
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 19
 */

20 21 22 23 24
/*
 * TODO:
 * - Handle redirects as native.
 */

25
#include "urlmon_main.h"
Jacek Caban's avatar
Jacek Caban committed
26
#include "wininet.h"
27 28 29 30 31

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(urlmon);

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
/* Flags are needed for, among other things, return HRESULTs from the Read function
 * to conform to native. For example, Read returns:
 *
 * 1. E_PENDING if called before the request has completed,
 *        (flags = 0)
 * 2. S_FALSE after all data has been read and S_OK has been reported,
 *        (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
 * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
 *    this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
 *        (flags = FLAG_REQUEST_COMPLETE)
 *    but upon subsequent calls to Read no reporting will take place, yet
 *    InternetQueryDataAvailable will still be called, and, on failure,
 *    INET_E_DATA_NOT_AVAILABLE will still be returned.
 *        (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
 *
 * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
 * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
 * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
 * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
 * if OnResponse does not return S_OK, Continue will not report data, and Read
 * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
 * data has been read.
 */
#define FLAG_REQUEST_COMPLETE 0x1
56 57 58 59 60
#define FLAG_FIRST_CONTINUE_COMPLETE 0x2
#define FLAG_FIRST_DATA_REPORTED 0x4
#define FLAG_ALL_DATA_READ 0x8
#define FLAG_LAST_DATA_REPORTED 0x10
#define FLAG_RESULT_REPORTED 0x20
61

62
typedef struct {
63 64 65
    const IInternetProtocolVtbl *lpInternetProtocolVtbl;
    const IInternetPriorityVtbl *lpInternetPriorityVtbl;

66
    DWORD flags, grfBINDF;
67
    BINDINFO bind_info;
68 69 70
    IInternetProtocolSink *protocol_sink;
    IHttpNegotiate *http_negotiate;
    HINTERNET internet, connect, request;
71
    LPWSTR full_header;
72 73
    HANDLE lock;
    ULONG current_position, content_length, available_bytes;
74 75
    LONG priority;

76 77 78
    LONG ref;
} HttpProtocol;

79 80 81 82
/* Default headers from native */
static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
                                   ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
/*
 * Helpers
 */

static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
{
    if (!(This->flags & FLAG_RESULT_REPORTED) &&
        This->protocol_sink)
    {
        This->flags |= FLAG_RESULT_REPORTED;
        IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
    }
}

static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
{
    DWORD bscf;
    if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
        This->protocol_sink)
    {
        if (This->flags & FLAG_FIRST_DATA_REPORTED)
        {
            bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
        }
        else
        {
            This->flags |= FLAG_FIRST_DATA_REPORTED;
            bscf = BSCF_FIRSTDATANOTIFICATION;
        }
        if (This->flags & FLAG_ALL_DATA_READ &&
            !(This->flags & FLAG_LAST_DATA_REPORTED))
        {
            This->flags |= FLAG_LAST_DATA_REPORTED;
            bscf |= BSCF_LASTDATANOTIFICATION;
        }
        IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
                                         This->current_position+This->available_bytes,
                                         This->content_length);
    }
}

static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
{
    if (!(This->flags & FLAG_ALL_DATA_READ))
        This->flags |= FLAG_ALL_DATA_READ;
    HTTPPROTOCOL_ReportData(This);
    HTTPPROTOCOL_ReportResult(This, S_OK);
}

static void HTTPPROTOCOL_Close(HttpProtocol *This)
{
    if (This->http_negotiate)
    {
        IHttpNegotiate_Release(This->http_negotiate);
        This->http_negotiate = 0;
    }
    if (This->request)
        InternetCloseHandle(This->request);
141 142 143 144 145 146 147
    if (This->connect)
        InternetCloseHandle(This->connect);
    if (This->internet)
    {
        InternetCloseHandle(This->internet);
        This->internet = 0;
    }
148 149 150
    if (This->full_header)
    {
        if (This->full_header != wszHeaders)
151
            heap_free(This->full_header);
152 153
        This->full_header = 0;
    }
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
    This->flags = 0;
}

static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
    HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
    LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
    HttpProtocol *This = (HttpProtocol *)dwContext;
    PROTOCOLDATA data;
    ULONG ulStatusCode;

    switch (dwInternetStatus)
    {
    case INTERNET_STATUS_RESOLVING_NAME:
        ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
        break;
    case INTERNET_STATUS_CONNECTING_TO_SERVER:
        ulStatusCode = BINDSTATUS_CONNECTING;
        break;
    case INTERNET_STATUS_SENDING_REQUEST:
        ulStatusCode = BINDSTATUS_SENDINGREQUEST;
        break;
    case INTERNET_STATUS_REQUEST_COMPLETE:
177
        This->flags |= FLAG_REQUEST_COMPLETE;
178 179 180
        /* PROTOCOLDATA same as native */
        memset(&data, 0, sizeof(data));
        data.dwState = 0xf1000000;
181
        if (This->flags & FLAG_FIRST_CONTINUE_COMPLETE)
182
            data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
183 184
        else
            data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
185 186 187 188
        if (This->grfBINDF & BINDF_FROMURLMON)
            IInternetProtocolSink_Switch(This->protocol_sink, &data);
        else
            IInternetProtocol_Continue((IInternetProtocol *)This, &data);
189
        return;
190 191 192
    case INTERNET_STATUS_HANDLE_CREATED:
        IInternetProtocol_AddRef((IInternetProtocol *)This);
        return;
193
    case INTERNET_STATUS_HANDLE_CLOSING:
194
        if (*(HINTERNET *)lpvStatusInformation == This->connect)
195
        {
196
            This->connect = 0;
197
        }
198
        else if (*(HINTERNET *)lpvStatusInformation == This->request)
199
        {
200 201 202 203 204 205 206 207 208 209 210
            This->request = 0;
            if (This->protocol_sink)
            {
                IInternetProtocolSink_Release(This->protocol_sink);
                This->protocol_sink = 0;
            }
            if (This->bind_info.cbSize)
            {
                ReleaseBindInfo(&This->bind_info);
                memset(&This->bind_info, 0, sizeof(This->bind_info));
            }
211
        }
212
        IInternetProtocol_Release((IInternetProtocol *)This);
213
        return;
214 215 216 217 218 219 220 221
    default:
        WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
        return;
    }

    IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
}

222
static inline LPWSTR strndupW(LPCWSTR string, int len)
223
{
224 225
    LPWSTR ret = NULL;
    if (string &&
226
        (ret = heap_alloc((len+1)*sizeof(WCHAR))) != NULL)
227 228 229 230 231 232 233 234 235 236 237
    {
        memcpy(ret, string, len*sizeof(WCHAR));
        ret[len] = 0;
    }
    return ret;
}

/*
 * Interface implementations
 */

238
#define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
239 240 241
#define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)

#define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256

static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);

    *ppv = NULL;
    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
    }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
    }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
        TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
        *ppv = PROTOCOL(This);
257 258 259
    }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
        TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
        *ppv = PRIORITY(This);
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
    }

    if(*ppv) {
        IInternetProtocol_AddRef(iface);
        return S_OK;
    }

    WARN("not supported interface %s\n", debugstr_guid(riid));
    return E_NOINTERFACE;
}

static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    LONG ref = InterlockedIncrement(&This->ref);
275
    TRACE("(%p) ref=%d\n", This, ref);
276 277 278 279 280 281 282 283
    return ref;
}

static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    LONG ref = InterlockedDecrement(&This->ref);

284
    TRACE("(%p) ref=%d\n", This, ref);
285 286

    if(!ref) {
287
        HTTPPROTOCOL_Close(This);
288
        heap_free(This);
289 290 291 292 293 294 295 296 297 298 299 300

        URLMON_UnlockModule();
    }

    return ref;
}

static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
        DWORD grfPI, DWORD dwReserved)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
301
    URL_COMPONENTSW url;
302
    DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
303 304 305
    ULONG num = 0;
    IServiceProvider *service_provider = 0;
    IHttpNegotiate2 *http_negotiate2 = 0;
306
    LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
307
        post_cookie = 0, optional = 0;
308
    BYTE security_id[512];
309
    LPOLESTR user_agent = NULL, accept_mimes[257];
310 311 312
    HRESULT hres;

    static const WCHAR wszHttp[] = {'h','t','t','p',':'};
313 314 315 316
    static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
        {{'G','E','T',0},
         {'P','O','S','T',0},
         {'P','U','T',0}};
317 318

    TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
319
            pOIBindInfo, grfPI, dwReserved);
320

321 322 323
    IInternetProtocolSink_AddRef(pOIProtSink);
    This->protocol_sink = pOIProtSink;

324 325 326
    memset(&This->bind_info, 0, sizeof(This->bind_info));
    This->bind_info.cbSize = sizeof(BINDINFO);
    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &This->grfBINDF, &This->bind_info);
327 328 329 330 331 332
    if (hres != S_OK)
    {
        WARN("GetBindInfo failed: %08x\n", hres);
        goto done;
    }

333
    if (strlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
334 335 336 337 338 339 340 341 342 343
        || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
    {
        hres = MK_E_SYNTAX;
        goto done;
    }

    memset(&url, 0, sizeof(url));
    url.dwStructSize = sizeof(url);
    url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
        url.dwPasswordLength = 1;
344
    if (!InternetCrackUrlW(szUrl, 0, 0, &url))
345 346 347 348 349 350 351 352 353 354 355
    {
        hres = MK_E_SYNTAX;
        goto done;
    }
    host = strndupW(url.lpszHostName, url.dwHostNameLength);
    path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
    user = strndupW(url.lpszUserName, url.dwUserNameLength);
    pass = strndupW(url.lpszPassword, url.dwPasswordLength);
    if (!url.nPort)
        url.nPort = INTERNET_DEFAULT_HTTP_PORT;

356
    if(!(This->grfBINDF & BINDF_FROMURLMON))
357
        IInternetProtocolSink_ReportProgress(This->protocol_sink, BINDSTATUS_DIRECTBIND, NULL);
358

359 360 361 362 363 364 365 366 367 368 369
    hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
                                           1, &num);
    if (hres != S_OK || !num)
    {
        CHAR null_char = 0;
        LPSTR user_agenta = NULL;
        len = 0;
        if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
        {
            WARN("ObtainUserAgentString failed: %08x\n", hres);
        }
370
        else if (!(user_agenta = heap_alloc(len*sizeof(CHAR))))
371 372 373 374 375 376 377 378 379 380 381 382
        {
            WARN("Out of memory\n");
        }
        else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
        {
            WARN("ObtainUserAgentString failed: %08x\n", hres);
        }
        else
        {
            if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
                WARN("Out of memory\n");
            else
383
                MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len);
384
        }
385
        heap_free(user_agenta);
386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    }

    This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
    if (!This->internet)
    {
        WARN("InternetOpen failed: %d\n", GetLastError());
        hres = INET_E_NO_SESSION;
        goto done;
    }

    /* Native does not check for success of next call, so we won't either */
    InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);

    This->connect = InternetConnectW(This->internet, host, url.nPort, user,
                                     pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
    if (!This->connect)
    {
        WARN("InternetConnect failed: %d\n", GetLastError());
        hres = INET_E_CANNOT_CONNECT;
        goto done;
    }

    num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
    hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
                                           accept_mimes,
                                           num, &num);
    if (hres != S_OK)
    {
        WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
        hres = INET_E_NO_VALID_MEDIA;
        goto done;
    }
    accept_mimes[num] = 0;

420 421
    if (This->grfBINDF & BINDF_NOWRITECACHE)
        request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
422 423
    if (This->grfBINDF & BINDF_NEEDFILE)
        request_flags |= INTERNET_FLAG_NEED_FILE;
424 425 426
    This->request = HttpOpenRequestW(This->connect, This->bind_info.dwBindVerb < BINDVERB_CUSTOM ?
                                     wszBindVerb[This->bind_info.dwBindVerb] :
                                     This->bind_info.szCustomVerb,
427 428
                                     path, NULL, NULL, (LPCWSTR *)accept_mimes,
                                     request_flags, (DWORD)This);
429 430 431 432 433 434 435
    if (!This->request)
    {
        WARN("HttpOpenRequest failed: %d\n", GetLastError());
        hres = INET_E_RESOURCE_NOT_FOUND;
        goto done;
    }

436
    hres = IInternetProtocolSink_QueryInterface(This->protocol_sink, &IID_IServiceProvider,
437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458
                                                (void **)&service_provider);
    if (hres != S_OK)
    {
        WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
        goto done;
    }

    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
                                         &IID_IHttpNegotiate, (void **)&This->http_negotiate);
    if (hres != S_OK)
    {
        WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
        goto done;
    }

    hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
                                               0, &addl_header);
    if (hres != S_OK)
    {
        WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
        goto done;
    }
459 460
    else if (addl_header == NULL)
    {
461
        This->full_header = (LPWSTR)wszHeaders;
462 463 464
    }
    else
    {
465
        int len_addl_header = lstrlenW(addl_header);
466
        This->full_header = heap_alloc(len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
467
        if (!This->full_header)
468 469 470 471 472
        {
            WARN("Out of memory\n");
            hres = E_OUTOFMEMORY;
            goto done;
        }
473 474
        lstrcpyW(This->full_header, addl_header);
        lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
475
    }
476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496

    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
                                         &IID_IHttpNegotiate2, (void **)&http_negotiate2);
    if (hres != S_OK)
    {
        WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
        /* No goto done as per native */
    }
    else
    {
        len = sizeof(security_id)/sizeof(security_id[0]);
        hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
        if (hres != S_OK)
        {
            WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
            /* No goto done as per native */
        }
    }

    /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */

497
    if (This->bind_info.dwBindVerb == BINDVERB_POST)
498 499 500 501 502 503 504 505 506 507 508 509 510
    {
        num = 0;
        hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_POST_COOKIE, &post_cookie,
                                               1, &num);
        if (hres == S_OK && num &&
            !InternetSetOptionW(This->request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
                                post_cookie, lstrlenW(post_cookie)))
        {
            WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
                 GetLastError());
        }
    }

511
    if (This->bind_info.dwBindVerb != BINDVERB_GET)
512 513
    {
        /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
514 515 516
        if (This->bind_info.stgmedData.tymed != TYMED_HGLOBAL)
            WARN("Expected This->bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
                 This->bind_info.stgmedData.tymed);
517
        else
518
            optional = (LPWSTR)This->bind_info.stgmedData.u.hGlobal;
519
    }
520
    if (!HttpSendRequestW(This->request, This->full_header, lstrlenW(This->full_header),
521
                          optional,
522
                          optional ? This->bind_info.cbstgmedData : 0) &&
523 524 525 526 527 528 529 530 531 532 533
        GetLastError() != ERROR_IO_PENDING)
    {
        WARN("HttpSendRequest failed: %d\n", GetLastError());
        hres = INET_E_DOWNLOAD_FAILURE;
        goto done;
    }

    hres = S_OK;
done:
    if (hres != S_OK)
    {
534
        IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
535 536 537
        HTTPPROTOCOL_Close(This);
    }

538
    CoTaskMemFree(post_cookie);
539 540 541 542 543 544 545 546 547 548 549
    CoTaskMemFree(addl_header);
    if (http_negotiate2)
        IHttpNegotiate2_Release(http_negotiate2);
    if (service_provider)
        IServiceProvider_Release(service_provider);

    while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
           accept_mimes[num])
        CoTaskMemFree(accept_mimes[num++]);
    CoTaskMemFree(user_agent);

550 551 552 553
    heap_free(pass);
    heap_free(user);
    heap_free(path);
    heap_free(host);
554 555

    return hres;
556 557 558 559 560
}

static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
561 562 563 564 565 566 567 568 569
    DWORD len = sizeof(DWORD), status_code;
    LPWSTR response_headers = 0, content_type = 0, content_length = 0;

    static const WCHAR wszDefaultContentType[] =
        {'t','e','x','t','/','h','t','m','l',0};

    TRACE("(%p)->(%p)\n", This, pProtocolData);

    if (!pProtocolData)
570
    {
571
        WARN("Expected pProtocolData to be non-NULL\n");
572
        return S_OK;
573
    }
574
    else if (!This->request)
575
    {
576
        WARN("Expected request to be non-NULL\n");
577
        return S_OK;
578
    }
579
    else if (!This->http_negotiate)
580
    {
581
        WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
582
        return S_OK;
583
    }
584
    else if (!This->protocol_sink)
585
    {
586
        WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
587
        return S_OK;
588 589 590
    }

    if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
591 592 593 594 595 596 597 598 599 600 601 602
    {
        if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
                            &status_code, &len, NULL))
        {
            WARN("HttpQueryInfo failed: %d\n", GetLastError());
        }
        else
        {
            len = 0;
            if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
                                 NULL) &&
                 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
603
                !(response_headers = heap_alloc(len)) ||
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
                !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
                                NULL))
            {
                WARN("HttpQueryInfo failed: %d\n", GetLastError());
            }
            else
            {
                HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
                                                         response_headers, NULL, NULL);
                if (hres != S_OK)
                {
                    WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
                    goto done;
                }
            }
        }

        len = 0;
        if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
             GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
624
            !(content_type = heap_alloc(len)) ||
625 626 627 628
            !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
        {
            WARN("HttpQueryInfo failed: %d\n", GetLastError());
            IInternetProtocolSink_ReportProgress(This->protocol_sink,
629 630 631
                                                 (This->grfBINDF & BINDF_FROMURLMON) ?
                                                 BINDSTATUS_MIMETYPEAVAILABLE :
                                                 BINDSTATUS_RAWMIMETYPE,
632 633 634 635
                                                 wszDefaultContentType);
        }
        else
        {
636 637 638 639
            /* remove the charset, if present */
            LPWSTR p = strchrW(content_type, ';');
            if (p) *p = '\0';

640
            IInternetProtocolSink_ReportProgress(This->protocol_sink,
641 642 643
                                                 (This->grfBINDF & BINDF_FROMURLMON) ?
                                                 BINDSTATUS_MIMETYPEAVAILABLE :
                                                 BINDSTATUS_RAWMIMETYPE,
644 645 646 647 648 649
                                                 content_type);
        }

        len = 0;
        if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
             GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
650
            !(content_length = heap_alloc(len)) ||
651 652 653 654 655 656 657 658 659
            !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
        {
            WARN("HttpQueryInfo failed: %d\n", GetLastError());
            This->content_length = 0;
        }
        else
        {
            This->content_length = atoiW(content_length);
        }
660

661 662 663 664 665 666 667 668 669 670 671 672 673 674 675
    if(This->grfBINDF & BINDF_NEEDFILE) {
        WCHAR cache_file[MAX_PATH];
        DWORD buflen = sizeof(cache_file);

        if(InternetQueryOptionW(This->request, INTERNET_OPTION_DATAFILE_NAME,
                                cache_file, &buflen))
        {
            IInternetProtocolSink_ReportProgress(This->protocol_sink,
                                                 BINDSTATUS_CACHEFILENAMEAVAILABLE,
                                                 cache_file);
        }else {
            FIXME("Could not get cache file\n");
        }
    }

676
        This->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
677
    }
678

679 680
    if (pProtocolData->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
    {
681 682 683 684
        /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
         * read, so clear the flag _before_ calling so it does not incorrectly get cleared
         * after the status callback is called */
        This->flags &= ~FLAG_REQUEST_COMPLETE;
685 686
        if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
        {
687
            if (GetLastError() != ERROR_IO_PENDING)
688
            {
689
                This->flags |= FLAG_REQUEST_COMPLETE;
690 691 692
                WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
                HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
            }
693 694 695
        }
        else
        {
696
            This->flags |= FLAG_REQUEST_COMPLETE;
697 698 699 700 701
            HTTPPROTOCOL_ReportData(This);
        }
    }

done:
702 703 704
    heap_free(response_headers);
    heap_free(content_type);
    heap_free(content_length);
705 706 707

    /* Returns S_OK on native */
    return S_OK;
708 709 710 711 712 713
}

static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
        DWORD dwOptions)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
714
    FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
715 716 717 718 719 720
    return E_NOTIMPL;
}

static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
721 722 723 724 725

    TRACE("(%p)->(%08x)\n", This, dwOptions);
    HTTPPROTOCOL_Close(This);

    return S_OK;
726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
}

static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
        ULONG cb, ULONG *pcbRead)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
746 747 748 749 750 751 752 753 754 755 756 757 758 759
    ULONG read = 0, len = 0;
    HRESULT hres = S_FALSE;

    TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);

    if (!(This->flags & FLAG_REQUEST_COMPLETE))
    {
        hres = E_PENDING;
    }
    else while (!(This->flags & FLAG_ALL_DATA_READ) &&
                read < cb)
    {
        if (This->available_bytes == 0)
        {
760 761 762 763
            /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
             * read, so clear the flag _before_ calling so it does not incorrectly get cleared
             * after the status callback is called */
            This->flags &= ~FLAG_REQUEST_COMPLETE;
764 765
            if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
            {
766 767 768 769 770 771 772 773 774 775
                if (GetLastError() == ERROR_IO_PENDING)
                {
                    hres = E_PENDING;
                }
                else
                {
                    WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
                    hres = INET_E_DATA_NOT_AVAILABLE;
                    HTTPPROTOCOL_ReportResult(This, hres);
                }
776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815
                goto done;
            }
            else if (This->available_bytes == 0)
            {
                HTTPPROTOCOL_AllDataRead(This);
            }
        }
        else
        {
            if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
                                  This->available_bytes > cb-read ?
                                  cb-read : This->available_bytes, &len))
            {
                WARN("InternetReadFile failed: %d\n", GetLastError());
                hres = INET_E_DOWNLOAD_FAILURE;
                HTTPPROTOCOL_ReportResult(This, hres);
                goto done;
            }
            else if (len == 0)
            {
                HTTPPROTOCOL_AllDataRead(This);
            }
            else
            {
                read += len;
                This->current_position += len;
                This->available_bytes -= len;
            }
        }
    }

    /* Per MSDN this should be if (read == cb), but native returns S_OK
     * if any bytes were read, so we will too */
    if (read)
        hres = S_OK;

done:
    if (pcbRead)
        *pcbRead = read;

816 817 818
    if (hres != E_PENDING)
        This->flags |= FLAG_REQUEST_COMPLETE;

819
    return hres;
820 821 822
}

static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
823
        DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
824 825
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
826
    FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
827 828 829 830 831 832
    return E_NOTIMPL;
}

static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
833 834 835 836 837 838 839

    TRACE("(%p)->(%08x)\n", This, dwOptions);

    if (!InternetLockRequestFile(This->request, &This->lock))
        WARN("InternetLockRequest failed: %d\n", GetLastError());

    return S_OK;
840 841 842 843 844
}

static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
{
    HttpProtocol *This = PROTOCOL_THIS(iface);
845 846 847 848 849 850 851 852 853 854 855

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

    if (This->lock)
    {
        if (!InternetUnlockRequestFile(This->lock))
            WARN("InternetUnlockRequest failed: %d\n", GetLastError());
        This->lock = 0;
    }

    return S_OK;
856 857 858 859
}

#undef PROTOCOL_THIS

860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883
#define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)

static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
{
    HttpProtocol *This = PRIORITY_THIS(iface);
    return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
}

static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
{
    HttpProtocol *This = PRIORITY_THIS(iface);
    return IInternetProtocol_AddRef(PROTOCOL(This));
}

static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
{
    HttpProtocol *This = PRIORITY_THIS(iface);
    return IInternetProtocol_Release(PROTOCOL(This));
}

static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
{
    HttpProtocol *This = PRIORITY_THIS(iface);

884
    TRACE("(%p)->(%d)\n", This, nPriority);
885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909

    This->priority = nPriority;
    return S_OK;
}

static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
{
    HttpProtocol *This = PRIORITY_THIS(iface);

    TRACE("(%p)->(%p)\n", This, pnPriority);

    *pnPriority = This->priority;
    return S_OK;
}

#undef PRIORITY_THIS

static const IInternetPriorityVtbl HttpPriorityVtbl = {
    HttpPriority_QueryInterface,
    HttpPriority_AddRef,
    HttpPriority_Release,
    HttpPriority_SetPriority,
    HttpPriority_GetPriority
};

910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
static const IInternetProtocolVtbl HttpProtocolVtbl = {
    HttpProtocol_QueryInterface,
    HttpProtocol_AddRef,
    HttpProtocol_Release,
    HttpProtocol_Start,
    HttpProtocol_Continue,
    HttpProtocol_Abort,
    HttpProtocol_Terminate,
    HttpProtocol_Suspend,
    HttpProtocol_Resume,
    HttpProtocol_Read,
    HttpProtocol_Seek,
    HttpProtocol_LockRequest,
    HttpProtocol_UnlockRequest
};

HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
{
    HttpProtocol *ret;

    TRACE("(%p %p)\n", pUnkOuter, ppobj);

    URLMON_LockModule();

934
    ret = heap_alloc(sizeof(HttpProtocol));
935 936

    ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
937
    ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
938
    ret->flags = ret->grfBINDF = 0;
939
    memset(&ret->bind_info, 0, sizeof(ret->bind_info));
940 941 942
    ret->protocol_sink = 0;
    ret->http_negotiate = 0;
    ret->internet = ret->connect = ret->request = 0;
943
    ret->full_header = 0;
944 945
    ret->lock = 0;
    ret->current_position = ret->content_length = ret->available_bytes = 0;
946
    ret->priority = 0;
947
    ret->ref = 1;
948

949 950 951 952
    *ppobj = PROTOCOL(ret);
    
    return S_OK;
}
953 954 955 956 957 958

HRESULT HttpSProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
{
    FIXME("(%p %p)\n", pUnkOuter, ppobj);
    return E_NOINTERFACE;
}