http.c 31.9 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
#define NONAMELESSUNION

22
#include "urlmon_main.h"
Jacek Caban's avatar
Jacek Caban committed
23
#include "wininet.h"
24

25 26 27
#define NO_SHLWAPI_REG
#include "shlwapi.h"

28 29 30 31 32
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(urlmon);

typedef struct {
33 34
    Protocol base;

35
    IUnknown            IUnknown_outer;
36 37 38
    IInternetProtocolEx IInternetProtocolEx_iface;
    IInternetPriority   IInternetPriority_iface;
    IWinInetHttpInfo    IWinInetHttpInfo_iface;
39

40
    BOOL https;
41
    IHttpNegotiate *http_negotiate;
42
    WCHAR *full_header;
43 44

    LONG ref;
45
    IUnknown *outer;
46 47
} HttpProtocol;

48 49 50 51 52
static inline HttpProtocol *impl_from_IUnknown(IUnknown *iface)
{
    return CONTAINING_RECORD(iface, HttpProtocol, IUnknown_outer);
}

53 54 55 56 57 58 59 60 61 62 63 64 65 66
static inline HttpProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
{
    return CONTAINING_RECORD(iface, HttpProtocol, IInternetProtocolEx_iface);
}

static inline HttpProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
{
    return CONTAINING_RECORD(iface, HttpProtocol, IInternetPriority_iface);
}

static inline HttpProtocol *impl_from_IWinInetHttpInfo(IWinInetHttpInfo *iface)
{
    return CONTAINING_RECORD(iface, HttpProtocol, IWinInetHttpInfo_iface);
}
67

