push 4d5485f9b89f417d46b39b93e8d940437007f325
[wine/hacks.git] / dlls / urlmon / http.c
blob2d1929db27333faf5338246ad280fd77536403b6
1 /*
2 * Copyright 2005 Jacek Caban
3 * Copyright 2007 Misha Koshelev
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.
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.
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
21 * TODO:
22 * - Handle redirects as native.
25 #include <stdarg.h>
27 #define COBJMACROS
28 #define NONAMELESSUNION
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"
38 #include "wine/debug.h"
39 #include "wine/unicode.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
43 /* Flags are needed for, among other things, return HRESULTs from the Read function
44 * to conform to native. For example, Read returns:
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)
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.
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
73 typedef struct {
74 const IInternetProtocolVtbl *lpInternetProtocolVtbl;
75 const IInternetPriorityVtbl *lpInternetPriorityVtbl;
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;
87 LONG ref;
88 } HttpProtocol;
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};
95 * Helpers
98 static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
100 if (!(This->flags & FLAG_RESULT_REPORTED) &&
101 This->protocol_sink)
103 This->flags |= FLAG_RESULT_REPORTED;
104 IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
108 static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
110 DWORD bscf;
111 if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
112 This->protocol_sink)
114 if (This->flags & FLAG_FIRST_DATA_REPORTED)
116 bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
118 else
120 This->flags |= FLAG_FIRST_DATA_REPORTED;
121 bscf = BSCF_FIRSTDATANOTIFICATION;
123 if (This->flags & FLAG_ALL_DATA_READ &&
124 !(This->flags & FLAG_LAST_DATA_REPORTED))
126 This->flags |= FLAG_LAST_DATA_REPORTED;
127 bscf |= BSCF_LASTDATANOTIFICATION;
129 IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
130 This->current_position+This->available_bytes,
131 This->content_length);
135 static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
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);
143 static void HTTPPROTOCOL_Close(HttpProtocol *This)
145 if (This->protocol_sink)
147 IInternetProtocolSink_Release(This->protocol_sink);
148 This->protocol_sink = 0;
150 if (This->http_negotiate)
152 IHttpNegotiate_Release(This->http_negotiate);
153 This->http_negotiate = 0;
155 if (This->request)
157 InternetCloseHandle(This->request);
158 This->request = 0;
160 if (This->connect)
162 InternetCloseHandle(This->connect);
163 This->connect = 0;
165 if (This->internet)
167 InternetCloseHandle(This->internet);
168 This->internet = 0;
170 if (This->full_header)
172 if (This->full_header != wszHeaders)
173 HeapFree(GetProcessHeap(), 0, This->full_header);
174 This->full_header = 0;
176 if (This->bind_info.cbSize)
178 ReleaseBindInfo(&This->bind_info);
179 memset(&This->bind_info, 0, sizeof(This->bind_info));
181 This->flags = 0;
184 static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
185 HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
186 LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
188 HttpProtocol *This = (HttpProtocol *)dwContext;
189 PROTOCOLDATA data;
190 ULONG ulStatusCode;
192 switch (dwInternetStatus)
194 case INTERNET_STATUS_RESOLVING_NAME:
195 ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
196 break;
197 case INTERNET_STATUS_CONNECTING_TO_SERVER:
198 ulStatusCode = BINDSTATUS_CONNECTING;
199 break;
200 case INTERNET_STATUS_SENDING_REQUEST:
201 ulStatusCode = BINDSTATUS_SENDINGREQUEST;
202 break;
203 case INTERNET_STATUS_REQUEST_COMPLETE:
204 This->flags |= FLAG_REQUEST_COMPLETE;
205 /* PROTOCOLDATA same as native */
206 memset(&data, 0, sizeof(data));
207 data.dwState = 0xf1000000;
208 if (This->flags & FLAG_FIRST_CONTINUE_COMPLETE)
209 data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
210 else
211 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
212 if (This->grfBINDF & BINDF_FROMURLMON)
213 IInternetProtocolSink_Switch(This->protocol_sink, &data);
214 else
215 IInternetProtocol_Continue((IInternetProtocol *)This, &data);
216 return;
217 default:
218 WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
219 return;
222 IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
225 static inline LPWSTR strndupW(LPWSTR string, int len)
227 LPWSTR ret = NULL;
228 if (string &&
229 (ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))) != NULL)
231 memcpy(ret, string, len*sizeof(WCHAR));
232 ret[len] = 0;
234 return ret;
238 * Interface implementations
241 #define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
242 #define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
244 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
246 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
248 HttpProtocol *This = PROTOCOL_THIS(iface);
250 *ppv = NULL;
251 if(IsEqualGUID(&IID_IUnknown, riid)) {
252 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
253 *ppv = PROTOCOL(This);
254 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
255 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
256 *ppv = PROTOCOL(This);
257 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
258 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
259 *ppv = PROTOCOL(This);
260 }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
261 TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
262 *ppv = PRIORITY(This);
265 if(*ppv) {
266 IInternetProtocol_AddRef(iface);
267 return S_OK;
270 WARN("not supported interface %s\n", debugstr_guid(riid));
271 return E_NOINTERFACE;
274 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
276 HttpProtocol *This = PROTOCOL_THIS(iface);
277 LONG ref = InterlockedIncrement(&This->ref);
278 TRACE("(%p) ref=%d\n", This, ref);
279 return ref;
282 static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
284 HttpProtocol *This = PROTOCOL_THIS(iface);
285 LONG ref = InterlockedDecrement(&This->ref);
287 TRACE("(%p) ref=%d\n", This, ref);
289 if(!ref) {
290 HTTPPROTOCOL_Close(This);
291 HeapFree(GetProcessHeap(), 0, This);
293 URLMON_UnlockModule();
296 return ref;
299 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
300 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
301 DWORD grfPI, DWORD dwReserved)
303 HttpProtocol *This = PROTOCOL_THIS(iface);
304 URL_COMPONENTSW url;
305 DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
306 ULONG num = 0;
307 IServiceProvider *service_provider = 0;
308 IHttpNegotiate2 *http_negotiate2 = 0;
309 LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
310 post_cookie = 0, optional = 0;
311 BYTE security_id[512];
312 LPOLESTR user_agent, accept_mimes[257];
313 HRESULT hres;
315 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
316 static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
317 {{'G','E','T',0},
318 {'P','O','S','T',0},
319 {'P','U','T',0}};
321 TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
322 pOIBindInfo, grfPI, dwReserved);
324 memset(&This->bind_info, 0, sizeof(This->bind_info));
325 This->bind_info.cbSize = sizeof(BINDINFO);
326 hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &This->grfBINDF, &This->bind_info);
327 if (hres != S_OK)
329 WARN("GetBindInfo failed: %08x\n", hres);
330 goto done;
333 if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
334 || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
336 hres = MK_E_SYNTAX;
337 goto done;
340 memset(&url, 0, sizeof(url));
341 url.dwStructSize = sizeof(url);
342 url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
343 url.dwPasswordLength = 1;
344 if (!InternetCrackUrlW(szUrl, 0, 0, &url))
346 hres = MK_E_SYNTAX;
347 goto done;
349 host = strndupW(url.lpszHostName, url.dwHostNameLength);
350 path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
351 user = strndupW(url.lpszUserName, url.dwUserNameLength);
352 pass = strndupW(url.lpszPassword, url.dwPasswordLength);
353 if (!url.nPort)
354 url.nPort = INTERNET_DEFAULT_HTTP_PORT;
356 if(!(This->grfBINDF & BINDF_FROMURLMON))
357 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
359 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
360 1, &num);
361 if (hres != S_OK || !num)
363 CHAR null_char = 0;
364 LPSTR user_agenta = NULL;
365 len = 0;
366 if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
368 WARN("ObtainUserAgentString failed: %08x\n", hres);
370 else if (!(user_agenta = HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
372 WARN("Out of memory\n");
374 else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
376 WARN("ObtainUserAgentString failed: %08x\n", hres);
378 else
380 if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
381 WARN("Out of memory\n");
382 else
383 MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len*sizeof(WCHAR));
385 HeapFree(GetProcessHeap(), 0, user_agenta);
388 This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
389 if (!This->internet)
391 WARN("InternetOpen failed: %d\n", GetLastError());
392 hres = INET_E_NO_SESSION;
393 goto done;
396 IInternetProtocolSink_AddRef(pOIProtSink);
397 This->protocol_sink = pOIProtSink;
399 /* Native does not check for success of next call, so we won't either */
400 InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
402 This->connect = InternetConnectW(This->internet, host, url.nPort, user,
403 pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
404 if (!This->connect)
406 WARN("InternetConnect failed: %d\n", GetLastError());
407 hres = INET_E_CANNOT_CONNECT;
408 goto done;
411 num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
412 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
413 accept_mimes,
414 num, &num);
415 if (hres != S_OK)
417 WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
418 hres = INET_E_NO_VALID_MEDIA;
419 goto done;
421 accept_mimes[num] = 0;
423 if (This->grfBINDF & BINDF_NOWRITECACHE)
424 request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
425 This->request = HttpOpenRequestW(This->connect, This->bind_info.dwBindVerb < BINDVERB_CUSTOM ?
426 wszBindVerb[This->bind_info.dwBindVerb] :
427 This->bind_info.szCustomVerb,
428 path, NULL, NULL, (LPCWSTR *)accept_mimes,
429 request_flags, (DWORD)This);
430 if (!This->request)
432 WARN("HttpOpenRequest failed: %d\n", GetLastError());
433 hres = INET_E_RESOURCE_NOT_FOUND;
434 goto done;
437 hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
438 (void **)&service_provider);
439 if (hres != S_OK)
441 WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
442 goto done;
445 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
446 &IID_IHttpNegotiate, (void **)&This->http_negotiate);
447 if (hres != S_OK)
449 WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
450 goto done;
453 hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
454 0, &addl_header);
455 if (hres != S_OK)
457 WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
458 goto done;
460 else if (addl_header == NULL)
462 This->full_header = (LPWSTR)wszHeaders;
464 else
466 int len_addl_header = lstrlenW(addl_header);
467 This->full_header = HeapAlloc(GetProcessHeap(), 0,
468 len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
469 if (!This->full_header)
471 WARN("Out of memory\n");
472 hres = E_OUTOFMEMORY;
473 goto done;
475 lstrcpyW(This->full_header, addl_header);
476 lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
479 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
480 &IID_IHttpNegotiate2, (void **)&http_negotiate2);
481 if (hres != S_OK)
483 WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
484 /* No goto done as per native */
486 else
488 len = sizeof(security_id)/sizeof(security_id[0]);
489 hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
490 if (hres != S_OK)
492 WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
493 /* No goto done as per native */
497 /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
499 if (This->bind_info.dwBindVerb == BINDVERB_POST)
501 num = 0;
502 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_POST_COOKIE, &post_cookie,
503 1, &num);
504 if (hres == S_OK && num &&
505 !InternetSetOptionW(This->request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
506 post_cookie, lstrlenW(post_cookie)))
508 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
509 GetLastError());
513 if (This->bind_info.dwBindVerb != BINDVERB_GET)
515 /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
516 if (This->bind_info.stgmedData.tymed != TYMED_HGLOBAL)
517 WARN("Expected This->bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
518 This->bind_info.stgmedData.tymed);
519 else
520 optional = (LPWSTR)This->bind_info.stgmedData.u.hGlobal;
522 if (!HttpSendRequestW(This->request, This->full_header, lstrlenW(This->full_header),
523 optional,
524 optional ? This->bind_info.cbstgmedData : 0) &&
525 GetLastError() != ERROR_IO_PENDING)
527 WARN("HttpSendRequest failed: %d\n", GetLastError());
528 hres = INET_E_DOWNLOAD_FAILURE;
529 goto done;
532 hres = S_OK;
533 done:
534 if (hres != S_OK)
536 IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
537 HTTPPROTOCOL_Close(This);
540 CoTaskMemFree(post_cookie);
541 CoTaskMemFree(addl_header);
542 if (http_negotiate2)
543 IHttpNegotiate2_Release(http_negotiate2);
544 if (service_provider)
545 IServiceProvider_Release(service_provider);
547 while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
548 accept_mimes[num])
549 CoTaskMemFree(accept_mimes[num++]);
550 CoTaskMemFree(user_agent);
552 HeapFree(GetProcessHeap(), 0, pass);
553 HeapFree(GetProcessHeap(), 0, user);
554 HeapFree(GetProcessHeap(), 0, path);
555 HeapFree(GetProcessHeap(), 0, host);
557 return hres;
560 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
562 HttpProtocol *This = PROTOCOL_THIS(iface);
563 DWORD len = sizeof(DWORD), status_code;
564 LPWSTR response_headers = 0, content_type = 0, content_length = 0;
566 static const WCHAR wszDefaultContentType[] =
567 {'t','e','x','t','/','h','t','m','l',0};
569 TRACE("(%p)->(%p)\n", This, pProtocolData);
571 if (!pProtocolData)
573 WARN("Expected pProtocolData to be non-NULL\n");
574 return S_OK;
576 else if (!This->request)
578 WARN("Expected request to be non-NULL\n");
579 return S_OK;
581 else if (!This->http_negotiate)
583 WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
584 return S_OK;
586 else if (!This->protocol_sink)
588 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
589 return S_OK;
592 if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
594 if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
595 &status_code, &len, NULL))
597 WARN("HttpQueryInfo failed: %d\n", GetLastError());
599 else
601 len = 0;
602 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
603 NULL) &&
604 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
605 !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
606 !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
607 NULL))
609 WARN("HttpQueryInfo failed: %d\n", GetLastError());
611 else
613 HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
614 response_headers, NULL, NULL);
615 if (hres != S_OK)
617 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
618 goto done;
623 len = 0;
624 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
625 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
626 !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
627 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
629 WARN("HttpQueryInfo failed: %d\n", GetLastError());
630 IInternetProtocolSink_ReportProgress(This->protocol_sink,
631 (This->grfBINDF & BINDF_FROMURLMON) ?
632 BINDSTATUS_MIMETYPEAVAILABLE :
633 BINDSTATUS_RAWMIMETYPE,
634 wszDefaultContentType);
636 else
638 IInternetProtocolSink_ReportProgress(This->protocol_sink,
639 (This->grfBINDF & BINDF_FROMURLMON) ?
640 BINDSTATUS_MIMETYPEAVAILABLE :
641 BINDSTATUS_RAWMIMETYPE,
642 content_type);
645 len = 0;
646 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
647 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
648 !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
649 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
651 WARN("HttpQueryInfo failed: %d\n", GetLastError());
652 This->content_length = 0;
654 else
656 This->content_length = atoiW(content_length);
659 This->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
662 if (pProtocolData->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
664 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
665 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
666 * after the status callback is called */
667 This->flags &= ~FLAG_REQUEST_COMPLETE;
668 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
670 if (GetLastError() != ERROR_IO_PENDING)
672 This->flags |= FLAG_REQUEST_COMPLETE;
673 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
674 HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
677 else
679 This->flags |= FLAG_REQUEST_COMPLETE;
680 HTTPPROTOCOL_ReportData(This);
684 done:
685 HeapFree(GetProcessHeap(), 0, response_headers);
686 HeapFree(GetProcessHeap(), 0, content_type);
687 HeapFree(GetProcessHeap(), 0, content_length);
689 /* Returns S_OK on native */
690 return S_OK;
693 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
694 DWORD dwOptions)
696 HttpProtocol *This = PROTOCOL_THIS(iface);
697 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
698 return E_NOTIMPL;
701 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
703 HttpProtocol *This = PROTOCOL_THIS(iface);
705 TRACE("(%p)->(%08x)\n", This, dwOptions);
706 HTTPPROTOCOL_Close(This);
708 return S_OK;
711 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
713 HttpProtocol *This = PROTOCOL_THIS(iface);
714 FIXME("(%p)\n", This);
715 return E_NOTIMPL;
718 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
720 HttpProtocol *This = PROTOCOL_THIS(iface);
721 FIXME("(%p)\n", This);
722 return E_NOTIMPL;
725 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
726 ULONG cb, ULONG *pcbRead)
728 HttpProtocol *This = PROTOCOL_THIS(iface);
729 ULONG read = 0, len = 0;
730 HRESULT hres = S_FALSE;
732 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
734 if (!(This->flags & FLAG_REQUEST_COMPLETE))
736 hres = E_PENDING;
738 else while (!(This->flags & FLAG_ALL_DATA_READ) &&
739 read < cb)
741 if (This->available_bytes == 0)
743 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
744 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
745 * after the status callback is called */
746 This->flags &= ~FLAG_REQUEST_COMPLETE;
747 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
749 if (GetLastError() == ERROR_IO_PENDING)
751 hres = E_PENDING;
753 else
755 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
756 hres = INET_E_DATA_NOT_AVAILABLE;
757 HTTPPROTOCOL_ReportResult(This, hres);
759 goto done;
761 else if (This->available_bytes == 0)
763 HTTPPROTOCOL_AllDataRead(This);
766 else
768 if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
769 This->available_bytes > cb-read ?
770 cb-read : This->available_bytes, &len))
772 WARN("InternetReadFile failed: %d\n", GetLastError());
773 hres = INET_E_DOWNLOAD_FAILURE;
774 HTTPPROTOCOL_ReportResult(This, hres);
775 goto done;
777 else if (len == 0)
779 HTTPPROTOCOL_AllDataRead(This);
781 else
783 read += len;
784 This->current_position += len;
785 This->available_bytes -= len;
790 /* Per MSDN this should be if (read == cb), but native returns S_OK
791 * if any bytes were read, so we will too */
792 if (read)
793 hres = S_OK;
795 done:
796 if (pcbRead)
797 *pcbRead = read;
799 if (hres != E_PENDING)
800 This->flags |= FLAG_REQUEST_COMPLETE;
802 return hres;
805 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
806 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
808 HttpProtocol *This = PROTOCOL_THIS(iface);
809 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
810 return E_NOTIMPL;
813 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
815 HttpProtocol *This = PROTOCOL_THIS(iface);
817 TRACE("(%p)->(%08x)\n", This, dwOptions);
819 if (!InternetLockRequestFile(This->request, &This->lock))
820 WARN("InternetLockRequest failed: %d\n", GetLastError());
822 return S_OK;
825 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
827 HttpProtocol *This = PROTOCOL_THIS(iface);
829 TRACE("(%p)\n", This);
831 if (This->lock)
833 if (!InternetUnlockRequestFile(This->lock))
834 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
835 This->lock = 0;
838 return S_OK;
841 #undef PROTOCOL_THIS
843 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
845 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
847 HttpProtocol *This = PRIORITY_THIS(iface);
848 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
851 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
853 HttpProtocol *This = PRIORITY_THIS(iface);
854 return IInternetProtocol_AddRef(PROTOCOL(This));
857 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
859 HttpProtocol *This = PRIORITY_THIS(iface);
860 return IInternetProtocol_Release(PROTOCOL(This));
863 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
865 HttpProtocol *This = PRIORITY_THIS(iface);
867 TRACE("(%p)->(%d)\n", This, nPriority);
869 This->priority = nPriority;
870 return S_OK;
873 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
875 HttpProtocol *This = PRIORITY_THIS(iface);
877 TRACE("(%p)->(%p)\n", This, pnPriority);
879 *pnPriority = This->priority;
880 return S_OK;
883 #undef PRIORITY_THIS
885 static const IInternetPriorityVtbl HttpPriorityVtbl = {
886 HttpPriority_QueryInterface,
887 HttpPriority_AddRef,
888 HttpPriority_Release,
889 HttpPriority_SetPriority,
890 HttpPriority_GetPriority
893 static const IInternetProtocolVtbl HttpProtocolVtbl = {
894 HttpProtocol_QueryInterface,
895 HttpProtocol_AddRef,
896 HttpProtocol_Release,
897 HttpProtocol_Start,
898 HttpProtocol_Continue,
899 HttpProtocol_Abort,
900 HttpProtocol_Terminate,
901 HttpProtocol_Suspend,
902 HttpProtocol_Resume,
903 HttpProtocol_Read,
904 HttpProtocol_Seek,
905 HttpProtocol_LockRequest,
906 HttpProtocol_UnlockRequest
909 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
911 HttpProtocol *ret;
913 TRACE("(%p %p)\n", pUnkOuter, ppobj);
915 URLMON_LockModule();
917 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(HttpProtocol));
919 ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
920 ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
921 ret->flags = ret->grfBINDF = 0;
922 memset(&ret->bind_info, 0, sizeof(ret->bind_info));
923 ret->protocol_sink = 0;
924 ret->http_negotiate = 0;
925 ret->internet = ret->connect = ret->request = 0;
926 ret->full_header = 0;
927 ret->lock = 0;
928 ret->current_position = ret->content_length = ret->available_bytes = 0;
929 ret->priority = 0;
930 ret->ref = 1;
932 *ppobj = PROTOCOL(ret);
934 return S_OK;