riched32/tests: Remove the todo_wine logic where appropriate.
[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  * Implement the IShellLinkA/W interfaces
24  * Handle the SetURL flags
25  * Implement any other interfaces? Does any software actually use them?
26  *
27  * The installer for the Zuma Deluxe Popcap game is good for testing.
28  */
29
30 #include "wine/debug.h"
31 #include "shdocvw.h"
32 #include "objidl.h"
33 #include "shobjidl.h"
34 #include "intshcut.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
37
38 typedef struct
39 {
40     IUniformResourceLocatorA uniformResourceLocatorA;
41     IUniformResourceLocatorW uniformResourceLocatorW;
42     IPersistFile persistFile;
43
44     LONG refCount;
45
46     WCHAR *url;
47     BOOLEAN isDirty;
48     LPOLESTR currentFile;
49 } InternetShortcut;
50
51 /* utility functions */
52
53 static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
54 {
55     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorA));
56 }
57
58 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
59 {
60     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorW));
61 }
62
63 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
64 {
65     return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, persistFile));
66 }
67
68 static BOOL StartLinkProcessor(LPCOLESTR szLink)
69 {
70     static const WCHAR szFormat[] = {
71         'w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',
72         ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
73     LONG len;
74     LPWSTR buffer;
75     STARTUPINFOW si;
76     PROCESS_INFORMATION pi;
77     BOOL ret;
78
79     len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
80     buffer = heap_alloc( len );
81     if( !buffer )
82         return FALSE;
83
84     wsprintfW( buffer, szFormat, szLink );
85
86     TRACE("starting %s\n",debugstr_w(buffer));
87
88     memset(&si, 0, sizeof(si));
89     si.cb = sizeof(si);
90
91     ret = CreateProcessW( NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi );
92
93     HeapFree( GetProcessHeap(), 0, buffer );
94
95     if (ret)
96     {
97         CloseHandle( pi.hProcess );
98         CloseHandle( pi.hThread );
99     }
100
101     return ret;
102 }
103
104 /* interface functions */
105
106 static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
107 {
108     TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
109     *ppvObject = NULL;
110     if (IsEqualGUID(&IID_IUnknown, riid))
111         *ppvObject = &This->uniformResourceLocatorA;
112     else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
113         *ppvObject = &This->uniformResourceLocatorA;
114     else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
115         *ppvObject = &This->uniformResourceLocatorW;
116     else if (IsEqualGUID(&IID_IPersistFile, riid))
117         *ppvObject = &This->persistFile;
118     else if (IsEqualGUID(&IID_IShellLinkA, riid))
119     {
120         FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
121         return E_NOINTERFACE;
122     }
123     else if (IsEqualGUID(&IID_IShellLinkW, riid))
124     {
125         FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
126         return E_NOINTERFACE;
127     }
128     else
129     {
130         FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid));
131         return E_NOINTERFACE;
132     }
133     IUnknown_AddRef((IUnknown*)*ppvObject);
134     return S_OK;
135 }
136
137 static ULONG Unknown_AddRef(InternetShortcut *This)
138 {
139     TRACE("(%p)\n", This);
140     return InterlockedIncrement(&This->refCount);
141 }
142
143 static ULONG Unknown_Release(InternetShortcut *This)
144 {
145     ULONG count;
146     TRACE("(%p)\n", This);
147     count = InterlockedDecrement(&This->refCount);
148     if (count == 0)
149     {
150         CoTaskMemFree(This->url);
151         CoTaskMemFree(This->currentFile);
152         heap_free(This);
153         SHDOCVW_UnlockModule();
154     }
155     return count;
156 }
157
158 static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject)
159 {
160     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
161     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
162     return Unknown_QueryInterface(This, riid, ppvObject);
163 }
164
165 static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url)
166 {
167     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
168     TRACE("(%p)\n", url);
169     return Unknown_AddRef(This);
170 }
171
172 static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url)
173 {
174     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
175     TRACE("(%p)\n", url);
176     return Unknown_Release(This);
177 }
178
179 static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags)
180 {
181     WCHAR *newURL = NULL;
182     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
183     TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags);
184     if (dwInFlags != 0)
185         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
186     if (pcszURL != NULL)
187     {
188         newURL = co_strdupW(pcszURL);
189         if (newURL == NULL)
190             return E_OUTOFMEMORY;
191     }
192     CoTaskMemFree(This->url);
193     This->url = newURL;
194     This->isDirty = TRUE;
195     return S_OK;
196 }
197
198 static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL)
199 {
200     HRESULT hr = S_OK;
201     InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
202     TRACE("(%p, %p)\n", url, ppszURL);
203     if (This->url == NULL)
204         *ppszURL = NULL;
205     else
206     {
207         *ppszURL = co_strdupW(This->url);
208         if (*ppszURL == NULL)
209             hr = E_OUTOFMEMORY;
210     }
211     return hr;
212 }
213
214 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
215 {
216     FIXME("(%p, %p): stub\n", url, pCommandInfo);
217     return E_NOTIMPL;
218 }
219
220 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
221 {
222     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
223     TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
224     return Unknown_QueryInterface(This, riid, ppvObject);
225 }
226
227 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
228 {
229     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
230     TRACE("(%p)\n", url);
231     return Unknown_AddRef(This);
232 }
233
234 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
235 {
236     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
237     TRACE("(%p)\n", url);
238     return Unknown_Release(This);
239 }
240
241 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
242 {
243     WCHAR *newURL = NULL;
244     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
245     TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
246     if (dwInFlags != 0)
247         FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
248     if (pcszURL != NULL)
249     {
250         newURL = co_strdupAtoW(pcszURL);
251         if (newURL == NULL)
252             return E_OUTOFMEMORY;
253     }
254     CoTaskMemFree(This->url);
255     This->url = newURL;
256     This->isDirty = TRUE;
257     return S_OK;
258 }
259
260 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
261 {
262     HRESULT hr = S_OK;
263     InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
264     TRACE("(%p, %p)\n", url, ppszURL);
265     if (This->url == NULL)
266         *ppszURL = NULL;
267     else
268     {
269         *ppszURL = co_strdupWtoA(This->url);
270         if (*ppszURL == NULL)
271             hr = E_OUTOFMEMORY;
272     }
273     return hr;
274 }
275
276 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
277 {
278     FIXME("(%p, %p): stub\n", url, pCommandInfo);
279     return E_NOTIMPL;
280 }
281
282 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
283 {
284     InternetShortcut *This = impl_from_IPersistFile(pFile);
285     TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
286     return Unknown_QueryInterface(This, riid, ppvObject);
287 }
288
289 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
290 {
291     InternetShortcut *This = impl_from_IPersistFile(pFile);
292     TRACE("(%p)\n", pFile);
293     return Unknown_AddRef(This);
294 }
295
296 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
297 {
298     InternetShortcut *This = impl_from_IPersistFile(pFile);
299     TRACE("(%p)\n", pFile);
300     return Unknown_Release(This);
301 }
302
303 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
304 {
305     TRACE("(%p, %p)\n", pFile, pClassID);
306     *pClassID = CLSID_InternetShortcut;
307     return S_OK;
308 }
309
310 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
311 {
312     InternetShortcut *This = impl_from_IPersistFile(pFile);
313     TRACE("(%p)\n", pFile);
314     return This->isDirty ? S_OK : S_FALSE;
315 }
316
317 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
318 {
319     WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
320     WCHAR str_URL[] = {'U','R','L',0};
321     WCHAR *filename = NULL;
322     HRESULT hr;
323     InternetShortcut *This = impl_from_IPersistFile(pFile);
324     TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
325     if (dwMode != 0)
326         FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
327     filename = co_strdupW(pszFileName);
328     if (filename != NULL)
329     {
330         DWORD len = 128;
331         DWORD r;
332         WCHAR *url = CoTaskMemAlloc(len*sizeof(WCHAR));
333         if (url != NULL)
334         {
335             r = GetPrivateProfileStringW(str_header, str_URL, NULL, url, len, pszFileName);
336             while (r == len-1)
337             {
338                 CoTaskMemFree(url);
339                 len *= 2;
340                 url = CoTaskMemAlloc(len);
341                 if (url == NULL)
342                     break;
343                 r = GetPrivateProfileStringW(str_header, str_URL, NULL, url, len, pszFileName);
344             }
345             if (r == 0)
346                 hr = E_FAIL;
347             else if (url != NULL)
348             {
349                 CoTaskMemFree(This->currentFile);
350                 This->currentFile = filename;
351                 CoTaskMemFree(This->url);
352                 This->url = url;
353                 This->isDirty = FALSE;
354                 return S_OK;
355             }
356             else
357                 hr = E_OUTOFMEMORY;
358             CoTaskMemFree(url);
359         }
360         else
361             hr = E_OUTOFMEMORY;
362         CoTaskMemFree(filename);
363     }
364     else
365         hr = E_OUTOFMEMORY;
366     return hr;
367 }
368
369 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
370 {
371     HRESULT hr = S_OK;
372     INT len;
373     CHAR *url;
374     InternetShortcut *This = impl_from_IPersistFile(pFile);
375
376     TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
377
378     if (pszFileName != NULL && fRemember)
379     {
380         LPOLESTR oldFile = This->currentFile;
381         This->currentFile = co_strdupW(pszFileName);
382         if (This->currentFile == NULL)
383         {
384             This->currentFile = oldFile;
385             return E_OUTOFMEMORY;
386         }
387         CoTaskMemFree(oldFile);
388     }
389     if (This->url == NULL)
390         return E_FAIL;
391
392     /* Windows seems to always write:
393      *   ASCII "[InternetShortcut]" headers
394      *   ASCII names in "name=value" pairs
395      *   An ASCII (probably UTF8?) value in "URL=..."
396      */
397     len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
398     url = heap_alloc(len);
399     if (url != NULL)
400     {
401         HANDLE file;
402         WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
403         file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
404         if (file != INVALID_HANDLE_VALUE)
405         {
406             DWORD bytesWritten;
407             char str_header[] = "[InternetShortcut]";
408             char str_URL[] = "URL=";
409             char str_eol[] = "\r\n";
410
411             WriteFile(file, str_header, lstrlenA(str_header), &bytesWritten, NULL);
412             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
413             WriteFile(file, str_URL, lstrlenA(str_URL), &bytesWritten, NULL);
414             WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
415             WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
416             CloseHandle(file);
417             if (pszFileName == NULL || fRemember)
418                 This->isDirty = FALSE;
419             StartLinkProcessor(pszFileName);
420         }
421         else
422             hr = E_FAIL;
423         heap_free(url);
424     }
425     else
426         hr = E_OUTOFMEMORY;
427
428     return hr;
429 }
430
431 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
432 {
433     FIXME("(%p, %p): stub\n", pFile, pszFileName);
434     return E_NOTIMPL;
435 }
436
437 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
438 {
439     HRESULT hr = S_OK;
440     InternetShortcut *This = impl_from_IPersistFile(pFile);
441     TRACE("(%p, %p)\n", pFile, ppszFileName);
442     if (This->currentFile == NULL)
443         *ppszFileName = NULL;
444     else
445     {
446         *ppszFileName = co_strdupW(This->currentFile);
447         if (*ppszFileName == NULL)
448             hr = E_OUTOFMEMORY;
449     }
450     return hr;
451 }
452
453
454
455 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
456     UniformResourceLocatorW_QueryInterface,
457     UniformResourceLocatorW_AddRef,
458     UniformResourceLocatorW_Release,
459     UniformResourceLocatorW_SetUrl,
460     UniformResourceLocatorW_GetUrl,
461     UniformResourceLocatorW_InvokeCommand
462 };
463
464 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
465     UniformResourceLocatorA_QueryInterface,
466     UniformResourceLocatorA_AddRef,
467     UniformResourceLocatorA_Release,
468     UniformResourceLocatorA_SetUrl,
469     UniformResourceLocatorA_GetUrl,
470     UniformResourceLocatorA_InvokeCommand
471 };
472
473 static const IPersistFileVtbl persistFileVtbl = {
474     PersistFile_QueryInterface,
475     PersistFile_AddRef,
476     PersistFile_Release,
477     PersistFile_GetClassID,
478     PersistFile_IsDirty,
479     PersistFile_Load,
480     PersistFile_Save,
481     PersistFile_SaveCompleted,
482     PersistFile_GetCurFile
483 };
484
485 HRESULT InternetShortcut_Create(IUnknown *pOuter, REFIID riid, void **ppv)
486 {
487     InternetShortcut *This;
488     HRESULT hr;
489
490     TRACE("(%p, %s, %p)\n", pOuter, debugstr_guid(riid), ppv);
491
492     *ppv = NULL;
493
494     if(pOuter)
495         return CLASS_E_NOAGGREGATION;
496
497     This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
498     if (This)
499     {
500         This->uniformResourceLocatorA.lpVtbl = &uniformResourceLocatorAVtbl;
501         This->uniformResourceLocatorW.lpVtbl = &uniformResourceLocatorWVtbl;
502         This->persistFile.lpVtbl = &persistFileVtbl;
503         This->refCount = 0;
504         hr = Unknown_QueryInterface(This, riid, ppv);
505         if (SUCCEEDED(hr))
506             SHDOCVW_LockModule();
507         else
508             heap_free(This);
509         return hr;
510     }
511     else
512         return E_OUTOFMEMORY;
513 }