urlmon: Use ifaces instead of vtbl pointers in FileProtocol.
[wine] / dlls / urlmon / file.c
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     IInternetProtocolEx IInternetProtocolEx_iface;
29     IInternetPriority   IInternetPriority_iface;
30
31     HANDLE file;
32     ULONG size;
33     LONG priority;
34
35     LONG ref;
36 } FileProtocol;
37
38 static inline FileProtocol *impl_from_IInternetProtocolEx(IInternetProtocolEx *iface)
39 {
40     return CONTAINING_RECORD(iface, FileProtocol, IInternetProtocolEx_iface);
41 }
42
43 static inline FileProtocol *impl_from_IInternetPriority(IInternetPriority *iface)
44 {
45     return CONTAINING_RECORD(iface, FileProtocol, IInternetPriority_iface);
46 }
47
48 static HRESULT WINAPI FileProtocol_QueryInterface(IInternetProtocolEx *iface, REFIID riid, void **ppv)
49 {
50     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
51
52     *ppv = NULL;
53     if(IsEqualGUID(&IID_IUnknown, riid)) {
54         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
55         *ppv = &This->IInternetProtocolEx_iface;
56     }else if(IsEqualGUID(&IID_IInternetProtocolRoot, riid)) {
57         TRACE("(%p)->(IID_IInternetProtocolRoot %p)\n", This, ppv);
58         *ppv = &This->IInternetProtocolEx_iface;
59     }else if(IsEqualGUID(&IID_IInternetProtocol, riid)) {
60         TRACE("(%p)->(IID_IInternetProtocol %p)\n", This, ppv);
61         *ppv = &This->IInternetProtocolEx_iface;
62     }else if(IsEqualGUID(&IID_IInternetProtocolEx, riid)) {
63         TRACE("(%p)->(IID_IInternetProtocolEx %p)\n", This, ppv);
64         *ppv = &This->IInternetProtocolEx_iface;
65     }else if(IsEqualGUID(&IID_IInternetPriority, riid)) {
66         TRACE("(%p)->(IID_IInternetPriority %p)\n", This, ppv);
67         *ppv = &This->IInternetPriority_iface;
68     }
69
70     if(*ppv) {
71         IInternetProtocol_AddRef(iface);
72         return S_OK;
73     }
74
75     WARN("not supported interface %s\n", debugstr_guid(riid));
76     return E_NOINTERFACE;
77 }
78
79 static ULONG WINAPI FileProtocol_AddRef(IInternetProtocolEx *iface)
80 {
81     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
82     LONG ref = InterlockedIncrement(&This->ref);
83     TRACE("(%p) ref=%d\n", This, ref);
84     return ref;
85 }
86
87 static ULONG WINAPI FileProtocol_Release(IInternetProtocolEx *iface)
88 {
89     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
90     LONG ref = InterlockedDecrement(&This->ref);
91
92     TRACE("(%p) ref=%d\n", This, ref);
93
94     if(!ref) {
95         if(This->file != INVALID_HANDLE_VALUE)
96             CloseHandle(This->file);
97         heap_free(This);
98
99         URLMON_UnlockModule();
100     }
101
102     return ref;
103 }
104
105 static HRESULT WINAPI FileProtocol_Start(IInternetProtocolEx *iface, LPCWSTR szUrl,
106         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
107         DWORD grfPI, HANDLE_PTR dwReserved)
108 {
109     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
110     IUri *uri;
111     HRESULT hres;
112
113     TRACE("(%p)->(%s %p %p %08x %lx)\n", This, debugstr_w(szUrl), pOIProtSink,
114             pOIBindInfo, grfPI, dwReserved);
115
116     hres = CreateUri(szUrl, Uri_CREATE_FILE_USE_DOS_PATH, 0, &uri);
117     if(FAILED(hres))
118         return hres;
119
120     hres = IInternetProtocolEx_StartEx(&This->IInternetProtocolEx_iface, uri, pOIProtSink,
121             pOIBindInfo, grfPI, (HANDLE*)dwReserved);
122
123     IUri_Release(uri);
124     return hres;
125 }
126
127 static HRESULT WINAPI FileProtocol_Continue(IInternetProtocolEx *iface, PROTOCOLDATA *pProtocolData)
128 {
129     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
130     FIXME("(%p)->(%p)\n", This, pProtocolData);
131     return E_NOTIMPL;
132 }
133
134 static HRESULT WINAPI FileProtocol_Abort(IInternetProtocolEx *iface, HRESULT hrReason,
135         DWORD dwOptions)
136 {
137     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
138     FIXME("(%p)->(%08x %08x)\n", This, hrReason, dwOptions);
139     return E_NOTIMPL;
140 }
141
142 static HRESULT WINAPI FileProtocol_Terminate(IInternetProtocolEx *iface, DWORD dwOptions)
143 {
144     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
145
146     TRACE("(%p)->(%08x)\n", This, dwOptions);
147
148     return S_OK;
149 }
150
151 static HRESULT WINAPI FileProtocol_Suspend(IInternetProtocolEx *iface)
152 {
153     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
154     FIXME("(%p)\n", This);
155     return E_NOTIMPL;
156 }
157
158 static HRESULT WINAPI FileProtocol_Resume(IInternetProtocolEx *iface)
159 {
160     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
161     FIXME("(%p)\n", This);
162     return E_NOTIMPL;
163 }
164
165 static HRESULT WINAPI FileProtocol_Read(IInternetProtocolEx *iface, void *pv,
166         ULONG cb, ULONG *pcbRead)
167 {
168     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
169     DWORD read = 0;
170
171     TRACE("(%p)->(%p %u %p)\n", This, pv, cb, pcbRead);
172
173     if (pcbRead)
174         *pcbRead = 0;
175
176     if(This->file == INVALID_HANDLE_VALUE)
177         return INET_E_DATA_NOT_AVAILABLE;
178
179     if (!ReadFile(This->file, pv, cb, &read, NULL))
180         return INET_E_DOWNLOAD_FAILURE;
181
182     if(pcbRead)
183         *pcbRead = read;
184     
185     return cb == read ? S_OK : S_FALSE;
186 }
187
188 static HRESULT WINAPI FileProtocol_Seek(IInternetProtocolEx *iface, LARGE_INTEGER dlibMove,
189         DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
190 {
191     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
192     FIXME("(%p)->(%d %d %p)\n", This, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
193     return E_NOTIMPL;
194 }
195
196 static HRESULT WINAPI FileProtocol_LockRequest(IInternetProtocolEx *iface, DWORD dwOptions)
197 {
198     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
199
200     TRACE("(%p)->(%08x)\n", This, dwOptions);
201
202     return S_OK;
203 }
204
205 static HRESULT WINAPI FileProtocol_UnlockRequest(IInternetProtocolEx *iface)
206 {
207     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
208
209     TRACE("(%p)\n", This);
210
211     return S_OK;
212 }
213
214 static inline HRESULT report_result(IInternetProtocolSink *protocol_sink, HRESULT hres, DWORD res)
215 {
216     IInternetProtocolSink_ReportResult(protocol_sink, hres, res, NULL);
217     return hres;
218 }
219
220 static HRESULT open_file(FileProtocol *This, const WCHAR *path, IInternetProtocolSink *protocol_sink)
221 {
222     LARGE_INTEGER size;
223     HANDLE file;
224
225     file = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, NULL,
226             OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
227     if(file == INVALID_HANDLE_VALUE)
228         return report_result(protocol_sink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
229
230     if(!GetFileSizeEx(file, &size)) {
231         CloseHandle(file);
232         return report_result(protocol_sink, INET_E_RESOURCE_NOT_FOUND, GetLastError());
233     }
234
235     This->file = file;
236     This->size = size.u.LowPart;
237
238     IInternetProtocolSink_ReportProgress(protocol_sink,
239             BINDSTATUS_CACHEFILENAMEAVAILABLE, path);
240     return S_OK;
241 }
242
243 static HRESULT WINAPI FileProtocol_StartEx(IInternetProtocolEx *iface, IUri *pUri,
244         IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo,
245         DWORD grfPI, HANDLE *dwReserved)
246 {
247     FileProtocol *This = impl_from_IInternetProtocolEx(iface);
248     BINDINFO bindinfo;
249     DWORD grfBINDF = 0;
250     DWORD scheme;
251     LPWSTR mime = NULL;
252     WCHAR null_char = 0;
253     BSTR path, url;
254     HRESULT hres;
255
256     TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
257             pOIBindInfo, grfPI, dwReserved);
258
259     if(!pUri)
260         return E_INVALIDARG;
261
262     scheme = 0;
263     hres = IUri_GetScheme(pUri, &scheme);
264     if(FAILED(hres))
265         return hres;
266     if(scheme != URL_SCHEME_FILE)
267         return E_INVALIDARG;
268
269     memset(&bindinfo, 0, sizeof(bindinfo));
270     bindinfo.cbSize = sizeof(BINDINFO);
271     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
272     if(FAILED(hres)) {
273         WARN("GetBindInfo failed: %08x\n", hres);
274         return hres;
275     }
276
277     ReleaseBindInfo(&bindinfo);
278
279     if(!(grfBINDF & BINDF_FROMURLMON))
280         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
281
282     if(This->file != INVALID_HANDLE_VALUE) {
283         IInternetProtocolSink_ReportData(pOIProtSink,
284                 BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
285                 This->size, This->size);
286         return S_OK;
287     }
288
289     IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, &null_char);
290
291     hres = IUri_GetPath(pUri, &path);
292     if(FAILED(hres)) {
293         ERR("GetPath failed: %08x\n", hres);
294         return report_result(pOIProtSink, hres, 0);
295     }
296
297     hres = open_file(This, path, pOIProtSink);
298     SysFreeString(path);
299     if(FAILED(hres))
300         return hres;
301
302     hres = IUri_GetDisplayUri(pUri, &url);
303     if(hres == S_OK) {
304         hres = FindMimeFromData(NULL, url, NULL, 0, NULL, 0, &mime, 0);
305         SysFreeString(url);
306         if(SUCCEEDED(hres)) {
307             IInternetProtocolSink_ReportProgress(pOIProtSink,
308                     (grfBINDF & BINDF_FROMURLMON) ?
309                      BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
310                     mime);
311             CoTaskMemFree(mime);
312         }
313     }
314
315     IInternetProtocolSink_ReportData(pOIProtSink,
316             BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
317             This->size, This->size);
318
319     return report_result(pOIProtSink, S_OK, 0);
320 }
321
322 static const IInternetProtocolExVtbl FileProtocolExVtbl = {
323     FileProtocol_QueryInterface,
324     FileProtocol_AddRef,
325     FileProtocol_Release,
326     FileProtocol_Start,
327     FileProtocol_Continue,
328     FileProtocol_Abort,
329     FileProtocol_Terminate,
330     FileProtocol_Suspend,
331     FileProtocol_Resume,
332     FileProtocol_Read,
333     FileProtocol_Seek,
334     FileProtocol_LockRequest,
335     FileProtocol_UnlockRequest,
336     FileProtocol_StartEx
337 };
338
339 static HRESULT WINAPI FilePriority_QueryInterface(IInternetPriority *iface,
340                                                   REFIID riid, void **ppv)
341 {
342     FileProtocol *This = impl_from_IInternetPriority(iface);
343     return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
344 }
345
346 static ULONG WINAPI FilePriority_AddRef(IInternetPriority *iface)
347 {
348     FileProtocol *This = impl_from_IInternetPriority(iface);
349     return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
350 }
351
352 static ULONG WINAPI FilePriority_Release(IInternetPriority *iface)
353 {
354     FileProtocol *This = impl_from_IInternetPriority(iface);
355     return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
356 }
357
358 static HRESULT WINAPI FilePriority_SetPriority(IInternetPriority *iface, LONG nPriority)
359 {
360     FileProtocol *This = impl_from_IInternetPriority(iface);
361
362     TRACE("(%p)->(%d)\n", This, nPriority);
363
364     This->priority = nPriority;
365     return S_OK;
366 }
367
368 static HRESULT WINAPI FilePriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
369 {
370     FileProtocol *This = impl_from_IInternetPriority(iface);
371
372     TRACE("(%p)->(%p)\n", This, pnPriority);
373
374     *pnPriority = This->priority;
375     return S_OK;
376 }
377
378 static const IInternetPriorityVtbl FilePriorityVtbl = {
379     FilePriority_QueryInterface,
380     FilePriority_AddRef,
381     FilePriority_Release,
382     FilePriority_SetPriority,
383     FilePriority_GetPriority
384 };
385
386 HRESULT FileProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
387 {
388     FileProtocol *ret;
389
390     TRACE("(%p %p)\n", pUnkOuter, ppobj);
391
392     URLMON_LockModule();
393
394     ret = heap_alloc(sizeof(FileProtocol));
395
396     ret->IInternetProtocolEx_iface.lpVtbl = &FileProtocolExVtbl;
397     ret->IInternetPriority_iface.lpVtbl = &FilePriorityVtbl;
398     ret->file = INVALID_HANDLE_VALUE;
399     ret->priority = 0;
400     ret->ref = 1;
401
402     *ppobj = &ret->IInternetProtocolEx_iface;
403     return S_OK;
404 }