|
|
//
// CallLog.cpp
//
// Created: ChrisPi 10-17-96
// Updated: RobD 10-30-96
//
// ToDo:
// - Expire records
// - UI to delete record(s)
// - system policy?
//
#include "precomp.h"
#include "rostinfo.h"
#include "CallLog.h"
#include "particip.h" // for MAX_PARTICIPANT_NAME
#include "ConfUtil.h"
#define MAX_DELETED_ENTRIES_BEFORE_REWRITE 10
#define LARGE_ENTRY_SIZE 256
CCallLogEntry::CCallLogEntry( LPCTSTR pcszName, DWORD dwFlags, CRosterInfo* pri, LPVOID pvRosterData, PBYTE pbCert, ULONG cbCert, LPSYSTEMTIME pst, DWORD dwFileOffset) : m_dwFlags (dwFlags), m_pri (NULL), m_pbCert (NULL), m_cbCert (0), m_dwFileOffset (dwFileOffset) { DebugEntry(CCallLogEntry::CCallLogEntry); ASSERT(NULL != pcszName); ASSERT(NULL != pst); // Only one of these two parameters should be non-NULL
ASSERT((NULL == pvRosterData) || (NULL == pri)); LPVOID pvData = pvRosterData; if (NULL != pri) { UINT cbData; if (SUCCEEDED(pri->Save(&pvData, &cbData))) { ASSERT(pvData); } } if (NULL != pvData) { m_pri = new CRosterInfo(); if (NULL != m_pri) { m_pri->Load(pvData); } } if (NULL != pbCert && 0 != cbCert) { m_pbCert = new BYTE[cbCert]; if (NULL == m_pbCert) { ERROR_OUT(("CCalllogEntry::CCalllogEntry() -- failed to allocate memory")); } else { memcpy(m_pbCert, pbCert, cbCert); m_cbCert = cbCert; } } m_st = *pst; m_pszName = PszAlloc(pcszName); DebugExitVOID(CCallLogEntry::CCallLogEntry); }
CCallLogEntry::~CCallLogEntry() { DebugEntry(CCallLogEntry::~CCallLogEntry); delete m_pszName; // NOTE: m_pri must be new'ed by the function that calls the
// constructor - this is an optimization to avoid unnecessary
// copying - but it's a little unclean.
delete m_pri; delete []m_pbCert; DebugExitVOID(CCallLogEntry::~CCallLogEntry); }
/////////////////////////////////////////////////////////////////////////
CCallLog::CCallLog(LPCTSTR pszKey, LPCTSTR pszDefault) : m_fUseList (FALSE), m_fDataRead (FALSE), m_cTotalEntries (0), m_cDeletedEntries (0) { InitLogData(pszKey, pszDefault); TRACE_OUT(("Using Call Log file [%s]", m_strFile)); }
CCallLog::~CCallLog() { DebugEntry(CCallLog::~CCallLog);
// Check to see if we are more than a number entries over our
// configured maximum and re-write the file if so
TRACE_OUT(("Entry count: total:%d deleted:%d", m_cTotalEntries, m_cDeletedEntries));
if ( m_fUseList && m_cDeletedEntries > MAX_DELETED_ENTRIES_BEFORE_REWRITE ) RewriteFile(); else { int size = GetSize();
for( int i = 0; i < size; i++ ) { ASSERT( NULL != (*this)[i] ); delete (*this)[i]; } }
DebugExitVOID(CCallLog::~CCallLog); }
/* A D D C A L L */ /*-------------------------------------------------------------------------
%%Function: AddCall
-------------------------------------------------------------------------*/ HRESULT CCallLog::AddCall(LPCTSTR pcszName, PLOGHDR pLogHdr, CRosterInfo* pri, PBYTE pbCert, ULONG cbCert) { TRACE_OUT( ("CCallLog::AddCall(\"%s\")", pcszName) ); DWORD dwFileOffset; HRESULT hr = S_OK;
ASSERT(NULL != pLogHdr);
// Grab the current local time
::GetLocalTime(&(pLogHdr->sysTime));
ApiDebugMsg(("CALL_LOG: [%s] %s", pcszName, (pLogHdr->dwCLEF & CLEF_ACCEPTED) ? "ACCEPTED" : "REJECTED"));
// Append the data to the file
dwFileOffset = WriteEntry(pcszName, pLogHdr, pri, pbCert, cbCert);
TRACE_OUT(("AddCall: adding entry with %d total, %d deleted, %d max", m_cTotalEntries, m_cDeletedEntries, m_cMaxEntries ));
// Create list entry only when necessary
if (m_fUseList) { CCallLogEntry* pcleNew = new CCallLogEntry( pcszName, pLogHdr->dwCLEF, pri, NULL, pbCert, cbCert, &(pLogHdr->sysTime), dwFileOffset);
if (NULL == pcleNew) return E_OUTOFMEMORY;
Add(pcleNew);
m_cTotalEntries++;
// Check to see if this put us over the top of valid entries
// and remove the oldest entry if so
if ( m_cTotalEntries - m_cDeletedEntries > m_cMaxEntries ) { // Remove oldest entry
DeleteEntry((*this)[0]); RemoveAt( 0 ); } } else { // Check to see if the file is getting large based on
// our target number of entries and a heuristic large
// entry size. If our file has grown over this point, load
// the file and trim the list so that we will re-write
// a smaller file on exit.
TRACE_OUT(("Checking file size %d against %d * %d", dwFileOffset, m_cMaxEntries, LARGE_ENTRY_SIZE));
if ( dwFileOffset > (DWORD)( m_cMaxEntries * LARGE_ENTRY_SIZE ) ) { TRACE_OUT(("Log file getting large, forcing LoadFileData"));
LoadFileData(); } }
return S_OK; }
/* I N I T L O G D A T A */ /*-------------------------------------------------------------------------
%%Function: InitLogData
Get the log data from the registry. - the expiration information - the file name (with path) If there is no entry for the file name, a new, unique file is created. -------------------------------------------------------------------------*/ VOID CCallLog::InitLogData(LPCTSTR pszKey, LPCTSTR pszDefault) { TCHAR szPath[MAX_PATH]; PTSTR pszFileName; HANDLE hFile;
ASSERT(m_strFile.IsEmpty());
RegEntry reLog(pszKey, HKEY_CURRENT_USER);
m_Expire = reLog.GetNumber(REGVAL_LOG_EXPIRE, 0); m_strFile = reLog.GetString(REGVAL_LOG_FILE);
m_cMaxEntries = reLog.GetNumber(REGVAL_LOG_MAX_ENTRIES, DEFAULT_LOG_MAX_ENTRIES );
TRACE_OUT(("Max Entries set to %d", m_cMaxEntries ));
// Make sure file exists and can be read/written
hFile = OpenLogFile(); if (NULL != hFile) { // valid file found
CloseHandle(hFile); return; } // String is invalid (or empty) - make sure it's empty
m_strFile.Empty(); // Create the new log file in the NetMeeting directory
if (!GetInstallDirectory(szPath)) { WARNING_OUT(("InitLogData: Unable to get Install Directory?")); return; } pszFileName = &szPath[lstrlen(szPath)];
// Try to use the default name
wsprintf(pszFileName, TEXT("%s%s"), pszDefault, TEXT(".dat"));
hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) { // Use a unique name to avoid other users' files
for (int iFile = 2; iFile < 999; iFile++) { wsprintf(pszFileName, TEXT("%s%d.dat"), pszDefault, iFile);
hFile = CreateFile(szPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE != hFile) break;
switch (GetLastError()) { case ERROR_FILE_EXISTS: // We get this with NT
case ERROR_ALREADY_EXISTS: // and this with Win95
break; default: WARNING_OUT(("Unable to create log file [%s] err=0x%08X", szPath, GetLastError())); break; } /* switch (GetLastError()) */ } }
if (INVALID_HANDLE_VALUE != hFile) { CloseHandle(hFile); m_strFile = szPath; reLog.SetValue(REGVAL_LOG_FILE, szPath); } }
/* O P E N L O G F I L E */ /*-------------------------------------------------------------------------
%%Function: OpenLogFile
Open the log file and return a handle to file. Return NULL if there was a problem. -------------------------------------------------------------------------*/ HANDLE CCallLog::OpenLogFile(VOID) { HANDLE hFile;
if (m_strFile.IsEmpty()) { WARNING_OUT(("Problem opening call log file")); return NULL; }
hFile = CreateFile(m_strFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hFile) { ERROR_OUT(("OpenLogFile: Unable to open call log file")); hFile = NULL; }
return hFile; }
/* R E A D D A T A */ /*-------------------------------------------------------------------------
%%Function: ReadData
-------------------------------------------------------------------------*/ BOOL CCallLog::ReadData(HANDLE hFile, PVOID pv, UINT cb) { DWORD cbRead;
ASSERT(NULL != hFile); ASSERT(NULL != pv);
if (0 == cb) return TRUE;
if (!ReadFile(hFile, pv, cb, &cbRead, NULL)) return FALSE;
return (cb == cbRead); }
/* W R I T E D A T A */ /*-------------------------------------------------------------------------
%%Function: WriteData
Write the data to the file. The file will be automatically opened/close if hFile is NULL. -------------------------------------------------------------------------*/ HRESULT CCallLog::WriteData(HANDLE hFile, LPDWORD pdwOffset, PVOID pv, DWORD cb) { HRESULT hr = E_FAIL; HANDLE hFileTemp = NULL; DWORD cbWritten;
if (0 == cb) return S_OK; // nothing to do
ASSERT(NULL != pv); ASSERT(NULL != pdwOffset); ASSERT(INVALID_FILE_SIZE != *pdwOffset);
if (NULL == hFile) { // Auto-open the file, if necessary
hFileTemp = OpenLogFile(); if (NULL == hFileTemp) return E_FAIL; hFile = hFileTemp; } ASSERT(INVALID_HANDLE_VALUE != hFile);
if (INVALID_FILE_SIZE != SetFilePointer(hFile, *pdwOffset, NULL, FILE_BEGIN)) { if (WriteFile(hFile, pv, cb, &cbWritten, NULL) && (cb == cbWritten)) { *pdwOffset += cbWritten; hr = S_OK; } }
if (NULL != hFileTemp) { // Close the temporary file handle
CloseHandle(hFileTemp); }
return hr; }
/* W R I T E E N T R Y */ /*-------------------------------------------------------------------------
%%Function: WriteEntry
Write a call log entry to the end of the log file. The function returns the file position at which the data was written or INVALID_FILE_SIZE (0xFFFFFFFF) if there was a problem. -------------------------------------------------------------------------*/ DWORD CCallLog::WriteEntry(LPCTSTR pcszName, PLOGHDR pLogHdr, CRosterInfo* pri, PBYTE pbCert, ULONG cbCert) { PVOID pvData; HANDLE hFile; DWORD dwFilePosition; DWORD dwPos; BSTR pcwszName;
ASSERT(NULL != pcszName); ASSERT(NULL != pLogHdr);
hFile = OpenLogFile(); if (NULL == hFile) return INVALID_FILE_SIZE;
dwFilePosition = SetFilePointer(hFile, 0, NULL, FILE_END); if (INVALID_FILE_SIZE != dwFilePosition) { dwPos = dwFilePosition;
// Always write display name in UNICODE
if(SUCCEEDED(LPTSTR_to_BSTR(&pcwszName, pcszName))) { pLogHdr->cbName = (lstrlenW((LPWSTR)pcwszName) + 1) * sizeof(WCHAR);
if ((NULL == pri) || (!SUCCEEDED(pri->Save(&pvData, (UINT *) &(pLogHdr->cbData)))) ) { // No data?
pLogHdr->cbData = 0; pvData = NULL; } pLogHdr->cbCert = cbCert; // Calculate total size of record
pLogHdr->dwSize = sizeof(LOGHDR) + pLogHdr->cbName + pLogHdr->cbData + pLogHdr->cbCert;
if ((S_OK != WriteData(hFile, &dwPos, pLogHdr, sizeof(LOGHDR))) || (S_OK != WriteData(hFile, &dwPos, pcwszName, pLogHdr->cbName)) || (S_OK != WriteData(hFile, &dwPos, pvData, pLogHdr->cbData)) || (S_OK != WriteData(hFile, &dwPos, pbCert, cbCert))) { dwFilePosition = INVALID_FILE_SIZE; }
SysFreeString(pcwszName); }
}
CloseHandle(hFile);
return dwFilePosition; }
/* R E A D E N T R Y */ /*-------------------------------------------------------------------------
%%Function: ReadEntry
Read the next entry from the file. *ppcle will be set to NULL if the entry was deleted.
Return Values: S_OK - data was read successfully S_FALSE - data exists, but was deleted E_FAIL - problem reading file -------------------------------------------------------------------------*/ HRESULT CCallLog::ReadEntry(HANDLE hFile, DWORD * pdwFileOffset, CCallLogEntry** ppcle) { DWORD dwOffsetSave; LOGHDR logHdr; WCHAR wszName[MAX_PARTICIPANT_NAME];
ASSERT(NULL != ppcle); ASSERT(NULL != hFile); ASSERT(NULL != pdwFileOffset); *ppcle = NULL; // initialize this in case we return with an error
dwOffsetSave = *pdwFileOffset; if (INVALID_FILE_SIZE == SetFilePointer(hFile, dwOffsetSave, NULL, FILE_BEGIN)) return E_FAIL;
// Read record header
if (!ReadData(hFile, &logHdr, sizeof(LOGHDR)) ) return E_FAIL;
// Return pointer to end of record
*pdwFileOffset += logHdr.dwSize;
if (logHdr.dwCLEF & CLEF_DELETED) { // Skip deleted record
ASSERT(NULL == *ppcle); return S_FALSE; } if (logHdr.cbName > sizeof(wszName)) logHdr.cbName = sizeof(wszName);
// Read Name
if (!ReadData(hFile, wszName, logHdr.cbName)) return E_FAIL;
// Read Extra Data
PVOID pvData = NULL; if (logHdr.cbData != 0) { pvData = new BYTE[logHdr.cbData]; if (NULL != pvData) { if (!ReadData(hFile, pvData, logHdr.cbData)) { WARNING_OUT(("Problem reading roster data from log")); } } }
PBYTE pbCert = NULL; if ((logHdr.dwCLEF & CLEF_SECURE ) && logHdr.cbCert != 0) { pbCert = new BYTE[logHdr.cbCert]; if (NULL != pbCert) { if (!ReadData(hFile, pbCert, logHdr.cbCert)) { WARNING_OUT(("Problem reading certificate data from log")); } } }
BSTR bstrName = ::SysAllocString(wszName);
if(bstrName) { LPTSTR szName; HRESULT hr = BSTR_to_LPTSTR (&szName, bstrName); if (SUCCEEDED(hr)) {
// Create the new log entry from the data read
*ppcle = new CCallLogEntry(szName, logHdr.dwCLEF, NULL, pvData, pbCert, logHdr.cbCert, &logHdr.sysTime, dwOffsetSave); delete szName; }
SysFreeString(bstrName); } delete [] pvData; delete [] pbCert; return S_OK; }
/* L O A D F I L E D A T A */ /*-------------------------------------------------------------------------
%%Function: LoadFileData
Load the call log data from the file -------------------------------------------------------------------------*/ VOID CCallLog::LoadFileData(VOID) { HANDLE hFile; DWORD dwFileOffset; CCallLogEntry * pcle;
hFile = OpenLogFile(); if (NULL == hFile) return;
m_cTotalEntries = 0; m_cDeletedEntries = 0;
dwFileOffset = 0; while (E_FAIL != ReadEntry(hFile, &dwFileOffset, &pcle)) { m_cTotalEntries++;
if (NULL == pcle) { m_cDeletedEntries++; continue; // deleted record
}
Add(pcle);
TRACE_OUT(("Read Entry: \"%s\" (%02d/%02d/%04d %02d:%02d:%02d) : %s", pcle->m_pszName, pcle->m_st.wMonth, pcle->m_st.wDay, pcle->m_st.wYear, pcle->m_st.wHour, pcle->m_st.wMinute, pcle->m_st.wSecond, (CLEF_ACCEPTED & pcle->m_dwFlags) ? "ACCEPTED" : "REJECTED")); }
CloseHandle(hFile);
m_fUseList = TRUE; m_fDataRead = TRUE;
// Now trim the list down to our configured maximum if
// the count exceeds our target. The file will be compacted
// when we write it out if we have more than a few deleted
// entries.
for( int nn = 0, delCount = m_cTotalEntries - m_cDeletedEntries - m_cMaxEntries; nn < delCount; nn++ ) { DeleteEntry((*this)[0]); RemoveAt( 0 ); } }
/* R E W R I T E F I L E */ /*-------------------------------------------------------------------------
%%Function: RewriteFile
Re-write the log file from the in-memory list, compress deleted entries -------------------------------------------------------------------------*/ VOID CCallLog::RewriteFile(VOID) { HANDLE hFile;
TRACE_OUT(("Rewriting log file"));
// Make sure we don't nuke the file without a list to write out
ASSERT(m_fUseList);
// Reset the file pointer and write the EOF marker
if (!m_strFile.IsEmpty()) { hFile = OpenLogFile();
if (NULL != hFile) { if (INVALID_FILE_SIZE != SetFilePointer(hFile, 0, NULL, FILE_BEGIN)) { SetEndOfFile(hFile); m_cTotalEntries = 0; m_cDeletedEntries = 0; } CloseHandle(hFile); } }
// Write out all non-deleted records
for( int i = 0; i < GetSize(); ++i ) { CCallLogEntry* pcle = (*this)[i]; ASSERT(NULL != pcle);
LOGHDR LogHdr;
// Initialize LogHdr items from memory object
LogHdr.dwCLEF = pcle->GetFlags(); LogHdr.dwPF = 0; LogHdr.sysTime = *pcle->GetTime();
// Write out entry
WriteEntry( pcle->m_pszName, &LogHdr, pcle->m_pri, pcle->m_pbCert, pcle->m_cbCert);
delete pcle; } }
// Grumble... Inefficient
HRESULT CCallLog::DeleteEntry(CCallLogEntry * pcle) { HRESULT hr; DWORD dwFlags; DWORD dwOffset;
if (NULL == pcle) { WARNING_OUT(("DeleteEntry: Unable to find entry")); return E_FAIL; }
// Calculate offset to "CLEF"
dwOffset = pcle->GetFileOffset() + offsetof(LOGHDR, dwCLEF);
dwFlags = pcle->GetFlags() | CLEF_DELETED; hr = WriteData(NULL, &dwOffset, &dwFlags, sizeof(DWORD));
m_cDeletedEntries++;
TRACE_OUT(("Marked [%s] pos=%08X for deletion", pcle->GetName(), pcle->GetFileOffset() ));
delete pcle;
return hr; }
|