urlmon: Implement HttpProtocol.
diff --git a/dlls/urlmon/http.c b/dlls/urlmon/http.c
index f761314..c501f05 100644
--- a/dlls/urlmon/http.c
+++ b/dlls/urlmon/http.c
@@ -1,5 +1,6 @@
/*
* Copyright 2005 Jacek Caban
+ * Copyright 2007 Misha Koshelev
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -16,6 +17,12 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
+/*
+ * TODO:
+ * - Handle redirects as native.
+ * - Add support for non-GET requests (e.g., POST).
+ */
+
#include <stdarg.h>
#define COBJMACROS
@@ -25,21 +32,177 @@
#include "winuser.h"
#include "ole2.h"
#include "urlmon.h"
+#include "wininet.h"
#include "urlmon_main.h"
#include "wine/debug.h"
+#include "wine/unicode.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 0x1
+#define FLAG_FIRST_DATA_REPORTED 0x2
+#define FLAG_ALL_DATA_READ 0x4
+#define FLAG_LAST_DATA_REPORTED 0x8
+#define FLAG_RESULT_REPORTED 0x10
+
typedef struct {
const IInternetProtocolVtbl *lpInternetProtocolVtbl;
const IInternetPriorityVtbl *lpInternetPriorityVtbl;
+ DWORD flags;
+ IInternetProtocolSink *protocol_sink;
+ IHttpNegotiate *http_negotiate;
+ HINTERNET internet, connect, request;
+ HANDLE lock;
+ ULONG current_position, content_length, available_bytes;
LONG priority;
LONG ref;
} HttpProtocol;
+/*
+ * Helpers
+ */
+
+static void HTTPPROTOCOL_ReportResult(HttpProtocol *This, HRESULT hres)
+{
+ if (!(This->flags & FLAG_RESULT_REPORTED) &&
+ This->protocol_sink)
+ {
+ This->flags |= FLAG_RESULT_REPORTED;
+ IInternetProtocolSink_ReportResult(This->protocol_sink, hres, 0, NULL);
+ }
+}
+
+static void HTTPPROTOCOL_ReportData(HttpProtocol *This)
+{
+ DWORD bscf;
+ if (!(This->flags & FLAG_LAST_DATA_REPORTED) &&
+ This->protocol_sink)
+ {
+ if (This->flags & FLAG_FIRST_DATA_REPORTED)
+ {
+ bscf = BSCF_INTERMEDIATEDATANOTIFICATION;
+ }
+ else
+ {
+ This->flags |= FLAG_FIRST_DATA_REPORTED;
+ bscf = BSCF_FIRSTDATANOTIFICATION;
+ }
+ if (This->flags & FLAG_ALL_DATA_READ &&
+ !(This->flags & FLAG_LAST_DATA_REPORTED))
+ {
+ This->flags |= FLAG_LAST_DATA_REPORTED;
+ bscf |= BSCF_LASTDATANOTIFICATION;
+ }
+ IInternetProtocolSink_ReportData(This->protocol_sink, bscf,
+ This->current_position+This->available_bytes,
+ This->content_length);
+ }
+}
+
+static void HTTPPROTOCOL_AllDataRead(HttpProtocol *This)
+{
+ if (!(This->flags & FLAG_ALL_DATA_READ))
+ This->flags |= FLAG_ALL_DATA_READ;
+ HTTPPROTOCOL_ReportData(This);
+ HTTPPROTOCOL_ReportResult(This, S_OK);
+}
+
+static void HTTPPROTOCOL_Close(HttpProtocol *This)
+{
+ if (This->protocol_sink)
+ {
+ IInternetProtocolSink_Release(This->protocol_sink);
+ This->protocol_sink = 0;
+ }
+ if (This->http_negotiate)
+ {
+ IHttpNegotiate_Release(This->http_negotiate);
+ This->http_negotiate = 0;
+ }
+ if (This->request)
+ InternetCloseHandle(This->request);
+ CloseHandle(This->connect);
+ CloseHandle(This->internet);
+ This->request = This->connect = This->internet = 0;
+ This->flags = 0;
+}
+
+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->flags |= FLAG_REQUEST_COMPLETE;
+ /* PROTOCOLDATA same as native */
+ memset(&data, 0, sizeof(data));
+ data.dwState = 0xf1000000;
+ data.pData = (LPVOID)BINDSTATUS_DOWNLOADINGDATA;
+ IInternetProtocolSink_Switch(This->protocol_sink, &data);
+ return;
+ default:
+ WARN("Unhandled Internet status callback %d\n", dwInternetStatus);
+ return;
+ }
+
+ IInternetProtocolSink_ReportProgress(This->protocol_sink, ulStatusCode, (LPWSTR)lpvStatusInformation);
+}
+
+static inline LPWSTR strndupW(LPWSTR string, int len)
+{
+ LPWSTR ret = HeapAlloc(GetProcessHeap(), 0, (len+1)*sizeof(WCHAR));
+ if (ret)
+ {
+ memcpy(ret, string, len*sizeof(WCHAR));
+ ret[len] = 0;
+ }
+ return ret;
+}
+
+/*
+ * Interface implementations
+ */
+
#define PROTOCOL(x) ((IInternetProtocol*) &(x)->lpInternetProtocolVtbl)
#define PRIORITY(x) ((IInternetPriority*) &(x)->lpInternetPriorityVtbl)
@@ -89,6 +252,7 @@
TRACE("(%p) ref=%d\n", This, ref);
if(!ref) {
+ HTTPPROTOCOL_Close(This);
HeapFree(GetProcessHeap(), 0, This);
URLMON_UnlockModule();
@@ -102,16 +266,310 @@
DWORD grfPI, DWORD dwReserved)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
- FIXME("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
+ URL_COMPONENTSW url;
+ BINDINFO bindinfo;
+ DWORD grfBINDF = 0, len = 0;
+ ULONG num = 0;
+ IServiceProvider *service_provider = 0;
+ IHttpNegotiate2 *http_negotiate2 = 0;
+ LPWSTR host = 0, path = 0, user = 0, pass = 0, addl_header = 0;
+ BYTE security_id[512];
+ LPOLESTR user_agent, accept_mimes[257];
+ HRESULT hres;
+
+ static const WCHAR wszHttp[] = {'h','t','t','p',':'};
+ static const WCHAR wszHeaders[] = {'A','c','c','e','p','t','-','E','n','c','o','d','i','n','g',
+ ':',' ','g','z','i','p',',',' ','d','e','f','l','a','t','e',0};
+
+ TRACE("(%p)->(%s %p %p %08x %d)\n", This, debugstr_w(szUrl), pOIProtSink,
pOIBindInfo, grfPI, dwReserved);
- return E_NOTIMPL;
+
+ memset(&bindinfo, 0, sizeof(bindinfo));
+ bindinfo.cbSize = sizeof(BINDINFO);
+ hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
+ if (hres != S_OK)
+ {
+ WARN("GetBindInfo failed: %08x\n", hres);
+ goto done;
+ }
+
+ if (lstrlenW(szUrl) < sizeof(wszHttp)/sizeof(WCHAR)
+ || memcmp(szUrl, wszHttp, sizeof(wszHttp)))
+ {
+ hres = MK_E_SYNTAX;
+ goto done;
+ }
+
+ memset(&url, 0, sizeof(url));
+ url.dwStructSize = sizeof(url);
+ url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength =
+ url.dwPasswordLength = 1;
+ if (!InternetCrackUrlW(szUrl, 0, ICU_ESCAPE, &url))
+ {
+ hres = MK_E_SYNTAX;
+ goto done;
+ }
+ host = strndupW(url.lpszHostName, url.dwHostNameLength);
+ path = strndupW(url.lpszUrlPath, url.dwUrlPathLength);
+ user = strndupW(url.lpszUserName, url.dwUserNameLength);
+ pass = strndupW(url.lpszPassword, url.dwPasswordLength);
+ if (!url.nPort)
+ url.nPort = INTERNET_DEFAULT_HTTP_PORT;
+
+ 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 = HeapAlloc(GetProcessHeap(), 0, 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*sizeof(WCHAR));
+ }
+ HeapFree(GetProcessHeap(), 0, user_agenta);
+ }
+
+ This->internet = InternetOpenW(user_agent, 0, NULL, NULL, INTERNET_FLAG_ASYNC);
+ if (!This->internet)
+ {
+ WARN("InternetOpen failed: %d\n", GetLastError());
+ hres = INET_E_NO_SESSION;
+ goto done;
+ }
+
+ IInternetProtocolSink_AddRef(pOIProtSink);
+ This->protocol_sink = pOIProtSink;
+
+ /* Native does not check for success of next call, so we won't either */
+ InternetSetStatusCallbackW(This->internet, HTTPPROTOCOL_InternetStatusCallback);
+
+ This->connect = InternetConnectW(This->internet, host, url.nPort, user,
+ pass, INTERNET_SERVICE_HTTP, 0, (DWORD)This);
+ if (!This->connect)
+ {
+ 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;
+
+ This->request = HttpOpenRequestW(This->connect, NULL, path, NULL, NULL,
+ (LPCWSTR *)accept_mimes, 0, (DWORD)This);
+ if (!This->request)
+ {
+ WARN("HttpOpenRequest failed: %d\n", GetLastError());
+ hres = INET_E_RESOURCE_NOT_FOUND;
+ goto done;
+ }
+
+ hres = IInternetProtocolSink_QueryInterface(pOIProtSink, &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;
+ }
+
+ 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 (!HttpSendRequestW(This->request, wszHeaders, lstrlenW(wszHeaders), NULL, 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(pOIProtSink, hres, 0, NULL);
+ HTTPPROTOCOL_Close(This);
+ }
+
+ 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);
+
+ HeapFree(GetProcessHeap(), 0, pass);
+ HeapFree(GetProcessHeap(), 0, user);
+ HeapFree(GetProcessHeap(), 0, path);
+ HeapFree(GetProcessHeap(), 0, host);
+
+ ReleaseBindInfo(&bindinfo);
+
+ return hres;
}
static HRESULT WINAPI HttpProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
- FIXME("(%p)->(%p)\n", This, pProtocolData);
- return E_NOTIMPL;
+ DWORD len = sizeof(DWORD), status_code;
+ LPWSTR response_headers = 0, content_type = 0, content_length = 0;
+
+ static const WCHAR wszDefaultContentType[] =
+ {'t','e','x','t','/','h','t','m','l',0};
+
+ TRACE("(%p)->(%p)\n", This, pProtocolData);
+
+ if (!pProtocolData)
+ WARN("Expected pProtocolData to be non-NULL\n");
+ else if (!This->request)
+ WARN("Expected request to be non-NULL\n");
+ else if (!This->http_negotiate)
+ WARN("Expected IHttpNegotiate pointer to be non-NULL\n");
+ else if (!This->protocol_sink)
+ WARN("Expected IInternetProtocolSink pointer to be non-NULL\n");
+ else if (pProtocolData->pData == (LPVOID)BINDSTATUS_DOWNLOADINGDATA)
+ {
+ if (!HttpQueryInfoW(This->request, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
+ &status_code, &len, NULL))
+ {
+ WARN("HttpQueryInfo failed: %d\n", GetLastError());
+ }
+ else
+ {
+ len = 0;
+ if ((!HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
+ NULL) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+ !(response_headers = HeapAlloc(GetProcessHeap(), 0, len)) ||
+ !HttpQueryInfoW(This->request, HTTP_QUERY_RAW_HEADERS_CRLF, response_headers, &len,
+ NULL))
+ {
+ WARN("HttpQueryInfo failed: %d\n", GetLastError());
+ }
+ else
+ {
+ HRESULT hres = IHttpNegotiate_OnResponse(This->http_negotiate, status_code,
+ response_headers, NULL, NULL);
+ if (hres != S_OK)
+ {
+ WARN("IHttpNegotiate_OnResponse failed: %08x\n", hres);
+ goto done;
+ }
+ }
+ }
+
+ len = 0;
+ if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+ !(content_type = HeapAlloc(GetProcessHeap(), 0, len)) ||
+ !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_TYPE, content_type, &len, NULL))
+ {
+ WARN("HttpQueryInfo failed: %d\n", GetLastError());
+ IInternetProtocolSink_ReportProgress(This->protocol_sink,
+ BINDSTATUS_MIMETYPEAVAILABLE,
+ wszDefaultContentType);
+ }
+ else
+ {
+ IInternetProtocolSink_ReportProgress(This->protocol_sink,
+ BINDSTATUS_MIMETYPEAVAILABLE,
+ content_type);
+ }
+
+ len = 0;
+ if ((!HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL) &&
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) ||
+ !(content_length = HeapAlloc(GetProcessHeap(), 0, len)) ||
+ !HttpQueryInfoW(This->request, HTTP_QUERY_CONTENT_LENGTH, content_length, &len, NULL))
+ {
+ WARN("HttpQueryInfo failed: %d\n", GetLastError());
+ This->content_length = 0;
+ }
+ else
+ {
+ This->content_length = atoiW(content_length);
+ }
+
+ if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
+ {
+ WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
+ HTTPPROTOCOL_ReportResult(This, INET_E_DATA_NOT_AVAILABLE);
+ }
+ else
+ {
+ HTTPPROTOCOL_ReportData(This);
+ }
+ }
+
+done:
+ HeapFree(GetProcessHeap(), 0, response_headers);
+ HeapFree(GetProcessHeap(), 0, content_type);
+ HeapFree(GetProcessHeap(), 0, content_length);
+
+ /* Returns S_OK on native */
+ return S_OK;
}
static HRESULT WINAPI HttpProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
@@ -147,8 +605,66 @@
ULONG cb, ULONG *pcbRead)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
- FIXME("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
- return E_NOTIMPL;
+ ULONG read = 0, len = 0;
+ HRESULT hres = S_FALSE;
+
+ TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
+
+ if (!(This->flags & FLAG_REQUEST_COMPLETE))
+ {
+ hres = E_PENDING;
+ }
+ else while (!(This->flags & FLAG_ALL_DATA_READ) &&
+ read < cb)
+ {
+ if (This->available_bytes == 0)
+ {
+ if (!InternetQueryDataAvailable(This->request, &This->available_bytes, 0, 0))
+ {
+ WARN("InternetQueryDataAvailable failed: %d\n", GetLastError());
+ hres = INET_E_DATA_NOT_AVAILABLE;
+ HTTPPROTOCOL_ReportResult(This, hres);
+ goto done;
+ }
+ else if (This->available_bytes == 0)
+ {
+ HTTPPROTOCOL_AllDataRead(This);
+ }
+ }
+ else
+ {
+ if (!InternetReadFile(This->request, ((BYTE *)pv)+read,
+ This->available_bytes > cb-read ?
+ cb-read : This->available_bytes, &len))
+ {
+ WARN("InternetReadFile failed: %d\n", GetLastError());
+ hres = INET_E_DOWNLOAD_FAILURE;
+ HTTPPROTOCOL_ReportResult(This, hres);
+ goto done;
+ }
+ else if (len == 0)
+ {
+ HTTPPROTOCOL_AllDataRead(This);
+ }
+ else
+ {
+ read += len;
+ This->current_position += len;
+ This->available_bytes -= len;
+ }
+ }
+ }
+
+ /* Per MSDN this should be if (read == cb), but native returns S_OK
+ * if any bytes were read, so we will too */
+ if (read)
+ hres = S_OK;
+
+done:
+ if (pcbRead)
+ *pcbRead = read;
+
+ return hres;
}
static HRESULT WINAPI HttpProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
@@ -162,15 +678,29 @@
static HRESULT WINAPI HttpProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
- FIXME("(%p)->(%08x)\n", This, dwOptions);
- return E_NOTIMPL;
+
+ TRACE("(%p)->(%08x)\n", This, dwOptions);
+
+ if (!InternetLockRequestFile(This->request, &This->lock))
+ WARN("InternetLockRequest failed: %d\n", GetLastError());
+
+ return S_OK;
}
static HRESULT WINAPI HttpProtocol_UnlockRequest(IInternetProtocol *iface)
{
HttpProtocol *This = PROTOCOL_THIS(iface);
- FIXME("(%p)\n", This);
- return E_NOTIMPL;
+
+ TRACE("(%p)\n", This);
+
+ if (This->lock)
+ {
+ if (!InternetUnlockRequestFile(This->lock))
+ WARN("InternetUnlockRequest failed: %d\n", GetLastError());
+ This->lock = 0;
+ }
+
+ return S_OK;
}
#undef PROTOCOL_THIS
@@ -253,10 +783,14 @@
ret->lpInternetProtocolVtbl = &HttpProtocolVtbl;
ret->lpInternetPriorityVtbl = &HttpPriorityVtbl;
-
- ret->ref = 1;
-
+ ret->flags = 0;
+ ret->protocol_sink = 0;
+ ret->http_negotiate = 0;
+ ret->internet = ret->connect = ret->request = 0;
+ ret->lock = 0;
+ ret->current_position = ret->content_length = ret->available_bytes = 0;
ret->priority = 0;
+ ret->ref = 1;
*ppobj = PROTOCOL(ret);
diff --git a/dlls/urlmon/tests/protocol.c b/dlls/urlmon/tests/protocol.c
index 2f46b5d..366a543 100644
--- a/dlls/urlmon/tests/protocol.c
+++ b/dlls/urlmon/tests/protocol.c
@@ -1057,9 +1057,7 @@
SET_EXPECT(GetRootSecurityId);
hres = IInternetProtocol_Start(http_protocol, url, &protocol_sink, &bind_info, 0, 0);
- todo_wine {
- ok(hres == S_OK, "Start failed: %08x\n", hres);
- }
+ ok(hres == S_OK, "Start failed: %08x\n", hres);
if(FAILED(hres))
return FALSE;