Synchronize up to trunk's revision r57689.
[reactos.git] / dll / win32 / urlmon / file.c
index a517d94..418b708 100644 (file)
  */
 
 #include "urlmon_main.h"
+#include "winreg.h"
+#include "shlwapi.h"
+
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
 
 typedef struct {
-    const IInternetProtocolVtbl  *lpIInternetProtocolVtbl;
-    const IInternetPriorityVtbl  *lpInternetPriorityVtbl;
+    IInternetProtocolEx IInternetProtocolEx_iface;
+    IInternetPriority   IInternetPriority_iface;
 
     HANDLE file;
+    ULONG size;
     LONG priority;
 
     LONG ref;
 } FileProtocol;
 
-#define PRIORITY(x)  ((IInternetPriority*)  &(x)->lpInternetPriorityVtbl)
+static inline FileProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
+{
+    return CONTAINING_RECORD(iface, FileProtocol, IInternetProtocolEx_iface);
+}
 
-#define PROTOCOL_THIS(iface) DEFINE_THIS(FileProtocol, IInternetProtocol, iface)
+static inline FileProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
+{
+    return CONTAINING_RECORD(iface, FileProtocol, IInternetPriority_iface);
+}
 
-static HRESULT WINAPI FileProtocol_QueryInterface(IInternetProtocol *iface, REFIID riid, void **ppv)
+static HRESULT WINAPI FileProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
 
     *ppv = NULL;
     if(IsEqualGUID(&IID_IUnknown, riid)) {
         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
-        *ppv = PROTOCOL(This);
+        *ppv = &This->IInternetProtocolEx_iface;
     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
-        *ppv = PROTOCOL(This);
+        *ppv = &This->IInternetProtocolEx_iface;
     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
-        *ppv = PROTOCOL(This);
+        *ppv = &This->IInternetProtocolEx_iface;
+    }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
+        TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv);
+        *ppv = &This->IInternetProtocolEx_iface;
     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
-        *ppv = PRIORITY(This);
+        *ppv = &This->IInternetPriority_iface;
     }
 
     if(*ppv) {
@@ -63,23 +76,23 @@ static HRESULT WINAPI FileProtocol_QueryInterface(IInternetProtocol *iface, REFI
     return E_NOINTERFACE;
 }
 
-static ULONG WINAPI FileProtocol_AddRef(IInternetProtocol *iface)
+static ULONG WINAPI FileProtocol_AddRef(IInternetProtocolEx *iface)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     LONG ref = InterlockedIncrement(&This->ref);
     TRACE("(%p) ref=%d\n", This, ref);
     return ref;
 }
 
-static ULONG WINAPI FileProtocol_Release(IInternetProtocol *iface)
+static ULONG WINAPI FileProtocol_Release(IInternetProtocolEx *iface)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     LONG ref = InterlockedDecrement(&This->ref);
 
     TRACE("(%p) ref=%d\n", This, ref);
 
     if(!ref) {
-        if(This->file)
+        if(This->file != INVALID_HANDLE_VALUE)
             CloseHandle(This->file);
         heap_free(This);
 
@@ -89,155 +102,70 @@ static ULONG WINAPI FileProtocol_Release(IInternetProtocol *iface)
     return ref;
 }
 
-static HRESULT WINAPI FileProtocol_Start(IInternetProtocol *iface, LPCWSTR szUrl,
+static HRESULT WINAPI FileProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl,
         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
         DWORD grfPI, HANDLE_PTR dwReserved)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
-    BINDINFO bindinfo;
-    DWORD grfBINDF = 0;
-    LARGE_INTEGER size;
-    DWORD len;
-    LPWSTR url, mime = NULL, file_name;
-    WCHAR null_char = 0;
-    BOOL first_call = FALSE;
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
+    IUri *uri;
     HRESULT hres;
 
-    static const WCHAR wszFile[]  = {'f','i','l','e',':'};
-
     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
             pOIBindInfo, grfPI, dwReserved);
 
