Windows2003-3790/inetcore/urlmon/download/dbglog.cxx
2020-09-30 16:53:55 +02:00

535 lines
18 KiB
C++

// ===========================================================================
// File: DBGLOG.CXX
// CDLDebugLog: Class for Debug Logs
// DebugLogElement: Node class for debug log messages
//
#include <cdlpch.h>
#include <stdio.h>
extern HINSTANCE g_hInst;
// Initialize static variables
CList<CDLDebugLog *, CDLDebugLog *> CDLDebugLog::s_dlogList;
TCHAR CDLDebugLog::s_szMessage[MAX_DEBUG_STRING_LENGTH];
CMutexSem CDLDebugLog::s_mxsDLogList;
BOOL CDLDebugLog::s_bMessage(FALSE);
CMutexSem CDLDebugLog::s_mxsMessage;
CDLDebugLog::CDLDebugLog()
:m_DebugLogList(),
m_fAddedDebugLogHead(FALSE),
m_iRefCount(0)
{
m_szFileName[0] = '\0';
m_szUrlName[0] = '\0';
m_szMainClsid[0] = '\0';
m_szMainType[0] = '\0';
m_szMainExt[0] = '\0';
m_szMainUrl[0] = '\0';
}
CDLDebugLog::~CDLDebugLog()
{
Clear();
}
int CDLDebugLog::AddRef()
{
return ++m_iRefCount;
}
int CDLDebugLog::Release()
{
ASSERT(m_iRefCount > 0);
m_iRefCount--;
if(m_iRefCount <= 0)
{
delete this;
return 0;
}
else
return m_iRefCount;
}
CDLDebugLog * CDLDebugLog::MakeDebugLog()
{
return new CDLDebugLog();
}
// Delete all new'd data and start a new log
void CDLDebugLog::Clear()
{
DebugLogElement *pMsg = NULL;
LISTPOSITION pos;
int iNumMessages;
int i;
// delete new'd messages
iNumMessages = m_DebugLogList.GetCount();
pos = m_DebugLogList.GetHeadPosition();
for (i = 0; i < iNumMessages; i++) {
pMsg = m_DebugLogList.GetNext(pos);
delete pMsg;
}
m_DebugLogList.RemoveAll();
m_fAddedDebugLogHead = FALSE;
// commit any lingering CacheEntry
if(m_szUrlName[0])
{
FILETIME ftExpireTime;
FILETIME ftTime;
GetSystemTimeAsFileTime(&ftTime);
ftExpireTime.dwLowDateTime = (DWORD)0;
ftExpireTime.dwHighDateTime = (DWORD)0;
CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime,
ftTime, NORMAL_CACHE_ENTRY,
NULL, 0, NULL, 0);
}
m_szUrlName[0] = 0;
m_szFileName[0] = 0;
}
// Initializes the main clsid, type, ext, and codebase of the debuglog from the
// corresponding values in the CCodeDownload
// If any of the CCodeDownload's parameters are NULL, they are ignored
// (An assertion is thrown if they are all NULL)
// for Retail: returns false if they are all NULL
BOOL CDLDebugLog::Init(CCodeDownload * pcdl)
{
int iRes = 0;
if(pcdl->GetMainDistUnit())
{
iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainDistUnit(), -1, m_szMainClsid,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(pcdl->GetMainType())
{
iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainType(), -1, m_szMainType,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(pcdl->GetMainExt())
{
iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainExt(), -1, m_szMainExt,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(pcdl->GetMainURL())
{
iRes = WideCharToMultiByte(CP_ACP, 0, pcdl->GetMainURL(), -1, m_szMainUrl,
INTERNET_MAX_URL_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
ASSERT(iRes != 0);
return(iRes != 0);
}
// Initializes the main clsid, type, ext, and codebase of the debuglog
// If any of the parameters are NULL, they are ignored
// (An assertion is thrown if they are all NULL)
// for Retail: returns false if they are all NULL
BOOL CDLDebugLog::Init(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl)
{
int iRes = 0;
if(wszMainClsid)
{
iRes = WideCharToMultiByte(CP_ACP, 0, wszMainClsid, -1, m_szMainClsid,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(wszMainType)
{
iRes = WideCharToMultiByte(CP_ACP, 0, wszMainType, -1, m_szMainType,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(wszMainExt)
{
WideCharToMultiByte(CP_ACP, 0, wszMainExt, -1, m_szMainExt,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
if(wszMainUrl)
{
WideCharToMultiByte(CP_ACP, 0, wszMainUrl, -1, m_szMainUrl,
INTERNET_MAX_URL_LENGTH, NULL, NULL);
ASSERT(iRes != 0);
}
ASSERT(iRes != 0);
return(iRes != 0);
}
// Make the cache file for the debug log using the current main clsid, type, ext,
// and url data. The previous values in m_szUrlName and m_szFileName are cleaned
// up and written over.
void CDLDebugLog::MakeFile()
{
TCHAR szExtension[] = TEXT("HTM");
if(m_szFileName[0])
{
return; // Only make the file if we don't already have one
}
m_szUrlName[0] = 0;
m_szFileName[0] = 0;
if(m_szMainClsid[0])
{
wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!name=%s", m_szMainClsid);
}
else if(m_szMainType[0])
{
wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!type=%s", m_szMainType);
}
else if(m_szMainExt[0])
{
wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!ext=%s", m_szMainExt);
}
else
{
wnsprintf(m_szUrlName, sizeof(m_szUrlName)-1, "?CodeDownloadErrorLog!");
}
CreateUrlCacheEntry(m_szUrlName, 0, szExtension, m_szFileName, 0);
}
// ---------------------------------------------------------------------------
// %%Function: CDLDebugLog::DebugOut(int iOption, const char *pscFormat, ...)
// Replacement for UrlMkDebugOut() and CCodeDownload::CodeDownloadDebugOut
// calls to log code download debug/error messages
// ---------------------------------------------------------------------------
void CDLDebugLog::DebugOut(int iOption, BOOL fOperationFailed,
UINT iResId, ...)
{
// Temp solution to prevent buffer overruns in debug logging code.
// Long term, the printfs should be constrained. It will be a must
// if URLs become fully dynamic.
static char szDebugString[MAX_DEBUG_STRING_LENGTH*5];
static char szFormatString[MAX_DEBUG_FORMAT_STRING_LENGTH];
va_list args;
LoadString(g_hInst, iResId, szFormatString, MAX_DEBUG_FORMAT_STRING_LENGTH);
va_start(args, iResId);
vsprintf(szDebugString, szFormatString, args);
va_end(args);
DebugOutPreFormatted(iOption, fOperationFailed, szDebugString);
}
// Debug out taken a preformatted string instead of a resid format address and
// an arbitrary list of arguments. szDebugString is the string which will be outputted
// as the debug statement
void CDLDebugLog::DebugOutPreFormatted(int iOption, BOOL fOperationFailed,
LPTSTR szDebugString)
{
static TCHAR szUrlMkDebugOutString[MAX_DEBUG_STRING_LENGTH];
DebugLogElement *pdbglog = NULL;
DebugLogElement *pdbglogHead = NULL;
pdbglog = new DebugLogElement(szDebugString);
if(! pdbglog)
return;
wnsprintf(szUrlMkDebugOutString, MAX_DEBUG_STRING_LENGTH-1, "CODE DL:%s", szDebugString);
UrlMkDebugOut((iOption,szUrlMkDebugOutString));
if (pdbglog != NULL) {
m_DebugLogList.AddTail(pdbglog);
if (fOperationFailed && !m_fAddedDebugLogHead) {
m_fAddedDebugLogHead = TRUE;
pdbglogHead = new DebugLogElement("--- Detailed Error Log Follows ---\n");
if(! pdbglogHead)
return;
m_DebugLogList.AddHead(pdbglogHead);
pdbglogHead = new DebugLogElement(*pdbglog);
if(! pdbglogHead)
return;
m_DebugLogList.AddHead(pdbglogHead);
}
}
}
#define DEBUG_LOG_HTML_START TEXT("<html><pre>\n")
#define DEBUG_LOG_HTML_END TEXT("\n</pre></html>")
#define PAD_DIGITS_FOR_STRING(x) (((x) > 9) ? TEXT("") : TEXT("0"))
// ---------------------------------------------------------------------------
// %%Function: CDLDebugLog::DumpDebugLog()
// Output the debug error log. This log is written as a cache entry.
// pszCacheFileName is an outparam for the name of the file in the cache,
// cbBufLen is the length of the buffer
// szErrorMsg is the error message causing the dump, and hrError the hr causing
// the dump
// ---------------------------------------------------------------------------
void CDLDebugLog::DumpDebugLog(LPTSTR pszCacheFileName, int cbBufLen, LPTSTR szErrorMsg,
HRESULT hrError)
{
DebugLogElement *pMsg = NULL;
LPCSTR pszStr = NULL;
LISTPOSITION pos = NULL;
int iNumMessages = 0;
int i = 0;
HANDLE hFile = INVALID_HANDLE_VALUE;
FILETIME ftTime;
FILETIME ftExpireTime;
DWORD dwBytes = 0;
SYSTEMTIME systime;
static TCHAR pszHeader[MAX_DEBUG_STRING_LENGTH];
static const TCHAR *ppszMonths[] = {TEXT("Jan"), TEXT("Feb"), TEXT("Mar"),
TEXT("Apr"), TEXT("May"), TEXT("Jun"),
TEXT("Jul"), TEXT("Aug"), TEXT("Sep"),
TEXT("Oct"), TEXT("Nov"), TEXT("Dec")};
// Get the filename and put it in the out LPTSTR
if(!m_szFileName[0])
MakeFile();
if(pszCacheFileName)
lstrcpyn(pszCacheFileName,m_szFileName, cbBufLen);
iNumMessages = m_DebugLogList.GetCount();
pos = m_DebugLogList.GetHeadPosition();
if (pos) {
pMsg = m_DebugLogList.GetAt(pos);
if (pMsg != NULL) {
hFile = CreateFile(m_szFileName, GENERIC_READ | GENERIC_WRITE,
0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile != INVALID_HANDLE_VALUE) {
// Write the header
WriteFile(hFile, DEBUG_LOG_HTML_START, strlen(DEBUG_LOG_HTML_START),
&dwBytes, NULL);
GetLocalTime(&systime);
wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "*** Code Download Log entry (%s%d %s %d @ %s%d:%s%d:%s%d) ***\n",
PAD_DIGITS_FOR_STRING(systime.wDay), systime.wDay,
ppszMonths[systime.wMonth - 1],
systime.wYear,
PAD_DIGITS_FOR_STRING(systime.wHour), systime.wHour,
PAD_DIGITS_FOR_STRING(systime.wMinute), systime.wMinute,
PAD_DIGITS_FOR_STRING(systime.wSecond), systime.wSecond);
WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
// Write what error caused this dump
wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Code Download Error: (hr = %lx) %s\n",
hrError,
(szErrorMsg == NULL) ? ("(null)") : (szErrorMsg));
WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
wnsprintf(pszHeader, ARRAY_ELEMENTS(pszHeader)-1, "Operation failed. Detailed Information:\n"
" CodeBase: %s\n"
" CLSID: %s\n"
" Extension: %s\n"
" Type: %s\n\n",
m_szMainUrl,
m_szMainClsid,
m_szMainExt,
m_szMainType);
WriteFile(hFile, pszHeader, strlen(pszHeader), &dwBytes, NULL);
// Write the Debug Log
pos = m_DebugLogList.GetHeadPosition();
iNumMessages = m_DebugLogList.GetCount();
for (i = 0; i < iNumMessages; i++) {
pMsg = m_DebugLogList.GetNext(pos);
pszStr = pMsg->GetLogMessage();
WriteFile(hFile, pszStr, strlen(pszStr), &dwBytes, NULL);
}
// Close and clean
WriteFile(hFile, DEBUG_LOG_HTML_END, strlen(DEBUG_LOG_HTML_END),
&dwBytes, NULL);
CloseHandle(hFile);
GetSystemTimeAsFileTime(&ftTime);
ftExpireTime.dwLowDateTime = (DWORD)0;
ftExpireTime.dwHighDateTime = (DWORD)0;
CommitUrlCacheEntry(m_szUrlName, m_szFileName, ftExpireTime,
ftTime, NORMAL_CACHE_ENTRY,
NULL, 0, NULL, 0);
m_fAddedDebugLogHead = FALSE;
m_szUrlName[0] = NULL;
m_szFileName[0] = NULL;
}
}
}
}
// returns TRUE if there is already a message saved
// (If there is a message saved already, it will not be written over
// unless bOverwrite is true)
BOOL CDLDebugLog::SetSavedMessage(LPCTSTR szMessage, BOOL bOverwrite)
{
CLock lck(s_mxsMessage);
BOOL bRet = FALSE;
if(s_bMessage)
bRet = TRUE;
if((!s_bMessage) || bOverwrite)
{
lstrcpyn(s_szMessage, szMessage, MAX_DEBUG_STRING_LENGTH);
s_bMessage=TRUE;
}
return bRet;
}
LPCTSTR CDLDebugLog::GetSavedMessage()
{
CLock lck(s_mxsMessage);
if(!s_bMessage) // Make sure to return an empty string if one has not been set
{
s_szMessage[0] = '\0';
}
return s_szMessage;
}
// add a debug log to the global list
void CDLDebugLog::AddDebugLog(CDLDebugLog * dlog)
{
CLock lck(s_mxsDLogList);
if(dlog)
s_dlogList.AddTail(dlog);
}
// Remove a given debug log from the global list
void CDLDebugLog::RemoveDebugLog(CDLDebugLog * dlog)
{
CLock lck(s_mxsDLogList);
if(dlog)
{
POSITION pos = s_dlogList.Find(dlog);
if(pos != NULL)
s_dlogList.RemoveAt(pos);
}
}
// Gets the debug log with the given clsid, mime type, file extension or url code base.
// Naming priority is the same as at debug file urlname creation: clsid, then type, then ext, then url
// The names are checked in this order (so two logs with the same extension but different classids will never
// be confused, since clsids are checked before extensions)
// If an earlier name is NULL or doesn't produce a match, the next name is checked.
// If all names fails to produce a match, the return value is NULL
CDLDebugLog * CDLDebugLog::GetDebugLog(LPCWSTR wszMainClsid, LPCWSTR wszMainType, LPCWSTR wszMainExt, LPCWSTR wszMainUrl)
{
TCHAR szComparer[MAX_DEBUG_STRING_LENGTH];
POSITION pos = NULL;
szComparer[0] = NULL;
CDLDebugLog * dlogCur = NULL;
const int iSwitchMax = 4;
LPCWSTR ppwszMains[] = {wszMainClsid, wszMainType, wszMainExt, wszMainUrl};
int iSwitch = iSwitchMax;
int iRes = 0;
// Find the first non-empty name
for(iSwitch = 0; iSwitch < iSwitchMax; iSwitch++)
{
if((ppwszMains[iSwitch]) && (ppwszMains[iSwitch])[0])
break;
}
if(iSwitch >= iSwitchMax)
return NULL;
// Grab mutex for accesing the list
CLock lck(s_mxsDLogList);
// Loop over the names; a higher array value means a lower priority name;
// don't check a lower priority name unless all higher priority names have
// failed
while(iSwitch < iSwitchMax)
{
iRes = WideCharToMultiByte(CP_ACP, 0, ppwszMains[iSwitch], -1, szComparer,
MAX_DEBUG_STRING_LENGTH, NULL, NULL);
if(iRes == 0)
return NULL;
// Look through the entire list for a debuglog whose name (clsid, type, ext, or url)
// matches the given one
for(pos = s_dlogList.GetHeadPosition(); pos != NULL; )
{
dlogCur = s_dlogList.GetNext(pos);
switch(iSwitch)
{
case 0:
if(!lstrcmp(szComparer, dlogCur->GetMainClsid()))
return dlogCur;
break;
case 1:
if(!lstrcmp(szComparer, dlogCur->GetMainType()))
return dlogCur;
break;
case 2:
if(!lstrcmp(szComparer, dlogCur->GetMainExt()))
return dlogCur;
break;
case 3:
if(!lstrcmp(szComparer, dlogCur->GetMainUrl()))
return dlogCur;
break;
default:
break;
}
}
iSwitch++;
}
// No match of any of the non-NULL passed in names was found
return NULL;
}
DebugLogElement::DebugLogElement(const DebugLogElement &ref)
{
SetLogMessage(ref.m_szMessage);
}
DebugLogElement::DebugLogElement(LPSTR szMessage)
: m_szMessage(NULL)
{
SetLogMessage(szMessage);
}
DebugLogElement::~DebugLogElement()
{
if (m_szMessage != NULL)
{
delete [] m_szMessage;
}
}
HRESULT DebugLogElement::SetLogMessage(LPSTR szMessage)
{
HRESULT hr = S_OK;
if (m_szMessage != NULL)
{
delete [] m_szMessage;
m_szMessage = NULL;
}
m_szMessage = new char[strlen(szMessage) + 1];
if (m_szMessage != NULL)
{
strcpy(m_szMessage, szMessage);
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}