Implement URLMonikerImpl_BindToStorage.

diff --git a/dlls/urlmon/Makefile.in b/dlls/urlmon/Makefile.in
index f74e056..9b1d4a5 100644
--- a/dlls/urlmon/Makefile.in
+++ b/dlls/urlmon/Makefile.in
@@ -10,6 +10,7 @@
 	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 284d9e9..c1f3e6b 100644
--- a/dlls/urlmon/umon.c
+++ b/dlls/urlmon/umon.c
@@ -61,6 +61,10 @@
     HWND hwndCallback;
     IBindCtx *pBC;
     HINTERNET hinternet, hconnect, hrequest;
+    HANDLE hCacheFile;
+    IUMCacheStream *pstrCache;
+    IBindStatusCallback *pbscb;
+    DWORD total_read, expected_size;
 } URLMonikerImpl;
 
 /*******************************************************************************
@@ -126,6 +130,15 @@
     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 @@
     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 @@
     return;
 }
 #endif
+
+
 /******************************************************************************
  *        URLMoniker_BindToStorage
  ******************************************************************************/
@@ -321,12 +435,10 @@
 {
     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 @@
 	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 0000000..da9e8f2
--- /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 <stdarg.h>
+
+#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 212dddc..f65e2d6 100644
--- a/dlls/urlmon/urlmon_main.h
+++ b/dlls/urlmon/urlmon_main.h
@@ -37,4 +37,17 @@
 
 #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 */