68 69
static const WCHAR default_headersW[] = {
    '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};
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
static LPWSTR query_http_info(HttpProtocol *This, DWORD option)
{
    LPWSTR ret = NULL;
    DWORD len = 0;
    BOOL res;

    res = HttpQueryInfoW(This->base.request, option, NULL, &len, NULL);
    if (!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
        ret = heap_alloc(len);
        res = HttpQueryInfoW(This->base.request, option, ret, &len, NULL);
    }
    if(!res) {
        TRACE("HttpQueryInfoW(%d) failed: %08x\n", option, GetLastError());
        heap_free(ret);
        return NULL;
    }

    return ret;
}

91
static inline BOOL set_security_flag(HttpProtocol *This, DWORD flags)
92 93 94
{
    BOOL res;

95
    res = InternetSetOptionW(This->base.request, INTERNET_OPTION_SECURITY_FLAGS, &flags, sizeof(flags));
96
    if(!res)
97
        ERR("Failed to set security flags: %x\n", flags);
98 99 100 101 102 103 104 105 106 107 108 109

    return res;
}

static inline HRESULT internet_error_to_hres(DWORD error)
{
    switch(error)
    {
    case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
    case ERROR_INTERNET_SEC_CERT_CN_INVALID:
    case ERROR_INTERNET_INVALID_CA:
    case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
110 111 112 113 114
    case ERROR_INTERNET_SEC_INVALID_CERT:
    case ERROR_INTERNET_SEC_CERT_ERRORS:
    case ERROR_INTERNET_SEC_CERT_REV_FAILED:
    case ERROR_INTERNET_SEC_CERT_NO_REV:
    case ERROR_INTERNET_SEC_CERT_REVOKED:
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
        return INET_E_INVALID_CERTIFICATE;
    case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
    case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR:
    case ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION:
        return INET_E_REDIRECT_FAILED;
    default:
        return INET_E_DOWNLOAD_FAILURE;
    }
}

static HRESULT handle_http_error(HttpProtocol *This, DWORD error)
{
    IServiceProvider *serv_prov;
    IWindowForBindingUI *wfb_ui;
    IHttpSecurity *http_security;
    BOOL security_problem;
131 132 133
    DWORD dlg_flags;
    HWND hwnd;
    DWORD res;
134 135
    HRESULT hres;

136 137
    TRACE("(%p %u)\n", This, error);

138 139 140 141 142 143 144
    switch(error) {
    case ERROR_INTERNET_SEC_CERT_DATE_INVALID:
    case ERROR_INTERNET_SEC_CERT_CN_INVALID:
    case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR:
    case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR:
    case ERROR_INTERNET_INVALID_CA:
    case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED:
145 146 147 148 149 150
    case ERROR_INTERNET_SEC_INVALID_CERT:
    case ERROR_INTERNET_SEC_CERT_ERRORS:
    case ERROR_INTERNET_SEC_CERT_REV_FAILED:
    case ERROR_INTERNET_SEC_CERT_NO_REV:
    case ERROR_INTERNET_SEC_CERT_REVOKED:
    case ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION:
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
        security_problem = TRUE;
        break;
    default:
        security_problem = FALSE;
    }

    hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
                                                (void**)&serv_prov);
    if(FAILED(hres)) {
        ERR("Failed to get IServiceProvider.\n");
        return E_ABORT;
    }

    if(security_problem) {
        hres = IServiceProvider_QueryService(serv_prov, &IID_IHttpSecurity, &IID_IHttpSecurity,
                                             (void**)&http_security);
        if(SUCCEEDED(hres)) {
            hres = IHttpSecurity_OnSecurityProblem(http_security, error);
            IHttpSecurity_Release(http_security);

171 172
            TRACE("OnSecurityProblem returned %08x\n", hres);

173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
            if(hres != S_FALSE)
            {
                BOOL res = FALSE;

                IServiceProvider_Release(serv_prov);

                if(hres == S_OK) {
                    if(error == ERROR_INTERNET_SEC_CERT_DATE_INVALID)
                        res = set_security_flag(This, SECURITY_FLAG_IGNORE_CERT_DATE_INVALID);
                    else if(error == ERROR_INTERNET_SEC_CERT_CN_INVALID)
                        res = set_security_flag(This, SECURITY_FLAG_IGNORE_CERT_CN_INVALID);
                    else if(error == ERROR_INTERNET_INVALID_CA)
                        res = set_security_flag(This, SECURITY_FLAG_IGNORE_UNKNOWN_CA);

                    if(res)
                        return RPC_E_RETRY;

                    FIXME("Don't know how to ignore error %d\n", error);
                    return E_ABORT;
                }

                if(hres == E_ABORT)
                    return E_ABORT;
                if(hres == RPC_E_RETRY)
                    return RPC_E_RETRY;

                return internet_error_to_hres(error);
            }
        }
    }

204 205
    switch(error) {
    case ERROR_INTERNET_SEC_CERT_REV_FAILED:
206
        if(hres != S_FALSE) {
207 208 209
            /* Silently ignore the error. We will get more detailed error from wininet anyway. */
            set_security_flag(This, SECURITY_FLAG_IGNORE_REVOCATION);
            hres = RPC_E_RETRY;
210
            break;
211
        }
212
        /* fallthrough */
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227
    default:
        hres = IServiceProvider_QueryService(serv_prov, &IID_IWindowForBindingUI, &IID_IWindowForBindingUI, (void**)&wfb_ui);
        if(SUCCEEDED(hres)) {
            const IID *iid_reason;

            if(security_problem)
                iid_reason = &IID_IHttpSecurity;
            else if(error == ERROR_INTERNET_INCORRECT_PASSWORD)
                iid_reason = &IID_IAuthenticate;
            else
                iid_reason = &IID_IWindowForBindingUI;

            hres = IWindowForBindingUI_GetWindow(wfb_ui, iid_reason, &hwnd);
            IWindowForBindingUI_Release(wfb_ui);
        }
228

229
        if(FAILED(hres)) hwnd = NULL;
230

231 232 233
        dlg_flags = FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA;
        if(This->base.bindf & BINDF_NO_UI)
            dlg_flags |= FLAGS_ERROR_UI_FLAGS_NO_UI;
234

235 236 237 238 239 240
        res = InternetErrorDlg(hwnd, This->base.request, error, dlg_flags, NULL);
        hres = res == ERROR_INTERNET_FORCE_RETRY || res == ERROR_SUCCESS ? RPC_E_RETRY : internet_error_to_hres(error);
    }

    IServiceProvider_Release(serv_prov);
    return hres;
241 242
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284
static ULONG send_http_request(HttpProtocol *This)
{
    INTERNET_BUFFERSW send_buffer = {sizeof(INTERNET_BUFFERSW)};
    BOOL res;

    send_buffer.lpcszHeader = This->full_header;
    send_buffer.dwHeadersLength = send_buffer.dwHeadersTotal = strlenW(This->full_header);

    if(This->base.bind_info.dwBindVerb != BINDVERB_GET) {
        switch(This->base.bind_info.stgmedData.tymed) {
        case TYMED_HGLOBAL:
            /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
            send_buffer.lpvBuffer = This->base.bind_info.stgmedData.u.hGlobal;
            send_buffer.dwBufferLength = send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData;
            break;
        case TYMED_ISTREAM: {
            LARGE_INTEGER offset;

            send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData;
            if(!This->base.post_stream) {
                This->base.post_stream = This->base.bind_info.stgmedData.u.pstm;
                IStream_AddRef(This->base.post_stream);
            }

            offset.QuadPart = 0;
            IStream_Seek(This->base.post_stream, offset, STREAM_SEEK_SET, NULL);
            break;
        }
        default:
            FIXME("Unsupported This->base.bind_info.stgmedData.tymed %d\n", This->base.bind_info.stgmedData.tymed);
        }
    }

    if(This->base.post_stream)
        res = HttpSendRequestExW(This->base.request, &send_buffer, NULL, 0, 0);
    else
        res = HttpSendRequestW(This->base.request, send_buffer.lpcszHeader, send_buffer.dwHeadersLength,
                send_buffer.lpvBuffer, send_buffer.dwBufferLength);

    return res ? 0 : GetLastError();
}

285 286 287 288
static inline HttpProtocol *impl_from_Protocol(Protocol *prot)
{
    return CONTAINING_RECORD(prot, HttpProtocol, base);
}
289

290
static HRESULT HttpProtocol_open_request(Protocol *prot, IUri *uri, DWORD request_flags,
291
        HINTERNET internet_session, IInternetBindInfo *bind_info)
292
{
293
    HttpProtocol *This = impl_from_Protocol(prot);
294
    WCHAR *addl_header = NULL, *post_cookie = NULL, *rootdoc_url = NULL;
295 296
    IServiceProvider *service_provider = NULL;
    IHttpNegotiate2 *http_negotiate2 = NULL;
297
    BSTR url, host, user, pass, path;
298
    LPOLESTR accept_mimes[257];
299
    const WCHAR **accept_types;
300
    BYTE security_id[512];
301
    DWORD len, port, flags;
302
    ULONG num, error;
303
    BOOL res, b;
304 305 306 307 308 309 310
    HRESULT hres;

    static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
        {{'G','E','T',0},
         {'P','O','S','T',0},
         {'P','U','T',0}};

311 312 313 314 315 316 317
    hres = IUri_GetPort(uri, &port);
    if(FAILED(hres))
        return hres;

    hres = IUri_GetHost(uri, &host);
    if(FAILED(hres))
        return hres;
318

319 320 321 322 323 324 325 326 327 328 329 330 331 332
    hres = IUri_GetUserName(uri, &user);
    if(SUCCEEDED(hres)) {
        hres = IUri_GetPassword(uri, &pass);

        if(SUCCEEDED(hres)) {
            This->base.connection = InternetConnectW(internet_session, host, port, user, pass,
                    INTERNET_SERVICE_HTTP, This->https ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)&This->base);
            SysFreeString(pass);
        }
        SysFreeString(user);
    }
    SysFreeString(host);
    if(FAILED(hres))
        return hres;
333 334 335 336 337
    if(!This->base.connection) {
        WARN("InternetConnect failed: %d\n", GetLastError());
        return INET_E_CANNOT_CONNECT;
    }

338 339 340 341 342 343 344
    num = 0;
    hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ROOTDOC_URL, &rootdoc_url, 1, &num);
    if(hres == S_OK && num) {
        FIXME("Use root doc URL %s\n", debugstr_w(rootdoc_url));
        CoTaskMemFree(rootdoc_url);
    }

