shell32: Avoid an unsafe iface to object cast in AddToEnumList().
[wine] / dlls / shell32 / enumidlist.c
1 /*
2  *      IEnumIDList
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
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 <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #define COBJMACROS
26
27 #include "wine/debug.h"
28 #include "wine/unicode.h"
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winreg.h"
32 #include "shlwapi.h"
33
34 #include "pidl.h"
35 #include "shell32_main.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(shell);
38
39 /**************************************************************************
40  *  AddToEnumList()
41  */
42 BOOL AddToEnumList(IEnumIDListImpl *This, LPITEMIDLIST pidl)
43 {
44         struct enumlist *pNew;
45
46         TRACE("(%p)->(pidl=%p)\n",This,pidl);
47
48     if (!This || !pidl)
49         return FALSE;
50
51         pNew = SHAlloc(sizeof(*pNew));
52         if(pNew)
53         {
54           /*set the next pointer */
55           pNew->pNext = NULL;
56           pNew->pidl = pidl;
57
58           /*is This the first item in the list? */
59           if(!This->mpFirst)
60           {
61             This->mpFirst = pNew;
62             This->mpCurrent = pNew;
63           }
64
65           if(This->mpLast)
66           {
67             /*add the new item to the end of the list */
68             This->mpLast->pNext = pNew;
69           }
70
71           /*update the last item pointer */
72           This->mpLast = pNew;
73           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
74           return TRUE;
75         }
76         return FALSE;
77 }
78
79 /**************************************************************************
80  *  CreateFolderEnumList()
81  */
82 BOOL CreateFolderEnumList(IEnumIDListImpl *list, LPCWSTR lpszPath, DWORD dwFlags)
83 {
84     LPITEMIDLIST pidl=NULL;
85     WIN32_FIND_DATAW stffile;
86     HANDLE hFile;
87     WCHAR  szPath[MAX_PATH];
88     BOOL succeeded = TRUE;
89     static const WCHAR stars[] = { '*','.','*',0 };
90     static const WCHAR dot[] = { '.',0 };
91     static const WCHAR dotdot[] = { '.','.',0 };
92
93     TRACE("(%p)->(path=%s flags=0x%08x)\n", list, debugstr_w(lpszPath), dwFlags);
94
95     if(!lpszPath || !lpszPath[0]) return FALSE;
96
97     strcpyW(szPath, lpszPath);
98     PathAddBackslashW(szPath);
99     strcatW(szPath,stars);
100
101     hFile = FindFirstFileW(szPath,&stffile);
102     if ( hFile != INVALID_HANDLE_VALUE )
103     {
104         BOOL findFinished = FALSE;
105
106         do
107         {
108             if ( !(stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) 
109              || (dwFlags & SHCONTF_INCLUDEHIDDEN) )
110             {
111                 if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
112                  dwFlags & SHCONTF_FOLDERS &&
113                  strcmpW(stffile.cFileName, dot) && strcmpW(stffile.cFileName, dotdot))
114                 {
115                     pidl = _ILCreateFromFindDataW(&stffile);
116                     succeeded = succeeded && AddToEnumList(list, pidl);
117                 }
118                 else if (!(stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
119                  && dwFlags & SHCONTF_NONFOLDERS)
120                 {
121                     pidl = _ILCreateFromFindDataW(&stffile);
122                     succeeded = succeeded && AddToEnumList(list, pidl);
123                 }
124             }
125             if (succeeded)
126             {
127                 if (!FindNextFileW(hFile, &stffile))
128                 {
129                     if (GetLastError() == ERROR_NO_MORE_FILES)
130                         findFinished = TRUE;
131                     else
132                         succeeded = FALSE;
133                 }
134             }
135         } while (succeeded && !findFinished);
136         FindClose(hFile);
137     }
138     return succeeded;
139 }
140
141 static BOOL DeleteList(IEnumIDListImpl *This)
142 {
143         struct enumlist *pDelete;
144
145         TRACE("(%p)->()\n",This);
146
147         while(This->mpFirst)
148         { pDelete = This->mpFirst;
149           This->mpFirst = pDelete->pNext;
150           SHFree(pDelete->pidl);
151           SHFree(pDelete);
152         }
153         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
154         return TRUE;
155 }
156
157 static inline IEnumIDListImpl *impl_from_IEnumIDList(IEnumIDList *iface)
158 {
159     return CONTAINING_RECORD(iface, IEnumIDListImpl, IEnumIDList_iface);
160 }
161
162 /**************************************************************************
163  *  IEnumIDList::QueryInterface
164  */
165 static HRESULT WINAPI IEnumIDList_fnQueryInterface(IEnumIDList *iface, REFIID riid, void **ppvObj)
166 {
167         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
168
169         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
170
171         *ppvObj = NULL;
172
173         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
174         { *ppvObj = This;
175         }
176         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
177         {    *ppvObj = This;
178         }
179
180         if(*ppvObj)
181         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
182           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
183           return S_OK;
184         }
185
186         TRACE("-- Interface: E_NOINTERFACE\n");
187         return E_NOINTERFACE;
188 }
189
190 /******************************************************************************
191  * IEnumIDList::AddRef
192  */
193 static ULONG WINAPI IEnumIDList_fnAddRef(IEnumIDList *iface)
194 {
195         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
196         ULONG refCount = InterlockedIncrement(&This->ref);
197
198         TRACE("(%p)->(%u)\n", This, refCount - 1);
199
200         return refCount;
201 }
202 /******************************************************************************
203  * IEnumIDList::Release
204  */
205 static ULONG WINAPI IEnumIDList_fnRelease(IEnumIDList *iface)
206 {
207         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
208         ULONG refCount = InterlockedDecrement(&This->ref);
209
210         TRACE("(%p)->(%u)\n", This, refCount + 1);
211
212         if (!refCount) {
213           TRACE(" destroying IEnumIDList(%p)\n",This);
214           DeleteList(This);
215           HeapFree(GetProcessHeap(),0,This);
216         }
217         return refCount;
218 }
219
220 /**************************************************************************
221  *  IEnumIDList::Next
222  */
223
224 static HRESULT WINAPI IEnumIDList_fnNext(IEnumIDList *iface, ULONG celt, LPITEMIDLIST *rgelt,
225         ULONG *pceltFetched)
226 {
227         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
228
229         ULONG    i;
230         HRESULT  hr = S_OK;
231         LPITEMIDLIST  temp;
232
233         TRACE("(%p)->(%d,%p, %p)\n",This,celt,rgelt,pceltFetched);
234
235 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
236  * subsystems actually use it (and so may a third party browser)
237  */
238         if(pceltFetched)
239           *pceltFetched = 0;
240
241         *rgelt=0;
242
243         if(celt > 1 && !pceltFetched)
244         { return E_INVALIDARG;
245         }
246
247         if(celt > 0 && !This->mpCurrent)
248         { return S_FALSE;
249         }
250
251         for(i = 0; i < celt; i++)
252         { if(!(This->mpCurrent))
253             break;
254
255           temp = ILClone(This->mpCurrent->pidl);
256           rgelt[i] = temp;
257           This->mpCurrent = This->mpCurrent->pNext;
258         }
259         if(pceltFetched)
260         {  *pceltFetched = i;
261         }
262
263         return hr;
264 }
265
266 /**************************************************************************
267 *  IEnumIDList::Skip
268 */
269 static HRESULT WINAPI IEnumIDList_fnSkip(IEnumIDList *iface, ULONG celt)
270 {
271         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
272
273         DWORD    dwIndex;
274         HRESULT  hr = S_OK;
275
276         TRACE("(%p)->(%u)\n",This,celt);
277
278         for(dwIndex = 0; dwIndex < celt; dwIndex++)
279         { if(!This->mpCurrent)
280           { hr = S_FALSE;
281             break;
282           }
283           This->mpCurrent = This->mpCurrent->pNext;
284         }
285         return hr;
286 }
287 /**************************************************************************
288 *  IEnumIDList::Reset
289 */
290 static HRESULT WINAPI IEnumIDList_fnReset(IEnumIDList *iface)
291 {
292         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
293
294         TRACE("(%p)\n",This);
295         This->mpCurrent = This->mpFirst;
296         return S_OK;
297 }
298 /**************************************************************************
299 *  IEnumIDList::Clone
300 */
301 static HRESULT WINAPI IEnumIDList_fnClone(IEnumIDList *iface, IEnumIDList **ppenum)
302 {
303         IEnumIDListImpl *This = impl_from_IEnumIDList(iface);
304
305         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
306         return E_NOTIMPL;
307 }
308
309 static const IEnumIDListVtbl eidlvt =
310 {
311         IEnumIDList_fnQueryInterface,
312         IEnumIDList_fnAddRef,
313         IEnumIDList_fnRelease,
314         IEnumIDList_fnNext,
315         IEnumIDList_fnSkip,
316         IEnumIDList_fnReset,
317         IEnumIDList_fnClone,
318 };
319
320 IEnumIDListImpl *IEnumIDList_Constructor(void)
321 {
322     IEnumIDListImpl *lpeidl = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*lpeidl));
323
324     if (lpeidl)
325     {
326         lpeidl->ref = 1;
327         lpeidl->IEnumIDList_iface.lpVtbl = &eidlvt;
328     }
329
330     TRACE("-- (%p)->()\n",lpeidl);
331
332     return lpeidl;
333 }