mshtml: Pass DispatchEx pointer instead of outer IUnknown to DispatchEx's vtbl functions.
[wine] / dlls / urlmon / http.c
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 #include "urlmon_main.h"
21 #include "wininet.h"
22
23 #define NO_SHLWAPI_REG
24 #include "shlwapi.h"
25
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
29
30 typedef struct {
31     Protocol base;
32
33     IInternetProtocolEx IInternetProtocolEx_iface;
34     IInternetPriority   IInternetPriority_iface;
35     IWinInetHttpInfo    IWinInetHttpInfo_iface;
36
37     BOOL https;
38     IHttpNegotiate *http_negotiate;
39     LPWSTR full_header;
40
41     LONG ref;
42 } HttpProtocol;
43
44 static inline HttpProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
45 {
46     return CONTAINING_RECORD(iface, HttpProtocol, IInternetProtocolEx_iface);
47 }
48
49 static inline HttpProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
50 {
51     return CONTAINING_RECORD(iface, HttpProtocol, IInternetPriority_iface);
52 }
53
54 static inline HttpProtocol *impl_from_IWinInetHttpInfo(IWinInetHttpInfo *iface)
55 {
56     return CONTAINING_RECORD(iface, HttpProtocol, IWinInetHttpInfo_iface);
57 }
58
59 /* Default headers from native */
60 static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
61                                    ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
62
63 static LPWSTR query_http_info(HttpProtocol *This, DWORD option)
64 {
65     LPWSTR ret = NULL;
66     DWORD len = 0;
67     BOOL res;
68
69     res = HttpQueryInfoW(This->base.request, option, NULL, &len, NULL);
70     if (!res && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
71         ret = heap_alloc(len);
72         res = HttpQueryInfoW(This->base.request, option, ret, &len, NULL);
73     }
74     if(!res) {
75         TRACE("HttpQueryInfoW(%d) failed: %08x\n", option, GetLastError());
76         heap_free(ret);
77         return NULL;
78     }
79
80     return ret;
81 }
82
83 static ULONG send_http_request(HttpProtocol *This)
84 {
85     INTERNET_BUFFERSW send_buffer = {sizeof(INTERNET_BUFFERSW)};
86     BOOL res;
87
88     send_buffer.lpcszHeader = This->full_header;
89     send_buffer.dwHeadersLength = send_buffer.dwHeadersTotal = strlenW(This->full_header);
90
91     if(This->base.bind_info.dwBindVerb != BINDVERB_GET) {
92         switch(This->base.bind_info.stgmedData.tymed) {
93         case TYMED_HGLOBAL:
94             /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
95             send_buffer.lpvBuffer = This->base.bind_info.stgmedData.u.hGlobal;
96             send_buffer.dwBufferLength = send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData;
97             break;
98         case TYMED_ISTREAM: {
99             LARGE_INTEGER offset;
100
101             send_buffer.dwBufferTotal = This->base.bind_info.cbstgmedData;
102             if(!This->base.post_stream) {
103                 This->base.post_stream = This->base.bind_info.stgmedData.u.pstm;
104                 IStream_AddRef(This->base.post_stream);
105             }
106
107             offset.QuadPart = 0;
108             IStream_Seek(This->base.post_stream, offset, STREAM_SEEK_SET, NULL);
109             break;
110         }
111         default:
112             FIXME("Unsupported This->base.bind_info.stgmedData.tymed %d\n", This->base.bind_info.stgmedData.tymed);
113         }
114     }
115
116     if(This->base.post_stream)
117         res = HttpSendRequestExW(This->base.request, &send_buffer, NULL, 0, 0);
118     else
119         res = HttpSendRequestW(This->base.request, send_buffer.lpcszHeader, send_buffer.dwHeadersLength,
120                 send_buffer.lpvBuffer, send_buffer.dwBufferLength);
121
122     return res ? 0 : GetLastError();
123 }
124
125 #define ASYNCPROTOCOL_THIS(iface) DEFINE_THIS2(HttpProtocol, base, iface)
126
127 static HRESULT HttpProtocol_open_request(Protocol *prot, IUri *uri, DWORD request_flags,
128         HINTERNET internet_session, IInternetBindInfo *bind_info)
129 {
130     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
131     LPWSTR addl_header = NULL, post_cookie = NULL;
132     IServiceProvider *service_provider = NULL;
133     IHttpNegotiate2 *http_negotiate2 = NULL;
134     BSTR url, host, user, pass, path;
135     LPOLESTR accept_mimes[257];
136     const WCHAR **accept_types;
137     BYTE security_id[512];
138     DWORD len = 0, port;
139     ULONG num, error;
140     BOOL res, b;
141     HRESULT hres;
142
143     static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
144         {{'G','E','T',0},
145          {'P','O','S','T',0},
146          {'P','U','T',0}};
147
148     hres = IUri_GetPort(uri, &port);
149     if(FAILED(hres))
150         return hres;
151
152     hres = IUri_GetHost(uri, &host);
153     if(FAILED(hres))
154         return hres;
155
156     hres = IUri_GetUserName(uri, &user);
157     if(SUCCEEDED(hres)) {
158         hres = IUri_GetPassword(uri, &pass);
159
160         if(SUCCEEDED(hres)) {
161             This->base.connection = InternetConnectW(internet_session, host, port, user, pass,
162                     INTERNET_SERVICE_HTTP, This->https ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)&This->base);
163             SysFreeString(pass);
164         }
165         SysFreeString(user);
166     }
167     SysFreeString(host);
168     if(FAILED(hres))
169         return hres;
170     if(!This->base.connection) {
171         WARN("InternetConnect failed: %d\n", GetLastError());
172         return INET_E_CANNOT_CONNECT;
173     }
174
175     num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
176     hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ACCEPT_MIMES, accept_mimes, num, &num);
177     if(hres == INET_E_USE_DEFAULT_SETTING) {
178         static const WCHAR default_accept_mimeW[] = {'*','/','*',0};
179         static const WCHAR *default_accept_mimes[] = {default_accept_mimeW, NULL};
180
181         accept_types = default_accept_mimes;
182         num = 0;
183     }else if(hres == S_OK) {
184         accept_types = (const WCHAR**)accept_mimes;
185     }else {
186         WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
187         return INET_E_NO_VALID_MEDIA;
188     }
189     accept_mimes[num] = 0;
190
191     if(This->https)
192         request_flags |= INTERNET_FLAG_SECURE;
193
194     hres = IUri_GetPathAndQuery(uri, &path);
195     if(SUCCEEDED(hres)) {
196         This->base.request = HttpOpenRequestW(This->base.connection,
197                 This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM
198                     ? wszBindVerb[This->base.bind_info.dwBindVerb] : This->base.bind_info.szCustomVerb,
199                 path, NULL, NULL, accept_types, request_flags, (DWORD_PTR)&This->base);
200         SysFreeString(path);
201     }
202     while(num--)
203         CoTaskMemFree(accept_mimes[num]);
204     if(FAILED(hres))
205         return hres;
206     if (!This->base.request) {
207         WARN("HttpOpenRequest failed: %d\n", GetLastError());
208         return INET_E_RESOURCE_NOT_FOUND;
209     }
210
211     hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
212             (void **)&service_provider);
213     if (hres != S_OK) {
214         WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
215         return hres;
216     }
217
218     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
219             &IID_IHttpNegotiate, (void **)&This->http_negotiate);
220     if (hres != S_OK) {
221         WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
222         IServiceProvider_Release(service_provider);
223         return hres;
224     }
225
226     hres = IUri_GetAbsoluteUri(uri, &url);
227     if(FAILED(hres)) {
228         IServiceProvider_Release(service_provider);
229         return hres;
230     }
231
232     hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, url, wszHeaders,
233             0, &addl_header);
234     SysFreeString(url);
235     if(hres != S_OK) {
236         WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
237         IServiceProvider_Release(service_provider);
238         return hres;
239     }
240
241     if(addl_header) {
242         int len_addl_header = strlenW(addl_header);
243
244         This->full_header = heap_alloc(len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
245
246         lstrcpyW(This->full_header, addl_header);
247         lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
248         CoTaskMemFree(addl_header);
249     }else {
250         This->full_header = (LPWSTR)wszHeaders;
251     }
252
253     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
254             &IID_IHttpNegotiate2, (void **)&http_negotiate2);
255     IServiceProvider_Release(service_provider);
256     if(hres != S_OK) {
257         WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
258         /* No goto done as per native */
259     }else {
260         len = sizeof(security_id)/sizeof(security_id[0]);
261         hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
262         IHttpNegotiate2_Release(http_negotiate2);
263         if (hres != S_OK)
264             WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
265     }
266
267     /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
268
269     if(This->base.bind_info.dwBindVerb == BINDVERB_POST) {
270         num = 0;
271         hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_POST_COOKIE, &post_cookie, 1, &num);
272         if(hres == S_OK && num) {
273             if(!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
274                                    post_cookie, lstrlenW(post_cookie)))
275                 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n", GetLastError());
276             CoTaskMemFree(post_cookie);
277         }
278     }
279
280     b = TRUE;
281     res = InternetSetOptionW(This->base.request, INTERNET_OPTION_HTTP_DECODING, &b, sizeof(b));
282     if(!res)
283         WARN("InternetSetOption(INTERNET_OPTION_HTTP_DECODING) failed: %08x\n", GetLastError());
284
285     error = send_http_request(This);
286
287     if(error == ERROR_IO_PENDING || error == ERROR_SUCCESS)
288         return S_OK;
289
290     WARN("HttpSendRequest failed: %d\n", error);
291     return INET_E_DOWNLOAD_FAILURE;
292 }
293
294 static HRESULT HttpProtocol_end_request(Protocol *protocol)
295 {
296     BOOL res;
297
298     res = HttpEndRequestW(protocol->request, NULL, 0, 0);
299     if(!res && GetLastError() != ERROR_IO_PENDING) {
300         FIXME("HttpEndRequest failed: %u\n", GetLastError());
301         return E_FAIL;
302     }
303
304     return S_OK;
305 }
306
307 static HRESULT HttpProtocol_start_downloading(Protocol *prot)
308 {
309     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
310     LPWSTR content_type, content_length, ranges;
311     DWORD len = sizeof(DWORD);
312     DWORD status_code;
313     BOOL res;
314     HRESULT hres;
315
316     static const WCHAR wszDefaultContentType[] =
317         {'t','e','x','t','/','h','t','m','l',0};
318
319     if(!This->http_negotiate) {
320         WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
321         return S_OK;
322     }
323
324     res = HttpQueryInfoW(This->base.request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
325             &status_code, &len, NULL);
326     if(res) {
327         LPWSTR response_headers = query_http_info(This, HTTP_QUERY_RAW_HEADERS_CRLF);
328         if(response_headers) {
329             hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code, response_headers,
330                     NULL, NULL);
331             heap_free(response_headers);
332             if (hres != S_OK) {
333                 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
334                 return S_OK;
335             }
336         }
337     }else {
338         WARN("HttpQueryInfo failed: %d\n", GetLastError());
339     }
340
341     ranges = query_http_info(This, HTTP_QUERY_ACCEPT_RANGES);
342     if(ranges) {
343         IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_ACCEPTRANGES, NULL);
344         heap_free(ranges);
345     }
346
347     content_type = query_http_info(This, HTTP_QUERY_CONTENT_TYPE);
348     if(content_type) {
349         /* remove the charset, if present */
350         LPWSTR p = strchrW(content_type, ';');
351         if (p) *p = '\0';
352
353         IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
354                 (This->base.bindf & BINDF_FROMURLMON)
355                  ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
356                  content_type);
357         heap_free(content_type);
358     }else {
359         WARN("HttpQueryInfo failed: %d\n", GetLastError());
360         IInternetProtocolSink_ReportProgress(This->base.protocol_sink,
361                  (This->base.bindf & BINDF_FROMURLMON)
362                   ? BINDSTATUS_MIMETYPEAVAILABLE : BINDSTATUS_RAWMIMETYPE,
363                   wszDefaultContentType);
364     }
365
366     content_length = query_http_info(This, HTTP_QUERY_CONTENT_LENGTH);
367     if(content_length) {
368         This->base.content_length = atoiW(content_length);
369         heap_free(content_length);
370     }
371
372     return S_OK;
373 }
374
375 static void HttpProtocol_close_connection(Protocol *prot)
376 {
377     HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
378
379     if(This->http_negotiate) {
380         IHttpNegotiate_Release(This->http_negotiate);
381         This->http_negotiate = 0;
382     }
383
384     if(This->full_header) {
385         if(This->full_header != wszHeaders)
386             heap_free(This->full_header);
387         This->full_header = 0;
388     }
389 }
390
391 #undef ASYNCPROTOCOL_THIS
392
393 static const ProtocolVtbl AsyncProtocolVtbl = {
394     HttpProtocol_open_request,
395     HttpProtocol_end_request,
396     HttpProtocol_start_downloading,
397     HttpProtocol_close_connection
398 };
399
400 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
401 {
402     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
403
404     *ppv = NULL;
405     if(IsEqualGUID(&IID_IUnknown, riid)) {
406         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
407         *ppv = &This->IInternetProtocolEx_iface;
408     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
409         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
410         *ppv = &This->IInternetProtocolEx_iface;
411     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
412         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
413         *ppv = &This->IInternetProtocolEx_iface;
414     }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
415         TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv);
416         *ppv = &This->IInternetProtocolEx_iface;
417     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
418         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
419         *ppv = &This->IInternetPriority_iface;
420     }else if(IsEqualGUID(&IID_IWinInetInfo, riid)) {
421         TRACE("(%p)->(IID_IWinInetInfo %p)\n", This, ppv);
422         *ppv = &This->IWinInetHttpInfo_iface;
423     }else if(IsEqualGUID(&IID_IWinInetHttpInfo, riid)) {
424         TRACE("(%p)->(IID_IWinInetHttpInfo %p)\n", This, ppv);
425         *ppv = &This->IWinInetHttpInfo_iface;
426     }
427
428     if(*ppv) {
429         IInternetProtocol_AddRef(iface);
430         return S_OK;
431     }
432
433     WARN("not supported interface %s\n", debugstr_guid(riid));
434     return E_NOINTERFACE;
435 }
436
437 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocolEx *iface)
438 {
439     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
440     LONG ref = InterlockedIncrement(&This->ref);
441     TRACE("(%p) ref=%d\n", This, ref);
442     return ref;
443 }
444
445 static ULONG WINAPI HttpProtocol_Release(IInternetProtocolEx *iface)
446 {
447     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
448     LONG ref = InterlockedDecrement(&This->ref);
449
450     TRACE("(%p) ref=%d\n", This, ref);
451
452     if(!ref) {
453         protocol_close_connection(&This->base);
454         heap_free(This);
455
456         URLMON_UnlockModule();
457     }
458
459     return ref;
460 }
461
462 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl,
463         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
464         DWORD grfPI, HANDLE_PTR dwReserved)
465 {
466     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
467     IUri *uri;
468     HRESULT hres;
469
470     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
471             pOIBindInfo, grfPI, dwReserved);
472
473     hres = CreateUri(szUrl, 0, 0, &uri);
474     if(FAILED(hres))
475         return hres;
476
477     hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink,
478             pOIBindInfo, grfPI, (HANDLE*)dwReserved);
479
480     IUri_Release(uri);
481     return hres;
482 }
483
484 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData)
485 {
486     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
487
488     TRACE("(%p)->(%p)\n", This, pProtocolData);
489
490     return protocol_continue(&This->base, pProtocolData);
491 }
492
493 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason,
494         DWORD dwOptions)
495 {
496     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
497
498     TRACE("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
499
500     return protocol_abort(&This->base, hrReason);
501 }
502
503 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions)
504 {
505     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
506
507     TRACE("(%p)->(%08x)\n", This, dwOptions);
508
509     protocol_close_connection(&This->base);
510     return S_OK;
511 }
512
513 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocolEx *iface)
514 {
515     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
516     FIXME("(%p)\n", This);
517     return E_NOTIMPL;
518 }
519
520 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocolEx *iface)
521 {
522     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
523     FIXME("(%p)\n", This);
524     return E_NOTIMPL;
525 }
526
527 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocolEx *iface, void *pv,
528         ULONG cb, ULONG *pcbRead)
529 {
530     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
531
532     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
533
534     return protocol_read(&This->base, pv, cb, pcbRead);
535 }
536
537 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove,
538         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
539 {
540     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
541     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
542     return E_NOTIMPL;
543 }
544
545 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions)
546 {
547     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
548
549     TRACE("(%p)->(%08x)\n", This, dwOptions);
550
551     return protocol_lock_request(&This->base);
552 }
553
554 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocolEx *iface)
555 {
556     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
557
558     TRACE("(%p)\n", This);
559
560     return protocol_unlock_request(&This->base);
561 }
562
563 static HRESULT WINAPI HttpProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri,
564         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
565         DWORD grfPI, HANDLE *dwReserved)
566 {
567     HttpProtocol *This = impl_from_IInternetProtocolEx(iface);
568     DWORD scheme = 0;
569     HRESULT hres;
570
571     TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
572             pOIBindInfo, grfPI, dwReserved);
573
574     hres = IUri_GetScheme(pUri, &scheme);
575     if(FAILED(hres))
576         return hres;
577     if(scheme != (This->https ? URL_SCHEME_HTTPS : URL_SCHEME_HTTP))
578         return MK_E_SYNTAX;
579
580     return protocol_start(&This->base, (IInternetProtocol*)&This->IInternetProtocolEx_iface, pUri,
581                           pOIProtSink, pOIBindInfo);
582 }
583
584 static const IInternetProtocolExVtbl HttpProtocolVtbl = {
585     HttpProtocol_QueryInterface,
586     HttpProtocol_AddRef,
587     HttpProtocol_Release,
588     HttpProtocol_Start,
589     HttpProtocol_Continue,
590     HttpProtocol_Abort,
591     HttpProtocol_Terminate,
592     HttpProtocol_Suspend,
593     HttpProtocol_Resume,
594     HttpProtocol_Read,
595     HttpProtocol_Seek,
596     HttpProtocol_LockRequest,
597     HttpProtocol_UnlockRequest,
598     HttpProtocol_StartEx
599 };
600
601 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
602 {
603     HttpProtocol *This = impl_from_IInternetPriority(iface);
604     return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
605 }
606
607 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
608 {
609     HttpProtocol *This = impl_from_IInternetPriority(iface);
610     return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
611 }
612
613 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
614 {
615     HttpProtocol *This = impl_from_IInternetPriority(iface);
616     return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
617 }
618
619 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
620 {
621     HttpProtocol *This = impl_from_IInternetPriority(iface);
622
623     TRACE("(%p)->(%d)\n", This, nPriority);
624
625     This->base.priority = nPriority;
626     return S_OK;
627 }
628
629 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
630 {
631     HttpProtocol *This = impl_from_IInternetPriority(iface);
632
633     TRACE("(%p)->(%p)\n", This, pnPriority);
634
635     *pnPriority = This->base.priority;
636     return S_OK;
637 }
638
639 static const IInternetPriorityVtbl HttpPriorityVtbl = {
640     HttpPriority_QueryInterface,
641     HttpPriority_AddRef,
642     HttpPriority_Release,
643     HttpPriority_SetPriority,
644     HttpPriority_GetPriority
645 };
646
647 static HRESULT WINAPI HttpInfo_QueryInterface(IWinInetHttpInfo *iface, REFIID riid, void **ppv)
648 {
649     HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
650     return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
651 }
652
653 static ULONG WINAPI HttpInfo_AddRef(IWinInetHttpInfo *iface)
654 {
655     HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
656     return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
657 }
658
659 static ULONG WINAPI HttpInfo_Release(IWinInetHttpInfo *iface)
660 {
661     HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
662     return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
663 }
664
665 static HRESULT WINAPI HttpInfo_QueryOption(IWinInetHttpInfo *iface, DWORD dwOption,
666         void *pBuffer, DWORD *pcbBuffer)
667 {
668     HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
669     FIXME("(%p)->(%x %p %p)\n", This, dwOption, pBuffer, pcbBuffer);
670     return E_NOTIMPL;
671 }
672
673 static HRESULT WINAPI HttpInfo_QueryInfo(IWinInetHttpInfo *iface, DWORD dwOption,
674         void *pBuffer, DWORD *pcbBuffer, DWORD *pdwFlags, DWORD *pdwReserved)
675 {
676     HttpProtocol *This = impl_from_IWinInetHttpInfo(iface);
677     FIXME("(%p)->(%x %p %p %p %p)\n", This, dwOption, pBuffer, pcbBuffer, pdwFlags, pdwReserved);
678     return E_NOTIMPL;
679 }
680
681 static const IWinInetHttpInfoVtbl WinInetHttpInfoVtbl = {
682     HttpInfo_QueryInterface,
683     HttpInfo_AddRef,
684     HttpInfo_Release,
685     HttpInfo_QueryOption,
686     HttpInfo_QueryInfo
687 };
688
689 static HRESULT create_http_protocol(BOOL https, void **ppobj)
690 {
691     HttpProtocol *ret;
692
693     ret = heap_alloc_zero(sizeof(HttpProtocol));
694     if(!ret)
695         return E_OUTOFMEMORY;
696
697     ret->base.vtbl = &AsyncProtocolVtbl;
698     ret->IInternetProtocolEx_iface.lpVtbl = &HttpProtocolVtbl;
699     ret->IInternetPriority_iface.lpVtbl   = &HttpPriorityVtbl;
700     ret->IWinInetHttpInfo_iface.lpVtbl    = &WinInetHttpInfoVtbl;
701
702     ret->https = https;
703     ret->ref = 1;
704
705     *ppobj = &ret->IInternetProtocolEx_iface;
706
707     URLMON_LockModule();
708     return S_OK;
709 }
710
711 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
712 {
713     TRACE("(%p %p)\n", pUnkOuter, ppobj);
714
715     return create_http_protocol(FALSE, ppobj);
716 }
717
718 HRESULT HttpSProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
719 {
720     TRACE("(%p %p)\n", pUnkOuter, ppobj);
721
722     return create_http_protocol(TRUE, ppobj);
723 }