345
    num = ARRAY_SIZE(accept_mimes) - 1;
346
    hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ACCEPT_MIMES, accept_mimes, num, &num);
347 348 349 350 351 352 353 354 355
    if(hres == INET_E_USE_DEFAULT_SETTING) {
        static const WCHAR default_accept_mimeW[] = {'*','/','*',0};
        static const WCHAR *default_accept_mimes[] = {default_accept_mimeW, NULL};

        accept_types = default_accept_mimes;
        num = 0;
    }else if(hres == S_OK) {
        accept_types = (const WCHAR**)accept_mimes;
    }else {
356 357 358 359 360 361 362
        WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
        return INET_E_NO_VALID_MEDIA;
    }
    accept_mimes[num] = 0;

    if(This->https)
        request_flags |= INTERNET_FLAG_SECURE;
363 364 365 366 367 368 369 370 371

    hres = IUri_GetPathAndQuery(uri, &path);
    if(SUCCEEDED(hres)) {
        This->base.request = HttpOpenRequestW(This->base.connection,
                This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM
                    ? wszBindVerb[This->base.bind_info.dwBindVerb] : This->base.bind_info.szCustomVerb,
                path, NULL, NULL, accept_types, request_flags, (DWORD_PTR)&This->base);
        SysFreeString(path);
    }
