urlmon: Move HttpProtocol::Start implementation to generic Protocol object.

This commit is contained in:
Jacek Caban 2009-03-02 03:21:07 +01:00 committed by Alexandre Julliard
parent c2ffe97779
commit a4ba18a50d
3 changed files with 316 additions and 355 deletions

View File

@ -29,48 +29,17 @@
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 0x1
#define FLAG_FIRST_CONTINUE_COMPLETE 0x2
#define FLAG_FIRST_DATA_REPORTED 0x4
#define FLAG_ALL_DATA_READ 0x8
#define FLAG_LAST_DATA_REPORTED 0x10
#define FLAG_RESULT_REPORTED 0x20
typedef struct {
Protocol base;
const IInternetProtocolVtbl *lpInternetProtocolVtbl;
const IInternetPriorityVtbl *lpInternetPriorityVtbl;
LONG ref;
BOOL https;
IHttpNegotiate *http_negotiate;
LPWSTR full_header;
HINTERNET connection;
LONG ref;
} HttpProtocol;
#define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
@ -102,6 +71,153 @@ static LPWSTR query_http_info(HttpProtocol *This, DWORD option)
#define ASYNCPROTOCOL_THIS(iface) DEFINE_THIS2(HttpProtocol, base, iface)
static HRESULT HttpProtocol_open_request(Protocol *prot, LPCWSTR url, DWORD request_flags,
IInternetBindInfo *bind_info)
{
HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
LPWSTR addl_header = NULL, post_cookie = NULL, optional = NULL;
IServiceProvider *service_provider = NULL;
IHttpNegotiate2 *http_negotiate2 = NULL;
LPWSTR host, user, pass, path;
LPOLESTR accept_mimes[257];
URL_COMPONENTSW url_comp;
BYTE security_id[512];
DWORD len = 0;
ULONG num = 0;
BOOL res;
HRESULT hres;
static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
{{'G','E','T',0},
{'P','O','S','T',0},
{'P','U','T',0}};
memset(&url_comp, 0, sizeof(url_comp));
url_comp.dwStructSize = sizeof(url_comp);
url_comp.dwSchemeLength = url_comp.dwHostNameLength = url_comp.dwUrlPathLength =
url_comp.dwUserNameLength = url_comp.dwPasswordLength = 1;
if (!InternetCrackUrlW(url, 0, 0, &url_comp))
return MK_E_SYNTAX;
if(!url_comp.nPort)
url_comp.nPort = This->https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
host = heap_strndupW(url_comp.lpszHostName, url_comp.dwHostNameLength);
user = heap_strndupW(url_comp.lpszUserName, url_comp.dwUserNameLength);
pass = heap_strndupW(url_comp.lpszPassword, url_comp.dwPasswordLength);
This->base.connection = InternetConnectW(This->base.internet, host, url_comp.nPort, user, pass,
INTERNET_SERVICE_HTTP, This->https ? INTERNET_FLAG_SECURE : 0, (DWORD_PTR)&This->base);
heap_free(pass);
heap_free(user);
heap_free(host);
if(!This->base.connection) {
WARN("InternetConnect failed: %d\n", GetLastError());
return INET_E_CANNOT_CONNECT;
}
num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_ACCEPT_MIMES, accept_mimes, num, &num);
if(hres != S_OK) {
WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
return INET_E_NO_VALID_MEDIA;
}
accept_mimes[num] = 0;
path = heap_strndupW(url_comp.lpszUrlPath, url_comp.dwUrlPathLength);
if(This->https)
request_flags |= INTERNET_FLAG_SECURE;
This->base.request = HttpOpenRequestW(This->base.connection,
This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM
? wszBindVerb[This->base.bind_info.dwBindVerb] : This->base.bind_info.szCustomVerb,
path, NULL, NULL, (LPCWSTR *)accept_mimes, request_flags, (DWORD_PTR)&This->base);
heap_free(path);
while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) && accept_mimes[num])
CoTaskMemFree(accept_mimes[num++]);
if (!This->base.request) {
WARN("HttpOpenRequest failed: %d\n", GetLastError());
return INET_E_RESOURCE_NOT_FOUND;
}
hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
(void **)&service_provider);
if (hres != S_OK) {
WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
return hres;
}
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
&IID_IHttpNegotiate, (void **)&This->http_negotiate);
if (hres != S_OK) {
WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
return hres;
}
hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, url, wszHeaders,
0, &addl_header);
if(hres != S_OK) {
WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
IServiceProvider_Release(service_provider);
return hres;
}
if(addl_header) {
int len_addl_header = strlenW(addl_header);
This->full_header = heap_alloc(len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
lstrcpyW(This->full_header, addl_header);
lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
CoTaskMemFree(addl_header);
}else {
This->full_header = (LPWSTR)wszHeaders;
}
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
&IID_IHttpNegotiate2, (void **)&http_negotiate2);
IServiceProvider_Release(service_provider);
if(hres != S_OK) {
WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
/* No goto done as per native */
}else {
len = sizeof(security_id)/sizeof(security_id[0]);
hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
IHttpNegotiate2_Release(http_negotiate2);
if (hres != S_OK)
WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
}
/* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
if(This->base.bind_info.dwBindVerb == BINDVERB_POST) {
num = 0;
hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_POST_COOKIE, &post_cookie, 1, &num);
if(hres == S_OK && num) {
if(!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
post_cookie, lstrlenW(post_cookie)))
WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n", GetLastError());
CoTaskMemFree(post_cookie);
}
}
if(This->base.bind_info.dwBindVerb != BINDVERB_GET) {
/* Native does not use GlobalLock/GlobalUnlock, so we won't either */
if (This->base.bind_info.stgmedData.tymed != TYMED_HGLOBAL)
WARN("Expected This->base.bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
This->base.bind_info.stgmedData.tymed);
else
optional = (LPWSTR)This->base.bind_info.stgmedData.u.hGlobal;
}
res = HttpSendRequestW(This->base.request, This->full_header, lstrlenW(This->full_header),
optional, optional ? This->base.bind_info.cbstgmedData : 0);
if(!res && GetLastError() != ERROR_IO_PENDING) {
WARN("HttpSendRequest failed: %d\n", GetLastError());
return INET_E_DOWNLOAD_FAILURE;
}
return S_OK;
}
static HRESULT HttpProtocol_start_downloading(Protocol *prot)
{
HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
@ -171,9 +287,6 @@ static void HttpProtocol_close_connection(Protocol *prot)
{
HttpProtocol *This = ASYNCPROTOCOL_THIS(prot);
if(This->connection)
InternetCloseHandle(This->connection);
if(This->http_negotiate) {
IHttpNegotiate_Release(This->http_negotiate);
This->http_negotiate = 0;
@ -189,79 +302,11 @@ static void HttpProtocol_close_connection(Protocol *prot)
#undef ASYNCPROTOCOL_THIS
static const ProtocolVtbl AsyncProtocolVtbl = {
HttpProtocol_open_request,
HttpProtocol_start_downloading,
HttpProtocol_close_connection
};
static void CALLBACK HTTPPROTOCOL_InternetStatusCallback(
HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus,
LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
HttpProtocol *This = (HttpProtocol *)dwContext;
PROTOCOLDATA data;
ULONG ulStatusCode;
switch (dwInternetStatus)
{
case INTERNET_STATUS_RESOLVING_NAME:
ulStatusCode = BINDSTATUS_FINDINGRESOURCE;
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
ulStatusCode = BINDSTATUS_CONNECTING;
break;
case INTERNET_STATUS_SENDING_REQUEST:
ulStatusCode = BINDSTATUS_SENDINGREQUEST;
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
This->base.flags |= FLAG_REQUEST_COMPLETE;
/* PROTOCOLDATA same as native */
memset(&data, 0, sizeof(data));
data.dwState = 0xf1000000;
if (This->base.flags & FLAG_FIRST_CONTINUE_COMPLETE)
data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
else
data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
if (This->base.bindf & BINDF_FROMURLMON)
IInternetProtocolSink_Switch(This->base.protocol_sink, &data);
else
IInternetProtocol_Continue(PROTOCOL(This), &data);
return;
case INTERNET_STATUS_HANDLE_CREATED:
IInternetProtocol_AddRef(PROTOCOL(This));
return;
case INTERNET_STATUS_HANDLE_CLOSING:
if (*(HINTERNET *)lpvStatusInformation == This->connection)
{
This->connection = 0;
}
else if (*(HINTERNET *)lpvStatusInformation == This->base.request)
{
This->base.request = 0;
if (This->base.protocol_sink)
{
IInternetProtocolSink_Release(This->base.protocol_sink);
This->base.protocol_sink = 0;
}
if (This->base.bind_info.cbSize)
{
ReleaseBindInfo(&This->base.bind_info);
memset(&This->base.bind_info, 0, sizeof(This->base.bind_info));
}
}
IInternetProtocol_Release(PROTOCOL(This));
return;
default:
WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
return;
}
IInternetProtocolSink_ReportProgress(This->base.protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
}
/*
* Interface implementations
*/
#define PROTOCOL_THIS(iface) DEFINE_THIS(HttpProtocol, InternetProtocol, iface)
static HRESULT WINAPI HttpProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
@ -322,267 +367,19 @@ static HRESULT WINAPI HttpProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl
DWORD grfPI, DWORD dwReserved)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
URL_COMPONENTSW url;
DWORD len = 0, request_flags = INTERNET_FLAG_KEEP_CONNECTION;
ULONG num = 0;
IServiceProvider *service_provider = 0;
IHttpNegotiate2 *http_negotiate2 = 0;
LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0,
post_cookie = 0, optional = 0;
BYTE security_id[512];
LPOLESTR user_agent = NULL, accept_mimes[257];
HRESULT hres;
static const WCHAR httpW[] = {'h','t','t','p',':'};
static const WCHAR httpsW[] = {'h','t','t','p','s',':'};
static const WCHAR wszBindVerb[BINDVERB_CUSTOM][5] =
{{'G','E','T',0},
{'P','O','S','T',0},
{'P','U','T',0}};
TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
pOIBindInfo, grfPI, dwReserved);
IInternetProtocolSink_AddRef(pOIProtSink);
This->base.protocol_sink = pOIProtSink;
memset(&This->base.bind_info, 0, sizeof(This->base.bind_info));
This->base.bind_info.cbSize = sizeof(BINDINFO);
hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &This->base.bindf, &This->base.bind_info);
if (hres != S_OK)
{
WARN("GetBindInfo failed: %08x\n", hres);
goto done;
}
if(This->https
? strncmpW(szUrl, httpsW, sizeof(httpsW)/sizeof(WCHAR))
: strncmpW(szUrl, httpW, sizeof(httpW)/sizeof(WCHAR)))
{
hres = MK_E_SYNTAX;
goto done;
}
return MK_E_SYNTAX;
memset(&url, 0, sizeof(url));
url.dwStructSize = sizeof(url);
url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
url.dwPasswordLength = 1;
if (!InternetCrackUrlW(szUrl, 0, 0, &url))
{
hres = MK_E_SYNTAX;
goto done;
}
host = heap_strndupW(url.lpszHostName, url.dwHostNameLength);
path = heap_strndupW(url.lpszUrlPath, url.dwUrlPathLength);
user = heap_strndupW(url.lpszUserName, url.dwUserNameLength);
pass = heap_strndupW(url.lpszPassword, url.dwPasswordLength);
if (!url.nPort)
url.nPort = This->https ? INTERNET_DEFAULT_HTTPS_PORT : INTERNET_DEFAULT_HTTP_PORT;
if(!(This->base.bindf & BINDF_FROMURLMON))
IInternetProtocolSink_ReportProgress(This->base.protocol_sink, BINDSTATUS_DIRECTBIND, NULL);
hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_USER_AGENT, &user_agent,
1, &num);
if (hres != S_OK || !num)
{
CHAR null_char = 0;
LPSTR user_agenta = NULL;
len = 0;
if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY)
{
WARN("ObtainUserAgentString failed: %08x\n", hres);
}
else if (!(user_agenta = heap_alloc(len*sizeof(CHAR))))
{
WARN("Out of memory\n");
}
else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK)
{
WARN("ObtainUserAgentString failed: %08x\n", hres);
}
else
{
if (!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
WARN("Out of memory\n");
else
MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len);
}
heap_free(user_agenta);
}
This->base.internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
if (!This->base.internet)
{
WARN("InternetOpen failed: %d\n", GetLastError());
hres = INET_E_NO_SESSION;
goto done;
}
/* Native does not check for success of next call, so we won't either */
InternetSetStatusCallbackW(This->base.internet, HTTPPROTOCOL_InternetStatusCallback);
This->connection = InternetConnectW(This->base.internet, host, url.nPort, user,
pass, INTERNET_SERVICE_HTTP,
This->https ? INTERNET_FLAG_SECURE : 0,
(DWORD_PTR)This);
if (!This->connection)
{
WARN("InternetConnect failed: %d\n", GetLastError());
hres = INET_E_CANNOT_CONNECT;
goto done;
}
num = sizeof(accept_mimes)/sizeof(accept_mimes[0])-1;
hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_ACCEPT_MIMES,
accept_mimes,
num, &num);
if (hres != S_OK)
{
WARN("GetBindString BINDSTRING_ACCEPT_MIMES failed: %08x\n", hres);
hres = INET_E_NO_VALID_MEDIA;
goto done;
}
accept_mimes[num] = 0;
if (This->base.bindf & BINDF_NOWRITECACHE)
request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
if (This->base.bindf & BINDF_NEEDFILE)
request_flags |= INTERNET_FLAG_NEED_FILE;
if (This->https)
request_flags |= INTERNET_FLAG_SECURE;
This->base.request = HttpOpenRequestW(This->connection, This->base.bind_info.dwBindVerb < BINDVERB_CUSTOM ?
wszBindVerb[This->base.bind_info.dwBindVerb] :
This->base.bind_info.szCustomVerb,
path, NULL, NULL, (LPCWSTR *)accept_mimes,
request_flags, (DWORD_PTR)This);
if (!This->base.request)
{
WARN("HttpOpenRequest failed: %d\n", GetLastError());
hres = INET_E_RESOURCE_NOT_FOUND;
goto done;
}
hres = IInternetProtocolSink_QueryInterface(This->base.protocol_sink, &IID_IServiceProvider,
(void **)&service_provider);
if (hres != S_OK)
{
WARN("IInternetProtocolSink_QueryInterface IID_IServiceProvider failed: %08x\n", hres);
goto done;
}
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate,
&IID_IHttpNegotiate, (void **)&This->http_negotiate);
if (hres != S_OK)
{
WARN("IServiceProvider_QueryService IID_IHttpNegotiate failed: %08x\n", hres);
goto done;
}
hres = IHttpNegotiate_BeginningTransaction(This->http_negotiate, szUrl, wszHeaders,
0, &addl_header);
if (hres != S_OK)
{
WARN("IHttpNegotiate_BeginningTransaction failed: %08x\n", hres);
goto done;
}
else if (addl_header == NULL)
{
This->full_header = (LPWSTR)wszHeaders;
}
else
{
int len_addl_header = lstrlenW(addl_header);
This->full_header = heap_alloc(len_addl_header*sizeof(WCHAR)+sizeof(wszHeaders));
if (!This->full_header)
{
WARN("Out of memory\n");
hres = E_OUTOFMEMORY;
goto done;
}
lstrcpyW(This->full_header, addl_header);
lstrcpyW(&This->full_header[len_addl_header], wszHeaders);
}
hres = IServiceProvider_QueryService(service_provider, &IID_IHttpNegotiate2,
&IID_IHttpNegotiate2, (void **)&http_negotiate2);
if (hres != S_OK)
{
WARN("IServiceProvider_QueryService IID_IHttpNegotiate2 failed: %08x\n", hres);
/* No goto done as per native */
}
else
{
len = sizeof(security_id)/sizeof(security_id[0]);
hres = IHttpNegotiate2_GetRootSecurityId(http_negotiate2, security_id, &len, 0);
if (hres != S_OK)
{
WARN("IHttpNegotiate2_GetRootSecurityId failed: %08x\n", hres);
/* No goto done as per native */
}
}
/* FIXME: Handle security_id. Native calls undocumented function IsHostInProxyBypassList. */
if (This->base.bind_info.dwBindVerb == BINDVERB_POST)
{
num = 0;
hres = IInternetBindInfo_GetBindString(pOIBindInfo, BINDSTRING_POST_COOKIE, &post_cookie,
1, &num);
if (hres == S_OK && num &&
!InternetSetOptionW(This->base.request, INTERNET_OPTION_SECONDARY_CACHE_KEY,
post_cookie, lstrlenW(post_cookie)))
{
WARN("InternetSetOption INTERNET_OPTION_SECONDARY_CACHE_KEY failed: %d\n",
GetLastError());
}
}
if (This->base.bind_info.dwBindVerb != BINDVERB_GET)
{
/* Native does not use GlobalLock/GlobalUnlock, so we won't either */
if (This->base.bind_info.stgmedData.tymed != TYMED_HGLOBAL)
WARN("Expected This->bind_info.stgmedData.tymed to be TYMED_HGLOBAL, not %d\n",
This->base.bind_info.stgmedData.tymed);
else
optional = (LPWSTR)This->base.bind_info.stgmedData.u.hGlobal;
}
if (!HttpSendRequestW(This->base.request, This->full_header, lstrlenW(This->full_header),
optional,
optional ? This->base.bind_info.cbstgmedData : 0) &&
GetLastError() != ERROR_IO_PENDING)
{
WARN("HttpSendRequest failed: %d\n", GetLastError());
hres = INET_E_DOWNLOAD_FAILURE;
goto done;
}
hres = S_OK;
done:
if (hres != S_OK)
{
IInternetProtocolSink_ReportResult(This->base.protocol_sink, hres, 0, NULL);
protocol_close_connection(&This->base);
}
CoTaskMemFree(post_cookie);
CoTaskMemFree(addl_header);
if (http_negotiate2)
IHttpNegotiate2_Release(http_negotiate2);
if (service_provider)
IServiceProvider_Release(service_provider);
while (num<sizeof(accept_mimes)/sizeof(accept_mimes[0]) &&
accept_mimes[num])
CoTaskMemFree(accept_mimes[num++]);
CoTaskMemFree(user_agent);
heap_free(pass);
heap_free(user);
heap_free(path);
heap_free(host);
return hres;
return protocol_start(&This->base, PROTOCOL(This), szUrl, pOIProtSink, pOIBindInfo);
}
static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)

