[SHELL32] Fix ParseDisplayName Part 2 (#6740)

Follow-up to #6721. Reduce
SHParseDisplayName failures.
JIRA issue: CORE-19495
- Re-implement CFSFolder::ParseDisplayName
  method to validate the names.
- Add CFSFolder::_ParseSimple,
  CFSFolder::_GetFindDataFromName, and
  CFSFolder::_CreateIDListFromName helper
  methods.
- Add PathIsDotOrDotDotW, PathIsValidElement,
  PathIsDosDevice, and SHILAppend helper
  functions.
- Delete GetNextElementW and add
  Shell_NextElement function.
This commit is contained in:
Katayama Hirofumi MZ 2024-04-13 21:07:12 +09:00 committed by GitHub
parent f61e14f554
commit 7fdec96009
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 288 additions and 138 deletions

View file

@ -4,7 +4,7 @@
* PURPOSE: file system folder
* COPYRIGHT: Copyright 1997 Marcus Meissner
* Copyright 1998, 1999, 2002 Juergen Schmied
* Copyright 2019 Katayama Hirofumi MZ
* Copyright 2019-2024 Katayama Hirofumi MZ
* Copyright 2020 Mark Jansen (mark.jansen@reactos.org)
*/
@ -539,46 +539,6 @@ static const shvheader GenericSFHeader[] = {
#define GENERICSHELLVIEWCOLUMNS 6
/**************************************************************************
* SHELL32_CreatePidlFromBindCtx [internal]
*
* If the caller bound File System Bind Data, assume it is the
* find data for the path.
* This allows binding of paths that don't exist.
*/
LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path)
{
IFileSystemBindData *fsbd = NULL;
LPITEMIDLIST pidl = NULL;
IUnknown *param = NULL;
WIN32_FIND_DATAW wfd;
HRESULT r;
TRACE("%p %s\n", pbc, debugstr_w(path));
if (!pbc)
return NULL;
/* see if the caller bound File System Bind Data */
r = pbc->GetObjectParam((LPOLESTR)STR_FILE_SYS_BIND_DATA, &param);
if (FAILED(r))
return NULL;
r = param->QueryInterface(IID_PPV_ARG(IFileSystemBindData,&fsbd));
if (SUCCEEDED(r))
{
r = fsbd->GetFindData(&wfd);
if (SUCCEEDED(r))
{
lstrcpynW(&wfd.cFileName[0], path, MAX_PATH);
pidl = _ILCreateFromFindDataW(&wfd);
}
fsbd->Release();
}
return pidl;
}
static HRESULT SHELL32_GetCLSIDForDirectory(LPCWSTR pwszDir, LPCWSTR KeyName, CLSID* pclsidFolder)
{
WCHAR wszCLSIDValue[CHARS_IN_GUID];
@ -705,6 +665,91 @@ HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDW
return S_OK;
}
HRESULT CFSFolder::_ParseSimple(
_In_ LPOLESTR lpszDisplayName,
_Out_ WIN32_FIND_DATAW *pFind,
_Out_ LPITEMIDLIST *ppidl)
{
HRESULT hr;
LPWSTR pchNext = lpszDisplayName;
*ppidl = NULL;
LPITEMIDLIST pidl;
for (hr = S_OK; SUCCEEDED(hr); hr = SHILAppend(pidl, ppidl))
{
hr = Shell_NextElement(&pchNext, pFind->cFileName, _countof(pFind->cFileName), FALSE);
if (hr != S_OK)
break;
if (pchNext)
pFind->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
pidl = _ILCreateFromFindDataW(pFind);
if (!pidl)
{
hr = E_OUTOFMEMORY;
break;
}
}
if (SUCCEEDED(hr))
return S_OK;
if (*ppidl)
{
ILFree(*ppidl);
*ppidl = NULL;
}
return hr;
}
BOOL CFSFolder::_GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind)
{
WCHAR szPath[MAX_PATH];
lstrcpynW(szPath, m_sPathTarget, _countof(szPath));
PathAppendW(szPath, pszName);
HANDLE hFind = ::FindFirstFileW(szPath, pFind);
if (hFind == INVALID_HANDLE_VALUE)
return FALSE;
::FindClose(hFind);
return TRUE;
}
HRESULT CFSFolder::_CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl)
{
*ppidl = NULL;
if (PathIsDosDevice(pszName))
return HRESULT_FROM_WIN32(ERROR_BAD_DEVICE);
WIN32_FIND_DATAW FindData = { 0 };
HRESULT hr = S_OK;
if (attrs == ULONG_MAX) // Invalid attributes
{
if (!_GetFindDataFromName(pszName, &FindData))
hr = HRESULT_FROM_WIN32(::GetLastError());
}
else // Pretend as an item of attrs
{
StringCchCopyW(FindData.cFileName, _countof(FindData.cFileName), pszName);
FindData.dwFileAttributes = attrs;
}
if (FAILED(hr))
return hr;
*ppidl = _ILCreateFromFindDataW(&FindData);
if (!*ppidl)
return E_OUTOFMEMORY;
return S_OK;
}
/**************************************************************************
* CFSFolder::ParseDisplayName {SHELL32}
*
@ -736,13 +781,6 @@ HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
DWORD *pchEaten, PIDLIST_RELATIVE *ppidl,
DWORD *pdwAttributes)
{
HRESULT hr = E_INVALIDARG;
LPCWSTR szNext = NULL;
WCHAR szElement[MAX_PATH];
WCHAR szPath[MAX_PATH];
LPITEMIDLIST pidlTemp = NULL;
DWORD len;
TRACE ("(%p)->(HWND=%p,%p,%p=%s,%p,pidl=%p,%p)\n",
this, hwndOwner, pbc, lpszDisplayName, debugstr_w (lpszDisplayName),
pchEaten, ppidl, pdwAttributes);
@ -750,68 +788,85 @@ HRESULT WINAPI CFSFolder::ParseDisplayName(HWND hwndOwner,
if (!ppidl)
return E_INVALIDARG;
if (!lpszDisplayName)
{
*ppidl = NULL;
return E_INVALIDARG;
}
*ppidl = NULL;
if (pchEaten)
*pchEaten = 0; /* strange but like the original */
if (!lpszDisplayName)
return E_INVALIDARG;
if (*lpszDisplayName)
HRESULT hr;
WIN32_FIND_DATAW FindData;
if (SHIsFileSysBindCtx(pbc, &FindData) == S_OK)
{
/* get the next element */
szNext = GetNextElementW (lpszDisplayName, szElement, MAX_PATH);
pidlTemp = SHELL32_CreatePidlFromBindCtx(pbc, szElement);
if (pidlTemp != NULL)
CComHeapPtr<ITEMIDLIST> pidlTemp;
hr = _ParseSimple(lpszDisplayName, &FindData, &pidlTemp);
if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
{
/* We are creating an id list without ensuring that the items exist.
If we have a remaining path, this must be a folder.
We have to do it now because it is set as a file by default */
if (szNext)
{
pidlTemp->mkid.abID[0] = PT_FOLDER;
}
hr = S_OK;
LPCITEMIDLIST pidlLast = ILFindLastID(pidlTemp);
GetAttributesOf(1, &pidlLast, pdwAttributes);
}
else
{
/* build the full pathname to the element */
lstrcpynW(szPath, m_sPathTarget, MAX_PATH - 1);
PathAddBackslashW(szPath);
len = wcslen(szPath);
lstrcpynW(szPath + len, szElement, MAX_PATH - len);
/* get the pidl */
hr = _ILCreateFromPathW(szPath, &pidlTemp);
if (SUCCEEDED(hr))
*ppidl = pidlTemp.Detach();
}
else
{
INT cchElement = lstrlenW(lpszDisplayName) + 1;
LPWSTR pszElement = (LPWSTR)alloca(cchElement * sizeof(WCHAR));
LPWSTR pchNext = lpszDisplayName;
hr = Shell_NextElement(&pchNext, pszElement, cchElement, TRUE);
if (FAILED(hr))
return hr;
hr = _CreateIDListFromName(pszElement, ULONG_MAX, pbc, ppidl);
if (FAILED(hr))
{
if (pchNext) // Is there the next element?
{
// pszElement seems like a directory
if (_GetFindDataFromName(pszElement, &FindData) &&
(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
{
hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_DIRECTORY, pbc, ppidl);
}
}
else
{
// pszElement seems like a non-directory
if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) ||
hr == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND)) &&
(BindCtx_GetMode(pbc, 0) & STGM_CREATE))
{
// Pretend like a normal file
hr = _CreateIDListFromName(pszElement, FILE_ATTRIBUTE_NORMAL, pbc, ppidl);
}
}
}
if (SUCCEEDED(hr))
{
if (szNext && *szNext)
if (pchNext) // Is there next?
{
/* try to analyse the next element */
hr = SHELL32_ParseNextElement(this, hwndOwner, pbc,
&pidlTemp, (LPOLESTR) szNext, pchEaten, pdwAttributes);
CComPtr<IShellFolder> psfChild;
hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psfChild));
if (FAILED(hr))
return hr;
DWORD chEaten;
CComHeapPtr<ITEMIDLIST> pidlChild;
hr = psfChild->ParseDisplayName(hwndOwner, pbc, pchNext, &chEaten, &pidlChild,
pdwAttributes);
// Append pidlChild to ppidl
if (SUCCEEDED(hr))
hr = SHILAppend(pidlChild.Detach(), ppidl);
}
else
else if (pdwAttributes && *pdwAttributes)
{
/* it's the last element */
if (pdwAttributes && *pdwAttributes)
hr = SHELL32_GetFSItemAttributes(this, pidlTemp, pdwAttributes);
GetAttributesOf(1, (LPCITEMIDLIST*)ppidl, pdwAttributes);
}
}
}
if (SUCCEEDED(hr))
*ppidl = pidlTemp;
else
*ppidl = NULL;
TRACE("(%p)->(-- pidl=%p ret=0x%08x)\n", this, ppidl ? *ppidl : 0, hr);
return hr;

