mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-10 19:07:17 +00:00
202 lines
6.6 KiB
C
202 lines
6.6 KiB
C
/*
|
|
* Copyright 2007 Misha Koshelev
|
|
* Copyright 2009 Jacek Caban for CodeWeavers
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include "urlmon_main.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
|
|
|
|
/* Flags are needed for, among other things, return HRESULTs from the Read function
|
|
* to conform to native. For example, Read returns:
|
|
*
|
|
* 1. E_PENDING if called before the request has completed,
|
|
* (flags = 0)
|
|
* 2. S_FALSE after all data has been read and S_OK has been reported,
|
|
* (flags = FLAG_REQUEST_COMPLETE | FLAG_ALL_DATA_READ | FLAG_RESULT_REPORTED)
|
|
* 3. INET_E_DATA_NOT_AVAILABLE if InternetQueryDataAvailable fails. The first time
|
|
* this occurs, INET_E_DATA_NOT_AVAILABLE will also be reported to the sink,
|
|
* (flags = FLAG_REQUEST_COMPLETE)
|
|
* but upon subsequent calls to Read no reporting will take place, yet
|
|
* InternetQueryDataAvailable will still be called, and, on failure,
|
|
* INET_E_DATA_NOT_AVAILABLE will still be returned.
|
|
* (flags = FLAG_REQUEST_COMPLETE | FLAG_RESULT_REPORTED)
|
|
*
|
|
* FLAG_FIRST_DATA_REPORTED and FLAG_LAST_DATA_REPORTED are needed for proper
|
|
* ReportData reporting. For example, if OnResponse returns S_OK, Continue will
|
|
* report BSCF_FIRSTDATANOTIFICATION, and when all data has been read Read will
|
|
* report BSCF_INTERMEDIATEDATANOTIFICATION|BSCF_LASTDATANOTIFICATION. However,
|
|
* if OnResponse does not return S_OK, Continue will not report data, and Read
|
|
* will report BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION when all
|
|
* data has been read.
|
|
*/
|
|
#define FLAG_REQUEST_COMPLETE 0x0001
|
|
#define FLAG_FIRST_CONTINUE_COMPLETE 0x0002
|
|
#define FLAG_FIRST_DATA_REPORTED 0x0004
|
|
#define FLAG_ALL_DATA_READ 0x0008
|
|
#define FLAG_LAST_DATA_REPORTED 0x0010
|
|
#define FLAG_RESULT_REPORTED 0x0020
|
|
|
|
static inline HRESULT report_result(Protocol *protocol, HRESULT hres)
|
|
{
|
|
if (!(protocol->flags & FLAG_RESULT_REPORTED) && protocol->protocol_sink) {
|
|
protocol->flags |= FLAG_RESULT_REPORTED;
|
|
IInternetProtocolSink_ReportResult(protocol->protocol_sink, hres, 0, NULL);
|
|
}
|
|
|
|
return hres;
|
|
}
|
|
|
|
static void report_data(Protocol *protocol)
|
|
{
|
|
DWORD bscf;
|
|
|
|
if((protocol->flags & FLAG_LAST_DATA_REPORTED) || !protocol->protocol_sink)
|
|
return;
|
|
|
|
if(protocol->flags & FLAG_FIRST_DATA_REPORTED) {
|
|
bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
|
|
}else {
|
|
protocol->flags |= FLAG_FIRST_DATA_REPORTED;
|
|
bscf = BSCF_FIRSTDATANOTIFICATION;
|
|
}
|
|
|
|
if(protocol->flags & FLAG_ALL_DATA_READ && !(protocol->flags & FLAG_LAST_DATA_REPORTED)) {
|
|
protocol->flags |= FLAG_LAST_DATA_REPORTED;
|
|
bscf |= BSCF_LASTDATANOTIFICATION;
|
|
}
|
|
|
|
IInternetProtocolSink_ReportData(protocol->protocol_sink, bscf,
|
|
protocol->current_position+protocol->available_bytes,
|
|
protocol->content_length);
|
|
}
|
|
|
|
static void all_data_read(Protocol *protocol)
|
|
{
|
|
protocol->flags |= FLAG_ALL_DATA_READ;
|
|
|
|
report_data(protocol);
|
|
report_result(protocol, S_OK);
|
|
}
|
|
|
|
HRESULT protocol_read(Protocol *protocol, void *buf, ULONG size, ULONG *read_ret)
|
|
{
|
|
ULONG read = 0;
|
|
BOOL res;
|
|
HRESULT hres = S_FALSE;
|
|
|
|
if(!(protocol->flags & FLAG_REQUEST_COMPLETE)) {
|
|
*read_ret = 0;
|
|
return E_PENDING;
|
|
}
|
|
|
|
if(protocol->flags & FLAG_ALL_DATA_READ) {
|
|
*read_ret = 0;
|
|
return S_FALSE;
|
|
}
|
|
|
|
while(read < size) {
|
|
if(protocol->available_bytes) {
|
|
ULONG len;
|
|
|
|
res = InternetReadFile(protocol->request, ((BYTE *)buf)+read,
|
|
protocol->available_bytes > size-read ? size-read : protocol->available_bytes, &len);
|
|
if(!res) {
|
|
WARN("InternetReadFile failed: %d\n", GetLastError());
|
|
hres = INET_E_DOWNLOAD_FAILURE;
|
|
report_result(protocol, hres);
|
|
break;
|
|
}
|
|
|
|
if(!len) {
|
|
all_data_read(protocol);
|
|
break;
|
|
}
|
|
|
|
read += len;
|
|
protocol->current_position += len;
|
|
protocol->available_bytes -= len;
|
|
}else {
|
|
/* InternetQueryDataAvailable may immediately fork and perform its asynchronous
|
|
* read, so clear the flag _before_ calling so it does not incorrectly get cleared
|
|
* after the status callback is called */
|
|
protocol->flags &= ~FLAG_REQUEST_COMPLETE;
|
|
res = InternetQueryDataAvailable(protocol->request, &protocol->available_bytes, 0, 0);
|
|
if(!res) {
|
|
if (GetLastError() == ERROR_IO_PENDING) {
|
|
hres = E_PENDING;
|
|
}else {
|
|
WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
|
|
hres = INET_E_DATA_NOT_AVAILABLE;
|
|
report_result(protocol, hres);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if(!protocol->available_bytes) {
|
|
all_data_read(protocol);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
*read_ret = read;
|
|
|
|
if (hres != E_PENDING)
|
|
protocol->flags |= FLAG_REQUEST_COMPLETE;
|
|
if(FAILED(hres))
|
|
return hres;
|
|
|
|
return read ? S_OK : S_FALSE;
|
|
}
|
|
|
|
HRESULT protocol_lock_request(Protocol *protocol)
|
|
{
|
|
if (!InternetLockRequestFile(protocol->request, &protocol->lock))
|
|
WARN("InternetLockRequest failed: %d\n", GetLastError());
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
HRESULT protocol_unlock_request(Protocol *protocol)
|
|
{
|
|
if(!protocol->lock)
|
|
return S_OK;
|
|
|
|
if(!InternetUnlockRequestFile(protocol->lock))
|
|
WARN("InternetUnlockRequest failed: %d\n", GetLastError());
|
|
protocol->lock = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void protocol_close_connection(Protocol *protocol)
|
|
{
|
|
protocol->vtbl->close_connection(protocol);
|
|
|
|
if(protocol->request)
|
|
InternetCloseHandle(protocol->request);
|
|
if(protocol->internet) {
|
|
InternetCloseHandle(protocol->internet);
|
|
protocol->internet = 0;
|
|
}
|
|
|
|
protocol->flags = 0;
|
|
}
|