xref: /reactos/dll/win32/urlmon/http.c (revision 8c2e9189)
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