View file

@ -33,6 +33,14 @@ class CFSFolder :
HRESULT _CreateExtensionUIObject(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut);
HRESULT _GetDropTarget(LPCITEMIDLIST pidl, LPVOID *ppvOut);
HRESULT _GetIconHandler(LPCITEMIDLIST pidl, REFIID riid, LPVOID *ppvOut);
HRESULT _ParseSimple(
_In_ LPOLESTR lpszDisplayName,
_Out_ WIN32_FIND_DATAW *pFind,
_Out_ LPITEMIDLIST *ppidl);
BOOL _GetFindDataFromName(_In_ LPCWSTR pszName, _Out_ WIN32_FIND_DATAW *pFind);
HRESULT _CreateIDListFromName(LPCWSTR pszName, DWORD attrs, IBindCtx *pbc, LPITEMIDLIST *ppidl);
public:
CFSFolder();
~CFSFolder();

View file

@ -161,8 +161,9 @@ Shell_TranslateIDListAlias(
_In_ DWORD dwFlags);
BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName);
DWORD BindCtx_GetMode(_In_ IBindCtx *pbc, _In_ DWORD dwDefault);
BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid);
HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW **ppFindData);
HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *pFindData);
BOOL Shell_FailForceReturn(_In_ HRESULT hr);
EXTERN_C INT
@ -211,4 +212,9 @@ BindCtx_RegisterObjectParam(
_In_opt_ IUnknown *punk,
_Out_ LPBC *ppbc);
BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath);
BOOL PathIsValidElement(_In_ LPCWSTR pszPath);
BOOL PathIsDosDevice(_In_ LPCWSTR pszName);
HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl);
#endif /* _PRECOMP_H__ */

