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