- rename PT_SPECIAL to PT_SHELLEXT
[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 <stdarg.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include "wine/debug.h"
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "undocshell.h"
29 #include "shlwapi.h"
30 #include "winerror.h"
31 #include "objbase.h"
32 #include <cpl.h>
33
34 #include "pidl.h"
35 #include "shlguid.h"
36 #include "shell32_main.h"
37 #include "cpanel.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(shell);
40
41 typedef struct tagENUMLIST
42 {
43         struct tagENUMLIST      *pNext;
44         LPITEMIDLIST            pidl;
45
46 } ENUMLIST, *LPENUMLIST;
47
48 typedef struct
49 {
50         ICOM_VFIELD(IEnumIDList);
51         DWORD                           ref;
52         LPENUMLIST                      mpFirst;
53         LPENUMLIST                      mpLast;
54         LPENUMLIST                      mpCurrent;
55
56 } IEnumIDListImpl;
57
58 static struct ICOM_VTABLE(IEnumIDList) eidlvt;
59
60 /**************************************************************************
61  *  AddToEnumList()
62  */
63 static BOOL AddToEnumList(
64         IEnumIDList * iface,
65         LPITEMIDLIST pidl)
66 {
67         ICOM_THIS(IEnumIDListImpl,iface);
68
69         LPENUMLIST  pNew;
70
71         TRACE("(%p)->(pidl=%p)\n",This,pidl);
72         pNew = (LPENUMLIST)SHAlloc(sizeof(ENUMLIST));
73         if(pNew)
74         {
75           /*set the next pointer */
76           pNew->pNext = NULL;
77           pNew->pidl = pidl;
78
79           /*is This the first item in the list? */
80           if(!This->mpFirst)
81           {
82             This->mpFirst = pNew;
83             This->mpCurrent = pNew;
84           }
85
86           if(This->mpLast)
87           {
88             /*add the new item to the end of the list */
89             This->mpLast->pNext = pNew;
90           }
91
92           /*update the last item pointer */
93           This->mpLast = pNew;
94           TRACE("-- (%p)->(first=%p, last=%p)\n",This,This->mpFirst,This->mpLast);
95           return TRUE;
96         }
97         return FALSE;
98 }
99
100 /**************************************************************************
101  *  CreateFolderEnumList()
102  */
103 static BOOL CreateFolderEnumList(
104         IEnumIDList * iface,
105         LPCSTR lpszPath,
106         DWORD dwFlags)
107 {
108         ICOM_THIS(IEnumIDListImpl,iface);
109
110         LPITEMIDLIST    pidl=NULL;
111         WIN32_FIND_DATAA stffile;
112         HANDLE hFile;
113         CHAR  szPath[MAX_PATH];
114
115         TRACE("(%p)->(path=%s flags=0x%08lx) \n",This,debugstr_a(lpszPath),dwFlags);
116
117         if(!lpszPath || !lpszPath[0]) return FALSE;
118
119         strcpy(szPath, lpszPath);
120         PathAddBackslashA(szPath);
121         strcat(szPath,"*.*");
122
123         /*enumerate the folders*/
124         if(dwFlags & SHCONTF_FOLDERS)
125         {
126           TRACE("-- (%p)-> enumerate SHCONTF_FOLDERS of %s\n",This,debugstr_a(szPath));
127           hFile = FindFirstFileA(szPath,&stffile);
128           if ( hFile != INVALID_HANDLE_VALUE )
129           {
130             do
131             {
132               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
133               if ( (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && strcmp (stffile.cFileName, ".") && strcmp (stffile.cFileName, ".."))
134               {
135                 pidl = _ILCreateFromFindDataA (&stffile);
136                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
137                 {
138                   continue;
139                 }
140                 return FALSE;
141               }
142             } while( FindNextFileA(hFile,&stffile));
143             FindClose (hFile);
144           }
145         }
146
147         /*enumerate the non-folder items (values) */
148         if(dwFlags & SHCONTF_NONFOLDERS)
149         {
150           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
151           hFile = FindFirstFileA(szPath,&stffile);
152           if ( hFile != INVALID_HANDLE_VALUE )
153           {
154             do
155             {
156               if ( !(dwFlags & SHCONTF_INCLUDEHIDDEN) && (stffile.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ) continue;
157               if (! (stffile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) )
158               {
159                 pidl = _ILCreateFromFindDataA(&stffile);
160                 if(pidl && AddToEnumList((IEnumIDList*)This, pidl))
161                 {
162                   continue;
163                 }
164                 return FALSE;
165               }
166             } while( FindNextFileA(hFile,&stffile));
167             FindClose (hFile);
168           }
169         }
170         return TRUE;
171 }
172
173 BOOL SHELL_RegisterCPanelApp(IEnumIDList* list, LPCSTR path)
174 {
175     LPITEMIDLIST pidl;
176     CPlApplet* applet;
177     CPanel panel;
178     CPLINFO info;
179     unsigned i;
180     int iconIdx;
181
182     char displayName[MAX_PATH];
183     char comment[MAX_PATH];
184
185     WCHAR wpath[MAX_PATH];
186
187     MultiByteToWideChar(CP_ACP, 0, path, -1, wpath, MAX_PATH);
188
189     panel.first = NULL;
190     applet = Control_LoadApplet(0, wpath, &panel);
191
192     if (applet) {
193         for(i=0; i<applet->count; ++i) {
194             WideCharToMultiByte(CP_ACP, 0, applet->info[i].szName, -1, displayName, MAX_PATH, 0, 0);
195             WideCharToMultiByte(CP_ACP, 0, applet->info[i].szInfo, -1, comment, MAX_PATH, 0, 0);
196
197             applet->proc(0, CPL_INQUIRE, i, (LPARAM)&info);
198
199             if (info.idIcon > 0)
200                 iconIdx = -info.idIcon; /* negative icon index instead of icon number */
201             else
202                 iconIdx = 0;
203
204             pidl = _ILCreateCPanel(path, displayName, comment, iconIdx);
205
206             if (pidl)
207                 AddToEnumList(list, pidl);
208         }
209
210         Control_UnloadApplet(applet);
211     }
212
213     return TRUE;
214 }
215
216 int SHELL_RegisterRegistryCPanelApps(IEnumIDList* list, HKEY hkey_root, LPCSTR szRepPath)
217 {
218   char name[MAX_PATH];
219   char value[MAX_PATH];
220   HKEY hkey;
221
222   int cnt = 0;
223
224   if (RegOpenKeyA(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
225   {
226     int idx = 0;
227     for(;; ++idx)
228     {
229       DWORD nameLen = MAX_PATH;
230       DWORD valueLen = MAX_PATH;
231
232       if (RegEnumValueA(hkey, idx, name, &nameLen, NULL, NULL, (LPBYTE)&value, &valueLen) != ERROR_SUCCESS)
233         break;
234
235       if (SHELL_RegisterCPanelApp(list, value))
236         ++cnt;
237     }
238
239     RegCloseKey(hkey);
240   }
241
242   return cnt;
243 }
244
245 int SHELL_RegisterCPanelFolders(IEnumIDList* list, HKEY hkey_root, LPCSTR szRepPath)
246 {
247   char name[MAX_PATH];
248   HKEY hkey;
249
250   int cnt = 0;
251
252   if (RegOpenKeyA(hkey_root, szRepPath, &hkey) == ERROR_SUCCESS)
253   {
254     int idx = 0;
255     for(;; ++idx)
256     {
257       if (RegEnumKeyA(hkey, idx, name, MAX_PATH) != ERROR_SUCCESS)
258         break;
259
260       if (*name == '{') {
261         LPITEMIDLIST pidl = _ILCreateGuidFromStrA(name);
262
263         if (pidl && AddToEnumList(list, pidl))
264             ++cnt;
265       }
266     }
267
268     RegCloseKey(hkey);
269   }
270
271   return cnt;
272 }
273
274 /**************************************************************************
275  *  CreateCPanelEnumList()
276  */
277 static BOOL CreateCPanelEnumList(
278         IEnumIDList * iface,
279         DWORD dwFlags)
280 {
281         ICOM_THIS(IEnumIDListImpl,iface);
282
283         CHAR szPath[MAX_PATH];
284         WIN32_FIND_DATAA wfd;
285         HANDLE hFile;
286
287         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
288
289         /* enumerate control panel folders folders */
290         if (dwFlags & SHCONTF_FOLDERS)
291           SHELL_RegisterCPanelFolders((IEnumIDList*)This, HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\NameSpace");
292
293         /* enumerate the control panel applets */
294         if (dwFlags & SHCONTF_NONFOLDERS)
295         {
296           LPSTR p;
297
298           GetSystemDirectoryA(szPath, MAX_PATH);
299           p = PathAddBackslashA(szPath);
300           strcpy(p, "*.cpl");
301
302           TRACE("-- (%p)-> enumerate SHCONTF_NONFOLDERS of %s\n",This,debugstr_a(szPath));
303           hFile = FindFirstFileA(szPath, &wfd);
304
305           if (hFile != INVALID_HANDLE_VALUE)
306           {
307             do
308             {
309               if (!(dwFlags & SHCONTF_INCLUDEHIDDEN) && (wfd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
310                 continue;
311
312               if (!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
313                 strcpy(p, wfd.cFileName);
314                 SHELL_RegisterCPanelApp((IEnumIDList*)This, szPath);
315               }
316             } while(FindNextFileA(hFile, &wfd));
317
318             FindClose(hFile);
319           }
320
321           SHELL_RegisterRegistryCPanelApps((IEnumIDList*)This, HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
322           SHELL_RegisterRegistryCPanelApps((IEnumIDList*)This, HKEY_CURRENT_USER, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Control Panel\\Cpls");
323         }
324
325         return TRUE;
326 }
327
328 /**************************************************************************
329  *  CreateDesktopEnumList()
330  */
331 static BOOL CreateDesktopEnumList(
332         IEnumIDList * iface,
333         DWORD dwFlags)
334 {
335         ICOM_THIS(IEnumIDListImpl,iface);
336
337         LPITEMIDLIST    pidl=NULL;
338         HKEY hkey;
339         char    szPath[MAX_PATH];
340
341         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
342
343         /*enumerate the root folders */
344         if(dwFlags & SHCONTF_FOLDERS)
345         {
346           /*create the pidl for This item */
347           pidl = _ILCreateMyComputer();
348           if(pidl)
349           {
350             if(!AddToEnumList((IEnumIDList*)This, pidl))
351               return FALSE;
352           }
353
354           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\desktop\\NameSpace", 0, KEY_READ, &hkey))
355           {
356             char iid[50];
357             int i=0;
358
359             while (1)
360             {
361               DWORD size = sizeof (iid);
362
363               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
364                 break;
365
366               pidl = _ILCreateGuidFromStrA(iid);
367
368               if(pidl)
369                 AddToEnumList((IEnumIDList*)This, pidl);
370
371               i++;
372             }
373             RegCloseKey(hkey);
374           }
375         }
376
377         /*enumerate the elements in %windir%\desktop */
378         SHGetSpecialFolderPathA(0, szPath, CSIDL_DESKTOPDIRECTORY, FALSE);
379         CreateFolderEnumList( (IEnumIDList*)This, szPath, dwFlags);
380
381         return TRUE;
382 }
383
384 /**************************************************************************
385  *  CreateMyCompEnumList()
386  */
387 static BOOL CreateMyCompEnumList(
388         IEnumIDList * iface,
389         DWORD dwFlags)
390 {
391         ICOM_THIS(IEnumIDListImpl,iface);
392
393         LPITEMIDLIST    pidl=NULL;
394         DWORD           dwDrivemap;
395         CHAR            szDriveName[4];
396         HKEY            hkey;
397
398         TRACE("(%p)->(flags=0x%08lx) \n",This,dwFlags);
399
400         /*enumerate the folders*/
401         if(dwFlags & SHCONTF_FOLDERS)
402         {
403           dwDrivemap = GetLogicalDrives();
404           strcpy (szDriveName,"A:\\");
405           while (szDriveName[0]<='Z')
406           {
407             if(dwDrivemap & 0x00000001L)
408             {
409               pidl = _ILCreateDrive(szDriveName);
410               if(pidl)
411               {
412                 if(!AddToEnumList((IEnumIDList*)This, pidl))
413                   return FALSE;
414               }
415             }
416             szDriveName[0]++;
417             dwDrivemap = dwDrivemap >> 1;
418           }
419
420           TRACE("-- (%p)-> enumerate (mycomputer shell extensions)\n",This);
421           if (! RegOpenKeyExA(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\explorer\\mycomputer\\NameSpace", 0, KEY_READ, &hkey))
422           {
423             char iid[50];
424             int i=0;
425
426             while (1)
427             {
428               DWORD size = sizeof (iid);
429
430               if (ERROR_SUCCESS!=RegEnumKeyExA(hkey, i, iid, &size, 0, NULL, NULL, NULL))
431                 break;
432
433               pidl = _ILCreateGuidFromStrA(iid);
434
435               if(pidl)
436                 AddToEnumList((IEnumIDList*)This, pidl);
437
438               i++;
439             }
440             RegCloseKey(hkey);
441           }
442         }
443         return TRUE;
444 }
445
446 /**************************************************************************
447 *   DeleteList()
448 */
449 static BOOL DeleteList(
450         IEnumIDList * iface)
451 {
452         ICOM_THIS(IEnumIDListImpl,iface);
453
454         LPENUMLIST  pDelete;
455
456         TRACE("(%p)->()\n",This);
457
458         while(This->mpFirst)
459         { pDelete = This->mpFirst;
460           This->mpFirst = pDelete->pNext;
461           SHFree(pDelete->pidl);
462           SHFree(pDelete);
463         }
464         This->mpFirst = This->mpLast = This->mpCurrent = NULL;
465         return TRUE;
466 }
467
468 /**************************************************************************
469  *  IEnumIDList_Folder_Constructor
470  *
471  */
472
473 IEnumIDList * IEnumIDList_Constructor(
474         LPCSTR lpszPath,
475         DWORD dwFlags,
476         DWORD dwKind)
477 {
478         IEnumIDListImpl*        lpeidl;
479         BOOL                    ret = FALSE;
480
481         TRACE("()->(%s flags=0x%08lx kind=0x%08lx)\n",debugstr_a(lpszPath),dwFlags, dwKind);
482
483         lpeidl = (IEnumIDListImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IEnumIDListImpl));
484
485         if (lpeidl)
486         {
487           lpeidl->ref = 1;
488           lpeidl->lpVtbl = &eidlvt;
489
490           switch (dwKind)
491           {
492             case EIDL_DESK:
493               ret = CreateDesktopEnumList((IEnumIDList*)lpeidl, dwFlags);
494               break;
495
496             case EIDL_MYCOMP:
497               ret = CreateMyCompEnumList((IEnumIDList*)lpeidl, dwFlags);
498               break;
499
500             case EIDL_FILE:
501               ret = CreateFolderEnumList((IEnumIDList*)lpeidl, lpszPath, dwFlags);
502               break;
503
504             case EIDL_CPANEL:
505               ret = CreateCPanelEnumList((IEnumIDList*)lpeidl, dwFlags);
506               break;
507           }
508
509             if(!ret) {
510                 HeapFree(GetProcessHeap(),0,lpeidl);
511                 lpeidl = NULL;
512             }
513         }
514
515         TRACE("-- (%p)->()\n",lpeidl);
516
517         return (IEnumIDList*)lpeidl;
518 }
519
520 /**************************************************************************
521  *  EnumIDList_QueryInterface
522  */
523 static HRESULT WINAPI IEnumIDList_fnQueryInterface(
524         IEnumIDList * iface,
525         REFIID riid,
526         LPVOID *ppvObj)
527 {
528         ICOM_THIS(IEnumIDListImpl,iface);
529
530         TRACE("(%p)->(\n\tIID:\t%s,%p)\n",This,debugstr_guid(riid),ppvObj);
531
532         *ppvObj = NULL;
533
534         if(IsEqualIID(riid, &IID_IUnknown))          /*IUnknown*/
535         { *ppvObj = This;
536         }
537         else if(IsEqualIID(riid, &IID_IEnumIDList))  /*IEnumIDList*/
538         {    *ppvObj = (IEnumIDList*)This;
539         }
540
541         if(*ppvObj)
542         { IEnumIDList_AddRef((IEnumIDList*)*ppvObj);
543           TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj);
544           return S_OK;
545         }
546
547         TRACE("-- Interface: E_NOINTERFACE\n");
548         return E_NOINTERFACE;
549 }
550
551 /******************************************************************************
552  * IEnumIDList_fnAddRef
553  */
554 static ULONG WINAPI IEnumIDList_fnAddRef(
555         IEnumIDList * iface)
556 {
557         ICOM_THIS(IEnumIDListImpl,iface);
558         TRACE("(%p)->(%lu)\n",This,This->ref);
559         return ++(This->ref);
560 }
561 /******************************************************************************
562  * IEnumIDList_fnRelease
563  */
564 static ULONG WINAPI IEnumIDList_fnRelease(
565         IEnumIDList * iface)
566 {
567         ICOM_THIS(IEnumIDListImpl,iface);
568
569         TRACE("(%p)->(%lu)\n",This,This->ref);
570
571         if (!--(This->ref)) {
572           TRACE(" destroying IEnumIDList(%p)\n",This);
573           DeleteList((IEnumIDList*)This);
574           HeapFree(GetProcessHeap(),0,This);
575           return 0;
576         }
577         return This->ref;
578 }
579
580 /**************************************************************************
581  *  IEnumIDList_fnNext
582  */
583
584 static HRESULT WINAPI IEnumIDList_fnNext(
585         IEnumIDList * iface,
586         ULONG celt,
587         LPITEMIDLIST * rgelt,
588         ULONG *pceltFetched)
589 {
590         ICOM_THIS(IEnumIDListImpl,iface);
591
592         ULONG    i;
593         HRESULT  hr = S_OK;
594         LPITEMIDLIST  temp;
595
596         TRACE("(%p)->(%ld,%p, %p)\n",This,celt,rgelt,pceltFetched);
597
598 /* It is valid to leave pceltFetched NULL when celt is 1. Some of explorer's
599  * subsystems actually use it (and so may a third party browser)
600  */
601         if(pceltFetched)
602           *pceltFetched = 0;
603
604         *rgelt=0;
605
606         if(celt > 1 && !pceltFetched)
607         { return E_INVALIDARG;
608         }
609
610         if(celt > 0 && !This->mpCurrent)
611         { return S_FALSE;
612         }
613
614         for(i = 0; i < celt; i++)
615         { if(!(This->mpCurrent))
616             break;
617
618           temp = ILClone(This->mpCurrent->pidl);
619           rgelt[i] = temp;
620           This->mpCurrent = This->mpCurrent->pNext;
621         }
622         if(pceltFetched)
623         {  *pceltFetched = i;
624         }
625
626         return hr;
627 }
628
629 /**************************************************************************
630 *  IEnumIDList_fnSkip
631 */
632 static HRESULT WINAPI IEnumIDList_fnSkip(
633         IEnumIDList * iface,ULONG celt)
634 {
635         ICOM_THIS(IEnumIDListImpl,iface);
636
637         DWORD    dwIndex;
638         HRESULT  hr = S_OK;
639
640         TRACE("(%p)->(%lu)\n",This,celt);
641
642         for(dwIndex = 0; dwIndex < celt; dwIndex++)
643         { if(!This->mpCurrent)
644           { hr = S_FALSE;
645             break;
646           }
647           This->mpCurrent = This->mpCurrent->pNext;
648         }
649         return hr;
650 }
651 /**************************************************************************
652 *  IEnumIDList_fnReset
653 */
654 static HRESULT WINAPI IEnumIDList_fnReset(
655         IEnumIDList * iface)
656 {
657         ICOM_THIS(IEnumIDListImpl,iface);
658
659         TRACE("(%p)\n",This);
660         This->mpCurrent = This->mpFirst;
661         return S_OK;
662 }
663 /**************************************************************************
664 *  IEnumIDList_fnClone
665 */
666 static HRESULT WINAPI IEnumIDList_fnClone(
667         IEnumIDList * iface,LPENUMIDLIST * ppenum)
668 {
669         ICOM_THIS(IEnumIDListImpl,iface);
670
671         TRACE("(%p)->() to (%p)->() E_NOTIMPL\n",This,ppenum);
672         return E_NOTIMPL;
673 }
674
675 /**************************************************************************
676  *  IEnumIDList_fnVTable
677  */
678 static ICOM_VTABLE (IEnumIDList) eidlvt =
679 {
680         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
681         IEnumIDList_fnQueryInterface,
682         IEnumIDList_fnAddRef,
683         IEnumIDList_fnRelease,
684         IEnumIDList_fnNext,
685         IEnumIDList_fnSkip,
686         IEnumIDList_fnReset,
687         IEnumIDList_fnClone,
688 };