xref: /reactos/dll/win32/urlmon/file.c (revision 8a978a17)
1 /*
2  * Copyright 2005 Jacek Caban
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 
19 #include "urlmon_main.h"
20 #include "winreg.h"
21 #include "shlwapi.h"
22 
23 #include "wine/debug.h"
24 
25 WINE_DEFAULT_DEBUG_CHANNEL(urlmon);
26 
27 typedef struct {
28     IUnknown            IUnknown_inner;
29     IInternetProtocolEx IInternetProtocolEx_iface;
30     IInternetPriority   IInternetPriority_iface;
31 
32     IUnknown *outer;
33 
34     HANDLE file;
35     ULONG size;
36     LONG priority;
37 
38     LONG ref;
39 } FileProtocol;
40 
41 static inline FileProtocol *impl_from_IUnknown(IUnknown *iface)
42 {
43     return CONTAINING_RECORD(iface, FileProtocol, IUnknown_inner);
44 }
45 
46 static inline FileProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
47 {
48     return CONTAINING_RECORD(iface, FileProtocol, IInternetProtocolEx_iface);
49 }
50 
51 static inline FileProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
52 {
53     return CONTAINING_RECORD(iface, FileProtocol, IInternetPriority_iface);
54 }
55 
56 static HRESULT WINAPI FileProtocolUnk_QueryInterface(IUnknown *iface, REFIID riid, void **ppv)
57 {
58     FileProtocol *This = impl_from_IUnknown(iface);
59 
60     *ppv = NULL;
61     if(IsEqualGUID(&IID_IUnknown, riid)) {
62         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
63         *ppv = &This->IUnknown_inner;
64     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
65         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
66         *ppv = &This->IInternetProtocolEx_iface;
67     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
68         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
69         *ppv = &This->IInternetProtocolEx_iface;
70     }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
71         TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv);
72         *ppv = &This->IInternetProtocolEx_iface;
73     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
74         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
75         *ppv = &This->IInternetPriority_iface;
76     }
77 
78     if(*ppv) {
79         IUnknown_AddRef((IUnknown*)*ppv);
80         return S_OK;
81     }
82 
83     WARN("not supported interface %s\n", debugstr_guid(riid));
84     return E_NOINTERFACE;
85 }
86 
87 static ULONG WINAPI FileProtocolUnk_AddRef(IUnknown *iface)
88 {
89     FileProtocol *This = impl_from_IUnknown(iface);
90     LONG ref = InterlockedIncrement(&This->ref);
91     TRACE("(%p) ref=%d\n", This, ref);
92     return ref;
93 }
94 
95 static ULONG WINAPI FileProtocolUnk_Release(IUnknown *iface)
96 {
97     FileProtocol *This = impl_from_IUnknown(iface);
98     LONG ref = InterlockedDecrement(&This->ref);
99 
100     TRACE("(%p) ref=%d\n", This, ref);
101 
102     if(!ref) {
103         if(This->file != INVALID_HANDLE_VALUE)
104             CloseHandle(This->file);
105         heap_free(This);
106 
107         URLMON_UnlockModule();
108     }
109 
110     return ref;
111 }
112 
113 static const IUnknownVtbl FileProtocolUnkVtbl = {
114     FileProtocolUnk_QueryInterface,
115     FileProtocolUnk_AddRef,
116     FileProtocolUnk_Release
117 };
118 
119 static HRESULT WINAPI FileProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
120 {
121     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
122     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
123     return IUnknown_QueryInterface(This->outer, riid, ppv);
124 }
125 
126 static ULONG WINAPI FileProtocol_AddRef(IInternetProtocolEx *iface)
127 {
128     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
129     TRACE("(%p)\n", This);
130     return IUnknown_AddRef(This->outer);
131 }
132 
133 static ULONG WINAPI FileProtocol_Release(IInternetProtocolEx *iface)
134 {
135     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
136     TRACE("(%p)\n", This);
137     return IUnknown_Release(This->outer);
138 }
139 
140 static HRESULT WINAPI FileProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl,
141         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
142         DWORD grfPI, HANDLE_PTR dwReserved)
143 {
144     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
145     IUri *uri;
146     HRESULT hres;
147 
148     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
149             pOIBindInfo, grfPI, dwReserved);
150 
151     hres = CreateUri(szUrl, Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
152     if(FAILED(hres))
153         return hres;
154 
155     hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink,
156             pOIBindInfo, grfPI, (HANDLE*)dwReserved);
157 
158     IUri_Release(uri);
159     return hres;
160 }
161 
162 static HRESULT WINAPI FileProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData)
163 {
164     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
165     FIXME("(%p)->(%p)\n", This, pProtocolData);
166     return E_NOTIMPL;
167 }
168 
169 static HRESULT WINAPI FileProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason,
170         DWORD dwOptions)
171 {
172     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
173     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
174     return E_NOTIMPL;
175 }
176 
177 static HRESULT WINAPI FileProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions)
178 {
179     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
180 
181     TRACE("(%p)->(%08x)\n", This, dwOptions);
182 
183     return S_OK;
184 }
185 
186 static HRESULT WINAPI FileProtocol_Suspend(IInternetProtocolEx *iface)
187 {
188     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
189     FIXME("(%p)\n", This);
190     return E_NOTIMPL;
191 }
192 
193 static HRESULT WINAPI FileProtocol_Resume(IInternetProtocolEx *iface)
194 {
195     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
196     FIXME("(%p)\n", This);
197     return E_NOTIMPL;
198 }
199 
200 static HRESULT WINAPI FileProtocol_Read(IInternetProtocolEx *iface, void *pv,
201         ULONG cb, ULONG *pcbRead)
202 {
203     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
204     DWORD read = 0;
205 
206     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
207 
208     if (pcbRead)
209         *pcbRead = 0;
210 
211     if(This->file == INVALID_HANDLE_VALUE)
212         return INET_E_DATA_NOT_AVAILABLE;
213 
214     if (!ReadFile(This->file, pv, cb, &read, NULL))
215         return INET_E_DOWNLOAD_FAILURE;
216 
217     if(pcbRead)
218         *pcbRead = read;
219 
220     return cb == read ? S_OK : S_FALSE;
221 }
222 
223 static HRESULT WINAPI FileProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove,
224         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
225 {
226     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
227     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
228     return E_NOTIMPL;
229 }
230 
231 static HRESULT WINAPI FileProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions)
232 {
233     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
234 
235     TRACE("(%p)->(%08x)\n", This, dwOptions);
236 
237     return S_OK;
238 }
239 
240 static HRESULT WINAPI FileProtocol_UnlockRequest(IInternetProtocolEx *iface)
241 {
242     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
243 
244     TRACE("(%p)\n", This);
245 
246     return S_OK;
247 }
248 
249 static inline HRESULT report_result(IInternetProtocolSink *protocol_sink, HRESULT hres, DWORD res)
250 {
251     IInternetProtocolSink_ReportResult(protocol_sink, hres, res, NULL);
252     return hres;
253 }
254 
255 static HRESULT WINAPI FileProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri,
256         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
257         DWORD grfPI, HANDLE *dwReserved)
258 {
259     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
260     WCHAR path[MAX_PATH], *ptr;
261     LARGE_INTEGER file_size;
262     HANDLE file_handle;
263     BINDINFO bindinfo;
264     DWORD grfBINDF = 0;
265     DWORD scheme, size;
266     LPWSTR mime = NULL;
267     WCHAR null_char = 0;
268     BSTR ext;
269     HRESULT hres;
270 
271     TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
272             pOIBindInfo, grfPI, dwReserved);
273 
274     if(!pUri)
275         return E_INVALIDARG;
276 
277     scheme = 0;
278     hres = IUri_GetScheme(pUri, &scheme);
279     if(FAILED(hres))
280         return hres;
281     if(scheme != URL_SCHEME_FILE)
282         return E_INVALIDARG;
283 
284     memset(&bindinfo, 0, sizeof(bindinfo));
285     bindinfo.cbSize = sizeof(BINDINFO);
286     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
287     if(FAILED(hres)) {
288         WARN("GetBindInfo failed: %08x\n", hres);
289         return hres;
290     }
291 
292     ReleaseBindInfo(&bindinfo);
293 
294     if(!(grfBINDF & BINDF_FROMURLMON))
295         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
296 
297     if(This->file != INVALID_HANDLE_VALUE) {
298         IInternetProtocolSink_ReportData(pOIProtSink,
299                 BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
300                 This->size, This->size);
301         return S_OK;
302     }
303 
304     IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, &null_char);
305 
306     size = 0;
307     hres = CoInternetParseIUri(pUri, PARSE_PATH_FROM_URL, 0, path, ARRAY_SIZE(path), &size, 0);
308     if(FAILED(hres)) {
309         WARN("CoInternetParseIUri failed: %08x\n", hres);
310         return report_result(pOIProtSink, hres, 0);
311     }
312 
313     file_handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
314             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
315     if(file_handle == INVALID_HANDLE_VALUE && (ptr = wcsrchr(path, '#'))) {
316         /* If path contains fragment part, try without it. */
317         *ptr = 0;
318         file_handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
319                 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
320     }
321     if(file_handle == INVALID_HANDLE_VALUE)
322         return report_result(pOIProtSink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
323 
324     if(!GetFileSizeEx(file_handle, &file_size)) {
325         CloseHandle(file_handle);
326         return report_result(pOIProtSink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
327     }
328 
329     This->file = file_handle;
330     This->size = file_size.u.LowPart;
331     IInternetProtocolSink_ReportProgress(pOIProtSink,  BINDSTATUS_CACHEFILENAMEAVAILABLE, path);
332 
333     hres = IUri_GetExtension(pUri, &ext);
334     if(SUCCEEDED(hres)) {
335         if(hres == S_OK && *ext) {
336             if((ptr = wcschr(ext, '#')))
337                 *ptr = 0;
338             hres = find_mime_from_ext(ext, &mime);
339             if(SUCCEEDED(hres)) {
340                 IInternetProtocolSink_ReportProgress(pOIProtSink,
341                         (grfBINDF & BINDF_FROMURLMON) ?
342                         BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
343                         mime);
344                 CoTaskMemFree(mime);
345             }
346         }
347         SysFreeString(ext);
348     }
349 
350     IInternetProtocolSink_ReportData(pOIProtSink,
351             BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
352             This->size, This->size);
353 
354     return report_result(pOIProtSink, S_OK, 0);
355 }
356 
357 static const IInternetProtocolExVtbl FileProtocolExVtbl = {
358     FileProtocol_QueryInterface,
359     FileProtocol_AddRef,
360     FileProtocol_Release,
361     FileProtocol_Start,
362     FileProtocol_Continue,
363     FileProtocol_Abort,
364     FileProtocol_Terminate,
365     FileProtocol_Suspend,
366     FileProtocol_Resume,
367     FileProtocol_Read,
368     FileProtocol_Seek,
369     FileProtocol_LockRequest,
370     FileProtocol_UnlockRequest,
371     FileProtocol_StartEx
372 };
373 
374 static HRESULT WINAPI FilePriority_QueryInterface(IInternetPriority *iface,
375                                                   REFIID riid, void **ppv)
376 {
377     FileProtocol *This = impl_from_IInternetPriority(iface);
378     return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
379 }
380 
381 static ULONG WINAPI FilePriority_AddRef(IInternetPriority *iface)
382 {
383     FileProtocol *This = impl_from_IInternetPriority(iface);
384     return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
385 }
386 
387 static ULONG WINAPI FilePriority_Release(IInternetPriority *iface)
388 {
389     FileProtocol *This = impl_from_IInternetPriority(iface);
390     return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
391 }
392 
393 static HRESULT WINAPI FilePriority_SetPriority(IInternetPriority *iface, LONG nPriority)
394 {
395     FileProtocol *This = impl_from_IInternetPriority(iface);
396 
397     TRACE("(%p)->(%d)\n", This, nPriority);
398 
399     This->priority = nPriority;
400     return S_OK;
401 }
402 
403 static HRESULT WINAPI FilePriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
404 {
405     FileProtocol *This = impl_from_IInternetPriority(iface);
406 
407     TRACE("(%p)->(%p)\n", This, pnPriority);
408 
409     *pnPriority = This->priority;
410     return S_OK;
411 }
412 
413 static const IInternetPriorityVtbl FilePriorityVtbl = {
414     FilePriority_QueryInterface,
415     FilePriority_AddRef,
416     FilePriority_Release,
417     FilePriority_SetPriority,
418     FilePriority_GetPriority
419 };
420 
421 HRESULT FileProtocol_Construct(IUnknown *outer, LPVOID *ppobj)
422 {
423     FileProtocol *ret;
424 
425     TRACE("(%p %p)\n", outer, ppobj);
426 
427     URLMON_LockModule();
428 
429     ret = heap_alloc(sizeof(FileProtocol));
430 
431     ret->IUnknown_inner.lpVtbl = &FileProtocolUnkVtbl;
432     ret->IInternetProtocolEx_iface.lpVtbl = &FileProtocolExVtbl;
433     ret->IInternetPriority_iface.lpVtbl = &FilePriorityVtbl;
434     ret->file = INVALID_HANDLE_VALUE;
435     ret->priority = 0;
436     ret->ref = 1;
437     ret->outer = outer ? outer : &ret->IUnknown_inner;
438 
439     *ppobj = &ret->IUnknown_inner;
440     return S_OK;
441 }
442