372 373
    while(num--)
        CoTaskMemFree(accept_mimes[num]);
374 375
    if(FAILED(hres))
        return hres;
376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
    if (!This->base.request) {
        WARN("HttpOpenRequest failed: %d\n", GetLastError());
        return INET_E_RESOURCE_NOT_FOUND;
    }

    hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
            (void **)&service_provider);
    if (hres != S_OK) {
        WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
        return hres;
    }

    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);
392
        IServiceProvider_Release(service_provider);
393 394 395
        return hres;
    }

396 397 398 399 400 401
    hres = IUri_GetAbsoluteUri(uri, &url);
    if(FAILED(hres)) {
        IServiceProvider_Release(service_provider);
        return hres;
    }

402
    hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, url, default_headersW,
403
            0, &addl_header);
404
    SysFreeString(url);
405 406 407 408 409 410
    if(hres != S_OK) {
        WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
        IServiceProvider_Release(service_provider);
        return hres;
    }

411
    len = addl_header ? strlenW(addl_header) : 0;
412

413 414 415 416
    This->full_header = heap_alloc(len*sizeof(WCHAR)+sizeof(default_headersW));
    if(!This->full_header) {
        IServiceProvider_Release(service_provider);
        return E_OUTOFMEMORY;
417 418
    }

419 420 421 422 423
    if(len)
        memcpy(This->full_header, addl_header, len*sizeof(WCHAR));
    CoTaskMemFree(addl_header);
    memcpy(This->full_header+len, default_headersW, sizeof(default_headersW));

424 425 426 427 428 429 430
    hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
            &IID_IHttpNegotiate2, (void **)&http_negotiate2);
    IServiceProvider_Release(service_provider);
    if(hres != S_OK) {
        WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
        /* No goto done as per native */
    }else {
431
        len = ARRAY_SIZE(security_id);
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
        hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
        IHttpNegotiate2_Release(http_negotiate2);
        if (hres != S_OK)
            WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
    }

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

    if(This->base.bind_info.dwBindVerb == BINDVERB_POST) {
        num = 0;
        hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_POST_COOKIE, &post_cookie, 1, &num);
        if(hres == S_OK && num) {
            if(!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
                                   post_cookie, lstrlenW(post_cookie)))
                WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n", GetLastError());
            CoTaskMemFree(post_cookie);
        }
    }

451 452 453 454 455
    flags = INTERNET_ERROR_MASK_COMBINED_SEC_CERT;
    res = InternetSetOptionW(This->base.request, INTERNET_OPTION_ERROR_MASK, &flags, sizeof(flags));
    if(!res)
        WARN("InternetSetOption(INTERNET_OPTION_ERROR_MASK) failed: %u\n", GetLastError());

456 457 458
    b = TRUE;
    res = InternetSetOptionW(This->base.request, INTERNET_OPTION_HTTP_DECODING, &b, sizeof(b));
    if(!res)
