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