View File

@ -100,6 +100,163 @@ static void all_data_read(Protocol *protocol)
report_result(protocol, S_OK);
}
static void request_complete(Protocol *protocol, INTERNET_ASYNC_RESULT *ar)
{
PROTOCOLDATA data;
if(!ar->dwResult) {
WARN("request failed: %d\n", ar->dwError);
return;
}
protocol->flags |= FLAG_REQUEST_COMPLETE;
if(!protocol->request) {
TRACE("setting request handle %p\n", (HINTERNET)ar->dwResult);
protocol->request = (HINTERNET)ar->dwResult;
}
/* PROTOCOLDATA same as native */
memset(&data, 0, sizeof(data));
data.dwState = 0xf1000000;
if(protocol->flags & FLAG_FIRST_CONTINUE_COMPLETE)
data.pData = (LPVOID)BINDSTATUS_ENDDOWNLOADCOMPONENTS;
else
data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
if (protocol->bindf & BINDF_FROMURLMON)
IInternetProtocolSink_Switch(protocol->protocol_sink, &data);
else
protocol_continue(protocol, &data);
}
static void WINAPI internet_status_callback(HINTERNET internet, DWORD_PTR context,
DWORD internet_status, LPVOID status_info, DWORD status_info_len)
{
Protocol *protocol = (Protocol*)context;
switch(internet_status) {
case INTERNET_STATUS_RESOLVING_NAME:
TRACE("%p INTERNET_STATUS_RESOLVING_NAME\n", protocol);
report_progress(protocol, BINDSTATUS_FINDINGRESOURCE, (LPWSTR)status_info);
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
TRACE("%p INTERNET_STATUS_CONNECTING_TO_SERVER\n", protocol);
report_progress(protocol, BINDSTATUS_CONNECTING, (LPWSTR)status_info);
break;
case INTERNET_STATUS_SENDING_REQUEST:
TRACE("%p INTERNET_STATUS_SENDING_REQUEST\n", protocol);
report_progress(protocol, BINDSTATUS_SENDINGREQUEST, (LPWSTR)status_info);
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
request_complete(protocol, status_info);
break;
case INTERNET_STATUS_HANDLE_CREATED:
TRACE("%p INTERNET_STATUS_HANDLE_CREATED\n", protocol);
IInternetProtocol_AddRef(protocol->protocol);
break;
case INTERNET_STATUS_HANDLE_CLOSING:
TRACE("%p INTERNET_STATUS_HANDLE_CLOSING\n", protocol);
if(*(HINTERNET *)status_info == protocol->request) {
protocol->request = NULL;
if(protocol->protocol_sink) {
IInternetProtocolSink_Release(protocol->protocol_sink);
protocol->protocol_sink = NULL;
}
if(protocol->bind_info.cbSize) {
ReleaseBindInfo(&protocol->bind_info);
memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
}
}else if(*(HINTERNET *)status_info == protocol->connection) {
protocol->connection = NULL;
}
IInternetProtocol_Release(protocol->protocol);
break;
default:
WARN("Unhandled Internet status callback %d\n", internet_status);
}
}
HRESULT protocol_start(Protocol *protocol, IInternetProtocol *prot, LPCWSTR url,
IInternetProtocolSink *protocol_sink, IInternetBindInfo *bind_info)
{
LPOLESTR user_agent = NULL;
DWORD request_flags;
ULONG size = 0;
HRESULT hres;
protocol->protocol = prot;
IInternetProtocolSink_AddRef(protocol_sink);
protocol->protocol_sink = protocol_sink;
memset(&protocol->bind_info, 0, sizeof(protocol->bind_info));
protocol->bind_info.cbSize = sizeof(BINDINFO);
hres = IInternetBindInfo_GetBindInfo(bind_info, &protocol->bindf, &protocol->bind_info);
if(hres != S_OK) {
WARN("GetBindInfo failed: %08x\n", hres);
return report_result(protocol, hres);
}
if(!(protocol->bindf & BINDF_FROMURLMON))
report_progress(protocol, BINDSTATUS_DIRECTBIND, NULL);
hres = IInternetBindInfo_GetBindString(bind_info, BINDSTRING_USER_AGENT, &user_agent, 1, &size);
if (hres != S_OK || !size) {
DWORD len;
CHAR null_char = 0;
LPSTR user_agenta = NULL;
len = 0;
if ((hres = ObtainUserAgentString(0, &null_char, &len)) != E_OUTOFMEMORY) {
WARN("ObtainUserAgentString failed: %08x\n", hres);
}else if (!(user_agenta = heap_alloc(len*sizeof(CHAR)))) {
WARN("Out of memory\n");
}else if ((hres = ObtainUserAgentString(0, user_agenta, &len)) != S_OK) {
WARN("ObtainUserAgentString failed: %08x\n", hres);
}else {
if(!(user_agent = CoTaskMemAlloc((len)*sizeof(WCHAR))))
WARN("Out of memory\n");
else
MultiByteToWideChar(CP_ACP, 0, user_agenta, -1, user_agent, len);
}
heap_free(user_agenta);
}
protocol->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
CoTaskMemFree(user_agent);
if(!protocol->internet) {
WARN("InternetOpen failed: %d\n", GetLastError());
return report_result(protocol, INET_E_NO_SESSION);
}
/* Native does not check for success of next call, so we won't either */
InternetSetStatusCallbackW(protocol->internet, internet_status_callback);
request_flags = INTERNET_FLAG_KEEP_CONNECTION;
if(protocol->bindf & BINDF_NOWRITECACHE)
request_flags |= INTERNET_FLAG_NO_CACHE_WRITE;
if(protocol->bindf & BINDF_NEEDFILE)
request_flags |= INTERNET_FLAG_NEED_FILE;
hres = protocol->vtbl->open_request(protocol, url, request_flags, bind_info);
if(FAILED(hres)) {
protocol_close_connection(protocol);
return report_result(protocol, hres);
}
return S_OK;
}
HRESULT protocol_continue(Protocol *protocol, PROTOCOLDATA *data)
{
HRESULT hres;
@ -260,6 +417,10 @@ void protocol_close_connection(Protocol *protocol)
if(protocol->request)
InternetCloseHandle(protocol->request);
if(protocol->connection)
InternetCloseHandle(protocol->connection);
if(protocol->internet) {
InternetCloseHandle(protocol->internet);
protocol->internet = 0;

View File

@ -92,6 +92,7 @@ typedef struct {
HINTERNET internet;
HINTERNET request;
HINTERNET connection;
DWORD flags;
HANDLE lock;
@ -103,10 +104,12 @@ typedef struct {
} Protocol;
struct ProtocolVtbl {
HRESULT (*open_request)(Protocol*,LPCWSTR,DWORD,IInternetBindInfo*);
HRESULT (*start_downloading)(Protocol*);
void (*close_connection)(Protocol*);
};
HRESULT protocol_start(Protocol*,IInternetProtocol*,LPCWSTR,IInternetProtocolSink*,IInternetBindInfo*);
HRESULT protocol_continue(Protocol*,PROTOCOLDATA*);
HRESULT protocol_read(Protocol*,void*,ULONG,ULONG*);
HRESULT protocol_lock_request(Protocol*);