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