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