📄 ftpfoldr.cpp
字号:
{
CFtpSite * pfs;
hr = SiteCache_PidlLookup(pidlBaseDir, TRUE, m_pm, &pfs);
if (EVAL(SUCCEEDED(hr)))
{
LPCWIRESTR pwLastDirName;
// If we are using a hidden password, then ::GetDisplayNameOf() hands out
// these "ftp://user@server/dir/" URLs and the password is hidden. If
// :: ParseDisplayName() is given one of these URLs and we are currently in
// that server w/that user name, then :: ParseDisplayNameOf() needs to hand
// out a pidl with the correct hidden password cookie.
//
// Is pidlNav the same as GetPublicRootPidlReference() except pidlNav doesn't
// have a password. The same means that the servers match, and the user names
// match.
EVAL(SUCCEEDED(pfs->UpdateHiddenPassword(pidlBaseDir)));
// This is sneaky because pwLastDirName will point into them itemID
// that will be removed. The memory won't really be removed, it will
// just have the size set to zero.
pwLastDirName = FtpPidl_GetLastItemWireName(pidlBaseDir);
ILRemoveLastID(pidlBaseDir);
pfs->GetFtpDir(pidlBaseDir, &pfd);
if (pfd)
{
LPITEMIDLIST pidlFromCache = (LPITEMIDLIST) pfd->GetPidlFromWireName(pwLastDirName);
if (pidlFromCache)
{
// It was found, this means we were probably in ftp://serverX/Dir1/
// and the user entered something from that directory or another directory
// taht we have alread displayed to the user and it's in our cache.
*ppidl = ILCombine(pidlBaseDir, pidlFromCache);
ILFree(pidlFromCache);
hr = S_OK;
}
else
{
// There is one last thing we need to try, we need to detect if:
// 1) the URL has an URL path, and
// 2) the last item in the path doesn't have an extension and doesn't
// end in a slash ('/') to indicate it's a directory.
// If this case is true, we then need to find out if it is a directory
// or file by hitting the server. This is needed because by the time
// we bind, it's too late to fall back to the other thing (IEnumIDList).
// The one thing we might need to be careful about is AutoComplete because
// they may call :: ParseDisplayName() for every character a user types.
// This won't be so bad because it's on a background thread, asynch, and
// the first enum within a segment will cause the cache to be populated
// within a that segment so subsequent enums will be fast. The problem
// it that it's not uncommon for users to enter between 2 and 5 segments,
// and there would be 1 enum per segment.
hr = _ForPopulateAndEnum(pfd, pidlBaseDir, pszDisplayName, pwLastDirName, ppidl);
}
pfd->Release();
}
else
hr = E_FAIL;
pfs->Release();
}
else
hr = E_FAIL;
}
else
hr = E_FAIL;
ILFree(pidlBaseDir);
}
}
else
{
// Create a new enumeration object for the caller.
// PERF: log 2 (sizeof(m_pflHfpl))
*ppidl = (LPITEMIDLIST) pfd->GetPidlFromDisplayName(pszDisplayName);
if (*ppidl)
{
hr = S_OK;
}
else
{
// If we got here, the cache for this directory is populated.
// So if the name doesn't match, then either:
// 1) it doesn't exist,
// 2) the cache is out of date, or
// 3) it's multilevel, (like "dir1\dir2\dir3") or
// 4) It's a weird parsing token that our parent parse should have remoted, like "..", ".", "\", etc.
// We will assome our parent parse takes care of #4, and #2 isn't true.
// Is this multilevel? (Case #3)
if (!StrChr(pszDisplayName, TEXT('/')))
{
// No, so reject it and don't let our caller blindly accept it.
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
}
pfd->Release();
}
}
return hr;
}
HRESULT CFtpFolder::_GetBindCtx(IBindCtx ** ppbc)
{
HRESULT hr = CreateBindCtx(NULL, ppbc);
if (SUCCEEDED(hr)) // Can fail with out of memory
{
hr = (*ppbc)->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellIcon *)); // We want IUnknown, not IShellIcon, but this is to disambigiuate.
}
return hr;
}
HRESULT CFtpFolder::_GetLegacyURL(LPCITEMIDLIST pidl, IBindCtx * pbc, LPTSTR pszUrl, DWORD cchSize)
{
HRESULT hr = S_OK;
LPITEMIDLIST pidlWithVRoot;
// We now need to insert the virtual root path into the path section
// of the URL because the old FTP support doesn't follow the correct
// FTP URL spec that says that the virtual root needs to be left out
// of the URL.
hr = _ConvertPidlForRootedFix(pidl, &pidlWithVRoot);
if (SUCCEEDED(hr))
{
WCHAR wzFrag[MAX_PATH];
// SECURITY ISSUE: We need to get the URL w/password or it won't work, but
// this will expose the password publicly. We need a way for
// the real FTP URL Pidl to hide the password.
hr = UrlCreateFromPidlW(pidlWithVRoot, SHGDN_FORPARSING, pszUrl, cchSize, (ICU_ESCAPE | ICU_USERNAME), FALSE);
if (ILGetHiddenStringW(pidl, IDLHID_URLFRAGMENT, wzFrag, ARRAYSIZE(wzFrag))) // Add fragment if it exists.
UrlCombineW(pszUrl, wzFrag, pszUrl, &cchSize, 0);
ILFree(pidlWithVRoot);
}
return hr;
}
HRESULT CFtpFolder::_GetLegacyPidl(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlLegacy)
{
IBindCtx * pbc = NULL;
HRESULT hr = _GetBindCtx(&pbc);
*ppidlLegacy = NULL;
if (SUCCEEDED(hr)) // Can fail with out of memory.
{
WCHAR wzUrl[MAX_URL_STRING];
hr = _GetLegacyURL(pidl, pbc, wzUrl, ARRAYSIZE(wzUrl));
if (EVAL(SUCCEEDED(hr)))
{
TraceMsg(TF_FTPISF, "_BindToObject_OriginalFtpSupport() navigating to=%ls", wzUrl);
hr = IEParseDisplayNameWithBCW(CP_ACP, wzUrl, pbc, ppidlLegacy);
}
pbc->Release();
}
return hr;
}
HRESULT CFtpFolder::_InitLegacyShellFolder(IShellFolder * psfLegacy, LPCITEMIDLIST pidlInit)
{
IPersistFolder * ppf;
HRESULT hr = psfLegacy->QueryInterface(IID_IPersistFolder, (void **) &ppf);
if (SUCCEEDED(hr))
{
hr = ppf->Initialize(pidlInit);
ppf->Release();
}
return hr;
}
HRESULT CFtpFolder::_INetBindToObject(LPCITEMIDLIST pidl, IBindCtx * pbc, REFIID riid, LPVOID * ppvObj)
{
HRESULT hr = E_OUTOFMEMORY;
LPITEMIDLIST pidlFirst = GetPublicPidlRootIDClone();
if (pidlFirst)
{
IShellFolder * psfInternetSF;
hr = IEBindToObject(pidlFirst, &psfInternetSF);
if (EVAL(SUCCEEDED(hr)))
{
hr = _InitLegacyShellFolder(psfInternetSF, pidlFirst);
if (SUCCEEDED(hr))
{
// Note the I use ILNext() in order to skip past the Desktop ItemID,
// which is internal knowledge I should not have.
hr = psfInternetSF->BindToObject(_ILNext(pidl), pbc, riid, ppvObj);
}
psfInternetSF->Release();
}
ILFree(pidlFirst);
}
return hr;
}
HRESULT CFtpFolder::_BindToObject_OriginalFtpSupport(LPCITEMIDLIST pidl, REFIID riid, LPVOID * ppvObj)
{
LPBC pbc = NULL;
HRESULT hr = CreateBindCtx(NULL, &pbc);
if (EVAL(SUCCEEDED(hr)))
{
hr = pbc->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellIcon *)); // We want IUnknown, not IShellIcon, but this is to disambigiuate.
if (EVAL(SUCCEEDED(hr)))
{
LPITEMIDLIST pidlLegacy;
hr = _GetLegacyPidl(pidl, &pidlLegacy);
if (EVAL(SUCCEEDED(hr)))
{
hr = _INetBindToObject(pidlLegacy, pbc, riid, ppvObj);
ILFree(pidlLegacy);
}
}
pbc->Release();
}
ASSERT_POINTER_MATCHES_HRESULT(*ppvObj, hr);
return hr;
}
/*****************************************************************************\
FUNCTION: _IsValidPidlParameter
DESCRIPTION:
If this IShellFolder is rooted within our name space, then the pidl needs
to be a valid relative pidl. If we are rooted at the base of our name space,
then it needs to be a full pidl.
\*****************************************************************************/
BOOL CFtpFolder::_IsValidPidlParameter(LPCITEMIDLIST pidl)
{
BOOL fResult = TRUE;
if (IsRoot())
fResult = FtpPidl_IsValidFull(pidl);
else
fResult = FtpPidl_IsValidRelative(pidl);
return fResult;
}
/*****************************************************************************\
FUNCTION: IShellFolder::_BindToObject
DESCRIPTION:
We are now sure that we want to handle the support, so check what they
want.
\*****************************************************************************/
HRESULT CFtpFolder::_BindToObject(LPCITEMIDLIST pidl, LPCITEMIDLIST pidlFull, IBindCtx * pbc, REFIID riid, LPVOID * ppvObj)
{
HRESULT hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Indicate we want the old functionality to kick in.
if (IsEqualIID(riid, IID_IShellFolder) || IsEqualIID(riid, IID_IShellFolder2))
{
LPITEMIDLIST pidlTarget = ILCombine(GetPublicTargetPidlReference(), pidl);
LPITEMIDLIST pidlRoot = (GetFolderPidl() ? ILCombine(GetFolderPidl(), pidl) : NULL);
// There's no point trying to verify that it's folders all
// the way down, because it's the caller's job not to combine
// pidls randomly. Furthermore, they might not actually be marked
// as folders if we got them via ParseDisplayName.
// NOTE: Binding will succeed even if the pidl isn't valid on the
// server. In the future we may want to verify now so we
// don't hand out a IEnumIDList that won't work. Currently,
// IEnumIDList will fail and cause a renavigation if it can
// connect to the server in a different way (different username
// password pair). It would be better to do a redirect because
// the renavigation causes the bad entry in the navigation stack.
// We can't verify the item exists on the server if we have a WebProxy
// installed.
hr = CFtpFolder_Create(pidlTarget, pidlRoot, GetPidlByteOffset(), riid, ppvObj);
//TraceMsg(TF_FOLDER_SHRTCUTS, "CFtpFolder::_BindToObject() creating an FTP IShellFolder psf=%#08lx, pidlTarget=%#08lx, pidlRoot=%#08lx", *ppvObj, pidlTarget, pidlRoot);
if (EVAL(SUCCEEDED(hr)))
{
IUnknown * punk = (IUnknown *) *ppvObj;
IDelegateFolder * pdf;
hr = punk->QueryInterface(IID_IDelegateFolder, (LPVOID *) &pdf);
if (EVAL(SUCCEEDED(hr)))
{
if (EVAL(SUCCEEDED(hr)))
hr = pdf->SetItemAlloc(m_pm);
pdf->Release();
}
}
ILFree(pidlTarget);
ILFree(pidlRoot);
//TraceMsg(TF_FTPISF, "CFtpFolder::BindToObject() IID_IShellFolder hr=%#08lx", hr);
}
else if (IsEqualIID(riid, IID_IMoniker))
{
hr = _PidlToMoniker(pidlFull, (IMoniker **) ppvObj);
}
else if (IsEqualIID(riid, IID_CFtpFolder))
{
IShellFolder * psf;
// Nothing like a little recursion to keep the code clean.
// The fact that we use IID_IShellFolder guarantees the breaking
// of the recursion.
hr = BindToObject(pidl, pbc, IID_IShellFolder, (void **) &psf);
if (EVAL(SUCCEEDED(hr)))
{
hr = psf->QueryInterface(riid, ppvObj);
psf->Release();
}
}
else
{
TraceMsg(TF_FTPISF, "CFtpFolder::BindToObject() unsupported interface hr=E_NOINTERFACE");
*ppvObj = NULL;
hr = E_NOINTERFACE;
}
return hr;
}
/*****************************************************************************\
FUNCTION: _ConvertPidlForRootedFix
DESCRIPTION:
If an FTP URL has a login name, that login may root the user under a directory other than "/".
The FTP URL spec (RFC 1738) says that URL paths need to be relative to the rooted directory. For example:
If UserA's rooted account is in \usr\GroupA\UserA and the url is:
ftp://UserA:FooBar@server/test/file.txt, then the real path is \usr\GroupA\UserA\test\file.txt.
The problem is that the old FTP code doesn't respect this and requires:
ftp://UserA:FooBar@server/usr/GroupA/UserA/test/file.txt, so we fix that here.
PARAMETERS:
pidlBefore [IN]: This will be a public pidl to the item to navigate to.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -