shell32: Reject NULL path in IShellLink_SetPath.
[wine] / dlls / shell32 / shellitem.c
1 /*
2  * IShellItem implementation
3  *
4  * Copyright 2008 Vincent Povirk 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 "config.h"
22 #include "wine/port.h"
23
24 #include <stdio.h>
25 #include <stdarg.h>
26
27 #define COBJMACROS
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "wine/debug.h"
34
35 #include "pidl.h"
36 #include "shell32_main.h"
37 #include "debughlp.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
40
41 typedef struct _ShellItem {
42     const IShellItemVtbl    *lpIShellItemVtbl;
43     LONG                    ref;
44     LPITEMIDLIST            pidl;
45     const IPersistIDListVtbl *lpIPersistIDListVtbl;
46 } ShellItem;
47
48
49 static inline ShellItem *impl_from_IPersistIDList( IPersistIDList *iface )
50 {
51     return (ShellItem*)((char*)iface - FIELD_OFFSET(ShellItem, lpIPersistIDListVtbl));
52 }
53
54
55 static HRESULT WINAPI ShellItem_QueryInterface(IShellItem *iface, REFIID riid,
56     void **ppv)
57 {
58     ShellItem *This = (ShellItem*)iface;
59
60     TRACE("(%p,%p,%p)\n", iface, riid, ppv);
61
62     if (!ppv) return E_INVALIDARG;
63
64     if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IShellItem, riid))
65     {
66         *ppv = This;
67     }
68     else if (IsEqualIID(&IID_IPersist, riid) || IsEqualIID(&IID_IPersistIDList, riid))
69     {
70         *ppv = &(This->lpIPersistIDListVtbl);
71     }
72     else {
73         FIXME("not implemented for %s\n", shdebugstr_guid(riid));
74         *ppv = NULL;
75         return E_NOINTERFACE;
76     }
77
78     IUnknown_AddRef((IUnknown*)*ppv);
79     return S_OK;
80 }
81
82 static ULONG WINAPI ShellItem_AddRef(IShellItem *iface)
83 {
84     ShellItem *This = (ShellItem*)iface;
85     ULONG ref = InterlockedIncrement(&This->ref);
86
87     TRACE("(%p), new refcount=%i\n", iface, ref);
88
89     return ref;
90 }
91
92 static ULONG WINAPI ShellItem_Release(IShellItem *iface)
93 {
94     ShellItem *This = (ShellItem*)iface;
95     ULONG ref = InterlockedDecrement(&This->ref);
96
97     TRACE("(%p), new refcount=%i\n", iface, ref);
98
99     if (ref == 0)
100     {
101         ILFree(This->pidl);
102         HeapFree(GetProcessHeap(), 0, This);
103     }
104
105     return ref;
106 }
107
108 static HRESULT ShellItem_get_parent_pidl(ShellItem *This, LPITEMIDLIST *parent_pidl)
109 {
110     *parent_pidl = ILClone(This->pidl);
111     if (*parent_pidl)
112     {
113         if (ILRemoveLastID(*parent_pidl))
114             return S_OK;
115         else
116         {
117             ILFree(*parent_pidl);
118             *parent_pidl = NULL;
119             return E_INVALIDARG;
120         }
121     }
122     else
123     {
124         *parent_pidl = NULL;
125         return E_OUTOFMEMORY;
126     }
127 }
128
129 static HRESULT ShellItem_get_parent_shellfolder(ShellItem *This, IShellFolder **ppsf)
130 {
131     LPITEMIDLIST parent_pidl;
132     IShellFolder *desktop;
133     HRESULT ret;
134
135     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
136     if (SUCCEEDED(ret))
137     {
138         ret = SHGetDesktopFolder(&desktop);
139         if (SUCCEEDED(ret))
140         {
141             ret = IShellFolder_BindToObject(desktop, parent_pidl, NULL, &IID_IShellFolder, (void**)ppsf);
142             IShellFolder_Release(desktop);
143         }
144         ILFree(parent_pidl);
145     }
146
147     return ret;
148 }
149
150 static HRESULT WINAPI ShellItem_BindToHandler(IShellItem *iface, IBindCtx *pbc,
151     REFGUID rbhid, REFIID riid, void **ppvOut)
152 {
153     FIXME("(%p,%p,%s,%p,%p)\n", iface, pbc, shdebugstr_guid(rbhid), riid, ppvOut);
154
155     *ppvOut = NULL;
156
157     return E_NOTIMPL;
158 }
159
160 static HRESULT WINAPI ShellItem_GetParent(IShellItem *iface, IShellItem **ppsi)
161 {
162     ShellItem *This = (ShellItem*)iface;
163     LPITEMIDLIST parent_pidl;
164     HRESULT ret;
165
166     TRACE("(%p,%p)\n", iface, ppsi);
167
168     ret = ShellItem_get_parent_pidl(This, &parent_pidl);
169     if (SUCCEEDED(ret))
170     {
171         ret = SHCreateShellItem(NULL, NULL, parent_pidl, ppsi);
172         ILFree(parent_pidl);
173     }
174
175     return ret;
176 }
177
178 static HRESULT WINAPI ShellItem_GetDisplayName(IShellItem *iface, SIGDN sigdnName,
179     LPWSTR *ppszName)
180 {
181     FIXME("(%p,%x,%p)\n", iface, sigdnName, ppszName);
182
183     *ppszName = NULL;
184
185     return E_NOTIMPL;
186 }
187
188 static HRESULT WINAPI ShellItem_GetAttributes(IShellItem *iface, SFGAOF sfgaoMask,
189     SFGAOF *psfgaoAttribs)
190 {
191     ShellItem *This = (ShellItem*)iface;
192     IShellFolder *parent_folder;
193     LPITEMIDLIST child_pidl;
194     HRESULT ret;
195
196     TRACE("(%p,%x,%p)\n", iface, sfgaoMask, psfgaoAttribs);
197
198     ret = ShellItem_get_parent_shellfolder(This, &parent_folder);
199     if (SUCCEEDED(ret))
200     {
201         child_pidl = ILFindLastID(This->pidl);
202         *psfgaoAttribs = sfgaoMask;
203         ret = IShellFolder_GetAttributesOf(parent_folder, 1, (LPCITEMIDLIST*)&child_pidl, psfgaoAttribs);
204         IShellFolder_Release(parent_folder);
205     }
206
207     return ret;
208 }
209
210 static HRESULT WINAPI ShellItem_Compare(IShellItem *iface, IShellItem *oth,
211     SICHINTF hint, int *piOrder)
212 {
213     FIXME("(%p,%p,%x,%p)\n", iface, oth, hint, piOrder);
214
215     return E_NOTIMPL;
216 }
217
218 static const IShellItemVtbl ShellItem_Vtbl = {
219     ShellItem_QueryInterface,
220     ShellItem_AddRef,
221     ShellItem_Release,
222     ShellItem_BindToHandler,
223     ShellItem_GetParent,
224     ShellItem_GetDisplayName,
225     ShellItem_GetAttributes,
226     ShellItem_Compare
227 };
228
229
230 static HRESULT ShellItem_GetClassID(ShellItem* This, CLSID *pClassID)
231 {
232     TRACE("(%p,%p)\n", This, pClassID);
233
234     *pClassID = CLSID_ShellItem;
235     return S_OK;
236 }
237
238
239 static HRESULT WINAPI ShellItem_IPersistIDList_QueryInterface(IPersistIDList *iface,
240     REFIID riid, void **ppv)
241 {
242     ShellItem *This = impl_from_IPersistIDList(iface);
243     return ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
244 }
245
246 static ULONG WINAPI ShellItem_IPersistIDList_AddRef(IPersistIDList *iface)
247 {
248     ShellItem *This = impl_from_IPersistIDList(iface);
249     return ShellItem_AddRef((IShellItem*)This);
250 }
251
252 static ULONG WINAPI ShellItem_IPersistIDList_Release(IPersistIDList *iface)
253 {
254     ShellItem *This = impl_from_IPersistIDList(iface);
255     return ShellItem_Release((IShellItem*)This);
256 }
257
258 static HRESULT WINAPI ShellItem_IPersistIDList_GetClassID(IPersistIDList* iface,
259     CLSID *pClassID)
260 {
261     ShellItem *This = impl_from_IPersistIDList(iface);
262
263     return ShellItem_GetClassID(This, pClassID);
264 }
265
266 static HRESULT WINAPI ShellItem_IPersistIDList_SetIDList(IPersistIDList* iface,
267     LPCITEMIDLIST pidl)
268 {
269     ShellItem *This = impl_from_IPersistIDList(iface);
270     LPITEMIDLIST new_pidl;
271
272     TRACE("(%p,%p)\n", This, pidl);
273
274     new_pidl = ILClone(pidl);
275
276     if (new_pidl)
277     {
278         ILFree(This->pidl);
279         This->pidl = new_pidl;
280         return S_OK;
281     }
282     else
283         return E_OUTOFMEMORY;
284 }
285
286 static HRESULT WINAPI ShellItem_IPersistIDList_GetIDList(IPersistIDList* iface,
287     LPITEMIDLIST *ppidl)
288 {
289     ShellItem *This = impl_from_IPersistIDList(iface);
290
291     TRACE("(%p,%p)\n", This, ppidl);
292
293     *ppidl = ILClone(This->pidl);
294     if (*ppidl)
295         return S_OK;
296     else
297         return E_OUTOFMEMORY;
298 }
299
300 static const IPersistIDListVtbl ShellItem_IPersistIDList_Vtbl = {
301     ShellItem_IPersistIDList_QueryInterface,
302     ShellItem_IPersistIDList_AddRef,
303     ShellItem_IPersistIDList_Release,
304     ShellItem_IPersistIDList_GetClassID,
305     ShellItem_IPersistIDList_SetIDList,
306     ShellItem_IPersistIDList_GetIDList
307 };
308
309
310 HRESULT WINAPI IShellItem_Constructor(IUnknown *pUnkOuter, REFIID riid, void **ppv)
311 {
312     ShellItem *This;
313     HRESULT ret;
314
315     TRACE("(%p,%s)\n",pUnkOuter, debugstr_guid(riid));
316
317     *ppv = NULL;
318
319     if (pUnkOuter) return CLASS_E_NOAGGREGATION;
320
321     This = HeapAlloc(GetProcessHeap(), 0, sizeof(ShellItem));
322     This->lpIShellItemVtbl = &ShellItem_Vtbl;
323     This->ref = 1;
324     This->pidl = NULL;
325     This->lpIPersistIDListVtbl = &ShellItem_IPersistIDList_Vtbl;
326
327     ret = ShellItem_QueryInterface((IShellItem*)This, riid, ppv);
328     ShellItem_Release((IShellItem*)This);
329
330     return ret;
331 }
332
333 HRESULT WINAPI SHCreateShellItem(LPCITEMIDLIST pidlParent,
334     IShellFolder *psfParent, LPCITEMIDLIST pidl, IShellItem **ppsi)
335 {
336     ShellItem *This;
337     LPITEMIDLIST new_pidl;
338     HRESULT ret;
339
340     TRACE("(%p,%p,%p,%p)\n", pidlParent, psfParent, pidl, ppsi);
341
342     if (!pidl)
343     {
344         return E_INVALIDARG;
345     }
346     else if (pidlParent || psfParent)
347     {
348         LPITEMIDLIST temp_parent=NULL;
349         if (!pidlParent)
350         {
351             IPersistFolder2* ppf2Parent;
352
353             if (!SUCCEEDED(IPersistFolder2_QueryInterface(psfParent, &IID_IPersistFolder2, (void**)&ppf2Parent)))
354             {
355                 FIXME("couldn't get IPersistFolder2 interface of parent\n");
356                 return E_NOINTERFACE;
357             }
358
359             if (!SUCCEEDED(IPersistFolder2_GetCurFolder(ppf2Parent, &temp_parent)))
360             {
361                 FIXME("couldn't get parent PIDL\n");
362                 IPersistFolder2_Release(ppf2Parent);
363                 return E_NOINTERFACE;
364             }
365
366             pidlParent = temp_parent;
367             IPersistFolder2_Release(ppf2Parent);
368         }
369
370         new_pidl = ILCombine(pidlParent, pidl);
371         ILFree(temp_parent);
372
373         if (!new_pidl)
374             return E_OUTOFMEMORY;
375     }
376     else
377     {
378         new_pidl = ILClone(pidl);
379         if (!new_pidl)
380             return E_OUTOFMEMORY;
381     }
382
383     ret = IShellItem_Constructor(NULL, &IID_IShellItem, (void**)&This);
384     if (This)
385     {
386         *ppsi = (IShellItem*)This;
387         This->pidl = new_pidl;
388     }
389     else
390     {
391         *ppsi = NULL;
392         ILFree(new_pidl);
393     }
394     return ret;
395 }