1 /*
2  * PROJECT:     shell32
3  * LICENSE:     LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4  * PURPOSE:     file system folder
5  * COPYRIGHT:   Copyright 1997 Marcus Meissner
6  *              Copyright 1998, 1999, 2002 Juergen Schmied
7  *              Copyright 2019-2024 Katayama Hirofumi MZ
8  *              Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
9  */
10 
11 #include <precomp.h>
12 
13 WINE_DEFAULT_DEBUG_CHANNEL (shell);
14 
15 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder);
16 
GetItemFileName(PCUITEMID_CHILD pidl,LPWSTR Buf,UINT cchMax)17 static LPCWSTR GetItemFileName(PCUITEMID_CHILD pidl, LPWSTR Buf, UINT cchMax)
18 {
19     FileStructW* pDataW = _ILGetFileStructW(pidl);
20     if (pDataW)
21         return pDataW->wszName;
22     LPPIDLDATA pdata = _ILGetDataPointer(pidl);
23     if ((pdata->type & PT_VALUEW) == PT_VALUEW)
24         return (LPWSTR)pdata->u.file.szNames;
25     if (_ILSimpleGetTextW(pidl, Buf, cchMax))
26         return Buf;
27     return NULL;
28 }
29 
OpenKeyFromFileType(LPCWSTR pExtension,LPCWSTR KeyName)30 static HKEY OpenKeyFromFileType(LPCWSTR pExtension, LPCWSTR KeyName)
31 {
32     HKEY hkey;
33 
34     WCHAR FullName[MAX_PATH];
35     DWORD dwSize = sizeof(FullName);
36     wsprintf(FullName, L"%s\\%s", pExtension, KeyName);
37 
38     LONG res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
39     if (!res)
40         return hkey;
41 
42     res = RegGetValueW(HKEY_CLASSES_ROOT, pExtension, NULL, RRF_RT_REG_SZ, NULL, FullName, &dwSize);
43     if (res)
44     {
45         WARN("Failed to get progid for extension %S (%x), error %d\n", pExtension, pExtension, res);
46         return NULL;
47     }
48 
49     wcscat(FullName, L"\\");
50     wcscat(FullName, KeyName);
51 
52     hkey = NULL;
53     res = RegOpenKeyExW(HKEY_CLASSES_ROOT, FullName, 0, KEY_READ, &hkey);
54     if (res)
55         WARN("Could not open key %S for extension %S\n", KeyName, pExtension);
56 
57     return hkey;
58 }
59 
ExtensionFromPidl(PCUIDLIST_RELATIVE pidl,LPWSTR Buf,UINT cchMax)60 static LPCWSTR ExtensionFromPidl(PCUIDLIST_RELATIVE pidl, LPWSTR Buf, UINT cchMax)
61 {
62     if (!_ILIsValue(pidl))
63     {
64         ERR("Invalid pidl!\n");
65         return NULL;
66     }
67 
68     LPCWSTR name = GetItemFileName(pidl, Buf, cchMax);
69     LPCWSTR pExtension = name ? PathFindExtensionW(name) : NULL;
70     if (!pExtension || *pExtension == UNICODE_NULL)
71     {
72         WARN("No extension for %S!\n", name);
73         return NULL;
74     }
75     return pExtension;
76 }
77 
GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension,LPCWSTR KeyName,CLSID * pclsid)78 static HRESULT GetCLSIDForFileTypeFromExtension(LPCWSTR pExtension, LPCWSTR KeyName, CLSID* pclsid)
79 {
80     HKEY hkeyProgId = OpenKeyFromFileType(pExtension, KeyName);
81     if (!hkeyProgId)
82     {
83         WARN("OpenKeyFromFileType failed for key %S\n", KeyName);
84         return S_FALSE;
85     }
86 
87     WCHAR wszCLSIDValue[CHARS_IN_GUID];
88     DWORD dwSize = sizeof(wszCLSIDValue);
89     LONG res = RegGetValueW(hkeyProgId, NULL, NULL, RRF_RT_REG_SZ, NULL, wszCLSIDValue, &dwSize);
90     RegCloseKey(hkeyProgId);
91     if (res)
92     {
93         ERR("OpenKeyFromFileType succeeded but RegGetValueW failed\n");
94         return S_FALSE;
95     }
96 
97 #if 0
98     {
99         res = RegGetValueW(HKEY_LOCAL_MACHINE,
100                            L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Approved",
101                            wszCLSIDValue,
102                            RRF_RT_REG_SZ,
103                            NULL,
104                            NULL,
105                            NULL);
106         if (res != ERROR_SUCCESS)
107         {
108             ERR("DropHandler extension %S not approved\n", wszName);
109             return E_ACCESSDENIED;
110         }
111     }
112 #endif
113 
114     if (RegGetValueW(HKEY_LOCAL_MACHINE,
115                      L"Software\\Microsoft\\Windows\\CurrentVersion\\Shell Extensions\\Blocked",
116                      wszCLSIDValue,
117                      RRF_RT_REG_SZ,
118                      NULL,
119                      NULL,
120                      NULL) == ERROR_SUCCESS)
121     {
122         ERR("Extension %S  not approved\n", wszCLSIDValue);
123         return E_ACCESSDENIED;
124     }
125 
126     HRESULT hres = CLSIDFromString (wszCLSIDValue, pclsid);
127     if (FAILED_UNEXPECTEDLY(hres))
128         return hres;
129 
130     return S_OK;
131 }
132 
GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl,LPCWSTR KeyName,CLSID * pclsid)133 HRESULT GetCLSIDForFileType(PCUIDLIST_RELATIVE pidl, LPCWSTR KeyName, CLSID* pclsid)
134 {
135     WCHAR buf[256];
136     LPCWSTR pExtension = ExtensionFromPidl(pidl, buf, _countof(buf));
137     if (!pExtension)
138         return S_FALSE;
139 
140     return GetCLSIDForFileTypeFromExtension(pExtension, KeyName, pclsid);
141 }
142 
143 static HRESULT
getDefaultIconLocation(LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT uFlags)144 getDefaultIconLocation(LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT uFlags)
145 {
146     if (!HLM_GetIconW(IDI_SHELL_FOLDER - 1, szIconFile, cchMax, piIndex))
147     {
148         if (!HCR_GetIconW(L"Folder", szIconFile, NULL, cchMax, piIndex))
149         {
150             StringCchCopyW(szIconFile, cchMax, swShell32Name);
151             *piIndex = -IDI_SHELL_FOLDER;
152         }
153     }
154 
155     if (uFlags & GIL_OPENICON)
156     {
157         // next icon
158         if (*piIndex < 0)
159             (*piIndex)--;
160         else
161             (*piIndex)++;
162     }
163 
164     return S_OK;
165 }
166 
167 static BOOL
getShellClassInfo(LPCWSTR Entry,LPWSTR pszValue,DWORD cchValueLen,LPCWSTR IniFile)168 getShellClassInfo(LPCWSTR Entry, LPWSTR pszValue, DWORD cchValueLen, LPCWSTR IniFile)
169 {
170     return GetPrivateProfileStringW(L".ShellClassInfo", Entry, NULL, pszValue, cchValueLen, IniFile);
171 }
172 
173 static HRESULT
getIconLocationForFolder(IShellFolder * psf,PCITEMID_CHILD pidl,UINT uFlags,LPWSTR szIconFile,UINT cchMax,int * piIndex,UINT * pwFlags)174 getIconLocationForFolder(IShellFolder * psf, PCITEMID_CHILD pidl, UINT uFlags,
175                          LPWSTR szIconFile, UINT cchMax, int *piIndex, UINT *pwFlags)
176 {
177     DWORD dwFileAttrs;
178     WCHAR wszPath[MAX_PATH];
179     WCHAR wszIniFullPath[MAX_PATH];
180 
181     if (uFlags & GIL_DEFAULTICON)
182         goto Quit;
183 
184     // get path
185     if (!ILGetDisplayNameExW(psf, pidl, wszPath, 0))
186         goto Quit;
187     if (!PathIsDirectoryW(wszPath))
188         goto Quit;
189 
190     // read-only or system folder?
191     dwFileAttrs = _ILGetFileAttributes(ILFindLastID(pidl), NULL, 0);
192     if ((dwFileAttrs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) == 0)
193         goto Quit;
194 
195     // build the full path of ini file
196     StringCchCopyW(wszIniFullPath, _countof(wszIniFullPath), wszPath);
197     PathAppendW(wszIniFullPath, L"desktop.ini");
198 
199     WCHAR wszValue[MAX_PATH], wszTemp[MAX_PATH];
200     if (getShellClassInfo(L"IconFile", wszValue, _countof(wszValue), wszIniFullPath))
201     {
202         // wszValue --> wszTemp
203         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
204 
205         // wszPath + wszTemp --> wszPath
206         if (PathIsRelativeW(wszTemp))
207             PathAppendW(wszPath, wszTemp);
208         else
209             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
210 
211         // wszPath --> szIconFile
212         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
213 
214         *piIndex = GetPrivateProfileIntW(L".ShellClassInfo", L"IconIndex", 0, wszIniFullPath);
215         return S_OK;
216     }
217     else if (getShellClassInfo(L"CLSID", wszValue, _countof(wszValue), wszIniFullPath) &&
218              HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
219     {
220         return S_OK;
221     }
222     else if (getShellClassInfo(L"CLSID2", wszValue, _countof(wszValue), wszIniFullPath) &&
223              HCR_GetIconW(wszValue, szIconFile, NULL, cchMax, piIndex))
224     {
225         return S_OK;
226     }
227     else if (getShellClassInfo(L"IconResource", wszValue, _countof(wszValue), wszIniFullPath))
228     {
229         // wszValue --> wszTemp
230         ExpandEnvironmentStringsW(wszValue, wszTemp, _countof(wszTemp));
231 
232         // parse the icon location
233         *piIndex = PathParseIconLocationW(wszTemp);
234 
235         // wszPath + wszTemp --> wszPath
236         if (PathIsRelativeW(wszTemp))
237             PathAppendW(wszPath, wszTemp);
238         else
239             StringCchCopyW(wszPath, _countof(wszPath), wszTemp);
240 
241         // wszPath --> szIconFile
242         GetFullPathNameW(wszPath, cchMax, szIconFile, NULL);
243         return S_OK;
244     }
245 
246 Quit:
247     return getDefaultIconLocation(szIconFile, cchMax, piIndex, uFlags);
248 }
249 
CFSExtractIcon_CreateInstance(IShellFolder * psf,LPCITEMIDLIST pidl,REFIID iid,LPVOID * ppvOut)250 HRESULT CFSExtractIcon_CreateInstance(IShellFolder * psf, LPCITEMIDLIST pidl, REFIID iid, LPVOID * ppvOut)
251 {
252     CComPtr<IDefaultExtractIconInit> initIcon;
253     HRESULT hr;
254     int icon_idx = 0;
255     UINT flags = 0; // FIXME: Use it!
256     WCHAR wTemp[MAX_PATH] = L"";
257 
258     hr = SHCreateDefaultExtractIcon(IID_PPV_ARG(IDefaultExtractIconInit,&initIcon));
259     if (FAILED(hr))
260         return hr;
261 
262     if (_ILIsFolder (pidl))
263     {
264         if (SUCCEEDED(getIconLocationForFolder(psf,
265                           pidl, 0, wTemp, _countof(wTemp),
266                           &icon_idx,
267                           &flags)))
268         {
269             initIcon->SetNormalIcon(wTemp, icon_idx);
270             // FIXME: if/when getIconLocationForFolder does something for
271             //        GIL_FORSHORTCUT, code below should be uncommented. and
272             //        the following line removed.
273             initIcon->SetShortcutIcon(wTemp, icon_idx);
274         }
275         if (SUCCEEDED(getIconLocationForFolder(psf,
276                           pidl, GIL_DEFAULTICON, wTemp, _countof(wTemp),
277                           &icon_idx,
278                           &flags)))
279         {
280             initIcon->SetDefaultIcon(wTemp, icon_idx);
281         }
282         // if (SUCCEEDED(getIconLocationForFolder(psf,
283         //                   pidl, GIL_FORSHORTCUT, wTemp, _countof(wTemp),
284         //                   &icon_idx,
285         //                   &flags)))
286         // {
287         //     initIcon->SetShortcutIcon(wTemp, icon_idx);
288         // }
289         if (SUCCEEDED(getIconLocationForFolder(psf,
290                           pidl, GIL_OPENICON, wTemp, _countof(wTemp),
291                           &icon_idx,
292                           &flags)))
293         {
294             initIcon->SetOpenIcon(wTemp, icon_idx);
295         }
296     }
297     else
298     {
299         WCHAR extbuf[256];
300         LPCWSTR pExtension = ExtensionFromPidl(pidl, extbuf, _countof(extbuf));
301         HKEY hkey = pExtension ? OpenKeyFromFileType(pExtension, L"DefaultIcon") : NULL;
302         if (!hkey)
303             WARN("Could not open DefaultIcon key!\n");
304 
305         DWORD dwSize = sizeof(wTemp);
306         if (hkey && !SHQueryValueExW(hkey, NULL, NULL, NULL, wTemp, &dwSize))
307         {
308             WCHAR sNum[5];
309             if (ParseFieldW (wTemp, 2, sNum, 5))
310                 icon_idx = _wtoi(sNum);
311             else
312                 icon_idx = 0; /* sometimes the icon number is missing */
313             ParseFieldW (wTemp, 1, wTemp, MAX_PATH);
314             PathUnquoteSpacesW(wTemp);
315 
316             if (!wcscmp(L"%1", wTemp)) /* icon is in the file */
317             {
318                 ILGetDisplayNameExW(psf, pidl, wTemp, ILGDN_FORPARSING);
319                 icon_idx = 0;
320 
321                 INT ret = PrivateExtractIconsW(wTemp, 0, 0, 0, NULL, NULL, 0, 0);
322                 if (ret <= 0)
323                 {
324                     StringCbCopyW(wTemp, sizeof(wTemp), swShell32Name);
325                     if (lstrcmpiW(pExtension, L".exe") == 0 || lstrcmpiW(pExtension, L".scr") == 0)
326                         icon_idx = -IDI_SHELL_EXE;
327                     else
328                         icon_idx = -IDI_SHELL_DOCUMENT;
329                 }
330             }
331 
332             initIcon->SetNormalIcon(wTemp, icon_idx);
333         }
334         else
335         {
336             initIcon->SetNormalIcon(swShell32Name, 0);
337         }
338 
339         if (hkey)
340             RegCloseKey(hkey);
341     }
342 
343     return initIcon->QueryInterface(iid, ppvOut);
344 }
345 
346 /*
347 CFileSysEnum should do an initial FindFirstFile and do a FindNextFile as each file is
348 returned by Next. When the enumerator is created, it can do numerous additional operations
349 including formatting a drive, reconnecting a network share drive, and requesting a disk
350 be inserted in a removable drive.
351 */
352 
353 
354 class CFileSysEnum :
355     public CEnumIDListBase
356 {
357 private:
_AddFindResult(LPWSTR sParentDir,const WIN32_FIND_DATAW & FindData,DWORD dwFlags)358     HRESULT _AddFindResult(LPWSTR sParentDir, const WIN32_FIND_DATAW& FindData, DWORD dwFlags)
359     {
360 #define SUPER_HIDDEN (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
361 
362         // Does it need special handling because it is hidden?
363         if (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
364         {
365             DWORD dwHidden = FindData.dwFileAttributes & SUPER_HIDDEN;
366 
367             // Is it hidden, but are we not asked to include hidden?
368             if (dwHidden == FILE_ATTRIBUTE_HIDDEN && !(dwFlags & SHCONTF_INCLUDEHIDDEN))
369                 return S_OK;
370 
371             // Is it a system file, but are we not asked to include those?
372             if (dwHidden == SUPER_HIDDEN && !(dwFlags & SHCONTF_INCLUDESUPERHIDDEN))
373                 return S_OK;
374         }
375 
376         BOOL bDirectory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
377 
378         HRESULT hr;
379         if (bDirectory)
380         {
381             // Skip the current and parent directory nodes
382             if (!strcmpW(FindData.cFileName, L".") || !strcmpW(FindData.cFileName, L".."))
383                 return S_OK;
384 
385             // Does this directory need special handling?
386             if ((FindData.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
387             {
388                 WCHAR Tmp[MAX_PATH];
389                 CLSID clsidFolder;
390 
391                 PathCombineW(Tmp, sParentDir, FindData.cFileName);
392 
393                 hr = SHELL32_GetCLSIDForDirectory(Tmp, L"CLSID", &clsidFolder);
394                 if (SUCCEEDED(hr))
395                 {
396                     ERR("Got CLSID override for '%S'\n", Tmp);
397                 }
398             }
399         }
400         else
401         {
402             CLSID clsidFile;
403             LPWSTR pExtension = PathFindExtensionW(FindData.cFileName);
404             if (pExtension)
405             {
406                 // FIXME: Cache this?
407                 hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
408                 if (hr == S_OK)
409                 {
410                     HKEY hkey;
411                     hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
412                     if (SUCCEEDED(hr))
413                     {
414                         ::RegCloseKey(hkey);
415 
416                         // This should be presented as directory!
417                         bDirectory = TRUE;
418                         TRACE("Treating '%S' as directory!\n", FindData.cFileName);
419                     }
420                 }
421             }
422         }
423 
424         LPITEMIDLIST pidl = NULL;
425         if (bDirectory)
426         {
427             if (dwFlags & SHCONTF_FOLDERS)
428             {
429                 TRACE("(%p)->  (folder=%s)\n", this, debugstr_w(FindData.cFileName));
430                 pidl = _ILCreateFromFindDataW(&FindData);
431             }
432         }
433         else
434         {
435             if (dwFlags & SHCONTF_NONFOLDERS)
436             {
437                 TRACE("(%p)->  (file  =%s)\n", this, debugstr_w(FindData.cFileName));
438                 pidl = _ILCreateFromFindDataW(&FindData);
439             }
440         }
441 
442         if (pidl && !AddToEnumList(pidl))
443         {
444             FAILED_UNEXPECTEDLY(E_FAIL);
445             return E_FAIL;
446         }
447 
448         return S_OK;
449     }
450 
451 public:
CFileSysEnum()452     CFileSysEnum()
453     {
454 
455     }
456 
~CFileSysEnum()457     ~CFileSysEnum()
458     {
459     }
460 
Initialize(LPWSTR sPathTarget,DWORD dwFlags)461     HRESULT WINAPI Initialize(LPWSTR sPathTarget, DWORD dwFlags)
462     {
463         TRACE("(%p)->(path=%s flags=0x%08x)\n", this, debugstr_w(sPathTarget), dwFlags);
464 
465         if (!sPathTarget || !sPathTarget[0])
466         {
467             WARN("No path for CFileSysEnum, empty result!\n");
468             return S_FALSE;
469         }
470 
471         WCHAR szFindPattern[MAX_PATH];
472         HRESULT hr = StringCchCopyW(szFindPattern, _countof(szFindPattern), sPathTarget);
473         if (FAILED_UNEXPECTEDLY(hr))
474             return hr;
475 
476         /* FIXME: UNSAFE CRAP */
477         PathAddBackslashW(szFindPattern);
478 
479         hr = StringCchCatW(szFindPattern, _countof(szFindPattern), L"*.*");
480         if (FAILED_UNEXPECTEDLY(hr))
481             return hr;
482 
483 
484         WIN32_FIND_DATAW FindData;
485         HANDLE hFind = FindFirstFileW(szFindPattern, &FindData);
486         if (hFind == INVALID_HANDLE_VALUE)
487             return HRESULT_FROM_WIN32(GetLastError());
488 
489         do
490         {
491             hr = _AddFindResult(sPathTarget, FindData, dwFlags);
492 
493             if (FAILED_UNEXPECTEDLY(hr))
494                 break;
495 
496         } while(FindNextFileW(hFind, &FindData));
497 
498         if (SUCCEEDED(hr))
499         {
500             DWORD dwError = GetLastError();
501             if (dwError != ERROR_NO_MORE_FILES)
502             {
503                 hr = HRESULT_FROM_WIN32(dwError);
504                 FAILED_UNEXPECTEDLY(hr);
505             }
506         }
507         TRACE("(%p)->(hr=0x%08x)\n", this, hr);
508         FindClose(hFind);
509         return hr;
510     }
511 
512     BEGIN_COM_MAP(CFileSysEnum)
513         COM_INTERFACE_ENTRY_IID(IID_IEnumIDList, IEnumIDList)
514     END_COM_MAP()
515 };
516 
517 
518 /***********************************************************************
519  *   IShellFolder implementation
520  */
521 
CFSFolder()522 CFSFolder::CFSFolder()
523 {
524     m_pclsid = &CLSID_ShellFSFolder;
525     m_sPathTarget = NULL;
526     m_pidlRoot = NULL;
527     m_bGroupPolicyActive = 0;
528 }
529 
~CFSFolder()530 CFSFolder::~CFSFolder()
531 {
532     TRACE("-- destroying IShellFolder(%p)\n", this);
533 
534     SHFree(m_pidlRoot);
535     SHFree(m_sPathTarget);
536 }
537 
538 static const shvheader GenericSFHeader[] = {
539     {IDS_SHV_COLUMN_NAME, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 18},
540     {IDS_SHV_COLUMN_SIZE, SHCOLSTATE_TYPE_INT | SHCOLSTATE_ONBYDEFAULT, LVCFMT_RIGHT, 10},
541     {IDS_SHV_COLUMN_TYPE, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 10},
542     {IDS_SHV_COLUMN_MODIFIED, SHCOLSTATE_TYPE_DATE | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 15},
543     {IDS_SHV_COLUMN_ATTRIBUTES, SHCOLSTATE_TYPE_STR | SHCOLSTATE_ONBYDEFAULT, LVCFMT_LEFT, 8},
544     {IDS_SHV_COLUMN_COMMENTS, SHCOLSTATE_TYPE_STR | SHCOLSTATE_SLOW, LVCFMT_LEFT, 10},  // We don't currently support comments but CRegFolder does
545 };
546 
547 #define GENERICSHELLVIEWCOLUMNS _countof(GenericSFHeader)
548 
GetFSColumnDetails(UINT iColumn,SHELLDETAILS & sd)549 HRESULT CFSFolder::GetFSColumnDetails(UINT iColumn, SHELLDETAILS &sd)
550 {
551     if (iColumn >= _countof(GenericSFHeader))
552         return E_INVALIDARG;
553 
554     sd.fmt = GenericSFHeader[iColumn].fmt;
555     sd.cxChar = GenericSFHeader[iColumn].cxChar;
556     return SHSetStrRet(&sd.str, GenericSFHeader[iColumn].colnameid);
557 }
558 
GetDefaultFSColumnState(UINT iColumn,SHCOLSTATEF & csFlags)559 HRESULT CFSFolder::GetDefaultFSColumnState(UINT iColumn, SHCOLSTATEF &csFlags)
560 {
561     if (iColumn >= _countof(GenericSFHeader))
562         return E_INVALIDARG;
563 
564     csFlags = GenericSFHeader[iColumn].colstate;
565     return S_OK;
566 }
567 
SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir,LPCWSTR KeyName,CLSID * pclsidFolder)568 static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder)
569 {
570     WCHAR wszCLSIDValue[CHARS_IN_GUID];
571     WCHAR wszDesktopIni[MAX_PATH];
572 
573     StringCchCopyW(wszDesktopIni, MAX_PATH, pwszDir);
574     StringCchCatW(wszDesktopIni, MAX_PATH, L"\\desktop.ini");
575 
576     if (GetPrivateProfileStringW(L".ShellClassInfo",
577                                  KeyName,
578                                  L"",
579                                  wszCLSIDValue,
580                                  CHARS_IN_GUID,
581                                  wszDesktopIni))
582     {
583         return CLSIDFromString(wszCLSIDValue, pclsidFolder);
584     }
585     return E_FAIL;
586 }
587 
SHELL32_GetFSItemAttributes(IShellFolder * psf,LPCITEMIDLIST pidl,LPDWORD pdwAttributes)588 HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes)
589 {
590     DWORD dwFileAttributes, dwShellAttributes;
591 
592     if (!_ILIsFolder(pidl) && !_ILIsValue(pidl))
593     {
594         ERR("Got wrong type of pidl!\n");
595         *pdwAttributes &= SFGAO_CANLINK;
596         return S_OK;
597     }
598 
599     dwFileAttributes = _ILGetFileAttributes(pidl, NULL, 0);
600 
601     /* Set common attributes */
602     dwShellAttributes = SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK | SFGAO_CANRENAME | SFGAO_CANDELETE |
603                         SFGAO_HASPROPSHEET | SFGAO_DROPTARGET | SFGAO_FILESYSTEM;
604 
605     BOOL bDirectory = (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
606 
607     if (SFGAO_VALIDATE & *pdwAttributes)
608     {
609         STRRET strret;
610         LPWSTR path;
611         if (SUCCEEDED(psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &strret)) &&
612             SUCCEEDED(StrRetToStrW(&strret, pidl, &path)))
613         {
614             BOOL exists = PathFileExistsW(path);
615             SHFree(path);
616             if (!exists)
617                 return E_FAIL;
618         }
619     }
620 
621     if (!bDirectory)
622     {
623         // https://git.reactos.org/?p=reactos.git;a=blob;f=dll/shellext/zipfldr/res/zipfldr.rgs;hb=032b5aacd233cd7b83ab6282aad638c161fdc400#l9
624         WCHAR szFileName[MAX_PATH];
625         LPWSTR pExtension;
626         BOOL hasName = _ILSimpleGetTextW(pidl, szFileName, _countof(szFileName));
627         dwShellAttributes |= SFGAO_STREAM;
628 
629         // Vista+ feature: Hidden files with a leading tilde treated as super-hidden
630         // See https://devblogs.microsoft.com/oldnewthing/20170526-00/?p=96235
631         if (hasName && szFileName[0] == '~' && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
632             dwShellAttributes |= SFGAO_HIDDEN | SFGAO_SYSTEM;
633 
634         if (hasName && (pExtension = PathFindExtensionW(szFileName)))
635         {
636             CLSID clsidFile;
637             // FIXME: Cache this?
638             HRESULT hr = GetCLSIDForFileTypeFromExtension(pExtension, L"CLSID", &clsidFile);
639             if (hr == S_OK)
640             {
641                 HKEY hkey;
642                 hr = SHRegGetCLSIDKeyW(clsidFile, L"ShellFolder", FALSE, FALSE, &hkey);
643                 if (SUCCEEDED(hr))
644                 {
645                     DWORD dwAttributes = 0;
646                     DWORD dwSize = sizeof(dwAttributes);
647                     LSTATUS Status;
648 
649                     Status = SHRegGetValueW(hkey, NULL, L"Attributes", RRF_RT_REG_DWORD, NULL, &dwAttributes, &dwSize);
650                     if (Status == STATUS_SUCCESS)
651                     {
652                         TRACE("Augmenting '%S' with dwAttributes=0x%x\n", szFileName, dwAttributes);
653                         dwShellAttributes |= dwAttributes;
654                     }
655                     ::RegCloseKey(hkey);
656 
657                     // This should be presented as directory!
658                     bDirectory = (dwAttributes & SFGAO_FOLDER) != 0 || dwAttributes == 0;
659                     TRACE("Treating '%S' as directory!\n", szFileName);
660                 }
661             }
662         }
663     }
664 
665     // This is a directory
666     if (bDirectory)
667     {
668         dwShellAttributes |= (SFGAO_FOLDER | /*SFGAO_HASSUBFOLDER |*/ SFGAO_STORAGE);
669 
670         // Is this a real directory?
671         if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
672         {
673             dwShellAttributes |= (SFGAO_FILESYSANCESTOR | SFGAO_STORAGEANCESTOR);
674         }
675     }
676 
677     if (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
678         dwShellAttributes |= SFGAO_HIDDEN | SFGAO_GHOSTED;
679 
680     if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
681         dwShellAttributes |= SFGAO_READONLY;
682 
683     if (dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)
684         dwShellAttributes |= SFGAO_SYSTEM;
685 
686     if (dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)
687         dwShellAttributes |= SFGAO_COMPRESSED;
688 
689     if (dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)
690         dwShellAttributes |= SFGAO_ENCRYPTED;
691 
692     if ((SFGAO_NONENUMERATED & *pdwAttributes) && (dwFileAttributes & FILE_ATTRIBUTE_HIDDEN))
693     {
694         SHCONTF shcf = SHELL_GetDefaultFolderEnumSHCONTF();
695         if ((!(shcf & SHCONTF_INCLUDEHIDDEN)) || ((dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) && !(shcf & SHCONTF_INCLUDESUPERHIDDEN)))
696             dwShellAttributes |= SFGAO_NONENUMERATED;
697     }
698 
699     if (SFGAO_LINK & *pdwAttributes)
700     {
701         WCHAR ext[MAX_PATH];
702 
703         if (_ILGetExtension(pidl, ext, _countof(ext)) && !lstrcmpiW(ext, L"lnk"))
704             dwShellAttributes |= SFGAO_LINK;
705     }
706 
707     if (SFGAO_HASSUBFOLDER & *pdwAttributes)
708     {
709         CComPtr<IShellFolder> psf2;
710         if (SUCCEEDED(psf->BindToObject(pidl, 0, IID_PPV_ARG(IShellFolder, &psf2))))
711         {
712             CComPtr<IEnumIDList> pEnumIL;
713             if (SUCCEEDED(psf2->EnumObjects(0, SHCONTF_FOLDERS, &pEnumIL)))
714             {
715                 if (pEnumIL->Skip(1) == S_OK)
716                     dwShellAttributes |= SFGAO_HASSUBFOLDER;
717             }
718         }
719     }
720 
721     *pdwAttributes = dwShellAttributes;
722 
723     TRACE ("-- 0x%08x\n", *pdwAttributes);
724     return S_OK;
725 }
726 
727 // This method is typically invoked from SHSimpleIDListFromPathA/W.
_ParseSimple(_In_ LPOLESTR lpszDisplayName,_Inout_ WIN32_FIND_DATAW * pFind,_Out_ LPITEMIDLIST * ppidl)728 HRESULT CFSFolder::_ParseSimple(
729     _In_ LPOLESTR lpszDisplayName,
730     _Inout_ WIN32_FIND_DATAW *pFind,
731     _Out_ LPITEMIDLIST *ppidl)
732 {
733     HRESULT hr;
734     LPWSTR pchNext = lpszDisplayName;
735 
736     *ppidl = NULL;
737 
738     const DWORD finalattr = pFind->dwFileAttributes;
739     const DWORD finalsizelo = pFind->nFileSizeLow;
740     LPITEMIDLIST pidl;
741     for (hr = S_OK; SUCCEEDED(hr); hr = SHILAppend(pidl, ppidl))
742     {
743         hr = Shell_NextElement(&pchNext, pFind->cFileName, _countof(pFind->cFileName), FALSE);
744         if (hr != S_OK)
745             break;
746 
747         pFind->dwFileAttributes = pchNext ? FILE_ATTRIBUTE_DIRECTORY : finalattr;
748         pFind->nFileSizeLow = pchNext ? 0 : finalsizelo;
749         pidl = _ILCreateFromFindDataW(pFind);
750         if (!pidl)
751         {
752             hr = E_OUTOFMEMORY;
753             break;
754         }
755     }
756 
757     if (SUCCEEDED(hr))
758         return S_OK;
759 
760     if (*ppidl)
761     {
762         ILFree(*ppidl);
763         *ppidl = NULL;
764     }
765 
766     return hr;
767 }
768 
_GetFindDataFromName(_In_ LPCWSTR pszName,_Out_ WIN32_FIND_DATAW * pFind)769 BOOL CFSFolder::_GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind)
770 {
771     WCHAR szPath[MAX_PATH];
772     lstrcpynW(szPath, m_sPathTarget, _countof(szPath));
773     PathAppendW(szPath, pszName);
774 
775     HANDLE hFind = ::FindFirstFileW(szPath, pFind);
776     if (hFind == INVALID_HANDLE_VALUE)
777         return FALSE;
778 
779     ::FindClose(hFind);
780     return TRUE;
781 }
782 
_CreateIDListFromName(LPCWSTR pszName,DWORD attrs,IBindCtx * pbc,LPITEMIDLIST * ppidl)783 HRESULT CFSFolder::_CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl)
784 {
785     *ppidl = NULL;
786 
787     if (PathIsDosDevice(pszName))
788         return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE);
789 
790     WIN32_FIND_DATAW FindData = { 0 };
791 
792     HRESULT hr = S_OK;
793     if (attrs == ULONG_MAX) // Invalid attributes
794     {
795         if (!_GetFindDataFromName(pszName, &FindData))
796             hr = HRESULT_FROM_WIN32(::GetLastError());
797     }
798     else // Pretend as an item of attrs
799     {
800         StringCchCopyW(FindData.cFileName, _countof(FindData.cFileName), pszName);
801         FindData.dwFileAttributes = attrs;
802     }
803 
804     if (FAILED(hr))
805         return hr;
806 
807     *ppidl = _ILCreateFromFindDataW(&FindData);
808     if (!*ppidl)
809         return E_OUTOFMEMORY;
810 
811     return S_OK;
812 }
813 
814 /**************************************************************************
815 * CFSFolder::ParseDisplayName {SHELL32}
816 *
817 * Parse a display name.
818 *
819 * PARAMS
820 *  hwndOwner       [in]  Parent window for any message's
821 *  pbc             [in]  optional FileSystemBindData context
822 *  lpszDisplayName [in]  Unicode displayname.
823 *  pchEaten        [out] (unicode) characters processed
824 *  ppidl           [out] complex pidl to item
825 *  pdwAttributes   [out] items attributes
826 *
827 * NOTES
828 *  Every folder tries to parse only its own (the leftmost) pidl and creates a
829 *  subfolder to evaluate the remaining parts.
830 *  Now we can parse into namespaces implemented by shell extensions
831 *
832 *  Behaviour on win98: lpszDisplayName=NULL -> crash
833 *                      lpszDisplayName="" -> returns mycoputer-pidl
834 *
835 * FIXME
836 *    pdwAttributes is not set
837 *    pchEaten is not set like in windows
838 */
ParseDisplayName(HWND hwndOwner,LPBC pbc,LPOLESTR lpszDisplayName,DWORD * pchEaten,PIDLIST_RELATIVE * ppidl,DWORD * pdwAttributes)839 HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
840         LPBC pbc,
841         LPOLESTR lpszDisplayName,
842         DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
843         DWORD *pdwAttributes)
844 {
845     TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
846            this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
847            pchEaten, ppidl, pdwAttributes);
848 
849     if (!ppidl)
850         return E_INVALIDARG;
851 
852     *ppidl = NULL;
853 
854     if (!lpszDisplayName)
855         return E_INVALIDARG;
856 
857     HRESULT hr;
858     WIN32_FIND_DATAW FindData;
859     if (SHIsFileSysBindCtx(pbc, &FindData) == S_OK)
860     {
861         CComHeapPtr<ITEMIDLIST> pidlTemp;
862         hr = _ParseSimple(lpszDisplayName, &FindData, &pidlTemp);
863         if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
864         {
865             LPCITEMIDLIST pidlLast = ILFindLastID(pidlTemp);
866             GetAttributesOf(1, &pidlLast, pdwAttributes);
867         }
868 
869         if (SUCCEEDED(hr))
870             *ppidl = pidlTemp.Detach();
871     }
872     else
873     {
874         INT cchElement = lstrlenW(lpszDisplayName) + 1;
875         LPWSTR pszElement = (LPWSTR)_alloca(cchElement * sizeof(WCHAR));
876         LPWSTR pchNext = lpszDisplayName;
877         hr = Shell_NextElement(&pchNext, pszElement, cchElement, TRUE);
878         if (FAILED(hr))
879             return hr;
880 
881         hr = _CreateIDListFromName(pszElement, ULONG_MAX, pbc, ppidl);
882         if (FAILED(hr))
883         {
884             if (pchNext) // Is there the next element?
885             {
886                 // pszElement seems like a directory
887                 if (_GetFindDataFromName(pszElement, &FindData) &&
888                     (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
889                 {
890                     hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl);
891                 }
892             }
893             else
894             {
895                 // pszElement seems like a non-directory
896                 if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
897                      hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) &&
898                     (BindCtx_GetMode(pbc, 0) & STGM_CREATE))
899                 {
900                     // Pretend like a normal file
901                     hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_NORMAL, pbc, ppidl);
902                 }
903             }
904         }
905 
906         if (SUCCEEDED(hr))
907         {
908             if (pchNext) // Is there next?
909             {
910                 CComPtr<IShellFolder> psfChild;
911                 hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
912                 if (FAILED(hr))
913                     return hr;
914 
915                 DWORD chEaten;
916                 CComHeapPtr<ITEMIDLIST> pidlChild;
917                 hr = psfChild->ParseDisplayName(hwndOwner, pbc, pchNext, &chEaten, &pidlChild,
918                                                 pdwAttributes);
919 
920                 // Append pidlChild to ppidl
921                 if (SUCCEEDED(hr))
922                     hr = SHILAppend(pidlChild.Detach(), ppidl);
923             }
924             else if (pdwAttributes && *pdwAttributes)
925             {
926                 GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes);
927             }
928         }
929     }
930 
931     TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
932 
933     return hr;
934 }
935 
936 /**************************************************************************
937 * CFSFolder::EnumObjects
938 * PARAMETERS
939 *  HWND          hwndOwner,    //[in ] Parent Window
940 *  DWORD         grfFlags,     //[in ] SHCONTF enumeration mask
941 *  LPENUMIDLIST* ppenumIDList  //[out] IEnumIDList interface
942 */
EnumObjects(HWND hwndOwner,DWORD dwFlags,LPENUMIDLIST * ppEnumIDList)943 HRESULT WINAPI CFSFolder::EnumObjects(
944     HWND hwndOwner,
945     DWORD dwFlags,
946     LPENUMIDLIST *ppEnumIDList)
947 {
948     return ShellObjectCreatorInit<CFileSysEnum>(m_sPathTarget, dwFlags, IID_PPV_ARG(IEnumIDList, ppEnumIDList));
949 }
950 
951 /**************************************************************************
952 * CFSFolder::BindToObject
953 * PARAMETERS
954 *  LPCITEMIDLIST pidl,       //[in ] relative pidl to open
955 *  LPBC          pbc,        //[in ] optional FileSystemBindData context
956 *  REFIID        riid,       //[in ] Initial Interface
957 *  LPVOID*       ppvObject   //[out] Interface*
958 */
BindToObject(PCUIDLIST_RELATIVE pidl,LPBC pbc,REFIID riid,LPVOID * ppvOut)959 HRESULT WINAPI CFSFolder::BindToObject(
960     PCUIDLIST_RELATIVE pidl,
961     LPBC pbc,
962     REFIID riid,
963     LPVOID * ppvOut)
964 {
965     TRACE("(%p)->(pidl=%p,%p,%s,%p)\n", this, pidl, pbc,
966           shdebugstr_guid(&riid), ppvOut);
967 
968     CComPtr<IShellFolder> pSF;
969     HRESULT hr;
970 
971     if (!m_pidlRoot || !ppvOut || !pidl || !pidl->mkid.cb)
972     {
973         ERR("CFSFolder::BindToObject: Invalid parameters\n");
974         return E_INVALIDARG;
975     }
976 
977     /* Get the pidl data */
978     FileStruct* pData = &_ILGetDataPointer(pidl)->u.file;
979     FileStructW* pDataW = _ILGetFileStructW(pidl);
980 
981     if (!pDataW)
982     {
983         ERR("CFSFolder::BindToObject: Invalid pidl!\n");
984         return E_INVALIDARG;
985     }
986 
987     *ppvOut = NULL;
988 
989     /* Create the target folder info */
990     PERSIST_FOLDER_TARGET_INFO pfti = {0};
991     pfti.dwAttributes = -1;
992     pfti.csidl = -1;
993     PathCombineW(pfti.szTargetParsingName, m_sPathTarget, pDataW->wszName);
994 
995     /* Get the CLSID to bind to */
996     CLSID clsidFolder;
997     if (_ILIsFolder(pidl))
998     {
999         if ((pData->uFileAttribs & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1000         {
1001             hr = SHELL32_GetCLSIDForDirectory(pfti.szTargetParsingName, L"CLSID", &clsidFolder);
1002 
1003             if (SUCCEEDED(hr))
1004             {
1005                 /* We got a GUID from a desktop.ini, let's try it */
1006                 hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
1007                 if (SUCCEEDED(hr))
1008                 {
1009                     TRACE("-- returning (%p) %08x, (%s)\n", *ppvOut, hr, wine_dbgstr_guid(&clsidFolder));
1010                     return hr;
1011                 }
1012 
1013                 /* Something went wrong, re-try it with a normal ShellFSFolder */
1014                 ERR("CFSFolder::BindToObject: %s failed to bind, using fallback (0x%08x)\n", wine_dbgstr_guid(&clsidFolder), hr);
1015             }
1016         }
1017         /* No system folder or the custom class failed */
1018         clsidFolder = CLSID_ShellFSFolder;
1019     }
1020     else
1021     {
1022         hr = GetCLSIDForFileType(pidl, L"CLSID", &clsidFolder);
1023         if (hr == S_FALSE)
1024             return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
1025         if (hr != S_OK)
1026             return hr;
1027     }
1028 
1029     hr = SHELL32_BindToSF(m_pidlRoot, &pfti, pidl, &clsidFolder, riid, ppvOut);
1030     if (FAILED_UNEXPECTEDLY(hr))
1031         return hr;
1032 
1033     TRACE ("-- returning (%p) %08x\n", *ppvOut, hr);
1034 
1035     return S_OK;
1036 
1037 }
1038 
1039 /**************************************************************************
1040 *  CFSFolder::BindToStorage
1041 * PARAMETERS
1042 *  LPCITEMIDLIST pidl,       //[in ] complex pidl to store
1043 *  LPBC          pbc,        //[in ] reserved
1044 *  REFIID        riid,       //[in ] Initial storage interface
1045 *  LPVOID*       ppvObject   //[out] Interface* returned
1046 */
BindToStorage(PCUIDLIST_RELATIVE pidl,LPBC pbcReserved,REFIID riid,LPVOID * ppvOut)1047 HRESULT WINAPI CFSFolder::BindToStorage(
1048     PCUIDLIST_RELATIVE pidl,
1049     LPBC pbcReserved,
1050     REFIID riid,
1051     LPVOID *ppvOut)
1052 {
1053     FIXME("(%p)->(pidl=%p,%p,%s,%p) stub\n", this, pidl, pbcReserved,
1054           shdebugstr_guid (&riid), ppvOut);
1055 
1056     *ppvOut = NULL;
1057     return E_NOTIMPL;
1058 }
1059 
1060 /**************************************************************************
1061 *  CFSFolder::CompareIDs
1062 */
1063 
CompareIDs(LPARAM lParam,PCUIDLIST_RELATIVE pidl1,PCUIDLIST_RELATIVE pidl2)1064 HRESULT WINAPI CFSFolder::CompareIDs(LPARAM lParam,
1065                                      PCUIDLIST_RELATIVE pidl1,
1066                                      PCUIDLIST_RELATIVE pidl2)
1067 {
1068     LPPIDLDATA pData1 = _ILGetDataPointer(pidl1);
1069     LPPIDLDATA pData2 = _ILGetDataPointer(pidl2);
1070     FileStructW* pDataW1 = _ILGetFileStructW(pidl1);
1071     FileStructW* pDataW2 = _ILGetFileStructW(pidl2);
1072     BOOL bIsFolder1 = _ILIsFolder(pidl1);
1073     BOOL bIsFolder2 = _ILIsFolder(pidl2);
1074     LPWSTR pExtension1, pExtension2;
1075 
1076     if (!pDataW1 || !pDataW2 || LOWORD(lParam) >= GENERICSHELLVIEWCOLUMNS)
1077         return E_INVALIDARG;
1078 
1079     /* When sorting between a File and a Folder, the Folder gets sorted first */
1080     if (bIsFolder1 != bIsFolder2)
1081     {
1082         return MAKE_COMPARE_HRESULT(bIsFolder1 ? -1 : 1);
1083     }
1084 
1085     int result = 0;
1086     switch (LOWORD(lParam))
1087     {
1088         case SHFSF_COL_NAME:
1089             result = _wcsicmp(pDataW1->wszName, pDataW2->wszName);
1090             break;
1091         case SHFSF_COL_SIZE:
1092             if (pData1->u.file.dwFileSize > pData2->u.file.dwFileSize)
1093                 result = 1;
1094             else if (pData1->u.file.dwFileSize < pData2->u.file.dwFileSize)
1095                 result = -1;
1096             else
1097                 result = 0;
1098             break;
1099         case SHFSF_COL_TYPE:
1100             pExtension1 = PathFindExtensionW(pDataW1->wszName);
1101             pExtension2 = PathFindExtensionW(pDataW2->wszName);
1102             result = _wcsicmp(pExtension1, pExtension2);
1103             break;
1104         case SHFSF_COL_MDATE:
1105             result = pData1->u.file.uFileDate - pData2->u.file.uFileDate;
1106             if (result == 0)
1107                 result = pData1->u.file.uFileTime - pData2->u.file.uFileTime;
1108             break;
1109         case SHFSF_COL_FATTS:
1110             return SHELL32_CompareDetails(this, lParam, pidl1, pidl2);
1111         case SHFSF_COL_COMMENT:
1112             result = 0;
1113             break;
1114         default:
1115             if (_ILIsPidlSimple(pidl1) || _ILIsPidlSimple(pidl2))
1116                 ERR("Unknown column %u, can't compare\n", LOWORD(lParam));
1117             else
1118                 TRACE("Unknown column %u, deferring to the subfolder\n", LOWORD(lParam));
1119     }
1120 
1121     if (result == 0)
1122         return SHELL32_CompareChildren(this, lParam, pidl1, pidl2);
1123 
1124     return MAKE_COMPARE_HRESULT(result);
1125 }
1126 
1127 /**************************************************************************
1128 * CFSFolder::CreateViewObject
1129 */
CreateViewObject(HWND hwndOwner,REFIID riid,LPVOID * ppvOut)1130 HRESULT WINAPI CFSFolder::CreateViewObject(HWND hwndOwner,
1131         REFIID riid, LPVOID * ppvOut)
1132 {
1133     CComPtr<IShellView> pShellView;
1134     HRESULT hr = E_INVALIDARG;
1135 
1136     TRACE ("(%p)->(hwnd=%p,%s,%p)\n", this, hwndOwner, shdebugstr_guid (&riid),
1137            ppvOut);
1138 
1139     if (ppvOut)
1140     {
1141         *ppvOut = NULL;
1142 
1143         BOOL bIsDropTarget = IsEqualIID (riid, IID_IDropTarget);
1144         BOOL bIsShellView = !bIsDropTarget && IsEqualIID (riid, IID_IShellView);
1145 
1146         if (bIsDropTarget || bIsShellView)
1147         {
1148             DWORD dwDirAttributes = _ILGetFileAttributes(ILFindLastID(m_pidlRoot), NULL, 0);
1149 
1150             if ((dwDirAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY)) != 0)
1151             {
1152                 CLSID clsidFolder;
1153                 hr = SHELL32_GetCLSIDForDirectory(m_sPathTarget, L"UICLSID", &clsidFolder);
1154                 if (SUCCEEDED(hr))
1155                 {
1156                     CComPtr<IPersistFolder> spFolder;
1157                     hr = SHCoCreateInstance(NULL, &clsidFolder, NULL, IID_PPV_ARG(IPersistFolder, &spFolder));
1158                     if (!FAILED_UNEXPECTEDLY(hr))
1159                     {
1160                         hr = spFolder->Initialize(m_pidlRoot);
1161 
1162                         if (!FAILED_UNEXPECTEDLY(hr))
1163                         {
1164                             hr = spFolder->QueryInterface(riid, ppvOut);
1165                         }
1166                     }
1167                 }
1168                 else
1169                 {
1170                     // No desktop.ini, or no UICLSID present, continue as if nothing happened
1171                     hr = E_INVALIDARG;
1172                 }
1173             }
1174         }
1175 
1176         if (!SUCCEEDED(hr))
1177         {
1178             // No UICLSID handler found, continue to the default handlers
1179             if (bIsDropTarget)
1180             {
1181                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, ppvOut);
1182             }
1183             else if (IsEqualIID (riid, IID_IContextMenu))
1184             {
1185                 HKEY hKeys[16];
1186                 UINT cKeys = 0;
1187                 AddClassKeyToArray(L"Directory\\Background", hKeys, &cKeys);
1188 
1189                 DEFCONTEXTMENU dcm;
1190                 dcm.hwnd = hwndOwner;
1191                 dcm.pcmcb = this;
1192                 dcm.pidlFolder = m_pidlRoot;
1193                 dcm.psf = this;
1194                 dcm.cidl = 0;
1195                 dcm.apidl = NULL;
1196                 dcm.cKeys = cKeys;
1197                 dcm.aKeys = hKeys;
1198                 dcm.punkAssociationInfo = NULL;
1199                 hr = SHCreateDefaultContextMenu (&dcm, riid, ppvOut);
1200             }
1201             else if (bIsShellView)
1202             {
1203                 SFV_CREATE sfvparams = {sizeof(SFV_CREATE), this, NULL, this};
1204                 hr = SHCreateShellFolderView(&sfvparams, (IShellView**)ppvOut);
1205             }
1206             else
1207             {
1208                 hr = E_INVALIDARG;
1209             }
1210         }
1211     }
1212     TRACE("-- (%p)->(interface=%p)\n", this, ppvOut);
1213     return hr;
1214 }
1215 
1216 /**************************************************************************
1217 *  CFSFolder::GetAttributesOf
1218 *
1219 * PARAMETERS
1220 *  UINT            cidl,     //[in ] num elements in pidl array
1221 *  LPCITEMIDLIST*  apidl,    //[in ] simple pidl array
1222 *  ULONG*          rgfInOut) //[out] result array
1223 *
1224 */
GetAttributesOf(UINT cidl,PCUITEMID_CHILD_ARRAY apidl,DWORD * rgfInOut)1225 HRESULT WINAPI CFSFolder::GetAttributesOf(UINT cidl,
1226         PCUITEMID_CHILD_ARRAY apidl, DWORD * rgfInOut)
1227 {
1228     HRESULT hr = S_OK;
1229 
1230     if (!rgfInOut)
1231         return E_INVALIDARG;
1232     if (cidl && !apidl)
1233         return E_INVALIDARG;
1234 
1235     if (*rgfInOut == 0)
1236         *rgfInOut = ~0;
1237 
1238     if(cidl == 0)
1239     {
1240         LPCITEMIDLIST rpidl = ILFindLastID(m_pidlRoot);
1241 
1242         if (_ILIsFolder(rpidl) || _ILIsValue(rpidl))
1243         {
1244             SHELL32_GetFSItemAttributes(this, rpidl, rgfInOut);
1245         }
1246         else if (_ILIsDrive(rpidl))
1247         {
1248             IShellFolder *psfParent = NULL;
1249             hr = SHBindToParent(m_pidlRoot, IID_PPV_ARG(IShellFolder, &psfParent), NULL);
1250             if(SUCCEEDED(hr))
1251             {
1252                 hr = psfParent->GetAttributesOf(1, &rpidl, (SFGAOF*)rgfInOut);
1253                 psfParent->Release();
1254             }
1255         }
1256         else
1257         {
1258             ERR("Got and unknown pidl!\n");
1259         }
1260     }
1261     else
1262     {
1263         while (cidl > 0 && *apidl)
1264         {
1265             pdump(*apidl);
1266             if(_ILIsFolder(*apidl) || _ILIsValue(*apidl))
1267                 SHELL32_GetFSItemAttributes(this, *apidl, rgfInOut);
1268             else
1269                 ERR("Got an unknown type of pidl!!!\n");
1270             apidl++;
1271             cidl--;
1272         }
1273     }
1274     /* make sure SFGAO_VALIDATE is cleared, some apps depend on that */
1275     *rgfInOut &= ~SFGAO_VALIDATE;
1276 
1277     TRACE("-- result=0x%08x\n", *rgfInOut);
1278 
1279     return hr;
1280 }
1281 
1282 /**************************************************************************
1283 *  CFSFolder::GetUIObjectOf
1284 *
1285 * PARAMETERS
1286 *  HWND           hwndOwner, //[in ] Parent window for any output
1287 *  UINT           cidl,      //[in ] array size
1288 *  LPCITEMIDLIST* apidl,     //[in ] simple pidl array
1289 *  REFIID         riid,      //[in ] Requested Interface
1290 *  UINT*          prgfInOut, //[   ] reserved
1291 *  LPVOID*        ppvObject) //[out] Resulting Interface
1292 *
1293 * NOTES
1294 *  This function gets asked to return "view objects" for one or more (multiple
1295 *  select) items:
1296 *  The viewobject typically is an COM object with one of the following
1297 *  interfaces:
1298 *  IExtractIcon,IDataObject,IContextMenu
1299 *  In order to support icon positions in the default Listview your DataObject
1300 *  must implement the SetData method (in addition to GetData :) - the shell
1301 *  passes a barely documented "Icon positions" structure to SetData when the
1302 *  drag starts, and GetData's it if the drop is in another explorer window that
1303 *  needs the positions.
1304 */
GetUIObjectOf(HWND hwndOwner,UINT cidl,PCUITEMID_CHILD_ARRAY apidl,REFIID riid,UINT * prgfInOut,LPVOID * ppvOut)1305 HRESULT WINAPI CFSFolder::GetUIObjectOf(HWND hwndOwner,
1306                                         UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
1307                                         REFIID riid, UINT * prgfInOut,
1308                                         LPVOID * ppvOut)
1309 {
1310     LPVOID pObj = NULL;
1311     HRESULT hr = E_INVALIDARG;
1312 
1313     TRACE ("(%p)->(%p,%u,apidl=%p,%s,%p,%p)\n",
1314            this, hwndOwner, cidl, apidl, shdebugstr_guid (&riid), prgfInOut, ppvOut);
1315 
1316     if (ppvOut)
1317     {
1318         *ppvOut = NULL;
1319 
1320         if (cidl == 1 && _ILIsValue(apidl[0]))
1321         {
1322             hr = _CreateExtensionUIObject(apidl[0], riid, ppvOut);
1323             if(hr != S_FALSE)
1324                 return hr;
1325         }
1326 
1327         if (IsEqualIID(riid, IID_IContextMenu) && (cidl >= 1))
1328         {
1329             HKEY hKeys[16];
1330             UINT cKeys = 0;
1331             AddFSClassKeysToArray(cidl, apidl, hKeys, &cKeys);
1332 
1333             DEFCONTEXTMENU dcm;
1334             dcm.hwnd = hwndOwner;
1335             dcm.pcmcb = this;
1336             dcm.pidlFolder = m_pidlRoot;
1337             dcm.psf = this;
1338             dcm.cidl = cidl;
1339             dcm.apidl = apidl;
1340             dcm.cKeys = cKeys;
1341             dcm.aKeys = hKeys;
1342             dcm.punkAssociationInfo = NULL;
1343             hr = SHCreateDefaultContextMenu (&dcm, riid, &pObj);
1344         }
1345         else if (IsEqualIID (riid, IID_IDataObject))
1346         {
1347             if (cidl >= 1)
1348             {
1349                 hr = IDataObject_Constructor (hwndOwner, m_pidlRoot, apidl, cidl, TRUE, (IDataObject **)&pObj);
1350             }
1351             else
1352             {
1353                 hr = E_INVALIDARG;
1354             }
1355         }
1356         else if ((IsEqualIID (riid, IID_IExtractIconA) || IsEqualIID (riid, IID_IExtractIconW)) && (cidl == 1))
1357         {
1358             if (_ILIsValue(apidl[0]))
1359                 hr = _GetIconHandler(apidl[0], riid, (LPVOID*)&pObj);
1360             if (hr != S_OK)
1361                 hr = CFSExtractIcon_CreateInstance(this, apidl[0], riid, &pObj);
1362         }
1363         else if (IsEqualIID (riid, IID_IDropTarget))
1364         {
1365             /* only interested in attempting to bind to shell folders, not files (except exe), so if we fail, rebind to root */
1366             if (cidl != 1 || FAILED(hr = this->_GetDropTarget(apidl[0], (LPVOID*) &pObj)))
1367             {
1368                 hr = CFSDropTarget_CreateInstance(m_sPathTarget, riid, (LPVOID*) &pObj);
1369             }
1370         }
1371         else
1372             hr = E_NOINTERFACE;
1373 
1374         if (SUCCEEDED(hr) && !pObj)
1375             hr = E_OUTOFMEMORY;
1376 
1377         *ppvOut = pObj;
1378     }
1379     TRACE("(%p)->hr=0x%08x\n", this, hr);
1380     return hr;
1381 }
1382 
1383 /******************************************************************************
1384  * SHELL_FS_HideExtension [Internal]
1385  *
1386  * Query the registry if the filename extension of a given path should be
1387  * hidden.
1388  *
1389  * PARAMS
1390  *  szPath [I] Relative or absolute path of a file
1391  *
1392  * RETURNS
1393  *  TRUE, if the filename's extension should be hidden
1394  *  FALSE, otherwise.
1395  */
SHELL_FS_HideExtension(LPCWSTR szPath)1396 BOOL SHELL_FS_HideExtension(LPCWSTR szPath)
1397 {
1398     HKEY hKey;
1399     BOOL doHide = FALSE; /* The default value is FALSE (win98 at least) */
1400     LONG lError;
1401 
1402     doHide = !SHELL_GetSetting(SSF_SHOWEXTENSIONS, fShowExtensions);
1403 
1404     if (!doHide)
1405     {
1406         LPCWSTR DotExt = PathFindExtensionW(szPath);
1407         if (*DotExt != 0)
1408         {
1409             WCHAR classname[MAX_PATH];
1410             LONG classlen = sizeof(classname);
1411             lError = RegQueryValueW(HKEY_CLASSES_ROOT, DotExt, classname, &classlen);
1412             if (lError == ERROR_SUCCESS)
1413             {
1414                 lError = RegOpenKeyW(HKEY_CLASSES_ROOT, classname, &hKey);
1415                 if (lError == ERROR_SUCCESS)
1416                 {
1417                     lError = RegQueryValueExW(hKey, L"NeverShowExt", NULL, NULL, NULL, NULL);
1418                     if (lError == ERROR_SUCCESS)
1419                         doHide = TRUE;
1420 
1421                     RegCloseKey(hKey);
1422                 }
1423             }
1424         }
1425     }
1426 
1427     return doHide;
1428 }
1429 
SHELL_FS_ProcessDisplayFilename(LPWSTR szPath,DWORD dwFlags)1430 void SHELL_FS_ProcessDisplayFilename(LPWSTR szPath, DWORD dwFlags)
1431 {
1432     /*FIXME: MSDN also mentions SHGDN_FOREDITING which is not yet handled. */
1433     if (!(dwFlags & SHGDN_FORPARSING) &&
1434         ((dwFlags & SHGDN_INFOLDER) || (dwFlags == SHGDN_NORMAL))) {
1435             if (SHELL_FS_HideExtension(szPath) && szPath[0] != '.')
1436                 PathRemoveExtensionW(szPath);
1437     }
1438 }
1439 
1440 /**************************************************************************
1441 *  CFSFolder::GetDisplayNameOf
1442 *  Retrieves the display name for the specified file object or subfolder
1443 *
1444 * PARAMETERS
1445 *  LPCITEMIDLIST pidl,    //[in ] complex pidl to item
1446 *  DWORD         dwFlags, //[in ] SHGNO formatting flags
1447 *  LPSTRRET      lpName)  //[out] Returned display name
1448 *
1449 * FIXME
1450 *  if the name is in the pidl the ret value should be a STRRET_OFFSET
1451 */
1452 
GetDisplayNameOf(PCUITEMID_CHILD pidl,DWORD dwFlags,LPSTRRET strRet)1453 HRESULT WINAPI CFSFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl,
1454         DWORD dwFlags, LPSTRRET strRet)
1455 {
1456     if (!strRet)
1457         return E_INVALIDARG;
1458 
1459     /* If it is a complex pidl, let the child handle it */
1460     if (!_ILIsPidlSimple (pidl)) /* complex pidl */
1461     {
1462         return SHELL32_GetDisplayNameOfChild(this, pidl, dwFlags, strRet);
1463     }
1464     else if (pidl && !pidl->mkid.cb) /* empty pidl */
1465     {
1466         /* If it is an empty pidl return only the path of the folder */
1467         if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1468             (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1469             m_sPathTarget)
1470         {
1471             return SHSetStrRet(strRet, m_sPathTarget);
1472         }
1473         return E_INVALIDARG;
1474     }
1475 
1476     int len = 0;
1477     LPWSTR pszPath = (LPWSTR)CoTaskMemAlloc((MAX_PATH + 1) * sizeof(WCHAR));
1478     if (!pszPath)
1479         return E_OUTOFMEMORY;
1480 
1481     if ((GET_SHGDN_FOR(dwFlags) & SHGDN_FORPARSING) &&
1482         (GET_SHGDN_RELATION(dwFlags) != SHGDN_INFOLDER) &&
1483         m_sPathTarget)
1484     {
1485         lstrcpynW(pszPath, m_sPathTarget, MAX_PATH);
1486         PathAddBackslashW(pszPath);
1487         len = wcslen(pszPath);
1488     }
1489     _ILSimpleGetTextW(pidl, pszPath + len, MAX_PATH + 1 - len);
1490     if (!_ILIsFolder(pidl)) SHELL_FS_ProcessDisplayFilename(pszPath, dwFlags);
1491 
1492     strRet->uType = STRRET_WSTR;
1493     strRet->pOleStr = pszPath;
1494 
1495     TRACE ("-- (%p)->(%s)\n", this, strRet->uType == STRRET_CSTR ? strRet->cStr : debugstr_w(strRet->pOleStr));
1496     return S_OK;
1497 }
1498 
1499 /**************************************************************************
1500 *  CFSFolder::SetNameOf
1501 *  Changes the name of a file object or subfolder, possibly changing its item
1502 *  identifier in the process.
1503 *
1504 * PARAMETERS
1505 *  HWND          hwndOwner,  //[in ] Owner window for output
1506 *  LPCITEMIDLIST pidl,       //[in ] simple pidl of item to change
1507 *  LPCOLESTR     lpszName,   //[in ] the items new display name
1508 *  DWORD         dwFlags,    //[in ] SHGNO formatting flags
1509 *  LPITEMIDLIST* ppidlOut)   //[out] simple pidl returned
1510 */
SetNameOf(HWND hwndOwner,PCUITEMID_CHILD pidl,LPCOLESTR lpName,DWORD dwFlags,PITEMID_CHILD * pPidlOut)1511 HRESULT WINAPI CFSFolder::SetNameOf(
1512     HWND hwndOwner,
1513     PCUITEMID_CHILD pidl,
1514     LPCOLESTR lpName,
1515     DWORD dwFlags,
1516     PITEMID_CHILD *pPidlOut)
1517 {
1518     WCHAR szSrc[MAX_PATH + 1], szDest[MAX_PATH + 1];
1519     BOOL bIsFolder = _ILIsFolder (ILFindLastID (pidl));
1520 
1521     TRACE ("(%p)->(%p,pidl=%p,%s,%u,%p)\n", this, hwndOwner, pidl,
1522            debugstr_w (lpName), dwFlags, pPidlOut);
1523 
1524     FileStructW* pDataW = _ILGetFileStructW(pidl);
1525     if (!pDataW)
1526     {
1527         ERR("Got garbage pidl:\n");
1528         pdump_always(pidl);
1529         return E_INVALIDARG;
1530     }
1531 
1532     /* build source path */
1533     PathCombineW(szSrc, m_sPathTarget, pDataW->wszName); // FIXME: PIDLs without wide string
1534 
1535     /* build destination path */
1536     if (dwFlags == SHGDN_NORMAL || dwFlags & SHGDN_INFOLDER)
1537         PathCombineW(szDest, m_sPathTarget, lpName);
1538     else
1539         lstrcpynW(szDest, lpName, MAX_PATH);
1540 
1541     if (!(dwFlags & SHGDN_FORPARSING) && !bIsFolder && SHELL_FS_HideExtension(szSrc))
1542     {
1543         LPCWSTR ext = PathFindExtensionW(szSrc);
1544         if (*ext)
1545             PathAddExtensionW(szDest, ext);
1546     }
1547 
1548     HRESULT hr = S_OK;
1549     TRACE ("src=%s dest=%s\n", debugstr_w(szSrc), debugstr_w(szDest));
1550     if (!wcscmp(szSrc, szDest))
1551     {
1552         /* src and destination is the same */
1553         if (pPidlOut)
1554             hr = SHILClone(pidl, pPidlOut);
1555     }
1556     else if (MoveFileW(szSrc, szDest))
1557     {
1558         if (pPidlOut)
1559             hr = ParseDisplayName(hwndOwner, NULL, PathFindFileNameW(szDest), NULL, pPidlOut, NULL);
1560 
1561         SHChangeNotify(bIsFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_PATHW, szSrc, szDest);
1562     }
1563     else
1564     {
1565         hr = HResultFromWin32(GetLastError());
1566     }
1567     return hr;
1568 }
1569 
GetDefaultSearchGUID(GUID * pguid)1570 HRESULT WINAPI CFSFolder::GetDefaultSearchGUID(GUID * pguid)
1571 {
1572     FIXME ("(%p)\n", this);
1573     return E_NOTIMPL;
1574 }
1575 
EnumSearches(IEnumExtraSearch ** ppenum)1576 HRESULT WINAPI CFSFolder::EnumSearches(IEnumExtraSearch ** ppenum)
1577 {
1578     FIXME ("(%p)\n", this);
1579     return E_NOTIMPL;
1580 }
1581 
GetDefaultColumn(DWORD dwRes,ULONG * pSort,ULONG * pDisplay)1582 HRESULT WINAPI CFSFolder::GetDefaultColumn(DWORD dwRes,
1583         ULONG * pSort, ULONG * pDisplay)
1584 {
1585     TRACE ("(%p)\n", this);
1586 
1587     if (pSort)
1588         *pSort = 0;
1589     if (pDisplay)
1590         *pDisplay = 0;
1591 
1592     return S_OK;
1593 }
1594 
GetDefaultColumnState(UINT iColumn,SHCOLSTATEF * pcsFlags)1595 HRESULT WINAPI CFSFolder::GetDefaultColumnState(UINT iColumn,
1596         SHCOLSTATEF *pcsFlags)
1597 {
1598     TRACE ("(%p)\n", this);
1599 
1600     if (!pcsFlags)
1601         return E_INVALIDARG;
1602     else
1603         return GetDefaultFSColumnState(iColumn, *pcsFlags);
1604 }
1605 
GetDetailsEx(PCUITEMID_CHILD pidl,const SHCOLUMNID * pscid,VARIANT * pv)1606 HRESULT WINAPI CFSFolder::GetDetailsEx(PCUITEMID_CHILD pidl,
1607                                        const SHCOLUMNID * pscid, VARIANT * pv)
1608 {
1609     FIXME ("(%p)\n", this);
1610 
1611     return E_NOTIMPL;
1612 }
1613 
GetDetailsOf(PCUITEMID_CHILD pidl,UINT iColumn,SHELLDETAILS * psd)1614 HRESULT WINAPI CFSFolder::GetDetailsOf(PCUITEMID_CHILD pidl,
1615                                        UINT iColumn, SHELLDETAILS * psd)
1616 {
1617     HRESULT hr = E_FAIL;
1618 
1619     TRACE ("(%p)->(%p %i %p)\n", this, pidl, iColumn, psd);
1620 
1621     if (!psd || iColumn >= GENERICSHELLVIEWCOLUMNS)
1622         return E_INVALIDARG;
1623 
1624     if (!pidl)
1625     {
1626         /* the header titles */
1627         return GetFSColumnDetails(iColumn, *psd);
1628     }
1629     else
1630     {
1631         hr = S_OK;
1632         psd->str.uType = STRRET_WSTR;
1633         psd->str.pOleStr = (LPWSTR)CoTaskMemAlloc(MAX_PATH * sizeof(WCHAR));
1634         /* the data from the pidl */
1635         switch (iColumn)
1636         {
1637             case SHFSF_COL_NAME:
1638                 hr = GetDisplayNameOf (pidl, SHGDN_NORMAL | SHGDN_INFOLDER, &psd->str);
1639                 break;
1640             case SHFSF_COL_SIZE:
1641                 _ILGetFileSize(pidl, psd->str.pOleStr, MAX_PATH);
1642                 break;
1643             case SHFSF_COL_TYPE:
1644                 _ILGetFileType(pidl, psd->str.pOleStr, MAX_PATH);
1645                 break;
1646             case SHFSF_COL_MDATE:
1647                 _ILGetFileDate(pidl, psd->str.pOleStr, MAX_PATH);
1648                 break;
1649             case SHFSF_COL_FATTS:
1650                 _ILGetFileAttributes(pidl, psd->str.pOleStr, MAX_PATH);
1651                 break;
1652             case SHFSF_COL_COMMENT:
1653                 psd->str.pOleStr[0] = UNICODE_NULL; // TODO: Extract comment from .lnk files? desktop.ini?
1654                 break;
1655 #if DBG
1656             default:
1657                 ERR("Missing case for column %d\n", iColumn);
1658 #else
1659             DEFAULT_UNREACHABLE;
1660 #endif
1661         }
1662     }
1663 
1664     return hr;
1665 }
1666 
MapColumnToSCID(UINT column,SHCOLUMNID * pscid)1667 HRESULT WINAPI CFSFolder::MapColumnToSCID (UINT column,
1668         SHCOLUMNID * pscid)
1669 {
1670     FIXME ("(%p)\n", this);
1671     return E_NOTIMPL;
1672 }
1673 
1674 /************************************************************************
1675  * CFSFolder::GetClassID
1676  */
GetClassID(CLSID * lpClassId)1677 HRESULT WINAPI CFSFolder::GetClassID(CLSID * lpClassId)
1678 {
1679     TRACE ("(%p)\n", this);
1680 
1681     if (!lpClassId)
1682         return E_POINTER;
1683 
1684     *lpClassId = *m_pclsid;
1685 
1686     return S_OK;
1687 }
1688 
1689 /************************************************************************
1690  * CFSFolder::Initialize
1691  *
1692  * NOTES
1693  *  m_sPathTarget is not set. Don't know how to handle in a non rooted environment.
1694  */
Initialize(PCIDLIST_ABSOLUTE pidl)1695 HRESULT WINAPI CFSFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
1696 {
1697     WCHAR wszTemp[MAX_PATH];
1698 
1699     TRACE ("(%p)->(%p)\n", this, pidl);
1700 
1701     SHFree(m_pidlRoot);     /* free the old pidl */
1702     m_pidlRoot = ILClone (pidl); /* set my pidl */
1703 
1704     SHFree (m_sPathTarget);
1705     m_sPathTarget = NULL;
1706 
1707     /* set my path */
1708     if (SHGetPathFromIDListW (pidl, wszTemp))
1709     {
1710         int len = wcslen(wszTemp);
1711         m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1712         if (!m_sPathTarget)
1713             return E_OUTOFMEMORY;
1714         memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1715     }
1716 
1717     TRACE ("--(%p)->(%s)\n", this, debugstr_w(m_sPathTarget));
1718     return S_OK;
1719 }
1720 
1721 /**************************************************************************
1722  * CFSFolder::GetCurFolder
1723  */
GetCurFolder(PIDLIST_ABSOLUTE * pidl)1724 HRESULT WINAPI CFSFolder::GetCurFolder(PIDLIST_ABSOLUTE * pidl)
1725 {
1726     TRACE ("(%p)->(%p)\n", this, pidl);
1727 
1728     if (!pidl)
1729         return E_POINTER;
1730 
1731     *pidl = ILClone(m_pidlRoot);
1732     return S_OK;
1733 }
1734 
1735 /**************************************************************************
1736  * CFSFolder::InitializeEx
1737  *
1738  * FIXME: error handling
1739  */
InitializeEx(IBindCtx * pbc,LPCITEMIDLIST pidlRootx,const PERSIST_FOLDER_TARGET_INFO * ppfti)1740 HRESULT WINAPI CFSFolder::InitializeEx(IBindCtx * pbc, LPCITEMIDLIST pidlRootx,
1741                                        const PERSIST_FOLDER_TARGET_INFO * ppfti)
1742 {
1743     WCHAR wszTemp[MAX_PATH];
1744 
1745     TRACE("(%p)->(%p,%p,%p)\n", this, pbc, pidlRootx, ppfti);
1746     if (ppfti)
1747         TRACE("--%p %s %s 0x%08x 0x%08x\n",
1748               ppfti->pidlTargetFolder, debugstr_w (ppfti->szTargetParsingName),
1749               debugstr_w (ppfti->szNetworkProvider), ppfti->dwAttributes,
1750               ppfti->csidl);
1751 
1752     pdump (pidlRootx);
1753     if (ppfti && ppfti->pidlTargetFolder)
1754         pdump(ppfti->pidlTargetFolder);
1755 
1756     if (m_pidlRoot)
1757         __SHFreeAndNil(&m_pidlRoot);    /* free the old */
1758     if (m_sPathTarget)
1759         __SHFreeAndNil(&m_sPathTarget);
1760 
1761     /*
1762      * Root path and pidl
1763      */
1764     m_pidlRoot = ILClone(pidlRootx);
1765 
1766     /*
1767      *  the target folder is spezified in csidl OR pidlTargetFolder OR
1768      *  szTargetParsingName
1769      */
1770     if (ppfti)
1771     {
1772         if (ppfti->csidl != -1)
1773         {
1774             if (SHGetSpecialFolderPathW(0, wszTemp, ppfti->csidl,
1775                                         ppfti->csidl & CSIDL_FLAG_CREATE)) {
1776                 int len = wcslen(wszTemp);
1777                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1778                 if (!m_sPathTarget)
1779                     return E_OUTOFMEMORY;
1780                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1781             }
1782         }
1783         else if (ppfti->szTargetParsingName[0])
1784         {
1785             int len = wcslen(ppfti->szTargetParsingName);
1786             m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1787             if (!m_sPathTarget)
1788                 return E_OUTOFMEMORY;
1789             memcpy(m_sPathTarget, ppfti->szTargetParsingName,
1790                    (len + 1) * sizeof(WCHAR));
1791         }
1792         else if (ppfti->pidlTargetFolder)
1793         {
1794             if (SHGetPathFromIDListW(ppfti->pidlTargetFolder, wszTemp))
1795             {
1796                 int len = wcslen(wszTemp);
1797                 m_sPathTarget = (WCHAR *)SHAlloc((len + 1) * sizeof(WCHAR));
1798                 if (!m_sPathTarget)
1799                     return E_OUTOFMEMORY;
1800                 memcpy(m_sPathTarget, wszTemp, (len + 1) * sizeof(WCHAR));
1801             }
1802         }
1803     }
1804 
1805     TRACE("--(%p)->(target=%s)\n", this, debugstr_w(m_sPathTarget));
1806     pdump(m_pidlRoot);
1807     return (m_sPathTarget) ? S_OK : E_FAIL;
1808 }
1809 
GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)1810 HRESULT WINAPI CFSFolder::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO * ppfti)
1811 {
1812     FIXME("(%p)->(%p)\n", this, ppfti);
1813     ZeroMemory(ppfti, sizeof (*ppfti));
1814     return E_NOTIMPL;
1815 }
1816 
_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl,REFIID riid,LPVOID * ppvOut)1817 HRESULT CFSFolder::_CreateExtensionUIObject(PCUIDLIST_RELATIVE pidl, REFIID riid, LPVOID *ppvOut)
1818 {
1819     WCHAR buf[MAX_PATH];
1820 
1821     sprintfW(buf, L"ShellEx\\{%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
1822              riid.Data1, riid.Data2, riid.Data3,
1823              riid.Data4[0], riid.Data4[1], riid.Data4[2], riid.Data4[3],
1824              riid.Data4[4], riid.Data4[5], riid.Data4[6], riid.Data4[7]);
1825 
1826     CLSID clsid;
1827     HRESULT hr;
1828 
1829     hr = GetCLSIDForFileType(pidl, buf, &clsid);
1830     if (hr != S_OK)
1831         return hr;
1832 
1833     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1834     if (FAILED_UNEXPECTEDLY(hr))
1835         return hr;
1836 
1837     return S_OK;
1838 }
1839 
_GetDropTarget(LPCITEMIDLIST pidl,LPVOID * ppvOut)1840 HRESULT CFSFolder::_GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut)
1841 {
1842     HRESULT hr;
1843 
1844     TRACE("CFSFolder::_GetDropTarget entered\n");
1845 
1846     if (_ILIsFolder (pidl))
1847     {
1848         CComPtr<IShellFolder> psfChild;
1849         hr = this->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfChild));
1850         if (FAILED_UNEXPECTEDLY(hr))
1851             return hr;
1852 
1853         return psfChild->CreateViewObject(NULL, IID_IDropTarget, ppvOut);
1854     }
1855 
1856     CLSID clsid;
1857     hr = GetCLSIDForFileType(pidl, L"shellex\\DropHandler", &clsid);
1858     if (hr != S_OK)
1859         return hr;
1860 
1861     hr = _CreateShellExtInstance(&clsid, pidl, IID_IDropTarget, ppvOut);
1862     if (FAILED_UNEXPECTEDLY(hr))
1863         return S_FALSE;
1864 
1865     return S_OK;
1866 }
1867 
_GetIconHandler(LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)1868 HRESULT CFSFolder::_GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1869 {
1870     CLSID clsid;
1871     HRESULT hr;
1872 
1873     hr = GetCLSIDForFileType(pidl, L"shellex\\IconHandler", &clsid);
1874     if (hr != S_OK)
1875         return hr;
1876 
1877     hr = _CreateShellExtInstance(&clsid, pidl, riid, ppvOut);
1878     if (FAILED_UNEXPECTEDLY(hr))
1879         return S_FALSE;
1880 
1881     return S_OK;
1882 }
1883 
_CreateShellExtInstance(const CLSID * pclsid,LPCITEMIDLIST pidl,REFIID riid,LPVOID * ppvOut)1884 HRESULT CFSFolder::_CreateShellExtInstance(const CLSID *pclsid, LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut)
1885 {
1886     HRESULT hr;
1887     WCHAR wszPath[MAX_PATH];
1888 
1889     FileStructW* pDataW = _ILGetFileStructW(pidl);
1890     if (!pDataW)
1891     {
1892         ERR("Got garbage pidl\n");
1893         pdump_always(pidl);
1894         return E_INVALIDARG;
1895     }
1896 
1897     PathCombineW(wszPath, m_sPathTarget, pDataW->wszName);
1898 
1899     CComPtr<IPersistFile> pp;
1900     hr = SHCoCreateInstance(NULL, pclsid, NULL, IID_PPV_ARG(IPersistFile, &pp));
1901     if (FAILED_UNEXPECTEDLY(hr))
1902         return hr;
1903 
1904     pp->Load(wszPath, 0);
1905 
1906     hr = pp->QueryInterface(riid, ppvOut);
1907     if (hr != S_OK)
1908     {
1909         ERR("Failed to query for interface IID_IShellExtInit hr %x pclsid %s\n", hr, wine_dbgstr_guid(pclsid));
1910         return hr;
1911     }
1912     return hr;
1913 }
1914 
CallBack(IShellFolder * psf,HWND hwndOwner,IDataObject * pdtobj,UINT uMsg,WPARAM wParam,LPARAM lParam)1915 HRESULT WINAPI CFSFolder::CallBack(IShellFolder *psf, HWND hwndOwner, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
1916 {
1917     enum { IDC_PROPERTIES };
1918     /* no data object means no selection */
1919     if (!pdtobj)
1920     {
1921         if (uMsg == DFM_INVOKECOMMAND && wParam == IDC_PROPERTIES)
1922         {
1923             // Create an data object
1924             CComHeapPtr<ITEMID_CHILD> pidlChild(ILClone(ILFindLastID(m_pidlRoot)));
1925             CComHeapPtr<ITEMIDLIST> pidlParent(ILClone(m_pidlRoot));
1926             ILRemoveLastID(pidlParent);
1927 
1928             CComPtr<IDataObject> pDataObj;
1929             HRESULT hr = SHCreateDataObject(pidlParent, 1, &pidlChild.m_pData, NULL, IID_PPV_ARG(IDataObject, &pDataObj));
1930             if (!FAILED_UNEXPECTEDLY(hr))
1931             {
1932                 // Ask for a title to display
1933                 CComHeapPtr<WCHAR> wszName;
1934                 if (!FAILED_UNEXPECTEDLY(SHGetNameFromIDList(m_pidlRoot, SIGDN_PARENTRELATIVEPARSING, &wszName)))
1935                 {
1936                     BOOL bSuccess = SH_ShowPropertiesDialog(wszName, pDataObj);
1937                     if (!bSuccess)
1938                         ERR("SH_ShowPropertiesDialog failed\n");
1939                 }
1940             }
1941             return hr;
1942         }
1943         else if (uMsg == DFM_MERGECONTEXTMENU)
1944         {
1945             QCMINFO *pqcminfo = (QCMINFO *)lParam;
1946             HMENU hpopup = CreatePopupMenu();
1947             _InsertMenuItemW(hpopup, 0, TRUE, IDC_PROPERTIES, MFT_STRING, MAKEINTRESOURCEW(IDS_PROPERTIES), MFS_ENABLED);
1948             pqcminfo->idCmdFirst = Shell_MergeMenus(pqcminfo->hmenu, hpopup, pqcminfo->indexMenu, pqcminfo->idCmdFirst, pqcminfo->idCmdLast, MM_ADDSEPARATOR);
1949             DestroyMenu(hpopup);
1950             return S_OK;
1951         }
1952     }
1953     return SHELL32_DefaultContextMenuCallBack(psf, pdtobj, uMsg);
1954 }
1955 
DoLoadPicture(LPCWSTR pszFileName)1956 static HBITMAP DoLoadPicture(LPCWSTR pszFileName)
1957 {
1958     // create stream from file
1959     HRESULT hr;
1960     CComPtr<IStream> pStream;
1961     hr = SHCreateStreamOnFileEx(pszFileName, STGM_READ, FILE_ATTRIBUTE_NORMAL,
1962                                 FALSE, NULL, &pStream);
1963     if (FAILED(hr))
1964         return NULL;
1965 
1966     // load the picture
1967     HBITMAP hbm = NULL;
1968     CComPtr<IPicture> pPicture;
1969     OleLoadPicture(pStream, 0, FALSE, IID_IPicture, (LPVOID *)&pPicture);
1970 
1971     // get the bitmap handle
1972     if (pPicture)
1973     {
1974         pPicture->get_Handle((OLE_HANDLE *)&hbm);
1975 
1976         // copy the bitmap handle
1977         hbm = (HBITMAP)CopyImage(hbm, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
1978     }
1979 
1980     return hbm;
1981 }
1982 
GetCustomViewInfo(ULONG unknown,SFVM_CUSTOMVIEWINFO_DATA * data)1983 HRESULT WINAPI CFSFolder::GetCustomViewInfo(ULONG unknown, SFVM_CUSTOMVIEWINFO_DATA *data)
1984 {
1985     if (data == NULL)
1986     {
1987         return E_POINTER;
1988     }
1989     if (data->cbSize != sizeof(*data))
1990     {
1991         // NOTE: You have to set the cbData member before SFVM_GET_CUSTOMVIEWINFO call.
1992         return E_INVALIDARG;
1993     }
1994 
1995     data->hbmBack = NULL;
1996     data->clrText = CLR_INVALID;
1997     data->clrTextBack = CLR_INVALID;
1998 
1999     WCHAR szPath[MAX_PATH], szIniFile[MAX_PATH];
2000 
2001     // does the folder exists?
2002     if (!SHGetPathFromIDListW(m_pidlRoot, szPath) || !PathIsDirectoryW(szPath))
2003     {
2004         return E_INVALIDARG;
2005     }
2006 
2007     // don't use custom view in network path for security
2008     if (PathIsNetworkPath(szPath))
2009     {
2010         return E_ACCESSDENIED;
2011     }
2012 
2013     // build the ini file path
2014     StringCchCopyW(szIniFile, _countof(szIniFile), szPath);
2015     PathAppend(szIniFile, L"desktop.ini");
2016 
2017     static LPCWSTR TheGUID = L"{BE098140-A513-11D0-A3A4-00C04FD706EC}";
2018     static LPCWSTR Space = L" \t\n\r\f\v";
2019 
2020     // get info from ini file
2021     WCHAR szImage[MAX_PATH], szText[64];
2022 
2023     // load the image
2024     szImage[0] = UNICODE_NULL;
2025     GetPrivateProfileStringW(TheGUID, L"IconArea_Image", L"", szImage, _countof(szImage), szIniFile);
2026     if (szImage[0])
2027     {
2028         StrTrimW(szImage, Space);
2029         if (PathIsRelativeW(szImage))
2030         {
2031             PathAppendW(szPath, szImage);
2032             StringCchCopyW(szImage, _countof(szImage), szPath);
2033         }
2034         data->hbmBack = DoLoadPicture(szImage);
2035     }
2036 
2037     // load the text color
2038     szText[0] = UNICODE_NULL;
2039     GetPrivateProfileStringW(TheGUID, L"IconArea_Text", L"", szText, _countof(szText), szIniFile);
2040     if (szText[0])
2041     {
2042         StrTrimW(szText, Space);
2043 
2044         LPWSTR pchEnd = NULL;
2045         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2046 
2047         if (pchEnd && !*pchEnd)
2048             data->clrText = cr;
2049     }
2050 
2051     // load the text background color
2052     szText[0] = UNICODE_NULL;
2053     GetPrivateProfileStringW(TheGUID, L"IconArea_TextBackground", L"", szText, _countof(szText), szIniFile);
2054     if (szText[0])
2055     {
2056         StrTrimW(szText, Space);
2057 
2058         LPWSTR pchEnd = NULL;
2059         COLORREF cr = (wcstol(szText, &pchEnd, 0) & 0xFFFFFF);
2060 
2061         if (pchEnd && !*pchEnd)
2062             data->clrTextBack = cr;
2063     }
2064 
2065     if (data->hbmBack != NULL || data->clrText != CLR_INVALID || data->clrTextBack != CLR_INVALID)
2066         return S_OK;
2067 
2068     return E_FAIL;
2069 }
2070 
MessageSFVCB(UINT uMsg,WPARAM wParam,LPARAM lParam)2071 HRESULT WINAPI CFSFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
2072 {
2073     HRESULT hr = E_NOTIMPL;
2074     switch (uMsg)
2075     {
2076     case SFVM_GET_CUSTOMVIEWINFO:
2077         hr = GetCustomViewInfo((ULONG)wParam, (SFVM_CUSTOMVIEWINFO_DATA *)lParam);
2078         break;
2079     case SFVM_GETCOMMANDDIR:
2080         if (m_sPathTarget)
2081             hr = StringCchCopyW((PWSTR)lParam, wParam, m_sPathTarget);
2082         break;
2083     }
2084     return hr;
2085 }
2086