crypt32: Introduce function to encode an array of items as a set.
[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 <stdarg.h>
26
27 #define COBJMACROS
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "ole2.h"
33 #include "urlmon.h"
34 #include "wininet.h"
35 #include "urlmon_main.h"
36
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
41
42 /* Flags are needed for, among other things, return HRESULTs from the Read function
43  * to conform to native. For example, Read returns:
44  *
45  * 1. E_PENDING if called before the request has completed,
46  *        (flags = 0)
47  * 2. S_FALSE after all data has been read and S_OK has been reported,
48  *        (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
49  * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
50  *    this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
51  *        (flags = FLAG_REQUEST_COMPLETE)
52  *    but upon subsequent calls to Read no reporting will take place, yet
53  *    InternetQueryDataAvailable will still be called, and, on failure,
54  *    INET_E_DATA_NOT_AVAILABLE will still be returned.
55  *        (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
56  *
57  * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
58  * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
59  * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
60  * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
61  * if OnResponse does not return S_OK, Continue will not report data, and Read
62  * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
63  * data has been read.
64  */
65 #define FLAG_REQUEST_COMPLETE 0x1
66 #define FLAG_FIRST_CONTINUE_COMPLETE 0x2
67 #define FLAG_FIRST_DATA_REPORTED 0x4
68 #define FLAG_ALL_DATA_READ 0x8
69 #define FLAG_LAST_DATA_REPORTED 0x10
70 #define FLAG_RESULT_REPORTED 0x20
71
72 typedef struct {
73     const IInternetProtocolVtbl *lpInternetProtocolVtbl;
74     const IInternetPriorityVtbl *lpInternetPriorityVtbl;
75
76     DWORD flags, grfBINDF;
77     IInternetProtocolSink *protocol_sink;
78     IHttpNegotiate *http_negotiate;
79     HINTERNET internet, connect, request;
80     HANDLE lock;
81     ULONG current_position, content_length, available_bytes;
82     LONG priority;
83
84     LONG ref;
85 } HttpProtocol;
86
87 /*
88  * Helpers
89  */
90
91 static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
92 {
93     if (!(This->flags & FLAG_RESULT_REPORTED) &&
94         This->protocol_sink)
95     {
96         This->flags |= FLAG_RESULT_REPORTED;
97         IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
98     }
99 }
100
101 static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
102 {
103     DWORD bscf;
104     if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
105         This->protocol_sink)
106     {
107         if (This->flags & FLAG_FIRST_DATA_REPORTED)
108         {
109             bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
110         }
111         else
112         {
113             This->flags |= FLAG_FIRST_DATA_REPORTED;
114             bscf = BSCF_FIRSTDATANOTIFICATION;
115         }
116         if (This->flags & FLAG_ALL_DATA_READ &&
117             !(This->flags & FLAG_LAST_DATA_REPORTED))
118         {
119             This->flags |= FLAG_LAST_DATA_REPORTED;
120             bscf |= BSCF_LASTDATANOTIFICATION;
121         }
122         IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
123                                          This->current_position+This->available_bytes,
124                                          This->content_length);
125     }
126 }
127
128 static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
129 {
130     if (!(This->flags & FLAG_ALL_DATA_READ))
131         This->flags |= FLAG_ALL_DATA_READ;
132     HTTPPROTOCOL_ReportData(This);
133     HTTPPROTOCOL_ReportResult(This, S_OK);
134 }
135
136 static void HTTPPROTOCOL_Close(HttpProtocol *This)
137 {
138     if (This->protocol_sink)
139     {
140         IInternetProtocolSink_Release(This->protocol_sink);
141         This->protocol_sink = 0;
142     }
143     if (This->http_negotiate)
144     {
145         IHttpNegotiate_Release(This->http_negotiate);
146         This->http_negotiate = 0;
147     }
148     if (This->request)
149     {
150         InternetCloseHandle(This->request);
151         This->request = 0;
152     }
153     if (This->connect)
154     {
155         InternetCloseHandle(This->connect);
156         This->connect = 0;
157     }
158     if (This->internet)
159     {
160         InternetCloseHandle(This->internet);
161         This->internet = 0;
162     }
163     This->flags = 0;
164 }
165
166 static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
167     HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
168     LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
169 {
170     HttpProtocol *This = (HttpProtocol *)dwContext;
171     PROTOCOLDATA data;
172     ULONG ulStatusCode;
173
174     switch (dwInternetStatus)
175     {
176     case INTERNET_STATUS_RESOLVING_NAME:
177         ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
178         break;
179     case INTERNET_STATUS_CONNECTING_TO_SERVER:
180         ulStatusCode = BINDSTATUS_CONNECTING;
181         break;
182     case INTERNET_STATUS_SENDING_REQUEST:
183         ulStatusCode = BINDSTATUS_SENDINGREQUEST;
184         break;
185     case INTERNET_STATUS_REQUEST_COMPLETE:
186         This->flags |= FLAG_REQUEST_COMPLETE;
187         /* PROTOCOLDATA same as native */
188         memset(&data, 0, sizeof(data));
189         data.dwState = 0xf1000000;
190         if (This->flags & FLAG_FIRST_CONTINUE_COMPLETE)
191             data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
192         else
193             data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
194         if (This->grfBINDF & BINDF_FROMURLMON)
195             IInternetProtocolSink_Switch(This->protocol_sink, &data);
196         else
197             IInternetProtocol_Continue((IInternetProtocol *)This, &data);
198         return;
199     default:
200         WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
201         return;
202     }
203
204     IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
205 }
206
207 static inline LPWSTR strndupW(LPWSTR string, int len)
208 {
209     LPWSTR ret = NULL;
210     if (string &&
211         (ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))) != NULL)
212     {
213         memcpy(ret, string, len*sizeof(WCHAR));
214         ret[len] = 0;
215     }
216     return ret;
217 }
218
219 /*
220  * Interface implementations
221  */
222
223 #define PROTOCOL(x)  ((IInternetProtocol*)  &(x)->lpInternetProtocolVtbl)
224 #define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)
225
226 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
227
228 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
229 {
230     HttpProtocol *This = PROTOCOL_THIS(iface);
231
232     *ppv = NULL;
233     if(IsEqualGUID(&IID_IUnknown, riid)) {
234         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
235         *ppv = PROTOCOL(This);
236     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
237         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
238         *ppv = PROTOCOL(This);
239     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
240         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
241         *ppv = PROTOCOL(This);
242     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
243         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
244         *ppv = PRIORITY(This);
245     }
246
247     if(*ppv) {
248         IInternetProtocol_AddRef(iface);
249         return S_OK;
250     }
251
252     WARN("not supported interface %s\n", debugstr_guid(riid));
253     return E_NOINTERFACE;
254 }
255
256 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
257 {
258     HttpProtocol *This = PROTOCOL_THIS(iface);
259     LONG ref = InterlockedIncrement(&This->ref);
260     TRACE("(%p) ref=%d\n", This, ref);
261     return ref;
262 }
263
264 static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
265 {
266     HttpProtocol *This = PROTOCOL_THIS(iface);
267     LONG ref = InterlockedDecrement(&This->ref);
268
269     TRACE("(%p) ref=%d\n", This, ref);
270
271     if(!ref) {
272         HTTPPROTOCOL_Close(This);
273         HeapFree(GetProcessHeap(), 0, This);
274
275         URLMON_UnlockModule();
276     }
277
278     return ref;
279 }
280
281 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
282         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
283         DWORD grfPI, DWORD dwReserved)
284 {
285     HttpProtocol *This = PROTOCOL_THIS(iface);
286     URL_COMPONENTSW url;
287     BINDINFO bindinfo;
288     DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
289     ULONG num = 0;
290     IServiceProvider *service_provider = 0;
291     IHttpNegotiate2 *http_negotiate2 = 0;
292     LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
293         full_header = 0, post_cookie = 0, optional = 0;
294     BYTE security_id[512];
295     LPOLESTR user_agent, accept_mimes[257];
296     HRESULT hres;
297
298     static const WCHAR wszHttp[] = {'h','t','t','p',':'};
299     static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
300                                        ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
301     static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
302         {{'G','E','T',0},
303          {'P','O','S','T',0},
304          {'P','U','T',0}};
305
306     TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
307             pOIBindInfo, grfPI, dwReserved);
308
309     memset(&bindinfo, 0, sizeof(bindinfo));
310     bindinfo.cbSize = sizeof(BINDINFO);
311     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &This->grfBINDF, &bindinfo);
312     if (hres != S_OK)
313     {
314         WARN("GetBindInfo failed: %08x\n", hres);
315         goto done;
316     }
317
318     if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
319         || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
320     {
321         hres = MK_E_SYNTAX;
322         goto done;
323     }
324
325     memset(&url, 0, sizeof(url));
326     url.dwStructSize = sizeof(url);
327     url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
328         url.dwPasswordLength = 1;
329     if (!InternetCrackUrlW(szUrl, 0, 0, &url))
330     {
331         hres = MK_E_SYNTAX;
332         goto done;
333     }
334     host = strndupW(url.lpszHostName, url.dwHostNameLength);
335     path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
336     user = strndupW(url.lpszUserName, url.dwUserNameLength);
337     pass = strndupW(url.lpszPassword, url.dwPasswordLength);
338     if (!url.nPort)
339         url.nPort = INTERNET_DEFAULT_HTTP_PORT;
340
341     if(!(This->grfBINDF & BINDF_FROMURLMON))
342         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
343
344     hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
345                                            1, &num);
346     if (hres != S_OK || !num)
347     {
348         CHAR null_char = 0;
349         LPSTR user_agenta = NULL;
350         len = 0;
351         if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
352         {
353             WARN("ObtainUserAgentString failed: %08x\n", hres);
354         }
355         else if (!(user_agenta = HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
356         {
357             WARN("Out of memory\n");
358         }
359         else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
360         {
361             WARN("ObtainUserAgentString failed: %08x\n", hres);
362         }
363         else
364         {
365             if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
366                 WARN("Out of memory\n");
367             else
368                 MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len*sizeof(WCHAR));
369         }
370         HeapFree(GetProcessHeap(), 0, user_agenta);
371     }
372
373     This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
374     if (!This->internet)
375     {
376         WARN("InternetOpen failed: %d\n", GetLastError());
377         hres = INET_E_NO_SESSION;
378         goto done;
379     }
380
381     IInternetProtocolSink_AddRef(pOIProtSink);
382     This->protocol_sink = pOIProtSink;
383
384     /* Native does not check for success of next call, so we won't either */
385     InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
386
387     This->connect = InternetConnectW(This->internet, host, url.nPort, user,
388                                      pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
389     if (!This->connect)
390     {
391         WARN("InternetConnect failed: %d\n", GetLastError());
392         hres = INET_E_CANNOT_CONNECT;
393         goto done;
394     }
395
396     num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
397     hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
398                                            accept_mimes,
399                                            num, &num);
400     if (hres != S_OK)
401     {
402         WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
403         hres = INET_E_NO_VALID_MEDIA;
404         goto done;
405     }
406     accept_mimes[num] = 0;
407
408     if (This->grfBINDF & BINDF_NOWRITECACHE)
409         request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
410     This->request = HttpOpenRequestW(This->connect, bindinfo.dwBindVerb < BINDVERB_CUSTOM ?
411                                      wszBindVerb[bindinfo.dwBindVerb] : bindinfo.szCustomVerb,
412                                      path, NULL, NULL, (LPCWSTR *)accept_mimes,
413                                      request_flags, (DWORD)This);
414     if (!This->request)
415     {
416         WARN("HttpOpenRequest failed: %d\n", GetLastError());
417         hres = INET_E_RESOURCE_NOT_FOUND;
418         goto done;
419     }
420
421     hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
422                                                 (void **)&service_provider);
423     if (hres != S_OK)
424     {
425         WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
426         goto done;
427     }
428
429     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
430                                          &IID_IHttpNegotiate, (void **)&This->http_negotiate);
431     if (hres != S_OK)
432     {
433         WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
434         goto done;
435     }
436
437     hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
438                                                0, &addl_header);
439     if (hres != S_OK)
440     {
441         WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
442         goto done;
443     }
444     else if (addl_header == NULL)
445     {
446         full_header = (LPWSTR)wszHeaders;
447     }
448     else
449     {
450         full_header = HeapAlloc(GetProcessHeap(), 0,
451                                 (lstrlenW(addl_header)+sizeof(wszHeaders))*sizeof(WCHAR));
452         if (!full_header)
453         {
454             WARN("Out of memory\n");
455             hres = E_OUTOFMEMORY;
456             goto done;
457         }
458         lstrcpyW(full_header, addl_header);
459         lstrcpyW(&full_header[lstrlenW(addl_header)], wszHeaders);
460     }
461
462     hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
463                                          &IID_IHttpNegotiate2, (void **)&http_negotiate2);
464     if (hres != S_OK)
465     {
466         WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
467         /* No goto done as per native */
468     }
469     else
470     {
471         len = sizeof(security_id)/sizeof(security_id[0]);
472         hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
473         if (hres != S_OK)
474         {
475             WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
476             /* No goto done as per native */
477         }
478     }
479
480     /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
481
482     if (bindinfo.dwBindVerb == BINDVERB_POST)
483     {
484         num = 0;
485         hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_POST_COOKIE, &post_cookie,
486                                                1, &num);
487         if (hres == S_OK && num &&
488             !InternetSetOptionW(This->request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
489                                 post_cookie, lstrlenW(post_cookie)))
490         {
491             WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
492                  GetLastError());
493         }
494     }
495
496     if (bindinfo.dwBindVerb != BINDVERB_GET)
497     {
498         /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
499         if (bindinfo.stgmedData.tymed != TYMED_HGLOBAL)
500             WARN("Expected bindinfo.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
501                  bindinfo.stgmedData.tymed);
502         else
503             optional = (LPWSTR)bindinfo.stgmedData.hGlobal;
504     }
505     if (!HttpSendRequestW(This->request, full_header, lstrlenW(full_header),
506                           optional,
507                           optional ? bindinfo.cbstgmedData : 0) &&
508         GetLastError() != ERROR_IO_PENDING)
509     {
510         WARN("HttpSendRequest failed: %d\n", GetLastError());
511         hres = INET_E_DOWNLOAD_FAILURE;
512         goto done;
513     }
514
515     hres = S_OK;
516 done:
517     if (hres != S_OK)
518     {
519         IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
520         HTTPPROTOCOL_Close(This);
521     }
522
523     CoTaskMemFree(post_cookie);
524     if (full_header != wszHeaders)
525         HeapFree(GetProcessHeap(), 0, full_header);
526     CoTaskMemFree(addl_header);
527     if (http_negotiate2)
528         IHttpNegotiate2_Release(http_negotiate2);
529     if (service_provider)
530         IServiceProvider_Release(service_provider);
531
532     while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
533            accept_mimes[num])
534         CoTaskMemFree(accept_mimes[num++]);
535     CoTaskMemFree(user_agent);
536
537     HeapFree(GetProcessHeap(), 0, pass);
538     HeapFree(GetProcessHeap(), 0, user);
539     HeapFree(GetProcessHeap(), 0, path);
540     HeapFree(GetProcessHeap(), 0, host);
541
542     ReleaseBindInfo(&bindinfo);
543
544     return hres;
545 }
546
547 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
548 {
549     HttpProtocol *This = PROTOCOL_THIS(iface);
550     DWORD len = sizeof(DWORD), status_code;
551     LPWSTR response_headers = 0, content_type = 0, content_length = 0;
552
553     static const WCHAR wszDefaultContentType[] =
554         {'t','e','x','t','/','h','t','m','l',0};
555
556     TRACE("(%p)->(%p)\n", This, pProtocolData);
557
558     if (!pProtocolData)
559     {
560         WARN("Expected pProtocolData to be non-NULL\n");
561         goto done;
562     }
563     else if (!This->request)
564     {
565         WARN("Expected request to be non-NULL\n");
566         goto done;
567     }
568     else if (!This->http_negotiate)
569     {
570         WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
571         goto done;
572     }
573     else if (!This->protocol_sink)
574     {
575         WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
576         goto done;
577     }
578
579     if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
580     {
581         if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
582                             &status_code, &len, NULL))
583         {
584             WARN("HttpQueryInfo failed: %d\n", GetLastError());
585         }
586         else
587         {
588             len = 0;
589             if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
590                                  NULL) &&
591                  GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
592                 !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
593                 !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
594                                 NULL))
595             {
596                 WARN("HttpQueryInfo failed: %d\n", GetLastError());
597             }
598             else
599             {
600                 HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
601                                                          response_headers, NULL, NULL);
602                 if (hres != S_OK)
603                 {
604                     WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
605                     goto done;
606                 }
607             }
608         }
609
610         len = 0;
611         if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
612              GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
613             !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
614             !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
615         {
616             WARN("HttpQueryInfo failed: %d\n", GetLastError());
617             IInternetProtocolSink_ReportProgress(This->protocol_sink,
618                                                  (This->grfBINDF & BINDF_FROMURLMON) ?
619                                                  BINDSTATUS_MIMETYPEAVAILABLE :
620                                                  BINDSTATUS_RAWMIMETYPE,
621                                                  wszDefaultContentType);
622         }
623         else
624         {
625             IInternetProtocolSink_ReportProgress(This->protocol_sink,
626                                                  (This->grfBINDF & BINDF_FROMURLMON) ?
627                                                  BINDSTATUS_MIMETYPEAVAILABLE :
628                                                  BINDSTATUS_RAWMIMETYPE,
629                                                  content_type);
630         }
631
632         len = 0;
633         if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
634              GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
635             !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
636             !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
637         {
638             WARN("HttpQueryInfo failed: %d\n", GetLastError());
639             This->content_length = 0;
640         }
641         else
642         {
643             This->content_length = atoiW(content_length);
644         }
645
646         This->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
647     }
648
649     if (pProtocolData->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
650     {
651         if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
652         {
653             if (GetLastError() == ERROR_IO_PENDING)
654             {
655                 This->flags &= ~FLAG_REQUEST_COMPLETE;
656             }
657             else
658             {
659                 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
660                 HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
661             }
662         }
663         else
664         {
665             HTTPPROTOCOL_ReportData(This);
666         }
667     }
668
669 done:
670     HeapFree(GetProcessHeap(), 0, response_headers);
671     HeapFree(GetProcessHeap(), 0, content_type);
672     HeapFree(GetProcessHeap(), 0, content_length);
673
674     /* Returns S_OK on native */
675     return S_OK;
676 }
677
678 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
679         DWORD dwOptions)
680 {
681     HttpProtocol *This = PROTOCOL_THIS(iface);
682     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
683     return E_NOTIMPL;
684 }
685
686 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
687 {
688     HttpProtocol *This = PROTOCOL_THIS(iface);
689
690     TRACE("(%p)->(%08x)\n", This, dwOptions);
691     HTTPPROTOCOL_Close(This);
692
693     return S_OK;
694 }
695
696 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
697 {
698     HttpProtocol *This = PROTOCOL_THIS(iface);
699     FIXME("(%p)\n", This);
700     return E_NOTIMPL;
701 }
702
703 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
704 {
705     HttpProtocol *This = PROTOCOL_THIS(iface);
706     FIXME("(%p)\n", This);
707     return E_NOTIMPL;
708 }
709
710 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
711         ULONG cb, ULONG *pcbRead)
712 {
713     HttpProtocol *This = PROTOCOL_THIS(iface);
714     ULONG read = 0, len = 0;
715     HRESULT hres = S_FALSE;
716
717     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
718
719     if (!(This->flags & FLAG_REQUEST_COMPLETE))
720     {
721         hres = E_PENDING;
722     }
723     else while (!(This->flags & FLAG_ALL_DATA_READ) &&
724                 read < cb)
725     {
726         if (This->available_bytes == 0)
727         {
728             if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
729             {
730                 if (GetLastError() == ERROR_IO_PENDING)
731                 {
732                     This->flags &= ~FLAG_REQUEST_COMPLETE;
733                     hres = E_PENDING;
734                 }
735                 else
736                 {
737                     WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
738                     hres = INET_E_DATA_NOT_AVAILABLE;
739                     HTTPPROTOCOL_ReportResult(This, hres);
740                 }
741                 goto done;
742             }
743             else if (This->available_bytes == 0)
744             {
745                 HTTPPROTOCOL_AllDataRead(This);
746             }
747         }
748         else
749         {
750             if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
751                                   This->available_bytes > cb-read ?
752                                   cb-read : This->available_bytes, &len))
753             {
754                 WARN("InternetReadFile failed: %d\n", GetLastError());
755                 hres = INET_E_DOWNLOAD_FAILURE;
756                 HTTPPROTOCOL_ReportResult(This, hres);
757                 goto done;
758             }
759             else if (len == 0)
760             {
761                 HTTPPROTOCOL_AllDataRead(This);
762             }
763             else
764             {
765                 read += len;
766                 This->current_position += len;
767                 This->available_bytes -= len;
768             }
769         }
770     }
771
772     /* Per MSDN this should be if (read == cb), but native returns S_OK
773      * if any bytes were read, so we will too */
774     if (read)
775         hres = S_OK;
776
777 done:
778     if (pcbRead)
779         *pcbRead = read;
780
781     return hres;
782 }
783
784 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
785         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
786 {
787     HttpProtocol *This = PROTOCOL_THIS(iface);
788     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
789     return E_NOTIMPL;
790 }
791
792 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
793 {
794     HttpProtocol *This = PROTOCOL_THIS(iface);
795
796     TRACE("(%p)->(%08x)\n", This, dwOptions);
797
798     if (!InternetLockRequestFile(This->request, &This->lock))
799         WARN("InternetLockRequest failed: %d\n", GetLastError());
800
801     return S_OK;
802 }
803
804 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
805 {
806     HttpProtocol *This = PROTOCOL_THIS(iface);
807
808     TRACE("(%p)\n", This);
809
810     if (This->lock)
811     {
812         if (!InternetUnlockRequestFile(This->lock))
813             WARN("InternetUnlockRequest failed: %d\n", GetLastError());
814         This->lock = 0;
815     }
816
817     return S_OK;
818 }
819
820 #undef PROTOCOL_THIS
821
822 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
823
824 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
825 {
826     HttpProtocol *This = PRIORITY_THIS(iface);
827     return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
828 }
829
830 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
831 {
832     HttpProtocol *This = PRIORITY_THIS(iface);
833     return IInternetProtocol_AddRef(PROTOCOL(This));
834 }
835
836 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
837 {
838     HttpProtocol *This = PRIORITY_THIS(iface);
839     return IInternetProtocol_Release(PROTOCOL(This));
840 }
841
842 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
843 {
844     HttpProtocol *This = PRIORITY_THIS(iface);
845
846     TRACE("(%p)->(%d)\n", This, nPriority);
847
848     This->priority = nPriority;
849     return S_OK;
850 }
851
852 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
853 {
854     HttpProtocol *This = PRIORITY_THIS(iface);
855
856     TRACE("(%p)->(%p)\n", This, pnPriority);
857
858     *pnPriority = This->priority;
859     return S_OK;
860 }
861
862 #undef PRIORITY_THIS
863
864 static const IInternetPriorityVtbl HttpPriorityVtbl = {
865     HttpPriority_QueryInterface,
866     HttpPriority_AddRef,
867     HttpPriority_Release,
868     HttpPriority_SetPriority,
869     HttpPriority_GetPriority
870 };
871
872 static const IInternetProtocolVtbl HttpProtocolVtbl = {
873     HttpProtocol_QueryInterface,
874     HttpProtocol_AddRef,
875     HttpProtocol_Release,
876     HttpProtocol_Start,
877     HttpProtocol_Continue,
878     HttpProtocol_Abort,
879     HttpProtocol_Terminate,
880     HttpProtocol_Suspend,
881     HttpProtocol_Resume,
882     HttpProtocol_Read,
883     HttpProtocol_Seek,
884     HttpProtocol_LockRequest,
885     HttpProtocol_UnlockRequest
886 };
887
888 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
889 {
890     HttpProtocol *ret;
891
892     TRACE("(%p %p)\n", pUnkOuter, ppobj);
893
894     URLMON_LockModule();
895
896     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(HttpProtocol));
897
898     ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
899     ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
900     ret->flags = ret->grfBINDF = 0;
901     ret->protocol_sink = 0;
902     ret->http_negotiate = 0;
903     ret->internet = ret->connect = ret->request = 0;
904     ret->lock = 0;
905     ret->current_position = ret->content_length = ret->available_bytes = 0;
906     ret->priority = 0;
907     ret->ref = 1;
908
909     *ppobj = PROTOCOL(ret);
910     
911     return S_OK;
912 }