View file

@ -36,14 +36,18 @@ typedef struct {
#define GET_SHGDN_FOR(dwFlags) ((DWORD)dwFlags & (DWORD)0x0000FF00)
#define GET_SHGDN_RELATION(dwFlags) ((DWORD)dwFlags & (DWORD)0x000000FF)
LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut);
HRESULT
Shell_NextElement(
_Inout_ LPWSTR *ppch,
_Out_ LPWSTR pszOut,
_In_ INT cchOut,
_In_ BOOL bValidate);
HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc, LPITEMIDLIST * pidlInOut,
LPOLESTR szNext, DWORD * pEaten, DWORD * pdwAttributes);
HRESULT SHELL32_GetDisplayNameOfChild (IShellFolder2 * psf, LPCITEMIDLIST pidl, DWORD dwFlags, LPSTRRET strRet);
LPITEMIDLIST SHELL32_CreatePidlFromBindCtx(IBindCtx *pbc, LPCWSTR path);
HRESULT SHELL32_GetFSItemAttributes(IShellFolder * psf, LPCITEMIDLIST pidl, LPDWORD pdwAttributes);
HRESULT SHELL32_CompareDetails(IShellFolder2* isf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);

View file

@ -26,45 +26,54 @@
WINE_DEFAULT_DEBUG_CHANNEL(shell);
/***************************************************************************
* GetNextElement (internal function)
*
* Gets a part of a string till the first backslash.
*
* PARAMETERS
* pszNext [IN] string to get the element from
* pszOut [IN] pointer to buffer which receives string
* dwOut [IN] length of pszOut
*
* RETURNS
* LPSTR pointer to first, not yet parsed char
*/
LPCWSTR GetNextElementW (LPCWSTR pszNext, LPWSTR pszOut, DWORD dwOut)
HRESULT
Shell_NextElement(
_Inout_ LPWSTR *ppch,
_Out_ LPWSTR pszOut,
_In_ INT cchOut,
_In_ BOOL bValidate)
{
LPCWSTR pszTail = pszNext;
DWORD dwCopy;
*pszOut = UNICODE_NULL;
TRACE ("(%s %p 0x%08x)\n", debugstr_w (pszNext), pszOut, dwOut);
if (!*ppch)
return S_FALSE;
*pszOut = 0x0000;
HRESULT hr;
LPWSTR pchNext = wcschr(*ppch, L'\\');
if (pchNext)
{
if (*ppch < pchNext)
{
/* Get an element */
StringCchCopyNW(pszOut, cchOut, *ppch, pchNext - *ppch);
++pchNext;
if (!pszNext || !*pszNext)
return NULL;
if (!*pchNext)
pchNext = NULL; /* No next */
while (*pszTail && (*pszTail != (WCHAR) '\\'))
pszTail++;
hr = S_OK;
}
else /* Double backslashes found? */
{
pchNext = NULL;
hr = E_INVALIDARG;
}
}
else /* No more next */
{
StringCchCopyW(pszOut, cchOut, *ppch);
hr = S_OK;
}
dwCopy = pszTail - pszNext + 1;
lstrcpynW (pszOut, pszNext, (dwOut < dwCopy) ? dwOut : dwCopy);
*ppch = pchNext; /* Go next */
if (*pszTail)
pszTail++;
else
pszTail = NULL;
if (hr == S_OK && bValidate && !PathIsValidElement(pszOut))
{
*pszOut = UNICODE_NULL;
hr = E_INVALIDARG;
}
TRACE ("--(%s %s 0x%08x %p)\n", debugstr_w (pszNext), debugstr_w (pszOut), dwOut, pszTail);
return pszTail;
return hr;
}
HRESULT SHELL32_ParseNextElement (IShellFolder2 * psf, HWND hwndOwner, LPBC pbc,

View file

@ -9,6 +9,68 @@
WINE_DEFAULT_DEBUG_CHANNEL(shell);
BOOL PathIsDotOrDotDotW(_In_ LPCWSTR pszPath)
{
if (pszPath[0] != L'.')
return FALSE;
return !pszPath[1] || (pszPath[1] == L'.' && !pszPath[2]);
}
#define PATH_VALID_ELEMENT ( \
PATH_CHAR_CLASS_DOT | PATH_CHAR_CLASS_SEMICOLON | PATH_CHAR_CLASS_COMMA | \
PATH_CHAR_CLASS_SPACE | PATH_CHAR_CLASS_OTHER_VALID \
)
BOOL PathIsValidElement(_In_ LPCWSTR pszPath)
{
if (!*pszPath || PathIsDotOrDotDotW(pszPath))
return FALSE;
for (LPCWSTR pch = pszPath; *pch; ++pch)
{
if (!PathIsValidCharW(*pch, PATH_VALID_ELEMENT))
return FALSE;
}
return TRUE;
}
BOOL PathIsDosDevice(_In_ LPCWSTR pszName)
{
WCHAR szPath[MAX_PATH];
StringCchCopyW(szPath, _countof(szPath), pszName);
PathRemoveExtensionW(szPath);
if (lstrcmpiW(szPath, L"NUL") == 0 || lstrcmpiW(szPath, L"PRN") == 0 ||
lstrcmpiW(szPath, L"CON") == 0 || lstrcmpiW(szPath, L"AUX") == 0)
{
return TRUE;
}
if (_wcsnicmp(szPath, L"LPT", 3) == 0 || _wcsnicmp(szPath, L"COM", 3) == 0)
{
if ((L'0' <= szPath[3] && szPath[3] <= L'9') && szPath[4] == UNICODE_NULL)
return TRUE;
}
return FALSE;
}
HRESULT SHILAppend(_Inout_ LPITEMIDLIST pidl, _Inout_ LPITEMIDLIST *ppidl)
{
LPITEMIDLIST pidlOld = *ppidl;
if (!pidlOld)
{
*ppidl = pidl;
return S_OK;
}
HRESULT hr = SHILCombine(*ppidl, pidl, ppidl);
ILFree(pidlOld);
ILFree(pidl);
return hr;
}
static BOOL
OpenEffectiveToken(
_In_ DWORD DesiredAccess,
@ -49,6 +111,19 @@ BOOL BindCtx_ContainsObject(_In_ IBindCtx *pBindCtx, _In_ LPCWSTR pszName)
return TRUE;
}
DWORD BindCtx_GetMode(_In_ IBindCtx *pbc, _In_ DWORD dwDefault)
{
if (!pbc)
return dwDefault;
BIND_OPTS BindOpts = { sizeof(BindOpts) };
HRESULT hr = pbc->GetBindOptions(&BindOpts);
if (FAILED(hr))
return dwDefault;
return BindOpts.grfMode;
}
BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid)
{
if (!pbc)
@ -61,7 +136,7 @@ BOOL SHSkipJunctionBinding(_In_ IBindCtx *pbc, _In_ CLSID *pclsid)
return pclsid && SHSkipJunction(pbc, pclsid);
}
HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW **ppFindData)
HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *pFindData)
{
CComPtr<IUnknown> punk;
CComPtr<IFileSystemBindData> pBindData;
@ -72,17 +147,10 @@ HRESULT SHIsFileSysBindCtx(_In_ IBindCtx *pBindCtx, _Out_opt_ WIN32_FIND_DATAW *
if (FAILED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pBindData))))
return S_FALSE;
HRESULT hr = S_OK;
if (ppFindData)
{
*ppFindData = (WIN32_FIND_DATAW*)LocalAlloc(LPTR, sizeof(WIN32_FIND_DATAW));
if (*ppFindData)
pBindData->GetFindData(*ppFindData);
else
hr = E_OUTOFMEMORY;
}
if (pFindData)
pBindData->GetFindData(pFindData);
return hr;
return S_OK;
}
BOOL Shell_FailForceReturn(_In_ HRESULT hr)