Release 1.5.29.
[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         IInternetProtocolEx_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     WCHAR path[MAX_PATH];
249     BINDINFO bindinfo;
250     DWORD grfBINDF = 0;
251     DWORD scheme, size;
252     LPWSTR mime = NULL;
253     WCHAR null_char = 0;
254     BSTR url;
255     HRESULT hres;
256
257     TRACE("(%p)->(%p %p %p %08x %p)\n", This, pUri, pOIProtSink,
258             pOIBindInfo, grfPI, dwReserved);
259
260     if(!pUri)
261         return E_INVALIDARG;
262
263     scheme = 0;
264     hres = IUri_GetScheme(pUri, &scheme);
265     if(FAILED(hres))
266         return hres;
267     if(scheme != URL_SCHEME_FILE)
268         return E_INVALIDARG;
269
270     memset(&bindinfo, 0, sizeof(bindinfo));
271     bindinfo.cbSize = sizeof(BINDINFO);
272     hres = IInternetBindInfo_GetBindInfo(pOIBindInfo, &grfBINDF, &bindinfo);
273     if(FAILED(hres)) {
274         WARN("GetBindInfo failed: %08x\n", hres);
275         return hres;
276     }
277
278     ReleaseBindInfo(&bindinfo);
279
280     if(!(grfBINDF & BINDF_FROMURLMON))
281         IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_DIRECTBIND, NULL);
282
283     if(This->file != INVALID_HANDLE_VALUE) {
284         IInternetProtocolSink_ReportData(pOIProtSink,
285                 BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
286                 This->size, This->size);
287         return S_OK;
288     }
289
290     IInternetProtocolSink_ReportProgress(pOIProtSink, BINDSTATUS_SENDINGREQUEST, &null_char);
291
292     size = 0;
293     hres = CoInternetParseIUri(pUri, PARSE_PATH_FROM_URL, 0, path, sizeof(path)/sizeof(WCHAR), &size, 0);
294     if(FAILED(hres)) {
295         WARN("CoInternetParseIUri failed: %08x\n", hres);
296         return report_result(pOIProtSink, hres, 0);
297     }
298
299     hres = open_file(This, path, pOIProtSink);
300     if(FAILED(hres))
301         return hres;
302
303     hres = IUri_GetDisplayUri(pUri, &url);
304     if(hres == S_OK) {
305         hres = FindMimeFromData(NULL, url, NULL, 0, NULL, 0, &mime, 0);
306         SysFreeString(url);
307         if(SUCCEEDED(hres)) {
308             IInternetProtocolSink_ReportProgress(pOIProtSink,
309                     (grfBINDF & BINDF_FROMURLMON) ?
310                      BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE : BINDSTATUS_MIMETYPEAVAILABLE,
311                     mime);
312             CoTaskMemFree(mime);
313         }
314     }
315
316     IInternetProtocolSink_ReportData(pOIProtSink,
317             BSCF_FIRSTDATANOTIFICATION|BSCF_LASTDATANOTIFICATION,
318             This->size, This->size);
319
320     return report_result(pOIProtSink, S_OK, 0);
321 }
322
323 static const IInternetProtocolExVtbl FileProtocolExVtbl = {
324     FileProtocol_QueryInterface,
325     FileProtocol_AddRef,
326     FileProtocol_Release,
327     FileProtocol_Start,
328     FileProtocol_Continue,
329     FileProtocol_Abort,
330     FileProtocol_Terminate,
331     FileProtocol_Suspend,
332     FileProtocol_Resume,
333     FileProtocol_Read,
334     FileProtocol_Seek,
335     FileProtocol_LockRequest,
336     FileProtocol_UnlockRequest,
337     FileProtocol_StartEx
338 };
339
340 static HRESULT WINAPI FilePriority_QueryInterface(IInternetPriority *iface,
341                                                   REFIID riid, void **ppv)
342 {
343     FileProtocol *This = impl_from_IInternetPriority(iface);
344     return IInternetProtocolEx_QueryInterface(&This->IInternetProtocolEx_iface, riid, ppv);
345 }
346
347 static ULONG WINAPI FilePriority_AddRef(IInternetPriority *iface)
348 {
349     FileProtocol *This = impl_from_IInternetPriority(iface);
350     return IInternetProtocolEx_AddRef(&This->IInternetProtocolEx_iface);
351 }
352
353 static ULONG WINAPI FilePriority_Release(IInternetPriority *iface)
354 {
355     FileProtocol *This = impl_from_IInternetPriority(iface);
356     return IInternetProtocolEx_Release(&This->IInternetProtocolEx_iface);
357 }
358
359 static HRESULT WINAPI FilePriority_SetPriority(IInternetPriority *iface, LONG nPriority)
360 {
361     FileProtocol *This = impl_from_IInternetPriority(iface);
362
363     TRACE("(%p)->(%d)\n", This, nPriority);
364
365     This->priority = nPriority;
366     return S_OK;
367 }
368
369 static HRESULT WINAPI FilePriority_GetPriority(IInternetPriority *iface, LONG *pnPriority)
370 {
371     FileProtocol *This = impl_from_IInternetPriority(iface);
372
373     TRACE("(%p)->(%p)\n", This, pnPriority);
374
375     *pnPriority = This->priority;
376     return S_OK;
377 }
378
379 static const IInternetPriorityVtbl FilePriorityVtbl = {
380     FilePriority_QueryInterface,
381     FilePriority_AddRef,
382     FilePriority_Release,
383     FilePriority_SetPriority,
384     FilePriority_GetPriority
385 };
386
387 HRESULT FileProtocol_Construct(IUnknown *pUnkOuter, LPVOID *ppobj)
388 {
389     FileProtocol *ret;
390
391     TRACE("(%p %p)\n", pUnkOuter, ppobj);
392
393     URLMON_LockModule();
394
395     ret = heap_alloc(sizeof(FileProtocol));
396
397     ret->IInternetProtocolEx_iface.lpVtbl = &FileProtocolExVtbl;
398     ret->IInternetPriority_iface.lpVtbl = &FilePriorityVtbl;
399     ret->file = INVALID_HANDLE_VALUE;
400     ret->priority = 0;
401     ret->ref = 1;
402
403     *ppobj = &ret->IInternetProtocolEx_iface;
404     return S_OK;
405 }