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