mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
591 lines
12 KiB
591 lines
12 KiB
//=======================================================================
|
|
//
|
|
// 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);
|
|
|
|
}
|
|
}
|
|
|
|
}
|