📄 ftpfoldr.cpp
字号:
/*****************************************************************************\
FILE: ftpfoldr.h
DESCRIPTION:
This class inherits from CBaseFolder for a base ShellFolder implementation
of IShellFolder and overrides methods to give Ftp Specific features.
_UNDOCUMENTED_: The shell violates Apartment model threading
when doing background enumeration, so even though this DLL is
marked as Apartment model, IShellFolder and IEnumIDList must
be written with the free threading model with respect to anything
that IEnumIDList can do in the background.
This means that you'll see lots of ENTER_CRITICAL() and
LEAVE_CRITICAL() calls when your brain would say, "I don't
need to do that because I'm Apartment-model." I'll try to
point them out as they occur; look for the marker _MT_.
CAUTION! Internally, our property sheet handler also invokes
methods on CFtpFolder on the wrong thread, so it's not just the
shell that is weird.
\*****************************************************************************/
#include "priv.h"
#include "ftpfoldr.h"
#include "ftpurl.h"
#include "ftppidl.h"
#include "ftpicon.h"
#include "view.h"
#include "proxycache.h"
#include <idhidden.h>
#define FEATURE_SOFTLINK_SHORTCUT_ICONOVERLAY
// {A11501B3-6EA4-11d2-B679-006097DF5BD4} Private to msieftp.dll
const GUID IID_CFtpFolder = { 0xa11501b3, 0x6ea4, 0x11d2, { 0xb6, 0x79, 0x0, 0x60, 0x97, 0xdf, 0x5b, 0xd4 } };
/*****************************************************************************
*
* More const statics.
*
*****************************************************************************/
#pragma BEGIN_CONST_DATA
WORD c_wZero = 0; /* As promised in ftpview.h */
/*
* String separator used when building relative names.
*/
char c_szSlash[] = "/";
#pragma END_CONST_DATA
HRESULT CFtpFolder::_AddToUrlHistory(LPCWSTR pwzUrl)
{
HRESULT hr = S_OK;
if (!m_puhs)
hr = CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER, IID_IUrlHistoryStg, (void **)&m_puhs);
if (EVAL(m_puhs))
EVAL(SUCCEEDED(hr = m_puhs->AddUrl(pwzUrl, pwzUrl, 0)));
return hr;
}
/* Not yet needed
HRESULT CFtpFolder::AddToUrlHistory(LPCTSTR pszUrl)
{
return _AddToUrlHistory(wzUrl);
}
*/
HRESULT CFtpFolder::AddToUrlHistory(LPCITEMIDLIST pidl)
{
WCHAR wzUrl[MAX_URL_STRING];
HRESULT hr = UrlCreateFromPidlW(pidl, SHGDN_FORPARSING, wzUrl, ARRAYSIZE(wzUrl), (ICU_ESCAPE | ICU_USERNAME), TRUE);
// BUGBUG This is used across threads, so make it thread safe.
if (EVAL(SUCCEEDED(hr)))
EVAL(SUCCEEDED(hr = _AddToUrlHistory(wzUrl)));
return hr;
}
CWireEncoding * CFtpFolder::GetCWireEncoding(void)
{
// GetFtpDir() may return NULL when we aren't rooted in an FTP server.
CFtpDir * pfd = GetFtpDir();
CWireEncoding * pwe = NULL;
if (pfd)
{
pwe = pfd->GetFtpSite()->GetCWireEncoding();
pfd->Release();
}
return pwe;
}
HRESULT CFtpFolder::_FixQuestionablePidl(LPCITEMIDLIST pidl)
{
HRESULT hr = S_OK;
// BUGBUG: Nuke comment if we can.
// TODO: In the future, we may want to hit the server to
// disambiguate this.
/*
BOOL fIsDir = TRUE;
LPCSTR pszName = FtpPidl_GetLastItemName(pidl);
// Can we get the name?
if (EVAL(pszName))
{
// Is the file extension non-NULL? (Meaning it exists)
if ('\0' != *PathFindExtensionA(pszName))
fIsDir = FALSE; // Yes, so asume it's a file.
}
hr = FtpPidl_SetFileItemType((LPITEMIDLIST) pidl, fIsDir);
*/
return hr;
}
BOOL CFtpFolder::_IsServerVMS(LPCITEMIDLIST pidl)
{
BOOL fIsServerVMS = FALSE; // Assume TRUE
CFtpSite * pfs;
// Some caller's don't pass the Server ID so let's assume
// that they already made it past that point.
if (FtpID_IsServerItemID(pidl) &&
EVAL(SUCCEEDED(SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfs))))
{
fIsServerVMS = pfs->IsServerVMS();
pfs->Release();
}
return fIsServerVMS;
}
/****************************************************\
FUNCTION: _IsProxyBlockingSite
DESCRIPTION:
We need to detect if we cannot connect to the
site because the proxy is a CERN or CERN type proxy
that blocks ALL ftp access. If this is true, we
need to inform the user can fall all
IShellFolder::BindToObject() calls.
We will detect this case by doing the normal
WININET FTP InternetConnect(). If that returns
hr=0x80002EE7 (ERROR_INTERNET_NAME_NOT_RESOLVED)
then it could either be that the name doesn't exist,
or there is a CERN proxy blocking the call. We will
then try connect the CERN method which will tell us
if it's the proxy that is blocking us.
\****************************************************/
BOOL CFtpFolder::_IsProxyBlockingSite(LPCITEMIDLIST pidl)
{
BOOL fCacheResult;
BOOL fResult = TRUE; // Assume TRUE
CFtpDir * pfd;
if (ProxyCache_IsProxyBlocking(pidl, &fCacheResult))
return fCacheResult;
if (EVAL(pfd = GetFtpDirFromPidl(pidl)))
{
HRESULT hr = pfd->WithHint(NULL, NULL, NULL, NULL, NULL, this);
// WithHint() often fails if a CERN style proxy blocks REAL wininet
// access to the server. If the server name is a DNS name, the error
// returned will be ERROR_INTERNET_NAME_NOT_RESOLVED because that is
// what is returned by the CERN proxy. If the server name is an IP
// Address, wininet will skip the CERN proxy and try to find it on the
// intranet. If not found (because it's past the firewall), then,
// the attempt will timeout with ERROR_INTERNET_TIMEOUT. We need
// to treat this as a proxy block if and ONLY if the server name is an
// IP address because DNS names can timeout for other reasons. Us
// treating IP server name timeouts as proxy blocks is going to have
// to be tolerated because wininet won't handle this case. It happens
// very infrequently so I don't care that much.
//
// Some authentication proxies fail with: ERROR_INTERNET_CANNOT_CONNECT
// We would like to fall back in that case, however, that may include
// other cases like the server refusing to allow us in.
// (password or too many logged in users?)
// It would be great if ERROR_INTERNET_INVALID_PROXY_REQUEST or
// ERROR_INTERNET_CLIENT_AUTH_NOT_SETUP could be used.
//
if ((HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED) == hr) ||
(HRESULT_FROM_WIN32(ERROR_INTERNET_CANNOT_CONNECT) == hr) ||
((HRESULT_FROM_WIN32(ERROR_INTERNET_TIMEOUT) == hr) && !FtpPidl_IsDNSServerName(pidl)))
{
TCHAR szUrl[MAX_URL_STRING];
if (EVAL(SUCCEEDED(UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), ICU_ESCAPE | ICU_USERNAME, FALSE))))
{
HINTERNET hintTemp;
ASSERT(GetWininetSessionHandle());
// For Web Proxies, InternetOpenUrl should work. The problem is that
// some (Netscape's) don't work.
if (SUCCEEDED(InternetOpenUrlWrap(GetWininetSessionHandle(), TRUE, szUrl, NULL, 0, INTERNET_FLAG_NO_UI, NULL, &hintTemp)))
{
InternetCloseHandle(hintTemp); // This did work, so we must have a CERN proxy.
}
else
fResult = FALSE; // We aren't blocked by the proxy. (Wrong IP Addr or Name?)
}
}
else
fResult = FALSE; // We aren't blocked by the proxy.
// Cache the result since finding out is so expensive.
ProxyCache_SetProxyBlocking(pidl, fResult);
pfd->Release();
}
return fResult;
}
/*****************************************************************************
*
* InvalidateCache
*
* Invalidate the pflHfpl cache in the corresponding FtpDir.
*
* _MT_: Note that the background enumerator calls this, so it must be
* multithread-safe.
*
*****************************************************************************/
void CFtpFolder::InvalidateCache(void)
{
CFtpDir * pfd = GetFtpDir();
if (EVAL(pfd))
{
// Should have created one on the GetHint()
pfd->SetCache(0);
pfd->Release();
}
}
HRESULT CFtpFolder::_InitFtpSite(void)
{
HRESULT hr = S_OK;
if (!m_pfs) // If we don't already got one...
{
ENTERCRITICAL;
if (!m_pfs) // Did it get created while we were waiting
{
if (EVAL(GetPrivatePidlReference()))
hr = SiteCache_PidlLookup(GetPrivatePidlReference(), TRUE, m_pm, &m_pfs);
else
{
// Not initialized
TraceMsg(TF_FTPISF, "CFtpFolder_GetFtpDir(%08x) NOT INITED", this);
hr = E_FAIL;
}
}
LEAVECRITICAL;
}
return hr;
}
/*****************************************************************************\
FUNCTION: GetFtpDir
DESCRIPTION:
Say where our dir info is.
We allocate the pfd only if somebody actually needs it, because
Explorer does a lot of ILCompare's when you open a new folder,
each of which creates a new IShellFolder for the sole purpose
of calling CompareIDs. We don't want to go through all the
hubbub of creating an FtpDir and FtpSite when we don't need one.
_MT_: Note that the background enumerator calls this, so it must be
multithread-safe. In such case, however, the IShellFolder is
marked cBusy, so we don't have to worry about the this->pfd
getting wiped out behind our back by a change of identity.
\*****************************************************************************/
CFtpDir * CFtpFolder::GetFtpDir(void)
{
HRESULT hres = S_OK;
CFtpDir * pfd = NULL;
_InitFtpSite(); // Okay if it fails.
if (m_pfs)
hres = m_pfs->GetFtpDir(GetPrivatePidlReference(), &pfd);
return pfd;
}
CFtpDir * CFtpFolder::GetFtpDirFromPidl(LPCITEMIDLIST pidl)
{
HRESULT hres = S_OK;
CFtpDir * pfd = NULL;
CFtpSite * pfs = NULL;
hres = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfs);
if (pfs)
{
hres = pfs->GetFtpDir(pidl, &pfd);
pfs->Release();
}
return pfd;
}
CFtpDir * CFtpFolder::GetFtpDirFromUrl(LPCTSTR pszUrl)
{
LPITEMIDLIST pidl;
CFtpDir * pfd = NULL;
if (EVAL(SUCCEEDED(CreateFtpPidlFromUrl(pszUrl, GetCWireEncoding(), NULL, &pidl, m_pm, FALSE))))
{
_InitFtpSite(); // Okay if it fails.
m_pfs->GetFtpDir(pidl, &pfd);
ILFree(pidl);
}
return pfd;
}
/*****************************************************************************\
* GetItemAllocator
*
* Return today's pidl allocator.
\*****************************************************************************/
HRESULT CFtpFolder::GetItemAllocator(IMalloc **ppm)
{
HRESULT hr = E_FAIL;
*ppm = NULL;
if (EVAL(m_pm))
{
IUnknown_Set(ppm, m_pm);
hr = S_OK;
}
else
TraceMsg(TF_FTPISF, "CFtpFolder_GetItemAllocator(%08x) NOT INITED", this);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -