windows-nt/Source/XPSP1/NT/shell/ext/ftp/ftp.cpp
2020-09-26 16:20:57 +08:00

317 lines
11 KiB
C++

/*****************************************************************************
*
* ftp.cpp - FTP folder bookkeeping
*
*****************************************************************************/
#include "priv.h"
#include "ftpinet.h"
#include "ftpsite.h"
#include "ftplist.h"
#include "msieftp.h"
#include "cookie.h"
extern CFtpList * g_FtpSiteCache;
extern DWORD g_dwOpenConnections;
/*****************************************************************************
*
* Dynamic Globals. There should be as few of these as possible.
*
* All access to dynamic globals must be thread-safe.
*
*****************************************************************************/
ULONG g_cRef = 0; /* Global reference count */
CRITICAL_SECTION g_csDll; /* The shared critical section */
extern HANDLE g_hthWorker; // Background worker thread
#ifdef DEBUG
DWORD g_TlsMem = 0xffffffff;
extern DWORD g_TLSliStopWatchStartHi;
extern DWORD g_TLSliStopWatchStartLo;
LEAKSTRUCT g_LeakList[] =
{
{0, "CFtpFolder"},
{0, "CFtpDir"},
{0, "CFtpSite"},
{0, "CFtpObj"},
{0, "CFtpEidl"},
{0, "CFtpDrop"},
{0, "CFtpList"},
{0, "CFtpStm"},
{0, "CAccount"},
{0, "CFtpFactory"},
{0, "CFtpContextMenu"},
{0, "CFtpEfe"},
{0, "CFtpGlob"},
{0, "CFtpIcon"},
{0, "CMallocItem"},
{0, "CFtpPidlList"},
{0, "CFtpProp"},
{0, "CStatusBar"},
{0, "CFtpView"},
{0, "CFtpWebView"},
{0, "CCookieList"},
{0, "CDropOperation"}
};
#endif // DEBUG
ULONG g_cRef_CFtpView = 0; // Needed to determine when to purge cache.
/*****************************************************************************
*
* DllAddRef / DllRelease
*
* Maintain the DLL reference count.
*
*****************************************************************************/
void DllAddRef(void)
{
CREATE_CALLERS_ADDRESS; // For debug spew.
InterlockedIncrement((LPLONG)&g_cRef);
TraceMsg(TF_FTPREF, "DllAddRef() g_cRef=%d, called from=%#08lx.", g_cRef, GET_CALLERS_ADDRESS);
}
void DllRelease(void)
{
CREATE_CALLERS_ADDRESS; // For debug spew.
TraceMsg(TF_FTPREF, "DllRelease() g_cRef=%d, called from=%#08lx.", g_cRef-1, GET_CALLERS_ADDRESS);
InterlockedDecrement((LPLONG)&g_cRef);
}
/*****************************************************************************
*
* DllGetClassObject
*
* OLE entry point. Produces an IClassFactory for the indicated GUID.
*
* The artificial refcount inside DllGetClassObject helps to
* avoid the race condition described in DllCanUnloadNow. It's
* not perfect, but it makes the race window much smaller.
*
*****************************************************************************/
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID * ppvObj)
{
HRESULT hres;
DllAddRef();
if (IsEqualIID(rclsid, CLSID_FtpFolder) ||
IsEqualIID(rclsid, CLSID_FtpWebView) ||
IsEqualIID(rclsid, CLSID_FtpDataObject) ||
IsEqualIID(rclsid, CLSID_FtpInstaller))
{
hres = CFtpFactory_Create(rclsid, riid, ppvObj);
}
else
{
*ppvObj = NULL;
hres = CLASS_E_CLASSNOTAVAILABLE;
}
DllRelease();
return hres;
}
/*****************************************************************************
*
* DllCanUnloadNow
*
* OLE entry point. Fail iff there are outstanding refs.
*
* There is an unavoidable race condition between DllCanUnloadNow
* and the creation of a new IClassFactory: Between the time we
* return from DllCanUnloadNow() and the caller inspects the value,
* another thread in the same process may decide to call
* DllGetClassObject, thus suddenly creating an object in this DLL
* when there previously was none.
*
* It is the caller's responsibility to prepare for this possibility;
* there is nothing we can do about it.
*
*****************************************************************************/
STDMETHODIMP DllCanUnloadNow(void)
{
HRESULT hres;
ENTERCRITICALNOASSERT;
// Purge Cache if there aren't any FtpViews open.
if ((0 == g_cRef_CFtpView))
{
// Since no views are open, we want to try to purge
// the Delayed Actions so we can closed down the background
// thread. Is it running?
if (AreOutstandingDelayedActions())
{
LEAVECRITICALNOASSERT;
PurgeDelayedActions(); // Try to close it down.
ENTERCRITICALNOASSERT;
}
if (!AreOutstandingDelayedActions()) // Did it close down?
{
// We need to purge the session key because we lost the password
// redirects in the CFtpSites. So we would login but later fail
// when we try to fish out the password when falling back to
// URLMON/shdocfl for file downloads. (NT #362108)
PurgeSessionKey();
CFtpPunkList_Purge(&g_FtpSiteCache); // Yes so purge the cache...
}
}
hres = g_cRef ? S_FALSE : S_OK;
TraceMsg(TF_FTP_DLLLOADING, "DllCanUnloadNow() DllRefs=%d, returning hres=%#08lx. (S_OK means yes)", g_cRef, hres);
LEAVECRITICALNOASSERT;
return hres;
}
void CheckForLeaks(BOOL fForce)
{
#ifdef DEBUG
DWORD dwLeakCount = 0;
if (fForce)
{
// Let's free our stuff so we can make sure not to leak it.
// This is done more to force our selves to be w/o leaks
// than anything else.
DllCanUnloadNow();
}
for (int nIndex = 0; nIndex < ARRAYSIZE(g_LeakList); nIndex++)
dwLeakCount += g_LeakList[nIndex].dwRef;
if ((!g_FtpSiteCache || fForce) && (dwLeakCount || g_dwOpenConnections || g_cRef))
{
TraceMsg(TF_ALWAYS, "***********************************************");
TraceMsg(TF_ALWAYS, "* LEAK - LEAK - LEAK - LEAK - LEAK *");
TraceMsg(TF_ALWAYS, "* *");
TraceMsg(TF_ALWAYS, "* WARNING: The FTP Shell Extension Leaked *");
TraceMsg(TF_ALWAYS, "* one or more objects *");
TraceMsg(TF_ALWAYS, "***********************************************");
TraceMsg(TF_ALWAYS, "* *");
for (int nIndex = 0; nIndex < ARRAYSIZE(g_LeakList); nIndex++)
{
if (g_LeakList[nIndex].dwRef)
TraceMsg(TF_ALWAYS, "* %hs, Leaked=%d *", g_LeakList[nIndex].szObject, g_LeakList[nIndex].dwRef);
}
TraceMsg(TF_ALWAYS, "* *");
TraceMsg(TF_ALWAYS, "* Open Wininet Connections=%d *", g_dwOpenConnections);
TraceMsg(TF_ALWAYS, "* DLL Refs=%d *", g_cRef);
TraceMsg(TF_ALWAYS, "* *");
TraceMsg(TF_ALWAYS, "***********************************************");
ASSERT(0);
}
#endif // DEBUG
}
// Globals to free.
extern CCookieList * g_pCookieList;
/*****************************************************************************\
DESCRIPTION:
DLL entry point.
\*****************************************************************************/
STDAPI_(BOOL) DllEntry(HINSTANCE hinst, DWORD dwReason, LPVOID lpReserved)
{
// This is called in two situations, FreeLibrary() is called and lpReserved is
// NULL, or the process is shutting down and lpReserved is not NULL.
BOOL fIsProcessShuttingDown = (lpReserved ? TRUE : FALSE);
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
SHFusionInitializeFromModule(hinst);
InitializeCriticalSection(&g_csDll);
#ifdef DEBUG
g_TlsMem = TlsAlloc();
g_TLSliStopWatchStartHi = TlsAlloc();
g_TLSliStopWatchStartLo = TlsAlloc();
#endif
// Don't put it under #ifdef DEBUG
CcshellGetDebugFlags();
DisableThreadLibraryCalls(hinst);
g_hthWorker = NULL;
g_hinst = hinst;
g_formatEtcOffsets.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLISTOFFSET);
g_formatPasteSucceeded.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PASTESUCCEEDED);
g_cfTargetCLSID = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_TARGETCLSID);
g_dropTypes[DROP_FCont].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILECONTENTS);
g_dropTypes[DROP_FGDW].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORW);
g_dropTypes[DROP_FGDA].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILEDESCRIPTORA);
g_dropTypes[DROP_IDList].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_SHELLIDLIST);
g_dropTypes[DROP_FNMA].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILENAMEMAPA);
g_dropTypes[DROP_FNMW].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_FILENAMEMAPW);
g_dropTypes[DROP_PrefDe].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
g_dropTypes[DROP_PerfDe].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_PERFORMEDDROPEFFECT);
g_dropTypes[DROP_FTP_PRIVATE].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("FtpPrivateData"));
g_dropTypes[DROP_URL].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("UniformResourceLocator"));
g_dropTypes[DROP_OLEPERSIST].cfFormat = (CLIPFORMAT)RegisterClipboardFormat(TEXT("OleClipboardPersistOnFlush"));
GetModuleFileNameA(GetModuleHandle(TEXT("SHELL32")), g_szShell32, ARRAYSIZE(g_szShell32));
if (FAILED(CFtpSite_Init()))
return 0;
break;
case DLL_PROCESS_DETACH:
{
CCookieList * pCookieList = (CCookieList *) InterlockedExchangePointer((void **) &g_pCookieList, NULL);
if (pCookieList)
delete pCookieList;
// Yes, so we need to make sure all of the CFtpView's have closed down
// or it's really bad to purge the FTP cache of FTP Servers (CFtpSite) and
// their directories (CFtpDir).
ASSERT(0 == g_cRef_CFtpView);
// Now force the Delayed Actions to happen now instead of waiting.
PurgeDelayedActions();
// OndrejS turned this on. It's firing but I think they are false positives. Since FTP
// Folders does so much caching, this is non-trivial to track down. I will turn this off
// until Ondrej has time to verify.
// CheckForLeaks(fIsProcessShuttingDown);
UnloadWininet();
DeleteCriticalSection(&g_csDll);
#ifdef DEBUG
if (g_TLSliStopWatchStartHi)
{
TlsFree(g_TLSliStopWatchStartHi);
g_TLSliStopWatchStartHi = NULL;
}
if (g_TLSliStopWatchStartLo)
{
TlsFree(g_TLSliStopWatchStartLo);
g_TLSliStopWatchStartLo = NULL;
}
#endif
SHFusionUninitialize();
}
break;
}
return 1;
}