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

588 lines
19 KiB
C++

//*********************************************************************
//* Microsoft Windows **
//* Copyright(c) Microsoft Corp., 1996 **
//*********************************************************************
/*Included Files------------------------------------------------------------*/
#include "msrating.h"
#include "ratings.h"
#include <ratingsp.h>
#include <npassert.h>
#include <npstring.h>
#include "mslubase.h"
#include "roll.h"
#include "rors.h"
#include "picsrule.h"
#include "parselbl.h"
#include "debug.h"
typedef HRESULT (STDAPICALLTYPE *PFNCoInitialize)(LPVOID pvReserved);
typedef void (STDAPICALLTYPE *PFNCoUninitialize)(void);
typedef HRESULT (STDAPICALLTYPE *PFNCoGetMalloc)(
DWORD dwMemContext, LPMALLOC FAR* ppMalloc);
typedef HRESULT (STDAPICALLTYPE *PFNCoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter,
DWORD dwClsContext, REFIID riid, LPVOID FAR* ppv);
typedef HRESULT (STDAPICALLTYPE *PFNCLSIDFromString)(LPOLESTR lpsz, LPCLSID pclsid);
PFNCoInitialize pfnCoInitialize = NULL;
PFNCoUninitialize pfnCoUninitialize = NULL;
PFNCoGetMalloc pfnCoGetMalloc = NULL;
PFNCoCreateInstance pfnCoCreateInstance = NULL;
PFNCLSIDFromString pfnCLSIDFromString = NULL;
#undef CoInitialize
#undef CoUninitialize
#undef CoGetMalloc
#undef CoCreateInstance
#undef CLSIDFromString
#define CoInitialize pfnCoInitialize
#define CoUninitialize pfnCoUninitialize
#define CoGetMalloc pfnCoGetMalloc
#define CoCreateInstance pfnCoCreateInstance
#define CLSIDFromString pfnCLSIDFromString
struct {
FARPROC *ppfn;
LPCSTR pszName;
} aOLEImports[] = {
{ (FARPROC *)&pfnCoInitialize, "CoInitialize" },
{ (FARPROC *)&pfnCoUninitialize, "CoUninitialize" },
{ (FARPROC *)&pfnCoGetMalloc, "CoGetMalloc" },
{ (FARPROC *)&pfnCoCreateInstance, "CoCreateInstance" },
{ (FARPROC *)&pfnCLSIDFromString, "CLSIDFromString" },
};
const UINT cOLEImports = sizeof(aOLEImports) / sizeof(aOLEImports[0]);
HINSTANCE hOLE32 = NULL;
BOOL fTriedOLELoad = FALSE;
BOOL LoadOLE(void)
{
if (fTriedOLELoad)
return (hOLE32 != NULL);
fTriedOLELoad = TRUE;
hOLE32 = ::LoadLibrary("OLE32.DLL");
if (hOLE32 == NULL)
return FALSE;
for (UINT i=0; i<cOLEImports; i++) {
*(aOLEImports[i].ppfn) = ::GetProcAddress(hOLE32, aOLEImports[i].pszName);
if (*(aOLEImports[i].ppfn) == NULL) {
CleanupOLE();
return FALSE;
}
}
return TRUE;
}
void CleanupOLE(void)
{
if (hOLE32 != NULL) {
for (UINT i=0; i<cOLEImports; i++) {
*(aOLEImports[i].ppfn) = NULL;
}
::FreeLibrary(hOLE32);
hOLE32 = NULL;
}
}
/*Obtain Rating Data--------------------------------------------------------*/
class RatingObtainData
{
public:
NLS_STR nlsTargetUrl;
HANDLE hAbortEvent;
DWORD dwUserData;
void (*fCallback)(DWORD dwUserData, HRESULT hr, LPCTSTR pszRating, LPVOID lpvRatingDetails) ;
RatingObtainData(LPCTSTR pszTargetUrl);
~RatingObtainData();
};
RatingObtainData::RatingObtainData(LPCTSTR pszTargetUrl)
: nlsTargetUrl(pszTargetUrl)
{
hAbortEvent = NULL;
dwUserData = 0;
fCallback = NULL;
}
RatingObtainData::~RatingObtainData()
{
if (hAbortEvent) CloseHandle(hAbortEvent);
}
struct RatingHelper {
CLSID clsid;
DWORD dwSort;
};
array<RatingHelper> *paRatingHelpers = NULL;
CustomRatingHelper *g_pCustomRatingHelperList;
BOOL fTriedLoadingHelpers = FALSE;
void InitRatingHelpers()
{
BOOL fCOMInitialized = FALSE;
RatingHelper helper;
if (fTriedLoadingHelpers || !LoadOLE())
{
TraceMsg( TF_WARNING, "InitRatingHelpers() - Tried Loading Helpers or OLE Load Failed!");
return;
}
fTriedLoadingHelpers = TRUE;
paRatingHelpers = new array<RatingHelper>;
if (paRatingHelpers == NULL)
{
TraceMsg( TF_ERROR, "InitRatingHelpers() - Failed to Create paRatingHelpers!");
return;
}
CRegKey key;
/* REARCHITECT - should this be in the policy file? it shouldn't be per-user, that's for sure. */
if ( key.Open( HKEY_LOCAL_MACHINE, szRATINGHELPERS, KEY_READ ) != ERROR_SUCCESS )
{
TraceMsg( TF_WARNING, "InitRatingHelpers() - Failed to Open key szRATINGHELPERS='%s'!", szRATINGHELPERS );
return;
}
UINT iValue = 0;
LONG err = ERROR_SUCCESS;
char szValue[39]; /* just big enough for a null-terminated GUID string */
WCHAR wszValue[39]; /* unicode version */
// YANGXU : 11/15/1999
// under custom mode, if we have a bureau string, load the bureau
// rating helper, but do not load any other rating helpers
if (g_fIsRunningUnderCustom)
{
if (gPRSI->etstrRatingBureau.fIsInit())
{
ASSERT(FALSE == fCOMInitialized);
if (SUCCEEDED(CoInitialize(NULL)))
{
fCOMInitialized = TRUE;
IObtainRating *pHelper;
helper.clsid = CLSID_RemoteSite;
if (SUCCEEDED(CoCreateInstance(helper.clsid, NULL,
CLSCTX_INPROC_SERVER,
IID_IObtainRating,
(LPVOID *)&pHelper)))
{
helper.dwSort = pHelper->GetSortOrder();
if (!paRatingHelpers->Append(helper))
{
err = ERROR_NOT_ENOUGH_MEMORY;
}
pHelper->Release();
#ifdef DEBUG
pHelper = NULL;
#endif
}
}
}
}
else
{
/* Note, special care is taken to make sure that we only CoInitialize for
* as long as we need to, and CoUninitialize when we're done. We cannot
* CoUninitialize on a different thread than we initialized on, nor do we
* want to call CoUninitialize at thread-detach time (would require using
* TLS). This is done here and in the asynchronous thread that actually
* calls into the rating helpers to get ratings.
*/
do
{
DWORD cchValue = sizeof(szValue);
err = RegEnumValue( key.m_hKey, iValue, szValue, &cchValue, NULL, NULL, NULL, NULL);
if (err == ERROR_SUCCESS)
{
if (!fCOMInitialized)
{
if (FAILED(CoInitialize(NULL)))
{
break;
}
fCOMInitialized = TRUE;
}
if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, szValue, -1, wszValue, ARRAYSIZE(wszValue))) {
if (SUCCEEDED(CLSIDFromString(wszValue, &helper.clsid)))
{
IObtainRating *pHelper;
if (SUCCEEDED(CoCreateInstance(helper.clsid, NULL,
CLSCTX_INPROC_SERVER,
IID_IObtainRating,
(LPVOID *)&pHelper)))
{
helper.dwSort = pHelper->GetSortOrder();
if (!paRatingHelpers->Append(helper))
{
err = ERROR_NOT_ENOUGH_MEMORY;
}
pHelper->Release();
#ifdef DEBUG
pHelper = NULL;
#endif
}
}
}
}
iValue++;
} while (ERROR_SUCCESS == err);
}
if (fCOMInitialized)
{
CoUninitialize();
}
/* If more than one helper, sort them by their reported sort orders.
* We will rarely have more than two or three of these guys, and this
* is one time code, so we don't need a super-slick sort algorithm.
*
* CODEWORK: could modify array<> template to support an Insert()
* method which reallocates the buffer like Append() does, but inserts
* at a specific location.
*/
if (paRatingHelpers->Length() > 1)
{
for (INT i=0; i < paRatingHelpers->Length() - 1; i++)
{
for (INT j=i+1; j < paRatingHelpers->Length(); j++)
{
if ((*paRatingHelpers)[i].dwSort > (*paRatingHelpers)[j].dwSort)
{
RatingHelper temp = (*paRatingHelpers)[i];
(*paRatingHelpers)[i] = (*paRatingHelpers)[j];
(*paRatingHelpers)[j] = temp;
}
}
}
}
}
void CleanupRatingHelpers(void)
{
if (paRatingHelpers != NULL) {
delete paRatingHelpers;
paRatingHelpers = NULL;
}
fTriedLoadingHelpers = FALSE;
}
/*
This procedure runs on its own thread (1 per request).
This cycles through all the helper DLLs looking for a ratings.
It goes on down the list one at a time until either a rating
is found, or the this is aborted by the programmer.
*/
DWORD __stdcall RatingCycleThread(LPVOID pData)
{
RatingObtainData *pOrd = (RatingObtainData*) pData;
LPVOID lpvRatingDetails = NULL;
HRESULT hrRet = E_FAIL;
int nProc;
BOOL fAbort = FALSE;
BOOL fFoundWithCustomHelper = FALSE;
IMalloc *pAllocator = NULL;
LPSTR pszRating = NULL;
LPSTR pszRatingName = NULL;
LPSTR pszRatingReason = NULL;
BOOL fCOMInitialized = FALSE;
CustomRatingHelper* pmrhCurrent = NULL;
ASSERT(pOrd);
//
// Check the Custom Helpers first
//
if(g_pCustomRatingHelperList)
{
// we should only have custom rating helpers under custom mode
ASSERT(g_fIsRunningUnderCustom);
if(SUCCEEDED(CoInitialize(NULL)))
{
fCOMInitialized = TRUE;
// get a cotaskmem allocator
hrRet = CoGetMalloc(MEMCTX_TASK, &pAllocator);
if (SUCCEEDED(hrRet))
{
pmrhCurrent = g_pCustomRatingHelperList;
while(pmrhCurrent)
{
HRESULT (* pfn)(REFCLSID, REFIID, LPVOID *) = NULL;
ICustomRatingHelper* pCustomHelper = NULL;
IClassFactory* pFactory = NULL;
ASSERT(pmrhCurrent->hLibrary);
*(FARPROC *) &pfn = GetProcAddress(pmrhCurrent->hLibrary, "DllGetClassObject");
if (pfn)
{
hrRet = pfn(pmrhCurrent->clsid, IID_IClassFactory, (void**)&pFactory);
if (SUCCEEDED(hrRet))
{
hrRet = pFactory->CreateInstance(NULL, IID_ICustomRatingHelper, (void**)&pCustomHelper);
if (SUCCEEDED(hrRet))
{
hrRet = pCustomHelper->ObtainCustomRating(pOrd->nlsTargetUrl.QueryPch(),
pOrd->hAbortEvent,
pAllocator,
&pszRating,
&pszRatingName,
&pszRatingReason);
pCustomHelper->Release();
pCustomHelper = NULL;
fAbort = (WAIT_OBJECT_0 == WaitForSingleObject(pOrd->hAbortEvent, 0));
if (fAbort || SUCCEEDED(hrRet))
break;
}
pFactory->Release();
} // if (SUCCEEDED(pfn(pmrhCurrent->clsid, IID_ICustomRatingHelper, (void**)&pCustomHelper)))
} // if (pfn)
else
{
hrRet = E_UNEXPECTED;
}
pmrhCurrent = pmrhCurrent->pNextHelper;
}
}
}
}
if(SUCCEEDED(hrRet))
{
fFoundWithCustomHelper = TRUE;
}
if(paRatingHelpers && paRatingHelpers->Length()>0 && !SUCCEEDED(hrRet) && !fAbort)
{
/* Note that CoInitialize and CoUninitialize must be done once per thread. */
if(!fCOMInitialized)
{
if(SUCCEEDED(CoInitialize(NULL)))
{
fCOMInitialized = TRUE;
}
}
if (fCOMInitialized) {
if (!pAllocator)
{
hrRet = CoGetMalloc(MEMCTX_TASK, &pAllocator);
}
if (pAllocator) {
//Cycle through list of rating procs till one gives us the answer, we abort, or there are no more
int nRatingHelperProcs = ::paRatingHelpers->Length();
for (nProc = 0; nProc < nRatingHelperProcs; ++nProc)
{
IObtainRating *pHelper;
if (SUCCEEDED(CoCreateInstance((*paRatingHelpers)[nProc].clsid, NULL,
CLSCTX_INPROC_SERVER,
IID_IObtainRating,
(LPVOID *)&pHelper))) {
hrRet = pHelper->ObtainRating(pOrd->nlsTargetUrl.QueryPch(),
pOrd->hAbortEvent, pAllocator, &pszRating);
pHelper->Release();
#ifdef DEBUG
pHelper = NULL;
#endif
}
fAbort = (WAIT_OBJECT_0 == WaitForSingleObject(pOrd->hAbortEvent, 0));
if (fAbort || SUCCEEDED(hrRet)) break;
}
}
}
else
hrRet = E_RATING_NOT_FOUND;
}
/*return results to user*/
if (!fAbort)
{
/*
* If one of the providers found a rating, we must call CheckUserAccess
* and tell the client whether the user has access or not. If we did
* not find a rating, then we tell the client that, by passing the
* callback a code of E_RATING_NOT_FOUND.
*
* The provider may also return S_RATING_ALLOW or S_RATING_DENY, which
* means that it has already checked the user's access (for example,
* against a system-wide exclusion list).
*/
if (hrRet == S_RATING_FOUND)
{
hrRet = RatingCheckUserAccess(NULL, pOrd->nlsTargetUrl.QueryPch(),
pszRating, NULL, PICS_LABEL_FROM_BUREAU,
&lpvRatingDetails);
}
else
{
if(S_RATING_DENY == hrRet && g_fIsRunningUnderCustom)
{
lpvRatingDetails = (LPVOID)(new CParsedLabelList);
if (lpvRatingDetails)
{
((CParsedLabelList*)lpvRatingDetails)->m_fDenied = TRUE;
((CParsedLabelList*)lpvRatingDetails)->m_pszURL = StrDup(pOrd->nlsTargetUrl.QueryPch());
}
else
{
hrRet = E_OUTOFMEMORY;
}
}
}
if (S_RATING_DENY == hrRet && g_fIsRunningUnderCustom)
{
// lpvRatingDetails should be non NULL at this point
ASSERT(lpvRatingDetails);
((CParsedLabelList*)lpvRatingDetails)->m_fIsHelper = TRUE;
if(fFoundWithCustomHelper)
{
((CParsedLabelList*)lpvRatingDetails)->m_fIsCustomHelper = TRUE;
if (pszRatingName)
{
if(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingName = new char[strlen(pszRatingName)+1])
{
strcpyf(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingName,pszRatingName);
} // if(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingName = new char[strlen(pszRatingName)+1])
}
if (pszRatingReason)
{
if(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingReason = new char[strlen(pszRatingReason)+1])
{
strcpyf(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingReason, pszRatingReason);
} // if(((CParsedLabelList*)lpvRatingDetails)->m_pszRatingReason = new char[strlen(pszRatingReason)+1])
}
}
}
/* Range-check other success codes to make sure they're not anything
* that the browser callback isn't expecting.
*/
if (SUCCEEDED(hrRet) && (hrRet != S_RATING_ALLOW && hrRet != S_RATING_DENY))
hrRet = E_RATING_NOT_FOUND;
(*pOrd->fCallback)(pOrd->dwUserData, hrRet, pszRating, (LPVOID) lpvRatingDetails);
}
/*cleanup*/
delete pOrd;
pOrd = NULL;
if (pAllocator != NULL) {
pAllocator->Free(pszRating);
if(pszRatingName)
{
pAllocator->Free(pszRatingName);
}
if(pszRatingReason)
{
pAllocator->Free(pszRatingReason);
}
pAllocator->Release();
}
if (fCOMInitialized)
CoUninitialize();
return (DWORD) fAbort;
}
/*Public Functions----------------------------------------------------------*/
//Startup thread that finds rating, return immediately
HRESULT WINAPI RatingObtainQuery(LPCTSTR pszTargetUrl, DWORD dwUserData, void (*fCallback)(DWORD dwUserData, HRESULT hr, LPCTSTR pszRating, LPVOID lpvRatingDetails), HANDLE *phRatingObtainQuery)
{
RatingObtainData *pOrd;
HANDLE hThread;
DWORD dwThid;
CheckGlobalInfoRev();
if (!g_fIsRunningUnderCustom)
{
if (::RatingEnabledQuery() != S_OK ||
!gPRSI->fSettingsValid) /* ratings not enabled? fail immediately. */
return E_RATING_NOT_FOUND;
}
InitRatingHelpers();
if (NULL == g_pCustomRatingHelperList
&& (::paRatingHelpers == NULL || ::paRatingHelpers->Length() < 1)) {
return E_RATING_NOT_FOUND;
}
if (fCallback && pszTargetUrl)
{
pOrd = new RatingObtainData(pszTargetUrl);
if (pOrd)
{
if (pOrd->nlsTargetUrl.QueryError() == ERROR_SUCCESS) {
pOrd->dwUserData = dwUserData;
pOrd->fCallback = fCallback;
pOrd->hAbortEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (pOrd->hAbortEvent)
{
hThread = CreateThread(NULL, 0, RatingCycleThread, (LPVOID) pOrd, 0, &dwThid);
if (hThread)
{
CloseHandle(hThread);
if (phRatingObtainQuery) *phRatingObtainQuery = pOrd->hAbortEvent;
return NOERROR;
}
CloseHandle(pOrd->hAbortEvent);
}
}
delete pOrd;
pOrd = NULL;
}
}
return E_FAIL;
}
//Cancel an existing query
HRESULT WINAPI RatingObtainCancel(HANDLE hRatingObtainQuery)
{
//what happens if hRatingObtainQuery has already been closed?!?!
if (hRatingObtainQuery)
{
if (SetEvent(hRatingObtainQuery)) return NOERROR;
}
return E_HANDLE;
}