1 /* 2 * Copyright 2005 Jacek Caban 3 * Copyright 2007 Misha Koshelev 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2.1 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * 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 #define NONAMELESSUNION 21 22 #include "urlmon_main.h" 23 #include "wininet.h" 24 25 #define NO_SHLWAPI_REG 26 #include "shlwapi.h" 27 28 #include "wine/debug.h" 29 30 WINE_DEFAULT_DEBUG_CHANNEL(urlmon); 31 32 typedef struct { 33 Protocol base; 34 35 IInternetProtocolEx IInternetProtocolEx_iface; 36 IInternetPriority IInternetPriority_iface; 37 IWinInetHttpInfo IWinInetHttpInfo_iface; 38 39 BOOL https; 40 IHttpNegotiate *http_negotiate; 41 WCHAR *full_header; 42 43 LONG ref; 44 } HttpProtocol; 45 46 static inline HttpProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface) 47 { 48 return CONTAINING_RECORD(iface, HttpProtocol, IInternetProtocolEx_iface); 49 } 50 51 static inline HttpProtocol *impl_from_IInternetPriority(IInternetPriority *iface) 52 { 53 return CONTAINING_RECORD(iface, HttpProtocol, IInternetPriority_iface); 54 } 55 56 static inline HttpProtocol *impl_from_IWinInetHttpInfo(IWinInetHttpInfo *iface) 57 { 58 return CONTAINING_RECORD(iface, HttpProtocol, IWinInetHttpInfo_iface); 59 } 60 61 static const WCHAR default_headersW[] = { 62 '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}; 63 64 static LPWSTR query_http_info(HttpProtocol *This, DWORD option) 65 { 66 LPWSTR ret = NULL; 67 DWORD len = 0; 68 BOOL res; 69 70 res = HttpQueryInfoW(This->base.request, option, NULL, &len, NULL); 71 if (!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER) { 72 ret = heap_alloc(len); 73 res = HttpQueryInfoW(This->base.request, option, ret, &len, NULL); 74 } 75 if(!res) { 76 TRACE("HttpQueryInfoW(%d) failed: %08x\n", option, GetLastError()); 77 heap_free(ret); 78 return NULL; 79 } 80 81 return ret; 82 } 83 84 static inline BOOL set_security_flag(HttpProtocol *This, DWORD flags) 85 { 86 BOOL res; 87 88 res = InternetSetOptionW(This->base.request, INTERNET_OPTION_SECURITY_FLAGS, &flags, sizeof(flags)); 89 if(!res) 90 ERR("Failed to set security flags: %x\n", flags); 91 92 return res; 93 } 94 95 static inline HRESULT internet_error_to_hres(DWORD error) 96 { 97 switch(error) 98 { 99 case ERROR_INTERNET_SEC_CERT_DATE_INVALID: 100 case ERROR_INTERNET_SEC_CERT_CN_INVALID: 101 case ERROR_INTERNET_INVALID_CA: 102 case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED: 103 case ERROR_INTERNET_SEC_INVALID_CERT: 104 case ERROR_INTERNET_SEC_CERT_ERRORS: 105 case ERROR_INTERNET_SEC_CERT_REV_FAILED: 106 case ERROR_INTERNET_SEC_CERT_NO_REV: 107 case ERROR_INTERNET_SEC_CERT_REVOKED: 108 return INET_E_INVALID_CERTIFICATE; 109 case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: 110 case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR: 111 case ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION: 112 return INET_E_REDIRECT_FAILED; 113 default: 114 return INET_E_DOWNLOAD_FAILURE; 115 } 116 } 117 118 static HRESULT handle_http_error(HttpProtocol *This, DWORD error) 119 { 120 IServiceProvider *serv_prov; 121 IWindowForBindingUI *wfb_ui; 122 IHttpSecurity *http_security; 123 BOOL security_problem; 124 DWORD dlg_flags; 125 HWND hwnd; 126 DWORD res; 127 HRESULT hres; 128 129 TRACE("(%p %u)\n", This, error); 130 131 switch(error) { 132 case ERROR_INTERNET_SEC_CERT_DATE_INVALID: 133 case ERROR_INTERNET_SEC_CERT_CN_INVALID: 134 case ERROR_INTERNET_HTTP_TO_HTTPS_ON_REDIR: 135 case ERROR_INTERNET_HTTPS_TO_HTTP_ON_REDIR: 136 case ERROR_INTERNET_INVALID_CA: 137 case ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED: 138 case ERROR_INTERNET_SEC_INVALID_CERT: 139 case ERROR_INTERNET_SEC_CERT_ERRORS: 140 case ERROR_INTERNET_SEC_CERT_REV_FAILED: 141 case ERROR_INTERNET_SEC_CERT_NO_REV: 142 case ERROR_INTERNET_SEC_CERT_REVOKED: 143 case ERROR_HTTP_REDIRECT_NEEDS_CONFIRMATION: 144 security_problem = TRUE; 145 break; 146 default: 147 security_problem = FALSE; 148 } 149 150 hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider, 151 (void**)&serv_prov); 152 if(FAILED(hres)) { 153 ERR("Failed to get IServiceProvider.\n"); 154 return E_ABORT; 155 } 156 157 if(security_problem) { 158 hres = IServiceProvider_QueryService(serv_prov, &IID_IHttpSecurity, &IID_IHttpSecurity, 159 (void**)&http_security); 160 if(SUCCEEDED(hres)) { 161 hres = IHttpSecurity_OnSecurityProblem(http_security, error); 162 IHttpSecurity_Release(http_security); 163 164 TRACE("OnSecurityProblem returned %08x\n", hres); 165 166 if(hres != S_FALSE) 167 { 168 BOOL res = FALSE; 169 170 IServiceProvider_Release(serv_prov); 171 172 if(hres == S_OK) { 173 if(error == ERROR_INTERNET_SEC_CERT_DATE_INVALID) 174 res = set_security_flag(This, SECURITY_FLAG_IGNORE_CERT_DATE_INVALID); 175 else if(error == ERROR_INTERNET_SEC_CERT_CN_INVALID) 176 res = set_security_flag(This, SECURITY_FLAG_IGNORE_CERT_CN_INVALID); 177 else if(error == ERROR_INTERNET_INVALID_CA) 178 res = set_security_flag(This, SECURITY_FLAG_IGNORE_UNKNOWN_CA); 179 180 if(res) 181 return RPC_E_RETRY; 182 183 FIXME("Don't know how to ignore error %d\n", error); 184 return E_ABORT; 185 } 186 187 if(hres == E_ABORT) 188 return E_ABORT; 189 if(hres == RPC_E_RETRY) 190 return RPC_E_RETRY; 191 192 return internet_error_to_hres(error); 193 } 194 } 195 } 196 197 switch(error) { 198 case ERROR_INTERNET_SEC_CERT_REV_FAILED: 199 if(hres != S_FALSE) { 200 /* Silently ignore the error. We will get more detailed error from wininet anyway. */ 201 set_security_flag(This, SECURITY_FLAG_IGNORE_REVOCATION); 202 hres = RPC_E_RETRY; 203 break; 204 } 205 /* fallthrough */ 206 default: 207 hres = IServiceProvider_QueryService(serv_prov, &IID_IWindowForBindingUI, &IID_IWindowForBindingUI, (void**)&wfb_ui); 208 if(SUCCEEDED(hres)) { 209 const IID *iid_reason; 210 211 if(security_problem) 212 iid_reason = &IID_IHttpSecurity; 213 else if(error == ERROR_INTERNET_INCORRECT_PASSWORD) 214 iid_reason = &IID_IAuthenticate; 215 else 216 iid_reason = &IID_IWindowForBindingUI; 217 218 hres = IWindowForBindingUI_GetWindow(wfb_ui, iid_reason, &hwnd); 219 IWindowForBindingUI_Release(wfb_ui); 220 } 221 222 if(FAILED(hres)) hwnd = NULL; 223 224 dlg_flags = FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS | FLAGS_ERROR_UI_FLAGS_GENERATE_DATA; 225 if(This->base.bindf & BINDF_NO_UI) 226 dlg_flags |= FLAGS_ERROR_UI_FLAGS_NO_UI; 227 228 res = InternetErrorDlg(hwnd, This->base.request, error, dlg_flags, NULL); 229 hres = res == ERROR_INTERNET_FORCE_RETRY || res == ERROR_SUCCESS ? RPC_E_RETRY : internet_error_to_hres(error); 230 } 231 232 IServiceProvider_Release(serv_prov); 233 return hres; 234 } 235 236 static ULONG send_http_request(HttpProtocol *This) 237 { 238 INTERNET_BUFFERSW send_buffer = {sizeof(INTERNET_BUFFERSW)}; 239 BOOL res; 240 241 send_buffer.lpcszHeader = This->full_header; 242 send_buffer.dwHeadersLength = send_buffer.dwHeadersTotal = strlenW(This->full_header); 243 244 if(This->base.bind_info.dwBindVerb != BINDVERB_GET) { 245 switch(This->base.bind_info.stgmedData.tymed) { 246 case TYMED_HGLOBAL: 247 /* Native does not use GlobalLock/GlobalUnlock, so we won't either */ 248 send_buffer.lpvBuffer = This->base.bind_info.stgmedData.u.hGlobal; 249 send_buffer.dwBufferLength = send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData; 250 break; 251 case TYMED_ISTREAM: { 252 LARGE_INTEGER offset; 253 254 send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData; 255 if(!This->base.post_stream) { 256 This->base.post_stream = This->base.bind_info.stgmedData.u.pstm; 257 IStream_AddRef(This->base.post_stream); 258 } 259 260 offset.QuadPart = 0; 261 IStream_Seek(This->base.post_stream, offset, STREAM_SEEK_SET, NULL); 262 break; 263 } 264 default: 265 FIXME("Unsupported This->base.bind_info.stgmedData.tymed %d\n", This->base.bind_info.stgmedData.tymed); 266 } 267 } 268 269 if(This->base.post_stream) 270 res = HttpSendRequestExW(This->base.request, &send_buffer, NULL, 0, 0); 271 else 272 res = HttpSendRequestW(This->base.request, send_buffer.lpcszHeader, send_buffer.dwHeadersLength, 273 send_buffer.lpvBuffer, send_buffer.dwBufferLength); 274 275 return res ? 0 : GetLastError(); 276 } 277 278 static inline HttpProtocol *impl_from_Protocol(Protocol *prot) 279 { 280 return CONTAINING_RECORD(prot, HttpProtocol, base); 281 } 282 283 static HRESULT HttpProtocol_open_request(Protocol *prot, IUri *uri, DWORD request_flags, 284 HINTERNET internet_session, IInternetBindInfo *bind_info) 285 { 286 HttpProtocol *This = impl_from_Protocol(prot); 287 WCHAR *addl_header = NULL, *post_cookie = NULL, *rootdoc_url = NULL; 288 IServiceProvider *service_provider = NULL; 289 IHttpNegotiate2 *http_negotiate2 = NULL; 290 BSTR url, host, user, pass, path; 291 LPOLESTR accept_mimes[257]; 292 const WCHAR **accept_types; 293 BYTE security_id[512]; 294 DWORD len, port, flags; 295 ULONG num, error; 296 BOOL res, b; 297 HRESULT hres; 298 299 static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] = 300 {{'G','E','T',0}, 301 {'P','O','S','T',0}, 302 {'P','U','T',0}}; 303 304 hres = IUri_GetPort(uri, &port); 305 if(FAILED(hres)) 306 return hres; 307 308 hres = IUri_GetHost(uri, &host); 309 if(FAILED(hres)) 310 return hres; 311 312 hres = IUri_GetUserName(uri, &user); 313 if(SUCCEEDED(hres)) { 314 hres = IUri_GetPassword(uri, &pass); 315 316 if(SUCCEEDED(hres)) { 317 This->base.connection = InternetConnectW(internet_session, host, port, user, pass, 318 INTERNET_SERVICE_HTTP, This->https ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)&This->base); 319 SysFreeString(pass); 320 } 321 SysFreeString(user); 322 } 323 SysFreeString(host); 324 if(FAILED(hres)) 325 return hres; 326 if(!This->base.connection) { 327 WARN("InternetConnect failed: %d\n", GetLastError()); 328 return INET_E_CANNOT_CONNECT; 329 } 330 331 num = 0; 332 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ROOTDOC_URL, &rootdoc_url, 1, &num); 333 if(hres == S_OK && num) { 334 FIXME("Use root doc URL %s\n", debugstr_w(rootdoc_url)); 335 CoTaskMemFree(rootdoc_url); 336 } 337 338 num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1; 339 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ACCEPT_MIMES, accept_mimes, num, &num); 340 if(hres == INET_E_USE_DEFAULT_SETTING) { 341 static const WCHAR default_accept_mimeW[] = {'*','/','*',0}; 342 static const WCHAR *default_accept_mimes[] = {default_accept_mimeW, NULL}; 343 344 accept_types = default_accept_mimes; 345 num = 0; 346 }else if(hres == S_OK) { 347 accept_types = (const WCHAR**)accept_mimes; 348 }else { 349 WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres); 350 return INET_E_NO_VALID_MEDIA; 351 } 352 accept_mimes[num] = 0; 353 354 if(This->https) 355 request_flags |= INTERNET_FLAG_SECURE; 356 357 hres = IUri_GetPathAndQuery(uri, &path); 358 if(SUCCEEDED(hres)) { 359 This->base.request = HttpOpenRequestW(This->base.connection, 360 This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM 361 ? wszBindVerb[This->base.bind_info.dwBindVerb] : This->base.bind_info.szCustomVerb, 362 path, NULL, NULL, accept_types, request_flags, (DWORD_PTR)&This->base); 363 SysFreeString(path); 364 } 365 while(num--) 366 CoTaskMemFree(accept_mimes[num]); 367 if(FAILED(hres)) 368 return hres; 369 if (!This->base.request) { 370 WARN("HttpOpenRequest failed: %d\n", GetLastError()); 371 return INET_E_RESOURCE_NOT_FOUND; 372 } 373 374 hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider, 375 (void **)&service_provider); 376 if (hres != S_OK) { 377 WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres); 378 return hres; 379 } 380 381 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate, 382 &IID_IHttpNegotiate, (void **)&This->http_negotiate); 383 if (hres != S_OK) { 384 WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres); 385 IServiceProvider_Release(service_provider); 386 return hres; 387 } 388 389 hres = IUri_GetAbsoluteUri(uri, &url); 390 if(FAILED(hres)) { 391 IServiceProvider_Release(service_provider); 392 return hres; 393 } 394 395 hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, url, default_headersW, 396 0, &addl_header); 397 SysFreeString(url); 398 if(hres != S_OK) { 399 WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres); 400 IServiceProvider_Release(service_provider); 401 return hres; 402 } 403 404 len = addl_header ? strlenW(addl_header) : 0; 405 406 This->full_header = heap_alloc(len*sizeof(WCHAR)+sizeof(default_headersW)); 407 if(!This->full_header) { 408 IServiceProvider_Release(service_provider); 409 return E_OUTOFMEMORY; 410 } 411 412 if(len) 413 memcpy(This->full_header, addl_header, len*sizeof(WCHAR)); 414 CoTaskMemFree(addl_header); 415 memcpy(This->full_header+len, default_headersW, sizeof(default_headersW)); 416 417 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2, 418 &IID_IHttpNegotiate2, (void **)&http_negotiate2); 419 IServiceProvider_Release(service_provider); 420 if(hres != S_OK) { 421 WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres); 422 /* No goto done as per native */ 423 }else { 424 len = sizeof(security_id)/sizeof(security_id[0]); 425 hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0); 426 IHttpNegotiate2_Release(http_negotiate2); 427 if (hres != S_OK) 428 WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres); 429 } 430 431 /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */ 432 433 if(This->base.bind_info.dwBindVerb == BINDVERB_POST) { 434 num = 0; 435 hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_POST_COOKIE, &post_cookie, 1, &num); 436 if(hres == S_OK && num) { 437 if(!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY, 438 post_cookie, lstrlenW(post_cookie))) 439 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n", GetLastError()); 440 CoTaskMemFree(post_cookie); 441 } 442 } 443 444 flags = INTERNET_ERROR_MASK_COMBINED_SEC_CERT; 445 res = InternetSetOptionW(This->base.request, INTERNET_OPTION_ERROR_MASK, &flags, sizeof(flags)); 446 if(!res) 447 WARN("InternetSetOption(INTERNET_OPTION_ERROR_MASK) failed: %u\n", GetLastError()); 448 449 b = TRUE; 450 res = InternetSetOptionW(This->base.request, INTERNET_OPTION_HTTP_DECODING, &b, sizeof(b)); 451 if(!res) 452 WARN("InternetSetOption(INTERNET_OPTION_HTTP_DECODING) failed: %u\n", GetLastError()); 453 454 do { 455 error = send_http_request(This); 456 457 switch(error) { 458 case ERROR_IO_PENDING: 459 return S_OK; 460 case ERROR_SUCCESS: 461 /* 462 * If sending response ended synchronously, it means that we have the whole data 463 * available locally (most likely in cache). 464 */ 465 return protocol_syncbinding(&This->base); 466 default: 467 hres = handle_http_error(This, error); 468 } 469 } while(hres == RPC_E_RETRY); 470 471 WARN("HttpSendRequest failed: %d\n", error); 472 return hres; 473 } 474 475 static HRESULT HttpProtocol_end_request(Protocol *protocol) 476 { 477 BOOL res; 478 479 res = HttpEndRequestW(protocol->request, NULL, 0, 0); 480 if(!res && GetLastError() != ERROR_IO_PENDING) { 481 FIXME("HttpEndRequest failed: %u\n", GetLastError()); 482 return E_FAIL; 483 } 484 485 return S_OK; 486 } 487 488 static BOOL is_redirect_response(DWORD status_code) 489 { 490 switch(status_code) { 491 case HTTP_STATUS_REDIRECT: 492 case HTTP_STATUS_MOVED: 493 case HTTP_STATUS_REDIRECT_KEEP_VERB: 494 case HTTP_STATUS_REDIRECT_METHOD: 495 return TRUE; 496 } 497 return FALSE; 498 } 499 500 static HRESULT HttpProtocol_start_downloading(Protocol *prot) 501 { 502 HttpProtocol *This = impl_from_Protocol(prot); 503 LPWSTR content_type, content_length, ranges; 504 DWORD len = sizeof(DWORD); 505 DWORD status_code; 506 BOOL res; 507 HRESULT hres; 508 509 static const WCHAR wszDefaultContentType[] = 510 {'t','e','x','t','/','h','t','m','l',0}; 511 512 if(!This->http_negotiate) { 513 WARN("Expected IHttpNegotiate pointer to be non-NULL\n"); 514 return S_OK; 515 } 516 517 res = HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, 518 &status_code, &len, NULL); 519 if(res) { 520 WCHAR *response_headers; 521 522 if((This->base.bind_info.dwOptions & BINDINFO_OPTIONS_DISABLEAUTOREDIRECTS) && is_redirect_response(status_code)) { 523 WCHAR *location; 524 525 TRACE("Got redirect with disabled auto redirects\n"); 526 527 location = query_http_info(This, HTTP_QUERY_LOCATION); 528 This->base.flags |= FLAG_RESULT_REPORTED | FLAG_LAST_DATA_REPORTED; 529 IInternetProtocolSink_ReportResult(This->base.protocol_sink, INET_E_REDIRECT_FAILED, 0, location); 530 heap_free(location); 531 return INET_E_REDIRECT_FAILED; 532 } 533 534 response_headers = query_http_info(This, HTTP_QUERY_RAW_HEADERS_CRLF); 535 if(response_headers) { 536 hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code, response_headers, 537 NULL, NULL); 538 heap_free(response_headers); 539 if (hres != S_OK) { 540 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres); 541 return S_OK; 542 } 543 } 544 }else { 545 WARN("HttpQueryInfo failed: %d\n", GetLastError()); 546 } 547 548 ranges = query_http_info(This, HTTP_QUERY_ACCEPT_RANGES); 549 if(ranges) { 550 IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL); 551 heap_free(ranges); 552 } 553 554 content_type = query_http_info(This, HTTP_QUERY_CONTENT_TYPE); 555 if(content_type) { 556 /* remove the charset, if present */ 557 LPWSTR p = strchrW(content_type, ';'); 558 if (p) *p = '\0'; 559 560 IInternetProtocolSink_ReportProgress(This->base.protocol_sink, 561 (This->base.bindf & BINDF_FROMURLMON) 562 ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE, 563 content_type); 564 heap_free(content_type); 565 }else { 566 WARN("HttpQueryInfo failed: %d\n", GetLastError()); 567 IInternetProtocolSink_ReportProgress(This->base.protocol_sink, 568 (This->base.bindf & BINDF_FROMURLMON) 569 ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE, 570 wszDefaultContentType); 571 } 572 573 content_length = query_http_info(This, HTTP_QUERY_CONTENT_LENGTH); 574 if(content_length) { 575 This->base.content_length = atoiW(content_length); 576 heap_free(content_length); 577 } 578 579 return S_OK; 580 } 581 582 static void HttpProtocol_close_connection(Protocol *prot) 583 { 584 HttpProtocol *This = impl_from_Protocol(prot); 585 586 if(This->http_negotiate) { 587 IHttpNegotiate_Release(This->http_negotiate); 588 This->http_negotiate = NULL; 589 } 590 591 heap_free(This->full_header); 592 This->full_header = NULL; 593 } 594 595 static void HttpProtocol_on_error(Protocol *prot, DWORD error) 596 { 597 HttpProtocol *This = impl_from_Protocol(prot); 598 HRESULT hres; 599 600 TRACE("(%p) %d\n", prot, error); 601 602 if(prot->flags & FLAG_FIRST_CONTINUE_COMPLETE) { 603 FIXME("Not handling error %d\n", error); 604 return; 605 } 606 607 while((hres = handle_http_error(This, error)) == RPC_E_RETRY) { 608 error = send_http_request(This); 609 610 if(error == ERROR_IO_PENDING || error == ERROR_SUCCESS) 611 return; 612 } 613 614 protocol_abort(prot, hres); 615 protocol_close_connection(prot); 616 return; 617 } 618 619 static const ProtocolVtbl AsyncProtocolVtbl = { 620 HttpProtocol_open_request, 621 HttpProtocol_end_request, 622 HttpProtocol_start_downloading, 623 HttpProtocol_close_connection, 624 HttpProtocol_on_error 625 }; 626 627 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv) 628 { 629 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 630 631 *ppv = NULL; 632 if(IsEqualGUID(&IID_IUnknown, riid)) { 633 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); 634 *ppv = &This->IInternetProtocolEx_iface; 635 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) { 636 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv); 637 *ppv = &This->IInternetProtocolEx_iface; 638 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) { 639 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv); 640 *ppv = &This->IInternetProtocolEx_iface; 641 }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) { 642 TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv); 643 *ppv = &This->IInternetProtocolEx_iface; 644 }else if(IsEqualGUID(&IID_IInternetPriority, riid)) { 645 TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv); 646 *ppv = &This->IInternetPriority_iface; 647 }else if(IsEqualGUID(&IID_IWinInetInfo, riid)) { 648 TRACE("(%p)->(IID_IWinInetInfo %p)\n", This, ppv); 649 *ppv = &This->IWinInetHttpInfo_iface; 650 }else if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) { 651 TRACE("(%p)->(IID_IWinInetHttpInfo %p)\n", This, ppv); 652 *ppv = &This->IWinInetHttpInfo_iface; 653 } 654 655 if(*ppv) { 656 IInternetProtocolEx_AddRef(iface); 657 return S_OK; 658 } 659 660 WARN("not supported interface %s\n", debugstr_guid(riid)); 661 return E_NOINTERFACE; 662 } 663 664 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocolEx *iface) 665 { 666 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 667 LONG ref = InterlockedIncrement(&This->ref); 668 TRACE("(%p) ref=%d\n", This, ref); 669 return ref; 670 } 671 672 static ULONG WINAPI HttpProtocol_Release(IInternetProtocolEx *iface) 673 { 674 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 675 LONG ref = InterlockedDecrement(&This->ref); 676 677 TRACE("(%p) ref=%d\n", This, ref); 678 679 if(!ref) { 680 protocol_close_connection(&This->base); 681 heap_free(This); 682 683 URLMON_UnlockModule(); 684 } 685 686 return ref; 687 } 688 689 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl, 690 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, 691 DWORD grfPI, HANDLE_PTR dwReserved) 692 { 693 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 694 IUri *uri; 695 HRESULT hres; 696 697 TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink, 698 pOIBindInfo, grfPI, dwReserved); 699 700 hres = CreateUri(szUrl, 0, 0, &uri); 701 if(FAILED(hres)) 702 return hres; 703 704 hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink, 705 pOIBindInfo, grfPI, (HANDLE*)dwReserved); 706 707 IUri_Release(uri); 708 return hres; 709 } 710 711 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData) 712 { 713 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 714 715 TRACE("(%p)->(%p)\n", This, pProtocolData); 716 717 return protocol_continue(&This->base, pProtocolData); 718 } 719 720 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason, 721 DWORD dwOptions) 722 { 723 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 724 725 TRACE("(%p)->(%08x %08x)\n", This, hrReason, dwOptions); 726 727 return protocol_abort(&This->base, hrReason); 728 } 729 730 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions) 731 { 732 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 733 734 TRACE("(%p)->(%08x)\n", This, dwOptions); 735 736 protocol_close_connection(&This->base); 737 return S_OK; 738 } 739 740 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocolEx *iface) 741 { 742 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 743 FIXME("(%p)\n", This); 744 return E_NOTIMPL; 745 } 746 747 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocolEx *iface) 748 { 749 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 750 FIXME("(%p)\n", This); 751 return E_NOTIMPL; 752 } 753 754 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocolEx *iface, void *pv, 755 ULONG cb, ULONG *pcbRead) 756 { 757 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 758 759 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead); 760 761 return protocol_read(&This->base, pv, cb, pcbRead); 762 } 763 764 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove, 765 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) 766 { 767 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 768 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition); 769 return E_NOTIMPL; 770 } 771 772 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions) 773 { 774 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 775 776 TRACE("(%p)->(%08x)\n", This, dwOptions); 777 778 return protocol_lock_request(&This->base); 779 } 780 781 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocolEx *iface) 782 { 783 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 784 785 TRACE("(%p)\n", This); 786 787 return protocol_unlock_request(&This->base); 788 } 789 790 static HRESULT WINAPI HttpProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri, 791 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, 792 DWORD grfPI, HANDLE *dwReserved) 793 { 794 HttpProtocol *This = impl_from_IInternetProtocolEx(iface); 795 DWORD scheme = 0; 796 HRESULT hres; 797 798 TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink, 799 pOIBindInfo, grfPI, dwReserved); 800 801 hres = IUri_GetScheme(pUri, &scheme); 802 if(FAILED(hres)) 803 return hres; 804 if(scheme != (This->https ? URL_SCHEME_HTTPS : URL_SCHEME_HTTP)) 805 return MK_E_SYNTAX; 806 807 return protocol_start(&This->base, (IInternetProtocol*)&This->IInternetProtocolEx_iface, pUri, 808 pOIProtSink, pOIBindInfo); 809 } 810 811 static const IInternetProtocolExVtbl HttpProtocolVtbl = { 812 HttpProtocol_QueryInterface, 813 HttpProtocol_AddRef, 814 HttpProtocol_Release, 815 HttpProtocol_Start, 816 HttpProtocol_Continue, 817 HttpProtocol_Abort, 818 HttpProtocol_Terminate, 819 HttpProtocol_Suspend, 820 HttpProtocol_Resume, 821 HttpProtocol_Read, 822 HttpProtocol_Seek, 823 HttpProtocol_LockRequest, 824 HttpProtocol_UnlockRequest, 825 HttpProtocol_StartEx 826 }; 827 828 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv) 829 { 830 HttpProtocol *This = impl_from_IInternetPriority(iface); 831 return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv); 832 } 833 834 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface) 835 { 836 HttpProtocol *This = impl_from_IInternetPriority(iface); 837 return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); 838 } 839 840 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface) 841 { 842 HttpProtocol *This = impl_from_IInternetPriority(iface); 843 return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface); 844 } 845 846 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority) 847 { 848 HttpProtocol *This = impl_from_IInternetPriority(iface); 849 850 TRACE("(%p)->(%d)\n", This, nPriority); 851 852 This->base.priority = nPriority; 853 return S_OK; 854 } 855 856 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority) 857 { 858 HttpProtocol *This = impl_from_IInternetPriority(iface); 859 860 TRACE("(%p)->(%p)\n", This, pnPriority); 861 862 *pnPriority = This->base.priority; 863 return S_OK; 864 } 865 866 static const IInternetPriorityVtbl HttpPriorityVtbl = { 867 HttpPriority_QueryInterface, 868 HttpPriority_AddRef, 869 HttpPriority_Release, 870 HttpPriority_SetPriority, 871 HttpPriority_GetPriority 872 }; 873 874 static HRESULT WINAPI HttpInfo_QueryInterface(IWinInetHttpInfo *iface, REFIID riid, void **ppv) 875 { 876 HttpProtocol *This = impl_from_IWinInetHttpInfo(iface); 877 return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv); 878 } 879 880 static ULONG WINAPI HttpInfo_AddRef(IWinInetHttpInfo *iface) 881 { 882 HttpProtocol *This = impl_from_IWinInetHttpInfo(iface); 883 return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface); 884 } 885 886 static ULONG WINAPI HttpInfo_Release(IWinInetHttpInfo *iface) 887 { 888 HttpProtocol *This = impl_from_IWinInetHttpInfo(iface); 889 return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface); 890 } 891 892 static HRESULT WINAPI HttpInfo_QueryOption(IWinInetHttpInfo *iface, DWORD dwOption, 893 void *pBuffer, DWORD *pcbBuffer) 894 { 895 HttpProtocol *This = impl_from_IWinInetHttpInfo(iface); 896 TRACE("(%p)->(%x %p %p)\n", This, dwOption, pBuffer, pcbBuffer); 897 898 if(!This->base.request) 899 return E_FAIL; 900 901 if(!InternetQueryOptionW(This->base.request, dwOption, pBuffer, pcbBuffer)) 902 return S_FALSE; 903 return S_OK; 904 } 905 906 static HRESULT WINAPI HttpInfo_QueryInfo(IWinInetHttpInfo *iface, DWORD dwOption, 907 void *pBuffer, DWORD *pcbBuffer, DWORD *pdwFlags, DWORD *pdwReserved) 908 { 909 HttpProtocol *This = impl_from_IWinInetHttpInfo(iface); 910 TRACE("(%p)->(%x %p %p %p %p)\n", This, dwOption, pBuffer, pcbBuffer, pdwFlags, pdwReserved); 911 912 if(!This->base.request) 913 return E_FAIL; 914 915 if(!HttpQueryInfoA(This->base.request, dwOption, pBuffer, pcbBuffer, pdwFlags)) 916 return S_FALSE; 917 918 return S_OK; 919 } 920 921 static const IWinInetHttpInfoVtbl WinInetHttpInfoVtbl = { 922 HttpInfo_QueryInterface, 923 HttpInfo_AddRef, 924 HttpInfo_Release, 925 HttpInfo_QueryOption, 926 HttpInfo_QueryInfo 927 }; 928 929 static HRESULT create_http_protocol(BOOL https, void **ppobj) 930 { 931 HttpProtocol *ret; 932 933 ret = heap_alloc_zero(sizeof(HttpProtocol)); 934 if(!ret) 935 return E_OUTOFMEMORY; 936 937 ret->base.vtbl = &AsyncProtocolVtbl; 938 ret->IInternetProtocolEx_iface.lpVtbl = &HttpProtocolVtbl; 939 ret->IInternetPriority_iface.lpVtbl = &HttpPriorityVtbl; 940 ret->IWinInetHttpInfo_iface.lpVtbl = &WinInetHttpInfoVtbl; 941 942 ret->https = https; 943 ret->ref = 1; 944 945 *ppobj = &ret->IInternetProtocolEx_iface; 946 947 URLMON_LockModule(); 948 return S_OK; 949 } 950 951 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj) 952 { 953 TRACE("(%p %p)\n", pUnkOuter, ppobj); 954 955 return create_http_protocol(FALSE, ppobj); 956 } 957 958 HRESULT HttpSProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj) 959 { 960 TRACE("(%p %p)\n", pUnkOuter, ppobj); 961 962 return create_http_protocol(TRUE, ppobj); 963 } 964