459
        WARN("InternetSetOption(INTERNET_OPTION_HTTP_DECODING) failed: %u\n", GetLastError());
460

461 462
    do {
        error = send_http_request(This);
463

464 465
        switch(error) {
        case ERROR_IO_PENDING:
466
            return S_OK;
467 468 469 470 471 472 473 474 475
        case ERROR_SUCCESS:
            /*
             * If sending response ended synchronously, it means that we have the whole data
             * available locally (most likely in cache).
             */
            return protocol_syncbinding(&This->base);
        default:
            hres = handle_http_error(This, error);
        }
476
    } while(hres == RPC_E_RETRY);
477

478
    WARN("HttpSendRequest failed: %d\n", error);
479
    return hres;
480 481
}

482 483 484 485 486 487 488 489 490 491 492 493 494
static HRESULT HttpProtocol_end_request(Protocol *protocol)
{
    BOOL res;

    res = HttpEndRequestW(protocol->request, NULL, 0, 0);
    if(!res && GetLastError() != ERROR_IO_PENDING) {
        FIXME("HttpEndRequest failed: %u\n", GetLastError());
        return E_FAIL;
    }

    return S_OK;
}

495 496 497 498 499 500 501 502 503 504 505 506
static BOOL is_redirect_response(DWORD status_code)
{
    switch(status_code) {
    case HTTP_STATUS_REDIRECT:
    case HTTP_STATUS_MOVED:
    case HTTP_STATUS_REDIRECT_KEEP_VERB:
    case HTTP_STATUS_REDIRECT_METHOD:
        return TRUE;
    }
    return FALSE;
}

507 508
static HRESULT HttpProtocol_start_downloading(Protocol *prot)
{
509
    HttpProtocol *This = impl_from_Protocol(prot);
510
    LPWSTR content_type, content_length, ranges;
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526
    DWORD len = sizeof(DWORD);
    DWORD status_code;
    BOOL res;
    HRESULT hres;

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

    if(!This->http_negotiate) {
        WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
        return S_OK;
    }

    res = HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
            &status_code, &len, NULL);
    if(res) {
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
        WCHAR *response_headers;

        if((This->base.bind_info.dwOptions & BINDINFO_OPTIONS_DISABLEAUTOREDIRECTS) && is_redirect_response(status_code)) {
            WCHAR *location;

            TRACE("Got redirect with disabled auto redirects\n");

            location = query_http_info(This, HTTP_QUERY_LOCATION);
            This->base.flags |= FLAG_RESULT_REPORTED | FLAG_LAST_DATA_REPORTED;
            IInternetProtocolSink_ReportResult(This->base.protocol_sink, INET_E_REDIRECT_FAILED, 0, location);
            heap_free(location);
            return INET_E_REDIRECT_FAILED;
        }

        response_headers = query_http_info(This, HTTP_QUERY_RAW_HEADERS_CRLF);
542 543 544 545 546 547 548 549 550 551 552 553 554
        if(response_headers) {
            hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code, response_headers,
                    NULL, NULL);
            heap_free(response_headers);
            if (hres != S_OK) {
                WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
                return S_OK;
            }
        }
    }else {
        WARN("HttpQueryInfo failed: %d\n", GetLastError());
    }

555 556
    ranges = query_http_info(This, HTTP_QUERY_ACCEPT_RANGES);
    if(ranges) {
557
        IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL);
558 559
        heap_free(ranges);
    }
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588

    content_type = query_http_info(This, HTTP_QUERY_CONTENT_TYPE);
    if(content_type) {
        /* remove the charset, if present */
        LPWSTR p = strchrW(content_type, ';');
        if (p) *p = '\0';

        IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
                (This->base.bindf & BINDF_FROMURLMON)
                 ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
                 content_type);
        heap_free(content_type);
    }else {
        WARN("HttpQueryInfo failed: %d\n", GetLastError());
        IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
                 (This->base.bindf & BINDF_FROMURLMON)
                  ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
                  wszDefaultContentType);
    }

    content_length = query_http_info(This, HTTP_QUERY_CONTENT_LENGTH);
    if(content_length) {
        This->base.content_length = atoiW(content_length);
        heap_free(content_length);
    }

    return S_OK;
}

