xref: /reactos/dll/win32/urlmon/http.c (revision 6c3c2e33)
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 
impl_from_IUnknown(IUnknown * iface)48 static inline HttpProtocol *impl_from_IUnknown(IUnknown *iface)
49 {
50     return CONTAINING_RECORD(iface, HttpProtocol, IUnknown_inner);
51 }
52 
impl_from_IInternetProtocolEx(IInternetProtocolEx * iface)53 static inline HttpProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
54 {
55     return CONTAINING_RECORD(iface, HttpProtocol, IInternetProtocolEx_iface);
56 }
57 
impl_from_IInternetPriority(IInternetPriority * iface)58 static inline HttpProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
59 {
60     return CONTAINING_RECORD(iface, HttpProtocol, IInternetPriority_iface);
61 }
62 
impl_from_IWinInetHttpInfo(IWinInetHttpInfo * iface)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 
query_http_info(HttpProtocol * This,DWORD option)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 
set_security_flag(HttpProtocol * This,DWORD flags)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 
internet_error_to_hres(DWORD error)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 
handle_http_error(HttpProtocol * This,DWORD error)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 
send_http_request(HttpProtocol * This)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 
impl_from_Protocol(Protocol * prot)285 static inline HttpProtocol *impl_from_Protocol(Protocol *prot)
286 {
287     return CONTAINING_RECORD(prot, HttpProtocol, base);
288 }
289 
HttpProtocol_open_request(Protocol * prot,IUri * uri,DWORD request_flags,HINTERNET internet_session,IInternetBindInfo * bind_info)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 
HttpProtocol_end_request(Protocol * protocol)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 
is_redirect_response(DWORD status_code)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 
HttpProtocol_start_downloading(Protocol * prot)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 
HttpProtocol_close_connection(Protocol * prot)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 
HttpProtocol_on_error(Protocol * prot,DWORD error)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 
HttpProtocolUnk_QueryInterface(IUnknown * iface,REFIID riid,void ** ppv)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 
HttpProtocolUnk_AddRef(IUnknown * iface)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 
HttpProtocolUnk_Release(IUnknown * iface)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 
HttpProtocol_QueryInterface(IInternetProtocolEx * iface,REFIID riid,void ** ppv)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 
HttpProtocol_AddRef(IInternetProtocolEx * iface)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 
HttpProtocol_Release(IInternetProtocolEx * iface)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 
HttpProtocol_Start(IInternetProtocolEx * iface,LPCWSTR szUrl,IInternetProtocolSink * pOIProtSink,IInternetBindInfo * pOIBindInfo,DWORD grfPI,HANDLE_PTR dwReserved)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 
HttpProtocol_Continue(IInternetProtocolEx * iface,PROTOCOLDATA * pProtocolData)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 
HttpProtocol_Abort(IInternetProtocolEx * iface,HRESULT hrReason,DWORD dwOptions)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 
HttpProtocol_Terminate(IInternetProtocolEx * iface,DWORD dwOptions)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 
HttpProtocol_Suspend(IInternetProtocolEx * iface)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 
HttpProtocol_Resume(IInternetProtocolEx * iface)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 
HttpProtocol_Read(IInternetProtocolEx * iface,void * pv,ULONG cb,ULONG * pcbRead)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 
HttpProtocol_Seek(IInternetProtocolEx * iface,LARGE_INTEGER dlibMove,DWORD dwOrigin,ULARGE_INTEGER * plibNewPosition)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 
HttpProtocol_LockRequest(IInternetProtocolEx * iface,DWORD dwOptions)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 
HttpProtocol_UnlockRequest(IInternetProtocolEx * iface)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 
HttpProtocol_StartEx(IInternetProtocolEx * iface,IUri * pUri,IInternetProtocolSink * pOIProtSink,IInternetBindInfo * pOIBindInfo,DWORD grfPI,HANDLE * dwReserved)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 
HttpPriority_QueryInterface(IInternetPriority * iface,REFIID riid,void ** ppv)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 
HttpPriority_AddRef(IInternetPriority * iface)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 
HttpPriority_Release(IInternetPriority * iface)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 
HttpPriority_SetPriority(IInternetPriority * iface,LONG nPriority)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 
HttpPriority_GetPriority(IInternetPriority * iface,LONG * pnPriority)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 
HttpInfo_QueryInterface(IWinInetHttpInfo * iface,REFIID riid,void ** ppv)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 
HttpInfo_AddRef(IWinInetHttpInfo * iface)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 
HttpInfo_Release(IWinInetHttpInfo * iface)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 
HttpInfo_QueryOption(IWinInetHttpInfo * iface,DWORD dwOption,void * pBuffer,DWORD * pcbBuffer)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 
HttpInfo_QueryInfo(IWinInetHttpInfo * iface,DWORD dwOption,void * pBuffer,DWORD * pcbBuffer,DWORD * pdwFlags,DWORD * pdwReserved)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 
create_http_protocol(BOOL https,IUnknown * outer,void ** ppobj)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 
HttpProtocol_Construct(IUnknown * outer,void ** ppv)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 
HttpSProtocol_Construct(IUnknown * outer,void ** ppv)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