diff --git a/dlls/urlmon/Makefile.in b/dlls/urlmon/Makefile.in index f74e05680aa..9b1d4a52f05 100644 --- a/dlls/urlmon/Makefile.in +++ b/dlls/urlmon/Makefile.in @@ -10,6 +10,7 @@ C_SRCS = \ regsvr.c \ sec_mgr.c \ umon.c \ + umstream.c \ urlmon_main.c SUBDIRS = tests diff --git a/dlls/urlmon/umon.c b/dlls/urlmon/umon.c index 284d9e99d92..c1f3e6b58e3 100644 --- a/dlls/urlmon/umon.c +++ b/dlls/urlmon/umon.c @@ -61,6 +61,10 @@ typedef struct URLMonikerImpl{ HWND hwndCallback; IBindCtx *pBC; HINTERNET hinternet, hconnect, hrequest; + HANDLE hCacheFile; + IUMCacheStream *pstrCache; + IBindStatusCallback *pbscb; + DWORD total_read, expected_size; } URLMonikerImpl; /******************************************************************************* @@ -126,6 +130,15 @@ static ULONG WINAPI URLMonikerImpl_Release(IMoniker* iface) if (!refCount) { HeapFree(GetProcessHeap(),0,This->URLName); HeapFree(GetProcessHeap(),0,This); + if (This->hCacheFile) + CloseHandle(This->hCacheFile); + if (This->pstrCache) + { + UMCloseCacheFileStream(This->pstrCache); + IStream_Release((IStream *)This->pstrCache); + } + if (This->pbscb) + IBindStatusCallback_Release(This->pbscb); } URLMON_UnlockModule(); @@ -133,6 +146,105 @@ static ULONG WINAPI URLMonikerImpl_Release(IMoniker* iface) return refCount; } +static void URLMonikerImpl_CloseCacheDownload(URLMonikerImpl *This) +{ + CloseHandle(This->hCacheFile); + This->hCacheFile = 0; + UMCloseCacheFileStream(This->pstrCache); + IStream_Release((IStream *)This->pstrCache); + This->pstrCache = 0; +} + +static HRESULT URLMonikerImpl_MoreCacheData(URLMonikerImpl *This, char *buf, DWORD dwBytes) +{ + DWORD written; + + if (WriteFile(This->hCacheFile, buf, dwBytes, &written, NULL) && written == dwBytes) + { + HRESULT hr; + + This->total_read += written; + hr = IBindStatusCallback_OnProgress(This->pbscb, + This->total_read + written, + This->expected_size, + (This->total_read == written) ? + BINDSTATUS_BEGINDOWNLOADDATA : + BINDSTATUS_DOWNLOADINGDATA, + NULL); + if (!hr) + { + STGMEDIUM stg; + FORMATETC fmt; + + fmt.cfFormat = 0; + fmt.ptd = NULL; + fmt.dwAspect = 0; + fmt.lindex = -1; + fmt.tymed = TYMED_ISTREAM; + + stg.tymed = TYMED_ISTREAM; + stg.u.pstm = (IStream *)This->pstrCache; + stg.pUnkForRelease = NULL; + + hr = IBindStatusCallback_OnDataAvailable(This->pbscb, + (This->total_read == written) ? + BSCF_FIRSTDATANOTIFICATION : + BSCF_INTERMEDIATEDATANOTIFICATION, + This->total_read + written, + &fmt, + &stg); + } + if (written < dwBytes) + return STG_E_MEDIUMFULL; + else + return hr; + } + return HRESULT_FROM_WIN32(GetLastError()); +} + +static void URLMonikerImpl_FinishedDownload(URLMonikerImpl *This, HRESULT hr) +{ + STGMEDIUM stg; + FORMATETC fmt; + + fmt.ptd = NULL; + fmt.dwAspect = 0; + fmt.lindex = -1; + fmt.tymed = TYMED_ISTREAM; + + stg.tymed = TYMED_ISTREAM; + stg.u.pstm = (IStream *)This->pstrCache; + stg.pUnkForRelease = NULL; + + IBindStatusCallback_OnProgress(This->pbscb, This->total_read, This->expected_size, BINDSTATUS_ENDDOWNLOADDATA, NULL); + IBindStatusCallback_OnDataAvailable(This->pbscb, BSCF_LASTDATANOTIFICATION, This->total_read, &fmt, &stg); + if (hr) + { + WCHAR *pwchError = 0; + + FormatMessageW (FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER, + NULL, (DWORD) hr, + 0, (LPWSTR) &pwchError, + 0, NULL); + if (!pwchError) + { + static WCHAR achFormat[] = { '%', '0', '8', 'x', 0 }; + + pwchError =(WCHAR *) LocalAlloc(LMEM_FIXED, sizeof(WCHAR) * 9); + wsprintfW(pwchError, achFormat, hr); + } + IBindStatusCallback_OnStopBinding(This->pbscb, hr, pwchError); + LocalFree(pwchError); + } + else + { + IBindStatusCallback_OnStopBinding(This->pbscb, hr, NULL); + } + IBindStatusCallback_Release(This->pbscb); + This->pbscb = 0; +} + /****************************************************************************** * URLMoniker_GetClassID ******************************************************************************/ @@ -310,6 +422,8 @@ static void CALLBACK URLMON_InternetCallback(HINTERNET hinet, /*DWORD_PTR*/ DWOR return; } #endif + + /****************************************************************************** * URLMoniker_BindToStorage ******************************************************************************/ @@ -321,12 +435,10 @@ static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface, { URLMonikerImpl *This = (URLMonikerImpl *)iface; HRESULT hres; - IBindStatusCallback *pbscb; BINDINFO bi; DWORD bindf; - IStream *pstr; + WCHAR szFileName[MAX_PATH + 1]; - FIXME("(%p)->(%p,%p,%s,%p): stub\n",This,pbc,pmkToLeft,debugstr_guid(riid),ppvObject); if(pmkToLeft) { FIXME("pmkToLeft != NULL\n"); return E_NOTIMPL; @@ -336,122 +448,277 @@ static HRESULT WINAPI URLMonikerImpl_BindToStorage(IMoniker* iface, return E_NOTIMPL; } - /* FIXME This is a bad hack (tm). We should clearly download to a temporary file. - We also need to implement IStream ourselves so that IStream_Read can return - E_PENDING */ - - hres = CreateStreamOnHGlobal(0, TRUE, &pstr); + hres = UMCreateStreamOnCacheFile(This->URLName, 0, szFileName, &This->hCacheFile, &This->pstrCache); if(SUCCEEDED(hres)) { - TRACE("Created dummy stream...\n"); + TRACE("Created stream...\n"); - hres = IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown**)&pbscb); + *ppvObject = (void *) This->pstrCache; + IStream_AddRef((IStream *) This->pstrCache); + + hres = IBindCtx_GetObjectParam(pbc, (LPOLESTR)BSCBHolder, (IUnknown**)&This->pbscb); if(SUCCEEDED(hres)) { TRACE("Got IBindStatusCallback...\n"); memset(&bi, 0, sizeof(bi)); bi.cbSize = sizeof(bi); bindf = 0; - hres = IBindStatusCallback_GetBindInfo(pbscb, &bindf, &bi); + hres = IBindStatusCallback_GetBindInfo(This->pbscb, &bindf, &bi); if(SUCCEEDED(hres)) { + WCHAR *urlcopy, *tmpwc; URL_COMPONENTSW url; - WCHAR *host, *path; - DWORD len, lensz = sizeof(len), total_read = 0; - LARGE_INTEGER last_read_pos; - FORMATETC fmt; - STGMEDIUM stg; + WCHAR *host, *path, *user, *pass; + DWORD lensz = sizeof(This->expected_size); + DWORD dwService = 0; + BOOL bSuccess; TRACE("got bindinfo. bindf = %08lx extrainfo = %s bindinfof = %08lx bindverb = %08lx iid %s\n", bindf, debugstr_w(bi.szExtraInfo), bi.grfBindInfoF, bi.dwBindVerb, debugstr_guid(&bi.iid)); - hres = IBindStatusCallback_OnStartBinding(pbscb, 0, (IBinding*)&This->lpvtbl2); + hres = IBindStatusCallback_OnStartBinding(This->pbscb, 0, (IBinding*)&This->lpvtbl2); TRACE("OnStartBinding rets %08lx\n", hres); -#if 0 - if(!registered_wndclass) { - WNDCLASSA urlmon_wndclass = {0, URLMON_WndProc,0, 0, URLMON_hInstance, 0, 0, 0, NULL, "URLMON_Callback_Window_Class"}; - RegisterClassA(&urlmon_wndclass); - registered_wndclass = TRUE; - } + /* This class will accept URLs with the backslash in them. But InternetCrackURL will not - it + * requires forward slashes (this is the behaviour of Microsoft's INETAPI). So we need to make + * a copy of the URL here and change the backslash to a forward slash everywhere it appears - + * but only before any '#' or '?', after which backslash should be left alone. + */ + urlcopy = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(This->URLName) + 1)); + lstrcpyW(urlcopy, This->URLName); + for (tmpwc = urlcopy; *tmpwc && *tmpwc != '#' && *tmpwc != '?'; ++tmpwc) + if (*tmpwc == '\\') + *tmpwc = '/'; - This->hwndCallback = CreateWindowA("URLMON_Callback_Window_Class", NULL, 0, 0, 0, 0, 0, 0, 0, - URLMON_hInstance, NULL); +#if 0 + if(!registered_wndclass) { + WNDCLASSA urlmon_wndclass = {0, URLMON_WndProc,0, 0, URLMON_hInstance, 0, 0, 0, NULL, "URLMON_Callback_Window_Class"}; + RegisterClassA(&urlmon_wndclass); + registered_wndclass = TRUE; + } + + This->hwndCallback = CreateWindowA("URLMON_Callback_Window_Class", NULL, 0, 0, 0, 0, 0, 0, 0, + URLMON_hInstance, NULL); #endif + This->expected_size = 0; + This->total_read = 0; + memset(&url, 0, sizeof(url)); url.dwStructSize = sizeof(url); - url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = 1; - InternetCrackUrlW(This->URLName, 0, 0, &url); + url.dwSchemeLength = url.dwHostNameLength = url.dwUrlPathLength = url.dwUserNameLength = url.dwPasswordLength = 1; + InternetCrackUrlW(urlcopy, 0, 0, &url); host = HeapAlloc(GetProcessHeap(), 0, (url.dwHostNameLength + 1) * sizeof(WCHAR)); memcpy(host, url.lpszHostName, url.dwHostNameLength * sizeof(WCHAR)); host[url.dwHostNameLength] = '\0'; path = HeapAlloc(GetProcessHeap(), 0, (url.dwUrlPathLength + 1) * sizeof(WCHAR)); memcpy(path, url.lpszUrlPath, url.dwUrlPathLength * sizeof(WCHAR)); path[url.dwUrlPathLength] = '\0'; - - This->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0 /*INTERNET_FLAG_ASYNC*/); -/* InternetSetStatusCallback(This->hinternet, URLMON_InternetCallback);*/ - - This->hconnect = InternetConnectW(This->hinternet, host, INTERNET_DEFAULT_HTTP_PORT, NULL, NULL, - INTERNET_SERVICE_HTTP, 0, (DWORD)This); - This->hrequest = HttpOpenRequestW(This->hconnect, NULL, path, NULL, NULL, NULL, 0, (DWORD)This); - - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, 0x22, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_CONNECTING, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL); - hres = E_OUTOFMEMORY; /* FIXME */ - if(HttpSendRequestW(This->hrequest, NULL, 0, NULL, 0)) { - len = 0; - HttpQueryInfoW(This->hrequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, &len, &lensz, NULL); - - TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), len); - - last_read_pos.u.LowPart = last_read_pos.u.HighPart = 0; - fmt.cfFormat = 0; - fmt.ptd = NULL; - fmt.dwAspect = 0; - fmt.lindex = -1; - fmt.tymed = TYMED_ISTREAM; - stg.tymed = TYMED_ISTREAM; - stg.u.pstm = pstr; - stg.pUnkForRelease = NULL; - - while(1) { - char buf[4096]; - DWORD bufread; - DWORD written; - if(InternetReadFile(This->hrequest, buf, sizeof(buf), &bufread)) { - TRACE("read %ld bytes %s...\n", bufread, debugstr_an(buf, 10)); - if(bufread == 0) break; - IStream_Write(pstr, buf, bufread, &written); - total_read += bufread; - IStream_Seek(pstr, last_read_pos, STREAM_SEEK_SET, NULL); - hres = IBindStatusCallback_OnProgress(pbscb, total_read, len, (total_read == bufread) ? - BINDSTATUS_BEGINDOWNLOADDATA : - BINDSTATUS_DOWNLOADINGDATA, NULL); - hres = IBindStatusCallback_OnDataAvailable(pbscb, - (total_read == bufread) ? BSCF_FIRSTDATANOTIFICATION : - BSCF_INTERMEDIATEDATANOTIFICATION, - total_read, &fmt, &stg); - last_read_pos.u.LowPart += bufread; /* FIXME */ - } else - break; - } - hres = IBindStatusCallback_OnProgress(pbscb, total_read, len, BINDSTATUS_ENDDOWNLOADDATA, NULL); - hres = IBindStatusCallback_OnDataAvailable(pbscb, BSCF_LASTDATANOTIFICATION, total_read, &fmt, &stg); - TRACE("OnDataAvail rets %08lx\n", hres); - hres = IBindStatusCallback_OnStopBinding(pbscb, S_OK, NULL); - TRACE("OnStop rets %08lx\n", hres); - hres = S_OK; + if (url.dwUserNameLength) + { + user = HeapAlloc(GetProcessHeap(), 0, ((url.dwUserNameLength + 1) * sizeof(WCHAR))); + memcpy(user, url.lpszUserName, url.dwUserNameLength * sizeof(WCHAR)); + user[url.dwUserNameLength] = 0; } - InternetCloseHandle(This->hrequest); - InternetCloseHandle(This->hconnect); - InternetCloseHandle(This->hinternet); - IBindStatusCallback_Release(pbscb); + else + { + user = 0; + } + if (url.dwPasswordLength) + { + pass = HeapAlloc(GetProcessHeap(), 0, ((url.dwPasswordLength + 1) * sizeof(WCHAR))); + memcpy(pass, url.lpszPassword, url.dwPasswordLength * sizeof(WCHAR)); + pass[url.dwPasswordLength] = 0; + } + else + { + pass = 0; + } + + switch ((DWORD) url.nScheme) + { + case INTERNET_SCHEME_FTP: + case INTERNET_SCHEME_GOPHER: + case INTERNET_SCHEME_HTTP: + case INTERNET_SCHEME_HTTPS: + + This->hinternet = InternetOpenA("User Agent", 0, NULL, NULL, 0 /*INTERNET_FLAG_ASYNC*/); +/* InternetSetStatusCallback(This->hinternet, URLMON_InternetCallback);*/ + if (!This->hinternet) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + } + + switch ((DWORD) url.nScheme) + { + case INTERNET_SCHEME_FTP: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_FTP_PORT; + dwService = INTERNET_SERVICE_FTP; + break; + + case INTERNET_SCHEME_GOPHER: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_GOPHER_PORT; + dwService = INTERNET_SERVICE_GOPHER; + break; + + case INTERNET_SCHEME_HTTP: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_HTTP_PORT; + dwService = INTERNET_SERVICE_HTTP; + break; + + case INTERNET_SCHEME_HTTPS: + if (!url.nPort) + url.nPort = INTERNET_DEFAULT_HTTPS_PORT; + dwService = INTERNET_SERVICE_HTTP; + break; + } + + This->hconnect = InternetConnectW(This->hinternet, host, url.nPort, user, pass, + dwService, 0, (DWORD)This); + if (!This->hconnect) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + CloseHandle(This->hinternet); + break; + } + + hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, 0x22, NULL); + hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_FINDINGRESOURCE, NULL); + hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CONNECTING, NULL); + hres = IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_SENDINGREQUEST, NULL); + + bSuccess = FALSE; + + switch (dwService) + { + case INTERNET_SERVICE_GOPHER: + This->hrequest = GopherOpenFileW(This->hconnect, + path, + 0, + INTERNET_FLAG_RELOAD, + 0); + if (This->hrequest) + bSuccess = TRUE; + else + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + + case INTERNET_SERVICE_FTP: + This->hrequest = FtpOpenFileW(This->hconnect, + path, + GENERIC_READ, + FTP_TRANSFER_TYPE_BINARY | + INTERNET_FLAG_TRANSFER_BINARY | + INTERNET_FLAG_RELOAD, + 0); + if (This->hrequest) + bSuccess = TRUE; + else + hres = HRESULT_FROM_WIN32(GetLastError()); + break; + + case INTERNET_SERVICE_HTTP: + This->hrequest = HttpOpenRequestW(This->hconnect, NULL, path, NULL, NULL, NULL, 0, (DWORD)This); + if (!This->hrequest) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + } + else if (!HttpSendRequestW(This->hrequest, NULL, 0, NULL, 0)) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + InternetCloseHandle(This->hrequest); + } + else + { + HttpQueryInfoW(This->hrequest, + HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, + &This->expected_size, + &lensz, + NULL); + bSuccess = TRUE; + } + break; + } + if(bSuccess) + { + TRACE("res = %ld gle = %08lx url len = %ld\n", hres, GetLastError(), This->expected_size); + + IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName); + + while(1) { + char buf[4096]; + DWORD bufread; + if(InternetReadFile(This->hrequest, buf, sizeof(buf), &bufread)) { + TRACE("read %ld bytes %s...\n", bufread, debugstr_an(buf, 10)); + if(bufread == 0) break; + hres = URLMonikerImpl_MoreCacheData(This, buf, bufread); + } else + break; + break; + } + InternetCloseHandle(This->hrequest); + hres = S_OK; + } + + InternetCloseHandle(This->hconnect); + InternetCloseHandle(This->hinternet); + break; + + case INTERNET_SCHEME_FILE: + path = This->URLName + 5; /* Skip the "file:" part */ + if ((path[0] != '/' && path[0] != '\\') || + (path[1] != '/' && path[1] != '\\')) + { + hres = E_FAIL; + } + else + { + HANDLE h; + + path += 2; + if (path[0] == '/' || path[0] == '\\') + ++path; + h = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (h == (HANDLE) HFILE_ERROR) + { + hres = HRESULT_FROM_WIN32(GetLastError()); + } + else + { + char buf[4096]; + DWORD bufread; + + IBindStatusCallback_OnProgress(This->pbscb, 0, 0, BINDSTATUS_CACHEFILENAMEAVAILABLE, szFileName); + + while (ReadFile(h, buf, sizeof(buf), &bufread, NULL) && bufread > 0) + hres = URLMonikerImpl_MoreCacheData(This, buf, bufread); + + CloseHandle(h); + hres = S_OK; + } + } + + break; + + default: + FIXME("Unsupported URI scheme"); + break; + } + URLMonikerImpl_CloseCacheDownload(This); + URLMonikerImpl_FinishedDownload(This, hres); + + if (user) + HeapFree(GetProcessHeap(), 0, user); + if (pass) + HeapFree(GetProcessHeap(), 0, pass); + HeapFree(GetProcessHeap(), 0, path); + HeapFree(GetProcessHeap(), 0, host); + HeapFree(GetProcessHeap(), 0, urlcopy); } } } - *ppvObject = (VOID*)pstr; return hres; } diff --git a/dlls/urlmon/umstream.c b/dlls/urlmon/umstream.c new file mode 100644 index 00000000000..da9e8f2cc65 --- /dev/null +++ b/dlls/urlmon/umstream.c @@ -0,0 +1,380 @@ +/* + * Based on ../shell32/memorystream.c + * + * Copyright 1999 Juergen Schmied + * Copyright 2003 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winternl.h" +#include "winuser.h" +#include "objbase.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "ole2.h" +#include "urlmon.h" +#include "wininet.h" +#include "shlwapi.h" +#include "urlmon_main.h" + +WINE_DEFAULT_DEBUG_CHANNEL(urlmon); + +static const IStreamVtbl stvt; + +HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL, + DWORD dwSize, + LPWSTR pszFileName, + HANDLE *phfile, + IUMCacheStream **ppstr) +{ + IUMCacheStream* ucstr; + HANDLE handle; + LPWSTR ext; + LPCWSTR c; + LPCWSTR eloc = 0; + HRESULT hr; + + for (c = pszURL; *c && *c != '#' && *c != '?'; ++c) + { + if (*c == '.') + eloc = c + 1; + else if (*c == '/' || *c == '\\') + eloc = 0; + } + + if (!eloc) + eloc = c; + + ext = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (c - eloc + 1)); + memcpy(ext, eloc, sizeof(WCHAR) * (c - eloc)); + ext[c - eloc] = 0; + + if(!CreateUrlCacheEntryW(pszURL, dwSize, ext, pszFileName, 0)) + hr = HRESULT_FROM_WIN32(GetLastError()); + else + hr = 0; + + HeapFree(GetProcessHeap(), 0, ext); + + if (hr) + return hr; + + TRACE("Opening %s\n", debugstr_w(pszFileName) ); + + handle = CreateFileW( pszFileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL ); + if( handle == INVALID_HANDLE_VALUE ) + return HRESULT_FROM_WIN32(GetLastError()); + + if (phfile) + { + /* Call CreateFileW again because we need a handle with its own file pointer, and DuplicateHandle will return + * a handle that shares its file pointer with the original. + */ + *phfile = CreateFileW( pszFileName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); + + if (*phfile == (HANDLE) HFILE_ERROR) + { + DWORD dwError = GetLastError(); + + CloseHandle(handle); + return HRESULT_FROM_WIN32(dwError); + } + } + + ucstr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,sizeof(IUMCacheStream)); + if(ucstr ) + { + ucstr->pszURL = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(WCHAR) * (lstrlenW(pszURL) + 1)); + if (ucstr->pszURL) + { + ucstr->pszFileName = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, + sizeof(WCHAR) * (lstrlenW(pszFileName) + 1)); + if (ucstr->pszFileName) + { + ucstr->lpVtbl=&stvt; + ucstr->ref = 1; + ucstr->handle = handle; + ucstr->closed = 0; + lstrcpyW(ucstr->pszURL, pszURL); + lstrcpyW(ucstr->pszFileName, pszFileName); + + *ppstr = ucstr; + + return S_OK; + } + HeapFree(GetProcessHeap(), 0, ucstr->pszURL); + } + HeapFree(GetProcessHeap(), 0, ucstr); + } + CloseHandle(handle); + if (phfile) + CloseHandle(*phfile); + return E_OUTOFMEMORY; +} + +void UMCloseCacheFileStream(IUMCacheStream *This) +{ + if (!This->closed) + { + FILETIME ftZero; + + ftZero.dwLowDateTime = ftZero.dwHighDateTime = 0; + + This->closed = 1; + CommitUrlCacheEntryW(This->pszURL, + This->pszFileName, + ftZero, + ftZero, + NORMAL_CACHE_ENTRY, + 0, + 0, + 0, + 0); + } +} + +/************************************************************************** +* IStream_fnQueryInterface +*/ +static HRESULT WINAPI IStream_fnQueryInterface(IStream *iface, + REFIID riid, + LPVOID *ppvObj) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj); + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IStream)) + { + *ppvObj = This; + } + + if(*ppvObj) + { + IStream_AddRef((IStream*)*ppvObj); + TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); + return S_OK; + } + TRACE("-- Interface: E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +/************************************************************************** +* IStream_fnAddRef +*/ +static ULONG WINAPI IStream_fnAddRef(IStream *iface) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + ULONG refCount = InterlockedIncrement(&This->ref); + + TRACE("(%p)->(count=%lu)\n", This, refCount - 1); + + return refCount; +} + +/************************************************************************** +* IStream_fnRelease +*/ +static ULONG WINAPI IStream_fnRelease(IStream *iface) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + ULONG refCount = InterlockedDecrement(&This->ref); + + TRACE("(%p)->(count=%lu)\n", This, refCount + 1); + + if (!refCount) + { + TRACE(" destroying UMCacheStream (%p)\n",This); + UMCloseCacheFileStream(This); + CloseHandle(This->handle); + HeapFree(GetProcessHeap(), 0, This->pszFileName); + HeapFree(GetProcessHeap(), 0, This->pszURL); + HeapFree(GetProcessHeap(),0,This); + } + return refCount; +} + +static HRESULT WINAPI IStream_fnRead (IStream * iface, + void* pv, + ULONG cb, + ULONG* pcbRead) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)->(%p,0x%08lx,%p)\n",This, pv, cb, pcbRead); + + if ( !pv ) + return STG_E_INVALIDPOINTER; + + if ( ! ReadFile( This->handle, pv, cb, pcbRead, NULL ) ) + return S_FALSE; + + if (!*pcbRead) + return This->closed ? S_FALSE : E_PENDING; + return S_OK; +} + +static HRESULT WINAPI IStream_fnWrite (IStream * iface, + const void* pv, + ULONG cb, + ULONG* pcbWritten) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnSeek ( IStream * iface, + LARGE_INTEGER dlibMove, + DWORD dwOrigin, + ULARGE_INTEGER* plibNewPosition) +{ + DWORD pos, newposlo, newposhi; + + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + pos = dlibMove.QuadPart; /* FIXME: truncates */ + newposhi = 0; + newposlo = SetFilePointer( This->handle, pos, &newposhi, dwOrigin ); + if( newposlo == INVALID_SET_FILE_POINTER ) + return E_FAIL; + + if (plibNewPosition) + plibNewPosition->QuadPart = newposlo | ( (LONGLONG)newposhi<<32); + + return S_OK; +} + +static HRESULT WINAPI IStream_fnSetSize (IStream * iface, + ULARGE_INTEGER libNewSize) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + if( ! SetFilePointer( This->handle, libNewSize.QuadPart, NULL, FILE_BEGIN ) ) + return E_FAIL; + + if( ! SetEndOfFile( This->handle ) ) + return E_FAIL; + + return S_OK; +} + +static HRESULT WINAPI IStream_fnCopyTo (IStream * iface, + IStream* pstm, + ULARGE_INTEGER cb, + ULARGE_INTEGER* pcbRead, + ULARGE_INTEGER* pcbWritten) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnCommit (IStream * iface, + DWORD grfCommitFlags) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static HRESULT WINAPI IStream_fnRevert (IStream * iface) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnLockRegion (IStream * iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnUnlockRegion (IStream * iface, + ULARGE_INTEGER libOffset, + ULARGE_INTEGER cb, + DWORD dwLockType) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnStat (IStream * iface, + STATSTG* pstatstg, + DWORD grfStatFlag) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} +static HRESULT WINAPI IStream_fnClone (IStream * iface, + IStream** ppstm) +{ + IUMCacheStream *This = (IUMCacheStream *)iface; + + TRACE("(%p)\n",This); + + return E_NOTIMPL; +} + +static const IStreamVtbl stvt = +{ + IStream_fnQueryInterface, + IStream_fnAddRef, + IStream_fnRelease, + IStream_fnRead, + IStream_fnWrite, + IStream_fnSeek, + IStream_fnSetSize, + IStream_fnCopyTo, + IStream_fnCommit, + IStream_fnRevert, + IStream_fnLockRegion, + IStream_fnUnlockRegion, + IStream_fnStat, + IStream_fnClone + +}; diff --git a/dlls/urlmon/urlmon_main.h b/dlls/urlmon/urlmon_main.h index 212dddc5ab9..f65e2d63a6e 100644 --- a/dlls/urlmon/urlmon_main.h +++ b/dlls/urlmon/urlmon_main.h @@ -37,4 +37,17 @@ static inline void URLMON_UnlockModule() { InterlockedDecrement( &URLMON_refCoun #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) +typedef struct +{ + const IStreamVtbl *lpVtbl; + DWORD ref; + HANDLE handle; + BOOL closed; + WCHAR *pszFileName; + WCHAR *pszURL; +} IUMCacheStream; + +HRESULT UMCreateStreamOnCacheFile(LPCWSTR pszURL, DWORD dwSize, LPWSTR pszFileName, HANDLE *phfile, IUMCacheStream **ppstr); +void UMCloseCacheFileStream(IUMCacheStream *pstr); + #endif /* __WINE_URLMON_MAIN_H */