589 590
static void HttpProtocol_close_connection(Protocol *prot)
{
591
    HttpProtocol *This = impl_from_Protocol(prot);
592 593 594

    if(This->http_negotiate) {
        IHttpNegotiate_Release(This->http_negotiate);
595
        This->http_negotiate = NULL;
596 597
    }

598 599
    heap_free(This->full_header);
    This->full_header = NULL;
600 601
}

602 603
static void HttpProtocol_on_error(Protocol *prot, DWORD error)
{
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
    HttpProtocol *This = impl_from_Protocol(prot);
    HRESULT hres;

    TRACE("(%p) %d\n", prot, error);

    if(prot->flags & FLAG_FIRST_CONTINUE_COMPLETE) {
        FIXME("Not handling error %d\n", error);
        return;
    }

    while((hres = handle_http_error(This, error)) == RPC_E_RETRY) {
        error = send_http_request(This);

        if(error == ERROR_IO_PENDING || error == ERROR_SUCCESS)
            return;
    }

    protocol_abort(prot, hres);
    protocol_close_connection(prot);
    return;
624 625
}

626
static const ProtocolVtbl AsyncProtocolVtbl = {
627
    HttpProtocol_open_request,
628
    HttpProtocol_end_request,
629
    HttpProtocol_start_downloading,
630 631
    HttpProtocol_close_connection,
    HttpProtocol_on_error
632
};
633

634
static HRESULT WINAPI HttpProtocolUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
635
{
636
    HttpProtocol *This = impl_from_IUnknown(iface);
637 638 639

    if(IsEqualGUID(&IID_IUnknown, riid)) {
        TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
640
        *ppv = &This->IUnknown_outer;
641 642
    }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
643
        *ppv = &This->IInternetProtocolEx_iface;
644 645
    }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
        TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
646
        *ppv = &This->IInternetProtocolEx_iface;
647 648
    }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
        TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv);
649
        *ppv = &This->IInternetProtocolEx_iface;
650 651
    }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
        TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
652
        *ppv = &This->IInternetPriority_iface;
653 654
    }else if(IsEqualGUID(&IID_IWinInetInfo, riid)) {
        TRACE("(%p)->(IID_IWinInetInfo %p)\n", This, ppv);
655
        *ppv = &This->IWinInetHttpInfo_iface;
656 657
    }else if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) {
        TRACE("(%p)->(IID_IWinInetHttpInfo %p)\n", This, ppv);
658
        *ppv = &This->IWinInetHttpInfo_iface;
659 660 661 662
    }else {
        *ppv = NULL;
        WARN("not supported interface %s\n", debugstr_guid(riid));
        return E_NOINTERFACE;
663 664
    }

665 666
    IUnknown_AddRef((IUnknown*)*ppv);
    return S_OK;
667 668
}

669
static ULONG WINAPI HttpProtocolUnk_AddRef(IUnknown *iface)
670
{
671
    HttpProtocol *This = impl_from_IUnknown(iface);
672
    LONG ref = InterlockedIncrement(&This->ref);
673
    TRACE("(%p) ref=%d\n", This, ref);
674 675 676
    return ref;
}

677
static ULONG WINAPI HttpProtocolUnk_Release(IUnknown *iface)
678
{
679
    HttpProtocol *This = impl_from_IUnknown(iface);
680 681
    LONG ref = InterlockedDecrement(&This->ref);

682
    TRACE("(%p) ref=%d\n", This, ref);
683 684

    if(!ref) {
685
        protocol_close_connection(&This->base);
686
        heap_free(This);
687 688 689 690 691 692 693

        URLMON_UnlockModule();
    }

    return ref;
}

694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720
static const IUnknownVtbl HttpProtocolUnkVtbl = {
    HttpProtocolUnk_QueryInterface,
    HttpProtocolUnk_AddRef,
    HttpProtocolUnk_Release
};

static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
{
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
    TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
    return IUnknown_QueryInterface(This->outer, riid, ppv);
}

