windows-nt/Source/XPSP1/NT/enduser/windows.com/wuv3/wuv3is/history.cpp
2020-09-26 16:20:57 +08:00

592 lines
12 KiB
C++

//=======================================================================
//
// Copyright (c) 1999 Microsoft Corporation. All Rights Reserved.
//
// File: history.cpp
//
// Purpose: History log
//
//=======================================================================
#include "history.h"
#define DATE_RTLREADING 0x00000020 // add marks for right to left reading order layout
extern CState g_v3state; //defined in CV3.CPP
static void CheckMigrateV2Log();
static void EscapeSep(LPSTR pszStr, BOOL bEscape);
//This function reads and returns the clients installation history. This history is
//returned as an array of History structures. The caller is responsible for passing
//in a reference to the the History Variable array.
void ReadHistory(
Varray<HISTORYSTRUCT> &History, //Returned History array.
int &iTotalItems //total number of items returned in history array.
)
{
USES_CONVERSION;
const int browserLocale = LOCALE_USER_DEFAULT;
HISTORYSTRUCT his;
char szLineType[32];
char szTemp[32];
SYSTEMTIME st;
// check to see if we have to migrate V2 log
CheckMigrateV2Log();
iTotalItems = 0;
g_v3state.AppLog().StartReading();
while (g_v3state.AppLog().ReadLine())
{
ZeroMemory(&his, sizeof(his));
// get line type (first field)
g_v3state.AppLog().CopyNextField(szLineType, sizeof(szLineType));
if (_stricmp(szLineType, LOG_PSS) == 0)
{
//
// skip this line since it is only meant for PSS
//
continue;
}
else if (_stricmp(szLineType, LOG_V2) == 0)
{
//
// line was migrated from V2
//
his.bV2 = TRUE;
g_v3state.AppLog().CopyNextField(his.szDate, sizeof(his.szDate));
g_v3state.AppLog().CopyNextField(his.szTime, sizeof(his.szTime));
g_v3state.AppLog().CopyNextField(his.szTitle, sizeof(his.szTitle));
EscapeSep(his.szTitle, FALSE);
}
else if ((_stricmp(szLineType, LOG_V3CAT) == 0) || (_stricmp(szLineType, LOG_V3_2) == 0))
{
//
// V3 line
//
int iV3LineVer = 1;
if (_stricmp(szLineType, LOG_V3_2) == 0)
{
iV3LineVer = 2;
}
// puid
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
his.puid = atoi(szTemp);
// installed/uninstalled
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
his.bInstall = (_stricmp(szTemp, LOG_INSTALL) == 0);
// title
g_v3state.AppLog().CopyNextField(his.szTitle, sizeof(his.szTitle));
EscapeSep(his.szTitle, FALSE);
// version
g_v3state.AppLog().CopyNextField(his.szVersion, sizeof(his.szVersion));
// date and time
if (iV3LineVer == 1)
{
// V3 Beta had two fields for date and time, we simly read these fields
// date
g_v3state.AppLog().CopyNextField(his.szDate, sizeof(his.szDate));
// time
g_v3state.AppLog().CopyNextField(his.szTime, sizeof(his.szTime));
}
else
{
// read the timestamp and convert
// timestamp
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
// if time stamp is a valid format, convert and populate the structure
// if its not we will have blank values since we initialized the structure to zero
if (ParseTimeStamp(szTemp, &st))
{
TCHAR szTmp[256];
GetDateFormat(browserLocale, DATE_LONGDATE, &st, NULL, szTmp, sizeof(szTmp)/sizeof(szTmp[0]));
strcpy(his.szDate, T2A(szTmp));
GetTimeFormat(browserLocale, LOCALE_NOUSEROVERRIDE, &st, NULL, szTmp, sizeof(szTmp)/sizeof(szTmp[0]));
strcpy(his.szTime, T2A(szTmp));
}
}
// record type
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
his.RecType = (BYTE)atoi(szTemp);
// result
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
if (_stricmp(szTemp, LOG_SUCCESS) == 0)
{
his.bResult = OPERATION_SUCCESS;
}
else if (_stricmp(szTemp, LOG_STARTED) == 0)
{
his.bResult = OPERATION_STARTED;
}
else
{
his.bResult = OPERATION_ERROR;
}
// error code
g_v3state.AppLog().CopyNextField(szTemp, sizeof(szTemp));
his.hrError = atoh(szTemp);
}
History[iTotalItems] = his;
iTotalItems++;
}
g_v3state.AppLog().StopReading();
}
void UpdateHistory(
PSELECTITEMINFO pInfo, //Pointer to selected item information.
int iTotalItems, //Total selected items
BOOL bInstall //TRUE for InstallSelectedItems, FALSE for RemoveSelectedItems.
)
{
PINVENTORY_ITEM pItem = NULL;
PWU_VARIABLE_FIELD pvTitle;
PWU_VARIABLE_FIELD pvDriverVer;
char szLine[1024];
char szTemp[256];
BOOL bSuccess;
SYSTEMTIME* pst;
BOOL bPSS;
BOOL bItem = FALSE; // used to check if the function GetCatalogAndItem return value and accordingly to decide pItem
// check to see if we have to migrate V2 log
CheckMigrateV2Log();
for (int i = 0; i < iTotalItems; i++)
{
if (pInfo[i].bInstall != bInstall)
continue;
if (pInfo[i].iStatus == ITEM_STATUS_SUCCESS ||
pInfo[i].iStatus == ITEM_STATUS_SUCCESS_REBOOT_REQUIRED ||
pInfo[i].iStatus == ITEM_STATUS_UNINSTALL_STARTED)
{
bSuccess = TRUE;
}
else
{
bSuccess = FALSE;
}
//
// NTBUG9#161018 PREFIX:dereferencing NULL pointer 'pItem' - waltw 8/16/00
// If GetCatalogAndItem returns FALSE, pItem is invalid.
//
if (bItem = g_v3state.GetCatalogAndItem(pInfo[i].puid, &pItem, NULL))
{
// we write hidden dependencies only as PSS entries
if(NULL != pItem) // pItem should never be NULL, but just to be anal...
{
bPSS = pItem->ps->bHidden;
}
}
else
{
// if GetCatalogAndItem returns False, the pItem is NULL and we shouldn't dereference the pointer
//continue;
//Can be continued as the string can be formed with the other elements, only thing requied is to check for validity of pItem before using it
bPSS = FALSE;
}
//
// start building a line for the log
//
// line type
strcpy(szLine, bPSS ? LOG_PSS : LOG_V3_2);
strcat(szLine, LOG_FIELD_SEPARATOR);
// puid
_itoa(pInfo[i].puid, szTemp, 10);
strcat(szLine, szTemp);
strcat(szLine, LOG_FIELD_SEPARATOR);
// install/uninstall
if (pInfo[i].bInstall)
strcat(szLine, LOG_INSTALL);
else
strcat(szLine, LOG_UNINSTALL);
strcat(szLine, LOG_FIELD_SEPARATOR);
// title
if (bItem)
{
if (pvTitle = pItem->pd->pv->Find(WU_DESCRIPTION_TITLE))
{
WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)(pvTitle->pData), -1, szTemp, sizeof(szTemp), NULL, NULL);
}
EscapeSep(szTemp, TRUE);
strcat(szLine, szTemp);
strcat(szLine, LOG_FIELD_SEPARATOR);
// version
szTemp[0] = '\0';
switch( pItem->recordType )
{
case WU_TYPE_ACTIVE_SETUP_RECORD:
VersionToString(&pItem->pf->a.version, szTemp);
break;
case WU_TYPE_CDM_RECORD:
case WU_TYPE_RECORD_TYPE_PRINTER:
if ((pvDriverVer = pItem->pv->Find(WU_VARIABLE_DRIVERVER)))
strzncpy(szTemp, (char *)pvDriverVer->pData, sizeof(szTemp));
break;
}
strcat(szLine, szTemp);
strcat(szLine, LOG_FIELD_SEPARATOR);
}
// timestamp
pst = &(pInfo[i].stDateTime);
// we expect the date/time format to be (TIMESTAMP_FMT) as follows:
// 01234567890123456789
// YYYY-MM-DD HH:MM:SS
sprintf(szTemp, TIMESTAMP_FMT,
pst->wYear, pst->wMonth, pst->wDay,
pst->wHour, pst->wMinute, pst->wSecond);
strcat(szLine, szTemp);
strcat(szLine, LOG_FIELD_SEPARATOR);
if (bItem)
{
// record type
_itoa(pItem->recordType, szTemp, 10);
strcat(szLine, szTemp);
strcat(szLine, LOG_FIELD_SEPARATOR);
}
// result
if (bSuccess)
{
if (pInfo[i].iStatus == ITEM_STATUS_UNINSTALL_STARTED)
strcat(szLine, LOG_STARTED);
else
strcat(szLine, LOG_SUCCESS);
}
else
{
strcat(szLine, LOG_FAIL);
}
strcat(szLine, LOG_FIELD_SEPARATOR);
// hresult
if (!bSuccess)
{
sprintf(szTemp, "%#08x", pInfo[i].hrError);
strcat(szLine, szTemp);
}
strcat(szLine, LOG_FIELD_SEPARATOR);
// error message
if (!bSuccess)
{
CAppLog::FormatErrMsg(pInfo[i].hrError, szTemp, sizeof(szTemp));
strcat(szLine, szTemp);
}
strcat(szLine, LOG_FIELD_SEPARATOR);
//
// write out the log
//
g_v3state.AppLog().Log(szLine);
}
}
// This function updates the local client installation history
void UpdateInstallHistory(PSELECTITEMINFO pInfo, int iTotalItems)
{
UpdateHistory(pInfo, iTotalItems, TRUE);
}
// This function updates the local client removal history
void UpdateRemoveHistory(PSELECTITEMINFO pInfo, int iTotalItems)
{
UpdateHistory(pInfo, iTotalItems, FALSE);
}
// migrates V2 log into the V3 log. This only happens if V3 log does not exist yet
// this is the condition we use to see we are running for the first time.
static void CheckMigrateV2Log()
{
USES_CONVERSION;
static BOOL bChecked = FALSE;
char szBuf[MAX_PATH];
VARIANT vDate;
SYSTEMTIME st;
if (bChecked)
return;
bChecked = TRUE;
if (FileExists(g_v3state.AppLog().GetLogFile()))
{
// V3 file exists, we will not migrate V2
return;
}
// build V2 log file name
TCHAR szFile[MAX_PATH];
if (! GetWindowsDirectory(szFile, sizeof(szFile) / sizeof(TCHAR)))
{
lstrcat(szFile, _T("C:\\Windows"));
}
AddBackSlash(szFile);
lstrcat(szFile, _T("WULog.txt"));
if (!FileExists(szFile))
{
// V2 file does not exists
return;
}
CAppLog V2Log(szFile);
char szLine[1024];
V2Log.StartReading();
while (V2Log.ReadLine())
{
// line type
strcpy(szLine, LOG_V2);
strcat(szLine, LOG_FIELD_SEPARATOR);
// v2 log starts out with | so we want to skip the first field
V2Log.CopyNextField(szBuf, sizeof(szBuf));
// date
V2Log.CopyNextField(szBuf, sizeof(szBuf));
//
// fixup date to ensure 4 digit year. We use OLE to intelligently parse out the date
// that will most probably be with 2 digit year code. OLE takes care of making sence
// out of it. Then we write the date out with a 4 digit year back to the same buffer
//
VariantInit(&vDate);
vDate.vt = VT_BSTR;
vDate.bstrVal = SysAllocString(A2OLE(szBuf));
if SUCCEEDED(VariantChangeType(&vDate, &vDate, VARIANT_NOVALUEPROP, VT_DATE))
{
if (VariantTimeToSystemTime(vDate.date, &st))
{
sprintf(szBuf, "%4d-%02d-%02d", st.wYear, st.wMonth, st.wDay);
}
}
strcat(szLine, szBuf);
strcat(szLine, LOG_FIELD_SEPARATOR);
// time
V2Log.CopyNextField(szBuf, sizeof(szBuf));
strcat(szLine, szBuf);
strcat(szLine, LOG_FIELD_SEPARATOR);
// title
V2Log.CopyNextField(szBuf, sizeof(szBuf));
EscapeSep(szBuf, TRUE);
strcat(szLine, szBuf);
strcat(szLine, LOG_FIELD_SEPARATOR);
// write out the log line
g_v3state.AppLog().Log(szLine);
}
V2Log.StopReading();
}
//
// we expect the date/time format to be (TIMESTAMP_FMT) as follows:
// 01234567890123456789
// YYYY-MM-DD HH:MM:SS
//
BOOL ParseTimeStamp(LPCSTR pszDateTime, SYSTEMTIME* ptm)
{
char szBuf[20];
if (strlen(pszDateTime) != 19)
{
return FALSE;
}
strcpy(szBuf, pszDateTime);
//
// validate format
//
for (int i = 0; i < 19; i++)
{
switch (i)
{
case 4:
case 7:
if (szBuf[i] != '-')
{
return FALSE;
}
break;
case 10:
if (szBuf[i] != ' ')
{
return FALSE;
}
break;
case 13:
case 16:
if (szBuf[i] != ':')
{
return FALSE;
}
break;
default:
if (szBuf[i] < '0' || pszDateTime[i] > '9')
{
return FALSE;
}
break;
}
}
//
// get values
//
szBuf[4] = '\0';
ptm->wYear = (WORD)atoi(szBuf);
szBuf[7] = '\0';
ptm->wMonth = (WORD)atoi(szBuf + 5);
szBuf[10] = '\0';
ptm->wDay = (WORD)atoi(szBuf + 8);
szBuf[13] = '\0';
ptm->wHour = (WORD)atoi(szBuf + 11);
szBuf[16] = '\0';
ptm->wMinute = (WORD)atoi(szBuf + 14);
ptm->wSecond = (WORD)atoi(szBuf + 17);
ptm->wMilliseconds = 0;
ptm->wDayOfWeek = 0;
//
// convert to file time and back. This will calculate the week day as well as tell us
// if all the fields are set to valid values
//
BOOL bRet;
FILETIME ft;
bRet = SystemTimeToFileTime(ptm, &ft);
if (bRet)
{
bRet = FileTimeToSystemTime(&ft, ptm);
}
return bRet;
}
static void EscapeSep(LPSTR pszStr, BOOL bEscape)
{
char szBuf[256];
char* ps;
char* pb;
if (bEscape)
{
// escape
if (strchr(pszStr, '|') != NULL)
{
ps = pszStr;
pb = szBuf;
for (;;)
{
if (*ps == '|')
{
*pb++ = '~';
*pb++ = '1';
}
else
{
*pb++ = *ps;
}
if (*ps == '\0')
{
break;
}
ps++;
}
strcpy(pszStr, szBuf);
}
}
else
{
// unescape
if (strstr(pszStr, "~1") != NULL)
{
ps = pszStr;
pb = szBuf;
for (;;)
{
if (*ps == '~' && *(ps + 1) == '1')
{
*pb++ = '|';
ps++;
}
else
{
*pb++ = *ps;
}
if (*ps == '\0')
{
break;
}
ps++;
}
strcpy(pszStr, szBuf);
}
}
}