- more checks for valid pidl formats
[wine] / dlls / shell32 / enumidlist.c
1 /*
2  *      IEnumIDList
3  *
4  *      Copyright 1998  Juergen Schmied <juergen.schmied@metronet.de>
5  */
6
7 #include <stdlib.h>
8 #include <string.h>
9 #include "debug.h"
10 #include "wine/obj_base.h"
11 #include "wine/obj_enumidlist.h"
12 #include "winerror.h"
13
14 #include "pidl.h"
15 #include "shlguid.h"
16 #include "shell32_main.h"
17
18 DEFAULT_DEBUG_CHANNEL(shell)
19
20 typedef struct tagENUMLIST
21 {
22         struct tagENUMLIST      *pNext;
23         LPITEMIDLIST            pidl;
24
25 } ENUMLIST, *LPENUMLIST;
26
27 typedef struct
28 {
29         ICOM_VTABLE(IEnumIDList)*       lpvtbl;
30         DWORD                           ref;
31         LPENUMLIST                      mpFirst;
32         LPENUMLIST                      mpLast;
33         LPENUMLIST                      mpCurrent;
34
35 } IEnumIDListImpl;
36
37 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
38
39 /**************************************************************************
40  *  IEnumIDList_fnConstructor
41  */
42
43 IEnumIDList * IEnumIDList_Constructor(
44         LPCSTR lpszPath,
45         DWORD dwFlags)
46 {       IEnumIDListImpl*        lpeidl;
47
48         lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(),0,sizeof(IEnumIDListImpl));
49         if (! lpeidl)
50           return NULL;
51
52         lpeidl->ref = 1;
53         lpeidl->lpvtbl = &eidlvt;
54         lpeidl->mpFirst=NULL;
55         lpeidl->mpLast=NULL;
56         lpeidl->mpCurrent=NULL;
57
58         TRACE(shell,"(%p)->(%s flags=0x%08lx)\n",lpeidl,debugstr_a(lpszPath),dwFlags);
59
60         if(!IEnumIDList_CreateEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags))
61         { if (lpeidl)
62           { HeapFree(GetProcessHeap(),0,lpeidl);
63           }
64           return NULL;
65         }
66
67         TRACE(shell,"-- (%p)->()\n",lpeidl);
68         shell32_ObjCount++;
69         return (IEnumIDList*)lpeidl;
70 }
71
72 /**************************************************************************
73  *  EnumIDList_QueryInterface
74  */
75 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
76         IEnumIDList * iface,
77         REFIID riid,
78         LPVOID *ppvObj)
79 {
80         ICOM_THIS(IEnumIDListImpl,iface);
81
82         char    xriid[50];
83         WINE_StringFromCLSID((LPCLSID)riid,xriid);
84         TRACE(shell,"(%p)->(\n\tIID:\t%s,%p)\n",This,xriid,ppvObj);
85
86         *ppvObj = NULL;
87
88         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
89         { *ppvObj = This; 
90         }
91         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
92         {    *ppvObj = (IEnumIDList*)This;
93         }
94
95         if(*ppvObj)
96         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
97           TRACE(shell,"-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
98           return S_OK;
99         }
100         
101         TRACE(shell,"-- Interface: E_NOINTERFACE\n");
102         return E_NOINTERFACE;
103 }
104
105 /******************************************************************************
106  * IEnumIDList_fnAddRef
107  */
108 static ULONG WINAPI IEnumIDList_fnAddRef(
109         IEnumIDList * iface)
110 {
111         ICOM_THIS(IEnumIDListImpl,iface);
112
113         TRACE(shell,"(%p)->(%lu)\n",This,This->ref);
114
115         shell32_ObjCount++;
116         return ++(This->ref);
117 }
118 /******************************************************************************
119  * IEnumIDList_fnRelease
120  */
121 static ULONG WINAPI IEnumIDList_fnRelease(
122         IEnumIDList * iface)
123 {
124         ICOM_THIS(IEnumIDListImpl,iface);
125
126         TRACE(shell,"(%p)->(%lu)\n",This,This->ref);
127
128         shell32_ObjCount--;
129
130         if (!--(This->ref)) 
131         { TRACE(shell," destroying IEnumIDList(%p)\n",This);
132           IEnumIDList_DeleteList((IEnumIDList*)This);
133           HeapFree(GetProcessHeap(),0,This);
134           return 0;
135         }
136         return This->ref;
137 }
138    
139 /**************************************************************************
140  *  IEnumIDList_fnNext
141  */
142
143 static HRESULT WINAPI IEnumIDList_fnNext(
144         IEnumIDList * iface,
145         ULONG celt,
146         LPITEMIDLIST * rgelt,
147         ULONG *pceltFetched) 
148 {
149         ICOM_THIS(IEnumIDListImpl,iface);
150
151         ULONG    i;
152         HRESULT  hr = S_OK;
153         LPITEMIDLIST  temp;
154
155         TRACE(shell,"(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
156
157 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
158  * subsystems actually use it (and so may a third party browser)
159  */
160         if(pceltFetched)
161           *pceltFetched = 0;
162
163         *rgelt=0;
164         
165         if(celt > 1 && !pceltFetched)
166         { return E_INVALIDARG;
167         }
168
169         for(i = 0; i < celt; i++)
170         { if(!(This->mpCurrent))
171           { hr =  S_FALSE;
172             break;
173           }
174           temp = ILClone(This->mpCurrent->pidl);
175           rgelt[i] = temp;
176           This->mpCurrent = This->mpCurrent->pNext;
177         }
178         if(pceltFetched)
179         {  *pceltFetched = i;
180         }
181
182         return hr;
183 }
184
185 /**************************************************************************
186 *  IEnumIDList_fnSkip
187 */
188 static HRESULT WINAPI IEnumIDList_fnSkip(
189         IEnumIDList * iface,ULONG celt)
190 {
191         ICOM_THIS(IEnumIDListImpl,iface);
192
193         DWORD    dwIndex;
194         HRESULT  hr = S_OK;
195
196         TRACE(shell,"(%p)->(%lu)\n",This,celt);
197
198         for(dwIndex = 0; dwIndex < celt; dwIndex++)
199         { if(!This->mpCurrent)
200           { hr = S_FALSE;
201             break;
202           }
203           This->mpCurrent = This->mpCurrent->pNext;
204         }
205         return hr;
206 }
207 /**************************************************************************
208 *  IEnumIDList_fnReset
209 */
210 static HRESULT WINAPI IEnumIDList_fnReset(
211         IEnumIDList * iface)
212 {
213         ICOM_THIS(IEnumIDListImpl,iface);
214
215         TRACE(shell,"(%p)\n",This);
216         This->mpCurrent = This->mpFirst;
217         return S_OK;
218 }
219 /**************************************************************************
220 *  IEnumIDList_fnClone
221 */
222 static HRESULT WINAPI IEnumIDList_fnClone(
223         IEnumIDList * iface,LPENUMIDLIST * ppenum)
224 {
225         ICOM_THIS(IEnumIDListImpl,iface);
226
227         TRACE(shell,"(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
228         return E_NOTIMPL;
229 }
230 /**************************************************************************
231  *  EnumIDList_CreateEnumList()
232  *  fixme: devices not handled
233  *  fixme: add wildcards to path
234  */
235 static BOOL WINAPI IEnumIDList_fnCreateEnumList(
236         IEnumIDList * iface,
237         LPCSTR lpszPath,
238         DWORD dwFlags)
239 {
240         ICOM_THIS(IEnumIDListImpl,iface);
241
242         LPITEMIDLIST    pidl=NULL;
243         LPPIDLDATA      pData=NULL;
244         WIN32_FIND_DATAA stffile;       
245         HANDLE hFile;
246         DWORD dwDrivemap;
247         CHAR  szDriveName[4];
248         CHAR  szPath[MAX_PATH];
249
250         TRACE(shell,"(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
251
252         if (lpszPath && lpszPath[0]!='\0')
253         { strcpy(szPath, lpszPath);
254           PathAddBackslashA(szPath);
255           strcat(szPath,"*.*");
256         }
257
258         /*enumerate the folders*/
259         if(dwFlags & SHCONTF_FOLDERS)
260         { /* special case - we can't enumerate the Desktop level Objects (MyComputer,Nethood...
261           so we need to fake an enumeration of those.*/
262           if(!lpszPath)
263           { TRACE (shell,"-- (%p)-> enumerate SHCONTF_FOLDERS (special) items\n",This);
264             /*create the pidl for This item */
265             pidl = _ILCreateMyComputer();
266             if(pidl)
267             { if(!IEnumIDList_AddToEnumList((IEnumIDList*)This, pidl))
268                 return FALSE;
269             }
270           }   
271           else if (lpszPath[0]=='\0') /* enumerate the drives*/
272           { TRACE (shell,"-- (%p)-> enumerate SHCONTF_FOLDERS (drives)\n",This);
273             dwDrivemap = GetLogicalDrives();
274             strcpy (szDriveName,"A:\\");
275             while (szDriveName[0]<='Z')
276             { if(dwDrivemap & 0x00000001L)
277               { pidl = _ILCreateDrive(szDriveName);
278                 if(pidl)
279                 { if(!IEnumIDList_AddToEnumList((IEnumIDList*)This, pidl))
280                   return FALSE;
281                 }
282               }
283               szDriveName[0]++;
284               dwDrivemap = dwDrivemap >> 1;
285             }   
286           }
287           else
288           { TRACE (shell,"-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
289             hFile = FindFirstFileA(szPath,&stffile);
290             if ( hFile != INVALID_HANDLE_VALUE )
291             { do
292               { if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
293                 { pidl = _ILCreateFolder( stffile.cAlternateFileName, stffile.cFileName);
294                   if(pidl)
295                   { pData = _ILGetDataPointer(pidl);
296                     FileTimeToDosDateTime(&stffile.ftLastWriteTime,&pData->u.folder.uFileDate,&pData->u.folder.uFileTime);
297                     pData->u.folder.dwFileSize = stffile.nFileSizeLow;
298                     pData->u.folder.uFileAttribs=stffile.dwFileAttributes;
299                     if(!IEnumIDList_AddToEnumList((IEnumIDList*)This, pidl))
300                     {  return FALSE;
301                     }
302                   }
303                   else
304                   { return FALSE;
305                   }   
306                 }
307               } while( FindNextFileA(hFile,&stffile));
308                         FindClose (hFile);
309             }
310           }   
311         }   
312         /*enumerate the non-folder items (values) */
313         if(dwFlags & SHCONTF_NONFOLDERS)
314         { if(lpszPath)
315           { TRACE (shell,"-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
316             hFile = FindFirstFileA(szPath,&stffile);
317             if ( hFile != INVALID_HANDLE_VALUE )
318             { do
319               { if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
320                 { pidl = _ILCreateValue( stffile.cAlternateFileName, stffile.cFileName);
321                   if(pidl)
322                   { pData = _ILGetDataPointer(pidl);
323                     FileTimeToDosDateTime(&stffile.ftLastWriteTime,&pData->u.file.uFileDate,&pData->u.file.uFileTime);
324                     pData->u.file.dwFileSize = stffile.nFileSizeLow;
325                     pData->u.file.uFileAttribs=stffile.dwFileAttributes;
326                     if(!IEnumIDList_AddToEnumList((IEnumIDList*)This, pidl))
327                     { return FALSE;
328                     }
329                   }
330                   else
331                   { return FALSE;
332                   }   
333                 }
334               } while( FindNextFileA(hFile,&stffile));
335               FindClose (hFile);
336             } 
337           }
338         } 
339         return TRUE;
340 }
341
342 /**************************************************************************
343  *  EnumIDList_AddToEnumList()
344  */
345 static BOOL WINAPI IEnumIDList_fnAddToEnumList(
346         IEnumIDList * iface,
347         LPITEMIDLIST pidl)
348 {
349         ICOM_THIS(IEnumIDListImpl,iface);
350
351  LPENUMLIST  pNew;
352
353   TRACE(shell,"(%p)->(pidl=%p)\n",This,pidl);
354   pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
355   if(pNew)
356   { /*set the next pointer */
357     pNew->pNext = NULL;
358     pNew->pidl = pidl;
359
360     /*is This the first item in the list? */
361     if(!This->mpFirst)
362     { This->mpFirst = pNew;
363       This->mpCurrent = pNew;
364     }
365    
366     if(This->mpLast)
367     { /*add the new item to the end of the list */
368       This->mpLast->pNext = pNew;
369     }
370    
371     /*update the last item pointer */
372     This->mpLast = pNew;
373     TRACE(shell,"-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
374     return TRUE;
375   }
376   return FALSE;
377 }
378 /**************************************************************************
379 *   EnumIDList_DeleteList()
380 */
381 static BOOL WINAPI IEnumIDList_fnDeleteList(
382         IEnumIDList * iface)
383 {
384         ICOM_THIS(IEnumIDListImpl,iface);
385
386         LPENUMLIST  pDelete;
387
388         TRACE(shell,"(%p)->()\n",This);
389         
390         while(This->mpFirst)
391         { pDelete = This->mpFirst;
392           This->mpFirst = pDelete->pNext;
393           SHFree(pDelete->pidl);
394           SHFree(pDelete);
395         }
396         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
397         return TRUE;
398 }
399
400 /**************************************************************************
401  *  IEnumIDList_fnVTable
402  */
403 static ICOM_VTABLE (IEnumIDList) eidlvt = 
404 {       IEnumIDList_fnQueryInterface,
405         IEnumIDList_fnAddRef,
406         IEnumIDList_fnRelease,
407         IEnumIDList_fnNext,
408         IEnumIDList_fnSkip,
409         IEnumIDList_fnReset,
410         IEnumIDList_fnClone,
411         IEnumIDList_fnCreateEnumList,
412         IEnumIDList_fnAddToEnumList,
413         IEnumIDList_fnDeleteList
414 };