gdiplus: GdipGetLineGammaCorrection should check result pointer. Test added.
[wine] / dlls / shdocvw / intshcut.c
1 /*
2  * Copyright 2008 Damjan Jovanovic
3  *
4  * ShellLink's barely documented cousin that handles URLs.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 /*
22  * TODO:
23  * fd.o desktop and menu integration
24  * Implement the IShellLinkA/W interfaces
25  * Handle the SetURL flags
26  * Loading .url files
27  * Implement any other interfaces? Does any software actually use them?
28  *
29  * The installer for the Zuma Deluxe Popcap game is good for testing.
30  */
31
32 #include "wine/debug.h"
33 #include "shdocvw.h"
34 #include "objidl.h"
35 #include "shobjidl.h"
36 #include "intshcut.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
39
40 typedef struct
41 {
42     IUniformResourceLocatorA uniformResourceLocatorA;
43     IUniformResourceLocatorW uniformResourceLocatorW;
44     IPersistFile persistFile;
45
46     LONG refCount;
47
48     WCHAR *url;
49     BOOLEAN isDirty;
50     LPOLESTR currentFile;
51 } InternetShortcut;
52
53 /* utility functions */
54
55 static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
56 {
57     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorA));
58 }
59
60 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
61 {
62     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorW));
63 }
64
65 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
66 {
67     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, persistFile));
68 }
69
70 /* interface functions */
71
72 static HRESULT WINAPI Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
73 {
74     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
75     *ppvObject = NULL;
76     if (IsEqualGUID(&IID_IUnknown, riid))
77         *ppvObject = &This->uniformResourceLocatorA;
78     else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
79         *ppvObject = &This->uniformResourceLocatorA;
80     else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
81         *ppvObject = &This->uniformResourceLocatorW;
82     else if (IsEqualGUID(&IID_IPersistFile, riid))
83         *ppvObject = &This->persistFile;
84     else if (IsEqualGUID(&IID_IShellLinkA, riid))
85     {
86         FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
87         return E_NOINTERFACE;
88     }
89     else if (IsEqualGUID(&IID_IShellLinkW, riid))
90     {
91         FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
92         return E_NOINTERFACE;
93     }
94     else
95     {
96         FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid));
97         return E_NOINTERFACE;
98     }
99     IUnknown_AddRef((IUnknown*)*ppvObject);
100     return S_OK;
101 }
102
103 static ULONG WINAPI Unknown_AddRef(InternetShortcut *This)
104 {
105     TRACE("(%p)\n", This);
106     return InterlockedIncrement(&This->refCount);
107 }
108
109 static ULONG WINAPI Unknown_Release(InternetShortcut *This)
110 {
111     ULONG count;
112     TRACE("(%p)\n", This);
113     count = InterlockedDecrement(&This->refCount);
114     if (count == 0)
115     {
116         CoTaskMemFree(This->url);
117         CoTaskMemFree(This->currentFile);
118         heap_free(This);
119         SHDOCVW_UnlockModule();
120     }
121     return count;
122 }
123
124 static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject)
125 {
126     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
127     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
128     return Unknown_QueryInterface(This, riid, ppvObject);
129 }
130
131 static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url)
132 {
133     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
134     TRACE("(%p)\n", url);
135     return Unknown_AddRef(This);
136 }
137
138 static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url)
139 {
140     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
141     TRACE("(%p)\n", url);
142     return Unknown_Release(This);
143 }
144
145 static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags)
146 {
147     WCHAR *newURL = NULL;
148     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
149     TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags);
150     if (dwInFlags != 0)
151         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
152     if (pcszURL != NULL)
153     {
154         newURL = co_strdupW(pcszURL);
155         if (newURL == NULL)
156             return E_OUTOFMEMORY;
157     }
158     CoTaskMemFree(This->url);
159     This->url = newURL;
160     This->isDirty = TRUE;
161     return S_OK;
162 }
163
164 static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL)
165 {
166     HRESULT hr = S_OK;
167     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
168     TRACE("(%p, %p)\n", url, ppszURL);
169     if (This->url == NULL)
170         *ppszURL = NULL;
171     else
172     {
173         *ppszURL = co_strdupW(This->url);
174         if (*ppszURL == NULL)
175             hr = E_OUTOFMEMORY;
176     }
177     return hr;
178 }
179
180 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
181 {
182     FIXME("(%p, %p): stub\n", url, pCommandInfo);
183     return E_NOTIMPL;
184 }
185
186 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
187 {
188     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
189     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
190     return Unknown_QueryInterface(This, riid, ppvObject);
191 }
192
193 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
194 {
195     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
196     TRACE("(%p)\n", url);
197     return Unknown_AddRef(This);
198 }
199
200 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
201 {
202     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
203     TRACE("(%p)\n", url);
204     return Unknown_Release(This);
205 }
206
207 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
208 {
209     WCHAR *newURL = NULL;
210     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
211     TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
212     if (dwInFlags != 0)
213         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
214     if (pcszURL != NULL)
215     {
216         newURL = co_strdupAtoW(pcszURL);
217         if (newURL == NULL)
218             return E_OUTOFMEMORY;
219     }
220     CoTaskMemFree(This->url);
221     This->url = newURL;
222     This->isDirty = TRUE;
223     return S_OK;
224 }
225
226 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
227 {
228     HRESULT hr = S_OK;
229     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
230     TRACE("(%p, %p)\n", url, ppszURL);
231     if (This->url == NULL)
232         *ppszURL = NULL;
233     else
234     {
235         *ppszURL = co_strdupWtoA(This->url);
236         if (*ppszURL == NULL)
237             hr = E_OUTOFMEMORY;
238     }
239     return hr;
240 }
241
242 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
243 {
244     FIXME("(%p, %p): stub\n", url, pCommandInfo);
245     return E_NOTIMPL;
246 }
247
248 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
249 {
250     InternetShortcut *This = impl_from_IPersistFile(pFile);
251     TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
252     return Unknown_QueryInterface(This, riid, ppvObject);
253 }
254
255 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
256 {
257     InternetShortcut *This = impl_from_IPersistFile(pFile);
258     TRACE("(%p)\n", pFile);
259     return Unknown_AddRef(This);
260 }
261
262 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
263 {
264     InternetShortcut *This = impl_from_IPersistFile(pFile);
265     TRACE("(%p)\n", pFile);
266     return Unknown_Release(This);
267 }
268
269 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
270 {
271     TRACE("(%p, %p)\n", pFile, pClassID);
272     *pClassID = CLSID_InternetShortcut;
273     return S_OK;
274 }
275
276 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
277 {
278     InternetShortcut *This = impl_from_IPersistFile(pFile);
279     TRACE("(%p)\n", pFile);
280     return This->isDirty ? S_OK : S_FALSE;
281 }
282
283 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
284 {
285     FIXME("(%p, %p, 0x%x): stub\n", pFile, pszFileName, dwMode);
286     return E_NOTIMPL;
287 }
288
289 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
290 {
291     HRESULT hr = S_OK;
292     INT len;
293     CHAR *url;
294     InternetShortcut *This = impl_from_IPersistFile(pFile);
295
296     TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
297
298     if (pszFileName != NULL && fRemember)
299     {
300         LPOLESTR oldFile = This->currentFile;
301         This->currentFile = co_strdupW(pszFileName);
302         if (This->currentFile == NULL)
303         {
304             This->currentFile = oldFile;
305             return E_OUTOFMEMORY;
306         }
307         CoTaskMemFree(oldFile);
308     }
309     if (This->url == NULL)
310         return E_FAIL;
311
312     /* Windows seems to always write:
313      *   ASCII "[InternetShortcut]" headers
314      *   ASCII names in "name=value" pairs
315      *   An ASCII (probably UTF8?) value in "URL=..."
316      */
317     len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
318     url = heap_alloc(len);
319     if (url != NULL)
320     {
321         HANDLE file;
322         WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
323         file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
324         if (file != INVALID_HANDLE_VALUE)
325         {
326             DWORD bytesWritten;
327             char str_header[] = "[InternetShortcut]";
328             char str_URL[] = "URL=";
329             char str_eol[] = "\r\n";
330
331             WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL);
332             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
333             WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL);
334             WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
335             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
336             CloseHandle(file);
337             if (pszFileName == NULL || fRemember)
338                 This->isDirty = FALSE;
339         }
340         else
341             hr = E_FAIL;
342         heap_free(url);
343     }
344     else
345         hr = E_OUTOFMEMORY;
346
347     return hr;
348 }
349
350 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
351 {
352     FIXME("(%p, %p): stub\n", pFile, pszFileName);
353     return E_NOTIMPL;
354 }
355
356 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
357 {
358     HRESULT hr = S_OK;
359     InternetShortcut *This = impl_from_IPersistFile(pFile);
360     TRACE("(%p, %p)\n", pFile, ppszFileName);
361     if (This->currentFile == NULL)
362         *ppszFileName = NULL;
363     else
364     {
365         *ppszFileName = co_strdupW(This->currentFile);
366         if (*ppszFileName == NULL)
367             hr = E_OUTOFMEMORY;
368     }
369     return hr;
370 }
371
372
373
374 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
375     UniformResourceLocatorW_QueryInterface,
376     UniformResourceLocatorW_AddRef,
377     UniformResourceLocatorW_Release,
378     UniformResourceLocatorW_SetUrl,
379     UniformResourceLocatorW_GetUrl,
380     UniformResourceLocatorW_InvokeCommand
381 };
382
383 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
384     UniformResourceLocatorA_QueryInterface,
385     UniformResourceLocatorA_AddRef,
386     UniformResourceLocatorA_Release,
387     UniformResourceLocatorA_SetUrl,
388     UniformResourceLocatorA_GetUrl,
389     UniformResourceLocatorA_InvokeCommand
390 };
391
392 static const IPersistFileVtbl persistFileVtbl = {
393     PersistFile_QueryInterface,
394     PersistFile_AddRef,
395     PersistFile_Release,
396     PersistFile_GetClassID,
397     PersistFile_IsDirty,
398     PersistFile_Load,
399     PersistFile_Save,
400     PersistFile_SaveCompleted,
401     PersistFile_GetCurFile
402 };
403
404 HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv)
405 {
406     InternetShortcut *This;
407     HRESULT hr;
408
409     TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv);
410
411     *ppv = NULL;
412
413     if(pOuter)
414         return CLASS_E_NOAGGREGATION;
415
416     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
417     if (This)
418     {
419         This->uniformResourceLocatorA.lpVtbl = &uniformResourceLocatorAVtbl;
420         This->uniformResourceLocatorW.lpVtbl = &uniformResourceLocatorWVtbl;
421         This->persistFile.lpVtbl = &persistFileVtbl;
422         This->refCount = 0;
423         hr = Unknown_QueryInterface(This, riid, ppv);
424         if (SUCCEEDED(hr))
425             SHDOCVW_LockModule();
426         else
427             heap_free(This);
428         return hr;
429     }
430     else
431         return E_OUTOFMEMORY;
432 }