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

570 lines
15 KiB
C++

//*********************************************************************
//* Microsoft Windows **
//* Copyright(c) Microsoft Corp., 1996 **
//*********************************************************************
/*Included Files------------------------------------------------------------*/
#include "msrating.h"
#pragma hdrstop
#include <npassert.h>
#include <buffer.h>
#include "ratings.h"
#include "mslubase.h"
#include "parselbl.h"
#include "rors.h"
#include "wininet.h"
typedef HINTERNET (WINAPI *PFNInternetOpen)(
IN LPCTSTR lpszCallerName,
IN DWORD dwAccessType,
IN LPCTSTR lpszServerName OPTIONAL,
IN INTERNET_PORT nServerPort,
IN DWORD dwFlags
);
typedef BOOL (WINAPI *PFNInternetCloseHandle)(
IN HINTERNET hInternet
);
typedef HINTERNET (WINAPI *PFNInternetConnect)(
IN HINTERNET hInternet,
IN LPCTSTR lpszServerName,
IN INTERNET_PORT nServerPort,
IN LPCTSTR lpszUsername OPTIONAL,
IN LPCTSTR lpszPassword OPTIONAL,
IN DWORD dwService,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
);
typedef BOOL (WINAPI *PFNInternetReadFile)(
IN HINTERNET hFile,
IN LPVOID lpBuffer,
IN DWORD dwNumberOfBytesToRead,
OUT LPDWORD lpdwNumberOfBytesRead
);
typedef INTERNET_STATUS_CALLBACK (WINAPI *PFNInternetSetStatusCallback)(
IN HINTERNET hInternet,
IN INTERNET_STATUS_CALLBACK lpfnInternetCallback
);
typedef HINTERNET (WINAPI *PFNHttpOpenRequest)(
IN HINTERNET hHttpSession,
IN LPCTSTR lpszVerb,
IN LPCTSTR lpszObjectName,
IN LPCTSTR lpszVersion,
IN LPCTSTR lpszReferrer OPTIONAL,
IN LPCTSTR FAR * lplpszAcceptTypes OPTIONAL,
IN DWORD dwFlags,
IN DWORD_PTR dwContext
);
typedef BOOL (WINAPI *PFNHttpSendRequest)(
IN HINTERNET hHttpRequest,
IN LPCTSTR lpszHeaders OPTIONAL,
IN DWORD dwHeadersLength,
IN LPVOID lpOptional OPTIONAL,
IN DWORD dwOptionalLength
);
typedef BOOL (WINAPI *PFNInternetCrackUrl)(
IN LPCTSTR lpszUrl,
IN DWORD dwUrlLength,
IN DWORD dwFlags,
IN OUT LPURL_COMPONENTS lpUrlComponents
);
typedef BOOL (WINAPI *PFNInternetCanonicalizeUrl)(
IN LPCSTR lpszUrl,
OUT LPSTR lpszBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN DWORD dwFlags
);
PFNInternetReadFile pfnInternetReadFile = NULL;
PFNHttpSendRequest pfnHttpSendRequest = NULL;
PFNInternetOpen pfnInternetOpen = NULL;
PFNInternetSetStatusCallback pfnInternetSetStatusCallback = NULL;
PFNInternetConnect pfnInternetConnect = NULL;
PFNHttpOpenRequest pfnHttpOpenRequest = NULL;
PFNInternetCloseHandle pfnInternetCloseHandle = NULL;
PFNInternetCrackUrl pfnInternetCrackUrl = NULL;
PFNInternetCanonicalizeUrl pfnInternetCanonicalizeUrl = NULL;
#undef InternetReadFile
#undef HttpSendRequest
#undef InternetOpen
#undef InternetSetStatusCallback
#undef InternetConnect
#undef HttpOpenRequest
#undef InternetCloseHandle
#undef InternetCrackUrl
#undef InternetCanonicalizeUrl
#define InternetReadFile pfnInternetReadFile
#define HttpSendRequest pfnHttpSendRequest
#define InternetOpen pfnInternetOpen
#define InternetSetStatusCallback pfnInternetSetStatusCallback
#define InternetConnect pfnInternetConnect
#define HttpOpenRequest pfnHttpOpenRequest
#define InternetCloseHandle pfnInternetCloseHandle
#define InternetCrackUrl pfnInternetCrackUrl
#define InternetCanonicalizeUrl pfnInternetCanonicalizeUrl
struct {
FARPROC *ppfn;
LPCSTR pszName;
} aImports[] = {
#ifndef UNICODE
{ (FARPROC *)&pfnInternetReadFile, "InternetReadFile" },
{ (FARPROC *)&pfnHttpSendRequest, "HttpSendRequestA" },
{ (FARPROC *)&pfnInternetOpen, "InternetOpenA" },
{ (FARPROC *)&pfnInternetSetStatusCallback, "InternetSetStatusCallback" },
{ (FARPROC *)&pfnInternetConnect, "InternetConnectA" },
{ (FARPROC *)&pfnHttpOpenRequest, "HttpOpenRequestA" },
{ (FARPROC *)&pfnInternetCloseHandle, "InternetCloseHandle" },
{ (FARPROC *)&pfnInternetCrackUrl, "InternetCrackUrlA" },
{ (FARPROC *)&pfnInternetCanonicalizeUrl, "InternetCanonicalizeUrlA" },
#else
{ (FARPROC *)&pfnInternetReadFile, "InternetReadFile" },
{ (FARPROC *)&pfnHttpSendRequest, "HttpSendRequestW" },
{ (FARPROC *)&pfnInternetOpen, "InternetOpenW" },
{ (FARPROC *)&pfnInternetSetStatusCallback, "InternetSetStatusCallback" },
{ (FARPROC *)&pfnInternetConnect, "InternetConnectW" },
{ (FARPROC *)&pfnHttpOpenRequest, "HttpOpenRequestW" },
{ (FARPROC *)&pfnInternetCloseHandle, "InternetCloseHandle" },
{ (FARPROC *)&pfnInternetCrackUrl, "InternetCrackUrlW" },
{ (FARPROC *)&pfnInternetCanonicalizeUrl, "InternetCanonicalizeUrlW" },
#endif
};
const UINT cImports = sizeof(aImports) / sizeof(aImports[0]);
HINSTANCE hWinINet = NULL;
BOOL fTriedLoad = FALSE;
HINTERNET hI = NULL;
void _stdcall WinInetCallbackProc(HINTERNET hInternet, DWORD_PTR Context, DWORD Status, LPVOID Info, DWORD Length);
#define USER_AGENT_STRING "Batcave(bcrs)"
BOOL LoadWinINet(void)
{
if (fTriedLoad)
{
return (hWinINet != NULL);
}
fTriedLoad = TRUE;
hWinINet = ::LoadLibrary("WININET.DLL");
if (hWinINet == NULL)
{
return FALSE;
}
for (UINT i=0; i<cImports; i++)
{
*(aImports[i].ppfn) = ::GetProcAddress(hWinINet, aImports[i].pszName);
if (*(aImports[i].ppfn) == NULL)
{
CleanupWinINet();
return FALSE;
}
}
hI = InternetOpen(USER_AGENT_STRING, PRE_CONFIG_INTERNET_ACCESS, NULL, 0, INTERNET_FLAG_ASYNC);
if (hI == NULL)
{
CleanupWinINet();
return FALSE;
}
InternetSetStatusCallback(hI, WinInetCallbackProc);
return TRUE;
}
void CleanupWinINet(void)
{
if (hI != NULL)
{
InternetCloseHandle(hI);
hI = NULL;
}
if (hWinINet != NULL)
{
for (UINT i=0; i<cImports; i++)
{
*(aImports[i].ppfn) = NULL;
}
::FreeLibrary(hWinINet);
hWinINet = NULL;
}
}
void _stdcall WinInetCallbackProc(HINTERNET hInternet, DWORD_PTR Context, DWORD Status, LPVOID Info, DWORD Length)
{
BOOL unknown = FALSE;
HANDLE hAsyncEvent = (HANDLE) Context;
char *type$;
switch (Status)
{
case INTERNET_STATUS_RESOLVING_NAME:
type$ = "RESOLVING NAME";
break;
case INTERNET_STATUS_NAME_RESOLVED:
type$ = "NAME RESOLVED";
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
type$ = "CONNECTING TO SERVER";
break;
case INTERNET_STATUS_CONNECTED_TO_SERVER:
type$ = "CONNECTED TO SERVER";
break;
case INTERNET_STATUS_SENDING_REQUEST:
type$ = "SENDING REQUEST";
break;
case INTERNET_STATUS_REQUEST_SENT:
type$ = "REQUEST SENT";
break;
case INTERNET_STATUS_RECEIVING_RESPONSE:
type$ = "RECEIVING RESPONSE";
break;
case INTERNET_STATUS_RESPONSE_RECEIVED:
type$ = "RESPONSE RECEIVED";
break;
case INTERNET_STATUS_CLOSING_CONNECTION:
type$ = "CLOSING CONNECTION";
break;
case INTERNET_STATUS_CONNECTION_CLOSED:
type$ = "CONNECTION CLOSED";
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
type$ = "REQUEST COMPLETE";
SetEvent(hAsyncEvent);
break;
default:
type$ = "???";
unknown = TRUE;
break;
}
/*
printf("callback: handle %x [context %x ] %s \n",
hInternet,
Context,
type$
);
*/
}
#define ABORT_EVENT 0
#define ASYNC_EVENT 1
BOOL ShouldAbort(HANDLE hAbort)
{
return (WAIT_OBJECT_0 == WaitForSingleObject(hAbort, 0));
}
BOOL WaitForAsync(HANDLE rgEvents[])
{
BOOL fAbort;
// if (ERROR_IO_PENDING != GetLastError()) return FALSE;
fAbort = (WAIT_OBJECT_0 == WaitForMultipleObjects(2, rgEvents, FALSE, INFINITE));
// fAbort = (WAIT_OBJECT_0 == WaitForSingleObject(rgEvents[ABORT_EVENT], 0));
return !fAbort;
}
void EncodeUrl(LPCTSTR pszTargetUrl, char *pBuf)
{
while (*pszTargetUrl)
{
switch (*pszTargetUrl)
{
case ':':
*pBuf++ = '%';
*pBuf++ = '3';
*pBuf++ = 'A';
break;
case '/':
*pBuf++ = '%';
*pBuf++ = '2';
*pBuf++ = 'F';
break;
default:
*pBuf++ = *pszTargetUrl;
break;
}
++pszTargetUrl;
}
*pBuf = 0;
}
STDMETHODIMP CRORemoteSite::QueryInterface(
/* [in] */ REFIID riid,
/* [out] */ void __RPC_FAR *__RPC_FAR *ppvObject)
{
*ppvObject = NULL;
if (IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IObtainRating))
{
*ppvObject = (LPVOID)this;
AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
STDMETHODIMP_(ULONG) CRORemoteSite::AddRef(void)
{
RefThisDLL(TRUE);
return ++m_cRef;
}
STDMETHODIMP_(ULONG) CRORemoteSite::Release(void)
{
RefThisDLL(FALSE);
if (!--m_cRef)
{
delete this;
return 0;
}
else
{
return m_cRef;
}
}
LPSTR FindRatingLabel(LPSTR pszResponse)
{
/* pszResponse is the complete response message from the HTTP server.
* It could be a simple response (just the PICS label we want) or it
* could be a full response including headers. In the former case we
* just return the label, in the latter we have to skip the headers
* to the message body.
*
* To be extra tolerant of poorly written label bureaus, we start by
* looking at the start of the data to see if it's a left paren. If
* it isn't, we assume we've got some headers, so we skip to the
* double CRLF which HTTP requires to terminate headers. We don't
* require a Status-Line (such as "HTTP/1.1 200 OK") even though
* technically HTTP does. If we don't find the double CRLF, then
* we look for the string "(PICS-" which is usually what begins a
* PICS label list. If they've done everything else wrong and they're
* also perverse enough to insert whitespace there (such as "( PICS-"),
* tough.
*/
SkipWhitespace(&pszResponse); /* skip leading whitespace just in case */
if (*pszResponse != '(')
{ /* doesn't seem to start with a label */
LPSTR pszBody = ::strstrf(pszResponse, ::szDoubleCRLF);
if (pszBody != NULL)
{ /* found double CRLF, end of HTTP headers */
pszResponse = pszBody + 4; /* length of CRLFCRLF */
}
else
{ /* no double CRLF, hunt for PICS label */
pszBody = ::strstrf(pszResponse, ::szPicsOpening);
if (pszBody != NULL)
{
pszResponse = pszBody; /* beginning of PICS label */
}
}
}
return pszResponse;
}
const char szRequestTemplate[] = "?opt=normal&u=\"";
const UINT cchRequestTemplate = sizeof(szRequestTemplate) + 1;
STDMETHODIMP CRORemoteSite::ObtainRating(THIS_ LPCTSTR pszTargetUrl, HANDLE hAbortEvent,
IMalloc *pAllocator, LPSTR *ppRatingOut)
{
HINTERNET hIC, hH;
HANDLE rgEvents[2];
BOOL fRet;
HRESULT hrRet = E_RATING_NOT_FOUND;
char rgBuf[10000], *pBuf; // PERF - way too much stack!
DWORD nRead, nBuf = sizeof(rgBuf) - 1;
LPSTR pszRatingServer;
if (!gPRSI->etstrRatingBureau.fIsInit())
{
return hrRet;
}
if (!LoadWinINet())
{
return hrRet;
}
pszRatingServer = gPRSI->etstrRatingBureau.Get();
BUFFER bufBureauHostName(INTERNET_MAX_HOST_NAME_LENGTH);
BUFFER bufBureauPath(INTERNET_MAX_PATH_LENGTH);
if (!bufBureauHostName.QueryPtr() || !bufBureauPath.QueryPtr())
{
return E_OUTOFMEMORY;
}
URL_COMPONENTS uc;
uc.dwStructSize = sizeof(uc);
uc.lpszScheme = NULL;
uc.dwSchemeLength = 0;
uc.lpszHostName = (LPSTR)bufBureauHostName.QueryPtr();
uc.dwHostNameLength = bufBureauHostName.QuerySize();
uc.lpszUserName = NULL;
uc.dwUserNameLength = 0;
uc.lpszPassword = NULL;
uc.dwPasswordLength = 0;
uc.lpszUrlPath = (LPSTR)bufBureauPath.QueryPtr();
uc.dwUrlPathLength = bufBureauPath.QuerySize();
uc.lpszExtraInfo = NULL;
uc.dwExtraInfoLength = 0;
if (!InternetCrackUrl(pszRatingServer, 0, 0, &uc))
{
return HRESULT_FROM_WIN32(GetLastError());
}
BUFFER bufRequest(INTERNET_MAX_URL_LENGTH + uc.dwUrlPathLength + cchRequestTemplate);
LPSTR pszRequest = (LPSTR)bufRequest.QueryPtr();
if (pszRequest == NULL)
{
return E_OUTOFMEMORY;
}
LPSTR pszCurrent = pszRequest;
::strcpyf(pszCurrent, uc.lpszUrlPath);
pszCurrent += uc.dwUrlPathLength;
::strcpyf(pszCurrent, szRequestTemplate);
pszCurrent += ::strlenf(pszCurrent);
/* Encode the target URL. */
EncodeUrl(pszTargetUrl, pszCurrent);
::strcatf(pszCurrent, "\"");
hIC = hH = NULL;
rgEvents[ABORT_EVENT] = hAbortEvent;
rgEvents[ASYNC_EVENT] = CreateEvent(NULL, FALSE, FALSE, NULL);
if (!rgEvents[ASYNC_EVENT])
{
goto STATE_CLEANUP;
}
hIC = InternetConnect(hI, uc.lpszHostName, uc.nPort, NULL, NULL,
INTERNET_SERVICE_HTTP, 0, (DWORD_PTR) rgEvents[ASYNC_EVENT]);
if (hIC == NULL || ShouldAbort(hAbortEvent))
{
goto STATE_CLEANUP;
}
hH = HttpOpenRequest(hIC, "GET", pszRequest, NULL, NULL, NULL,
INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD,
(DWORD_PTR) rgEvents[ASYNC_EVENT]);
if (hH == NULL || ShouldAbort(hAbortEvent))
{
goto STATE_CLEANUP;
}
fRet = HttpSendRequest(hH, NULL, (DWORD) 0, NULL, 0);
if (!fRet && !WaitForAsync(rgEvents))
{
goto STATE_CLEANUP;
}
pBuf = rgBuf;
nRead = 0;
do
{
fRet = InternetReadFile(hH, pBuf, nBuf-nRead, &nRead);
if (!fRet && !WaitForAsync(rgEvents))
{
goto STATE_CLEANUP;
}
if (nRead)
{
pBuf += nRead;
hrRet = NOERROR;
}
} while (nRead);
STATE_CLEANUP:
if (hH) InternetCloseHandle(hH);
if (hIC) InternetCloseHandle(hIC);
if (rgEvents[ASYNC_EVENT])
{
CloseHandle(rgEvents[ASYNC_EVENT]);
}
if (hrRet == NOERROR)
{
(*ppRatingOut) = (char*) pAllocator->Alloc((int)(pBuf - rgBuf + 1));
if (*ppRatingOut != NULL)
{
*pBuf = '\0';
LPSTR pszLabel = FindRatingLabel(rgBuf);
strcpyf(*ppRatingOut, pszLabel);
}
else
{
hrRet = ResultFromScode(E_OUTOFMEMORY);
}
}
if (hrRet == NOERROR)
{
hrRet = S_RATING_FOUND;
}
return hrRet;
}
STDMETHODIMP_(ULONG) CRORemoteSite::GetSortOrder(THIS)
{
return RATING_ORDER_REMOTESITE;
}