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
22 * - Handle redirects as native.
25 #include "urlmon_main.h"
28 #include "wine/debug.h"
30 WINE_DEFAULT_DEBUG_CHANNEL(urlmon
);
32 /* Flags are needed for, among other things, return HRESULTs from the Read function
33 * to conform to native. For example, Read returns:
35 * 1. E_PENDING if called before the request has completed,
37 * 2. S_FALSE after all data has been read and S_OK has been reported,
38 * (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
39 * 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
40 * this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
41 * (flags = FLAG_REQUEST_COMPLETE)
42 * but upon subsequent calls to Read no reporting will take place, yet
43 * InternetQueryDataAvailable will still be called, and, on failure,
44 * INET_E_DATA_NOT_AVAILABLE will still be returned.
45 * (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
47 * FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
48 * ReportData reporting. For example, if OnResponse returns S_OK, Continue will
49 * report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
50 * report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
51 * if OnResponse does not return S_OK, Continue will not report data, and Read
52 * will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
55 #define FLAG_REQUEST_COMPLETE 0x1
56 #define FLAG_FIRST_CONTINUE_COMPLETE 0x2
57 #define FLAG_FIRST_DATA_REPORTED 0x4
58 #define FLAG_ALL_DATA_READ 0x8
59 #define FLAG_LAST_DATA_REPORTED 0x10
60 #define FLAG_RESULT_REPORTED 0x20
63 const IInternetProtocolVtbl
*lpInternetProtocolVtbl
;
64 const IInternetPriorityVtbl
*lpInternetPriorityVtbl
;
67 DWORD flags
, grfBINDF
;
69 IInternetProtocolSink
*protocol_sink
;
70 IHttpNegotiate
*http_negotiate
;
71 HINTERNET internet
, connect
, request
;
74 ULONG current_position
, content_length
, available_bytes
;
80 /* Default headers from native */
81 static const WCHAR wszHeaders
[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
82 ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
88 static void HTTPPROTOCOL_ReportResult(HttpProtocol
*This
, HRESULT hres
)
90 if (!(This
->flags
& FLAG_RESULT_REPORTED
) &&
93 This
->flags
|= FLAG_RESULT_REPORTED
;
94 IInternetProtocolSink_ReportResult(This
->protocol_sink
, hres
, 0, NULL
);
98 static void HTTPPROTOCOL_ReportData(HttpProtocol
*This
)
101 if (!(This
->flags
& FLAG_LAST_DATA_REPORTED
) &&
104 if (This
->flags
& FLAG_FIRST_DATA_REPORTED
)
106 bscf
= BSCF_INTERMEDIATEDATANOTIFICATION
;
110 This
->flags
|= FLAG_FIRST_DATA_REPORTED
;
111 bscf
= BSCF_FIRSTDATANOTIFICATION
;
113 if (This
->flags
& FLAG_ALL_DATA_READ
&&
114 !(This
->flags
& FLAG_LAST_DATA_REPORTED
))
116 This
->flags
|= FLAG_LAST_DATA_REPORTED
;
117 bscf
|= BSCF_LASTDATANOTIFICATION
;
119 IInternetProtocolSink_ReportData(This
->protocol_sink
, bscf
,
120 This
->current_position
+This
->available_bytes
,
121 This
->content_length
);
125 static void HTTPPROTOCOL_AllDataRead(HttpProtocol
*This
)
127 if (!(This
->flags
& FLAG_ALL_DATA_READ
))
128 This
->flags
|= FLAG_ALL_DATA_READ
;
129 HTTPPROTOCOL_ReportData(This
);
130 HTTPPROTOCOL_ReportResult(This
, S_OK
);
133 static void HTTPPROTOCOL_Close(HttpProtocol
*This
)
135 if (This
->http_negotiate
)
137 IHttpNegotiate_Release(This
->http_negotiate
);
138 This
->http_negotiate
= 0;
141 InternetCloseHandle(This
->request
);
143 InternetCloseHandle(This
->connect
);
146 InternetCloseHandle(This
->internet
);
149 if (This
->full_header
)
151 if (This
->full_header
!= wszHeaders
)
152 heap_free(This
->full_header
);
153 This
->full_header
= 0;
158 static void CALLBACK
HTTPPROTOCOL_InternetStatusCallback(
159 HINTERNET hInternet
, DWORD_PTR dwContext
, DWORD dwInternetStatus
,
160 LPVOID lpvStatusInformation
, DWORD dwStatusInformationLength
)
162 HttpProtocol
*This
= (HttpProtocol
*)dwContext
;
166 switch (dwInternetStatus
)
168 case INTERNET_STATUS_RESOLVING_NAME
:
169 ulStatusCode
= BINDSTATUS_FINDINGRESOURCE
;
171 case INTERNET_STATUS_CONNECTING_TO_SERVER
:
172 ulStatusCode
= BINDSTATUS_CONNECTING
;
174 case INTERNET_STATUS_SENDING_REQUEST
:
175 ulStatusCode
= BINDSTATUS_SENDINGREQUEST
;
177 case INTERNET_STATUS_REQUEST_COMPLETE
:
178 This
->flags
|= FLAG_REQUEST_COMPLETE
;
179 /* PROTOCOLDATA same as native */
180 memset(&data
, 0, sizeof(data
));
181 data
.dwState
= 0xf1000000;
182 if (This
->flags
& FLAG_FIRST_CONTINUE_COMPLETE
)
183 data
.pData
= (LPVOID
)BINDSTATUS_ENDDOWNLOADCOMPONENTS
;
185 data
.pData
= (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
;
186 if (This
->grfBINDF
& BINDF_FROMURLMON
)
187 IInternetProtocolSink_Switch(This
->protocol_sink
, &data
);
189 IInternetProtocol_Continue((IInternetProtocol
*)This
, &data
);
191 case INTERNET_STATUS_HANDLE_CREATED
:
192 IInternetProtocol_AddRef((IInternetProtocol
*)This
);
194 case INTERNET_STATUS_HANDLE_CLOSING
:
195 if (*(HINTERNET
*)lpvStatusInformation
== This
->connect
)
199 else if (*(HINTERNET
*)lpvStatusInformation
== This
->request
)
202 if (This
->protocol_sink
)
204 IInternetProtocolSink_Release(This
->protocol_sink
);
205 This
->protocol_sink
= 0;
207 if (This
->bind_info
.cbSize
)
209 ReleaseBindInfo(&This
->bind_info
);
210 memset(&This
->bind_info
, 0, sizeof(This
->bind_info
));
213 IInternetProtocol_Release((IInternetProtocol
*)This
);
216 WARN("Unhandled Internet status callback %d\n", dwInternetStatus
);
220 IInternetProtocolSink_ReportProgress(This
->protocol_sink
, ulStatusCode
, (LPWSTR
)lpvStatusInformation
);
223 static inline LPWSTR
strndupW(LPCWSTR string
, int len
)
227 (ret
= heap_alloc((len
+1)*sizeof(WCHAR
))) != NULL
)
229 memcpy(ret
, string
, len
*sizeof(WCHAR
));
236 * Interface implementations
239 #define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
240 #define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
242 #define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
244 static HRESULT WINAPI
HttpProtocol_QueryInterface(IInternetProtocol
*iface
, REFIID riid
, void **ppv
)
246 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
249 if(IsEqualGUID(&IID_IUnknown
, riid
)) {
250 TRACE("(%p)->(IID_IUnknown %p)\n", This
, ppv
);
251 *ppv
= PROTOCOL(This
);
252 }else if(IsEqualGUID(&IID_IInternetProtocolRoot
, riid
)) {
253 TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This
, ppv
);
254 *ppv
= PROTOCOL(This
);
255 }else if(IsEqualGUID(&IID_IInternetProtocol
, riid
)) {
256 TRACE("(%p)->(IID_IInternetProtocol %p)\n", This
, ppv
);
257 *ppv
= PROTOCOL(This
);
258 }else if(IsEqualGUID(&IID_IInternetPriority
, riid
)) {
259 TRACE("(%p)->(IID_IInternetPriority %p)\n", This
, ppv
);
260 *ppv
= PRIORITY(This
);
264 IInternetProtocol_AddRef(iface
);
268 WARN("not supported interface %s\n", debugstr_guid(riid
));
269 return E_NOINTERFACE
;
272 static ULONG WINAPI
HttpProtocol_AddRef(IInternetProtocol
*iface
)
274 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
275 LONG ref
= InterlockedIncrement(&This
->ref
);
276 TRACE("(%p) ref=%d\n", This
, ref
);
280 static ULONG WINAPI
HttpProtocol_Release(IInternetProtocol
*iface
)
282 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
283 LONG ref
= InterlockedDecrement(&This
->ref
);
285 TRACE("(%p) ref=%d\n", This
, ref
);
288 HTTPPROTOCOL_Close(This
);
291 URLMON_UnlockModule();
297 static HRESULT WINAPI
HttpProtocol_Start(IInternetProtocol
*iface
, LPCWSTR szUrl
,
298 IInternetProtocolSink
*pOIProtSink
, IInternetBindInfo
*pOIBindInfo
,
299 DWORD grfPI
, DWORD dwReserved
)
301 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
303 DWORD len
= 0, request_flags
= INTERNET_FLAG_KEEP_CONNECTION
;
305 IServiceProvider
*service_provider
= 0;
306 IHttpNegotiate2
*http_negotiate2
= 0;
307 LPWSTR host
= 0, path
= 0, user
= 0, pass
= 0, addl_header
= 0,
308 post_cookie
= 0, optional
= 0;
309 BYTE security_id
[512];
310 LPOLESTR user_agent
= NULL
, accept_mimes
[257];
313 static const WCHAR httpW
[] = {'h','t','t','p',':'};
314 static const WCHAR httpsW
[] = {'h','t','t','p','s',':'};
315 static const WCHAR wszBindVerb
[BINDVERB_CUSTOM
][5] =
320 TRACE("(%p)->(%s %p %p %08x %d)\n", This
, debugstr_w(szUrl
), pOIProtSink
,
321 pOIBindInfo
, grfPI
, dwReserved
);
323 IInternetProtocolSink_AddRef(pOIProtSink
);
324 This
->protocol_sink
= pOIProtSink
;
326 memset(&This
->bind_info
, 0, sizeof(This
->bind_info
));
327 This
->bind_info
.cbSize
= sizeof(BINDINFO
);
328 hres
= IInternetBindInfo_GetBindInfo(pOIBindInfo
, &This
->grfBINDF
, &This
->bind_info
);
331 WARN("GetBindInfo failed: %08x\n", hres
);
336 ? strncmpW(szUrl
, httpsW
, sizeof(httpsW
)/sizeof(WCHAR
))
337 : strncmpW(szUrl
, httpW
, sizeof(httpW
)/sizeof(WCHAR
)))
343 memset(&url
, 0, sizeof(url
));
344 url
.dwStructSize
= sizeof(url
);
345 url
.dwSchemeLength
= url
.dwHostNameLength
= url
.dwUrlPathLength
= url
.dwUserNameLength
=
346 url
.dwPasswordLength
= 1;
347 if (!InternetCrackUrlW(szUrl
, 0, 0, &url
))
352 host
= strndupW(url
.lpszHostName
, url
.dwHostNameLength
);
353 path
= strndupW(url
.lpszUrlPath
, url
.dwUrlPathLength
);
354 user
= strndupW(url
.lpszUserName
, url
.dwUserNameLength
);
355 pass
= strndupW(url
.lpszPassword
, url
.dwPasswordLength
);
357 url
.nPort
= This
->https
? INTERNET_DEFAULT_HTTPS_PORT
: INTERNET_DEFAULT_HTTP_PORT
;
359 if(!(This
->grfBINDF
& BINDF_FROMURLMON
))
360 IInternetProtocolSink_ReportProgress(This
->protocol_sink
, BINDSTATUS_DIRECTBIND
, NULL
);
362 hres
= IInternetBindInfo_GetBindString(pOIBindInfo
, BINDSTRING_USER_AGENT
, &user_agent
,
364 if (hres
!= S_OK
|| !num
)
367 LPSTR user_agenta
= NULL
;
369 if ((hres
= ObtainUserAgentString(0, &null_char
, &len
)) != E_OUTOFMEMORY
)
371 WARN("ObtainUserAgentString failed: %08x\n", hres
);
373 else if (!(user_agenta
= heap_alloc(len
*sizeof(CHAR
))))
375 WARN("Out of memory\n");
377 else if ((hres
= ObtainUserAgentString(0, user_agenta
, &len
)) != S_OK
)
379 WARN("ObtainUserAgentString failed: %08x\n", hres
);
383 if (!(user_agent
= CoTaskMemAlloc((len
)*sizeof(WCHAR
))))
384 WARN("Out of memory\n");
386 MultiByteToWideChar(CP_ACP
, 0, user_agenta
, -1, user_agent
, len
);
388 heap_free(user_agenta
);
391 This
->internet
= InternetOpenW(user_agent
, 0, NULL
, NULL
, INTERNET_FLAG_ASYNC
);
394 WARN("InternetOpen failed: %d\n", GetLastError());
395 hres
= INET_E_NO_SESSION
;
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
,
404 This
->https
? INTERNET_FLAG_SECURE
: 0,
408 WARN("InternetConnect failed: %d\n", GetLastError());
409 hres
= INET_E_CANNOT_CONNECT
;
413 num
= sizeof(accept_mimes
)/sizeof(accept_mimes
[0])-1;
414 hres
= IInternetBindInfo_GetBindString(pOIBindInfo
, BINDSTRING_ACCEPT_MIMES
,
419 WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres
);
420 hres
= INET_E_NO_VALID_MEDIA
;
423 accept_mimes
[num
] = 0;
425 if (This
->grfBINDF
& BINDF_NOWRITECACHE
)
426 request_flags
|= INTERNET_FLAG_NO_CACHE_WRITE
;
427 if (This
->grfBINDF
& BINDF_NEEDFILE
)
428 request_flags
|= INTERNET_FLAG_NEED_FILE
;
430 request_flags
|= INTERNET_FLAG_SECURE
;
431 This
->request
= HttpOpenRequestW(This
->connect
, This
->bind_info
.dwBindVerb
< BINDVERB_CUSTOM
?
432 wszBindVerb
[This
->bind_info
.dwBindVerb
] :
433 This
->bind_info
.szCustomVerb
,
434 path
, NULL
, NULL
, (LPCWSTR
*)accept_mimes
,
435 request_flags
, (DWORD_PTR
)This
);
438 WARN("HttpOpenRequest failed: %d\n", GetLastError());
439 hres
= INET_E_RESOURCE_NOT_FOUND
;
443 hres
= IInternetProtocolSink_QueryInterface(This
->protocol_sink
, &IID_IServiceProvider
,
444 (void **)&service_provider
);
447 WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres
);
451 hres
= IServiceProvider_QueryService(service_provider
, &IID_IHttpNegotiate
,
452 &IID_IHttpNegotiate
, (void **)&This
->http_negotiate
);
455 WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres
);
459 hres
= IHttpNegotiate_BeginningTransaction(This
->http_negotiate
, szUrl
, wszHeaders
,
463 WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres
);
466 else if (addl_header
== NULL
)
468 This
->full_header
= (LPWSTR
)wszHeaders
;
472 int len_addl_header
= lstrlenW(addl_header
);
473 This
->full_header
= heap_alloc(len_addl_header
*sizeof(WCHAR
)+sizeof(wszHeaders
));
474 if (!This
->full_header
)
476 WARN("Out of memory\n");
477 hres
= E_OUTOFMEMORY
;
480 lstrcpyW(This
->full_header
, addl_header
);
481 lstrcpyW(&This
->full_header
[len_addl_header
], wszHeaders
);
484 hres
= IServiceProvider_QueryService(service_provider
, &IID_IHttpNegotiate2
,
485 &IID_IHttpNegotiate2
, (void **)&http_negotiate2
);
488 WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres
);
489 /* No goto done as per native */
493 len
= sizeof(security_id
)/sizeof(security_id
[0]);
494 hres
= IHttpNegotiate2_GetRootSecurityId(http_negotiate2
, security_id
, &len
, 0);
497 WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres
);
498 /* No goto done as per native */
502 /* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
504 if (This
->bind_info
.dwBindVerb
== BINDVERB_POST
)
507 hres
= IInternetBindInfo_GetBindString(pOIBindInfo
, BINDSTRING_POST_COOKIE
, &post_cookie
,
509 if (hres
== S_OK
&& num
&&
510 !InternetSetOptionW(This
->request
, INTERNET_OPTION_SECONDARY_CACHE_KEY
,
511 post_cookie
, lstrlenW(post_cookie
)))
513 WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
518 if (This
->bind_info
.dwBindVerb
!= BINDVERB_GET
)
520 /* Native does not use GlobalLock/GlobalUnlock, so we won't either */
521 if (This
->bind_info
.stgmedData
.tymed
!= TYMED_HGLOBAL
)
522 WARN("Expected This->bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
523 This
->bind_info
.stgmedData
.tymed
);
525 optional
= (LPWSTR
)This
->bind_info
.stgmedData
.u
.hGlobal
;
527 if (!HttpSendRequestW(This
->request
, This
->full_header
, lstrlenW(This
->full_header
),
529 optional
? This
->bind_info
.cbstgmedData
: 0) &&
530 GetLastError() != ERROR_IO_PENDING
)
532 WARN("HttpSendRequest failed: %d\n", GetLastError());
533 hres
= INET_E_DOWNLOAD_FAILURE
;
541 IInternetProtocolSink_ReportResult(This
->protocol_sink
, hres
, 0, NULL
);
542 HTTPPROTOCOL_Close(This
);
545 CoTaskMemFree(post_cookie
);
546 CoTaskMemFree(addl_header
);
548 IHttpNegotiate2_Release(http_negotiate2
);
549 if (service_provider
)
550 IServiceProvider_Release(service_provider
);
552 while (num
<sizeof(accept_mimes
)/sizeof(accept_mimes
[0]) &&
554 CoTaskMemFree(accept_mimes
[num
++]);
555 CoTaskMemFree(user_agent
);
565 static HRESULT WINAPI
HttpProtocol_Continue(IInternetProtocol
*iface
, PROTOCOLDATA
*pProtocolData
)
567 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
568 DWORD len
= sizeof(DWORD
), status_code
;
569 LPWSTR response_headers
= 0, content_type
= 0, content_length
= 0;
571 static const WCHAR wszDefaultContentType
[] =
572 {'t','e','x','t','/','h','t','m','l',0};
574 TRACE("(%p)->(%p)\n", This
, pProtocolData
);
578 WARN("Expected pProtocolData to be non-NULL\n");
581 else if (!This
->request
)
583 WARN("Expected request to be non-NULL\n");
586 else if (!This
->http_negotiate
)
588 WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
591 else if (!This
->protocol_sink
)
593 WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
597 if (pProtocolData
->pData
== (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
)
599 if (!HttpQueryInfoW(This
->request
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
,
600 &status_code
, &len
, NULL
))
602 WARN("HttpQueryInfo failed: %d\n", GetLastError());
607 if ((!HttpQueryInfoW(This
->request
, HTTP_QUERY_RAW_HEADERS_CRLF
, response_headers
, &len
,
609 GetLastError() != ERROR_INSUFFICIENT_BUFFER
) ||
610 !(response_headers
= heap_alloc(len
)) ||
611 !HttpQueryInfoW(This
->request
, HTTP_QUERY_RAW_HEADERS_CRLF
, response_headers
, &len
,
614 WARN("HttpQueryInfo failed: %d\n", GetLastError());
618 HRESULT hres
= IHttpNegotiate_OnResponse(This
->http_negotiate
, status_code
,
619 response_headers
, NULL
, NULL
);
622 WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres
);
629 IInternetProtocolSink_ReportProgress(This
->protocol_sink
, BINDSTATUS_ACCEPTRANGES
, NULL
);
632 if ((!HttpQueryInfoW(This
->request
, HTTP_QUERY_CONTENT_TYPE
, content_type
, &len
, NULL
) &&
633 GetLastError() != ERROR_INSUFFICIENT_BUFFER
) ||
634 !(content_type
= heap_alloc(len
)) ||
635 !HttpQueryInfoW(This
->request
, HTTP_QUERY_CONTENT_TYPE
, content_type
, &len
, NULL
))
637 WARN("HttpQueryInfo failed: %d\n", GetLastError());
638 IInternetProtocolSink_ReportProgress(This
->protocol_sink
,
639 (This
->grfBINDF
& BINDF_FROMURLMON
) ?
640 BINDSTATUS_MIMETYPEAVAILABLE
:
641 BINDSTATUS_RAWMIMETYPE
,
642 wszDefaultContentType
);
646 /* remove the charset, if present */
647 LPWSTR p
= strchrW(content_type
, ';');
650 IInternetProtocolSink_ReportProgress(This
->protocol_sink
,
651 (This
->grfBINDF
& BINDF_FROMURLMON
) ?
652 BINDSTATUS_MIMETYPEAVAILABLE
:
653 BINDSTATUS_RAWMIMETYPE
,
658 if ((!HttpQueryInfoW(This
->request
, HTTP_QUERY_CONTENT_LENGTH
, content_length
, &len
, NULL
) &&
659 GetLastError() != ERROR_INSUFFICIENT_BUFFER
) ||
660 !(content_length
= heap_alloc(len
)) ||
661 !HttpQueryInfoW(This
->request
, HTTP_QUERY_CONTENT_LENGTH
, content_length
, &len
, NULL
))
663 WARN("HttpQueryInfo failed: %d\n", GetLastError());
664 This
->content_length
= 0;
668 This
->content_length
= atoiW(content_length
);
671 if(This
->grfBINDF
& BINDF_NEEDFILE
) {
672 WCHAR cache_file
[MAX_PATH
];
673 DWORD buflen
= sizeof(cache_file
);
675 if(InternetQueryOptionW(This
->request
, INTERNET_OPTION_DATAFILE_NAME
,
676 cache_file
, &buflen
))
678 IInternetProtocolSink_ReportProgress(This
->protocol_sink
,
679 BINDSTATUS_CACHEFILENAMEAVAILABLE
,
682 FIXME("Could not get cache file\n");
686 This
->flags
|= FLAG_FIRST_CONTINUE_COMPLETE
;
689 if (pProtocolData
->pData
>= (LPVOID
)BINDSTATUS_DOWNLOADINGDATA
)
691 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
692 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
693 * after the status callback is called */
694 This
->flags
&= ~FLAG_REQUEST_COMPLETE
;
695 if (!InternetQueryDataAvailable(This
->request
, &This
->available_bytes
, 0, 0))
697 if (GetLastError() != ERROR_IO_PENDING
)
699 This
->flags
|= FLAG_REQUEST_COMPLETE
;
700 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
701 HTTPPROTOCOL_ReportResult(This
, INET_E_DATA_NOT_AVAILABLE
);
706 This
->flags
|= FLAG_REQUEST_COMPLETE
;
707 HTTPPROTOCOL_ReportData(This
);
712 heap_free(response_headers
);
713 heap_free(content_type
);
714 heap_free(content_length
);
716 /* Returns S_OK on native */
720 static HRESULT WINAPI
HttpProtocol_Abort(IInternetProtocol
*iface
, HRESULT hrReason
,
723 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
724 FIXME("(%p)->(%08x %08x)\n", This
, hrReason
, dwOptions
);
728 static HRESULT WINAPI
HttpProtocol_Terminate(IInternetProtocol
*iface
, DWORD dwOptions
)
730 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
732 TRACE("(%p)->(%08x)\n", This
, dwOptions
);
733 HTTPPROTOCOL_Close(This
);
738 static HRESULT WINAPI
HttpProtocol_Suspend(IInternetProtocol
*iface
)
740 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
741 FIXME("(%p)\n", This
);
745 static HRESULT WINAPI
HttpProtocol_Resume(IInternetProtocol
*iface
)
747 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
748 FIXME("(%p)\n", This
);
752 static HRESULT WINAPI
HttpProtocol_Read(IInternetProtocol
*iface
, void *pv
,
753 ULONG cb
, ULONG
*pcbRead
)
755 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
756 ULONG read
= 0, len
= 0;
757 HRESULT hres
= S_FALSE
;
759 TRACE("(%p)->(%p %u %p)\n", This
, pv
, cb
, pcbRead
);
761 if (!(This
->flags
& FLAG_REQUEST_COMPLETE
))
765 else while (!(This
->flags
& FLAG_ALL_DATA_READ
) &&
768 if (This
->available_bytes
== 0)
770 /* InternetQueryDataAvailable may immediately fork and perform its asynchronous
771 * read, so clear the flag _before_ calling so it does not incorrectly get cleared
772 * after the status callback is called */
773 This
->flags
&= ~FLAG_REQUEST_COMPLETE
;
774 if (!InternetQueryDataAvailable(This
->request
, &This
->available_bytes
, 0, 0))
776 if (GetLastError() == ERROR_IO_PENDING
)
782 WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
783 hres
= INET_E_DATA_NOT_AVAILABLE
;
784 HTTPPROTOCOL_ReportResult(This
, hres
);
788 else if (This
->available_bytes
== 0)
790 HTTPPROTOCOL_AllDataRead(This
);
795 if (!InternetReadFile(This
->request
, ((BYTE
*)pv
)+read
,
796 This
->available_bytes
> cb
-read
?
797 cb
-read
: This
->available_bytes
, &len
))
799 WARN("InternetReadFile failed: %d\n", GetLastError());
800 hres
= INET_E_DOWNLOAD_FAILURE
;
801 HTTPPROTOCOL_ReportResult(This
, hres
);
806 HTTPPROTOCOL_AllDataRead(This
);
811 This
->current_position
+= len
;
812 This
->available_bytes
-= len
;
817 /* Per MSDN this should be if (read == cb), but native returns S_OK
818 * if any bytes were read, so we will too */
826 if (hres
!= E_PENDING
)
827 This
->flags
|= FLAG_REQUEST_COMPLETE
;
832 static HRESULT WINAPI
HttpProtocol_Seek(IInternetProtocol
*iface
, LARGE_INTEGER dlibMove
,
833 DWORD dwOrigin
, ULARGE_INTEGER
*plibNewPosition
)
835 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
836 FIXME("(%p)->(%d %d %p)\n", This
, dlibMove
.u
.LowPart
, dwOrigin
, plibNewPosition
);
840 static HRESULT WINAPI
HttpProtocol_LockRequest(IInternetProtocol
*iface
, DWORD dwOptions
)
842 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
844 TRACE("(%p)->(%08x)\n", This
, dwOptions
);
846 if (!InternetLockRequestFile(This
->request
, &This
->lock
))
847 WARN("InternetLockRequest failed: %d\n", GetLastError());
852 static HRESULT WINAPI
HttpProtocol_UnlockRequest(IInternetProtocol
*iface
)
854 HttpProtocol
*This
= PROTOCOL_THIS(iface
);
856 TRACE("(%p)\n", This
);
860 if (!InternetUnlockRequestFile(This
->lock
))
861 WARN("InternetUnlockRequest failed: %d\n", GetLastError());
870 #define PRIORITY_THIS(iface) DEFINE_THIS(HttpProtocol, InternetPriority, iface)
872 static HRESULT WINAPI
HttpPriority_QueryInterface(IInternetPriority
*iface
, REFIID riid
, void **ppv
)
874 HttpProtocol
*This
= PRIORITY_THIS(iface
);
875 return IInternetProtocol_QueryInterface(PROTOCOL(This
), riid
, ppv
);
878 static ULONG WINAPI
HttpPriority_AddRef(IInternetPriority
*iface
)
880 HttpProtocol
*This
= PRIORITY_THIS(iface
);
881 return IInternetProtocol_AddRef(PROTOCOL(This
));
884 static ULONG WINAPI
HttpPriority_Release(IInternetPriority
*iface
)
886 HttpProtocol
*This
= PRIORITY_THIS(iface
);
887 return IInternetProtocol_Release(PROTOCOL(This
));
890 static HRESULT WINAPI
HttpPriority_SetPriority(IInternetPriority
*iface
, LONG nPriority
)
892 HttpProtocol
*This
= PRIORITY_THIS(iface
);
894 TRACE("(%p)->(%d)\n", This
, nPriority
);
896 This
->priority
= nPriority
;
900 static HRESULT WINAPI
HttpPriority_GetPriority(IInternetPriority
*iface
, LONG
*pnPriority
)
902 HttpProtocol
*This
= PRIORITY_THIS(iface
);
904 TRACE("(%p)->(%p)\n", This
, pnPriority
);
906 *pnPriority
= This
->priority
;
912 static const IInternetPriorityVtbl HttpPriorityVtbl
= {
913 HttpPriority_QueryInterface
,
915 HttpPriority_Release
,
916 HttpPriority_SetPriority
,
917 HttpPriority_GetPriority
920 static const IInternetProtocolVtbl HttpProtocolVtbl
= {
921 HttpProtocol_QueryInterface
,
923 HttpProtocol_Release
,
925 HttpProtocol_Continue
,
927 HttpProtocol_Terminate
,
928 HttpProtocol_Suspend
,
932 HttpProtocol_LockRequest
,
933 HttpProtocol_UnlockRequest
936 HRESULT
create_http_protocol(BOOL https
, void **ppobj
)
940 ret
= heap_alloc_zero(sizeof(HttpProtocol
));
942 return E_OUTOFMEMORY
;
944 ret
->lpInternetProtocolVtbl
= &HttpProtocolVtbl
;
945 ret
->lpInternetPriorityVtbl
= &HttpPriorityVtbl
;
950 *ppobj
= PROTOCOL(ret
);
956 HRESULT
HttpProtocol_Construct(IUnknown
*pUnkOuter
, LPVOID
*ppobj
)
958 TRACE("(%p %p)\n", pUnkOuter
, ppobj
);
960 return create_http_protocol(FALSE
, ppobj
);
963 HRESULT
HttpSProtocol_Construct(IUnknown
*pUnkOuter
, LPVOID
*ppobj
)
965 TRACE("(%p %p)\n", pUnkOuter
, ppobj
);
967 return create_http_protocol(TRUE
, ppobj
);