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