570 lines
15 KiB
C++
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;
|
|
}
|