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