msi: Fix the Create/RemoveFolders actions to actually create and remove empty folders.
[wine] / dlls / hlink / link.c
1 /*
2  * Implementation of hyperlinking (hlink.dll)
3  *
4  * Copyright 2005 Aric Stewart for CodeWeavers
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 #include "hlink_private.h"
22
23 #include "shellapi.h"
24 #include "hlguids.h"
25
26 #include "wine/debug.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(hlink);
29
30 #define HLINK_SAVE_MAGIC    0x00000002
31 #define HLINK_SAVE_MONIKER_PRESENT      0x01
32 #define HLINK_SAVE_MONIKER_IS_ABSOLUTE  0x02
33 #define HLINK_SAVE_LOCATION_PRESENT     0x08
34 #define HLINK_SAVE_FRIENDLY_PRESENT     0x10
35 /* 0x20, 0x40 unknown */
36 #define HLINK_SAVE_TARGET_FRAME_PRESENT 0x80
37 /* known flags */
38 #define HLINK_SAVE_ALL (HLINK_SAVE_TARGET_FRAME_PRESENT|HLINK_SAVE_FRIENDLY_PRESENT|HLINK_SAVE_LOCATION_PRESENT|0x04|HLINK_SAVE_MONIKER_IS_ABSOLUTE|HLINK_SAVE_MONIKER_PRESENT)
39
40 static const IHlinkVtbl              hlvt;
41 static const IPersistStreamVtbl      psvt;
42 static const IDataObjectVtbl         dovt;
43
44 typedef struct
45 {
46     const IHlinkVtbl    *lpVtbl;
47     LONG                ref;
48
49     const IPersistStreamVtbl    *lpPSVtbl;
50     const IDataObjectVtbl       *lpDOVtbl;
51
52     LPWSTR              FriendlyName;
53     LPWSTR              Location;
54     LPWSTR              TargetFrameName;
55     IMoniker            *Moniker;
56     IHlinkSite          *Site;
57     DWORD               SiteData;
58     BOOL                absolute;
59 } HlinkImpl;
60
61
62 static inline HlinkImpl* HlinkImpl_from_IPersistStream( IPersistStream* iface)
63 {
64     return (HlinkImpl*) ((CHAR*)iface - FIELD_OFFSET(HlinkImpl, lpPSVtbl));
65 }
66
67 static inline HlinkImpl* HlinkImpl_from_IDataObject( IDataObject* iface)
68 {
69     return (HlinkImpl*) ((CHAR*)iface - FIELD_OFFSET(HlinkImpl, lpDOVtbl));
70 }
71
72 static HRESULT __GetMoniker(HlinkImpl* This, IMoniker** moniker,
73         DWORD ref_type)
74 {
75     HRESULT hres;
76
77     if (ref_type == HLINKGETREF_DEFAULT)
78         ref_type = HLINKGETREF_RELATIVE;
79
80     if (ref_type == HLINKGETREF_ABSOLUTE && This->Site)
81     {
82         IMoniker *hls_moniker;
83
84         hres = IHlinkSite_GetMoniker(This->Site, This->SiteData,
85                 OLEGETMONIKER_FORCEASSIGN, OLEWHICHMK_CONTAINER, &hls_moniker);
86         if (FAILED(hres))
87             return hres;
88
89         if (This->Moniker)
90         {
91             hres = IMoniker_ComposeWith(hls_moniker, This->Moniker, FALSE,
92                     moniker);
93             IMoniker_Release(hls_moniker);
94             return hres;
95         }
96
97         *moniker = hls_moniker;
98         return S_OK;
99     }
100
101     *moniker = This->Moniker;
102     if (*moniker)
103         IMoniker_AddRef(*moniker);
104
105     return S_OK;
106 }
107
108 HRESULT WINAPI HLink_Constructor(IUnknown *pUnkOuter, REFIID riid,
109         LPVOID *ppv)
110 {
111     HlinkImpl * hl;
112
113     TRACE("unkOut=%p riid=%s\n", pUnkOuter, debugstr_guid(riid));
114     *ppv = NULL;
115
116     if (pUnkOuter)
117         return CLASS_E_NOAGGREGATION;
118
119     hl = heap_alloc_zero(sizeof(HlinkImpl));
120     if (!hl)
121         return E_OUTOFMEMORY;
122
123     hl->ref = 1;
124     hl->lpVtbl = &hlvt;
125     hl->lpPSVtbl = &psvt;
126     hl->lpDOVtbl = &dovt;
127
128     *ppv = hl;
129     return S_OK;
130 }
131
132 static HRESULT WINAPI IHlink_fnQueryInterface(IHlink* iface, REFIID riid,
133         LPVOID *ppvObj)
134 {
135     HlinkImpl  *This = (HlinkImpl*)iface;
136
137     TRACE ("(%p)->(%s,%p)\n", This, debugstr_guid (riid), ppvObj);
138
139     *ppvObj = NULL;
140
141     if (IsEqualIID(riid, &IID_IUnknown) || (IsEqualIID(riid, &IID_IHlink)))
142         *ppvObj = This;
143     else if (IsEqualIID(riid, &IID_IPersistStream))
144         *ppvObj = &(This->lpPSVtbl);
145     else if (IsEqualIID(riid, &IID_IDataObject))
146         *ppvObj = &(This->lpDOVtbl);
147
148     if (*ppvObj)
149     {
150         IUnknown_AddRef((IUnknown*)(*ppvObj));
151         return S_OK;
152     }
153     return E_NOINTERFACE;
154 }
155
156 static ULONG WINAPI IHlink_fnAddRef (IHlink* iface)
157 {
158     HlinkImpl  *This = (HlinkImpl*)iface;
159     ULONG refCount = InterlockedIncrement(&This->ref);
160
161     TRACE("(%p)->(count=%u)\n", This, refCount - 1);
162
163     return refCount;
164 }
165
166 static ULONG WINAPI IHlink_fnRelease (IHlink* iface)
167 {
168     HlinkImpl  *This = (HlinkImpl*)iface;
169     ULONG refCount = InterlockedDecrement(&This->ref);
170
171     TRACE("(%p)->(count=%u)\n", This, refCount + 1);
172     if (refCount)
173         return refCount;
174
175     TRACE("-- destroying IHlink (%p)\n", This);
176     heap_free(This->FriendlyName);
177     heap_free(This->TargetFrameName);
178     heap_free(This->Location);
179     if (This->Moniker)
180         IMoniker_Release(This->Moniker);
181     if (This->Site)
182         IHlinkSite_Release(This->Site);
183     heap_free(This);
184     return 0;
185 }
186
187 static HRESULT WINAPI IHlink_fnSetHlinkSite( IHlink* iface,
188         IHlinkSite* pihlSite, DWORD dwSiteData)
189 {
190     HlinkImpl  *This = (HlinkImpl*)iface;
191
192     TRACE("(%p)->(%p %i)\n", This, pihlSite, dwSiteData);
193
194     if (This->Site)
195         IHlinkSite_Release(This->Site);
196
197     This->Site = pihlSite;
198     if (This->Site)
199         IHlinkSite_AddRef(This->Site);
200
201     This->SiteData = dwSiteData;
202
203     return S_OK;
204 }
205
206 static HRESULT WINAPI IHlink_fnGetHlinkSite( IHlink* iface,
207         IHlinkSite** ppihlSite, DWORD *pdwSiteData)
208 {
209     HlinkImpl  *This = (HlinkImpl*)iface;
210
211     TRACE("(%p)->(%p %p)\n", This, ppihlSite, pdwSiteData);
212
213     *ppihlSite = This->Site;
214
215     if (This->Site) {
216         IHlinkSite_AddRef(This->Site);
217         *pdwSiteData = This->SiteData;
218     }
219
220     return S_OK;
221 }
222
223 static HRESULT WINAPI IHlink_fnSetMonikerReference( IHlink* iface,
224         DWORD rfHLSETF, IMoniker *pmkTarget, LPCWSTR pwzLocation)
225 {
226     HlinkImpl  *This = (HlinkImpl*)iface;
227
228     TRACE("(%p)->(%i %p %s)\n", This, rfHLSETF, pmkTarget,
229             debugstr_w(pwzLocation));
230
231     if(rfHLSETF == 0)
232         return E_INVALIDARG;
233     if(!(rfHLSETF & (HLINKSETF_TARGET | HLINKSETF_LOCATION)))
234         return rfHLSETF;
235
236     if(rfHLSETF & HLINKSETF_TARGET){
237         if (This->Moniker)
238             IMoniker_Release(This->Moniker);
239
240         This->Moniker = pmkTarget;
241         if (This->Moniker)
242         {
243             LPOLESTR display_name;
244             IMoniker_AddRef(This->Moniker);
245             IMoniker_GetDisplayName(This->Moniker, NULL, NULL, &display_name);
246             This->absolute = display_name && strchrW(display_name, ':');
247             CoTaskMemFree(display_name);
248         }
249     }
250
251     if(rfHLSETF & HLINKSETF_LOCATION){
252         heap_free(This->Location);
253         This->Location = hlink_strdupW( pwzLocation );
254     }
255
256     return S_OK;
257 }
258
259 static HRESULT WINAPI IHlink_fnSetStringReference(IHlink* iface,
260         DWORD grfHLSETF, LPCWSTR pwzTarget, LPCWSTR pwzLocation)
261 {
262     HlinkImpl  *This = (HlinkImpl*)iface;
263
264     TRACE("(%p)->(%i %s %s)\n", This, grfHLSETF, debugstr_w(pwzTarget),
265             debugstr_w(pwzLocation));
266
267     if(grfHLSETF > (HLINKSETF_TARGET | HLINKSETF_LOCATION) &&
268             grfHLSETF < -(HLINKSETF_TARGET | HLINKSETF_LOCATION))
269         return grfHLSETF;
270
271     if (grfHLSETF & HLINKSETF_TARGET)
272     {
273         if (This->Moniker)
274         {
275             IMoniker_Release(This->Moniker);
276             This->Moniker = NULL;
277         }
278         if (pwzTarget && *pwzTarget)
279         {
280             IMoniker *pMon;
281             IBindCtx *pbc = NULL;
282             ULONG eaten;
283             HRESULT r;
284
285             r = CreateBindCtx(0, &pbc);
286             if (FAILED(r))
287                 return E_OUTOFMEMORY;
288
289             r = MkParseDisplayName(pbc, pwzTarget, &eaten, &pMon);
290             IBindCtx_Release(pbc);
291
292             if (FAILED(r))
293             {
294                 LPCWSTR p = strchrW(pwzTarget, ':');
295                 if (p && (p - pwzTarget > 1))
296                     r = CreateURLMoniker(NULL, pwzTarget, &pMon);
297                 else
298                     r = CreateFileMoniker(pwzTarget, &pMon);
299                 if (FAILED(r))
300                 {
301                     ERR("couldn't create moniker for %s, failed with error 0x%08x\n",
302                         debugstr_w(pwzTarget), r);
303                     return r;
304                 }
305             }
306
307             IHlink_SetMonikerReference(iface, HLINKSETF_TARGET, pMon, NULL);
308             IMoniker_Release(pMon);
309         }
310     }
311
312     if (grfHLSETF & HLINKSETF_LOCATION)
313     {
314         heap_free(This->Location);
315         This->Location = NULL;
316         if (pwzLocation && *pwzLocation)
317             This->Location = hlink_strdupW( pwzLocation );
318     }
319
320     return S_OK;
321 }
322
323 static HRESULT WINAPI IHlink_fnGetMonikerReference(IHlink* iface,
324         DWORD dwWhichRef, IMoniker **ppimkTarget, LPWSTR *ppwzLocation)
325 {
326     HlinkImpl  *This = (HlinkImpl*)iface;
327
328     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppimkTarget,
329             ppwzLocation);
330
331     if (ppimkTarget)
332     {
333         HRESULT hres = __GetMoniker(This, ppimkTarget, dwWhichRef);
334         if (FAILED(hres))
335         {
336             if (ppwzLocation)
337                 *ppwzLocation = NULL;
338             return hres;
339         }
340     }
341
342     if (ppwzLocation)
343         IHlink_GetStringReference(iface, dwWhichRef, NULL, ppwzLocation);
344
345     return S_OK;
346 }
347
348 static HRESULT WINAPI IHlink_fnGetStringReference (IHlink* iface,
349         DWORD dwWhichRef, LPWSTR *ppwzTarget, LPWSTR *ppwzLocation)
350 {
351     HlinkImpl  *This = (HlinkImpl*)iface;
352
353     TRACE("(%p) -> (%i %p %p)\n", This, dwWhichRef, ppwzTarget, ppwzLocation);
354
355     if(dwWhichRef != -1 && dwWhichRef & ~(HLINKGETREF_DEFAULT | HLINKGETREF_ABSOLUTE | HLINKGETREF_RELATIVE))
356     {
357         if(ppwzTarget)
358             *ppwzTarget = NULL;
359         if(ppwzLocation)
360             *ppwzLocation = NULL;
361         return E_INVALIDARG;
362     }
363
364     if (ppwzTarget)
365     {
366         IMoniker* mon;
367         HRESULT hres = __GetMoniker(This, &mon, dwWhichRef);
368         if (FAILED(hres))
369         {
370             if (ppwzLocation)
371                 *ppwzLocation = NULL;
372             return hres;
373         }
374         if (mon)
375         {
376             IBindCtx *pbc;
377
378             CreateBindCtx( 0, &pbc);
379             IMoniker_GetDisplayName(mon, pbc, NULL, ppwzTarget);
380             IBindCtx_Release(pbc);
381             IMoniker_Release(mon);
382         }
383         else
384             *ppwzTarget = NULL;
385     }
386     if (ppwzLocation)
387         *ppwzLocation = hlink_co_strdupW( This->Location );
388
389     TRACE("(Target: %s Location: %s)\n",
390             (ppwzTarget)?debugstr_w(*ppwzTarget):"<NULL>",
391             (ppwzLocation)?debugstr_w(*ppwzLocation):"<NULL>");
392
393     return S_OK;
394 }
395
396 static HRESULT WINAPI IHlink_fnSetFriendlyName (IHlink *iface,
397         LPCWSTR pwzFriendlyName)
398 {
399     HlinkImpl  *This = (HlinkImpl*)iface;
400
401     TRACE("(%p) -> (%s)\n", This, debugstr_w(pwzFriendlyName));
402
403     heap_free(This->FriendlyName);
404     This->FriendlyName = hlink_strdupW( pwzFriendlyName );
405
406     return S_OK;
407 }
408
409 static HRESULT WINAPI IHlink_fnGetFriendlyName (IHlink* iface,
410         DWORD grfHLFNAMEF, LPWSTR* ppwzFriendlyName)
411 {
412     HlinkImpl  *This = (HlinkImpl*)iface;
413
414     TRACE("(%p) -> (%i %p)\n", This, grfHLFNAMEF, ppwzFriendlyName);
415
416     /* FIXME: Only using explicitly set and cached friendly names */
417
418     if (This->FriendlyName)
419         *ppwzFriendlyName = hlink_co_strdupW( This->FriendlyName );
420     else
421     {
422         IMoniker *moniker;
423         HRESULT hres = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
424         if (FAILED(hres))
425         {
426             *ppwzFriendlyName = NULL;
427             return hres;
428         }
429         if (moniker)
430         {
431             IBindCtx *bcxt;
432             CreateBindCtx(0, &bcxt);
433
434             IMoniker_GetDisplayName(moniker, bcxt, NULL, ppwzFriendlyName);
435             IBindCtx_Release(bcxt);
436             IMoniker_Release(moniker);
437         }
438         else
439             *ppwzFriendlyName = NULL;
440     }
441
442     return S_OK;
443 }
444
445 static HRESULT WINAPI IHlink_fnSetTargetFrameName(IHlink* iface,
446         LPCWSTR pwzTargetFramename)
447 {
448     HlinkImpl  *This = (HlinkImpl*)iface;
449     TRACE("(%p)->(%s)\n", This, debugstr_w(pwzTargetFramename));
450
451     heap_free(This->TargetFrameName);
452     This->TargetFrameName = hlink_strdupW( pwzTargetFramename );
453
454     return S_OK;
455 }
456
457 static HRESULT WINAPI IHlink_fnGetTargetFrameName(IHlink* iface,
458         LPWSTR *ppwzTargetFrameName)
459 {
460     HlinkImpl  *This = (HlinkImpl*)iface;
461
462     TRACE("(%p)->(%p)\n", This, ppwzTargetFrameName);
463     *ppwzTargetFrameName = hlink_co_strdupW( This->TargetFrameName );
464
465     return S_OK;
466 }
467
468 static HRESULT WINAPI IHlink_fnGetMiscStatus(IHlink* iface, DWORD* pdwStatus)
469 {
470     FIXME("\n");
471     return E_NOTIMPL;
472 }
473
474 static HRESULT WINAPI IHlink_fnNavigate(IHlink* iface, DWORD grfHLNF, LPBC pbc,
475         IBindStatusCallback *pbsc, IHlinkBrowseContext *phbc)
476 {
477     HlinkImpl  *This = (HlinkImpl*)iface;
478     IMoniker *mon = NULL;
479     HRESULT r;
480
481     FIXME("Semi-Stub:(%p)->(%i %p %p %p)\n", This, grfHLNF, pbc, pbsc, phbc);
482
483     r = __GetMoniker(This, &mon, HLINKGETREF_ABSOLUTE);
484     TRACE("Moniker %p\n", mon);
485
486     if (SUCCEEDED(r))
487     {
488         IBindCtx *bcxt;
489         IHlinkTarget *target = NULL;
490
491         CreateBindCtx(0, &bcxt);
492
493         RegisterBindStatusCallback(bcxt, pbsc, NULL, 0);
494
495         r = IMoniker_BindToObject(mon, bcxt, NULL, &IID_IHlinkTarget,
496                 (LPVOID*)&target);
497         TRACE("IHlinkTarget returned 0x%x\n", r);
498         if (r == S_OK)
499         {
500             IHlinkTarget_SetBrowseContext(target, phbc);
501             IHlinkTarget_Navigate(target, grfHLNF, This->Location);
502             IHlinkTarget_Release(target);
503         }
504         else
505         {
506             static const WCHAR szOpen[] = {'o','p','e','n',0};
507             LPWSTR target = NULL;
508
509             r = IHlink_GetStringReference(iface, HLINKGETREF_DEFAULT, &target, NULL);
510             if (SUCCEEDED(r) && target)
511             {
512                 ShellExecuteW(NULL, szOpen, target, NULL, NULL, SW_SHOW);
513                 CoTaskMemFree(target);
514             }
515         }
516
517         RevokeBindStatusCallback(bcxt, pbsc);
518
519         IBindCtx_Release(bcxt);
520         IMoniker_Release(mon);
521     }
522
523     if (This->Site)
524         IHlinkSite_OnNavigationComplete(This->Site, This->SiteData, 0, r, NULL);
525
526     TRACE("Finished Navigation\n");
527     return r;
528 }
529
530 static HRESULT WINAPI IHlink_fnSetAdditonalParams(IHlink* iface,
531         LPCWSTR pwzAdditionalParams)
532 {
533     TRACE("Not implemented in native IHlink\n");
534     return E_NOTIMPL;
535 }
536
537 static HRESULT WINAPI IHlink_fnGetAdditionalParams(IHlink* iface,
538         LPWSTR* ppwzAdditionalParams)
539 {
540     TRACE("Not implemented in native IHlink\n");
541     return E_NOTIMPL;
542 }
543
544 static const IHlinkVtbl hlvt =
545 {
546     IHlink_fnQueryInterface,
547     IHlink_fnAddRef,
548     IHlink_fnRelease,
549     IHlink_fnSetHlinkSite,
550     IHlink_fnGetHlinkSite,
551     IHlink_fnSetMonikerReference,
552     IHlink_fnGetMonikerReference,
553     IHlink_fnSetStringReference,
554     IHlink_fnGetStringReference,
555     IHlink_fnSetFriendlyName,
556     IHlink_fnGetFriendlyName,
557     IHlink_fnSetTargetFrameName,
558     IHlink_fnGetTargetFrameName,
559     IHlink_fnGetMiscStatus,
560     IHlink_fnNavigate,
561     IHlink_fnSetAdditonalParams,
562     IHlink_fnGetAdditionalParams
563 };
564
565 static HRESULT WINAPI IDataObject_fnQueryInterface(IDataObject* iface,
566         REFIID riid, LPVOID *ppvObj)
567 {
568     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
569     TRACE("%p\n", This);
570     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
571 }
572
573 static ULONG WINAPI IDataObject_fnAddRef (IDataObject* iface)
574 {
575     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
576     TRACE("%p\n", This);
577     return IHlink_AddRef((IHlink*)This);
578 }
579
580 static ULONG WINAPI IDataObject_fnRelease (IDataObject* iface)
581 {
582     HlinkImpl *This = HlinkImpl_from_IDataObject(iface);
583     TRACE("%p\n", This);
584     return IHlink_Release((IHlink*)This);
585 }
586
587 static HRESULT WINAPI IDataObject_fnGetData(IDataObject* iface,
588         FORMATETC* pformatetcIn, STGMEDIUM* pmedium)
589 {
590     FIXME("\n");
591     return E_NOTIMPL;
592 }
593
594 static HRESULT WINAPI IDataObject_fnGetDataHere(IDataObject* iface,
595         FORMATETC* pformatetc, STGMEDIUM* pmedium)
596 {
597     FIXME("\n");
598     return E_NOTIMPL;
599 }
600
601 static HRESULT WINAPI IDataObject_fnQueryGetData(IDataObject* iface,
602         FORMATETC* pformatetc)
603 {
604     FIXME("\n");
605     return E_NOTIMPL;
606 }
607
608 static HRESULT WINAPI IDataObject_fnGetConicalFormatEtc(IDataObject* iface,
609         FORMATETC* pformatetcIn, FORMATETC* pformatetcOut)
610 {
611     FIXME("\n");
612     return E_NOTIMPL;
613 }
614
615 static HRESULT WINAPI IDataObject_fnSetData(IDataObject* iface,
616         FORMATETC* pformatetc, STGMEDIUM* pmedium, BOOL fRelease)
617 {
618     FIXME("\n");
619     return E_NOTIMPL;
620 }
621
622 static HRESULT WINAPI IDataObject_fnEnumFormatEtc(IDataObject* iface,
623         DWORD dwDirection, IEnumFORMATETC** ppenumFormatEtc)
624 {
625     FIXME("\n");
626     return E_NOTIMPL;
627 }
628
629 static HRESULT WINAPI IDataObject_fnDAdvise(IDataObject* iface,
630         FORMATETC* pformatetc, DWORD advf, IAdviseSink* pAdvSink,
631         DWORD* pdwConnection)
632 {
633     FIXME("\n");
634     return E_NOTIMPL;
635 }
636
637 static HRESULT WINAPI IDataObject_fnDUnadvise(IDataObject* iface,
638         DWORD dwConnection)
639 {
640     FIXME("\n");
641     return E_NOTIMPL;
642 }
643
644 static HRESULT WINAPI IDataObject_fnEnumDAdvise(IDataObject* iface,
645         IEnumSTATDATA** ppenumAdvise)
646 {
647     FIXME("\n");
648     return E_NOTIMPL;
649 }
650
651 static const IDataObjectVtbl dovt =
652 {
653     IDataObject_fnQueryInterface,
654     IDataObject_fnAddRef,
655     IDataObject_fnRelease,
656     IDataObject_fnGetData,
657     IDataObject_fnGetDataHere,
658     IDataObject_fnQueryGetData,
659     IDataObject_fnGetConicalFormatEtc,
660     IDataObject_fnSetData,
661     IDataObject_fnEnumFormatEtc,
662     IDataObject_fnDAdvise,
663     IDataObject_fnDUnadvise,
664     IDataObject_fnEnumDAdvise
665 };
666
667 static HRESULT WINAPI IPersistStream_fnQueryInterface(IPersistStream* iface,
668         REFIID riid, LPVOID *ppvObj)
669 {
670     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
671     TRACE("(%p)\n", This);
672     return IHlink_QueryInterface((IHlink*)This, riid, ppvObj);
673 }
674
675 static ULONG WINAPI IPersistStream_fnAddRef (IPersistStream* iface)
676 {
677     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
678     TRACE("(%p)\n", This);
679     return IHlink_AddRef((IHlink*)This);
680 }
681
682 static ULONG WINAPI IPersistStream_fnRelease (IPersistStream* iface)
683 {
684     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
685     TRACE("(%p)\n", This);
686     return IHlink_Release((IHlink*)This);
687 }
688
689 static HRESULT WINAPI IPersistStream_fnGetClassID(IPersistStream* iface,
690         CLSID* pClassID)
691 {
692     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
693     TRACE("(%p)\n", This);
694     *pClassID = CLSID_StdHlink;
695     return S_OK;
696 }
697
698 static HRESULT WINAPI IPersistStream_fnIsDirty(IPersistStream* iface)
699 {
700     FIXME("\n");
701     return E_NOTIMPL;
702 }
703
704 static HRESULT write_hlink_string(IStream *pStm, LPCWSTR str)
705 {
706     DWORD len;
707     HRESULT hr;
708
709     TRACE("(%p, %s)\n", pStm, debugstr_w(str));
710
711     len = strlenW(str) + 1;
712
713     hr = IStream_Write(pStm, &len, sizeof(len), NULL);
714     if (FAILED(hr)) return hr;
715
716     hr = IStream_Write(pStm, str, len * sizeof(WCHAR), NULL);
717     if (FAILED(hr)) return hr;
718
719     return S_OK;
720 }
721
722 static inline ULONG size_hlink_string(LPCWSTR str)
723 {
724     return sizeof(DWORD) + (strlenW(str) + 1) * sizeof(WCHAR);
725 }
726
727 static HRESULT read_hlink_string(IStream *pStm, LPWSTR *out_str)
728 {
729     LPWSTR str;
730     DWORD len;
731     ULONG read;
732     HRESULT hr;
733
734     hr = IStream_Read(pStm, &len, sizeof(len), &read);
735     if (FAILED(hr)) return hr;
736     if (read != sizeof(len)) return STG_E_READFAULT;
737
738     TRACE("read len %d\n", len);
739
740     str = heap_alloc(len * sizeof(WCHAR));
741     if (!str) return E_OUTOFMEMORY;
742
743     hr = IStream_Read(pStm, str, len * sizeof(WCHAR), &read);
744     if (FAILED(hr))
745     {
746         heap_free(str);
747         return hr;
748     }
749     if (read != len * sizeof(WCHAR))
750     {
751         heap_free(str);
752         return STG_E_READFAULT;
753     }
754     TRACE("read string %s\n", debugstr_w(str));
755
756     *out_str = str;
757     return S_OK;
758 }
759
760 static HRESULT WINAPI IPersistStream_fnLoad(IPersistStream* iface,
761         IStream* pStm)
762 {
763     HRESULT r;
764     DWORD hdr[2];
765     DWORD read;
766     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
767
768     r = IStream_Read(pStm, hdr, sizeof(hdr), &read);
769     if (read != sizeof(hdr) || (hdr[0] != HLINK_SAVE_MAGIC))
770     {
771         r = E_FAIL;
772         goto end;
773     }
774     if (hdr[1] & ~HLINK_SAVE_ALL)
775         FIXME("unknown flag(s) 0x%x\n", hdr[1] & ~HLINK_SAVE_ALL);
776
777     if (hdr[1] & HLINK_SAVE_TARGET_FRAME_PRESENT)
778     {
779         TRACE("loading target frame name\n");
780         r = read_hlink_string(pStm, &This->TargetFrameName);
781         if (FAILED(r)) goto end;
782     }
783
784     if (hdr[1] & HLINK_SAVE_FRIENDLY_PRESENT)
785     {
786         TRACE("loading target friendly name\n");
787         if (!(hdr[1] & 0x4))
788             FIXME("0x4 flag not present with friendly name flag - not sure what this means\n");
789         r = read_hlink_string(pStm, &This->FriendlyName);
790         if (FAILED(r)) goto end;
791     }
792
793     if (hdr[1] & HLINK_SAVE_MONIKER_PRESENT)
794     {
795         TRACE("loading moniker\n");
796         r = OleLoadFromStream(pStm, &IID_IMoniker, (LPVOID*)&(This->Moniker));
797         if (FAILED(r))
798             goto end;
799         This->absolute = hdr[1] & HLINK_SAVE_MONIKER_IS_ABSOLUTE ? TRUE : FALSE;
800     }
801
802     if (hdr[1] & HLINK_SAVE_LOCATION_PRESENT)
803     {
804         TRACE("loading location\n");
805         r = read_hlink_string(pStm, &This->Location);
806         if (FAILED(r)) goto end;
807     }
808
809 end:
810     TRACE("Load Result 0x%x (%p)\n", r, This->Moniker);
811
812     return r;
813 }
814
815 static HRESULT WINAPI IPersistStream_fnSave(IPersistStream* iface,
816         IStream* pStm, BOOL fClearDirty)
817 {
818     HRESULT r;
819     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
820     DWORD hdr[2];
821     IMoniker *moniker;
822
823     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
824
825     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
826     if (FAILED(r))
827         return r;
828     r = E_FAIL;
829
830     hdr[0] = HLINK_SAVE_MAGIC;
831     hdr[1] = 0;
832
833     if (moniker)
834         hdr[1] |= HLINK_SAVE_MONIKER_PRESENT;
835     if (This->absolute)
836         hdr[1] |= HLINK_SAVE_MONIKER_IS_ABSOLUTE;
837     if (This->Location)
838         hdr[1] |= HLINK_SAVE_LOCATION_PRESENT;
839     if (This->FriendlyName)
840         hdr[1] |= HLINK_SAVE_FRIENDLY_PRESENT | 4 /* FIXME */;
841     if (This->TargetFrameName)
842         hdr[1] |= HLINK_SAVE_TARGET_FRAME_PRESENT;
843
844     IStream_Write(pStm, hdr, sizeof(hdr), NULL);
845
846     if (This->TargetFrameName)
847     {
848         r = write_hlink_string(pStm, This->TargetFrameName);
849         if (FAILED(r)) goto end;
850     }
851
852     if (This->FriendlyName)
853     {
854         r = write_hlink_string(pStm, This->FriendlyName);
855         if (FAILED(r)) goto end;
856     }
857
858     if (moniker)
859     {
860         IPersistStream* monstream;
861
862         monstream = NULL;
863         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
864                 (LPVOID*)&monstream);
865         if (monstream)
866         {
867             r = OleSaveToStream(monstream, pStm);
868             IPersistStream_Release(monstream);
869         }
870         if (FAILED(r)) goto end;
871     }
872
873     if (This->Location)
874     {
875         r = write_hlink_string(pStm, This->Location);
876         if (FAILED(r)) goto end;
877     }
878
879 end:
880     if (moniker) IMoniker_Release(moniker);
881     TRACE("Save Result 0x%x\n", r);
882
883     return r;
884 }
885
886 static HRESULT WINAPI IPersistStream_fnGetSizeMax(IPersistStream* iface,
887         ULARGE_INTEGER* pcbSize)
888 {
889     HRESULT r;
890     HlinkImpl *This = HlinkImpl_from_IPersistStream(iface);
891     IMoniker *moniker;
892
893     TRACE("(%p) Moniker(%p)\n", This, This->Moniker);
894
895     pcbSize->QuadPart = sizeof(DWORD)*2;
896
897     if (This->TargetFrameName)
898         pcbSize->QuadPart += size_hlink_string(This->TargetFrameName);
899
900     if (This->FriendlyName)
901         pcbSize->QuadPart += size_hlink_string(This->FriendlyName);
902
903     r = __GetMoniker(This, &moniker, HLINKGETREF_DEFAULT);
904     if (FAILED(r))
905         return r;
906     r = E_FAIL;
907
908     if (moniker)
909     {
910         IPersistStream* monstream = NULL;
911         IMoniker_QueryInterface(moniker, &IID_IPersistStream,
912                 (LPVOID*)&monstream);
913         if (monstream)
914         {
915             ULARGE_INTEGER mon_size;
916             r = IPersistStream_GetSizeMax(monstream, &mon_size);
917             pcbSize->QuadPart += mon_size.QuadPart;
918             IPersistStream_Release(monstream);
919         }
920         IMoniker_Release(moniker);
921     }
922
923     if (This->Location)
924         pcbSize->QuadPart += size_hlink_string(This->Location);
925
926     return r;
927 }
928
929 static const IPersistStreamVtbl psvt =
930 {
931     IPersistStream_fnQueryInterface,
932     IPersistStream_fnAddRef,
933     IPersistStream_fnRelease,
934     IPersistStream_fnGetClassID,
935     IPersistStream_fnIsDirty,
936     IPersistStream_fnLoad,
937     IPersistStream_fnSave,
938     IPersistStream_fnGetSizeMax,
939 };