588 lines
19 KiB
C++
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;
|
|
}
|
|
|