wintrust: Use a helper function to get a signer's cert info from a message.
[wine] / dlls / shell32 / shlfolder.c
1 /*
2  *      Shell Folder stuff
3  *
4  *      Copyright 1997                  Marcus Meissner
5  *      Copyright 1998, 1999, 2002      Juergen Schmied
6  *
7  *      IShellFolder2 and related interfaces
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25 #include "wine/port.h"
26
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdarg.h>
30 #include <stdio.h>
31
32 #define COBJMACROS
33
34 #include "winerror.h"
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winreg.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40
41 #include "ole2.h"
42 #include "shlguid.h"
43
44 #include "pidl.h"
45 #include "undocshell.h"
46 #include "shell32_main.h"
47 #include "shlwapi.h"
48 #include "wine/debug.h"
49 #include "shfldr.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL (shell);
52
53 static CRITICAL_SECTION SHELL32_SF_ClassCacheCS;
54 static CRITICAL_SECTION_DEBUG critsect_debug =
55 {
56     0, 0, &SHELL32_SF_ClassCacheCS,
57     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
58       0, 0, { (DWORD_PTR)(__FILE__ ": SHELL32_SF_ClassCacheCS") }
59 };
60 static CRITICAL_SECTION SHELL32_SF_ClassCacheCS = { &critsect_debug, -1, 0, 0, 0, 0 };
61
62 /* IShellFolder class cache */
63 struct _sf_cls_cache_entry
64 {
65     CLSID clsid;
66     LPVOID pv;
67 };
68
69 static struct _sf_class_cache
70 {
71     DWORD allocated, used;
72     struct _sf_cls_cache_entry *sf_cls_cache_entry;
73 } sf_cls_cache;
74
75 static const WCHAR wszDotShellClassInfo[] = {
76     '.','S','h','e','l','l','C','l','a','s','s','I','n','f','o',0};
77
78 /***************************************************************************
79  *  SHELL32_GetCustomFolderAttribute (internal function)
80  *
81  * Gets a value from the folder's desktop.ini file, if one exists.
82  *
83  * PARAMETERS
84  *  pidl          [I] Folder containing the desktop.ini file.
85  *  pwszHeading   [I] Heading in .ini file.
86  *  pwszAttribute [I] Attribute in .ini file.
87  *  pwszValue     [O] Buffer to store value into.
88  *  cchValue      [I] Size in characters including NULL of buffer pointed to
89  *                    by pwszValue.
90  *
91  *  RETURNS
92  *    TRUE if returned non-NULL value.
93  *    FALSE otherwise.
94  */
95 static inline BOOL SHELL32_GetCustomFolderAttributeFromPath(
96     LPWSTR pwszFolderPath, LPCWSTR pwszHeading, LPCWSTR pwszAttribute,
97     LPWSTR pwszValue, DWORD cchValue)
98 {
99     static const WCHAR wszDesktopIni[] =
100             {'d','e','s','k','t','o','p','.','i','n','i',0};
101     static const WCHAR wszDefault[] = {0};
102
103     PathAddBackslashW(pwszFolderPath);
104     PathAppendW(pwszFolderPath, wszDesktopIni);
105     return GetPrivateProfileStringW(pwszHeading, pwszAttribute, wszDefault, 
106                                     pwszValue, cchValue, pwszFolderPath);
107 }
108
109 BOOL SHELL32_GetCustomFolderAttribute(
110     LPCITEMIDLIST pidl, LPCWSTR pwszHeading, LPCWSTR pwszAttribute,
111     LPWSTR pwszValue, DWORD cchValue)
112 {
113     DWORD dwAttrib = FILE_ATTRIBUTE_SYSTEM;
114     WCHAR wszFolderPath[MAX_PATH];
115
116     /* Hack around not having system attribute on non-Windows file systems */
117     if (0)
118         dwAttrib = _ILGetFileAttributes(pidl, NULL, 0);
119
120     if (dwAttrib & FILE_ATTRIBUTE_SYSTEM)
121     {
122         if (!SHGetPathFromIDListW(pidl, wszFolderPath))
123             return FALSE;
124
125         return SHELL32_GetCustomFolderAttributeFromPath(wszFolderPath, pwszHeading, 
126                                                 pwszAttribute, pwszValue, cchValue);
127     }
128     return FALSE;
129 }
130
131 /***************************************************************************
132  *  GetNextElement (internal function)
133  *
134  * Gets a part of a string till the first backslash.
135  *
136  * PARAMETERS
137  *  pszNext [IN] string to get the element from
138  *  pszOut  [IN] pointer to buffer whitch receives string
139  *  dwOut   [IN] length of pszOut
140  *
141  *  RETURNS
142  *    LPSTR pointer to first, not yet parsed char
143  */
144
145 LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut)
146 {
147     LPCWSTR pszTail = pszNext;
148     DWORD dwCopy;
149
150     TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext), pszOut, dwOut);
151
152     *pszOut = 0x0000;
153
154     if (!pszNext || !*pszNext)
155         return NULL;
156
157     while (*pszTail && (*pszTail != (WCHAR) '\\'))
158         pszTail++;
159
160     dwCopy = (const WCHAR *) pszTail - (const WCHAR *) pszNext + 1;
161     lstrcpynW (pszOut, pszNext, (dwOut < dwCopy) ? dwOut : dwCopy);
162
163     if (*pszTail)
164         pszTail++;
165     else
166         pszTail = NULL;
167
168     TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext), debugstr_w (pszOut), dwOut, pszTail);
169     return pszTail;
170 }
171
172 HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,
173                                   LPITEMIDLIST * pidlInOut, LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes)
174 {
175     HRESULT hr = E_INVALIDARG;
176     LPITEMIDLIST pidlOut = NULL,
177       pidlTemp = NULL;
178     IShellFolder *psfChild;
179
180     TRACE ("(%p, %p, %p, %s)\n", psf, pbc, pidlInOut ? *pidlInOut : NULL, debugstr_w (szNext));
181
182     /* get the shellfolder for the child pidl and let it analyse further */
183     hr = IShellFolder_BindToObject (psf, *pidlInOut, pbc, &IID_IShellFolder, (LPVOID *) & psfChild);
184
185     if (SUCCEEDED(hr)) {
186         hr = IShellFolder_ParseDisplayName (psfChild, hwndOwner, pbc, szNext, pEaten, &pidlOut, pdwAttributes);
187         IShellFolder_Release (psfChild);
188
189         if (SUCCEEDED(hr)) {
190             pidlTemp = ILCombine (*pidlInOut, pidlOut);
191
192             if (!pidlTemp)
193                 hr = E_OUTOFMEMORY;
194         }
195
196         if (pidlOut)
197             ILFree (pidlOut);
198     }
199
200     ILFree (*pidlInOut);
201     *pidlInOut = pidlTemp;
202
203     TRACE ("-- pidl=%p ret=0x%08x\n", pidlInOut ? *pidlInOut : NULL, hr);
204     return hr;
205 }
206
207 static BOOL get_iface_from_cache(REFCLSID clsid, LPVOID *ppvOut)
208 {
209     BOOL ret = FALSE;
210     int i;
211
212     EnterCriticalSection(&SHELL32_SF_ClassCacheCS);
213
214     for (i = 0; i < sf_cls_cache.used; i++)
215     {
216         if (IsEqualIID(&sf_cls_cache.sf_cls_cache_entry[i].clsid, clsid))
217         {
218             *ppvOut = sf_cls_cache.sf_cls_cache_entry[i].pv;
219             /* Pin it */
220             IUnknown_AddRef((IUnknown *)*ppvOut);
221             ret = TRUE;
222             break;
223         }
224     }
225
226     LeaveCriticalSection(&SHELL32_SF_ClassCacheCS);
227     return ret;
228 }
229
230 static void add_iface_to_cache(REFCLSID clsid, LPVOID pv)
231 {
232     EnterCriticalSection(&SHELL32_SF_ClassCacheCS);
233
234     if (sf_cls_cache.used >= sf_cls_cache.allocated)
235     {
236         DWORD allocated;
237         struct _sf_cls_cache_entry *sf_cls_cache_entry;
238
239         if (!sf_cls_cache.allocated)
240         {
241             allocated = 4;
242             sf_cls_cache_entry = HeapAlloc(GetProcessHeap(), 0,
243                                            4 * sizeof(*sf_cls_cache_entry));
244         }
245         else
246         {
247             allocated = sf_cls_cache.allocated * 2;
248             sf_cls_cache_entry = HeapReAlloc(GetProcessHeap(), 0, sf_cls_cache.sf_cls_cache_entry,
249                                              allocated * sizeof(*sf_cls_cache_entry));
250         }
251         if (!sf_cls_cache_entry)
252         {
253             LeaveCriticalSection(&SHELL32_SF_ClassCacheCS);
254             return;
255         }
256
257         sf_cls_cache.allocated = allocated;
258         sf_cls_cache.sf_cls_cache_entry = sf_cls_cache_entry;
259     }
260
261     /* Pin it */
262     IUnknown_AddRef((IUnknown *)pv);
263
264     sf_cls_cache.sf_cls_cache_entry[sf_cls_cache.used].clsid = *clsid;
265     sf_cls_cache.sf_cls_cache_entry[sf_cls_cache.used].pv = pv;
266     sf_cls_cache.used++;
267
268     LeaveCriticalSection(&SHELL32_SF_ClassCacheCS);
269 }
270
271 /***********************************************************************
272  *      SHELL32_CoCreateInitSF
273  *
274  * Creates a shell folder and initializes it with a pidl and a root folder
275  * via IPersistFolder3 or IPersistFolder.
276  *
277  * NOTES
278  *   pathRoot can be NULL for Folders being a drive.
279  *   In this case the absolute path is built from pidlChild (eg. C:)
280  */
281 static HRESULT SHELL32_CoCreateInitSF (LPCITEMIDLIST pidlRoot, LPCWSTR pathRoot,
282                 LPCITEMIDLIST pidlChild, REFCLSID clsid, LPVOID * ppvOut)
283 {
284     HRESULT hr = S_OK;
285
286     TRACE ("%p %s %p\n", pidlRoot, debugstr_w(pathRoot), pidlChild);
287
288     if (!get_iface_from_cache(clsid, ppvOut))
289     {
290         hr = SHCoCreateInstance(NULL, clsid, NULL, &IID_IShellFolder, ppvOut);
291         if (SUCCEEDED(hr))
292         {
293             TRACE("loaded %p %s\n", *ppvOut, wine_dbgstr_guid(clsid));
294             add_iface_to_cache(clsid, *ppvOut);
295         }
296     }
297     else
298         TRACE("found in the cache %p %s\n", *ppvOut, wine_dbgstr_guid(clsid));
299
300     if (SUCCEEDED (hr))
301     {
302         LPITEMIDLIST pidlAbsolute = ILCombine (pidlRoot, pidlChild);
303         IPersistFolder *pPF;
304         IPersistFolder3 *ppf;
305
306         if (_ILIsFolder(pidlChild) &&
307             SUCCEEDED (IUnknown_QueryInterface ((IUnknown *) * ppvOut, &IID_IPersistFolder3, (LPVOID *) & ppf))) 
308         {
309             PERSIST_FOLDER_TARGET_INFO ppfti;
310
311             ZeroMemory (&ppfti, sizeof (ppfti));
312
313             /* fill the PERSIST_FOLDER_TARGET_INFO */
314             ppfti.dwAttributes = -1;
315             ppfti.csidl = -1;
316
317             /* build path */
318             if (pathRoot) {
319                 lstrcpynW (ppfti.szTargetParsingName, pathRoot, MAX_PATH - 1);
320                 PathAddBackslashW(ppfti.szTargetParsingName); /* FIXME: why have drives a backslash here ? */
321             }
322
323             if (pidlChild) {
324                 int len = lstrlenW(ppfti.szTargetParsingName);
325
326                 if (!_ILSimpleGetTextW(pidlChild, ppfti.szTargetParsingName + len, MAX_PATH - len))
327                         hr = E_INVALIDARG;
328             }
329
330             IPersistFolder3_InitializeEx (ppf, NULL, pidlAbsolute, &ppfti);
331             IPersistFolder3_Release (ppf);
332         }
333         else if (SUCCEEDED ((hr = IUnknown_QueryInterface ((IUnknown *) * ppvOut, &IID_IPersistFolder, (LPVOID *) & pPF)))) {
334             IPersistFolder_Initialize (pPF, pidlAbsolute);
335             IPersistFolder_Release (pPF);
336         }
337         ILFree (pidlAbsolute);
338     }
339     TRACE ("-- (%p) ret=0x%08x\n", *ppvOut, hr);
340     return hr;
341 }
342
343 /***********************************************************************
344  *      SHELL32_BindToChild [Internal]
345  *
346  * Common code for IShellFolder_BindToObject.
347  * 
348  * PARAMS
349  *  pidlRoot     [I] The parent shell folder's absolute pidl.
350  *  pathRoot     [I] Absolute dos path of the parent shell folder.
351  *  pidlComplete [I] PIDL of the child. Relative to pidlRoot.
352  *  riid         [I] GUID of the interface, which ppvOut shall be bound to.
353  *  ppvOut       [O] A reference to the child's interface (riid).
354  *
355  * NOTES
356  *  pidlComplete has to contain at least one non empty SHITEMID.
357  *  This function makes special assumptions on the shell namespace, which
358  *  means you probably can't use it for your IShellFolder implementation.
359  */
360 HRESULT SHELL32_BindToChild (LPCITEMIDLIST pidlRoot,
361                              LPCWSTR pathRoot, LPCITEMIDLIST pidlComplete, REFIID riid, LPVOID * ppvOut)
362 {
363     GUID const *clsid;
364     IShellFolder *pSF;
365     HRESULT hr;
366     LPITEMIDLIST pidlChild;
367
368     if (!pidlRoot || !ppvOut || !pidlComplete || !pidlComplete->mkid.cb)
369         return E_INVALIDARG;
370
371     *ppvOut = NULL;
372
373     pidlChild = ILCloneFirst (pidlComplete);
374
375     if ((clsid = _ILGetGUIDPointer (pidlChild))) {
376         /* virtual folder */
377         hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild, clsid, (LPVOID *)&pSF);
378     } else {
379         /* file system folder */
380         CLSID clsidFolder = CLSID_ShellFSFolder;
381         static const WCHAR wszCLSID[] = {'C','L','S','I','D',0};
382         WCHAR wszCLSIDValue[CHARS_IN_GUID], wszFolderPath[MAX_PATH], *pwszPathTail = wszFolderPath;
383        
384         /* see if folder CLSID should be overridden by desktop.ini file */
385         if (pathRoot) {
386             lstrcpynW(wszFolderPath, pathRoot, MAX_PATH);
387             pwszPathTail = PathAddBackslashW(wszFolderPath);
388         }
389
390         _ILSimpleGetTextW(pidlChild,pwszPathTail,MAX_PATH - (int)(pwszPathTail - wszFolderPath));
391
392         if (SHELL32_GetCustomFolderAttributeFromPath (wszFolderPath,
393             wszDotShellClassInfo, wszCLSID, wszCLSIDValue, CHARS_IN_GUID))
394             CLSIDFromString (wszCLSIDValue, &clsidFolder);
395
396         hr = SHELL32_CoCreateInitSF (pidlRoot, pathRoot, pidlChild,
397                                      &clsidFolder, (LPVOID *)&pSF);
398     }
399     ILFree (pidlChild);
400
401     if (SUCCEEDED (hr)) {
402         if (_ILIsPidlSimple (pidlComplete)) {
403             /* no sub folders */
404             hr = IShellFolder_QueryInterface (pSF, riid, ppvOut);
405         } else {
406             /* go deeper */
407             hr = IShellFolder_BindToObject (pSF, ILGetNext (pidlComplete), NULL, riid, ppvOut);
408         }
409         IShellFolder_Release (pSF);
410     }
411
412     TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
413
414     return hr;
415 }
416
417 /***********************************************************************
418  *      SHELL32_GetDisplayNameOfChild
419  *
420  * Retrieves the display name of a child object of a shellfolder.
421  *
422  * For a pidl eg. [subpidl1][subpidl2][subpidl3]:
423  * - it binds to the child shellfolder [subpidl1]
424  * - asks it for the displayname of [subpidl2][subpidl3]
425  *
426  * Is possible the pidl is a simple pidl. In this case it asks the
427  * subfolder for the displayname of an empty pidl. The subfolder
428  * returns the own displayname eg. "::{guid}". This is used for
429  * virtual folders with the registry key WantsFORPARSING set.
430  */
431 HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf,
432                                        LPCITEMIDLIST pidl, DWORD dwFlags, LPWSTR szOut, DWORD dwOutLen)
433 {
434     LPITEMIDLIST pidlFirst;
435     HRESULT hr = E_INVALIDARG;
436
437     TRACE ("(%p)->(pidl=%p 0x%08x %p 0x%08x)\n", psf, pidl, dwFlags, szOut, dwOutLen);
438     pdump (pidl);
439
440     pidlFirst = ILCloneFirst (pidl);
441     if (pidlFirst) {
442         IShellFolder2 *psfChild;
443
444         hr = IShellFolder_BindToObject (psf, pidlFirst, NULL, &IID_IShellFolder, (LPVOID *) & psfChild);
445         if (SUCCEEDED (hr)) {
446             STRRET strTemp;
447             LPITEMIDLIST pidlNext = ILGetNext (pidl);
448
449             hr = IShellFolder_GetDisplayNameOf (psfChild, pidlNext, dwFlags, &strTemp);
450             if (SUCCEEDED (hr)) {
451                 if(!StrRetToStrNW (szOut, dwOutLen, &strTemp, pidlNext))
452                     hr = E_FAIL;
453             }
454             IShellFolder_Release (psfChild);
455         }
456         ILFree (pidlFirst);
457     } else
458         hr = E_OUTOFMEMORY;
459
460     TRACE ("-- ret=0x%08x %s\n", hr, debugstr_w(szOut));
461
462     return hr;
463 }
464
465 /***********************************************************************
466  *  SHELL32_GetItemAttributes
467  *
468  * NOTES
469  * Observed values:
470  *  folder:     0xE0000177      FILESYSTEM | HASSUBFOLDER | FOLDER
471  *  file:       0x40000177      FILESYSTEM
472  *  drive:      0xf0000144      FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR
473  *  mycomputer: 0xb0000154      HASSUBFOLDER | FOLDER | FILESYSANCESTOR
474  *  (seems to be default for shell extensions if no registry entry exists)
475  *
476  * win2k:
477  *  folder:    0xF0400177      FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR | CANMONIKER
478  *  file:      0x40400177      FILESYSTEM | CANMONIKER
479  *  drive      0xF0400154      FILESYSTEM | HASSUBFOLDER | FOLDER | FILESYSANCESTOR | CANMONIKER | CANRENAME (LABEL)
480  *
481  * According to the MSDN documentation this function should not set flags. It claims only to reset flags when necessary.
482  * However it turns out the native shell32.dll _sets_ flags in several cases - so do we.
483  */
484 HRESULT SHELL32_GetItemAttributes (IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
485 {
486     DWORD dwAttributes;
487     BOOL has_guid;
488     static const DWORD dwSupportedAttr=
489                           SFGAO_CANCOPY |           /*0x00000001 */
490                           SFGAO_CANMOVE |           /*0x00000002 */
491                           SFGAO_CANLINK |           /*0x00000004 */
492                           SFGAO_CANRENAME |         /*0x00000010 */
493                           SFGAO_CANDELETE |         /*0x00000020 */
494                           SFGAO_HASPROPSHEET |      /*0x00000040 */
495                           SFGAO_DROPTARGET |        /*0x00000100 */
496                           SFGAO_LINK |              /*0x00010000 */
497                           SFGAO_READONLY |          /*0x00040000 */
498                           SFGAO_HIDDEN |            /*0x00080000 */
499                           SFGAO_FILESYSANCESTOR |   /*0x10000000 */
500                           SFGAO_FOLDER |            /*0x20000000 */
501                           SFGAO_FILESYSTEM |        /*0x40000000 */
502                           SFGAO_HASSUBFOLDER;       /*0x80000000 */
503     
504     TRACE ("0x%08x\n", *pdwAttributes);
505
506     if (*pdwAttributes & ~dwSupportedAttr)
507     {
508         WARN ("attributes 0x%08x not implemented\n", (*pdwAttributes & ~dwSupportedAttr));
509         *pdwAttributes &= dwSupportedAttr;
510     }
511
512     has_guid = _ILGetGUIDPointer(pidl) != NULL;
513
514     dwAttributes = *pdwAttributes;
515
516     if (_ILIsDrive (pidl)) {
517         *pdwAttributes &= SFGAO_HASSUBFOLDER|SFGAO_FILESYSTEM|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR|
518             SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANLINK;
519     } else if (has_guid && HCR_GetFolderAttributes(pidl, &dwAttributes)) {
520         *pdwAttributes = dwAttributes;
521     } else if (_ILGetDataPointer (pidl)) {
522         dwAttributes = _ILGetFileAttributes (pidl, NULL, 0);
523
524         if (!dwAttributes && has_guid) {
525             WCHAR path[MAX_PATH];
526             STRRET strret;
527
528             /* File attributes are not present in the internal PIDL structure, so get them from the file system. */
529
530             HRESULT hr = IShellFolder_GetDisplayNameOf(psf, pidl, SHGDN_FORPARSING, &strret);
531
532             if (SUCCEEDED(hr)) {
533                 hr = StrRetToBufW(&strret, pidl, path, MAX_PATH);
534
535                 /* call GetFileAttributes() only for file system paths, not for parsing names like "::{...}" */
536                 if (SUCCEEDED(hr) && path[0]!=':')
537                     dwAttributes = GetFileAttributesW(path);
538             }
539         }
540
541         /* Set common attributes */
542         *pdwAttributes |= SFGAO_FILESYSTEM | SFGAO_DROPTARGET | SFGAO_HASPROPSHEET | SFGAO_CANDELETE | 
543                           SFGAO_CANRENAME | SFGAO_CANLINK | SFGAO_CANMOVE | SFGAO_CANCOPY;
544
545         if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY)
546             *pdwAttributes |=  (SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
547         else
548             *pdwAttributes &= ~(SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_FILESYSANCESTOR);
549
550         if (dwAttributes & FILE_ATTRIBUTE_HIDDEN)
551             *pdwAttributes |=  SFGAO_HIDDEN;
552         else
553             *pdwAttributes &= ~SFGAO_HIDDEN;
554
555         if (dwAttributes & FILE_ATTRIBUTE_READONLY)
556             *pdwAttributes |=  SFGAO_READONLY;
557         else
558             *pdwAttributes &= ~SFGAO_READONLY;
559
560         if (SFGAO_LINK & *pdwAttributes) {
561             char ext[MAX_PATH];
562
563             if (!_ILGetExtension(pidl, ext, MAX_PATH) || lstrcmpiA(ext, "lnk"))
564                 *pdwAttributes &= ~SFGAO_LINK;
565         }
566
567         if (SFGAO_HASSUBFOLDER & *pdwAttributes)
568         {
569             IShellFolder *psf2;
570             if (SUCCEEDED(IShellFolder_BindToObject(psf, pidl, 0, (REFIID)&IID_IShellFolder, (LPVOID *)&psf2)))
571             {
572                 IEnumIDList     *pEnumIL = NULL;
573                 if (SUCCEEDED(IShellFolder_EnumObjects(psf2, 0, SHCONTF_FOLDERS, &pEnumIL)))
574                 {
575                     if (IEnumIDList_Skip(pEnumIL, 1) != S_OK)
576                         *pdwAttributes &= ~SFGAO_HASSUBFOLDER;
577                     IEnumIDList_Release(pEnumIL);
578                 }
579                 IShellFolder_Release(psf2);
580             }
581         }
582     } else {
583         *pdwAttributes &= SFGAO_HASSUBFOLDER|SFGAO_FOLDER|SFGAO_FILESYSANCESTOR|SFGAO_DROPTARGET|SFGAO_HASPROPSHEET|SFGAO_CANRENAME|SFGAO_CANLINK;
584     }
585     TRACE ("-- 0x%08x\n", *pdwAttributes);
586     return S_OK;
587 }
588
589 /***********************************************************************
590  *  SHELL32_CompareIDs
591  */
592 HRESULT SHELL32_CompareIDs (IShellFolder * iface, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
593 {
594     int type1,
595       type2;
596     char szTemp1[MAX_PATH];
597     char szTemp2[MAX_PATH];
598     HRESULT nReturn;
599     LPITEMIDLIST firstpidl,
600       nextpidl1,
601       nextpidl2;
602     IShellFolder *psf;
603
604     /* test for empty pidls */
605     BOOL isEmpty1 = _ILIsDesktop (pidl1);
606     BOOL isEmpty2 = _ILIsDesktop (pidl2);
607
608     if (isEmpty1 && isEmpty2)
609         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 0 );
610     if (isEmpty1)
611         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, (WORD)-1 );
612     if (isEmpty2)
613         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 1 );
614
615     /* test for different types. Sort order is the PT_* constant */
616     type1 = _ILGetDataPointer (pidl1)->type;
617     type2 = _ILGetDataPointer (pidl2)->type;
618     if (type1 < type2)
619         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, (WORD)-1 );
620     else if (type1 > type2)
621         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 1 );
622
623     /* test for name of pidl */
624     _ILSimpleGetText (pidl1, szTemp1, MAX_PATH);
625     _ILSimpleGetText (pidl2, szTemp2, MAX_PATH);
626     nReturn = lstrcmpiA (szTemp1, szTemp2);
627     if (nReturn < 0)
628         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, (WORD)-1 );
629     else if (nReturn > 0)
630         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 1 );
631
632     /* test of complex pidls */
633     firstpidl = ILCloneFirst (pidl1);
634     nextpidl1 = ILGetNext (pidl1);
635     nextpidl2 = ILGetNext (pidl2);
636
637     /* optimizing: test special cases and bind not deeper */
638     /* the deeper shellfolder would do the same */
639     isEmpty1 = _ILIsDesktop (nextpidl1);
640     isEmpty2 = _ILIsDesktop (nextpidl2);
641
642     if (isEmpty1 && isEmpty2) {
643         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 0 );
644     } else if (isEmpty1) {
645         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, (WORD)-1 );
646     } else if (isEmpty2) {
647         return MAKE_HRESULT( SEVERITY_SUCCESS, 0, 1 );
648     /* optimizing end */
649     } else if (SUCCEEDED (IShellFolder_BindToObject (iface, firstpidl, NULL, &IID_IShellFolder, (LPVOID *) & psf))) {
650         nReturn = IShellFolder_CompareIDs (psf, lParam, nextpidl1, nextpidl2);
651         IShellFolder_Release (psf);
652     }
653     ILFree (firstpidl);
654     return nReturn;
655 }
656
657 /***********************************************************************
658  *  SHCreateLinks
659  *
660  *   Undocumented.
661  */
662 HRESULT WINAPI SHCreateLinks( HWND hWnd, LPCSTR lpszDir, LPDATAOBJECT lpDataObject,
663                               UINT uFlags, LPITEMIDLIST *lppidlLinks)
664 {
665     FIXME("%p %s %p %08x %p\n",hWnd,lpszDir,lpDataObject,uFlags,lppidlLinks);
666     return E_NOTIMPL;
667 }