Fixes to allow IE to do local file loading and some limited browsing.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <stdlib.h>
22 #include <string.h>
23 #include "wine/debug.h"
24 #include "winreg.h"
25 #include "undocshell.h"
26 #include "shlwapi.h"
27 #include "winerror.h"
28 #include "wine/obj_base.h"
29 #include "wine/obj_enumidlist.h"
30
31 #include "pidl.h"
32 #include "shlguid.h"
33 #include "shell32_main.h"
34
35 WINE_DEFAULT_DEBUG_CHANNEL(shell);
36
37 typedef struct tagENUMLIST
38 {
39         struct tagENUMLIST      *pNext;
40         LPITEMIDLIST            pidl;
41
42 } ENUMLIST, *LPENUMLIST;
43
44 typedef struct
45 {
46         ICOM_VFIELD(IEnumIDList);
47         DWORD                           ref;
48         LPENUMLIST                      mpFirst;
49         LPENUMLIST                      mpLast;
50         LPENUMLIST                      mpCurrent;
51
52 } IEnumIDListImpl;
53
54 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
55
56 /**************************************************************************
57  *  AddToEnumList()
58  */
59 static BOOL AddToEnumList(
60         IEnumIDList * iface,
61         LPITEMIDLIST pidl)
62 {
63         ICOM_THIS(IEnumIDListImpl,iface);
64
65         LPENUMLIST  pNew;
66
67         TRACE("(%p)->(pidl=%p)\n",This,pidl);
68         pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
69         if(pNew)
70         {
71           /*set the next pointer */
72           pNew->pNext = NULL;
73           pNew->pidl = pidl;
74
75           /*is This the first item in the list? */
76           if(!This->mpFirst)
77           {
78             This->mpFirst = pNew;
79             This->mpCurrent = pNew;
80           }
81
82           if(This->mpLast)
83           {
84             /*add the new item to the end of the list */
85             This->mpLast->pNext = pNew;
86           }
87
88           /*update the last item pointer */
89           This->mpLast = pNew;
90           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
91           return TRUE;
92         }
93         return FALSE;
94 }
95
96 /**************************************************************************
97  *  CreateFolderEnumList()
98  */
99 static BOOL CreateFolderEnumList(
100         IEnumIDList * iface,
101         LPCSTR lpszPath,
102         DWORD dwFlags)
103 {
104         ICOM_THIS(IEnumIDListImpl,iface);
105
106         LPITEMIDLIST    pidl=NULL;
107         WIN32_FIND_DATAA stffile;
108         HANDLE hFile;
109         CHAR  szPath[MAX_PATH];
110
111         TRACE("(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
112
113         if(!lpszPath || !lpszPath[0]) return FALSE;
114
115         strcpy(szPath, lpszPath);
116         PathAddBackslashA(szPath);
117         strcat(szPath,"*.*");
118
119         /*enumerate the folders*/
120         if(dwFlags & SHCONTF_FOLDERS)
121         {
122           TRACE("-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
123           hFile = FindFirstFileA(szPath,&stffile);
124           if ( hFile != INVALID_HANDLE_VALUE )
125           {
126             do
127             {
128               if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
129               {
130                 pidl = _ILCreateFolder (&stffile);
131                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
132                 {
133                   continue;
134                 }
135                 return FALSE;
136               }
137             } while( FindNextFileA(hFile,&stffile));
138             FindClose (hFile);
139           }
140         }
141
142         /*enumerate the non-folder items (values) */
143         if(dwFlags & SHCONTF_NONFOLDERS)
144         {
145           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
146           hFile = FindFirstFileA(szPath,&stffile);
147           if ( hFile != INVALID_HANDLE_VALUE )
148           {
149             do
150             {
151               if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
152               {
153                 pidl = _ILCreateValue(&stffile);
154                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
155                 {
156                   continue;
157                 }
158                 return FALSE;
159               }
160             } while( FindNextFileA(hFile,&stffile));
161             FindClose (hFile);
162           }
163         }
164         return TRUE;
165 }
166
167 /**************************************************************************
168  *  CreateDesktopEnumList()
169  */
170 static BOOL CreateDesktopEnumList(
171         IEnumIDList * iface,
172         DWORD dwFlags)
173 {
174         ICOM_THIS(IEnumIDListImpl,iface);
175
176         LPITEMIDLIST    pidl=NULL;
177         HKEY hkey;
178         char    szPath[MAX_PATH];
179
180         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
181
182         /*enumerate the root folders */
183         if(dwFlags & SHCONTF_FOLDERS)
184         {
185           /*create the pidl for This item */
186           pidl = _ILCreateMyComputer();
187           if(pidl)
188           {
189             if(!AddToEnumList((IEnumIDList*)This, pidl))
190               return FALSE;
191           }
192
193           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", 0, KEY_READ, &hkey))
194           {
195             char iid[50];
196             int i=0;
197
198             while (1)
199             {
200               DWORD size = sizeof (iid);
201
202               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
203                 break;
204
205               pidl = _ILCreateSpecial(iid);
206
207               if(pidl)
208                 AddToEnumList((IEnumIDList*)This, pidl);
209
210               i++;
211             }
212             RegCloseKey(hkey);
213           }
214         }
215
216         /*enumerate the elements in %windir%\desktop */
217         SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
218         CreateFolderEnumList( (IEnumIDList*)This, szPath, dwFlags);
219
220         return TRUE;
221 }
222
223 /**************************************************************************
224  *  CreateMyCompEnumList()
225  */
226 static BOOL CreateMyCompEnumList(
227         IEnumIDList * iface,
228         DWORD dwFlags)
229 {
230         ICOM_THIS(IEnumIDListImpl,iface);
231
232         LPITEMIDLIST    pidl=NULL;
233         DWORD           dwDrivemap;
234         CHAR            szDriveName[4];
235         HKEY            hkey;
236
237         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
238
239         /*enumerate the folders*/
240         if(dwFlags & SHCONTF_FOLDERS)
241         {
242           dwDrivemap = GetLogicalDrives();
243           strcpy (szDriveName,"A:\\");
244           while (szDriveName[0]<='Z')
245           {
246             if(dwDrivemap & 0x00000001L)
247             {
248               pidl = _ILCreateDrive(szDriveName);
249               if(pidl)
250               {
251                 if(!AddToEnumList((IEnumIDList*)This, pidl))
252                   return FALSE;
253               }
254             }
255             szDriveName[0]++;
256             dwDrivemap = dwDrivemap >> 1;
257           }
258
259           TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",This);
260           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", 0, KEY_READ, &hkey))
261           {
262             char iid[50];
263             int i=0;
264
265             while (1)
266             {
267               DWORD size = sizeof (iid);
268
269               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
270                 break;
271
272               pidl = _ILCreateSpecial(iid);
273
274               if(pidl)
275                 AddToEnumList((IEnumIDList*)This, pidl);
276
277               i++;
278             }
279             RegCloseKey(hkey);
280           }
281         }
282         return TRUE;
283 }
284
285 /**************************************************************************
286 *   DeleteList()
287 */
288 static BOOL DeleteList(
289         IEnumIDList * iface)
290 {
291         ICOM_THIS(IEnumIDListImpl,iface);
292
293         LPENUMLIST  pDelete;
294
295         TRACE("(%p)->()\n",This);
296
297         while(This->mpFirst)
298         { pDelete = This->mpFirst;
299           This->mpFirst = pDelete->pNext;
300           SHFree(pDelete->pidl);
301           SHFree(pDelete);
302         }
303         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
304         return TRUE;
305 }
306
307 /**************************************************************************
308  *  IEnumIDList_Folder_Constructor
309  *
310  */
311
312 IEnumIDList * IEnumIDList_Constructor(
313         LPCSTR lpszPath,
314         DWORD dwFlags,
315         DWORD dwKind)
316 {
317         IEnumIDListImpl*        lpeidl;
318         BOOL                    ret = FALSE;
319
320         TRACE("()->(%s flags=0x%08lx kind=0x%08lx)\n",debugstr_a(lpszPath),dwFlags, dwKind);
321
322         lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
323
324         if (lpeidl)
325         {
326           lpeidl->ref = 1;
327           ICOM_VTBL(lpeidl) = &eidlvt;
328
329           switch (dwKind)
330           {
331             case EIDL_DESK:
332               ret = CreateDesktopEnumList((IEnumIDList*)lpeidl, dwFlags);
333               break;
334
335             case EIDL_MYCOMP:
336               ret = CreateMyCompEnumList((IEnumIDList*)lpeidl, dwFlags);
337               break;
338
339             case EIDL_FILE:
340               ret = CreateFolderEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags);
341               break;
342           }
343
344             if(!ret) {
345                 HeapFree(GetProcessHeap(),0,lpeidl);
346                 lpeidl = NULL;
347             }
348         }
349
350         TRACE("-- (%p)->()\n",lpeidl);
351
352         return (IEnumIDList*)lpeidl;
353 }
354
355 /**************************************************************************
356  *  EnumIDList_QueryInterface
357  */
358 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
359         IEnumIDList * iface,
360         REFIID riid,
361         LPVOID *ppvObj)
362 {
363         ICOM_THIS(IEnumIDListImpl,iface);
364
365         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
366
367         *ppvObj = NULL;
368
369         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
370         { *ppvObj = This;
371         }
372         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
373         {    *ppvObj = (IEnumIDList*)This;
374         }
375
376         if(*ppvObj)
377         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
378           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
379           return S_OK;
380         }
381
382         TRACE("-- Interface: E_NOINTERFACE\n");
383         return E_NOINTERFACE;
384 }
385
386 /******************************************************************************
387  * IEnumIDList_fnAddRef
388  */
389 static ULONG WINAPI IEnumIDList_fnAddRef(
390         IEnumIDList * iface)
391 {
392         ICOM_THIS(IEnumIDListImpl,iface);
393         TRACE("(%p)->(%lu)\n",This,This->ref);
394         return ++(This->ref);
395 }
396 /******************************************************************************
397  * IEnumIDList_fnRelease
398  */
399 static ULONG WINAPI IEnumIDList_fnRelease(
400         IEnumIDList * iface)
401 {
402         ICOM_THIS(IEnumIDListImpl,iface);
403
404         TRACE("(%p)->(%lu)\n",This,This->ref);
405
406         if (!--(This->ref)) {
407           TRACE(" destroying IEnumIDList(%p)\n",This);
408           DeleteList((IEnumIDList*)This);
409           HeapFree(GetProcessHeap(),0,This);
410           return 0;
411         }
412         return This->ref;
413 }
414
415 /**************************************************************************
416  *  IEnumIDList_fnNext
417  */
418
419 static HRESULT WINAPI IEnumIDList_fnNext(
420         IEnumIDList * iface,
421         ULONG celt,
422         LPITEMIDLIST * rgelt,
423         ULONG *pceltFetched)
424 {
425         ICOM_THIS(IEnumIDListImpl,iface);
426
427         ULONG    i;
428         HRESULT  hr = S_OK;
429         LPITEMIDLIST  temp;
430
431         TRACE("(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
432
433 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
434  * subsystems actually use it (and so may a third party browser)
435  */
436         if(pceltFetched)
437           *pceltFetched = 0;
438
439         *rgelt=0;
440
441         if(celt > 1 && !pceltFetched)
442         { return E_INVALIDARG;
443         }
444
445         for(i = 0; i < celt; i++)
446         { if(!(This->mpCurrent))
447           { hr =  S_FALSE;
448             break;
449           }
450           temp = ILClone(This->mpCurrent->pidl);
451           rgelt[i] = temp;
452           This->mpCurrent = This->mpCurrent->pNext;
453         }
454         if(pceltFetched)
455         {  *pceltFetched = i;
456         }
457
458         return hr;
459 }
460
461 /**************************************************************************
462 *  IEnumIDList_fnSkip
463 */
464 static HRESULT WINAPI IEnumIDList_fnSkip(
465         IEnumIDList * iface,ULONG celt)
466 {
467         ICOM_THIS(IEnumIDListImpl,iface);
468
469         DWORD    dwIndex;
470         HRESULT  hr = S_OK;
471
472         TRACE("(%p)->(%lu)\n",This,celt);
473
474         for(dwIndex = 0; dwIndex < celt; dwIndex++)
475         { if(!This->mpCurrent)
476           { hr = S_FALSE;
477             break;
478           }
479           This->mpCurrent = This->mpCurrent->pNext;
480         }
481         return hr;
482 }
483 /**************************************************************************
484 *  IEnumIDList_fnReset
485 */
486 static HRESULT WINAPI IEnumIDList_fnReset(
487         IEnumIDList * iface)
488 {
489         ICOM_THIS(IEnumIDListImpl,iface);
490
491         TRACE("(%p)\n",This);
492         This->mpCurrent = This->mpFirst;
493         return S_OK;
494 }
495 /**************************************************************************
496 *  IEnumIDList_fnClone
497 */
498 static HRESULT WINAPI IEnumIDList_fnClone(
499         IEnumIDList * iface,LPENUMIDLIST * ppenum)
500 {
501         ICOM_THIS(IEnumIDListImpl,iface);
502
503         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
504         return E_NOTIMPL;
505 }
506
507 /**************************************************************************
508  *  IEnumIDList_fnVTable
509  */
510 static ICOM_VTABLE (IEnumIDList) eidlvt =
511 {
512         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
513         IEnumIDList_fnQueryInterface,
514         IEnumIDList_fnAddRef,
515         IEnumIDList_fnRelease,
516         IEnumIDList_fnNext,
517         IEnumIDList_fnSkip,
518         IEnumIDList_fnReset,
519         IEnumIDList_fnClone,
520 };