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 <stdio.h>
31
32 #define NONAMELESSUNION
33
34 #include "ieframe.h"
35
36 #include "shlobj.h"
37 #include "shobjidl.h"
38 #include "intshcut.h"
39 #include "shellapi.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "shlguid.h"
43
44 #include "wine/debug.h"
45
46 WINE_DEFAULT_DEBUG_CHANNEL(ieframe);
47
48 typedef struct
49 {
50 IUniformResourceLocatorA IUniformResourceLocatorA_iface;
51 IUniformResourceLocatorW IUniformResourceLocatorW_iface;
52 IPersistFile IPersistFile_iface;
53 IPropertySetStorage IPropertySetStorage_iface;
54
55 LONG refCount;
56
57 IPropertySetStorage *property_set_storage;
58 WCHAR *url;
59 BOOLEAN isDirty;
60 LPOLESTR currentFile;
61 } InternetShortcut;
62
63 /* utility functions */
64
impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA * iface)65 static inline InternetShortcut* impl_from_IUniformResourceLocatorA(IUniformResourceLocatorA *iface)
66 {
67 return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorA_iface);
68 }
69
impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW * iface)70 static inline InternetShortcut* impl_from_IUniformResourceLocatorW(IUniformResourceLocatorW *iface)
71 {
72 return CONTAINING_RECORD(iface, InternetShortcut, IUniformResourceLocatorW_iface);
73 }
74
impl_from_IPersistFile(IPersistFile * iface)75 static inline InternetShortcut* impl_from_IPersistFile(IPersistFile *iface)
76 {
77 return CONTAINING_RECORD(iface, InternetShortcut, IPersistFile_iface);
78 }
79
impl_from_IPropertySetStorage(IPropertySetStorage * iface)80 static inline InternetShortcut* impl_from_IPropertySetStorage(IPropertySetStorage *iface)
81 {
82 return CONTAINING_RECORD(iface, InternetShortcut, IPropertySetStorage_iface);
83 }
84
run_winemenubuilder(const WCHAR * args)85 static BOOL run_winemenubuilder( const WCHAR *args )
86 {
87 static const WCHAR menubuilder[] = {'\\','w','i','n','e','m','e','n','u','b','u','i','l','d','e','r','.','e','x','e',0};
88 LONG len;
89 LPWSTR buffer;
90 STARTUPINFOW si;
91 PROCESS_INFORMATION pi;
92 BOOL ret;
93 WCHAR app[MAX_PATH];
94 void *redir;
95
96 GetSystemDirectoryW( app, MAX_PATH - ARRAY_SIZE( menubuilder ));
97 lstrcatW( app, menubuilder );
98
99 len = (lstrlenW( app ) + lstrlenW( args ) + 1) * sizeof(WCHAR);
100 buffer = heap_alloc( len );
101 if( !buffer )
102 return FALSE;
103
104 lstrcpyW( buffer, app );
105 lstrcatW( buffer, args );
106
107 TRACE("starting %s\n",debugstr_w(buffer));
108
109 memset(&si, 0, sizeof(si));
110 si.cb = sizeof(si);
111
112 Wow64DisableWow64FsRedirection( &redir );
113 ret = CreateProcessW( app, buffer, NULL, NULL, FALSE, DETACHED_PROCESS, NULL, NULL, &si, &pi );
114 Wow64RevertWow64FsRedirection( redir );
115
116 heap_free( buffer );
117
118 if (ret)
119 {
120 CloseHandle( pi.hProcess );
121 CloseHandle( pi.hThread );
122 }
123
124 return ret;
125 }
126
StartLinkProcessor(LPCOLESTR szLink)127 static BOOL StartLinkProcessor( LPCOLESTR szLink )
128 {
129 static const WCHAR szFormat[] = { ' ','-','w',' ','-','u',' ','"','%','s','"',0 };
130 LONG len;
131 LPWSTR buffer;
132 BOOL ret;
133
134 len = sizeof(szFormat) + lstrlenW( szLink ) * sizeof(WCHAR);
135 buffer = heap_alloc( len );
136 if( !buffer )
137 return FALSE;
138
139 swprintf( buffer, szFormat, szLink );
140 ret = run_winemenubuilder( buffer );
141 heap_free( buffer );
142 return ret;
143 }
144
145 /* interface functions */
146
Unknown_QueryInterface(InternetShortcut * This,REFIID riid,PVOID * ppvObject)147 static HRESULT Unknown_QueryInterface(InternetShortcut *This, REFIID riid, PVOID *ppvObject)
148 {
149 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppvObject);
150 *ppvObject = NULL;
151 if (IsEqualGUID(&IID_IUnknown, riid))
152 *ppvObject = &This->IUniformResourceLocatorA_iface;
153 else if (IsEqualGUID(&IID_IUniformResourceLocatorA, riid))
154 *ppvObject = &This->IUniformResourceLocatorA_iface;
155 else if (IsEqualGUID(&IID_IUniformResourceLocatorW, riid))
156 *ppvObject = &This->IUniformResourceLocatorW_iface;
157 else if (IsEqualGUID(&IID_IPersistFile, riid))
158 *ppvObject = &This->IPersistFile_iface;
159 else if (IsEqualGUID(&IID_IPropertySetStorage, riid))
160 *ppvObject = &This->IPropertySetStorage_iface;
161 else if (IsEqualGUID(&IID_IShellLinkA, riid))
162 {
163 FIXME("The IShellLinkA interface is not yet supported by InternetShortcut\n");
164 return E_NOINTERFACE;
165 }
166 else if (IsEqualGUID(&IID_IShellLinkW, riid))
167 {
168 FIXME("The IShellLinkW interface is not yet supported by InternetShortcut\n");
169 return E_NOINTERFACE;
170 }
171 else
172 {
173 FIXME("Interface with GUID %s not yet implemented by InternetShortcut\n", debugstr_guid(riid));
174 return E_NOINTERFACE;
175 }
176 IUnknown_AddRef((IUnknown*)*ppvObject);
177 return S_OK;
178 }
179
Unknown_AddRef(InternetShortcut * This)180 static ULONG Unknown_AddRef(InternetShortcut *This)
181 {
182 TRACE("(%p)\n", This);
183 return InterlockedIncrement(&This->refCount);
184 }
185
Unknown_Release(InternetShortcut * This)186 static ULONG Unknown_Release(InternetShortcut *This)
187 {
188 ULONG count;
189 TRACE("(%p)\n", This);
190 count = InterlockedDecrement(&This->refCount);
191 if (count == 0)
192 {
193 CoTaskMemFree(This->url);
194 CoTaskMemFree(This->currentFile);
195 IPropertySetStorage_Release(This->property_set_storage);
196 heap_free(This);
197 unlock_module();
198 }
199 return count;
200 }
201
UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW * url,REFIID riid,PVOID * ppvObject)202 static HRESULT WINAPI UniformResourceLocatorW_QueryInterface(IUniformResourceLocatorW *url, REFIID riid, PVOID *ppvObject)
203 {
204 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
205 TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
206 return Unknown_QueryInterface(This, riid, ppvObject);
207 }
208
UniformResourceLocatorW_AddRef(IUniformResourceLocatorW * url)209 static ULONG WINAPI UniformResourceLocatorW_AddRef(IUniformResourceLocatorW *url)
210 {
211 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
212 TRACE("(%p)\n", url);
213 return Unknown_AddRef(This);
214 }
215
UniformResourceLocatorW_Release(IUniformResourceLocatorW * url)216 static ULONG WINAPI UniformResourceLocatorW_Release(IUniformResourceLocatorW *url)
217 {
218 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
219 TRACE("(%p)\n", url);
220 return Unknown_Release(This);
221 }
222
UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW * url,LPCWSTR pcszURL,DWORD dwInFlags)223 static HRESULT WINAPI UniformResourceLocatorW_SetUrl(IUniformResourceLocatorW *url, LPCWSTR pcszURL, DWORD dwInFlags)
224 {
225 WCHAR *newURL = NULL;
226 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
227 TRACE("(%p, %s, 0x%x)\n", url, debugstr_w(pcszURL), dwInFlags);
228 if (dwInFlags != 0)
229 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
230 if (pcszURL != NULL)
231 {
232 newURL = co_strdupW(pcszURL);
233 if (newURL == NULL)
234 return E_OUTOFMEMORY;
235 }
236 CoTaskMemFree(This->url);
237 This->url = newURL;
238 This->isDirty = TRUE;
239 return S_OK;
240 }
241
UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW * url,LPWSTR * ppszURL)242 static HRESULT WINAPI UniformResourceLocatorW_GetUrl(IUniformResourceLocatorW *url, LPWSTR *ppszURL)
243 {
244 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
245
246 TRACE("(%p, %p)\n", url, ppszURL);
247
248 if (!This->url) {
249 *ppszURL = NULL;
250 return S_FALSE;
251 }
252
253 *ppszURL = co_strdupW(This->url);
254 if (!*ppszURL)
255 return E_OUTOFMEMORY;
256
257 return S_OK;
258 }
259
UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW * url,PURLINVOKECOMMANDINFOW pCommandInfo)260 static HRESULT WINAPI UniformResourceLocatorW_InvokeCommand(IUniformResourceLocatorW *url, PURLINVOKECOMMANDINFOW pCommandInfo)
261 {
262 InternetShortcut *This = impl_from_IUniformResourceLocatorW(url);
263 WCHAR app[64];
264 HKEY hkey;
265 static const WCHAR wszURLProtocol[] = {'U','R','L',' ','P','r','o','t','o','c','o','l',0};
266 SHELLEXECUTEINFOW sei;
267 DWORD res, type;
268 HRESULT hres;
269
270 TRACE("%p %p\n", This, pCommandInfo );
271
272 if (pCommandInfo->dwcbSize < sizeof (URLINVOKECOMMANDINFOW))
273 return E_INVALIDARG;
274
275 if (pCommandInfo->dwFlags != IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB)
276 {
277 FIXME("(%p, %p): non-default verbs not implemented\n", url, pCommandInfo);
278 return E_NOTIMPL;
279 }
280
281 hres = CoInternetParseUrl(This->url, PARSE_SCHEMA, 0, app, ARRAY_SIZE(app), NULL, 0);
282 if(FAILED(hres))
283 return E_FAIL;
284
285 res = RegOpenKeyW(HKEY_CLASSES_ROOT, app, &hkey);
286 if(res != ERROR_SUCCESS)
287 return E_FAIL;
288
289 res = RegQueryValueExW(hkey, wszURLProtocol, NULL, &type, NULL, NULL);
290 RegCloseKey(hkey);
291 if(res != ERROR_SUCCESS || type != REG_SZ)
292 return E_FAIL;
293
294 memset(&sei, 0, sizeof(sei));
295 sei.cbSize = sizeof(sei);
296 sei.lpFile = This->url;
297 sei.nShow = SW_SHOW;
298
299 if( ShellExecuteExW(&sei) )
300 return S_OK;
301 else
302 return E_FAIL;
303 }
304
UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA * url,REFIID riid,PVOID * ppvObject)305 static HRESULT WINAPI UniformResourceLocatorA_QueryInterface(IUniformResourceLocatorA *url, REFIID riid, PVOID *ppvObject)
306 {
307 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
308 TRACE("(%p, %s, %p)\n", url, debugstr_guid(riid), ppvObject);
309 return Unknown_QueryInterface(This, riid, ppvObject);
310 }
311
UniformResourceLocatorA_AddRef(IUniformResourceLocatorA * url)312 static ULONG WINAPI UniformResourceLocatorA_AddRef(IUniformResourceLocatorA *url)
313 {
314 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
315 TRACE("(%p)\n", url);
316 return Unknown_AddRef(This);
317 }
318
UniformResourceLocatorA_Release(IUniformResourceLocatorA * url)319 static ULONG WINAPI UniformResourceLocatorA_Release(IUniformResourceLocatorA *url)
320 {
321 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
322 TRACE("(%p)\n", url);
323 return Unknown_Release(This);
324 }
325
UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA * url,LPCSTR pcszURL,DWORD dwInFlags)326 static HRESULT WINAPI UniformResourceLocatorA_SetUrl(IUniformResourceLocatorA *url, LPCSTR pcszURL, DWORD dwInFlags)
327 {
328 WCHAR *newURL = NULL;
329 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
330 TRACE("(%p, %s, 0x%x)\n", url, debugstr_a(pcszURL), dwInFlags);
331 if (dwInFlags != 0)
332 FIXME("ignoring unsupported flags 0x%x\n", dwInFlags);
333 if (pcszURL != NULL)
334 {
335 newURL = co_strdupAtoW(pcszURL);
336 if (newURL == NULL)
337 return E_OUTOFMEMORY;
338 }
339 CoTaskMemFree(This->url);
340 This->url = newURL;
341 This->isDirty = TRUE;
342 return S_OK;
343 }
344
UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA * url,LPSTR * ppszURL)345 static HRESULT WINAPI UniformResourceLocatorA_GetUrl(IUniformResourceLocatorA *url, LPSTR *ppszURL)
346 {
347 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
348
349 TRACE("(%p, %p)\n", url, ppszURL);
350
351 if (!This->url) {
352 *ppszURL = NULL;
353 return S_FALSE;
354
355 }
356
357 *ppszURL = co_strdupWtoA(This->url);
358 if (!*ppszURL)
359 return E_OUTOFMEMORY;
360
361 return S_OK;
362 }
363
UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA * url,PURLINVOKECOMMANDINFOA pCommandInfo)364 static HRESULT WINAPI UniformResourceLocatorA_InvokeCommand(IUniformResourceLocatorA *url, PURLINVOKECOMMANDINFOA pCommandInfo)
365 {
366 URLINVOKECOMMANDINFOW wideCommandInfo;
367 int len;
368 WCHAR *wideVerb;
369 HRESULT res;
370 InternetShortcut *This = impl_from_IUniformResourceLocatorA(url);
371
372 wideCommandInfo.dwcbSize = sizeof wideCommandInfo;
373 wideCommandInfo.dwFlags = pCommandInfo->dwFlags;
374 wideCommandInfo.hwndParent = pCommandInfo->hwndParent;
375
376 len = MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, NULL, 0);
377 wideVerb = heap_alloc(len * sizeof(WCHAR));
378 MultiByteToWideChar(CP_ACP, 0, pCommandInfo->pcszVerb, -1, wideVerb, len);
379
380 wideCommandInfo.pcszVerb = wideVerb;
381
382 res = UniformResourceLocatorW_InvokeCommand(&This->IUniformResourceLocatorW_iface, &wideCommandInfo);
383 heap_free(wideVerb);
384
385 return res;
386 }
387
PersistFile_QueryInterface(IPersistFile * pFile,REFIID riid,PVOID * ppvObject)388 static HRESULT WINAPI PersistFile_QueryInterface(IPersistFile *pFile, REFIID riid, PVOID *ppvObject)
389 {
390 InternetShortcut *This = impl_from_IPersistFile(pFile);
391 TRACE("(%p, %s, %p)\n", pFile, debugstr_guid(riid), ppvObject);
392 return Unknown_QueryInterface(This, riid, ppvObject);
393 }
394
PersistFile_AddRef(IPersistFile * pFile)395 static ULONG WINAPI PersistFile_AddRef(IPersistFile *pFile)
396 {
397 InternetShortcut *This = impl_from_IPersistFile(pFile);
398 TRACE("(%p)\n", pFile);
399 return Unknown_AddRef(This);
400 }
401
PersistFile_Release(IPersistFile * pFile)402 static ULONG WINAPI PersistFile_Release(IPersistFile *pFile)
403 {
404 InternetShortcut *This = impl_from_IPersistFile(pFile);
405 TRACE("(%p)\n", pFile);
406 return Unknown_Release(This);
407 }
408
PersistFile_GetClassID(IPersistFile * pFile,CLSID * pClassID)409 static HRESULT WINAPI PersistFile_GetClassID(IPersistFile *pFile, CLSID *pClassID)
410 {
411 TRACE("(%p, %p)\n", pFile, pClassID);
412 *pClassID = CLSID_InternetShortcut;
413 return S_OK;
414 }
415
PersistFile_IsDirty(IPersistFile * pFile)416 static HRESULT WINAPI PersistFile_IsDirty(IPersistFile *pFile)
417 {
418 InternetShortcut *This = impl_from_IPersistFile(pFile);
419 TRACE("(%p)\n", pFile);
420 return This->isDirty ? S_OK : S_FALSE;
421 }
422
423 /* Returns allocated profile string and a standard return code. */
get_profile_string(LPCWSTR lpAppName,LPCWSTR lpKeyName,LPCWSTR lpFileName,WCHAR ** rString)424 static HRESULT get_profile_string(LPCWSTR lpAppName, LPCWSTR lpKeyName,
425 LPCWSTR lpFileName, WCHAR **rString )
426 {
427 DWORD r = 0;
428 DWORD len = 128;
429 WCHAR *buffer;
430
431 *rString = NULL;
432 buffer = CoTaskMemAlloc(len * sizeof(*buffer));
433 if (!buffer)
434 return E_OUTOFMEMORY;
435
436 r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
437 while (r == len-1)
438 {
439 WCHAR *realloc_buf;
440
441 len *= 2;
442 realloc_buf = CoTaskMemRealloc(buffer, len * sizeof(*buffer));
443 if (realloc_buf == NULL)
444 {
445 CoTaskMemFree(buffer);
446 return E_OUTOFMEMORY;
447 }
448 buffer = realloc_buf;
449
450 r = GetPrivateProfileStringW(lpAppName, lpKeyName, NULL, buffer, len, lpFileName);
451 }
452
453 *rString = buffer;
454 return r ? S_OK : E_FAIL;
455 }
456
PersistFile_Load(IPersistFile * pFile,LPCOLESTR pszFileName,DWORD dwMode)457 static HRESULT WINAPI PersistFile_Load(IPersistFile *pFile, LPCOLESTR pszFileName, DWORD dwMode)
458 {
459 static const WCHAR str_header[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
460 static const WCHAR str_URL[] = {'U','R','L',0};
461 static const WCHAR str_iconfile[] = {'i','c','o','n','f','i','l','e',0};
462 static const WCHAR str_iconindex[] = {'i','c','o','n','i','n','d','e','x',0};
463 InternetShortcut *This = impl_from_IPersistFile(pFile);
464 WCHAR *filename = NULL;
465 WCHAR *url;
466 HRESULT hr;
467 IPropertyStorage *pPropStg;
468 WCHAR *iconfile;
469 WCHAR *iconindexstring;
470
471 TRACE("(%p, %s, 0x%x)\n", pFile, debugstr_w(pszFileName), dwMode);
472
473 if (dwMode != 0)
474 FIXME("ignoring unimplemented mode 0x%x\n", dwMode);
475
476 filename = co_strdupW(pszFileName);
477 if (!filename)
478 return E_OUTOFMEMORY;
479
480 if (FAILED(hr = get_profile_string(str_header, str_URL, pszFileName, &url)))
481 {
482 CoTaskMemFree(filename);
483 return hr;
484 }
485
486 hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut,
487 STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &pPropStg);
488 if (FAILED(hr))
489 {
490 CoTaskMemFree(filename);
491 CoTaskMemFree(url);
492 return hr;
493 }
494
495 CoTaskMemFree(This->currentFile);
496 This->currentFile = filename;
497 CoTaskMemFree(This->url);
498 This->url = url;
499 This->isDirty = FALSE;
500
501 /* Now we're going to read in the iconfile and iconindex.
502 If we don't find them, that's not a failure case -- it's possible
503 that they just aren't in there. */
504
505 if (get_profile_string(str_header, str_iconfile, pszFileName, &iconfile) == S_OK)
506 {
507 PROPSPEC ps;
508 PROPVARIANT pv;
509 ps.ulKind = PRSPEC_PROPID;
510 ps.u.propid = PID_IS_ICONFILE;
511 pv.vt = VT_LPWSTR;
512 pv.u.pwszVal = iconfile;
513 hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
514 if (FAILED(hr))
515 TRACE("Failed to store the iconfile to our property storage. hr = 0x%x\n", hr);
516 }
517 CoTaskMemFree(iconfile);
518
519 if (get_profile_string(str_header, str_iconindex, pszFileName, &iconindexstring) == S_OK)
520 {
521 int iconindex;
522 PROPSPEC ps;
523 PROPVARIANT pv;
524 iconindex = wcstol(iconindexstring, NULL, 10);
525 ps.ulKind = PRSPEC_PROPID;
526 ps.u.propid = PID_IS_ICONINDEX;
527 pv.vt = VT_I4;
528 pv.u.iVal = iconindex;
529 hr = IPropertyStorage_WriteMultiple(pPropStg, 1, &ps, &pv, 0);
530 if (FAILED(hr))
531 TRACE("Failed to store the iconindex to our property storage. hr = 0x%x\n", hr);
532 }
533 CoTaskMemFree(iconindexstring);
534
535 IPropertyStorage_Release(pPropStg);
536 return hr;
537 }
538
PersistFile_Save(IPersistFile * pFile,LPCOLESTR pszFileName,BOOL fRemember)539 static HRESULT WINAPI PersistFile_Save(IPersistFile *pFile, LPCOLESTR pszFileName, BOOL fRemember)
540 {
541 HRESULT hr = S_OK;
542 INT len;
543 CHAR *url;
544 InternetShortcut *This = impl_from_IPersistFile(pFile);
545
546 TRACE("(%p, %s, %d)\n", pFile, debugstr_w(pszFileName), fRemember);
547
548 if (pszFileName != NULL && fRemember)
549 {
550 LPOLESTR oldFile = This->currentFile;
551 This->currentFile = co_strdupW(pszFileName);
552 if (This->currentFile == NULL)
553 {
554 This->currentFile = oldFile;
555 return E_OUTOFMEMORY;
556 }
557 CoTaskMemFree(oldFile);
558 }
559 if (This->url == NULL)
560 return E_FAIL;
561
562 /* Windows seems to always write:
563 * ASCII "[InternetShortcut]" headers
564 * ASCII names in "name=value" pairs
565 * An ASCII (probably UTF8?) value in "URL=..."
566 */
567 len = WideCharToMultiByte(CP_UTF8, 0, This->url, -1, NULL, 0, 0, 0);
568 url = heap_alloc(len);
569 if (url != NULL)
570 {
571 HANDLE file;
572 WideCharToMultiByte(CP_UTF8, 0, This->url, -1, url, len, 0, 0);
573 file = CreateFileW(pszFileName, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
574 if (file != INVALID_HANDLE_VALUE)
575 {
576 static const char str_header[] = "[InternetShortcut]";
577 static const char str_URL[] = "URL=";
578 static const char str_ICONFILE[] = "ICONFILE=";
579 static const char str_eol[] = "\r\n";
580 DWORD bytesWritten;
581 char *iconfile;
582 IPropertyStorage *pPropStgRead;
583 PROPSPEC ps[2];
584 PROPVARIANT pvread[2];
585 ps[0].ulKind = PRSPEC_PROPID;
586 ps[0].u.propid = PID_IS_ICONFILE;
587 ps[1].ulKind = PRSPEC_PROPID;
588 ps[1].u.propid = PID_IS_ICONINDEX;
589
590 WriteFile(file, str_header, ARRAY_SIZE(str_header) - 1, &bytesWritten, NULL);
591 WriteFile(file, str_eol, ARRAY_SIZE(str_eol) - 1, &bytesWritten, NULL);
592 WriteFile(file, str_URL, ARRAY_SIZE(str_URL) - 1, &bytesWritten, NULL);
593 WriteFile(file, url, lstrlenA(url), &bytesWritten, NULL);
594 WriteFile(file, str_eol, ARRAY_SIZE(str_eol) - 1, &bytesWritten, NULL);
595
596 hr = IPropertySetStorage_Open(This->property_set_storage, &FMTID_Intshcut, STGM_READ|STGM_SHARE_EXCLUSIVE, &pPropStgRead);
597 if (SUCCEEDED(hr))
598 {
599 hr = IPropertyStorage_ReadMultiple(pPropStgRead, 2, ps, pvread);
600 if (hr == S_FALSE)
601 {
602 /* None of the properties are present, that's ok */
603 hr = S_OK;
604 IPropertyStorage_Release(pPropStgRead);
605 }
606 else if (SUCCEEDED(hr))
607 {
608 char indexString[50];
609 len = WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, NULL, 0, 0, 0);
610 iconfile = heap_alloc(len);
611 if (iconfile != NULL)
612 {
613 WideCharToMultiByte(CP_UTF8, 0, pvread[0].u.pwszVal, -1, iconfile, len, 0, 0);
614 WriteFile(file, str_ICONFILE, lstrlenA(str_ICONFILE), &bytesWritten, NULL);
615 WriteFile(file, iconfile, lstrlenA(iconfile), &bytesWritten, NULL);
616 WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
617 }
618
619 sprintf(indexString, "ICONINDEX=%d", pvread[1].u.iVal);
620 WriteFile(file, indexString, lstrlenA(indexString), &bytesWritten, NULL);
621 WriteFile(file, str_eol, lstrlenA(str_eol), &bytesWritten, NULL);
622
623 IPropertyStorage_Release(pPropStgRead);
624 PropVariantClear(&pvread[0]);
625 PropVariantClear(&pvread[1]);
626 }
627 else
628 {
629 TRACE("Unable to read properties.\n");
630 }
631 }
632 else
633 {
634 TRACE("Unable to get the IPropertyStorage.\n");
635 }
636
637 CloseHandle(file);
638 if (pszFileName == NULL || fRemember)
639 This->isDirty = FALSE;
640 StartLinkProcessor(pszFileName);
641 }
642 else
643 hr = E_FAIL;
644 heap_free(url);
645 }
646 else
647 hr = E_OUTOFMEMORY;
648
649 return hr;
650 }
651
PersistFile_SaveCompleted(IPersistFile * pFile,LPCOLESTR pszFileName)652 static HRESULT WINAPI PersistFile_SaveCompleted(IPersistFile *pFile, LPCOLESTR pszFileName)
653 {
654 FIXME("(%p, %p): stub\n", pFile, pszFileName);
655 return E_NOTIMPL;
656 }
657
PersistFile_GetCurFile(IPersistFile * pFile,LPOLESTR * ppszFileName)658 static HRESULT WINAPI PersistFile_GetCurFile(IPersistFile *pFile, LPOLESTR *ppszFileName)
659 {
660 HRESULT hr = S_OK;
661 InternetShortcut *This = impl_from_IPersistFile(pFile);
662 TRACE("(%p, %p)\n", pFile, ppszFileName);
663 if (This->currentFile == NULL)
664 *ppszFileName = NULL;
665 else
666 {
667 *ppszFileName = co_strdupW(This->currentFile);
668 if (*ppszFileName == NULL)
669 hr = E_OUTOFMEMORY;
670 }
671 return hr;
672 }
673
PropertySetStorage_QueryInterface(IPropertySetStorage * iface,REFIID riid,PVOID * ppvObject)674 static HRESULT WINAPI PropertySetStorage_QueryInterface(IPropertySetStorage *iface, REFIID riid, PVOID *ppvObject)
675 {
676 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
677 TRACE("(%p)\n", iface);
678 return Unknown_QueryInterface(This, riid, ppvObject);
679 }
680
PropertySetStorage_AddRef(IPropertySetStorage * iface)681 static ULONG WINAPI PropertySetStorage_AddRef(IPropertySetStorage *iface)
682 {
683 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
684 TRACE("(%p)\n", iface);
685 return Unknown_AddRef(This);
686 }
687
PropertySetStorage_Release(IPropertySetStorage * iface)688 static ULONG WINAPI PropertySetStorage_Release(IPropertySetStorage *iface)
689 {
690 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
691 TRACE("(%p)\n", iface);
692 return Unknown_Release(This);
693 }
694
PropertySetStorage_Create(IPropertySetStorage * iface,REFFMTID rfmtid,const CLSID * pclsid,DWORD grfFlags,DWORD grfMode,IPropertyStorage ** ppprstg)695 static HRESULT WINAPI PropertySetStorage_Create(
696 IPropertySetStorage* iface,
697 REFFMTID rfmtid,
698 const CLSID *pclsid,
699 DWORD grfFlags,
700 DWORD grfMode,
701 IPropertyStorage **ppprstg)
702 {
703 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
704 TRACE("(%s, %p, 0x%x, 0x%x, %p)\n", debugstr_guid(rfmtid), pclsid, grfFlags, grfMode, ppprstg);
705
706 return IPropertySetStorage_Create(This->property_set_storage,
707 rfmtid,
708 pclsid,
709 grfFlags,
710 grfMode,
711 ppprstg);
712 }
713
PropertySetStorage_Open(IPropertySetStorage * iface,REFFMTID rfmtid,DWORD grfMode,IPropertyStorage ** ppprstg)714 static HRESULT WINAPI PropertySetStorage_Open(
715 IPropertySetStorage* iface,
716 REFFMTID rfmtid,
717 DWORD grfMode,
718 IPropertyStorage **ppprstg)
719 {
720 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
721 TRACE("(%s, 0x%x, %p)\n", debugstr_guid(rfmtid), grfMode, ppprstg);
722
723 /* Note: The |STGM_SHARE_EXCLUSIVE is to cope with a bug in the implementation. Should be fixed in ole32. */
724 return IPropertySetStorage_Open(This->property_set_storage,
725 rfmtid,
726 grfMode|STGM_SHARE_EXCLUSIVE,
727 ppprstg);
728 }
729
PropertySetStorage_Delete(IPropertySetStorage * iface,REFFMTID rfmtid)730 static HRESULT WINAPI PropertySetStorage_Delete(IPropertySetStorage *iface, REFFMTID rfmtid)
731 {
732 InternetShortcut *This = impl_from_IPropertySetStorage(iface);
733 TRACE("(%s)\n", debugstr_guid(rfmtid));
734
735
736 return IPropertySetStorage_Delete(This->property_set_storage,
737 rfmtid);
738 }
739
PropertySetStorage_Enum(IPropertySetStorage * iface,IEnumSTATPROPSETSTG ** ppenum)740 static HRESULT WINAPI PropertySetStorage_Enum(IPropertySetStorage *iface, IEnumSTATPROPSETSTG **ppenum)
741 {
742 FIXME("(%p): stub\n", ppenum);
743 return E_NOTIMPL;
744 }
745
746 static const IUniformResourceLocatorWVtbl uniformResourceLocatorWVtbl = {
747 UniformResourceLocatorW_QueryInterface,
748 UniformResourceLocatorW_AddRef,
749 UniformResourceLocatorW_Release,
750 UniformResourceLocatorW_SetUrl,
751 UniformResourceLocatorW_GetUrl,
752 UniformResourceLocatorW_InvokeCommand
753 };
754
755 static const IUniformResourceLocatorAVtbl uniformResourceLocatorAVtbl = {
756 UniformResourceLocatorA_QueryInterface,
757 UniformResourceLocatorA_AddRef,
758 UniformResourceLocatorA_Release,
759 UniformResourceLocatorA_SetUrl,
760 UniformResourceLocatorA_GetUrl,
761 UniformResourceLocatorA_InvokeCommand
762 };
763
764 static const IPersistFileVtbl persistFileVtbl = {
765 PersistFile_QueryInterface,
766 PersistFile_AddRef,
767 PersistFile_Release,
768 PersistFile_GetClassID,
769 PersistFile_IsDirty,
770 PersistFile_Load,
771 PersistFile_Save,
772 PersistFile_SaveCompleted,
773 PersistFile_GetCurFile
774 };
775
776 static const IPropertySetStorageVtbl propertySetStorageVtbl = {
777 PropertySetStorage_QueryInterface,
778 PropertySetStorage_AddRef,
779 PropertySetStorage_Release,
780 PropertySetStorage_Create,
781 PropertySetStorage_Open,
782 PropertySetStorage_Delete,
783 PropertySetStorage_Enum
784 };
785
create_shortcut(void)786 static InternetShortcut *create_shortcut(void)
787 {
788 InternetShortcut *newshortcut;
789
790 newshortcut = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(InternetShortcut));
791 if (newshortcut)
792 {
793 HRESULT hr;
794 IPropertyStorage *dummy;
795
796 newshortcut->IUniformResourceLocatorA_iface.lpVtbl = &uniformResourceLocatorAVtbl;
797 newshortcut->IUniformResourceLocatorW_iface.lpVtbl = &uniformResourceLocatorWVtbl;
798 newshortcut->IPersistFile_iface.lpVtbl = &persistFileVtbl;
799 newshortcut->IPropertySetStorage_iface.lpVtbl = &propertySetStorageVtbl;
800 newshortcut->refCount = 1;
801 hr = StgCreateStorageEx(NULL, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE,
802 STGFMT_STORAGE, 0, NULL, NULL, &IID_IPropertySetStorage, (void **) &newshortcut->property_set_storage);
803 if (FAILED(hr))
804 {
805 TRACE("Failed to create the storage object needed for the shortcut.\n");
806 heap_free(newshortcut);
807 return NULL;
808 }
809
810 hr = IPropertySetStorage_Create(newshortcut->property_set_storage, &FMTID_Intshcut, NULL, PROPSETFLAG_DEFAULT, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, &dummy);
811 if (FAILED(hr))
812 {
813 TRACE("Failed to create the property object needed for the shortcut.\n");
814 IPropertySetStorage_Release(newshortcut->property_set_storage);
815 heap_free(newshortcut);
816 return NULL;
817 }
818 IPropertyStorage_Release(dummy);
819 }
820
821 return newshortcut;
822 }
823
InternetShortcut_Create(IClassFactory * iface,IUnknown * outer,REFIID riid,void ** ppv)824 HRESULT WINAPI InternetShortcut_Create(IClassFactory *iface, IUnknown *outer, REFIID riid, void **ppv)
825 {
826 InternetShortcut *This;
827 HRESULT hres;
828
829 TRACE("(%p, %s, %p)\n", outer, debugstr_guid(riid), ppv);
830
831 *ppv = NULL;
832
833 if(outer)
834 return CLASS_E_NOAGGREGATION;
835
836 This = create_shortcut();
837 if(!This)
838 return E_OUTOFMEMORY;
839
840 hres = Unknown_QueryInterface(This, riid, ppv);
841 Unknown_Release(This);
842 return hres;
843 }
844
845
846 /**********************************************************************
847 * OpenURL (ieframe.@)
848 */
OpenURL(HWND hWnd,HINSTANCE hInst,LPCSTR lpcstrUrl,int nShowCmd)849 void WINAPI OpenURL(HWND hWnd, HINSTANCE hInst, LPCSTR lpcstrUrl, int nShowCmd)
850 {
851 InternetShortcut *shortcut;
852 WCHAR* urlfilepath = NULL;
853 int len;
854
855 shortcut = create_shortcut();
856
857 if(!shortcut)
858 return;
859
860 len = MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, NULL, 0);
861 urlfilepath = heap_alloc(len * sizeof(WCHAR));
862 MultiByteToWideChar(CP_ACP, 0, lpcstrUrl, -1, urlfilepath, len);
863
864 if(SUCCEEDED(IPersistFile_Load(&shortcut->IPersistFile_iface, urlfilepath, 0))) {
865 URLINVOKECOMMANDINFOW ici;
866
867 memset( &ici, 0, sizeof ici );
868 ici.dwcbSize = sizeof ici;
869 ici.dwFlags = IURL_INVOKECOMMAND_FL_USE_DEFAULT_VERB;
870 ici.hwndParent = hWnd;
871
872 if(FAILED(UniformResourceLocatorW_InvokeCommand(&shortcut->IUniformResourceLocatorW_iface, (PURLINVOKECOMMANDINFOW) &ici)))
873 TRACE("failed to open URL: %s\n", debugstr_a(lpcstrUrl));
874 }
875
876 heap_free(urlfilepath);
877 Unknown_Release(shortcut);
878 }
879