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