static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocolEx *iface)
{
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
    TRACE("(%p)\n", This);
    return IUnknown_AddRef(This->outer);
}

static ULONG WINAPI HttpProtocol_Release(IInternetProtocolEx *iface)
{
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
    TRACE("(%p)\n", This);
    return IUnknown_Release(This->outer);
}

721
static HRESULT WINAPI HttpProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl,
722
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
723
        DWORD grfPI, HANDLE_PTR dwReserved)
724
{
725
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
726 727
    IUri *uri;
    HRESULT hres;
728

729
    TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
730
            pOIBindInfo, grfPI, dwReserved);
731

732 733 734 735
    hres = CreateUri(szUrl, 0, 0, &uri);
    if(FAILED(hres))
        return hres;

736 737
    hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink,
            pOIBindInfo, grfPI, (HANDLE*)dwReserved);
738 739 740

    IUri_Release(uri);
    return hres;
741 742
}

743
static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData)
744
{
745
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
746 747 748

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

749
    return protocol_continue(&This->base, pProtocolData);
750 751
}

752
static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason,
753 754
        DWORD dwOptions)
{
755
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
756 757 758 759

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

    return protocol_abort(&This->base, hrReason);
760 761
}

762
static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions)
763
{
764
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
765 766 767

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

768
    protocol_close_connection(&This->base);
769
    return S_OK;
770 771
}

772
static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocolEx *iface)
773
{
774
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
775 776 777 778
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

779
static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocolEx *iface)
780
{
781
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
782 783 784 785
    FIXME("(%p)\n", This);
    return E_NOTIMPL;
}

786
static HRESULT WINAPI HttpProtocol_Read(IInternetProtocolEx *iface, void *pv,
787 788
        ULONG cb, ULONG *pcbRead)
{
789
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
790 791 792

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

793
    return protocol_read(&This->base, pv, cb, pcbRead);
794 795
}

796
static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove,
797
        DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
798
{
799
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
800
    FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
801 802 803
    return E_NOTIMPL;
}

804
static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions)
805
{
806
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
807 808 809

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

810
    return protocol_lock_request(&This->base);
811 812
}

813
static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocolEx *iface)
814
{
815
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
816 817 818

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

819
    return protocol_unlock_request(&This->base);
820 821
}

822 823 824 825
static HRESULT WINAPI HttpProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri,
        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
        DWORD grfPI, HANDLE *dwReserved)
{
826
    HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
827 828 829 830 831 832 833 834 835 836 837 838
    DWORD scheme = 0;
    HRESULT hres;

    TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
            pOIBindInfo, grfPI, dwReserved);

    hres = IUri_GetScheme(pUri, &scheme);
    if(FAILED(hres))
        return hres;
    if(scheme != (This->https ? URL_SCHEME_HTTPS : URL_SCHEME_HTTP))
        return MK_E_SYNTAX;

839 840
    return protocol_start(&This->base, (IInternetProtocol*)&This->IInternetProtocolEx_iface, pUri,
                          pOIProtSink, pOIBindInfo);
841 842 843
}

static const IInternetProtocolExVtbl HttpProtocolVtbl = {
844 845 846 847 848 849 850 851 852 853 854 855
    HttpProtocol_QueryInterface,
    HttpProtocol_AddRef,
    HttpProtocol_Release,
    HttpProtocol_Start,
    HttpProtocol_Continue,
    HttpProtocol_Abort,
    HttpProtocol_Terminate,
    HttpProtocol_Suspend,
    HttpProtocol_Resume,
    HttpProtocol_Read,
    HttpProtocol_Seek,
    HttpProtocol_LockRequest,
856 857
    HttpProtocol_UnlockRequest,
    HttpProtocol_StartEx
858 859
};

860 861
static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
{
862 863
    HttpProtocol *This = impl_from_IInternetPriority(iface);
    return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
864 865 866 867
}

static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
{
868 869
    HttpProtocol *This = impl_from_IInternetPriority(iface);
    return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
870 871 872 873
}

static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
{
874 875
    HttpProtocol *This = impl_from_IInternetPriority(iface);
    return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
876 877 878 879
}

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