-    if(!szUrl || strlenW(szUrl) < sizeof(wszFile)/sizeof(WCHAR)
-            || memcmp(szUrl, wszFile, sizeof(wszFile)))
-        return E_INVALIDARG;
-
-    memset(&bindinfo, 0, sizeof(bindinfo));
-    bindinfo.cbSize = sizeof(BINDINFO);
-    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
-    if(FAILED(hres)) {
-        WARN("GetBindInfo failed: %08x\n", hres);
-        return hres;
-    }
-
-    ReleaseBindInfo(&bindinfo);
-
-    len = lstrlenW(szUrl)+16;
-    url = heap_alloc(len*sizeof(WCHAR));
-    hres = CoInternetParseUrl(szUrl, PARSE_ENCODE, 0, url, len, &len, 0);
-    if(FAILED(hres)) {
-        heap_free(url);
+    hres = CreateUri(szUrl, Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
+    if(FAILED(hres))
         return hres;
-    }
-
-    if(!(grfBINDF & BINDF_FROMURLMON))
-        IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
-
-    if(!This->file) {
-        WCHAR *ptr;
-
-        first_call = TRUE;
-
-        IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, &null_char);
 
-        file_name = url+sizeof(wszFile)/sizeof(WCHAR);
+    hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink,
+            pOIBindInfo, grfPI, (HANDLE*)dwReserved);
 
-        /* Strip both forward and back slashes */
-        if( (file_name[0] == '/' && file_name[1] == '/') ||
-            (file_name[0] == '\\' && file_name[1] == '\\'))
-            file_name += 2;
-        if(*file_name == '/')
-            file_name++;
-
-        for(ptr = file_name; *ptr; ptr++) {
-            if(*ptr == '?' || *ptr == '#') {
-                *ptr = 0;
-                break;
-            }
-        }
-
-        if(file_name[1] == '|')
-            file_name[1] = ':';
-
-        This->file = CreateFileW(file_name, GENERIC_READ, FILE_SHARE_READ, NULL,
-                                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
-
-        if(This->file == INVALID_HANDLE_VALUE) {
-            This->file = NULL;
-            IInternetProtocolSink_ReportResult(pOIProtSink, INET_E_RESOURCE_NOT_FOUND,
-                    GetLastError(), NULL);
-            heap_free(url);
-            return INET_E_RESOURCE_NOT_FOUND;
-        }
-
-        IInternetProtocolSink_ReportProgress(pOIProtSink,
-                BINDSTATUS_CACHEFILENAMEAVAILABLE, file_name);
-
-        hres = FindMimeFromData(NULL, url, NULL, 0, NULL, 0, &mime, 0);
-        if(SUCCEEDED(hres)) {
-            IInternetProtocolSink_ReportProgress(pOIProtSink,
-                    (grfBINDF & BINDF_FROMURLMON) ?
-                    BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
-                    mime);
-            CoTaskMemFree(mime);
-        }
-    }
-
-    heap_free(url);
-
-    if(GetFileSizeEx(This->file, &size))
-        IInternetProtocolSink_ReportData(pOIProtSink,
-                BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
-                size.u.LowPart, size.u.LowPart);
-
-    if(first_call)
-        IInternetProtocolSink_ReportResult(pOIProtSink, S_OK, 0, NULL);
-
-    return S_OK;
+    IUri_Release(uri);
+    return hres;
 }
 
-static HRESULT WINAPI FileProtocol_Continue(IInternetProtocol *iface, PROTOCOLDATA *pProtocolData)
+static HRESULT WINAPI FileProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     FIXME("(%p)->(%p)\n", This, pProtocolData);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI FileProtocol_Abort(IInternetProtocol *iface, HRESULT hrReason,
+static HRESULT WINAPI FileProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason,
         DWORD dwOptions)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI FileProtocol_Terminate(IInternetProtocol *iface, DWORD dwOptions)
+static HRESULT WINAPI FileProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
 
     TRACE("(%p)->(%08x)\n", This, dwOptions);
 
     return S_OK;
 }
 
