urlmon: Add support for non-GET requests (e.g., POST) in HttpProtocol.
[wine/hacks.git] / dlls / urlmon / http.c
blob2afe267ca34542845dceae32090a7f75b0792ad5
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
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "ole2.h"
33 #include "urlmon.h"
34 #include "wininet.h"
35 #include "urlmon_main.h"
37 #include "wine/debug.h"
38 #include "wine/unicode.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
42 /* Flags are needed for, among other things, return HRESULTs from the Read function
43 * to conform to native. For example, Read returns:
45 * 1. E_PENDING if called before the request has completed,
46 * (flags = 0)
47 * 2. S_FALSE after all data has been read and S_OK has been reported,
48 * (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
49 * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
50 * this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
51 * (flags = FLAG_REQUEST_COMPLETE)
52 * but upon subsequent calls to Read no reporting will take place, yet
53 * InternetQueryDataAvailable will still be called, and, on failure,
54 * INET_E_DATA_NOT_AVAILABLE will still be returned.
55 * (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
57 * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
58 * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
59 * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
60 * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
61 * if OnResponse does not return S_OK, Continue will not report data, and Read
62 * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
63 * data has been read.
65 #define FLAG_REQUEST_COMPLETE 0x1
66 #define FLAG_FIRST_CONTINUE_COMPLETE 0x2
67 #define FLAG_FIRST_DATA_REPORTED 0x4
68 #define FLAG_ALL_DATA_READ 0x8
69 #define FLAG_LAST_DATA_REPORTED 0x10
70 #define FLAG_RESULT_REPORTED 0x20
72 typedef struct {
73 const IInternetProtocolVtbl *lpInternetProtocolVtbl;
74 const IInternetPriorityVtbl *lpInternetPriorityVtbl;
76 DWORD flags, grfBINDF;
77 IInternetProtocolSink *protocol_sink;
78 IHttpNegotiate *http_negotiate;
79 HINTERNET internet, connect, request;
80 HANDLE lock;
81 ULONG current_position, content_length, available_bytes;
82 LONG priority;
84 LONG ref;
85 } HttpProtocol;
88 * Helpers
91 static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
93 if (!(This->flags & FLAG_RESULT_REPORTED) &&
94 This->protocol_sink)
96 This->flags |= FLAG_RESULT_REPORTED;
97 IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
101 static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
103 DWORD bscf;
104 if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
105 This->protocol_sink)
107 if (This->flags & FLAG_FIRST_DATA_REPORTED)
109 bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
111 else
113 This->flags |= FLAG_FIRST_DATA_REPORTED;
114 bscf = BSCF_FIRSTDATANOTIFICATION;
116 if (This->flags & FLAG_ALL_DATA_READ &&
117 !(This->flags & FLAG_LAST_DATA_REPORTED))
119 This->flags |= FLAG_LAST_DATA_REPORTED;
120 bscf |= BSCF_LASTDATANOTIFICATION;
122 IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
123 This->current_position+This->available_bytes,
124 This->content_length);
128 static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
130 if (!(This->flags & FLAG_ALL_DATA_READ))
131 This->flags |= FLAG_ALL_DATA_READ;
132 HTTPPROTOCOL_ReportData(This);
133 HTTPPROTOCOL_ReportResult(This, S_OK);
136 static void HTTPPROTOCOL_Close(HttpProtocol *This)
138 if (This->protocol_sink)
140 IInternetProtocolSink_Release(This->protocol_sink);
141 This->protocol_sink = 0;
143 if (This->http_negotiate)
145 IHttpNegotiate_Release(This->http_negotiate);
146 This->http_negotiate = 0;
148 if (This->request)
150 InternetCloseHandle(This->request);
151 This->request = 0;
153 if (This->connect)
155 InternetCloseHandle(This->connect);
156 This->connect = 0;
158 if (This->internet)
160 InternetCloseHandle(This->internet);
161 This->internet = 0;
163 This->flags = 0;
166 static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
167 HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
168 LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
170 HttpProtocol *This = (HttpProtocol *)dwContext;
171 PROTOCOLDATA data;
172 ULONG ulStatusCode;
174 switch (dwInternetStatus)
176 case INTERNET_STATUS_RESOLVING_NAME:
177 ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
178 break;
179 case INTERNET_STATUS_CONNECTING_TO_SERVER:
180 ulStatusCode = BINDSTATUS_CONNECTING;
181 break;
182 case INTERNET_STATUS_SENDING_REQUEST:
183 ulStatusCode = BINDSTATUS_SENDINGREQUEST;
184 break;
185 case INTERNET_STATUS_REQUEST_COMPLETE:
186 This->flags |= FLAG_REQUEST_COMPLETE;
187 /* PROTOCOLDATA same as native */
188 memset(&data, 0, sizeof(data));
189 data.dwState = 0xf1000000;
190 if (This->flags & FLAG_FIRST_CONTINUE_COMPLETE)
191 data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
192 else
193 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
194 if (This->grfBINDF & BINDF_FROMURLMON)
195 IInternetProtocolSink_Switch(This->protocol_sink, &data);
196 else
197 IInternetProtocol_Continue((IInternetProtocol *)This, &data);
198 return;
199 default:
200 WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
201 return;
204 IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
207 static inline LPWSTR strndupW(LPWSTR string, int len)
209 LPWSTR ret = NULL;
210 if (string &&
211 (ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR))) != NULL)
213 memcpy(ret, string, len*sizeof(WCHAR));
214 ret[len] = 0;
216 return ret;
220 * Interface implementations
223 #define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
224 #define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
226 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
228 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
230 HttpProtocol *This = PROTOCOL_THIS(iface);
232 *ppv = NULL;
233 if(IsEqualGUID(&IID_IUnknown, riid)) {
234 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
235 *ppv = PROTOCOL(This);
236 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
237 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
238 *ppv = PROTOCOL(This);
239 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
240 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
241 *ppv = PROTOCOL(This);
242 }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
243 TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
244 *ppv = PRIORITY(This);
247 if(*ppv) {
248 IInternetProtocol_AddRef(iface);
249 return S_OK;
252 WARN("not supported interface %s\n", debugstr_guid(riid));
253 return E_NOINTERFACE;
256 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
258 HttpProtocol *This = PROTOCOL_THIS(iface);
259 LONG ref = InterlockedIncrement(&This->ref);
260 TRACE("(%p) ref=%d\n", This, ref);
261 return ref;
264 static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
266 HttpProtocol *This = PROTOCOL_THIS(iface);
267 LONG ref = InterlockedDecrement(&This->ref);
269 TRACE("(%p) ref=%d\n", This, ref);
271 if(!ref) {
272 HTTPPROTOCOL_Close(This);
273 HeapFree(GetProcessHeap(), 0, This);
275 URLMON_UnlockModule();
278 return ref;
281 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
282 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
283 DWORD grfPI, DWORD dwReserved)
285 HttpProtocol *This = PROTOCOL_THIS(iface);
286 URL_COMPONENTSW url;
287 BINDINFO bindinfo;
288 DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
289 ULONG num = 0;
290 IServiceProvider *service_provider = 0;
291 IHttpNegotiate2 *http_negotiate2 = 0;
292 LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
293 full_header = 0, post_cookie = 0, optional = 0;
294 BYTE security_id[512];
295 LPOLESTR user_agent, accept_mimes[257];
296 HRESULT hres;
298 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
299 static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
300 ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
301 static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
302 {{'G','E','T',0},
303 {'P','O','S','T',0},
304 {'P','U','T',0}};
306 TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
307 pOIBindInfo, grfPI, dwReserved);
309 memset(&bindinfo, 0, sizeof(bindinfo));
310 bindinfo.cbSize = sizeof(BINDINFO);
311 hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &This->grfBINDF, &bindinfo);
312 if (hres != S_OK)
314 WARN("GetBindInfo failed: %08x\n", hres);
315 goto done;
318 if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
319 || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
321 hres = MK_E_SYNTAX;
322 goto done;
325 memset(&url, 0, sizeof(url));
326 url.dwStructSize = sizeof(url);
327 url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
328 url.dwPasswordLength = 1;
329 if (!InternetCrackUrlW(szUrl, 0, 0, &url))
331 hres = MK_E_SYNTAX;
332 goto done;
334 host = strndupW(url.lpszHostName, url.dwHostNameLength);
335 path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
336 user = strndupW(url.lpszUserName, url.dwUserNameLength);
337 pass = strndupW(url.lpszPassword, url.dwPasswordLength);
338 if (!url.nPort)
339 url.nPort = INTERNET_DEFAULT_HTTP_PORT;
341 if(!(This->grfBINDF & BINDF_FROMURLMON))
342 IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
344 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
345 1, &num);
346 if (hres != S_OK || !num)
348 CHAR null_char = 0;
349 LPSTR user_agenta = NULL;
350 len = 0;
351 if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
353 WARN("ObtainUserAgentString failed: %08x\n", hres);
355 else if (!(user_agenta = HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
357 WARN("Out of memory\n");
359 else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
361 WARN("ObtainUserAgentString failed: %08x\n", hres);
363 else
365 if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
366 WARN("Out of memory\n");
367 else
368 MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len*sizeof(WCHAR));
370 HeapFree(GetProcessHeap(), 0, user_agenta);
373 This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
374 if (!This->internet)
376 WARN("InternetOpen failed: %d\n", GetLastError());
377 hres = INET_E_NO_SESSION;
378 goto done;
381 IInternetProtocolSink_AddRef(pOIProtSink);
382 This->protocol_sink = pOIProtSink;
384 /* Native does not check for success of next call, so we won't either */
385 InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
387 This->connect = InternetConnectW(This->internet, host, url.nPort, user,
388 pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
389 if (!This->connect)
391 WARN("InternetConnect failed: %d\n", GetLastError());
392 hres = INET_E_CANNOT_CONNECT;
393 goto done;
396 num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
397 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
398 accept_mimes,
399 num, &num);
400 if (hres != S_OK)
402 WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
403 hres = INET_E_NO_VALID_MEDIA;
404 goto done;
406 accept_mimes[num] = 0;
408 if (This->grfBINDF & BINDF_NOWRITECACHE)
409 request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
410 This->request = HttpOpenRequestW(This->connect, bindinfo.dwBindVerb < BINDVERB_CUSTOM ?
411 wszBindVerb[bindinfo.dwBindVerb] : bindinfo.szCustomVerb,
412 path, NULL, NULL, (LPCWSTR *)accept_mimes,
413 request_flags, (DWORD)This);
414 if (!This->request)
416 WARN("HttpOpenRequest failed: %d\n", GetLastError());
417 hres = INET_E_RESOURCE_NOT_FOUND;
418 goto done;
421 hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
422 (void **)&service_provider);
423 if (hres != S_OK)
425 WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
426 goto done;
429 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
430 &IID_IHttpNegotiate, (void **)&This->http_negotiate);
431 if (hres != S_OK)
433 WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
434 goto done;
437 hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
438 0, &addl_header);
439 if (hres != S_OK)
441 WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
442 goto done;
444 else if (addl_header == NULL)
446 full_header = (LPWSTR)wszHeaders;
448 else
450 full_header = HeapAlloc(GetProcessHeap(), 0,
451 (lstrlenW(addl_header)+sizeof(wszHeaders))*sizeof(WCHAR));
452 if (!full_header)
454 WARN("Out of memory\n");
455 hres = E_OUTOFMEMORY;
456 goto done;
458 lstrcpyW(full_header, addl_header);
459 lstrcpyW(&full_header[lstrlenW(addl_header)], wszHeaders);
462 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
463 &IID_IHttpNegotiate2, (void **)&http_negotiate2);
464 if (hres != S_OK)
466 WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
467 /* No goto done as per native */
469 else
471 len = sizeof(security_id)/sizeof(security_id[0]);
472 hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
473 if (hres != S_OK)
475 WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
476 /* No goto done as per native */
480 /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
482 if (bindinfo.dwBindVerb == BINDVERB_POST)
484 num = 0;
485 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_POST_COOKIE, &post_cookie,
486 1, &num);
487 if (hres == S_OK && num &&
488 !InternetSetOptionW(This->request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
489 post_cookie, lstrlenW(post_cookie)))
491 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
492 GetLastError());
496 if (bindinfo.dwBindVerb != BINDVERB_GET)
498 /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
499 if (bindinfo.stgmedData.tymed != TYMED_HGLOBAL)
500 WARN("Expected bindinfo.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
501 bindinfo.stgmedData.tymed);
502 else
503 optional = (LPWSTR)bindinfo.stgmedData.hGlobal;
505 if (!HttpSendRequestW(This->request, full_header, lstrlenW(full_header),
506 optional,
507 optional ? bindinfo.cbstgmedData : 0) &&
508 GetLastError() != ERROR_IO_PENDING)
510 WARN("HttpSendRequest failed: %d\n", GetLastError());
511 hres = INET_E_DOWNLOAD_FAILURE;
512 goto done;
515 hres = S_OK;
516 done:
517 if (hres != S_OK)
519 IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
520 HTTPPROTOCOL_Close(This);
523 CoTaskMemFree(post_cookie);
524 if (full_header != wszHeaders)
525 HeapFree(GetProcessHeap(), 0, full_header);
526 CoTaskMemFree(addl_header);
527 if (http_negotiate2)
528 IHttpNegotiate2_Release(http_negotiate2);
529 if (service_provider)
530 IServiceProvider_Release(service_provider);
532 while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
533 accept_mimes[num])
534 CoTaskMemFree(accept_mimes[num++]);
535 CoTaskMemFree(user_agent);
537 HeapFree(GetProcessHeap(), 0, pass);
538 HeapFree(GetProcessHeap(), 0, user);
539 HeapFree(GetProcessHeap(), 0, path);
540 HeapFree(GetProcessHeap(), 0, host);
542 ReleaseBindInfo(&bindinfo);
544 return hres;
547 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
549 HttpProtocol *This = PROTOCOL_THIS(iface);
550 DWORD len = sizeof(DWORD), status_code;
551 LPWSTR response_headers = 0, content_type = 0, content_length = 0;
553 static const WCHAR wszDefaultContentType[] =
554 {'t','e','x','t','/','h','t','m','l',0};
556 TRACE("(%p)->(%p)\n", This, pProtocolData);
558 if (!pProtocolData)
560 WARN("Expected pProtocolData to be non-NULL\n");
561 goto done;
563 else if (!This->request)
565 WARN("Expected request to be non-NULL\n");
566 goto done;
568 else if (!This->http_negotiate)
570 WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
571 goto done;
573 else if (!This->protocol_sink)
575 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
576 goto done;
579 if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
581 if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
582 &status_code, &len, NULL))
584 WARN("HttpQueryInfo failed: %d\n", GetLastError());
586 else
588 len = 0;
589 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
590 NULL) &&
591 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
592 !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
593 !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
594 NULL))
596 WARN("HttpQueryInfo failed: %d\n", GetLastError());
598 else
600 HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
601 response_headers, NULL, NULL);
602 if (hres != S_OK)
604 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
605 goto done;
610 len = 0;
611 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
612 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
613 !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
614 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
616 WARN("HttpQueryInfo failed: %d\n", GetLastError());
617 IInternetProtocolSink_ReportProgress(This->protocol_sink,
618 (This->grfBINDF & BINDF_FROMURLMON) ?
619 BINDSTATUS_MIMETYPEAVAILABLE :
620 BINDSTATUS_RAWMIMETYPE,
621 wszDefaultContentType);
623 else
625 IInternetProtocolSink_ReportProgress(This->protocol_sink,
626 (This->grfBINDF & BINDF_FROMURLMON) ?
627 BINDSTATUS_MIMETYPEAVAILABLE :
628 BINDSTATUS_RAWMIMETYPE,
629 content_type);
632 len = 0;
633 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
634 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
635 !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
636 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
638 WARN("HttpQueryInfo failed: %d\n", GetLastError());
639 This->content_length = 0;
641 else
643 This->content_length = atoiW(content_length);
646 This->flags |= FLAG_FIRST_CONTINUE_COMPLETE;
649 if (pProtocolData->pData >= (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
651 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
653 if (GetLastError() == ERROR_IO_PENDING)
655 This->flags &= ~FLAG_REQUEST_COMPLETE;
657 else
659 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
660 HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
663 else
665 HTTPPROTOCOL_ReportData(This);
669 done:
670 HeapFree(GetProcessHeap(), 0, response_headers);
671 HeapFree(GetProcessHeap(), 0, content_type);
672 HeapFree(GetProcessHeap(), 0, content_length);
674 /* Returns S_OK on native */
675 return S_OK;
678 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
679 DWORD dwOptions)
681 HttpProtocol *This = PROTOCOL_THIS(iface);
682 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
683 return E_NOTIMPL;
686 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
688 HttpProtocol *This = PROTOCOL_THIS(iface);
690 TRACE("(%p)->(%08x)\n", This, dwOptions);
691 HTTPPROTOCOL_Close(This);
693 return S_OK;
696 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
698 HttpProtocol *This = PROTOCOL_THIS(iface);
699 FIXME("(%p)\n", This);
700 return E_NOTIMPL;
703 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
705 HttpProtocol *This = PROTOCOL_THIS(iface);
706 FIXME("(%p)\n", This);
707 return E_NOTIMPL;
710 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
711 ULONG cb, ULONG *pcbRead)
713 HttpProtocol *This = PROTOCOL_THIS(iface);
714 ULONG read = 0, len = 0;
715 HRESULT hres = S_FALSE;
717 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
719 if (!(This->flags & FLAG_REQUEST_COMPLETE))
721 hres = E_PENDING;
723 else while (!(This->flags & FLAG_ALL_DATA_READ) &&
724 read < cb)
726 if (This->available_bytes == 0)
728 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
730 if (GetLastError() == ERROR_IO_PENDING)
732 This->flags &= ~FLAG_REQUEST_COMPLETE;
733 hres = E_PENDING;
735 else
737 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
738 hres = INET_E_DATA_NOT_AVAILABLE;
739 HTTPPROTOCOL_ReportResult(This, hres);
741 goto done;
743 else if (This->available_bytes == 0)
745 HTTPPROTOCOL_AllDataRead(This);
748 else
750 if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
751 This->available_bytes > cb-read ?
752 cb-read : This->available_bytes, &len))
754 WARN("InternetReadFile failed: %d\n", GetLastError());
755 hres = INET_E_DOWNLOAD_FAILURE;
756 HTTPPROTOCOL_ReportResult(This, hres);
757 goto done;
759 else if (len == 0)
761 HTTPPROTOCOL_AllDataRead(This);
763 else
765 read += len;
766 This->current_position += len;
767 This->available_bytes -= len;
772 /* Per MSDN this should be if (read == cb), but native returns S_OK
773 * if any bytes were read, so we will too */
774 if (read)
775 hres = S_OK;
777 done:
778 if (pcbRead)
779 *pcbRead = read;
781 return hres;
784 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
785 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
787 HttpProtocol *This = PROTOCOL_THIS(iface);
788 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
789 return E_NOTIMPL;
792 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
794 HttpProtocol *This = PROTOCOL_THIS(iface);
796 TRACE("(%p)->(%08x)\n", This, dwOptions);
798 if (!InternetLockRequestFile(This->request, &This->lock))
799 WARN("InternetLockRequest failed: %d\n", GetLastError());
801 return S_OK;
804 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
806 HttpProtocol *This = PROTOCOL_THIS(iface);
808 TRACE("(%p)\n", This);
810 if (This->lock)
812 if (!InternetUnlockRequestFile(This->lock))
813 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
814 This->lock = 0;
817 return S_OK;
820 #undef PROTOCOL_THIS
822 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
824 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
826 HttpProtocol *This = PRIORITY_THIS(iface);
827 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
830 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
832 HttpProtocol *This = PRIORITY_THIS(iface);
833 return IInternetProtocol_AddRef(PROTOCOL(This));
836 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
838 HttpProtocol *This = PRIORITY_THIS(iface);
839 return IInternetProtocol_Release(PROTOCOL(This));
842 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
844 HttpProtocol *This = PRIORITY_THIS(iface);
846 TRACE("(%p)->(%d)\n", This, nPriority);
848 This->priority = nPriority;
849 return S_OK;
852 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
854 HttpProtocol *This = PRIORITY_THIS(iface);
856 TRACE("(%p)->(%p)\n", This, pnPriority);
858 *pnPriority = This->priority;
859 return S_OK;
862 #undef PRIORITY_THIS
864 static const IInternetPriorityVtbl HttpPriorityVtbl = {
865 HttpPriority_QueryInterface,
866 HttpPriority_AddRef,
867 HttpPriority_Release,
868 HttpPriority_SetPriority,
869 HttpPriority_GetPriority
872 static const IInternetProtocolVtbl HttpProtocolVtbl = {
873 HttpProtocol_QueryInterface,
874 HttpProtocol_AddRef,
875 HttpProtocol_Release,
876 HttpProtocol_Start,
877 HttpProtocol_Continue,
878 HttpProtocol_Abort,
879 HttpProtocol_Terminate,
880 HttpProtocol_Suspend,
881 HttpProtocol_Resume,
882 HttpProtocol_Read,
883 HttpProtocol_Seek,
884 HttpProtocol_LockRequest,
885 HttpProtocol_UnlockRequest
888 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
890 HttpProtocol *ret;
892 TRACE("(%p %p)\n", pUnkOuter, ppobj);
894 URLMON_LockModule();
896 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(HttpProtocol));
898 ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
899 ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
900 ret->flags = ret->grfBINDF = 0;
901 ret->protocol_sink = 0;
902 ret->http_negotiate = 0;
903 ret->internet = ret->connect = ret->request = 0;
904 ret->lock = 0;
905 ret->current_position = ret->content_length = ret->available_bytes = 0;
906 ret->priority = 0;
907 ret->ref = 1;
909 *ppobj = PROTOCOL(ret);
911 return S_OK;