Sync trunk.
[reactos.git] / dll / win32 / 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);
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 }