msi: Add tests for adding properties in a transform.
[wine.git] / dlls / urlmon / http.c
blobc501f059baa520bafa11ebda1267c1865f91d6b7
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.
23 * - Add support for non-GET requests (e.g., POST).
26 #include <stdarg.h>
28 #define COBJMACROS
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_DATA_REPORTED 0x2
68 #define FLAG_ALL_DATA_READ 0x4
69 #define FLAG_LAST_DATA_REPORTED 0x8
70 #define FLAG_RESULT_REPORTED 0x10
72 typedef struct {
73 const IInternetProtocolVtbl *lpInternetProtocolVtbl;
74 const IInternetPriorityVtbl *lpInternetPriorityVtbl;
76 DWORD flags;
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)
149 InternetCloseHandle(This->request);
150 CloseHandle(This->connect);
151 CloseHandle(This->internet);
152 This->request = This->connect = This->internet = 0;
153 This->flags = 0;
156 static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
157 HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
158 LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
160 HttpProtocol *This = (HttpProtocol *)dwContext;
161 PROTOCOLDATA data;
162 ULONG ulStatusCode;
164 switch (dwInternetStatus)
166 case INTERNET_STATUS_RESOLVING_NAME:
167 ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
168 break;
169 case INTERNET_STATUS_CONNECTING_TO_SERVER:
170 ulStatusCode = BINDSTATUS_CONNECTING;
171 break;
172 case INTERNET_STATUS_SENDING_REQUEST:
173 ulStatusCode = BINDSTATUS_SENDINGREQUEST;
174 break;
175 case INTERNET_STATUS_REQUEST_COMPLETE:
176 This->flags |= FLAG_REQUEST_COMPLETE;
177 /* PROTOCOLDATA same as native */
178 memset(&data, 0, sizeof(data));
179 data.dwState = 0xf1000000;
180 data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
181 IInternetProtocolSink_Switch(This->protocol_sink, &data);
182 return;
183 default:
184 WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
185 return;
188 IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
191 static inline LPWSTR strndupW(LPWSTR string, int len)
193 LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
194 if (ret)
196 memcpy(ret, string, len*sizeof(WCHAR));
197 ret[len] = 0;
199 return ret;
203 * Interface implementations
206 #define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
207 #define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
209 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
211 static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
213 HttpProtocol *This = PROTOCOL_THIS(iface);
215 *ppv = NULL;
216 if(IsEqualGUID(&IID_IUnknown, riid)) {
217 TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
218 *ppv = PROTOCOL(This);
219 }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
220 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
221 *ppv = PROTOCOL(This);
222 }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
223 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
224 *ppv = PROTOCOL(This);
225 }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
226 TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
227 *ppv = PRIORITY(This);
230 if(*ppv) {
231 IInternetProtocol_AddRef(iface);
232 return S_OK;
235 WARN("not supported interface %s\n", debugstr_guid(riid));
236 return E_NOINTERFACE;
239 static ULONG WINAPI HttpProtocol_AddRef(IInternetProtocol *iface)
241 HttpProtocol *This = PROTOCOL_THIS(iface);
242 LONG ref = InterlockedIncrement(&This->ref);
243 TRACE("(%p) ref=%d\n", This, ref);
244 return ref;
247 static ULONG WINAPI HttpProtocol_Release(IInternetProtocol *iface)
249 HttpProtocol *This = PROTOCOL_THIS(iface);
250 LONG ref = InterlockedDecrement(&This->ref);
252 TRACE("(%p) ref=%d\n", This, ref);
254 if(!ref) {
255 HTTPPROTOCOL_Close(This);
256 HeapFree(GetProcessHeap(), 0, This);
258 URLMON_UnlockModule();
261 return ref;
264 static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
265 IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
266 DWORD grfPI, DWORD dwReserved)
268 HttpProtocol *This = PROTOCOL_THIS(iface);
269 URL_COMPONENTSW url;
270 BINDINFO bindinfo;
271 DWORD grfBINDF = 0, len = 0;
272 ULONG num = 0;
273 IServiceProvider *service_provider = 0;
274 IHttpNegotiate2 *http_negotiate2 = 0;
275 LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0;
276 BYTE security_id[512];
277 LPOLESTR user_agent, accept_mimes[257];
278 HRESULT hres;
280 static const WCHAR wszHttp[] = {'h','t','t','p',':'};
281 static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
282 ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
284 TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
285 pOIBindInfo, grfPI, dwReserved);
287 memset(&bindinfo, 0, sizeof(bindinfo));
288 bindinfo.cbSize = sizeof(BINDINFO);
289 hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
290 if (hres != S_OK)
292 WARN("GetBindInfo failed: %08x\n", hres);
293 goto done;
296 if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
297 || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
299 hres = MK_E_SYNTAX;
300 goto done;
303 memset(&url, 0, sizeof(url));
304 url.dwStructSize = sizeof(url);
305 url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
306 url.dwPasswordLength = 1;
307 if (!InternetCrackUrlW(szUrl, 0, ICU_ESCAPE, &url))
309 hres = MK_E_SYNTAX;
310 goto done;
312 host = strndupW(url.lpszHostName, url.dwHostNameLength);
313 path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
314 user = strndupW(url.lpszUserName, url.dwUserNameLength);
315 pass = strndupW(url.lpszPassword, url.dwPasswordLength);
316 if (!url.nPort)
317 url.nPort = INTERNET_DEFAULT_HTTP_PORT;
319 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
320 1, &num);
321 if (hres != S_OK || !num)
323 CHAR null_char = 0;
324 LPSTR user_agenta = NULL;
325 len = 0;
326 if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
328 WARN("ObtainUserAgentString failed: %08x\n", hres);
330 else if (!(user_agenta = HeapAlloc(GetProcessHeap(), 0, len*sizeof(CHAR))))
332 WARN("Out of memory\n");
334 else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
336 WARN("ObtainUserAgentString failed: %08x\n", hres);
338 else
340 if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
341 WARN("Out of memory\n");
342 else
343 MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len*sizeof(WCHAR));
345 HeapFree(GetProcessHeap(), 0, user_agenta);
348 This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
349 if (!This->internet)
351 WARN("InternetOpen failed: %d\n", GetLastError());
352 hres = INET_E_NO_SESSION;
353 goto done;
356 IInternetProtocolSink_AddRef(pOIProtSink);
357 This->protocol_sink = pOIProtSink;
359 /* Native does not check for success of next call, so we won't either */
360 InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
362 This->connect = InternetConnectW(This->internet, host, url.nPort, user,
363 pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
364 if (!This->connect)
366 WARN("InternetConnect failed: %d\n", GetLastError());
367 hres = INET_E_CANNOT_CONNECT;
368 goto done;
371 num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
372 hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
373 accept_mimes,
374 num, &num);
375 if (hres != S_OK)
377 WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
378 hres = INET_E_NO_VALID_MEDIA;
379 goto done;
381 accept_mimes[num] = 0;
383 This->request = HttpOpenRequestW(This->connect, NULL, path, NULL, NULL,
384 (LPCWSTR *)accept_mimes, 0, (DWORD)This);
385 if (!This->request)
387 WARN("HttpOpenRequest failed: %d\n", GetLastError());
388 hres = INET_E_RESOURCE_NOT_FOUND;
389 goto done;
392 hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &IID_IServiceProvider,
393 (void **)&service_provider);
394 if (hres != S_OK)
396 WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
397 goto done;
400 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
401 &IID_IHttpNegotiate, (void **)&This->http_negotiate);
402 if (hres != S_OK)
404 WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
405 goto done;
408 hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
409 0, &addl_header);
410 if (hres != S_OK)
412 WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
413 goto done;
416 hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
417 &IID_IHttpNegotiate2, (void **)&http_negotiate2);
418 if (hres != S_OK)
420 WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
421 /* No goto done as per native */
423 else
425 len = sizeof(security_id)/sizeof(security_id[0]);
426 hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
427 if (hres != S_OK)
429 WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
430 /* No goto done as per native */
434 /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
436 if (!HttpSendRequestW(This->request, wszHeaders, lstrlenW(wszHeaders), NULL, 0) &&
437 GetLastError() != ERROR_IO_PENDING)
439 WARN("HttpSendRequest failed: %d\n", GetLastError());
440 hres = INET_E_DOWNLOAD_FAILURE;
441 goto done;
444 hres = S_OK;
445 done:
446 if (hres != S_OK)
448 IInternetProtocolSink_ReportResult(pOIProtSink, hres, 0, NULL);
449 HTTPPROTOCOL_Close(This);
452 CoTaskMemFree(addl_header);
453 if (http_negotiate2)
454 IHttpNegotiate2_Release(http_negotiate2);
455 if (service_provider)
456 IServiceProvider_Release(service_provider);
458 while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
459 accept_mimes[num])
460 CoTaskMemFree(accept_mimes[num++]);
461 CoTaskMemFree(user_agent);
463 HeapFree(GetProcessHeap(), 0, pass);
464 HeapFree(GetProcessHeap(), 0, user);
465 HeapFree(GetProcessHeap(), 0, path);
466 HeapFree(GetProcessHeap(), 0, host);
468 ReleaseBindInfo(&bindinfo);
470 return hres;
473 static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
475 HttpProtocol *This = PROTOCOL_THIS(iface);
476 DWORD len = sizeof(DWORD), status_code;
477 LPWSTR response_headers = 0, content_type = 0, content_length = 0;
479 static const WCHAR wszDefaultContentType[] =
480 {'t','e','x','t','/','h','t','m','l',0};
482 TRACE("(%p)->(%p)\n", This, pProtocolData);
484 if (!pProtocolData)
485 WARN("Expected pProtocolData to be non-NULL\n");
486 else if (!This->request)
487 WARN("Expected request to be non-NULL\n");
488 else if (!This->http_negotiate)
489 WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
490 else if (!This->protocol_sink)
491 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
492 else if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
494 if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
495 &status_code, &len, NULL))
497 WARN("HttpQueryInfo failed: %d\n", GetLastError());
499 else
501 len = 0;
502 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
503 NULL) &&
504 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
505 !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
506 !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
507 NULL))
509 WARN("HttpQueryInfo failed: %d\n", GetLastError());
511 else
513 HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
514 response_headers, NULL, NULL);
515 if (hres != S_OK)
517 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
518 goto done;
523 len = 0;
524 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
525 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
526 !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
527 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
529 WARN("HttpQueryInfo failed: %d\n", GetLastError());
530 IInternetProtocolSink_ReportProgress(This->protocol_sink,
531 BINDSTATUS_MIMETYPEAVAILABLE,
532 wszDefaultContentType);
534 else
536 IInternetProtocolSink_ReportProgress(This->protocol_sink,
537 BINDSTATUS_MIMETYPEAVAILABLE,
538 content_type);
541 len = 0;
542 if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
543 GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
544 !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
545 !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
547 WARN("HttpQueryInfo failed: %d\n", GetLastError());
548 This->content_length = 0;
550 else
552 This->content_length = atoiW(content_length);
555 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
557 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
558 HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
560 else
562 HTTPPROTOCOL_ReportData(This);
566 done:
567 HeapFree(GetProcessHeap(), 0, response_headers);
568 HeapFree(GetProcessHeap(), 0, content_type);
569 HeapFree(GetProcessHeap(), 0, content_length);
571 /* Returns S_OK on native */
572 return S_OK;
575 static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
576 DWORD dwOptions)
578 HttpProtocol *This = PROTOCOL_THIS(iface);
579 FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
580 return E_NOTIMPL;
583 static HRESULT WINAPI HttpProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
585 HttpProtocol *This = PROTOCOL_THIS(iface);
586 FIXME("(%p)->(%08x)\n", This, dwOptions);
587 return E_NOTIMPL;
590 static HRESULT WINAPI HttpProtocol_Suspend(IInternetProtocol *iface)
592 HttpProtocol *This = PROTOCOL_THIS(iface);
593 FIXME("(%p)\n", This);
594 return E_NOTIMPL;
597 static HRESULT WINAPI HttpProtocol_Resume(IInternetProtocol *iface)
599 HttpProtocol *This = PROTOCOL_THIS(iface);
600 FIXME("(%p)\n", This);
601 return E_NOTIMPL;
604 static HRESULT WINAPI HttpProtocol_Read(IInternetProtocol *iface, void *pv,
605 ULONG cb, ULONG *pcbRead)
607 HttpProtocol *This = PROTOCOL_THIS(iface);
608 ULONG read = 0, len = 0;
609 HRESULT hres = S_FALSE;
611 TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
613 if (!(This->flags & FLAG_REQUEST_COMPLETE))
615 hres = E_PENDING;
617 else while (!(This->flags & FLAG_ALL_DATA_READ) &&
618 read < cb)
620 if (This->available_bytes == 0)
622 if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
624 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
625 hres = INET_E_DATA_NOT_AVAILABLE;
626 HTTPPROTOCOL_ReportResult(This, hres);
627 goto done;
629 else if (This->available_bytes == 0)
631 HTTPPROTOCOL_AllDataRead(This);
634 else
636 if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
637 This->available_bytes > cb-read ?
638 cb-read : This->available_bytes, &len))
640 WARN("InternetReadFile failed: %d\n", GetLastError());
641 hres = INET_E_DOWNLOAD_FAILURE;
642 HTTPPROTOCOL_ReportResult(This, hres);
643 goto done;
645 else if (len == 0)
647 HTTPPROTOCOL_AllDataRead(This);
649 else
651 read += len;
652 This->current_position += len;
653 This->available_bytes -= len;
658 /* Per MSDN this should be if (read == cb), but native returns S_OK
659 * if any bytes were read, so we will too */
660 if (read)
661 hres = S_OK;
663 done:
664 if (pcbRead)
665 *pcbRead = read;
667 return hres;
670 static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
671 DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
673 HttpProtocol *This = PROTOCOL_THIS(iface);
674 FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
675 return E_NOTIMPL;
678 static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
680 HttpProtocol *This = PROTOCOL_THIS(iface);
682 TRACE("(%p)->(%08x)\n", This, dwOptions);
684 if (!InternetLockRequestFile(This->request, &This->lock))
685 WARN("InternetLockRequest failed: %d\n", GetLastError());
687 return S_OK;
690 static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
692 HttpProtocol *This = PROTOCOL_THIS(iface);
694 TRACE("(%p)\n", This);
696 if (This->lock)
698 if (!InternetUnlockRequestFile(This->lock))
699 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
700 This->lock = 0;
703 return S_OK;
706 #undef PROTOCOL_THIS
708 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
710 static HRESULT WINAPI HttpPriority_QueryInterface(IInternetPriority *iface, REFIID riid, void **ppv)
712 HttpProtocol *This = PRIORITY_THIS(iface);
713 return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
716 static ULONG WINAPI HttpPriority_AddRef(IInternetPriority *iface)
718 HttpProtocol *This = PRIORITY_THIS(iface);
719 return IInternetProtocol_AddRef(PROTOCOL(This));
722 static ULONG WINAPI HttpPriority_Release(IInternetPriority *iface)
724 HttpProtocol *This = PRIORITY_THIS(iface);
725 return IInternetProtocol_Release(PROTOCOL(This));
728 static HRESULT WINAPI HttpPriority_SetPriority(IInternetPriority *iface, LONG nPriority)
730 HttpProtocol *This = PRIORITY_THIS(iface);
732 TRACE("(%p)->(%d)\n", This, nPriority);
734 This->priority = nPriority;
735 return S_OK;
738 static HRESULT WINAPI HttpPriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
740 HttpProtocol *This = PRIORITY_THIS(iface);
742 TRACE("(%p)->(%p)\n", This, pnPriority);
744 *pnPriority = This->priority;
745 return S_OK;
748 #undef PRIORITY_THIS
750 static const IInternetPriorityVtbl HttpPriorityVtbl = {
751 HttpPriority_QueryInterface,
752 HttpPriority_AddRef,
753 HttpPriority_Release,
754 HttpPriority_SetPriority,
755 HttpPriority_GetPriority
758 static const IInternetProtocolVtbl HttpProtocolVtbl = {
759 HttpProtocol_QueryInterface,
760 HttpProtocol_AddRef,
761 HttpProtocol_Release,
762 HttpProtocol_Start,
763 HttpProtocol_Continue,
764 HttpProtocol_Abort,
765 HttpProtocol_Terminate,
766 HttpProtocol_Suspend,
767 HttpProtocol_Resume,
768 HttpProtocol_Read,
769 HttpProtocol_Seek,
770 HttpProtocol_LockRequest,
771 HttpProtocol_UnlockRequest
774 HRESULT HttpProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
776 HttpProtocol *ret;
778 TRACE("(%p %p)\n", pUnkOuter, ppobj);
780 URLMON_LockModule();
782 ret = HeapAlloc(GetProcessHeap(), 0, sizeof(HttpProtocol));
784 ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
785 ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
786 ret->flags = 0;
787 ret->protocol_sink = 0;
788 ret->http_negotiate = 0;
789 ret->internet = ret->connect = ret->request = 0;
790 ret->lock = 0;
791 ret->current_position = ret->content_length = ret->available_bytes = 0;
792 ret->priority = 0;
793 ret->ref = 1;
795 *ppobj = PROTOCOL(ret);
797 return S_OK;