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