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