-static HRESULT WINAPI FileProtocol_Suspend(IInternetProtocol *iface)
+static HRESULT WINAPI FileProtocol_Suspend(IInternetProtocolEx *iface)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     FIXME("(%p)\n", This);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI FileProtocol_Resume(IInternetProtocol *iface)
+static HRESULT WINAPI FileProtocol_Resume(IInternetProtocolEx *iface)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     FIXME("(%p)\n", This);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI FileProtocol_Read(IInternetProtocol *iface, void *pv,
+static HRESULT WINAPI FileProtocol_Read(IInternetProtocolEx *iface, void *pv,
         ULONG cb, ULONG *pcbRead)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     DWORD read = 0;
 
     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
@@ -245,7 +173,7 @@ static HRESULT WINAPI FileProtocol_Read(IInternetProtocol *iface, void *pv,
     if (pcbRead)
         *pcbRead = 0;
 
-    if(!This->file)
+    if(This->file == INVALID_HANDLE_VALUE)
         return INET_E_DATA_NOT_AVAILABLE;
 
     if (!ReadFile(This->file, pv, cb, &read, NULL))
@@ -257,35 +185,141 @@ static HRESULT WINAPI FileProtocol_Read(IInternetProtocol *iface, void *pv,
     return cb == read ? S_OK : S_FALSE;
 }
 
-static HRESULT WINAPI FileProtocol_Seek(IInternetProtocol *iface, LARGE_INTEGER dlibMove,
+static HRESULT WINAPI FileProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove,
         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
     return E_NOTIMPL;
 }
 
-static HRESULT WINAPI FileProtocol_LockRequest(IInternetProtocol *iface, DWORD dwOptions)
+static HRESULT WINAPI FileProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
 
     TRACE("(%p)->(%08x)\n", This, dwOptions);
 
     return S_OK;
 }
 
-static HRESULT WINAPI FileProtocol_UnlockRequest(IInternetProtocol *iface)
+static HRESULT WINAPI FileProtocol_UnlockRequest(IInternetProtocolEx *iface)
 {
-    FileProtocol *This = PROTOCOL_THIS(iface);
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
 
     TRACE("(%p)\n", This);
 
     return S_OK;
 }
 
-#undef PROTOCOL_THIS
+static inline HRESULT report_result(IInternetProtocolSink *protocol_sink, HRESULT hres, DWORD res)
+{
+    IInternetProtocolSink_ReportResult(protocol_sink, hres, res, NULL);
+    return hres;
+}
+
+static HRESULT open_file(FileProtocol *This, const WCHAR *path, IInternetProtocolSink *protocol_sink)
+{
+    LARGE_INTEGER size;
+    HANDLE file;
+
+    file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
+            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+    if(file == INVALID_HANDLE_VALUE)
+        return report_result(protocol_sink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
 
-static const IInternetProtocolVtbl FileProtocolVtbl = {
+    if(!GetFileSizeEx(file, &size)) {
+        CloseHandle(file);
+        return report_result(protocol_sink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
+    }
+
+    This->file = file;
+    This->size = size.u.LowPart;
+
+    IInternetProtocolSink_ReportProgress(protocol_sink,
+            BINDSTATUS_CACHEFILENAMEAVAILABLE, path);
+    return S_OK;
+}
+
+static HRESULT WINAPI FileProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri,
+        IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
+        DWORD grfPI, HANDLE *dwReserved)
+{
+    FileProtocol *This = impl_from_IInternetProtocolEx(iface);
+    BINDINFO bindinfo;
+    DWORD grfBINDF = 0;
+    DWORD scheme;
+    LPWSTR mime = NULL;
+    WCHAR null_char = 0;
+    BSTR path, url;
+    HRESULT hres;
+
+    TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
+            pOIBindInfo, grfPI, dwReserved);
+
+    if(!pUri)
+        return E_INVALIDARG;
+
+    scheme = 0;
+    hres = IUri_GetScheme(pUri, &scheme);
+    if(FAILED(hres))
+        return hres;
+    if(scheme != URL_SCHEME_FILE)
+        return E_INVALIDARG;
+
+    memset(&bindinfo, 0, sizeof(bindinfo));
+    bindinfo.cbSize = sizeof(BINDINFO);
+    hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
+    if(FAILED(hres)) {
+        WARN("GetBindInfo failed: %08x\n", hres);
+        return hres;
+    }
+
+    ReleaseBindInfo(&bindinfo);
+
+    if(!(grfBINDF & BINDF_FROMURLMON))
+        IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
+
+    if(This->file != INVALID_HANDLE_VALUE) {
+        IInternetProtocolSink_ReportData(pOIProtSink,
+                BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
+                This->size, This->size);
+        return S_OK;
+    }
+
+    IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, &null_char);
+
+    hres = IUri_GetPath(pUri, &path);
+    if(FAILED(hres)) {
+        ERR("GetPath failed: %08x\n", hres);
+        return report_result(pOIProtSink, hres, 0);
+    }
+
+    hres = open_file(This, path, pOIProtSink);
+    SysFreeString(path);
+    if(FAILED(hres))
+        return hres;
+
+    hres = IUri_GetDisplayUri(pUri, &url);
+    if(hres == S_OK) {
+        hres = FindMimeFromData(NULL, url, NULL, 0, NULL, 0, &mime, 0);
+        SysFreeString(url);
+        if(SUCCEEDED(hres)) {
+            IInternetProtocolSink_ReportProgress(pOIProtSink,
+                    (grfBINDF & BINDF_FROMURLMON) ?
+                     BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
+                    mime);
+            CoTaskMemFree(mime);
+        }
+    }
+
+    IInternetProtocolSink_ReportData(pOIProtSink,
+            BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
+            This->size, This->size);
+
+    return report_result(pOIProtSink, S_OK, 0);
+}
+
+static const IInternetProtocolExVtbl FileProtocolExVtbl = {
     FileProtocol_QueryInterface,
     FileProtocol_AddRef,
     FileProtocol_Release,
@@ -298,33 +332,32 @@ static const IInternetProtocolVtbl FileProtocolVtbl = {
     FileProtocol_Read,
     FileProtocol_Seek,
     FileProtocol_LockRequest,
-    FileProtocol_UnlockRequest
+    FileProtocol_UnlockRequest,
+    FileProtocol_StartEx
 };
 
-#define PRIORITY_THIS(iface) DEFINE_THIS(FileProtocol, InternetPriority, iface)
-
 static HRESULT WINAPI FilePriority_QueryInterface(IInternetPriority *iface,
                                                   REFIID riid, void **ppv)
 {
-    FileProtocol *This = PRIORITY_THIS(iface);
-    return IInternetProtocol_QueryInterface(PROTOCOL(This), riid, ppv);
+    FileProtocol *This = impl_from_IInternetPriority(iface);
+    return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
 }
 
 static ULONG WINAPI FilePriority_AddRef(IInternetPriority *iface)
 {
-    FileProtocol *This = PRIORITY_THIS(iface);
-    return IInternetProtocol_AddRef(PROTOCOL(This));
+    FileProtocol *This = impl_from_IInternetPriority(iface);
+    return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
 }
 
 static ULONG WINAPI FilePriority_Release(IInternetPriority *iface)
 {
-    FileProtocol *This = PRIORITY_THIS(iface);
-    return IInternetProtocol_Release(PROTOCOL(This));
+    FileProtocol *This = impl_from_IInternetPriority(iface);
+    return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
 }
 
 static HRESULT WINAPI FilePriority_SetPriority(IInternetPriority *iface, LONG nPriority)
 {
-    FileProtocol *This = PRIORITY_THIS(iface);
+    FileProtocol *This = impl_from_IInternetPriority(iface);
 
     TRACE("(%p)->(%d)\n", This, nPriority);
 
@@ -334,7 +367,7 @@ static HRESULT WINAPI FilePriority_SetPriority(IInternetPriority *iface, LONG nP
 
 static HRESULT WINAPI FilePriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
 {
-    FileProtocol *This = PRIORITY_THIS(iface);
+    FileProtocol *This = impl_from_IInternetPriority(iface);
 
     TRACE("(%p)->(%p)\n", This, pnPriority);
 
@@ -342,8 +375,6 @@ static HRESULT WINAPI FilePriority_GetPriority(IInternetPriority *iface, LONG *p
     return S_OK;
 }
 
-#undef PRIORITY_THIS
-
 static const IInternetPriorityVtbl FilePriorityVtbl = {
     FilePriority_QueryInterface,
     FilePriority_AddRef,
@@ -362,13 +393,12 @@ HRESULT FileProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
 
     ret = heap_alloc(sizeof(FileProtocol));
 
-    ret->lpIInternetProtocolVtbl = &FileProtocolVtbl;
-    ret->lpInternetPriorityVtbl = &FilePriorityVtbl;
-    ret->file = NULL;
+    ret->IInternetProtocolEx_iface.lpVtbl = &FileProtocolExVtbl;
+    ret->IInternetPriority_iface.lpVtbl = &FilePriorityVtbl;
+    ret->file = INVALID_HANDLE_VALUE;
     ret->priority = 0;
     ret->ref = 1;
 
-    *ppobj = PROTOCOL(ret);
-    
+    *ppobj = &ret->IInternetProtocolEx_iface;
     return S_OK;
 }