882
    TRACE("(%p)->(%d)\n", This, nPriority);
883

884
    This->base.priority = nPriority;
885 886 887 888 889
    return S_OK;
}

static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
{
890
    HttpProtocol *This = impl_from_IInternetPriority(iface);
891 892 893

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

894
    *pnPriority = This->base.priority;
895 896 897 898 899 900 901 902 903 904 905
    return S_OK;
}

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

906 907
static HRESULT WINAPI HttpInfo_QueryInterface(IWinInetHttpInfo *iface, REFIID riid, void **ppv)
{
908 909
    HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
    return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
910 911 912 913
}

static ULONG WINAPI HttpInfo_AddRef(IWinInetHttpInfo *iface)
{
914 915
    HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
    return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
916 917 918 919
}

static ULONG WINAPI HttpInfo_Release(IWinInetHttpInfo *iface)
{
920 921
    HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
    return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
922 923 924 925 926
}

static HRESULT WINAPI HttpInfo_QueryOption(IWinInetHttpInfo *iface, DWORD dwOption,
        void *pBuffer, DWORD *pcbBuffer)
{
927
    HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
928 929 930 931 932 933 934 935
    TRACE("(%p)->(%x %p %p)\n", This, dwOption, pBuffer, pcbBuffer);

    if(!This->base.request)
        return E_FAIL;

    if(!InternetQueryOptionW(This->base.request, dwOption, pBuffer, pcbBuffer))
        return S_FALSE;
    return S_OK;
936 937 938 939 940
}

static HRESULT WINAPI HttpInfo_QueryInfo(IWinInetHttpInfo *iface, DWORD dwOption,
        void *pBuffer, DWORD *pcbBuffer, DWORD *pdwFlags, DWORD *pdwReserved)
{
941
    HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
942 943 944 945 946
    TRACE("(%p)->(%x %p %p %p %p)\n", This, dwOption, pBuffer, pcbBuffer, pdwFlags, pdwReserved);

    if(!This->base.request)
        return E_FAIL;

947 948 949
    if(!HttpQueryInfoA(This->base.request, dwOption, pBuffer, pcbBuffer, pdwFlags))
        return S_FALSE;

950
    return S_OK;
951 952 953 954 955 956 957 958 959 960
}

static const IWinInetHttpInfoVtbl WinInetHttpInfoVtbl = {
    HttpInfo_QueryInterface,
    HttpInfo_AddRef,
    HttpInfo_Release,
    HttpInfo_QueryOption,
    HttpInfo_QueryInfo
};

961
static HRESULT create_http_protocol(BOOL https, IUnknown *outer, void **ppobj)
962 963 964
{
    HttpProtocol *ret;

965 966 967
    ret = heap_alloc_zero(sizeof(HttpProtocol));
    if(!ret)
        return E_OUTOFMEMORY;
968

969
    ret->base.vtbl = &AsyncProtocolVtbl;
970
    ret->IUnknown_outer.lpVtbl            = &HttpProtocolUnkVtbl;
971 972 973
    ret->IInternetProtocolEx_iface.lpVtbl = &HttpProtocolVtbl;
    ret->IInternetPriority_iface.lpVtbl   = &HttpPriorityVtbl;
    ret->IWinInetHttpInfo_iface.lpVtbl    = &WinInetHttpInfoVtbl;
974 975

    ret->https = https;
976
    ret->ref = 1;
977
    ret->outer = outer ? outer : &ret->IUnknown_outer;
978

979
    *ppobj = &ret->IUnknown_outer;
980

981
    URLMON_LockModule();
982 983
    return S_OK;
}
984

985
HRESULT HttpProtocol_Construct(IUnknown *outer, void **ppv)
986
{
987
    TRACE("(%p %p)\n", outer, ppv);
988

989
    return create_http_protocol(FALSE, outer, ppv);
990 991
}

992
HRESULT HttpSProtocol_Construct(IUnknown *outer, void **ppv)
993
{
994
    TRACE("(%p %p)\n", outer, ppv);
995

996
    return create_http_protocol(TRUE, outer, ppv);
997
}