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