📄 ftpsite.cpp
字号:
/*****************************************************************************\
* ftpsite.cpp - Internal object that manages a single FTP site
\*****************************************************************************/
#include "priv.h"
#include "ftpsite.h"
#include "ftpinet.h"
#include "ftpurl.h"
#include "statusbr.h"
#include "offline.h"
#include <ratings.h>
#include <wininet.h>
#include <dbgmem.h>
#ifdef DEBUG
DWORD g_dwOpenConnections = 0; // Ref Counting Open Connections
#endif // DEBUG
/*****************************************************************************\
* CFtpSite
*
* EEK! RFC 1738 is really scary. FTP sites don't necessarily
* start you at the root, and RFC1738 says that ftp://foo/bar asks
* for the file bar in the DEFAULT directory, not the root!
\*****************************************************************************/
CFtpList * g_FtpSiteCache = NULL; /* The list of all open FTP sites */
void CFtpSite::FlushHint(void)
{
HINTERNET hint = m_hint;
m_hint = NULL;
if (hint)
{
// Our caller needs to be holding the critical section
// while we modify m_hint
ASSERTCRITICAL;
InternetCloseHandle(hint);
// DEBUG_CODE(g_dwOpenConnections--;);
}
}
void CFtpSite::FlushHintCritial(void)
{
ASSERTNONCRITICAL;
ENTERCRITICAL;
FlushHint();
LEAVECRITICAL;
}
void CFtpSite::FlushHintCB(LPVOID pvFtpSite)
{
CFtpSite * pfs = (CFtpSite *) pvFtpSite;
if (pfs)
{
pfs->FlushHint();
pfs->Release();
}
}
/*****************************************************************************\
* An InternetConnect has just completed. Get the motd and cache it.
*
* hint - the connected handle, possibly 0 if error
\*****************************************************************************/
void CFtpSite::CollectMotd(HINTERNET hint)
{
CFtpGlob * pfg = GetFtpResponse(&m_cwe);
remove_from_memlist(pfg); // We will probably free this on a separate thread.
ENTERCRITICAL;
m_fMotd = m_pfgMotd ? TRUE : FALSE; // We have a motd
IUnknown_Set(&m_pfgMotd, NULL);
m_pfgMotd = pfg;
LEAVECRITICAL;
}
/*****************************************************************************\
FUNCTION: ReleaseHint
DESCRIPTION:
An FtpDir client is finished with a handle to the FTP site.
Put it into the cache, and throw away what used to be there.
We always keep the most recent handle, because that reduces the
likelihood that the server will close the connection due to extended
inactivity.
The critical section around this entire procedure is important,
else we open up all sorts of really ugly race conditions. E.g.,
the timeout might trigger before we're finished initializing it.
Or somebody might ask for the handle before we're ready.
\*****************************************************************************/
void CFtpSite::ReleaseHint(LPCITEMIDLIST pidlFtpPath, HINTERNET hint)
{
ENTERCRITICAL;
TriggerDelayedAction(&m_hgti); // Kick out the old one
_SetPidl(pidlFtpPath);
m_hint = hint;
if (EVAL(SUCCEEDED(SetDelayedAction(FlushHintCB, (LPVOID) this, &m_hgti))))
AddRef(); // We just gave away a ref.
else
FlushHint(); // Oh well, can't cache it
LEAVECRITICAL;
}
// NT #362108: We need to set the redirect password for the CFtpSite that
// contains the server, the user name, but a blank password to be redirected
// to the CFtpSite that does have the correct password. This way, if a user
// logs in and doesn't save the password in the URL or the secure cache, we
// then put it in the in memory password cache so it stays valid for that
// "browser" session (defined by process lifetime). We then need to redirect
// future navigations that go to that
HRESULT CFtpSite::_SetRedirPassword(LPCTSTR pszServer, INTERNET_PORT ipPortNum, LPCTSTR pszUser, LPCTSTR pszPassword, LPCITEMIDLIST pidlFtpPath, LPCTSTR pszFragment)
{
TCHAR szUrl[MAX_URL_STRING];
HRESULT hr;
hr = UrlCreate(pszServer, pszUser, TEXT(""), TEXT(""), pszFragment, ipPortNum, NULL, szUrl, ARRAYSIZE(szUrl));
if (EVAL(SUCCEEDED(hr)))
{
LPITEMIDLIST pidlServer;
hr = CreateFtpPidlFromUrl(szUrl, GetCWireEncoding(), NULL, &pidlServer, m_pm, TRUE);
if (EVAL(SUCCEEDED(hr)))
{
LPITEMIDLIST pidl = ILCombine(pidlServer, pidlFtpPath);
if (pidl)
{
CFtpSite * pfsDest = NULL;
// The user name has changed so we need to update the
// CFtpSite with the new user name also.
hr = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfsDest);
if (EVAL(SUCCEEDED(hr)))
{
pfsDest->SetRedirPassword(pszPassword);
pfsDest->Release();
}
ILFree(pidl);
}
ILFree(pidlServer);
}
}
return hr;
}
HRESULT CFtpSite::_RedirectAndUpdate(LPCTSTR pszServer, INTERNET_PORT ipPortNum, LPCTSTR pszUser, LPCTSTR pszPassword, LPCITEMIDLIST pidlFtpPath, LPCTSTR pszFragment, IUnknown * punkSite, CFtpFolder * pff)
{
TCHAR szUrl[MAX_URL_STRING];
TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
HRESULT hr;
StrCpyN(szUser, pszUser, ARRAYSIZE(szUser)); // Copy because of possible reentrancy
EscapeString(NULL, szUser, ARRAYSIZE(szUser));
hr = UrlCreate(pszServer, szUser, pszPassword, TEXT(""), pszFragment, ipPortNum, NULL, szUrl, ARRAYSIZE(szUrl));
if (EVAL(SUCCEEDED(hr) && pff))
{
LPITEMIDLIST pidlServer;
hr = CreateFtpPidlFromUrl(szUrl, GetCWireEncoding(), NULL, &pidlServer, m_pm, TRUE);
if (EVAL(SUCCEEDED(hr)))
{
LPITEMIDLIST pidl = ILCombine(pidlServer, pidlFtpPath);
if (pidl)
{
// If the user changed the password, we need to setup a redirect so
// they can return later. (NT #362108)
if (m_pszUser && !StrCmp(m_pszUser, szUser) && StrCmp(m_pszPassword, pszPassword))
{
_SetRedirPassword(pszServer, ipPortNum, szUser, pszPassword, pidlFtpPath, pszFragment);
}
// If the user name changed, set a redirect.
if (!m_pszUser || StrCmp(m_pszUser, szUser))
{
CFtpSite * pfsDest = NULL;
// The user name has changed so we need to update the
// CFtpSite with the new user name also.
hr = SiteCache_PidlLookup(pidl, FALSE, m_pm, &pfsDest);
if (EVAL(SUCCEEDED(hr)))
{
pfsDest->SetRedirPassword(pszPassword);
pfsDest->Release();
}
}
hr = _Redirect(pidl, punkSite, pff);
ILFree(pidl);
}
ILFree(pidlServer);
}
}
return hr;
}
HRESULT CFtpSite::_Redirect(LPITEMIDLIST pidl, IUnknown * punkSite, CFtpFolder * pff)
{
LPITEMIDLIST pidlFull = pff->CreateFullPublicPidl(pidl);
HRESULT hr = E_INVALIDARG;
if (EVAL(pidlFull))
{
hr = IUnknown_PidlNavigate(punkSite, pidlFull, FALSE);
ASSERT(SUCCEEDED(hr));
ILFree(pidlFull);
}
return hr;
}
/*****************************************************************************\
FUNCTION: _SetDirectory
DESCRIPTION:
When the caller wants a handle to the server, they often want a different
directory than what's in the cache. This function needs to change into
the new directory.
\*****************************************************************************/
HRESULT CFtpSite::_SetDirectory(HINTERNET hint, HWND hwnd, LPCITEMIDLIST pidlNewDir, CStatusBar * psb, int * pnTriesLeft)
{
HRESULT hr = S_OK;
if (pidlNewDir && FtpID_IsServerItemID(pidlNewDir))
pidlNewDir = _ILNext(pidlNewDir); // Skip the server.
ASSERT(m_pidl);
// NT #300889: I would like to cache the dir but sometimes it gets
// out of wack and m_pidl doesn't match the HINTERNET's
// cwd. PERF: This could be fixed in the future but
// this perf tweak isn't work the work now (small gain).
// if (m_pidl && !FtpPidl_IsPathEqual(_ILNext(m_pidl), pidlNewDir))
{
LPITEMIDLIST pidlWithVirtualRoot;
if (psb)
{
WCHAR wzDisplayPath[MAX_PATH]; // For Statusbar.
if (pidlNewDir && SUCCEEDED(GetDisplayPathFromPidl(pidlNewDir, wzDisplayPath, ARRAYSIZE(wzDisplayPath), TRUE)))
psb->SetStatusMessage(IDS_CHDIR, wzDisplayPath);
else
psb->SetStatusMessage(IDS_CHDIR, L"\\");
}
hr = PidlInsertVirtualRoot(pidlNewDir, &pidlWithVirtualRoot);
if (EVAL(SUCCEEDED(hr)))
{
hr = FtpSetCurrentDirectoryPidlWrap(hint, TRUE, pidlWithVirtualRoot, TRUE, TRUE);
if (SUCCEEDED(hr)) // Ok if failed. (No Access?)
{
hr = _SetPidl(pidlNewDir);
}
else
{
ReleaseHint(NULL, hint); // Nowhere
if (hr == HRESULT_FROM_WIN32(ERROR_FTP_DROPPED))
FlushHintCritial(); // Don't cache dead hint
else
{
DisplayWininetError(hwnd, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_CHANGEDIR, IDS_FTPERR_WININET, MB_OK, NULL);
*pnTriesLeft = 0; // Make sure we don't keep display UI.
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
hint = 0;
}
ILFree(pidlWithVirtualRoot);
}
if (psb)
psb->SetStatusMessage(IDS_EMPTY, 0);
}
return hr;
}
/*****************************************************************************\
FUNCTION: _LoginToTheServer
DESCRIPTION:
We want an HINTERNET to do some FTP operation but we don't have one
cached. So, login to create it.
WARNING: This function will be called in a critical section and needs to
return in one. However, it may leave the critical section for a
while.
\*****************************************************************************/
HRESULT CFtpSite::_LoginToTheServer(HWND hwnd, HINTERNET hintDll, HINTERNET * phint, LPCITEMIDLIST pidlFtpPath, CStatusBar * psb, IUnknown * punkSite, CFtpFolder * pff)
{
HRESULT hr = S_OK;
ASSERTCRITICAL;
BOOL fKeepTryingToLogin = FALSE;
BOOL fTryOldPassword = TRUE;
LEAVECRITICALNOASSERT;
TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
StrCpyN(szUser, m_pszUser, ARRAYSIZE(szUser));
StrCpyN(szPassword, m_pszPassword, ARRAYSIZE(szPassword));
ASSERT(m_pszServer);
if (psb)
psb->SetStatusMessage(IDS_CONNECTING, m_pszServer);
do
{
hr = InternetConnectWrap(hintDll, TRUE, HANDLE_NULLSTR(m_pszServer), m_ipPortNum, NULL_FOR_EMPTYSTR(szUser), NULL_FOR_EMPTYSTR(szPassword), INTERNET_SERVICE_FTP, 0, 0, phint);
if (*phint)
fKeepTryingToLogin = FALSE; // Move up.
else
{
BOOL fSkipLoginDialog = FALSE;
// Display Login dialog to get new user name/password to try again or cancel login.
// fKeepTryingToLogin = TRUE if Dialog said [LOGIN].
if (((ERROR_INTERNET_LOGIN_FAILURE == HRESULT_CODE(hr)) ||
(ERROR_INTERNET_INCORRECT_USER_NAME == HRESULT_CODE(hr)) ||
(ERROR_INTERNET_INCORRECT_PASSWORD == HRESULT_CODE(hr))) && hwnd)
{
BOOL fIsAnonymous = (!szUser[0] || !StrCmpI(szUser, TEXT("anonymous")) ? TRUE : FALSE);
DWORD dwLoginFlags = (fIsAnonymous ? LOGINFLAGS_ANON_LOGINJUSTFAILED : LOGINFLAGS_USER_LOGINJUSTFAILED);
if (fTryOldPassword)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -