blob: b389ec3727a01d9af44c3856951b9d398807c558 [file] [log] [blame]
Damjan Jovanovicad886d62008-08-03 11:18:26 +02001/*
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:
Damjan Jovanovicad886d62008-08-03 11:18:26 +020023 * Implement the IShellLinkA/W interfaces
24 * Handle the SetURL flags
Damjan Jovanovicad886d62008-08-03 11:18:26 +020025 * 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
36WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
37
38typedef 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
53static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
54{
55 return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorA));
56}
57
58static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
59{
60 return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, uniformResourceLocatorW));
61}
62
63static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
64{
65 return (InternetShortcut*)((char*)iface - FIELD_OFFSET(InternetShortcut, persistFile));
66}
67
Alexandre Julliard0856f772009-10-27 11:22:32 +010068static BOOL run_winemenubuilder( const WCHAR *args )
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +020069{
Alexandre Julliard0856f772009-10-27 11:22:32 +010070 static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +020071 LONG len;
72 LPWSTR buffer;
73 STARTUPINFOW si;
74 PROCESS_INFORMATION pi;
75 BOOL ret;
Alexandre Julliard0856f772009-10-27 11:22:32 +010076 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
107static BOOL StartLinkProcessor( LPCOLESTR szLink )
108{
109 static const WCHAR szFormat[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
110 LONG len;
111 LPWSTR buffer;
112 BOOL ret;
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +0200113
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 );
Alexandre Julliard0856f772009-10-27 11:22:32 +0100120 ret = run_winemenubuilder( buffer );
121 heap_free( buffer );
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +0200122 return ret;
123}
124
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200125/* interface functions */
126
Francois Gouget2a2387b2008-11-25 16:42:43 +0100127static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200128{
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
Francois Gouget2a2387b2008-11-25 16:42:43 +0100158static ULONG Unknown_AddRef(InternetShortcut *This)
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200159{
160 TRACE("(%p)\n", This);
161 return InterlockedIncrement(&This->refCount);
162}
163
Francois Gouget2a2387b2008-11-25 16:42:43 +0100164static ULONG Unknown_Release(InternetShortcut *This)
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200165{
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
179static 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
186static 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
193static 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
200static 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
219static 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
235static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
236{
237 FIXME("(%p, %p): stub\n", url, pCommandInfo);
238 return E_NOTIMPL;
239}
240
241static 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
248static 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
255static 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
262static 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
281static 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
297static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
298{
299 FIXME("(%p, %p): stub\n", url, pCommandInfo);
300 return E_NOTIMPL;
301}
302
303static 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
310static 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
317static 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
324static 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
331static 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
338static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
339{
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +0200340 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;
Marcus Meissnerbc295ef2008-11-16 10:32:37 +0100353 WCHAR *url = CoTaskMemAlloc(len*sizeof(WCHAR));
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +0200354 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;
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200388}
389
390static 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;
Damjan Jovanovic9ff230d2008-11-13 08:19:25 +0200440 StartLinkProcessor(pszFileName);
Damjan Jovanovicad886d62008-08-03 11:18:26 +0200441 }
442 else
443 hr = E_FAIL;
444 heap_free(url);
445 }
446 else
447 hr = E_OUTOFMEMORY;
448
449 return hr;
450}
451
452static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
453{
454 FIXME("(%p, %p): stub\n", pFile, pszFileName);
455 return E_NOTIMPL;
456}
457
458static 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
476static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
477 UniformResourceLocatorW_QueryInterface,
478 UniformResourceLocatorW_AddRef,
479 UniformResourceLocatorW_Release,
480 UniformResourceLocatorW_SetUrl,
481 UniformResourceLocatorW_GetUrl,
482 UniformResourceLocatorW_InvokeCommand
483};
484
485static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
486 UniformResourceLocatorA_QueryInterface,
487 UniformResourceLocatorA_AddRef,
488 UniformResourceLocatorA_Release,
489 UniformResourceLocatorA_SetUrl,
490 UniformResourceLocatorA_GetUrl,
491 UniformResourceLocatorA_InvokeCommand
492};
493
494static 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
506HRESULT 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}