Windows2003-3790/inetcore/outlookexpress/msoert/clogfile.cpp
2020-09-30 16:53:55 +02:00

577 lines
17 KiB
C++

//--------------------------------------------------------------------------
// LogFile.cpp
// Copyright (C) Microsoft Corporation, 1997 - Rocket Database
//--------------------------------------------------------------------------
#include "pch.hxx"
#include <stdio.h>
#include <time.h>
#include <winver.h>
#include "clogfile.h"
#include <shlwapi.h>
#include <demand.h>
#include <BadStrFunctions.h>
//--------------------------------------------------------------------------
// LogFileTypes - RX = Receive, TX = Transmit, DB = Debug
//--------------------------------------------------------------------------
static LPSTR s_rgszPrefix[LOGFILE_MAX] = {
"rx",
"tx",
"db"
};
//--------------------------------------------------------------------------
// These are strings that we shouldn't log in plaintext
//--------------------------------------------------------------------------
static LPSTR s_rgszPassPrefix[] = {
"AUTHINFO PASS ",
"PASS ",
NULL
};
//--------------------------------------------------------------------------
// CreateSystemHandleName
//--------------------------------------------------------------------------
HRESULT CreateSystemHandleName(LPCSTR pszBase, LPCSTR pszSpecific,
LPSTR *ppszName)
{
// Locals
HRESULT hr=S_OK;
DWORD cchName;
// Trace
TraceCall("CreateSystemHandleName");
// Invalid Args
Assert(pszBase && pszSpecific && ppszName);
// Init
*ppszName = NULL;
// Compute Length
cchName = lstrlen(pszBase) + lstrlen(pszSpecific) + 15;
// Allocate
IF_NULLEXIT(*ppszName = PszAllocA(cchName));
// Format the name
wnsprintf(*ppszName, cchName, "%s%s", pszBase, pszSpecific);
// Remove backslashes from this string
ReplaceChars(*ppszName, '\\', '_');
// Lower Case
CharLower(*ppszName);
exit:
// Done
return hr;
}
//--------------------------------------------------------------------------
// CreateLogFile
//--------------------------------------------------------------------------
OESTDAPI_(HRESULT) CreateLogFile(HINSTANCE hInst, LPCSTR pszLogFile,
LPCSTR pszPrefix, DWORD cbTruncate, ILogFile **ppLogFile,
DWORD dwShareMode)
{
// Locals
HRESULT hr=S_OK;
CLogFile *pNew=NULL;
// Trace
TraceCall("CreateLogFile");
// Invalid Args
Assert(ppLogFile && pszLogFile);
// Initialize
*ppLogFile = NULL;
// Create me
pNew = new CLogFile;
if (NULL == pNew)
{
hr = TraceResult(E_OUTOFMEMORY);
goto exit;
}
// Open It
IF_FAILEXIT(hr = pNew->Open(hInst, pszLogFile, pszPrefix, cbTruncate, dwShareMode));
// Open It
*ppLogFile = (ILogFile *)pNew;
// Don't release it
pNew = NULL;
exit:
// Cleanup
SafeRelease(pNew);
// Done
return hr;
}
//--------------------------------------------------------------------------
// CLogFile::CLogFile
//--------------------------------------------------------------------------
CLogFile::CLogFile(void)
{
TraceCall("CLogFile::CLogFile");
m_cRef = 1;
m_hMutex = NULL;
m_hFile = INVALID_HANDLE_VALUE;
InitializeCriticalSection(&m_cs);
}
//--------------------------------------------------------------------------
// CLogFile::~CLogFile
//--------------------------------------------------------------------------
CLogFile::~CLogFile(void)
{
TraceCall("CLogFile::~CLogFile");
if (m_hFile != INVALID_HANDLE_VALUE)
CloseHandle_F16(m_hFile);
SafeCloseHandle(m_hMutex);
DeleteCriticalSection(&m_cs);
}
//--------------------------------------------------------------------------
// CLogFile::AddRef
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CLogFile::AddRef(void)
{
TraceCall("CLogFile::AddRef");
return InterlockedIncrement(&m_cRef);
}
//--------------------------------------------------------------------------
// CLogFile::Release
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CLogFile::Release(void)
{
TraceCall("CLogFile::Release");
LONG cRef = InterlockedDecrement(&m_cRef);
if (0 == cRef)
delete this;
return (ULONG)cRef;
}
//--------------------------------------------------------------------------
// CLogFile::QueryInterface
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::QueryInterface(REFIID riid, LPVOID *ppv)
{
// Locals
HRESULT hr=S_OK;
// Stack
TraceCall("CLogFile::QueryInterface");
// Find IID
if (IID_IUnknown == riid)
*ppv = (IUnknown *)this;
else
{
*ppv = NULL;
hr = TraceResult(E_NOINTERFACE);
goto exit;
}
// AddRef It
((IUnknown *)*ppv)->AddRef();
exit:
// Done
return hr;
}
//--------------------------------------------------------------------------
// CLogFile::Open
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::Open(HINSTANCE hInst, LPCSTR pszFile, LPCSTR pszPrefix,
DWORD cbTruncate, DWORD dwShareMode)
{
// Locals
HRESULT hr=S_OK;
CHAR szVersion[MAX_PATH];
CHAR szPath[MAX_PATH];
LPSTR pszInfo=NULL;
DWORD dwVerHnd;
DWORD dwVerInfoSize;
DWORD cbFile;
CHAR szGet[MAX_PATH];
UINT uLen;
LPWORD pdwTrans;
LPSTR pszT;
SYSTEMTIME st;
LPSTR pszVersion;
CHAR szPathMinusExt[MAX_PATH + 2];
CHAR szExt[4];
DWORD dwBytesWritten;
LPSTR pszMutex=NULL;
BOOL fReleaseMutex=FALSE;
int iCurrentLogNum; // For unique logfile generation
// Tracing
TraceCall("CLogFile::Open");
// Save the Prefix
StrCpyN(m_szPrefix, pszPrefix ? pszPrefix : "", ARRAYSIZE(m_szPrefix));
// Create a Mutex Name
IF_FAILEXIT(hr = CreateSystemHandleName(pszFile, "logfile", &pszMutex));
// Create the Mutex
m_hMutex = CreateMutex(NULL, FALSE, pszMutex);
if (m_hMutex == NULL)
{
hr = TraceResult(E_FAIL);
goto exit;
}
// If we have a mutex
if (WAIT_OBJECT_0 != WaitForSingleObject(m_hMutex, INFINITE))
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Release the Mutex
fReleaseMutex = TRUE;
// Split the logfile into path+filename and extension
iCurrentLogNum = 0;
StrCpyN(szPathMinusExt, pszFile, ARRAYSIZE(szPathMinusExt));
pszT = PathFindExtension(szPathMinusExt);
if (pszT && '.' == *pszT)
{
StrCpyN(szExt, pszT + 1, ARRAYSIZE(szExt));
*pszT = '\0'; // Remove extension from path and filename
}
else
{
// Use default extension of "log"
StrCpyN(szExt, "log", ARRAYSIZE(szExt));
}
// Generate first logfile name
wnsprintf(szPath, ARRAYSIZE(szPath), "%s.%s", szPathMinusExt, szExt);
// Open|Create the log file
do
{
m_hFile = CreateFile(szPath, GENERIC_WRITE, dwShareMode, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == m_hFile)
{
DWORD dwLastErr;
dwLastErr = GetLastError();
// If file is already in use, then try to create a separate logfile. Otherwise bail.
if (ERROR_SHARING_VIOLATION == dwLastErr)
{
// Generate next unique file name
iCurrentLogNum += 1;
wnsprintf(szPath, ARRAYSIZE(szPath), "%s (%d).%s", szPathMinusExt, iCurrentLogNum, szExt);
}
else
{
hr = TraceResultSz(E_FAIL, _MSG("Can't open logfile %s, GetLastError() = %d", szPath, dwLastErr));
goto exit;
}
}
} while (INVALID_HANDLE_VALUE == m_hFile);
// Get the File Size
cbFile = GetFileSize(m_hFile, NULL);
// Get the size of the file
if (0xFFFFFFFF != cbFile)
{
// Truncate It
if (cbFile >= cbTruncate)
{
// Set the file pointer to the end of the file
if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_BEGIN))
{
Assert(FALSE);
hr = TraceResult(E_FAIL);
goto exit;
}
// Set the End of file
if (0 == SetEndOfFile(m_hFile))
{
Assert(FALSE);
hr = TraceResult(E_FAIL);
goto exit;
}
// File is zero-length
cbFile = 0;
}
// Set the file pointer to the end of the file
if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_END))
{
Assert(FALSE);
hr = TraceResultSz(E_FAIL, _MSG("Can't seek to the end of the logfile %s, GetLastError() = %d", szPath, GetLastError()));
goto exit;
}
}
// Get Module FileName
GetModuleFileName(hInst, szPath, sizeof(szPath));
// Initialize szVersion
szVersion[0] = '\0';
// Get version information from athena
dwVerInfoSize = GetFileVersionInfoSize(szPath, &dwVerHnd);
if (dwVerInfoSize)
{
// Allocate
IF_NULLEXIT(pszInfo = (LPSTR)g_pMalloc->Alloc(dwVerInfoSize));
// Get version info
if (GetFileVersionInfo(szPath, dwVerHnd, dwVerInfoSize, pszInfo))
{
// VerQueryValue
if (VerQueryValue(pszInfo, "\\VarFileInfo\\Translation", (LPVOID *)&pdwTrans, &uLen) && uLen >= (2 * sizeof(WORD)))
{
// set up buffer for calls to VerQueryValue()
wnsprintf(szGet, ARRAYSIZE(szGet), "\\StringFileInfo\\%04X%04X\\", pdwTrans[0], pdwTrans[1]);
// What is this doing
DWORD cchLen = lstrlen(szGet);
pszT = szGet + cchLen;
// Setup file description
StrCatBuff(szGet, "FileDescription", ARRAYSIZE(szGet));
// Get the file description
if (VerQueryValue(pszInfo, szGet, (LPVOID *)&pszVersion, &uLen) && uLen)
{
StrCpyN(szVersion, pszVersion, ARRAYSIZE(szVersion));
StrCatBuff(szVersion, " ", ARRAYSIZE(szVersion));
}
// Setup Version String
StrCpyN(pszT, "FileVersion", ARRAYSIZE(szGet) - cchLen);
// Get the file version
if (VerQueryValue(pszInfo, szGet, (LPVOID *)&pszVersion, &uLen) && uLen)
StrCatBuff(szVersion, pszVersion, ARRAYSIZE(szVersion));
}
}
}
// Write the Date
GetLocalTime(&st);
wnsprintf(szPath, ARRAYSIZE(szPath), "\r\n%s\r\n%s Log started at %.2d/%.2d/%.4d %.2d:%.2d:%.2d\r\n", szVersion, pszPrefix, st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
// add a new line
WriteFile(m_hFile, szPath, lstrlen(szPath), &dwBytesWritten, NULL);
exit:
// Failure
AssertSz(SUCCEEDED(hr), "Log file could not be opened.");
// Cleanup
if (fReleaseMutex)
ReleaseMutex(m_hMutex);
SafeMemFree(pszInfo);
SafeMemFree(pszMutex);
// Done
return hr;
}
//--------------------------------------------------------------------------
// WriteLogMsg
//--------------------------------------------------------------------------
STDMETHODIMP WriteLogMsg(CLogFile *pLogFile, LOGFILETYPE lft, LPTSTR pszFormat, ...)
{
static TCHAR szBuffer[2048];
va_list arglist;
va_start(arglist, pszFormat);
wvnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, arglist);
va_end(arglist);
return pLogFile->WriteLog(lft, szBuffer);
}
//--------------------------------------------------------------------------
// CLogFile::TraceLog
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::TraceLog(SHOWTRACEMASK dwMask, TRACEMACROTYPE tracetype, ULONG ulLine, HRESULT hrResult, LPCSTR pszMessage)
{
// Write the message
if (TRACE_INFO == tracetype && pszMessage)
{
if (ISFLAGSET(dwMask, SHOW_TRACE_INFO))
WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Info: %s", GetCurrentThreadId(), ulLine, pszMessage);
}
else if (TRACE_RESULT == tracetype && pszMessage)
WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Result: HRESULT(0x%08X) - GetLastError() = %d - %s", GetCurrentThreadId(), ulLine, hrResult, GetLastError(), pszMessage);
else if (TRACE_RESULT == tracetype && NULL == pszMessage)
WriteLogMsg(this, LOGFILE_DB, "0x%08X: L(%d), Result: HRESULT(0x%08X) - GetLastError() = %d", GetCurrentThreadId(), ulLine, hrResult, GetLastError());
else
Assert(FALSE);
// Done
return hrResult;
}
//--------------------------------------------------------------------------
// CLogFile::WriteLog
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::WriteLog(LOGFILETYPE lft, LPCSTR pszData)
{
// Locals
HRESULT hr=S_OK;
DWORD bWrite;
DWORD cBytesWritten;
SYSTEMTIME st;
CHAR szLogPrefx[30];
INT cb;
LPSTR *ppszPrefix;
LPSTR pszFree=NULL;
BOOL fReleaseMutex=FALSE;
// Trace
TraceCall("CLogFile::WriteLog");
// File is not open
if (m_hFile == INVALID_HANDLE_VALUE)
return TraceResult(E_UNEXPECTED);
// Invalid Args
Assert(pszData && lft < LOGFILE_MAX);
// Thread Safety
EnterCriticalSection(&m_cs);
// Initialized
Assert(m_hMutex);
// If we have a mutex
if (WAIT_OBJECT_0 != WaitForSingleObject(m_hMutex, INFINITE))
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Release the Mutex
fReleaseMutex = TRUE;
// Get the time
GetLocalTime(&st);
// Write the log prefix and time stamp
wnsprintf(szLogPrefx, ARRAYSIZE(szLogPrefx), "%s: %.2d:%.2d:%.2d [%s] ", m_szPrefix, st.wHour, st.wMinute, st.wSecond, s_rgszPrefix[lft]);
// Set the file pointer to the end of the file (otherwise multiple writers overwrite each other)
if (0xFFFFFFFF == SetFilePointer(m_hFile, 0, NULL, FILE_END))
{
hr = TraceResult(E_FAIL);
goto exit;
}
// Write the time and prefix
if (0 == WriteFile(m_hFile, szLogPrefx, lstrlen(szLogPrefx), &cBytesWritten, NULL))
{
hr = TraceResultSz(E_FAIL, _MSG("Can't write to logfile. GetLastError() = %d", GetLastError()));
goto exit;
}
// Loop through prefixes
for (ppszPrefix = s_rgszPassPrefix; *ppszPrefix; ppszPrefix++)
{
// Does the data start with one of these prefixes
if (0 == StrCmpNI(pszData, *ppszPrefix, lstrlen(*ppszPrefix)))
{
// Dup the buffer that was passed in
IF_NULLEXIT(pszFree = PszDupA(pszData));
// Reset pszData
pszData = pszFree;
// Fixup the buffer
for (LPSTR pszTmp = (LPSTR)pszData + lstrlen(*ppszPrefix); *pszTmp && *pszTmp != '\r'; pszTmp++)
*pszTmp = '*';
// Done
break;
}
}
// Get the length of pszData
cb = lstrlen(pszData);
// write the log data
if (0 == WriteFile(m_hFile, pszData, cb, &cBytesWritten, NULL))
{
hr = TraceResultSz(E_FAIL, _MSG("Can't write to logfile. GetLastError() = %d", GetLastError()));
goto exit;
}
// Add a CRLF if not there already
if (cb < 2 || pszData[cb-1] != '\n' || pszData[cb-2] != '\r')
WriteFile(m_hFile, "\r\n", 2, &cBytesWritten, NULL);
exit:
// Cleanup
if (fReleaseMutex)
ReleaseMutex(m_hMutex);
// Thread Safety
LeaveCriticalSection(&m_cs);
// Cleanup
SafeMemFree(pszFree);
// Done
return hr;
}
//--------------------------------------------------------------------------
// CLogFile::DebugLog
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::DebugLog(LPCSTR pszData)
{
return WriteLog(LOGFILE_DB, pszData);
}
//--------------------------------------------------------------------------
// CLogFile::DebugLogs
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::DebugLogs(LPCSTR pszFormat, LPCSTR pszString)
{
// Locals
CHAR szBuffer[1024];
// Build the String
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, pszString);
// Call Debug Log
return DebugLog(szBuffer);
}
//--------------------------------------------------------------------------
// CLogFile::DebugLogd
//--------------------------------------------------------------------------
STDMETHODIMP CLogFile::DebugLogd(LPCSTR pszFormat, INT d)
{
// Locals
CHAR szBuffer[1024];
// Build the String
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), pszFormat, d);
// Call Debug Log
return DebugLog(szBuffer);
}