1556 lines
38 KiB
C++
1556 lines
38 KiB
C++
//=======================================================================
|
|
//
|
|
// Copyright (c) 2001 Microsoft Corporation. All Rights Reserved.
|
|
//
|
|
// File: URLLogging.cpp
|
|
//
|
|
// Description:
|
|
//
|
|
// URL logging utility class
|
|
// This class helps you construct the server ping URL and
|
|
// then send the ping to the designed server.
|
|
//
|
|
// The default base URL is defined in IUIdent, under section [IUPingServer]
|
|
// and entry is "ServerUrl".
|
|
//
|
|
// This class implements single-thread version only. So it is suitable
|
|
// to call it at operation level, i.e., create a separate object
|
|
// for each operation in a single thread.
|
|
//
|
|
// The ping string send to the ping server has the following format:
|
|
// /wutrack.bin
|
|
// ?U=<user>
|
|
// &C=<client>
|
|
// &A=<activity>
|
|
// &I=<item>
|
|
// &D=<device>
|
|
// &P=<platform>
|
|
// &L=<language>
|
|
// &S=<status>
|
|
// &E=<error>
|
|
// &M=<message>
|
|
// &X=<proxy>
|
|
// where
|
|
// <user> a static 128-bit value that unique-ifies each copy
|
|
// of Windows installed. The class will automatically
|
|
// reuse one previously assigned to the running OS; or
|
|
// will generate one if it does not exist.
|
|
// <client> a string that identifies the entity that performed
|
|
// activity <activity>. Here are the possible values
|
|
// and their meanings:
|
|
// "iu" IU control
|
|
// "au" Automatic Updates
|
|
// "du" Dynamic Update
|
|
// "CDM" Code Download Manager
|
|
// "IU_SITE" IU Consumer site
|
|
// "IU_Corp" IU Catalog site
|
|
// <activity> a letter that identifies the activity performed.
|
|
// Here are the possible values and their meanings:
|
|
// "n" IU control initization
|
|
// "d" detection
|
|
// "s" self-update
|
|
// "w" download
|
|
// "i" installation
|
|
// <item> a string that identifies an update item.
|
|
// <device> a string that identifies either a device's PNPID when
|
|
// device driver not found during detection; or a
|
|
// PNPID/CompatID used by item <item> for activity
|
|
// <activity> if the item is a device driver.
|
|
// <platform> a string that identifies the platform of the running
|
|
// OS and processor architecture. The class will
|
|
// compute this value for the pingback.
|
|
// <language> a string that identifies the language of the OS
|
|
// binaries. The class will compute this value for the
|
|
// pingback.
|
|
// <status> a letter that specifies the status that activity
|
|
// <activity> reached. Here are the possible values and
|
|
// their meanings:
|
|
// "s" succeeded
|
|
// "r" succeeded (reboot required)
|
|
// "f" failed
|
|
// "c" cancelled by user
|
|
// "d" declined by user
|
|
// "n" no items
|
|
// "p" pending
|
|
// <error> a 32-bit error code in hex (w/o "0x" as prefix).
|
|
// <message> a string that provides additional information for the
|
|
// status <status>.
|
|
// <proxy> a 32-bit random value in hex for overriding proxy
|
|
// caching. This class will compute this value for
|
|
// each pingback.
|
|
//
|
|
//=======================================================================
|
|
|
|
#include <tchar.h>
|
|
#include <windows.h> // ZeroMemory()
|
|
#include <shlwapi.h> // PathAppend()
|
|
#include <stdlib.h> // srand(), rand(), malloc() and free()
|
|
#include <sys/timeb.h> // _ftime() and _timeb
|
|
#include <malloc.h> // malloc() and free()
|
|
|
|
#include <fileutil.h> // GetIndustryUpdateDirectory()
|
|
#include <logging.h> // LOG_Block, LOG_ErrorMsg, LOG_Error and LOG_Internet
|
|
#include <MemUtil.h> // USES_IU_CONVERSION, W2T() and T2W()
|
|
#include <osdet.h> // LookupLocaleString()
|
|
#include <download.h> // DownloadFile()
|
|
#include <wusafefn.h> // PathCchAppend()
|
|
#include <safefunc.h> // SafeFreeNULL()
|
|
#include <MISTSafe.h>
|
|
|
|
#include <URLLogging.h>
|
|
|
|
// Header of the log file
|
|
typedef struct tagULHEADER
|
|
{
|
|
WORD wVersion; // file version
|
|
} ULHEADER, PULHEADER;
|
|
|
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
#define CACHE_FILE_VERSION ((WORD) 10004) // must be bigger what we had in V3 (10001)
|
|
|
|
// bug 600602: must end all server URL with '/'
|
|
const TCHAR c_tszLiveServerUrl[] = _T("http://wustat.windows.com/");
|
|
|
|
|
|
HRESULT ValidateFileHeader(HANDLE hFile, BOOL fCheckHeader, BOOL fFixHeader);
|
|
|
|
#ifdef DBG
|
|
BOOL MustPingOffline(void)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
HKEY hkey;
|
|
|
|
if (NO_ERROR == RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
_T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate"),
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&hkey))
|
|
{
|
|
DWORD dwForceOfflinePing;
|
|
DWORD dwSize = sizeof(dwForceOfflinePing);
|
|
DWORD dwType;
|
|
|
|
if (NO_ERROR == RegQueryValueEx(
|
|
hkey,
|
|
_T("ForceOfflinePing"),
|
|
0,
|
|
&dwType,
|
|
(LPBYTE) &dwForceOfflinePing,
|
|
&dwSize))
|
|
{
|
|
if (REG_DWORD == dwType &&
|
|
sizeof(dwForceOfflinePing) == dwSize &&
|
|
1 == dwForceOfflinePing)
|
|
{
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
return fRet;
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
//
|
|
// PUBLIC MEMBER FUNCTIONS
|
|
//
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
CUrlLog::CUrlLog(void)
|
|
: m_ptszLiveServerUrl(NULL), m_ptszCorpServerUrl(NULL)
|
|
{
|
|
Init();
|
|
m_tszDefaultClientName[0] = _T('\0');
|
|
}
|
|
|
|
|
|
CUrlLog::CUrlLog(LPCTSTR ptszClientName, LPCTSTR ptszLiveServerUrl, LPCTSTR ptszCorpServerUrl)
|
|
: m_ptszLiveServerUrl(NULL), m_ptszCorpServerUrl(NULL)
|
|
{
|
|
Init();
|
|
(void) SetDefaultClientName(ptszClientName);
|
|
(void) SetLiveServerUrl(ptszLiveServerUrl);
|
|
(void) SetCorpServerUrl(ptszCorpServerUrl);
|
|
}
|
|
|
|
|
|
CUrlLog::~CUrlLog(void)
|
|
{
|
|
if (NULL != m_ptszLiveServerUrl)
|
|
{
|
|
free(m_ptszLiveServerUrl);
|
|
}
|
|
if (NULL != m_ptszCorpServerUrl)
|
|
{
|
|
free(m_ptszCorpServerUrl);
|
|
}
|
|
}
|
|
|
|
// Assume ptszServerUrl, if non-NULL, is of size INTERNET_MAX_URL_LENGTH in TCHARs
|
|
BOOL CUrlLog::SetServerUrl(LPCTSTR ptszUrl, LPTSTR & ptszServerUrl)
|
|
{
|
|
LPTSTR ptszEnd = NULL;
|
|
size_t cchRemaining = 0;
|
|
|
|
if (NULL == ptszUrl ||
|
|
_T('\0') == *ptszUrl)
|
|
{
|
|
SafeFreeNULL(ptszServerUrl);
|
|
}
|
|
else if (
|
|
// Ensure ptszServerUrl is malloc'ed
|
|
(NULL == ptszServerUrl &&
|
|
NULL == (ptszServerUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH))) ||
|
|
// Copy URL
|
|
FAILED(StringCchCopyEx(ptszServerUrl, INTERNET_MAX_URL_LENGTH, ptszUrl, &ptszEnd, &cchRemaining, MISTSAFE_STRING_FLAGS)) ||
|
|
// Ensure URL ending with '/'
|
|
(_T('/') != ptszEnd[-1] &&
|
|
FAILED(StringCchCopyEx(ptszEnd, cchRemaining, _T("/"), NULL, NULL, MISTSAFE_STRING_FLAGS))))
|
|
{
|
|
SafeFreeNULL(ptszServerUrl);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// Watch out for the size of m_tszDefaultClientName.
|
|
BOOL CUrlLog::SetDefaultClientName(LPCTSTR ptszClientName)
|
|
{
|
|
if (NULL == ptszClientName)
|
|
{
|
|
// E_INVALIDARG
|
|
m_tszDefaultClientName[0] = _T('\0');
|
|
return FALSE;
|
|
}
|
|
|
|
return SUCCEEDED(StringCchCopyEx(m_tszDefaultClientName, ARRAYSIZE(m_tszDefaultClientName), ptszClientName, NULL, NULL, MISTSAFE_STRING_FLAGS));
|
|
}
|
|
|
|
|
|
HRESULT CUrlLog::Ping(
|
|
BOOL fOnline, // online or offline ping
|
|
URLLOGDESTINATION destination, // live or corp WU ping server
|
|
PHANDLE phQuitEvents, // ptr to handles for cancelling the operation
|
|
UINT nQuitEventCount, // number of handles
|
|
URLLOGACTIVITY activity,// activity code
|
|
URLLOGSTATUS status, // status code
|
|
DWORD dwError, // error code
|
|
LPCTSTR ptszItemID, // uniquely identify an item
|
|
LPCTSTR ptszDeviceID, // PNPID or CompatID
|
|
LPCTSTR ptszMessage, // additional info
|
|
LPCTSTR ptszClientName) // client name string
|
|
{
|
|
LOG_Block("CUrlLog::Ping");
|
|
|
|
LPTSTR ptszUrl = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
switch (activity)
|
|
{
|
|
case URLLOGACTIVITY_Initialization: // fall thru
|
|
case URLLOGACTIVITY_Detection: // fall thru
|
|
case URLLOGACTIVITY_SelfUpdate: // fall thru
|
|
case URLLOGACTIVITY_Download: // fall thru
|
|
case URLLOGACTIVITY_Installation:
|
|
break;
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
goto CleanUp;
|
|
}
|
|
|
|
switch (status)
|
|
{
|
|
case URLLOGSTATUS_Success: // fall thru
|
|
case URLLOGSTATUS_Reboot: // fall thru
|
|
case URLLOGSTATUS_Failed: // fall thru
|
|
case URLLOGSTATUS_Cancelled: // fall thru
|
|
case URLLOGSTATUS_Declined: // fall thru
|
|
case URLLOGSTATUS_NoItems: // fall thru
|
|
case URLLOGSTATUS_Pending:
|
|
break;
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
goto CleanUp;
|
|
}
|
|
|
|
//
|
|
// handle optional (nullable) arguments
|
|
//
|
|
if (NULL == ptszClientName)
|
|
{
|
|
ptszClientName = m_tszDefaultClientName;
|
|
}
|
|
|
|
if (_T('\0') == *ptszClientName)
|
|
{
|
|
LOG_Error(_T("client name not initialized"));
|
|
hr = E_INVALIDARG;
|
|
goto CleanUp;
|
|
}
|
|
|
|
switch (destination)
|
|
{
|
|
case URLLOGDESTINATION_DEFAULT:
|
|
destination = (
|
|
NULL == m_ptszCorpServerUrl ||
|
|
_T('\0') == *m_ptszCorpServerUrl) ?
|
|
URLLOGDESTINATION_LIVE :
|
|
URLLOGDESTINATION_CORPWU;
|
|
break;
|
|
case URLLOGDESTINATION_LIVE: // fall thru
|
|
case URLLOGDESTINATION_CORPWU:
|
|
break;
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
goto CleanUp;
|
|
}
|
|
|
|
LPCTSTR ptszServerUrl;
|
|
|
|
if (URLLOGDESTINATION_LIVE == destination)
|
|
{
|
|
if (NULL != m_ptszLiveServerUrl)
|
|
{
|
|
ptszServerUrl = m_ptszLiveServerUrl;
|
|
}
|
|
else
|
|
{
|
|
ptszServerUrl = c_tszLiveServerUrl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ptszServerUrl = m_ptszCorpServerUrl;
|
|
}
|
|
|
|
if (NULL == ptszServerUrl ||
|
|
_T('\0') == *ptszServerUrl)
|
|
{
|
|
LOG_Error(_T("status server Url not initialized"));
|
|
hr = E_INVALIDARG;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (NULL == (ptszUrl = (TCHAR*) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (FAILED(hr = MakePingUrl(
|
|
ptszUrl,
|
|
INTERNET_MAX_URL_LENGTH,
|
|
ptszServerUrl,
|
|
ptszClientName,
|
|
activity,
|
|
ptszItemID,
|
|
ptszDeviceID,
|
|
status,
|
|
dwError,
|
|
ptszMessage)))
|
|
{
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (fOnline)
|
|
{
|
|
hr = PingStatus(destination, ptszUrl, phQuitEvents, nQuitEventCount);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
(void) Flush(phQuitEvents, nQuitEventCount);
|
|
goto CleanUp;
|
|
}
|
|
}
|
|
|
|
{
|
|
USES_IU_CONVERSION;
|
|
|
|
LPWSTR pwszUrl = T2W(ptszUrl);
|
|
HRESULT hr2;
|
|
|
|
if (NULL == pwszUrl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
ULENTRYHEADER ulentryheader;
|
|
ulentryheader.progress = URLLOGPROGRESS_ToBeSent;
|
|
ulentryheader.destination = destination;
|
|
ulentryheader.wRequestSize = lstrlen(ptszUrl) + 1;
|
|
ulentryheader.wServerUrlLen = (WORD) lstrlen(ptszServerUrl);
|
|
|
|
if (SUCCEEDED(hr2 = SaveEntry(ulentryheader, pwszUrl)))
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else if (SUCCEEDED(hr))
|
|
{
|
|
hr = hr2;
|
|
}
|
|
}
|
|
|
|
CleanUp:
|
|
if (NULL != ptszUrl)
|
|
{
|
|
free(ptszUrl);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
//
|
|
// PRIVATE MEMBER FUNCTIONS
|
|
//
|
|
// ----------------------------------------------------------------------------------
|
|
|
|
// Init member variables within a constructor. No memory clean-up done here.
|
|
void CUrlLog::Init()
|
|
{
|
|
LookupPingID();
|
|
LookupPlatform();
|
|
LookupSystemLanguage();
|
|
GetLogFileName();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------------------------------
|
|
// Construct a URL used to ping server
|
|
//
|
|
// Returned value indicates success/failure
|
|
// ----------------------------------------------------------------------------------
|
|
HRESULT CUrlLog::MakePingUrl(
|
|
LPTSTR ptszUrl, // buffer to receive result
|
|
int cChars, // the number of chars this buffer can take, including ending null
|
|
LPCTSTR ptszBaseUrl, // server URL
|
|
LPCTSTR ptszClientName, // which client called
|
|
URLLOGACTIVITY activity,
|
|
LPCTSTR ptszItemID,
|
|
LPCTSTR ptszDeviceID,
|
|
URLLOGSTATUS status,
|
|
DWORD dwError, // return code of activity
|
|
LPCTSTR ptszMessage)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPTSTR ptszEscapedItemID = NULL;
|
|
LPTSTR ptszEscapedDeviceID = NULL;
|
|
LPTSTR ptszEscapedMessage = NULL;
|
|
|
|
LOG_Block("CUrlLog::MakePingUrl");
|
|
|
|
// Retry to get info strings if we failed within the constructor.
|
|
if (_T('\0') == m_tszPlatform[0] ||
|
|
_T('\0') == m_tszLanguage[0])
|
|
{
|
|
LOG_Error(_T("Invalid platform or language info string"));
|
|
hr = E_UNEXPECTED;
|
|
goto CleanUp;
|
|
}
|
|
|
|
// allocate enough memory for URL manipulation. Since the buffer needs
|
|
// to be at least 2Kbytes in size, stack buffer is not suitable here.
|
|
// we involve mem utility to similate stack memory allocation
|
|
if ((NULL != ptszItemID &&
|
|
(NULL == (ptszEscapedItemID = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
|
|
!EscapeString(ptszItemID, ptszEscapedItemID, INTERNET_MAX_URL_LENGTH))) ||
|
|
(NULL != ptszDeviceID &&
|
|
(NULL == (ptszEscapedDeviceID = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
|
|
!EscapeString(ptszDeviceID, ptszEscapedDeviceID, INTERNET_MAX_URL_LENGTH))) ||
|
|
(NULL != ptszMessage &&
|
|
(NULL == (ptszEscapedMessage = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)) ||
|
|
!EscapeString(ptszMessage, ptszEscapedMessage, INTERNET_MAX_URL_LENGTH))))
|
|
{
|
|
// Either out-of-memory or the escaped string is too lengthy.
|
|
LOG_Error(_T("Out of memory or EscapeString failure"));
|
|
hr = E_OUTOFMEMORY; // actually could be HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) as well
|
|
goto CleanUp;
|
|
}
|
|
|
|
const TCHAR c_tszEmpty[] = _T("");
|
|
|
|
// Use system time as proxy cache breaker
|
|
SYSTEMTIME st;
|
|
|
|
GetSystemTime(&st);
|
|
|
|
hr = StringCchPrintfEx(
|
|
ptszUrl,
|
|
cChars,
|
|
NULL,
|
|
NULL,
|
|
MISTSAFE_STRING_FLAGS,
|
|
_T("%swutrack.bin?U=%s&C=%s&A=%c&I=%s&D=%s&P=%s&L=%s&S=%c&E=%08x&M=%s&X=%02d%02d%02d%02d%02d%02d%03d"),
|
|
NULL == ptszBaseUrl ? c_tszEmpty : ptszBaseUrl, // server URL
|
|
m_tszPingID, // ping ID
|
|
ptszClientName, // client name
|
|
activity, // activity code
|
|
NULL == ptszEscapedItemID ? c_tszEmpty : ptszEscapedItemID, // escaped item ID
|
|
NULL == ptszEscapedDeviceID ? c_tszEmpty : ptszEscapedDeviceID, // escaped device ID
|
|
m_tszPlatform, // platform info
|
|
m_tszLanguage, // sys lang info
|
|
status, // status code
|
|
dwError, // activity error code
|
|
NULL == ptszEscapedMessage ? c_tszEmpty : ptszEscapedMessage, // escaped message str
|
|
st.wYear % 100, // proxy override
|
|
st.wMonth,
|
|
st.wDay,
|
|
st.wHour,
|
|
st.wMinute,
|
|
st.wSecond,
|
|
st.wMilliseconds);
|
|
|
|
CleanUp:
|
|
if (NULL != ptszEscapedItemID)
|
|
{
|
|
free(ptszEscapedItemID);
|
|
}
|
|
if (NULL != ptszEscapedDeviceID)
|
|
{
|
|
free(ptszEscapedDeviceID);
|
|
}
|
|
if (NULL != ptszEscapedMessage)
|
|
{
|
|
free(ptszEscapedMessage);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Obtain the existing ping ID from the registry, or generate one if not available.
|
|
void CUrlLog::LookupPingID(void)
|
|
{
|
|
const TCHAR c_tszRegKeyWU[] = _T("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\WindowsUpdate");
|
|
const TCHAR c_tszRegUrlLogPingID[] = _T("PingID");
|
|
|
|
BOOL fRet = FALSE;
|
|
LONG lErr;
|
|
HKEY hkey;
|
|
UUID uuidPingID;
|
|
|
|
LOG_Block("CUrlLog::LookupPingID");
|
|
|
|
//fixcode: wrap registry manipulation w/ a critical section
|
|
|
|
if (NO_ERROR == (lErr = RegOpenKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
c_tszRegKeyWU,
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_SET_VALUE,
|
|
&hkey)))
|
|
{
|
|
BOOL fFixValue = TRUE;
|
|
DWORD dwSize = sizeof(uuidPingID);
|
|
DWORD dwType;
|
|
|
|
lErr = RegQueryValueEx(
|
|
hkey,
|
|
c_tszRegUrlLogPingID,
|
|
0,
|
|
&dwType,
|
|
(LPBYTE) &uuidPingID,
|
|
&dwSize);
|
|
if (NO_ERROR == lErr)
|
|
{
|
|
if (REG_BINARY == dwType && sizeof(uuidPingID) == dwSize)
|
|
{
|
|
// successfully read ping ID from registry
|
|
fFixValue = FALSE;
|
|
fRet = TRUE;
|
|
}
|
|
}
|
|
else if (ERROR_MORE_DATA == lErr || ERROR_FILE_NOT_FOUND == lErr)
|
|
{
|
|
// Data not of right length or not found.
|
|
// We will try to fix it. Treat it as no error for now.
|
|
lErr = NO_ERROR;
|
|
}
|
|
|
|
if (NO_ERROR == lErr)
|
|
{
|
|
if (fFixValue)
|
|
{
|
|
MakeUUID(&uuidPingID);
|
|
lErr = RegSetValueEx(
|
|
hkey,
|
|
c_tszRegUrlLogPingID,
|
|
0,
|
|
REG_BINARY,
|
|
(CONST BYTE*) &uuidPingID,
|
|
sizeof(uuidPingID));
|
|
if (NO_ERROR == lErr)
|
|
{
|
|
fRet = TRUE; // This is not a final value yet. Still depends on RegCloseKey().
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lErr);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lErr);
|
|
}
|
|
#endif
|
|
|
|
if (NO_ERROR != (lErr = RegCloseKey(hkey)))
|
|
{
|
|
if (fFixValue)
|
|
{
|
|
fRet = FALSE;
|
|
}
|
|
LOG_ErrorMsg(lErr);
|
|
}
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
LOG_ErrorMsg(lErr);
|
|
}
|
|
#endif
|
|
|
|
if (!fRet)
|
|
{
|
|
// Only happens if something failed.
|
|
// Make a ping ID of zeroes.
|
|
ZeroMemory(&uuidPingID, sizeof(uuidPingID));
|
|
}
|
|
|
|
LPTSTR p = m_tszPingID;
|
|
LPBYTE q = (LPBYTE) &uuidPingID;
|
|
for (int i = 0; i<sizeof(uuidPingID); i++, q++)
|
|
{
|
|
BYTE nibble = *q >> 4; // high nibble
|
|
*p++ = nibble >= 0xA ? _T('a') + (nibble - 0xA) : _T('0') + nibble;
|
|
nibble = *q & 0xF; // low nibble
|
|
*p++ = nibble >= 0xA ? _T('a') + (nibble - 0xA) : _T('0') + nibble;
|
|
}
|
|
*p = _T('\0');
|
|
}
|
|
|
|
|
|
|
|
// Obtain platfrom info for ping
|
|
void CUrlLog::LookupPlatform(void)
|
|
{
|
|
LOG_Block("CUrlLog::LookupPlatform");
|
|
|
|
m_tszPlatform[0] = _T('\0');
|
|
|
|
OSVERSIONINFOEX osversioninfoex;
|
|
|
|
ZeroMemory(&osversioninfoex, sizeof(osversioninfoex));
|
|
|
|
// pretend to be OSVERSIONINFO for W9X/Mil
|
|
osversioninfoex.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
if (!GetVersionEx((LPOSVERSIONINFO) &osversioninfoex))
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
return;
|
|
}
|
|
|
|
if (VER_PLATFORM_WIN32_NT == osversioninfoex.dwPlatformId &&
|
|
(5 <= osversioninfoex.dwMajorVersion ||
|
|
(4 == osversioninfoex.dwMajorVersion &&
|
|
6 <= osversioninfoex.wServicePackMajor)))
|
|
{
|
|
// OS is Windows NT/2000 or later: Windows NT 4.0 SP6 or later.
|
|
// It supports OSVERSIONINFOEX.
|
|
osversioninfoex.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); // use actual size
|
|
|
|
if (!GetVersionEx((LPOSVERSIONINFO) &osversioninfoex))
|
|
{
|
|
LOG_ErrorMsg(GetLastError());
|
|
return;
|
|
}
|
|
}
|
|
|
|
SYSTEM_INFO systeminfo;
|
|
|
|
GetSystemInfo(&systeminfo);
|
|
|
|
(void) StringCchPrintfEx(
|
|
m_tszPlatform,
|
|
ARRAYSIZE(m_tszPlatform),
|
|
NULL,
|
|
NULL,
|
|
MISTSAFE_STRING_FLAGS,
|
|
_T("%lx.%lx.%lx.%lx.%x.%x.%x"),
|
|
osversioninfoex.dwMajorVersion,
|
|
osversioninfoex.dwMinorVersion,
|
|
osversioninfoex.dwBuildNumber,
|
|
osversioninfoex.dwPlatformId,
|
|
osversioninfoex.wSuiteMask,
|
|
osversioninfoex.wProductType,
|
|
systeminfo.wProcessorArchitecture);
|
|
}
|
|
|
|
|
|
|
|
// Obtain system language info for ping
|
|
void CUrlLog::LookupSystemLanguage(void)
|
|
{
|
|
LOG_Block("CUrlLog::LookupSystemLanguage");
|
|
|
|
(void) LookupLocaleString(m_tszLanguage, ARRAYSIZE(m_tszLanguage), FALSE);
|
|
|
|
if (0 == _tcscmp(m_tszLanguage, _T("Error")))
|
|
{
|
|
LOG_Error(_T("call to LookupLocaleString() failed."));
|
|
m_tszLanguage[0] = _T('\0');
|
|
}
|
|
}
|
|
|
|
|
|
// Ping server to report status
|
|
// ptszUrl - the URL string to be pinged
|
|
// phQuitEvents - ptr to handles for cancelling the operation
|
|
// nQuitEventCount - number of handles
|
|
HRESULT CUrlLog::PingStatus(URLLOGDESTINATION destination, LPCTSTR ptszUrl, PHANDLE phQuitEvents, UINT nQuitEventCount) const
|
|
{
|
|
#ifdef DBG
|
|
LOG_Block("CUrlLog::PingStatus");
|
|
|
|
LOG_Internet(_T("Ping request=\"%s\""), ptszUrl);
|
|
|
|
if (MustPingOffline())
|
|
{
|
|
LOG_Internet(_T("ForceOfflinePing = 1"));
|
|
return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
|
|
}
|
|
#endif
|
|
|
|
if (!IsConnected(ptszUrl, URLLOGDESTINATION_LIVE == destination))
|
|
{
|
|
// There is no connection.
|
|
LOG_ErrorMsg(ERROR_CONNECTION_INVALID);
|
|
return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
|
|
}
|
|
|
|
if (!HandleEvents(phQuitEvents, nQuitEventCount))
|
|
{
|
|
LOG_ErrorMsg(E_ABORT);
|
|
return E_ABORT;
|
|
}
|
|
|
|
TCHAR tszIUdir[MAX_PATH];
|
|
|
|
GetIndustryUpdateDirectory(tszIUdir);
|
|
|
|
DWORD dwFlags = WUDF_CHECKREQSTATUSONLY; // we don't actually need a file,
|
|
// just need to check return code
|
|
if (URLLOGDESTINATION_CORPWU == destination)
|
|
{
|
|
// don't allow proxy if destination is corp WU
|
|
dwFlags |= WUDF_DONTALLOWPROXY;
|
|
}
|
|
|
|
HRESULT hr = DownloadFile(
|
|
ptszUrl,
|
|
tszIUdir, // local directory to download file to
|
|
NULL, // optional local file name for downloaded file
|
|
// if pszLocalPath doesn't contain a file name
|
|
NULL, // ptr to bytes downloaded for this file
|
|
phQuitEvents, // quit event, if signalled, abort downloading
|
|
nQuitEventCount,
|
|
NULL,
|
|
NULL, // parameter for call back function to use
|
|
dwFlags
|
|
);
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_Error(_T("DownloadFile() returned error %lx"), hr);
|
|
}
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Obtain file names for offline ping
|
|
void CUrlLog::GetLogFileName(void)
|
|
{
|
|
const TCHAR c_tszLogFile_Local[] = _T("urllog.dat");
|
|
|
|
GetIndustryUpdateDirectory(m_tszLogFile);
|
|
|
|
if (FAILED(PathCchAppend(m_tszLogFile, ARRAYSIZE(m_tszLogFile), c_tszLogFile_Local)))
|
|
{
|
|
m_tszLogFile[0] = _T('\0');
|
|
}
|
|
}
|
|
|
|
|
|
// Read cache entry header and request in entry
|
|
// hFile - an open file handle to read the entry from
|
|
// ulentryheader - reference to the struct to store the entry header
|
|
// pwszBuffer - the WCHAR buffer to store the request (including trailing null character) in the entry
|
|
// dwBufferSize - the size of buffer in WCHARs
|
|
// Returned value:
|
|
// S_OK - entry successfully read
|
|
// S_FALSE - no more entry to read from the file
|
|
// other - error codes
|
|
HRESULT CUrlLog::ReadEntry(HANDLE hFile, ULENTRYHEADER & ulentryheader, LPWSTR pwszBuffer, DWORD dwBufferSize) const
|
|
{
|
|
LOG_Block("CUrlLog::ReadEntry");
|
|
|
|
DWORD dwBytes;
|
|
DWORD dwErr;
|
|
|
|
if (!ReadFile(
|
|
hFile,
|
|
&ulentryheader,
|
|
sizeof(ulentryheader),
|
|
&dwBytes,
|
|
NULL))
|
|
{
|
|
// We failed to read the entry header.
|
|
// There is nothing we can do at this point.
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
if (0 == dwBytes)
|
|
{
|
|
// This is the end of the file.
|
|
// There is no other entries after this point.
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (sizeof(ulentryheader) < dwBytes ||
|
|
(URLLOGPROGRESS_ToBeSent != ulentryheader.progress &&
|
|
URLLOGPROGRESS_Sent != ulentryheader.progress) ||
|
|
(URLLOGDESTINATION_LIVE != ulentryheader.destination &&
|
|
URLLOGDESTINATION_CORPWU != ulentryheader.destination) ||
|
|
dwBufferSize < ulentryheader.wRequestSize ||
|
|
ulentryheader.wRequestSize <= ulentryheader.wServerUrlLen)
|
|
{
|
|
LOG_Error(_T("Invalid entry header"));
|
|
return E_FAIL;
|
|
}
|
|
|
|
if (!ReadFile(
|
|
hFile,
|
|
pwszBuffer,
|
|
sizeof(WCHAR) * ulentryheader.wRequestSize,
|
|
&dwBytes,
|
|
NULL))
|
|
{
|
|
// We failed to read the string in the entry.
|
|
dwErr = GetLastError();
|
|
LOG_ErrorMsg(dwErr);
|
|
return HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
|
|
if (dwBytes < sizeof(WCHAR) * ulentryheader.wRequestSize ||
|
|
_T('\0') != pwszBuffer[ulentryheader.wRequestSize-1] ||
|
|
ulentryheader.wRequestSize-1 != lstrlenW(pwszBuffer))
|
|
{
|
|
// The entry does not contain the complete string.
|
|
return E_FAIL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Save a string to the log file
|
|
// destination - going to the live or corp WU ping server
|
|
// wServerUrlLen - length of server URL part of the request, in WCHARs (not including trailing NULL)
|
|
// pwszString - the string to be saved into the specific log file
|
|
// Returned value:
|
|
// S_OK - entry was written to file
|
|
// S_FALSE - the file was created by a CUrlLog class of newer version than this; entry was not written to file
|
|
// other - error codes; entry was not written to file
|
|
HRESULT CUrlLog::SaveEntry(ULENTRYHEADER & ulentryheader, LPCWSTR pwszString) const
|
|
{
|
|
HRESULT hr;
|
|
BOOL fDeleteFile = FALSE;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
DWORD dwBytes;
|
|
|
|
LOG_Block("CUrlLog::SaveEntry");
|
|
|
|
LOG_Internet(
|
|
_T("destination = %s"),
|
|
URLLOGDESTINATION_LIVE == ulentryheader.destination ? _T("live") : _T("corp WU"));
|
|
|
|
if (_T('\0') == m_tszLogFile[0])
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
LOG_Error(_T("log file name not initialized"));
|
|
goto CleanUp;
|
|
}
|
|
|
|
if(INVALID_HANDLE_VALUE == (hFile = CreateFile(
|
|
m_tszLogFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, // no sharing
|
|
NULL,
|
|
OPEN_ALWAYS,
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_RANDOM_ACCESS,
|
|
NULL)))
|
|
{
|
|
// We failed to open or create the file.
|
|
// Someone may be currently using it.
|
|
|
|
//fixcode: allow multiple pingback users
|
|
// access the file sequentially.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
hr = ValidateFileHeader(hFile, ERROR_ALREADY_EXISTS == GetLastError(), TRUE);
|
|
if (S_OK != hr)
|
|
{
|
|
if (S_FALSE != hr)
|
|
{
|
|
// The file header is bad or there was problem validating it.
|
|
fDeleteFile = TRUE; // destroy the file and fail the function
|
|
}
|
|
// else
|
|
// The file header has a newer version than this library code.
|
|
// Keep the file around.
|
|
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
// Set outselves to the right position before writing to the file.
|
|
DWORD nCurrPos;
|
|
|
|
if (INVALID_SET_FILE_POINTER == (nCurrPos = SetFilePointer(
|
|
hFile,
|
|
0,
|
|
NULL,
|
|
FILE_END)))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Write the entry to the log.
|
|
if (!WriteFile(
|
|
hFile,
|
|
&ulentryheader,
|
|
sizeof(ulentryheader),
|
|
&dwBytes,
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
sizeof(ulentryheader) != dwBytes)
|
|
{
|
|
LOG_Error(_T("Failed to write entry header to file (%d bytes VS %d bytes)"), sizeof(ulentryheader), dwBytes);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
!WriteFile(
|
|
hFile,
|
|
pwszString,
|
|
sizeof(WCHAR) * ulentryheader.wRequestSize,
|
|
&dwBytes,
|
|
NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
|
|
if (SUCCEEDED(hr) &&
|
|
sizeof(WCHAR) * ulentryheader.wRequestSize != dwBytes)
|
|
{
|
|
LOG_Error(_T("Failed to write entry header to file (%d bytes VS %d bytes)"), sizeof(WCHAR) * ulentryheader.wRequestSize, dwBytes);
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
// We failed to wrote the entry into the log.
|
|
if (FAILED(hr))
|
|
{
|
|
// We don't want to get rid of the other entries.
|
|
// We can only try to remove the portion of the entry
|
|
// we have appended from the file.
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(
|
|
hFile,
|
|
nCurrPos,
|
|
NULL,
|
|
FILE_BEGIN) ||
|
|
!SetEndOfFile(hFile))
|
|
{
|
|
// We failed to remove the new entry.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
fDeleteFile = TRUE;
|
|
}
|
|
// else
|
|
// We successfully got rid of this entry.
|
|
// And preserved existing entries in log.
|
|
}
|
|
|
|
CleanUp:
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
if (fDeleteFile)
|
|
{
|
|
(void) DeleteFile(m_tszLogFile);
|
|
// We don't delete the log file if the operation was successful.
|
|
// Thus, no need to modify the fRet value even if DeleteFile() failed.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Send all pending (offline) ping requests to server
|
|
HRESULT CUrlLog::Flush(PHANDLE phQuitEvents, UINT nQuitEventCount)
|
|
{
|
|
LPWSTR pwszBuffer = NULL;
|
|
LPTSTR ptszUrl = NULL;
|
|
HANDLE hFile = INVALID_HANDLE_VALUE;
|
|
BOOL fKeepFile = FALSE;
|
|
DWORD dwErr;
|
|
HRESULT hr;
|
|
|
|
LOG_Block("CUrlLog::Flush");
|
|
|
|
if (NULL == (pwszBuffer = (LPWSTR) malloc(sizeof(WCHAR) * INTERNET_MAX_URL_LENGTH)) ||
|
|
NULL == (ptszUrl = (LPTSTR) malloc(sizeof(TCHAR) * INTERNET_MAX_URL_LENGTH)))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto CleanUp;
|
|
}
|
|
|
|
if (_T('\0') == m_tszLogFile[0])
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
LOG_Error(_T("log file name not initialized"));
|
|
goto CleanUp;
|
|
}
|
|
|
|
// Open existing log
|
|
if(INVALID_HANDLE_VALUE == (hFile = CreateFile(
|
|
m_tszLogFile,
|
|
GENERIC_READ | GENERIC_WRITE,
|
|
0, // no sharing
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_HIDDEN | FILE_FLAG_RANDOM_ACCESS,
|
|
NULL)))
|
|
{
|
|
// We failed to open the file.
|
|
// The file may not exist or someone may be currently using it.
|
|
dwErr = GetLastError();
|
|
|
|
if (ERROR_FILE_NOT_FOUND == dwErr)
|
|
{
|
|
// We are done. There is nothing more to do.
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
//fixcode: allow multiple pingback users
|
|
// access the file sequentially.
|
|
LOG_ErrorMsg(dwErr);
|
|
hr = HRESULT_FROM_WIN32(dwErr);
|
|
}
|
|
goto CleanUp;
|
|
}
|
|
|
|
|
|
// File opened. Check header.
|
|
hr = ValidateFileHeader(hFile, TRUE, FALSE);
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
if (S_FALSE == hr)
|
|
{
|
|
// The file header has a newer version than this library code.
|
|
goto CleanUp; // Keep the file around.
|
|
}
|
|
// else
|
|
// The file header is bad or there was problem validating it.
|
|
// destroy the file and fail the function
|
|
}
|
|
else
|
|
{
|
|
BOOL fLiveServerFailed = FALSE;
|
|
BOOL fCorpServerFailed = FALSE;
|
|
|
|
// It is time to read an entry.
|
|
for (;;)
|
|
{
|
|
ULENTRYHEADER ulentryheader;
|
|
|
|
if (!HandleEvents(phQuitEvents, nQuitEventCount))
|
|
{
|
|
hr = E_ABORT;
|
|
LOG_ErrorMsg(hr);
|
|
break;
|
|
}
|
|
|
|
// Assume we are in the right position to read
|
|
// the next entry from the file.
|
|
|
|
// Read the entry header and request in entry.
|
|
if (FAILED(hr = ReadEntry(hFile, ulentryheader, pwszBuffer, INTERNET_MAX_URL_LENGTH)))
|
|
{
|
|
LOG_Error(_T("Failed to read entry from cache (%#lx)"), hr);
|
|
break;
|
|
}
|
|
|
|
if (S_FALSE == hr)
|
|
{
|
|
// There are no more unprocessed entries.
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
|
|
// We have successfully read the entry from the cache file.
|
|
if (URLLOGPROGRESS_Sent != ulentryheader.progress)
|
|
{
|
|
// The entry hasn't been successfully sent yet.
|
|
LPCTSTR ptszBaseUrl = NULL;
|
|
BOOL *pfWhichServerFailed;
|
|
|
|
if (URLLOGDESTINATION_LIVE == ulentryheader.destination)
|
|
{
|
|
ptszBaseUrl = m_ptszLiveServerUrl;
|
|
pfWhichServerFailed = &fLiveServerFailed;
|
|
}
|
|
else
|
|
{
|
|
ptszBaseUrl = m_ptszCorpServerUrl;
|
|
pfWhichServerFailed = &fCorpServerFailed;
|
|
}
|
|
|
|
if (*pfWhichServerFailed)
|
|
{
|
|
continue; // this base URL has failed before. go on to the next entry.
|
|
}
|
|
|
|
LPTSTR ptszRelativeUrl;
|
|
|
|
USES_IU_CONVERSION;
|
|
|
|
if (NULL == (ptszRelativeUrl = W2T(pwszBuffer + ulentryheader.wServerUrlLen)))
|
|
{
|
|
// Running out of memory. Will retry later.
|
|
hr = E_OUTOFMEMORY;
|
|
break;
|
|
}
|
|
|
|
if (NULL != ptszBaseUrl)
|
|
{
|
|
// Form the request URL
|
|
DWORD dwUrlLen = INTERNET_MAX_URL_LENGTH;
|
|
|
|
if (S_OK != UrlCombine( // requires IE3 for 95/NT4
|
|
ptszBaseUrl,
|
|
ptszRelativeUrl,
|
|
ptszUrl,
|
|
&dwUrlLen,
|
|
URL_DONT_SIMPLIFY))
|
|
{
|
|
// Either the buffer is too small to hold both the base and
|
|
// the relative URLs, or the host name is invalid.
|
|
// We will retry this entry just in case we will have a
|
|
// shorter/better host name.
|
|
fKeepFile = TRUE;
|
|
continue; // go on to the next entry
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
if (FAILED(hr = StringCchCopyExW(ptszUrl, INTERNET_MAX_URL_LENGTH, pwszBuffer, NULL, NULL, MISTSAFE_STRING_FLAGS)))
|
|
{
|
|
LOG_Error(_T("Failed to construct ping URL (%#lx)"), hr);
|
|
break;
|
|
}
|
|
#else
|
|
if (0 == AtlW2AHelper(ptszUrl, pwszBuffer, INTERNET_MAX_URL_LENGTH))
|
|
{
|
|
// The buffer is probably too small to hold both the base and
|
|
// the relative URLs. We will retry this entry just in case
|
|
// we will have a shorter/better host name.
|
|
fKeepFile = TRUE;
|
|
continue; // go on to the next entry
|
|
}
|
|
#endif
|
|
}
|
|
|
|
hr = PingStatus(ulentryheader.destination, ptszUrl, phQuitEvents, nQuitEventCount);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (E_ABORT == hr)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// We will resend this entry later.
|
|
LOG_Internet(_T("Failed to send message (%#lx). Will retry later."), hr);
|
|
*pfWhichServerFailed = TRUE;
|
|
fKeepFile = TRUE;
|
|
|
|
if (fLiveServerFailed && fCorpServerFailed)
|
|
{
|
|
// Failed to send ping messages to both destinations.
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
DWORD dwBytes;
|
|
|
|
// Mark the entry off the cache file.
|
|
ulentryheader.progress = URLLOGPROGRESS_Sent;
|
|
// Go to the beginning of the current entry and change the entry header.
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(
|
|
hFile,
|
|
- ((LONG) (sizeof(ulentryheader) +
|
|
sizeof(WCHAR) * ulentryheader.wRequestSize)),
|
|
NULL,
|
|
FILE_CURRENT) ||
|
|
!WriteFile(
|
|
hFile,
|
|
&ulentryheader,
|
|
sizeof(ulentryheader),
|
|
&dwBytes,
|
|
NULL))
|
|
{
|
|
// We failed to mark this entry 'sent'.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
break;
|
|
}
|
|
|
|
if (sizeof(ulentryheader) != dwBytes)
|
|
{
|
|
// We failed to write the header.
|
|
LOG_Error(_T("Failed to write header (%d bytes VS %d bytes)"), sizeof(ulentryheader), dwBytes);
|
|
hr = E_FAIL;
|
|
break;
|
|
}
|
|
|
|
// Set the file pointer to the start of the next entry
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(
|
|
hFile,
|
|
sizeof(WCHAR) * ulentryheader.wRequestSize,
|
|
NULL,
|
|
FILE_CURRENT))
|
|
{
|
|
// We failed to skip the current entry.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(hFile);
|
|
hFile = INVALID_HANDLE_VALUE;
|
|
|
|
if ((FAILED(hr) && E_ABORT != hr && E_OUTOFMEMORY != hr) ||
|
|
(SUCCEEDED(hr) && !fKeepFile))
|
|
{
|
|
(void) DeleteFile(m_tszLogFile);
|
|
}
|
|
|
|
CleanUp:
|
|
if (NULL != pwszBuffer)
|
|
{
|
|
free(pwszBuffer);
|
|
}
|
|
if (NULL != ptszUrl)
|
|
{
|
|
free(ptszUrl);
|
|
}
|
|
if (INVALID_HANDLE_VALUE != hFile)
|
|
{
|
|
CloseHandle(hFile);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
// Escape unsafe chars in a TCHAR string
|
|
// Returned value: non-zero if successful; zero otherwise
|
|
BOOL EscapeString(
|
|
LPCTSTR ptszUnescaped,
|
|
LPTSTR ptszBuffer,
|
|
DWORD dwCharsInBuffer)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
LOG_Block("CUrlLog::EscapeString");
|
|
|
|
if (NULL != ptszUnescaped &&
|
|
NULL != ptszBuffer &&
|
|
0 != dwCharsInBuffer)
|
|
{
|
|
for (DWORD i=0, j=0; _T('\0') != ptszUnescaped[i] && j+1<dwCharsInBuffer; i++, j++)
|
|
{
|
|
TCHAR tch = ptszUnescaped[i];
|
|
|
|
if ((_T('a') <= tch && _T('z') >= tch) ||
|
|
(_T('A') <= tch && _T('Z') >= tch) ||
|
|
(_T('0') <= tch && _T('9') >= tch) ||
|
|
NULL != _tcschr(_T("-_.!~*'()"), tch))
|
|
{
|
|
ptszBuffer[j] = tch;
|
|
}
|
|
else if (j+3 >= dwCharsInBuffer)
|
|
{
|
|
// We don't have enough buffer to hold the escaped string.
|
|
// Bail out.
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
TCHAR nibble = tch >> 4;
|
|
|
|
ptszBuffer[j++] = _T('%');
|
|
ptszBuffer[j++] = nibble + (nibble >= 0x0a ? _T('A') - 0x0a : _T('0'));
|
|
nibble = tch & 0x0f;
|
|
ptszBuffer[j] = nibble + (nibble >= 0x0a ? _T('A') - 0x0a : _T('0'));
|
|
}
|
|
}
|
|
|
|
if (_T('\0') == ptszUnescaped[i])
|
|
{
|
|
ptszBuffer[j] = _T('\0');
|
|
fRet = TRUE;
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
// Couldn't escape the whole string due to insufficient buffer.
|
|
LOG_ErrorMsg(ERROR_INSUFFICIENT_BUFFER);
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef DBG
|
|
else
|
|
{
|
|
LOG_ErrorMsg(E_INVALIDARG);
|
|
}
|
|
#endif
|
|
|
|
return fRet;
|
|
}
|
|
|
|
|
|
|
|
// Create a UUID that is not linked to MAC address of a NIC, if any, on the system.
|
|
// pUuid - ptr to the UUID structure to hold the returning value.
|
|
void MakeUUID(UUID* pUuid)
|
|
{
|
|
HRESULT hr;
|
|
OSVERSIONINFO osverinfo;
|
|
|
|
LOG_Block("CUrlLog::MakeUUID");
|
|
|
|
// check OS version
|
|
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
|
|
if (!(GetVersionEx(&osverinfo)))
|
|
{
|
|
hr = GetLastError();
|
|
#ifdef DBG
|
|
if (FAILED(hr))
|
|
{
|
|
LOG_ErrorMsg(hr); // log this error
|
|
}
|
|
#endif
|
|
}
|
|
else if (5 <= osverinfo.dwMajorVersion && // Check for Win2k & up
|
|
VER_PLATFORM_WIN32_NT == osverinfo.dwPlatformId)
|
|
{
|
|
// The OS is Win2K & up.
|
|
// We can safely use CoCreateGuid().
|
|
hr = CoCreateGuid(pUuid);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
goto Done;
|
|
}
|
|
|
|
LOG_ErrorMsg(hr); // log this error
|
|
}
|
|
|
|
// Either the OS is something older than Win2K, or
|
|
// somehow we failed to get a GUID with CoCreateGuid.
|
|
// We still have to do something to resolve the proxy caching problem.
|
|
// Here we construct this psudo GUID by using:
|
|
// - ticks since last reboot
|
|
// - the current process ID
|
|
// - time in seconds since 00:00:00 1/1/1970 UTC
|
|
// - fraction of a second in milliseconds for the above time.
|
|
// - a 15-bit unsigned random number
|
|
//
|
|
pUuid->Data1 = GetTickCount();
|
|
*((DWORD*) &pUuid->Data2) = GetCurrentProcessId();
|
|
|
|
// Use the first 6 bytes of m_uuidPingID.Data1 to store sys date/time.
|
|
{
|
|
_timeb tm;
|
|
|
|
_ftime(&tm);
|
|
*((DWORD*) &pUuid->Data4) = (DWORD) tm.time;
|
|
((WORD*) &pUuid->Data4)[2] = tm.millitm;
|
|
}
|
|
|
|
// Use the last 2 bytes of m_uuidPingID.Data1 to store another random number.
|
|
srand(pUuid->Data1);
|
|
((WORD*) &pUuid->Data4)[3] = (WORD) rand(); // rand() returns only positive values.
|
|
|
|
|
|
Done:
|
|
return;
|
|
}
|
|
|
|
|
|
// Check and/or fix (if necessary) the header of the log file.
|
|
//
|
|
// Returned value:
|
|
// S_OK - the header has been fixed or the file contains
|
|
// a valid header. The file pointer now points to
|
|
// the first entry in the log file.
|
|
// S_FALSE - the file has a valid header but the version
|
|
// of the file is newer than this library code.
|
|
// The caller should not try to overwrite the
|
|
// file's contents.
|
|
// Others (failure) - the header is invalid or there was
|
|
// a problem accessing the file. The
|
|
// file should be deleted.
|
|
HRESULT ValidateFileHeader(HANDLE hFile, BOOL fCheckHeader, BOOL fFixHeader)
|
|
{
|
|
ULHEADER ulheader;
|
|
DWORD dwBytes;
|
|
HRESULT hr = E_FAIL;
|
|
|
|
LOG_Block("ValidateFileHeader");
|
|
|
|
if (fCheckHeader)
|
|
{
|
|
DWORD dwFileSize = GetFileSize(hFile, NULL);
|
|
// Log file existed before we opened it
|
|
if (INVALID_FILE_SIZE == dwFileSize)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
else if (1024 * 100 < dwFileSize) // no more than 100Kbytes
|
|
{
|
|
LOG_Error(_T("too many stale entries in cache."));
|
|
}
|
|
else if (!ReadFile(hFile, &ulheader, sizeof(ulheader), &dwBytes, NULL))
|
|
{
|
|
// We failed to read the header. We must then fix up the
|
|
// header.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
}
|
|
else if (sizeof(ulheader) == dwBytes)
|
|
{
|
|
if (CACHE_FILE_VERSION < ulheader.wVersion)
|
|
{
|
|
// A log file of newer version already exists.
|
|
// We should not mess it up with an entry of older
|
|
// format. The query string will not be saved.
|
|
LOG_Internet(_T("log file is of a newer version. operation cancelled."));
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (CACHE_FILE_VERSION == ulheader.wVersion)
|
|
{
|
|
// Correct version number. We're done.
|
|
return S_OK;
|
|
}
|
|
// else
|
|
// out-dated header
|
|
// We don't care about the entries in it. We will replace everything
|
|
// in order to fix the header.
|
|
}
|
|
// else
|
|
// incorrect header size
|
|
// We don't care about the entries in it. We will replace everything
|
|
// in order to fix the header.
|
|
|
|
if (!fFixHeader)
|
|
{
|
|
return hr;
|
|
}
|
|
|
|
// Truncate the file to zero byte.
|
|
if (INVALID_SET_FILE_POINTER == SetFilePointer(
|
|
hFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN) ||
|
|
!SetEndOfFile(hFile))
|
|
{
|
|
// Nothing we can do if we failed to clear the
|
|
// contents of the file in order to fix it up.
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
}
|
|
else if (!fFixHeader)
|
|
{
|
|
// The caller needs to pick at least one operation.
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
|
|
// Assume we are at the beginning of the file.
|
|
// We need to (re)initialize the file.
|
|
if (fFixHeader)
|
|
{
|
|
ZeroMemory(&ulheader, sizeof(ulheader));
|
|
|
|
ulheader.wVersion = CACHE_FILE_VERSION;
|
|
if (!WriteFile(hFile, &ulheader, sizeof(ulheader), &dwBytes, NULL))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
LOG_ErrorMsg(hr);
|
|
return hr;
|
|
}
|
|
else if (sizeof(ulheader) != dwBytes)
|
|
{
|
|
LOG_Error(_T("Failed to write file header (%d bytes VS %d bytes)"), sizeof(ulheader), dwBytes);
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|