|
|
/*===================================================================
Microsoft Denali
Microsoft Confidential. Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Error handling
File: Error.cpp
Owner: AndrewS
This file contains general error reporting routines for Denali. ===================================================================*/ #include "denpre.h"
#pragma hdrstop
#include <psapi.h>
#include "debugger.h"
#include "asperror.h"
#include "memchk.h"
#define DELIMITER "~"
#define MAX_HEADERSIZE 128
#define MAX_TEMPLATELEN 128
//The order of ErrTemplate_Index should be exactly the same order as the IDS_BROWSER_TEMPLATE
//in the aspresource.h, and as the same order we output the template to the browser.
//Implementation will loop through the index and picking the string from the resource file.
//Implementation will also loop through the index and write the string to browser.
#define ErrTemplate_BEGIN 0
#define ErrTemplate_ENGINE_BEGIN 1
#define ErrTemplate_ENGINE_END 2
#define ErrTemplate_ERROR_BEGIN 3
#define ErrTemplate_ERROR_END 4
#define ErrTemplate_SHORT_BEGIN 5
#define ErrTemplate_SHORT_END 6
#define ErrTemplate_FILE_BEGIN 7
#define ErrTemplate_FILE_END 8
#define ErrTemplate_LINE_BEGIN 9
#define ErrTemplate_LINE_END 10
#define ErrTemplate_CODE_BEGIN 11
#define ErrTemplate_CODE_END 12
#define ErrTemplate_LONG_BEGIN 13
#define ErrTemplate_LONG_END 14
#define ErrTemplate_END 15
#define ErrTemplateMAX 16
const DWORD dwDefaultMask = 0x6; // toNTLog(OFF), toIISLog(ON), toBrowser(ON)
CHAR g_szErrTemplate[ErrTemplateMAX][MAX_TEMPLATELEN]; const LPSTR szErrSysTemplate[] = { "<html><body><h1> HTTP/1.1 ", "</h1></body></html>"}; CErrInfo g_ErrInfoOOM, g_ErrInfoUnExpected; CHAR szIISErrorPrefix[20]; DWORD cszIISErrorPrefix; CPINFO g_SystemCPInfo; // global System CodePage default info.
static char s_szContentTypeTextHtml[] = "Content-type: text/html\r\n\r\n";
CHAR *SzScodeToErrorCode(HRESULT hrError); void FreeNullifySz(CHAR **szIn); BOOL FIsResStrFormatted(char *szIn);
extern LONG g_nOOMErrors;
/*===================================================================
FreeNullifySz
Free the memory allocated in szIn, and Nullify the szIn. ===================================================================*/ void FreeNullifySz(CHAR **szIn) { if(*szIn) { free(*szIn); *szIn = NULL; } }
/*===================================================================
ErrHandleInit
PreLoad strings for 1> OOM 2> Browser Output Template Returns: HRESULT ===================================================================*/ HRESULT ErrHandleInit(void) { INT iEntry, iEntryID; HRESULT hr;
// Retrieve global system codepage and stores it.
GetCPInfo(CP_ACP, &g_SystemCPInfo);
//Init g_szErrTemplate
//Loop through, and load strings from resource file.
for (iEntry = ErrTemplate_BEGIN, iEntryID = IDS_BROWSER_TEMPLATE_BEGIN; iEntry < ErrTemplateMAX; iEntry ++, iEntryID++) { CchLoadStringOfId(iEntryID, (CHAR *)g_szErrTemplate[iEntry], MAX_TEMPLATELEN); }
g_ErrInfoOOM.m_szItem[Im_szErrorCode] = (CHAR *)malloc(sizeof(CHAR)*20*g_SystemCPInfo.MaxCharSize); g_ErrInfoOOM.m_szItem[Im_szShortDescription] = (CHAR *)malloc(sizeof(CHAR)*256*g_SystemCPInfo.MaxCharSize); g_ErrInfoOOM.m_szItem[Im_szLongDescription] = (CHAR *)malloc(sizeof(CHAR)*512*g_SystemCPInfo.MaxCharSize);
if (!g_ErrInfoOOM.m_szItem[Im_szErrorCode] || !g_ErrInfoOOM.m_szItem[Im_szShortDescription] || !g_ErrInfoOOM.m_szItem[Im_szLongDescription]) { return E_OUTOFMEMORY; } hr = LoadErrResString(IDE_OOM, &g_ErrInfoOOM.m_dwMask, g_ErrInfoOOM.m_szItem[Im_szErrorCode], g_ErrInfoOOM.m_szItem[Im_szShortDescription], g_ErrInfoOOM.m_szItem[Im_szLongDescription]);
g_ErrInfoUnExpected.m_szItem[Im_szErrorCode] = (CHAR *)malloc(sizeof(CHAR)*20*g_SystemCPInfo.MaxCharSize); g_ErrInfoUnExpected.m_szItem[Im_szShortDescription] = (CHAR *)malloc(sizeof(CHAR)*256*g_SystemCPInfo.MaxCharSize); g_ErrInfoUnExpected.m_szItem[Im_szLongDescription] = (CHAR *)malloc(sizeof(CHAR)*512*g_SystemCPInfo.MaxCharSize);
if (!g_ErrInfoUnExpected.m_szItem[Im_szErrorCode] || !g_ErrInfoUnExpected.m_szItem[Im_szShortDescription] || !g_ErrInfoUnExpected.m_szItem[Im_szLongDescription]) { return E_OUTOFMEMORY; } hr = LoadErrResString(IDE_UNEXPECTED, &g_ErrInfoUnExpected.m_dwMask, g_ErrInfoUnExpected.m_szItem[Im_szErrorCode], g_ErrInfoUnExpected.m_szItem[Im_szShortDescription], g_ErrInfoUnExpected.m_szItem[Im_szLongDescription]);
cszIISErrorPrefix = CchLoadStringOfId(IDS_IISLOG_PREFIX , szIISErrorPrefix, 20); return hr; } /*===================================================================
ErrHandleUnInit
Unit the global err-handling data.
Free up the OOM CErrInfo.
Side Effect:
Free up memory. ===================================================================*/ HRESULT ErrHandleUnInit(void) { FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szErrorCode]); FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szShortDescription]); FreeNullifySz((CHAR **)&g_ErrInfoOOM.m_szItem[Im_szLongDescription]);
FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szErrorCode]); FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szShortDescription]); FreeNullifySz((CHAR **)&g_ErrInfoUnExpected.m_szItem[Im_szLongDescription]); return S_OK; } /*===================================================================
Constructor
===================================================================*/ CErrInfo::CErrInfo() { for (UINT iErrInfo = 0; iErrInfo < Im_szItemMAX; iErrInfo++) m_szItem[iErrInfo] = NULL; m_bstrLineText = NULL; m_nColumn = -1;
m_dwMask = 0; m_pIReq = NULL; m_pHitObj = NULL;
m_dwHttpErrorCode = 0; m_dwHttpSubErrorCode = 0; }
/*===================================================================
CErrInfo::ParseResourceString
Parse Resource String to get default mask, error type, short description, and long description.
Assume resource string is proper formmated. Format of resource string DefaultMask~errortype~shortdescription~longdescription
In case we can not allocate szResourceString(szResourceString), we use default. Returns: Nothing ===================================================================*/ HRESULT CErrInfo::ParseResourceString(CHAR *szResourceString) { CHAR *szToken = NULL; INT cfield = 0; INT iItem = 0; INT iIndex = 0; INT rgErrInfoIndex[3] = {Im_szErrorCode, Im_szShortDescription, Im_szLongDescription};
if(NULL == szResourceString) { m_dwMask = dwDefaultMask; for(iItem = 0, iIndex = 0; iIndex < 3; iIndex++) { iItem = rgErrInfoIndex[iIndex]; m_szItem[iItem] = g_ErrInfoUnExpected.m_szItem[iItem]; } return S_OK; } //Mask
szToken = (char *)_mbstok((unsigned char *)szResourceString, (unsigned char *)DELIMITER); if(szToken != NULL) { m_dwMask = atoi(szToken); cfield++; } else { m_dwMask = dwDefaultMask; }
//3 String Items, ErrorCode,ShortDescription,LongDescription
for(iItem = 0, iIndex = 0; iIndex < 3; iIndex++) { szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER); iItem = rgErrInfoIndex[iIndex]; if (szToken != NULL) { m_szItem[iItem] = szToken; cfield++; } else { // Long Description is optional.
if (Im_szLongDescription != iItem) { m_szItem[iItem] = g_ErrInfoUnExpected.m_szItem[iItem]; } cfield++; } }
//check wether we have wrong format of resource string.
Assert(cfield == 4);
return S_OK; }
/*===================================================================
CErrInfo::LogError(void)
Perform all the switch logic in this functions. Send error to NT Log, IIS Log, or Browser. When reach this point, we assume all the strings have allocated, and will not be used after this function.
Side effects:
Returns: HRESULT ===================================================================*/ HRESULT CErrInfo::LogError(void) { HRESULT hr = S_OK; HRESULT hr_ret = S_OK; UINT iEInfo = 0; BOOL fIISLogFailed, fDupToNTLog;
#if DBG
// Print details about the error to debug window; don't bother if
// info is NULL (happens for things like 404 not found, etc.)
if (m_szItem[Im_szEngine] != NULL && m_szItem[Im_szFileName] != NULL) { DBGERROR((DBG_CONTEXT, "%s error in %s at line %s\n", m_szItem[Im_szEngine], m_szItem[Im_szFileName], m_szItem[Im_szLineNum]? m_szItem[Im_szLineNum] : "?"));
DBGPRINTF((DBG_CONTEXT, " %s: %s\n", m_szItem[Im_szErrorCode], m_szItem[Im_szShortDescription])); } else DBGERROR((DBG_CONTEXT, "ASP Error: %s\n", m_szItem[Im_szShortDescription]? m_szItem[Im_szShortDescription] : "?")); #endif
// Attach ASP error to HitObj (if exists and in 'executing' state)
if (m_pHitObj && m_pHitObj->FExecuting()) { CASPError *pASPError = new CASPError(this); if (pASPError) m_pHitObj->SetASPError(pASPError); } hr = LogErrortoIISLog(&fIISLogFailed, &fDupToNTLog); if (FAILED(hr)) { hr_ret = hr; }
//fIISLogFailed, if it is TRUE, then, this error was upgraded, and should be a WARNING type in
//NT event log.
hr = LogErrortoNTEventLog(fIISLogFailed, fDupToNTLog); if (FAILED(hr)) { hr_ret = hr; } hr = LogErrortoBrowserWrapper(); if (FAILED(hr)) { hr_ret = hr; } if (m_pHitObj) { m_pHitObj->SetExecStatus(eExecFailed); } //In case of an error, hr_ret is the last error reported from the 3 logging functions.
return hr_ret; }
/*===================================================================
CErrInfo::LogErrortoNTEventLog
Log Error/Event to NT Event Log.
Returns: Nothing ===================================================================*/ HRESULT CErrInfo::LogErrortoNTEventLog ( BOOL fIISLogFailed, BOOL fDupToNTLog ) { CHAR szErrNTLogEntry[4096]; CHAR szStringTemp[MAX_PATH]; INT cch = 0;
if(Glob(fLogErrorRequests)) { //Is the error serious enough to get into NT log
if(ERR_FLogtoNT(m_dwMask) || fIISLogFailed || fDupToNTLog) { szErrNTLogEntry[0] = '\0';
if (fIISLogFailed) { cch = CchLoadStringOfId(IDS_LOG_IISLOGFAILED, szStringTemp, MAX_PATH); strncat(szErrNTLogEntry, szStringTemp, cch); } if (m_szItem[Im_szFileName] != NULL) { cch = CchLoadStringOfId(IDS_LOGTOEVENTLOG_FILE, szStringTemp, MAX_PATH); strncat(szErrNTLogEntry, szStringTemp, cch); strncat(szErrNTLogEntry, m_szItem[Im_szFileName], 512); } strncat(szErrNTLogEntry, " ", 1);
if (m_szItem[Im_szLineNum] != NULL) { cch = CchLoadStringOfId(IDS_LOGTOEVENTLOG_LINE, szStringTemp, MAX_PATH); strncat(szErrNTLogEntry, szStringTemp, cch); strncat(szErrNTLogEntry, m_szItem[Im_szLineNum], 48); } strncat(szErrNTLogEntry, " ", 1); //Ok, do we have something to log.
if (m_szItem[Im_szShortDescription] != NULL) { // ShortDescription does not have ". " at the end.
// Therefore, the next strncat need to concatenate two sentences together with
// a period ". ".
char szTempPeriod[] = ". "; strncat(szErrNTLogEntry, m_szItem[Im_szShortDescription], 512); strncat(szErrNTLogEntry, szTempPeriod, 512); } else { DWORD dwMask; CHAR szDenaliNotWorking[MAX_PATH]; LoadErrResString(IDE_UNEXPECTED, &dwMask, NULL, szDenaliNotWorking, NULL); strncat(szErrNTLogEntry, szDenaliNotWorking, strlen(szDenaliNotWorking)); }
//Ok, do we have something to log.
if (m_szItem[Im_szLongDescription] != NULL) { strncat(szErrNTLogEntry, m_szItem[Im_szLongDescription], 512); }
if (fIISLogFailed || fDupToNTLog) MSG_Warning((LPCSTR)szErrNTLogEntry); else MSG_Error((LPCSTR)szErrNTLogEntry); } }
return S_OK; }
/*===================================================================
CErrInfo::LogErrortoIISLog
Log Error/Event to IIS Log.
If we fail to log the message then upgrade logging to NT event Log with entries indicate the error and the IIS log failed.
Also do the upgrade if the global setting says so.
Returns: Nothing ===================================================================*/ HRESULT CErrInfo::LogErrortoIISLog ( BOOL *pfIISLogFailed, BOOL *pfDupToNTLog ) { HRESULT hr = S_OK; const LPSTR szIISDelimiter = "|"; const DWORD cszIISDelimiter = 1; // strlen("|");
const LPSTR szIISNoInfo = "-"; const DWORD cszIISNoInfo = 1; // strlen("-");
const CHAR chProxy = '_'; CIsapiReqInfo *pIReq = NULL;
*pfIISLogFailed = FALSE; *pfDupToNTLog = FALSE; if (m_pIReq == NULL && m_pHitObj == NULL) return S_OK; //Try to write to IISLog via pIReq->QueryPszLogData()
if (ERR_FLogtoIIS(m_dwMask)) { //get pIReq
if (m_pHitObj) { pIReq = m_pHitObj->PIReq(); }
if (NULL == pIReq) { pIReq = m_pIReq; }
if (pIReq == NULL) { *pfIISLogFailed = TRUE; return E_FAIL; }
// Setup the sub-string array
const DWORD crgsz = 3; LPSTR rgsz[crgsz];
rgsz[0] = m_szItem[Im_szLineNum]; rgsz[1] = m_szItem[Im_szErrorCode]; rgsz[2] = m_szItem[Im_szShortDescription];
// Allocate the log entry string
CHAR *szLogEntry = NULL; DWORD cszLogEntry = (cszIISDelimiter * crgsz) + 1; DWORD dwIndex;
for (dwIndex = 0; dwIndex < crgsz; dwIndex++) { if (rgsz[dwIndex]) cszLogEntry += strlen(rgsz[dwIndex]); else cszLogEntry += cszIISNoInfo; }
szLogEntry = new CHAR[cszLogEntry]; if (NULL == szLogEntry) { return E_OUTOFMEMORY; }
// Copy the entry, proxy bad characters
CHAR *szSource = NULL; CHAR *szDest = szLogEntry;
// Start with a delimiter to separate us from
// the request query
memcpy(szDest, szIISDelimiter, cszIISDelimiter); szDest += cszIISDelimiter;
for (dwIndex = 0; dwIndex < crgsz; dwIndex++) { szSource = rgsz[dwIndex]; if (szSource) { while (*szSource) { if (isleadbyte(*szSource)) { *szDest++ = *szSource++; *szDest++ = *szSource++; } else if ((*szSource == ',') || (*szSource == ' ') || (*szSource == '\r') || (*szSource == '\n') || (*szSource == '\"')) { *szDest++ = chProxy; szSource++; } else *szDest++ = *szSource++; } } else { memcpy(szDest, szIISNoInfo, cszIISNoInfo); szDest += cszIISNoInfo; }
if ((dwIndex + 1) < crgsz) { // Another sub-string comming, use a delimiter
memcpy(szDest, szIISDelimiter, cszIISDelimiter); szDest += cszIISDelimiter; } } *szDest = '\0'; // Log it
BOOL fResult = TRUE;
fResult = SUCCEEDED(pIReq->AppendLogParameter(szLogEntry));
// Set "500" error in log.
if (pIReq->ECB()->dwHttpStatusCode == 200) // error content sent, OK, but really an error
pIReq->ECB()->dwHttpStatusCode = 500;
// Release log string
delete [] szLogEntry; // If any error occurred while writing to log, upgrade to NT Event log
if (!fResult) { m_dwMask = ERR_SetLogtoNT(m_dwMask); *pfIISLogFailed = TRUE; } // Even if successful we might still want the message
// in the NT event log if the global setting to do so is on.
else if (Glob(fDupIISLogToNTLog)) { if (!ERR_FLogtoNT(m_dwMask)) { // Need to remember the flag in order to insert
// the upgraded IIS log error as NT log warnings.
// The errors already destined for NT log should
// stay as errors.
m_dwMask = ERR_SetLogtoNT(m_dwMask); *pfDupToNTLog = TRUE; } } hr = S_OK; } return(hr); }
/*===================================================================
CErrInfo::LogErrortoBrowserWrapper
Just a Wrapper around Log Error/Event to Browser.
In this function, pIReq or pResponse is resolved.
NOTE: Unfortunately, this function can not tell pResponse is inited or not. In case when pResponse has not been inited, pResponse is not NULL, but things in pResponse are invalid. Therefore, caller need to provide pIReq in case where pResponse has not been inited.
Returns: HRESULT ===================================================================*/ HRESULT CErrInfo::LogErrortoBrowserWrapper() { HRESULT hr = S_OK;
//
// Must have passed in either an CIsapiReqInfo or a HITOBJ. Otherwise, there is nothing we can do.
// This condition occurs in case of HandleOOMError
//
if (m_pIReq == NULL && m_pHitObj == NULL) return E_FAIL;
// Remember response object if any
CResponse *pResponse = m_pHitObj ? m_pHitObj->PResponse() : NULL;
CIsapiReqInfo *pIReq = (m_pHitObj && pResponse && m_pHitObj->PIReq()) ? m_pHitObj->PIReq() : m_pIReq; if (!pIReq) return E_FAIL;
// Do custom errors only if response headers aren't written already
// ALSO: No custom error if called from global.asa, with intrinsic objects hidden.
// (Appln_OnStart & Session_OnStart)
//
// for errors in Appln_OnEnd or Session_OnEnd, these are not browser requests
// and so pResponse == NULL in this case.
if (!pResponse || !pResponse->FHeadersWritten()) { BOOL fIntrinsicsWereHidden = FALSE; if (m_pHitObj) { fIntrinsicsWereHidden = m_pHitObj->FRequestAndResponseIntrinsicsHidden(); m_pHitObj->UnHideRequestAndResponseIntrinsics(); }
BOOL fCustom = FALSE; hr = LogCustomErrortoBrowser(pIReq, &fCustom);
if (fIntrinsicsWereHidden) m_pHitObj->HideRequestAndResponseIntrinsics();
if (fCustom) return hr; }
// No custom error - do regular error from this object
if (m_szItem[Im_szHeader]) { BOOL fRet = pIReq->SendHeader ( m_szItem[Im_szHeader], strlen(m_szItem[Im_szHeader]) + 1, s_szContentTypeTextHtml, sizeof(s_szContentTypeTextHtml), FALSE );
if (!fRet) return E_FAIL; }
if (pResponse) hr = LogErrortoBrowser(pResponse); else hr = LogErrortoBrowser(pIReq); return hr; }
/*===================================================================
CErrInfo::LogCustomErrortoBrowser
Called by LogErrortoBrowserWrapper.
Parameters pIReq pfCustomErrorProcessed
Returns: HRESULT ===================================================================*/ HRESULT CErrInfo::LogCustomErrortoBrowser ( CIsapiReqInfo *pIReq, BOOL *pfCustomErrorProcessed ) { // Custom errors when HttpErrorCode is specified (404 or 500),
// or '500;100' ASP scripting error case
BOOL fTryErrorTransfer = FALSE; DWORD dwCode, dwSubCode;
if (m_dwHttpErrorCode == 404 || m_dwHttpErrorCode == 500 || m_dwHttpErrorCode == 401) { dwCode = m_dwHttpErrorCode; dwSubCode = m_dwHttpSubErrorCode; } else if (m_dwHttpErrorCode == 0 && m_pHitObj && m_pHitObj->FHasASPError() && // there's an error on this page
m_pHitObj->FExecuting() && // while executing
!m_pHitObj->FInTransferOnError() && // not inside transfer-on-error already
m_pHitObj->PAppln() && m_pHitObj->PResponse() && m_pHitObj->PServer() && m_pHitObj->PAppln()->QueryAppConfig()->pCLSIDDefaultEngine()) // engine in the registry is valid
{ dwCode = 500; dwSubCode = 100; fTryErrorTransfer = TRUE; } else { // no need to try
*pfCustomErrorProcessed = FALSE; return S_OK; }
// Get custom error from W3SVC
STACK_BUFFER( tempParamBuf, MAX_PATH ); TCHAR *szBuf = (TCHAR *)tempParamBuf.QueryPtr(); DWORD dwLen = MAX_PATH; BOOL fIsFileError; BOOL fSendErrorBody;
BOOL fRet = pIReq->GetCustomError(dwCode, dwSubCode, dwLen, szBuf, &dwLen, &fIsFileError, &fSendErrorBody); if (!fRet && (GetLastError() == ERROR_INSUFFICIENT_BUFFER)) { if (tempParamBuf.Resize(dwLen) == TRUE) { szBuf = (TCHAR *)tempParamBuf.QueryPtr(); fRet = pIReq->GetCustomError(dwCode, dwSubCode, dwLen, szBuf, &dwLen, &fIsFileError, &fSendErrorBody); } } if (fRet) {
if (fSendErrorBody == FALSE) { // suppress all output through intrinsic
if (m_pHitObj && m_pHitObj->PResponse()) m_pHitObj->PResponse()->SetIgnoreWrites(); } if (fIsFileError) { // Verify that the error file can be read
if (FAILED(AspGetFileAttributes(szBuf))) fRet = FALSE; } else { // Avoid circular client redirections
// (check if the current URL is the same as error URL
if (_tcsicmp(szBuf, pIReq->QueryPszPathInfo()) == 0) fRet = FALSE; } }
if (!fRet) { // no custom error found
*pfCustomErrorProcessed = FALSE; return S_OK; }
// There's a custom error - use it
HRESULT hr = S_OK;
if (fIsFileError) {
if (fSendErrorBody) // in case of file errors mime type follows the file path
// in the returned buffer
hr = WriteCustomFileError(pIReq, szBuf, szBuf+_tcslen(szBuf)+1); } else if (fTryErrorTransfer) { // transfer to URL
// need to Map Path first
TCHAR szTemplate[MAX_PATH]; WCHAR *pErrorURL; #if UNICODE
pErrorURL = szBuf; #else
CMBCSToWChar convStr; if (FAILED(convStr.Init(szBuf))) { *pfCustomErrorProcessed = FALSE; return S_OK; } pErrorURL = convStr.GetString(); #endif
if (FAILED(m_pHitObj->PServer()->MapPathInternal(0, pErrorURL, szTemplate))) { // could use custom error
*pfCustomErrorProcessed = FALSE; return S_OK; } Normalize(szTemplate);
// do the transfer
m_pHitObj->SetInTransferOnError(); hr = m_pHitObj->ExecuteChildRequest(TRUE, szTemplate, szBuf);
if (FAILED(hr)) { // error while reporting error -- report both
LogErrortoBrowser(m_pHitObj->PResponse()); } } else { // client redirect to URL
hr = WriteCustomURLError(pIReq, szBuf); }
if (fIsFileError || !fTryErrorTransfer) { // suppress all output through intrinsic
if (m_pHitObj && m_pHitObj->PResponse()) m_pHitObj->PResponse()->SetIgnoreWrites(); }
*pfCustomErrorProcessed = TRUE; return hr; }
/*===================================================================
CErrInfo::WriteCustomFileError
Dumps the content of a custom error file to the browser
Returns: NONE. ===================================================================*/ HRESULT CErrInfo::WriteCustomFileError ( CIsapiReqInfo *pIReq, TCHAR *szPath, TCHAR *szMimeType ) { HRESULT hr = S_OK; char *szStatus = m_szItem[Im_szHeader]; char *pszMBCSMimeType;
#if UNICODE
CWCharToMBCS convStr;
if (FAILED(hr = convStr.Init(szMimeType, 65001))) { return hr; } else { pszMBCSMimeType = convStr.GetString(); } #else
pszMBCSMimeType = szMimeType; #endif
if (szStatus == NULL) { // no status set -- get it from the response object if available
CResponse *pResponse = m_pHitObj ? m_pHitObj->PResponse() : NULL; if (pResponse) szStatus = pResponse->PCustomStatus(); }
hr = CResponse::SyncWriteFile(pIReq, szPath, pszMBCSMimeType, szStatus); // NULL is OK - means 200
return hr; }
/*===================================================================
CErrInfo::WriteCustomURLError
Sends client redirect to the custom URL error
Returns: NONE. ===================================================================*/ HRESULT CErrInfo::WriteCustomURLError( CIsapiReqInfo *pIReq, TCHAR *sztURL) { // Header is
// Location: redirect_URL?code;http://original_url
HRESULT hr = S_OK; char *szURL; #if UNICODE
CWCharToMBCS convRedirURL;
if (FAILED(hr = convRedirURL.Init(sztURL,65001))) { return hr; }
szURL = convRedirURL.GetString(); #else
szURL = sztURL; #endif
// code
char szCode[8]; if (m_dwHttpErrorCode > 0 && m_dwHttpErrorCode < 1000) ltoa(m_dwHttpErrorCode, szCode, 10); else return E_FAIL;
// get the current URL
char szServer[128]; DWORD dwServerSize = sizeof(szServer);
STACK_BUFFER( tempHeader, 256 ); if (!pIReq->GetServerVariableA("SERVER_NAME", szServer, &dwServerSize)) return E_FAIL; // shouldn't happen
char *szOrigURL; #if UNICODE
CWCharToMBCS convOrigStr;
if (FAILED(hr = convOrigStr.Init(pIReq->QueryPszPathInfo(), 65001))) { return hr; }
szOrigURL = convOrigStr.GetString(); #else
szOrigURL = pIReq->QueryPszPathInfo(); #endif
// estimate of the length
DWORD cchHeaderMax = strlen(szURL) + strlen(szServer) + strlen(szOrigURL) + 80; // decorations
if (tempHeader.Resize(cchHeaderMax) == FALSE) { return E_OUTOFMEMORY; } char *szHeader = (char *)tempHeader.QueryPtr();
// construct the redirection header
char *szBuf = szHeader; szBuf = strcpyExA(szBuf, "Location: "); szBuf = strcpyExA(szBuf, szURL); szBuf = strcpyExA(szBuf, "?"); szBuf = strcpyExA(szBuf, szCode); szBuf = strcpyExA(szBuf, ";http://"); szBuf = strcpyExA(szBuf, szServer); szBuf = strcpyExA(szBuf, szOrigURL); szBuf = strcpyExA(szBuf, "\r\n\r\n"); Assert(strlen(szHeader) < cchHeaderMax);
// set the status
static char s_szRedirected[] = "302 Object moved"; pIReq->SetDwHttpStatusCode(302);
// send the header
BOOL fRet = pIReq->SendHeader(s_szRedirected, sizeof(s_szRedirected), szHeader, strlen(szHeader) + 1, FALSE);
return (fRet ? S_OK : E_FAIL); }
/*===================================================================
CErrInfo::WriteHTMLEncodedErrToBrowser
Log Error/Event to Browser with HTMLEncoded via either pResponse or pIReq.
Either pResponse or pIReq must be valid.
Returns: NONE. ===================================================================*/ void CErrInfo::WriteHTMLEncodedErrToBrowser ( const CHAR *StrIn, CResponse *pResponse, CIsapiReqInfo *pIReq ) { CHAR szHTMLEncoded[2*MAX_RESSTRINGSIZE]; LPSTR pszHTMLEncoded = NULL; LPSTR pStartszHTMLEncoded = NULL; DWORD nszHTMLEncoded = 0; BOOL fStrAllocated = FALSE;
nszHTMLEncoded = HTMLEncodeLen(StrIn, CP_ACP, NULL);
if (nszHTMLEncoded > 0) { if (nszHTMLEncoded > 2 * MAX_RESSTRINGSIZE) { pszHTMLEncoded = new char[nszHTMLEncoded+2]; if (pszHTMLEncoded) { fStrAllocated = TRUE; } else { HandleOOMError(NULL, NULL); return; } } else pszHTMLEncoded = &szHTMLEncoded[0];
pStartszHTMLEncoded = pszHTMLEncoded; pszHTMLEncoded = HTMLEncode(pszHTMLEncoded, StrIn, CP_ACP, NULL); nszHTMLEncoded--; // take out the count for '\0'.
if (pResponse) pResponse->WriteSz((CHAR *)pStartszHTMLEncoded, nszHTMLEncoded); else CResponse::StaticWrite(pIReq, pStartszHTMLEncoded, nszHTMLEncoded); }
if (fStrAllocated) delete [] pStartszHTMLEncoded;
return; }
/*===================================================================
CErrInfo::LogErrortoBrowser
Log Error/Event to Browser via pResponse.
We will output 1> default ScriptErrorMessage or 2> Error Info/Default Template/has long description available or 3> Error Info/Default Template/no long description available
Returns: HRESULT ===================================================================*/ HRESULT CErrInfo::LogErrortoBrowser(CResponse *pResponse) { INT cch = 0; INT cLine = 0; INT iErrTemplate = 0; Assert(NULL != pResponse);
// When the error code is zero, then it's coming from a 500 error code path.
// (HandleSysError presets the code to 404 or 204.)
//
if (!pResponse->FHeadersWritten() && (m_dwHttpErrorCode == 500 || m_dwHttpErrorCode == 0)) pResponse->put_Status(L"500 Internal Server Error");
if(ERR_FIsSysFormat(m_dwMask)) { DWORD cChHeader = strlen(szErrSysTemplate[0]); DWORD cChTail = strlen(szErrSysTemplate[1]); pResponse->WriteSz((CHAR *)szErrSysTemplate[0], cChHeader); WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[Im_szShortDescription], pResponse, NULL); pResponse->WriteSz((CHAR *)szErrSysTemplate[1], cChTail); return S_OK; } if (!(m_pHitObj->QueryAppConfig())->fScriptErrorsSentToBrowser()) { cch = strlen((CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage())); GlobStringUseLock(); pResponse->WriteSz((CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage()),cch); GlobStringUseUnLock(); } else { // line 0 is the begin line.
cch = strlen((CHAR *)g_szErrTemplate[ErrTemplate_BEGIN]); pResponse->WriteSz((CHAR *)g_szErrTemplate[ErrTemplate_BEGIN], cch);
//7 standard items(file, line, engine, error#, short description, code, long description)
//If any info missing(is NULL), we skip.
for (cLine = 0; cLine < 7; cLine++) { if (NULL == m_szItem[cLine]) continue; iErrTemplate = cLine * 2 + 1; /* BUG 78782 (IIS Active) */ //WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], pResponse, NULL);
pResponse->WriteSz((CHAR *)g_szErrTemplate[iErrTemplate], strlen((CHAR *)g_szErrTemplate[iErrTemplate])); WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[cLine], pResponse, NULL); iErrTemplate++; /* BUG 78782 (IIS Active) */ //WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], pResponse, NULL);
pResponse->WriteSz((CHAR *)g_szErrTemplate[iErrTemplate], strlen((CHAR *)g_szErrTemplate[iErrTemplate])); }
//ouput the end line
cch = strlen((CHAR *)g_szErrTemplate[ErrTemplate_END]); pResponse->WriteSz((CHAR *)g_szErrTemplate[ErrTemplate_END], cch); } return S_OK; }
/*===================================================================
CErrInfo::LogErrortoBrowser
Log Error/Event to Browser via pIReq.
We will output 1> default ScriptErrorMessage or 2> Error Info/Default Template/has long description available or 3> Error Info/Default Template/no long description available
Returns: HRESULT ===================================================================*/ HRESULT CErrInfo::LogErrortoBrowser(CIsapiReqInfo *pIReq) { INT cLine = 0; INT iErrTemplate = 0; Assert(NULL != pIReq);
//HTTP type error, 204, 404, 500
//mimic IIS error reporting
//And send out the header.
if(ERR_FIsSysFormat(m_dwMask)) { CResponse::StaticWrite(pIReq, szErrSysTemplate[0]); WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[Im_szShortDescription], NULL, pIReq); CResponse::StaticWrite(pIReq, szErrSysTemplate[1]); return S_OK; }
if (!(m_pHitObj->QueryAppConfig())->fScriptErrorsSentToBrowser()) { GlobStringUseLock(); CResponse::StaticWrite(pIReq, (CHAR *)((m_pHitObj->QueryAppConfig())->szScriptErrorMessage())); GlobStringUseUnLock(); } else { // line 0 is the begin line.
CResponse::StaticWrite(pIReq, g_szErrTemplate[ErrTemplate_BEGIN]);
//7 standard items(file, line, engine, error#, short description, code, long description)
//If any info missing(is NULL), we skip.
for (cLine = 0; cLine < 5; cLine++) { if (NULL == m_szItem[cLine]) continue; iErrTemplate = cLine * 2 + 1; WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], NULL, pIReq); WriteHTMLEncodedErrToBrowser((CHAR *)m_szItem[cLine], NULL, pIReq);
iErrTemplate++; WriteHTMLEncodedErrToBrowser((CHAR *)g_szErrTemplate[iErrTemplate], NULL, pIReq); }
//ouput the end line
CResponse::StaticWrite(pIReq, g_szErrTemplate[ErrTemplate_END]); }
return S_OK; }
/*===================================================================
CchLoadStringOfId
Loads a string from the string table.
Returns: sz - the returned string INT - 0 if string load failed, otherwise number of characters loaded. ===================================================================*/ INT CchLoadStringOfId ( UINT id, CHAR *sz, INT cchMax ) { INT cchRet; // The handle to the DLL instance should have been set up when we were loaded
if (g_hResourceDLL == (HINSTANCE)0) { // Totally bogus
Assert(FALSE); return(0); }
cchRet = LoadStringA(g_hResourceDLL, id, sz, cchMax);
IF_DEBUG(ERROR) { // For debugging purposes, if we get back 0, get the last error info
if (cchRet == 0) { DWORD err = GetLastError(); DBGERROR((DBG_CONTEXT, "Failed to load string resource. Id = %d, error = %d\n", id, err)); DBG_ASSERT(FALSE); } }
return(cchRet); }
/*===================================================================
CwchLoadStringOfId
Loads a string from the string table as a UNICODE string.
Returns: sz - the returned string INT - 0 if string load failed, otherwise number of characters loaded. ===================================================================*/ INT CwchLoadStringOfId ( UINT id, WCHAR *sz, INT cchMax ) { INT cchRet; // The handle to the DLL instance should have been set up when we were loaded
if (g_hResourceDLL == (HINSTANCE)0) { // Totally bogus
Assert(FALSE); return(0); }
cchRet = LoadStringW(g_hResourceDLL, id, sz, cchMax);
IF_DEBUG(ERROR) { // For debugging purposes, if we get back 0, get the last error info
if (cchRet == 0) { DWORD err = GetLastError(); DBGERROR((DBG_CONTEXT, "Failed to load string resource. Id = %d, error = %d\n", id, err)); DBG_ASSERT(FALSE); } }
return(cchRet); }
/*===================================================================
HandleSysError
Dumps the error to the client and/or to the log Loads a string from the string table as a UNICODE string.
Returns: sz - the returned string INT - 0 if string load failed, otherwise number of characters loaded. ===================================================================*/ HRESULT HandleSysError( DWORD dwHttpError, DWORD dwHttpSubError, UINT ErrorID, UINT ErrorHeaderID, CIsapiReqInfo *pIReq, CHitObj *pHitObj) { CErrInfo SysErrInfo; CErrInfo *pErrInfo; CHAR szResourceStr[MAX_RESSTRINGSIZE]; CHAR szHeader[MAX_HEADERSIZE]; INT cch; pErrInfo = (CErrInfo *)&SysErrInfo;
pErrInfo->m_pHitObj = pHitObj; pErrInfo->m_pIReq = pIReq; if (ErrorHeaderID != 0) { cch = CchLoadStringOfId(ErrorHeaderID, szHeader, MAX_HEADERSIZE); pErrInfo->m_szItem[Im_szHeader] = szHeader; } else { pErrInfo->m_szItem[Im_szHeader] = NULL; } if (ErrorID != 0) cch = CchLoadStringOfId(ErrorID, szResourceStr, MAX_RESSTRINGSIZE); pErrInfo->ParseResourceString(szResourceStr);
pErrInfo->m_dwMask = ERR_SetSysFormat(pErrInfo->m_dwMask);
pErrInfo->m_dwHttpErrorCode = dwHttpError; pErrInfo->m_dwHttpSubErrorCode = dwHttpSubError; pErrInfo->LogError();
return S_OK; }
/*===================================================================
Handle500Error
Based on ErrorID determines headerID, code, sub-code, and calls HandleSysError()
Returns: HRESULT ===================================================================*/ HRESULT Handle500Error( UINT errorId, CIsapiReqInfo *pIReq) { UINT headerId; DWORD dwHttpSubError;
switch (errorId) { case IDE_SERVER_TOO_BUSY: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_SERVER_TOO_BUSY; break;
case IDE_SERVER_SHUTTING_DOWN: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_SHUTTING_DOWN; break;
case IDE_GLOBAL_ASA_CHANGED: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_RESTARTING_APP; break;
case IDE_INVALID_APPLICATION: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_INVALID_APP; break;
case IDE_GLOBAL_ASA_FORBIDDEN: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_GLOBALASA_FORBIDDEN; break;
default: headerId = IDH_500_SERVER_ERROR; dwHttpSubError = SUBERRORCODE500_SERVER_ERROR; break; }
pIReq->SetDwHttpStatusCode(500); return HandleSysError(500, dwHttpSubError, errorId, headerId, pIReq, NULL); }
/*===================================================================
HandleOOMError
Handle OOM error with special care, because we can not do any dynamic allocation.
if pIReq or pHitObj is NULL, nothing will be reported to browser
Returns: Nothing ===================================================================*/ HRESULT HandleOOMError( CIsapiReqInfo *pIReq, CHitObj *pHitObj) { CErrInfo OOMErrInfo; CErrInfo *pErrInfo;
// Note the OOM occurred
InterlockedIncrement(&g_nOOMErrors);
pErrInfo = (CErrInfo *)&OOMErrInfo;
pErrInfo->m_pIReq = pIReq; pErrInfo->m_pHitObj = pHitObj; pErrInfo->m_dwMask = g_ErrInfoOOM.m_dwMask; pErrInfo->m_szItem[Im_szErrorCode] = g_ErrInfoOOM.m_szItem[Im_szErrorCode]; pErrInfo->m_szItem[Im_szShortDescription] = g_ErrInfoOOM.m_szItem[Im_szShortDescription]; pErrInfo->m_szItem[Im_szLongDescription] = g_ErrInfoOOM.m_szItem[Im_szLongDescription];
pErrInfo->LogError();
return S_OK; } /*===================================================================
HandleError
Handle reporting of errors given ErrorID, FileName, and LineNum.
If Caller provide ErrCode or LongDescription, the default value will be overwriten.
Strings passed in will be freed. That is, consider the function as a sink. Caller should not use strings after the call.
Returns: Nothing ===================================================================*/ HRESULT HandleError( UINT ErrorID, CHAR *szFileName, CHAR *szLineNum, CHAR *szEngine, CHAR *szErrCode, CHAR *szLongDes, CIsapiReqInfo *pIReq, CHitObj *pHitObj, va_list *pArgs) { CErrInfo SysErrInfo; CErrInfo *pErrInfo; CHAR szResourceStr[MAX_RESSTRINGSIZE]; CHAR szUnformattedResStr[MAX_RESSTRINGSIZE]; HRESULT hr = S_OK;
pErrInfo = (CErrInfo *)&SysErrInfo;
pErrInfo->m_szItem[Im_szFileName] = szFileName; pErrInfo->m_szItem[Im_szLineNum] = szLineNum; pErrInfo->m_szItem[Im_szEngine] = szEngine;
pErrInfo->m_pHitObj = pHitObj; pErrInfo->m_pIReq = pIReq;
//Load resource string according to the resource ID.
if (pArgs) { CchLoadStringOfId(ErrorID, szUnformattedResStr, MAX_RESSTRINGSIZE); vsprintf(szResourceStr, szUnformattedResStr, *pArgs); } else { CchLoadStringOfId(ErrorID, szResourceStr, MAX_RESSTRINGSIZE); }
pErrInfo->ParseResourceString(szResourceStr);
//NOTE: if ErrorCode/LongDescription not NULL, caller want to overwrite.
if (szErrCode) { pErrInfo->m_szItem[Im_szErrorCode] = szErrCode; } if(szLongDes) { pErrInfo->m_szItem[Im_szLongDescription] = szLongDes; } hr = pErrInfo->LogError();
//free up the inputs
FreeNullifySz((CHAR **)&szFileName); FreeNullifySz((CHAR **)&szLineNum); FreeNullifySz((CHAR **)&szEngine); FreeNullifySz((CHAR **)&szErrCode); FreeNullifySz((CHAR **)&szLongDes); return hr; } /*===================================================================
HandleError
Handle reporting of errors given all the info.
This is basically a cover over HandleErrorSz which called from OnScriptError.
Strings passed in will be freed. That is, consider the function as a sink. Caller should not use strings after the call.
Returns: Nothing ===================================================================*/ HRESULT HandleError( CHAR *szShortDes, CHAR *szLongDes, DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szEngine, CHAR *szErrCode, CIsapiReqInfo *pIReq, CHitObj *pHitObj) { CErrInfo SysErrInfo; CErrInfo *pErrInfo; HRESULT hr = S_OK; pErrInfo = (CErrInfo *)&SysErrInfo; pErrInfo->m_dwMask = dwMask;
pErrInfo->m_szItem[Im_szHeader] = NULL; // Caller has already sent out header
pErrInfo->m_szItem[Im_szFileName] = szFileName; pErrInfo->m_szItem[Im_szLineNum] = szLineNum; pErrInfo->m_szItem[Im_szEngine] = szEngine; pErrInfo->m_szItem[Im_szErrorCode] = szErrCode; pErrInfo->m_szItem[Im_szShortDescription] = szShortDes; pErrInfo->m_szItem[Im_szLongDescription] = szLongDes; pErrInfo->m_pHitObj = pHitObj; pErrInfo->m_pIReq = pIReq;
hr = pErrInfo->LogError();
//free up the inputs
FreeNullifySz((CHAR **)&szFileName); FreeNullifySz((CHAR **)&szLineNum); FreeNullifySz((CHAR **)&szEngine); FreeNullifySz((CHAR **)&szErrCode); FreeNullifySz((CHAR **)&szShortDes); FreeNullifySz((CHAR **)&szLongDes); return hr; } /*===================================================================
HandleError
Handle reporting of errors given the IActiveScriptError and PFNLINEMAP.
This is basically a cover over HandleErrorSz which called from OnScriptError.
Returns: Nothing ===================================================================*/ HRESULT HandleError( IActiveScriptError *pscripterror, CTemplate *pTemplate, DWORD dwEngineID, CIsapiReqInfo *pIReq, CHitObj *pHitObj ) { UINT cchBuf = 0; CHAR *szOrigin = NULL; CHAR *szDesc = NULL; CHAR *szLine = NULL; CHAR *szPrefix = NULL; UINT cchOrigin = 0; UINT cchDesc = 0; UINT cchLineNum = 0; UINT cchLine = 0; EXCEPINFO excepinfo = {0}; CHAR *szResult = NULL; BSTR bstrLine = NULL; HRESULT hr; DWORD dwSourceContext = 0; // Don't trust this one
ULONG ulLineError = 0; BOOLB fGuessedLine = FALSE; // see bug 379
CHAR *szLineNumT = NULL; LPTSTR szPathInfo = NULL; LPTSTR szPathTranslated = NULL; LONG ichError = -1; CErrInfo SysErrInfo; CErrInfo *pErrInfo; wchar_t wszUnknownException[128]; wchar_t wszUnknownEngine[32]; CWCharToMBCS convStr; pErrInfo = (CErrInfo *)&SysErrInfo;
pErrInfo->m_pIReq = pIReq; pErrInfo->m_pHitObj = pHitObj;
if (pscripterror == NULL) return E_POINTER;
hr = pscripterror->GetExceptionInfo(&excepinfo); if (FAILED(hr)) goto LExit;
// bug 99543 If details are deferrred, use the callback to get
// detailed information.
if (excepinfo.pfnDeferredFillIn) excepinfo.pfnDeferredFillIn(&excepinfo);
// if error is OOM, then increment the global counter.
if ((excepinfo.wCode == ERROR_OUTOFMEMORY) || ((excepinfo.wCode == 0) && (excepinfo.scode == ERROR_OUTOFMEMORY))) InterlockedIncrement(&g_nOOMErrors);
hr = pscripterror->GetSourcePosition(&dwSourceContext, &ulLineError, &ichError); if (FAILED(hr)) goto LExit;
// Intentionally ignore any error
(VOID)pscripterror->GetSourceLineText(&bstrLine);
if (pTemplate == NULL) goto LExit;
// call GetScriptSourceInfo to get path-info (of file) and actual line number where error occurred
// bug 379: if GetScriptSourceInfo returns fGuessedLine = TRUE, it means we gave it a non-authored line,
// so we adjust below by not printing bstrLine in the error msg
if (ulLineError > 0) pTemplate->GetScriptSourceInfo(dwEngineID, ulLineError, &szPathInfo, &szPathTranslated, &ulLineError, NULL, &fGuessedLine); else { // ulLineError was zero - no line # specified, so assume main file (usually this will be "out of memory"
// so effect will be to display the script that was running when this occurred.
//
szPathInfo = pTemplate->GetSourceFileName(SOURCEPATHTYPE_VIRTUAL); szPathTranslated = pTemplate->GetSourceFileName(SOURCEPATHTYPE_PHYSICAL); }
// if we have HitObj use it to get the virtual path to avoid
// displaying of wrong path for shared templates
//
// first verify that PathTranslated == main file path; this file could be
// an include file, in which case PszPathInfo is incorrect.
//
if (!pTemplate->FGlobalAsa() && szPathTranslated && _tcscmp(szPathTranslated, pTemplate->GetSourceFileName()) == 0 && pHitObj != NULL && pHitObj->PIReq()) szPathInfo = pHitObj->PSzCurrTemplateVirtPath();
#if UNICODE
pErrInfo->m_szItem[Im_szFileName] = StringDupUTF8(szPathInfo); #else
pErrInfo->m_szItem[Im_szFileName] = StringDupA(szPathInfo); #endif
szLineNumT = (CHAR *)malloc(10*sizeof(CHAR)); if (szLineNumT) { // convert the line number
_ltoa(ulLineError, szLineNumT, 10); } pErrInfo->m_szItem[Im_szLineNum] = szLineNumT;
// is the scode one of the VBScript of JavaScript errors (this needs to be lang independent)
// excepinfo.bstrDescription now has the formatted error string.
if (excepinfo.bstrSource && excepinfo.bstrDescription) { // Bug 81954: Misbehaved objects may throw an exception without providing any information
wchar_t *wszDescription; if (excepinfo.bstrDescription[0] == L'\0') { HRESULT hrError;
if (0 == excepinfo.wCode) hrError = excepinfo.scode; else hrError = excepinfo.wCode;
wszUnknownException[0] = '\0'; // Bug 91847 Attempt to get a description via FormatMessage()
if (!HResultToWsz(hrError, wszUnknownException, 128)) CwchLoadStringOfId(IDE_SCRIPT_UNKNOWN, wszUnknownException, sizeof(wszUnknownException)/sizeof(WCHAR)); wszDescription = wszUnknownException; } else wszDescription = excepinfo.bstrDescription;
wchar_t *wszSource; if (excepinfo.bstrSource[0] == L'\0') { wszUnknownEngine[0] = '\0'; CwchLoadStringOfId(IDS_DEBUG_APP, wszUnknownEngine, sizeof(wszUnknownEngine)/sizeof(WCHAR)); wszSource = wszUnknownEngine; } else wszSource = excepinfo.bstrSource;
CHAR *ch = NULL; // convert the Source to ascii
if (convStr.Init(wszSource) != NO_ERROR) { szOrigin = NULL; } else { szOrigin = convStr.GetString(TRUE); } if (NULL != szOrigin) { // Remove the word "error"from the string, if any, because we will
//print out "error" when we print out the errorID
cchOrigin = strlen(szOrigin); if (cchOrigin > 5) // 5 is strlen("error")
{ ch = szOrigin + cchOrigin - 5; if (!strncmp(ch, "error", 5)) {// we found the word "error", truncate the szOrigin by null out the word "error"
*ch = '\0'; } } ch = NULL; } pErrInfo->m_szItem[Im_szEngine] = szOrigin;
// convert the sDescription to ascii
if (convStr.Init(wszDescription) != NO_ERROR) { szDesc = NULL; } else { szDesc = convStr.GetString(TRUE); } //check whether the szDesc is Denali/formatted error resource string or other unformatted string
if (FALSE == FIsResStrFormatted(szDesc)) { //unformatted string.
pErrInfo->m_dwMask = dwDefaultMask; if (0 == excepinfo.wCode) pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.scode); else pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.wCode); pErrInfo->m_szItem[Im_szShortDescription] = StringDupA(szDesc); pErrInfo->m_szItem[Im_szLongDescription] = NULL; } else { pErrInfo->ParseResourceString(szDesc);
char *szTempErrorCode = SzScodeToErrorCode(excepinfo.scode); char *szTempErrorASPCode = StringDupA(pErrInfo->m_szItem[Im_szErrorCode]); int nstrlen = strlen(szTempErrorCode) + strlen(szTempErrorASPCode); pErrInfo->m_szItem[Im_szErrorCode] = new char[nstrlen+4]; if(pErrInfo->m_szItem[Im_szErrorCode]) sprintf(pErrInfo->m_szItem[Im_szErrorCode], "%s : %s", szTempErrorASPCode, szTempErrorCode);
if (szTempErrorCode) delete [] szTempErrorCode; if(szTempErrorASPCode) delete [] szTempErrorASPCode; //pErrInfo->m_szItem[Im_szErrorCode] = StrDup(pErrInfo->m_szItem[Im_szErrorCode]);
pErrInfo->m_szItem[Im_szShortDescription] = StringDupA(pErrInfo->m_szItem[Im_szShortDescription]); pErrInfo->m_szItem[Im_szLongDescription] = StringDupA(pErrInfo->m_szItem[Im_szLongDescription]); }
/*
* If we didnt guess a line, and we have a line of source code to display * then attempt to display it and hopefully a line of ------^ to point to the error */ if (!fGuessedLine && bstrLine != NULL) { INT cchDBCS = 0; // Number of DBCS characters in source line
CHAR *pszTemp = NULL; // Temp sz pointer used to calculate cchDBCS
// convert the source code line
if (FAILED(hr = convStr.Init(bstrLine))) { goto LExit; } szLine = convStr.GetString(); cchLine = strlen(szLine); if (0 == cchLine) goto LExit;
// Check for DBCS character, and cchLine -= NumberofDBCScharacter, such that
// the ----^ will point to the right position.
pszTemp = szLine; while(*pszTemp != NULL) { if (IsDBCSLeadByte(*pszTemp)) { cchDBCS++; pszTemp += 2; // skip 2 bytes
} else { pszTemp++; // single byte
} }
// compute the size of the source code indicator:
// "<source line> + '\r\n' + <error pos>*'-' + '^'
// 3 chars. without source line and '-'
LONG ichErrorT = ichError; cchBuf += cchLine + ichErrorT + 3;
// allocate the result buffer
szResult = new(char[cchBuf + 2]); if (szResult == NULL) goto LExit;
// fill up the buffer
ch = szResult;
// append the <PRE>
// bug 87118, moved to template for a proper HTML encoding
// <source line>
if (cchLine) strncpy(ch, szLine, cchLine); ch += cchLine;
// stick the "----^" string on the end
if (ichErrorT > -1) { // prepend the '\n'
strncpy(ch, "\r\n", 2); ch += 2; // put in the "---"'s, and shrink "---" by #ofDBCS
ichErrorT -= cchDBCS; while (ichErrorT-- > 0) *ch++ = '-';
*ch++ = '^'; }
// append the </PRE>
// bug 87118, moved to template for a proper HTML encoding
// terminate the string
*ch++ = '\0'; pErrInfo->m_szItem[Im_szCode] = szResult;
// add line and column to error object
pErrInfo->m_nColumn = ichError; pErrInfo->m_bstrLineText = bstrLine; } } else { // Not VBS or other Engine errors/Unknown error
// Load Default
// try to compute a specific error message
HRESULT hr_def; hr_def = GetSpecificError(pErrInfo, excepinfo.scode); CHAR *szShortDescription = new CHAR[256];
// if that failed try to compute a generic error
if (FAILED(hr_def)) { pErrInfo->m_dwMask = dwDefaultMask; if (0 == excepinfo.wCode) { pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.scode); // Bug 91847 Attempt to get a description via FormatMessage()
if ((szShortDescription != NULL) && !HResultToSz(excepinfo.scode, szShortDescription, 256)) { // Displaying the error number twice would be redundant, delete it
delete [] szShortDescription; szShortDescription = NULL; } } else { pErrInfo->m_szItem[Im_szErrorCode] = SzScodeToErrorCode(excepinfo.wCode); // Bug 91847 Attempt to get a description via FormatMessage()
if ((szShortDescription != NULL) && !HResultToSz(excepinfo.wCode, szShortDescription, 256)) { // Displaying the error number twice would be redundant, delete it
delete [] szShortDescription; szShortDescription = NULL; } } pErrInfo->m_szItem[Im_szEngine] = NULL; pErrInfo->m_szItem[Im_szShortDescription] = szShortDescription; pErrInfo->m_szItem[Im_szLongDescription] = NULL; } }
LExit: if (excepinfo.bstrSource) { SysFreeString(excepinfo.bstrSource); }
if (excepinfo.bstrDescription) { SysFreeString(excepinfo.bstrDescription); }
if (excepinfo.bstrHelpFile) { SysFreeString(excepinfo.bstrHelpFile); }
pErrInfo->LogError();
if (bstrLine) { SysFreeString(bstrLine); }
FreeNullifySz((CHAR **)&szDesc);
for(INT iErrInfo = 0; iErrInfo < Im_szItemMAX; iErrInfo++) { FreeNullifySz((CHAR **)&pErrInfo->m_szItem[iErrInfo]); } return S_OK; } /*===================================================================
LoadErrResString
Loads an error string(formatted) from the string table.
Returns: pdwMask szErrorCode szShortDes szLongDes
if any of the szVariable is NULL, that particular string value will not be loaded.
S_OK if successes. E_FAIL if fails. Side Effect NONE ===================================================================*/ HRESULT LoadErrResString( UINT ErrID/*IN*/, DWORD *pdwMask, CHAR *szErrorCode, CHAR *szShortDes, CHAR *szLongDes) { CHAR *szToken = NULL; CHAR szResTemp[2*MAX_RESSTRINGSIZE]; //ResourceTempString
INT cch = 0;
cch = CchLoadStringOfId(ErrID, szResTemp, MAX_RESSTRINGSIZE);
//Mask
szToken = (char *)_mbstok((unsigned char *)szResTemp, (unsigned char *)DELIMITER); if (NULL != szToken) *pdwMask = atoi(szToken); else Assert(FALSE);
//ErrorCode
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER); if (NULL != szToken && NULL != szErrorCode) { cch = strlen(szToken); memcpy(szErrorCode, szToken, cch); szErrorCode[cch] = '\0'; }
//ShortDescription
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER); if (NULL != szToken && NULL != szShortDes) { cch = strlen(szToken); memcpy(szShortDes, szToken, cch); szShortDes[cch] = '\0'; }
//LongDescription
szToken = (char *)_mbstok(NULL, (unsigned char *)DELIMITER); if (NULL != szToken && NULL != szLongDes) { cch = strlen(szToken); memcpy(szLongDes, szToken, cch); szLongDes[cch] = '\0'; }
return S_OK; }
/*===================================================================
SzScodeToErrorCode
Conver Scode to string
Returns: Composed string
Side Effects: ***ALLOCATES MEMORY -- CALLER MUST FREE*** ===================================================================*/ CHAR *SzScodeToErrorCode ( HRESULT hrError ) { CHAR *szResult = NULL; CHAR szBuf[17]; CHAR *szNumber; CHAR *szError; INT iC; INT cch; // put a bunch of zeros into the buffer
for (iC = 0; iC < 16; ++iC) szBuf[iC] = '0';
// szNumber points half way into the buffer
szNumber = &szBuf[8];
// get the error szNumber as a hex string
_ltoa(hrError, szNumber, 16);
// back up szNumber to allow a total of 8 digits
szNumber -= 8 - strlen(szNumber);
cch = strlen(szNumber) + 1;
szError = new(CHAR[cch]); if (szError != NULL) { szError[0] = '\0'; strcat(szError, szNumber); szResult = szError; } else { HandleOOMError(NULL, NULL); }
return(szResult); }
/*===================================================================
SzComposeSpecificError
Compose a specific error for an HRESULT of the form: <string> <error-number>
This is our last resort if there is not more useful information to be had.
Returns: Composed string
Side Effects: ***ALLOCATES MEMORY -- CALLER MUST FREE*** ===================================================================*/ HRESULT GetSpecificError ( CErrInfo *pErrInfo, HRESULT hrError ) { HRESULT hr_return = E_FAIL; UINT idErr;
switch (hrError) { case DISP_E_MEMBERNOTFOUND: idErr = IDE_SCRIPT_METHOD_NOT_FOUND; break;
case DISP_E_UNKNOWNNAME: idErr = IDE_SCRIPT_UNKNOWN_NAME; break;
case DISP_E_UNKNOWNINTERFACE: idErr = IDE_SCRIPT_UNKNOWN_INTERFACE; break;
case DISP_E_PARAMNOTOPTIONAL: idErr = IDE_SCRIPT_MISSING_PARAMETER; break;
default: // Not one of the errors we know how to handle specially. E_FAIL will be returned.
idErr = 0; break; }
// build a szResult string if we find a match
if (idErr != 0) { hr_return = LoadErrResString(idErr, &(pErrInfo->m_dwMask), pErrInfo->m_szItem[Im_szErrorCode], pErrInfo->m_szItem[Im_szShortDescription], pErrInfo->m_szItem[Im_szLongDescription] ); }
return(hr_return); }
/*===================================================================
HResultToWsz
Tranlates a HRESULT to a description string of the HRESULT. Attempts to use FormatMessage() to get a
Parameters: hrIn The error to lookup wszOut String to output the description to cdwOut Number of WCHARs wszOut can hold
Returns: TRUE if a description string was found FALSE if the error number was output instead
Notes: Added to resolve bug 91847. When unexpected errors are processed the naked error number was output, which developers would then have to look up in winerror.h. ===================================================================*/ BOOL HResultToWsz(HRESULT hrIn, WCHAR *wszOut, DWORD cdwOut) { LANGID langID = LANG_NEUTRAL; DWORD dwFound = 0; HMODULE hMsgModule = NULL;
#ifdef USE_LOCALE
LANGID *pLangID; pLangID = (LANGID *)TlsGetValue(g_dwTLS);
if (NULL != pLangID) langID = *pLangID; #endif
if (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_INTERNET) hMsgModule = GetModuleHandleA("METADATA"); else hMsgModule = GetModuleHandleA("ASP");
dwFound = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, hMsgModule, hrIn, langID, wszOut, cdwOut, NULL);
if (!dwFound) { // Error not found, make a string out of the error number
WCHAR *wszResult = NULL; WCHAR wszBuf[17]; WCHAR *wszNumber; WCHAR *wszError; INT iC; // put a bunch of zeros into the buffer
for (iC = 0; iC < 16; ++iC) wszBuf[iC] = L'0';
// wszNumber points half way into the buffer
wszNumber = &wszBuf[8];
// get the error wszNumber as a hex string
_ltow(hrIn, wszNumber, 16);
// back up szNumber to allow a total of 8 digits
wszNumber -= 8 - wcslen(wszNumber);
// Copy the result to wszOut
wcsncpy(wszOut, wszNumber, cdwOut);
return FALSE; } else return TRUE; }
HMODULE GetModuleHandleForHRESULT(HRESULT hrIn) { char szModuleName[MAX_PATH]; DWORD pathLen = 0; char *pch;
szModuleName[0] = '\0';
if (g_fOOP) {
strcat(szModuleName, "INETSRV\\"); }
if (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_INTERNET) strcat(szModuleName, "METADATA.DLL"); else strcat(szModuleName, "ASP.DLL");
return(LoadLibraryExA(szModuleName, NULL, LOAD_LIBRARY_AS_DATAFILE)); }
/*===================================================================
HResultToSz
Tranlates a HRESULT to a description string of the HRESULT. Attempts to use FormatMessage() to get a
Parameters: hrIn The error to lookup szOut String to output the description to cdwOut Number of WCHARs wszOut can hold
Returns: TRUE if a description string was found FALSE if the error number was output instead
Notes: Added to resolve bug 91847. When unexpected errors are processed the naked error number was output, which developers would then have to look up in winerror.h. ===================================================================*/ BOOL HResultToSz(HRESULT hrIn, CHAR *szOut, DWORD cdwOut) { LANGID langID = LANG_NEUTRAL; HMODULE hMsgModule = NULL; BOOL bFound = FALSE;
#ifdef USE_LOCALE
LANGID *pLangID; pLangID = (LANGID *)TlsGetValue(g_dwTLS);
if (NULL != pLangID) langID = *pLangID; #endif
hMsgModule = GetModuleHandleForHRESULT(hrIn);
HRESULT hr = GetLastError();
bFound = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, hMsgModule, hrIn, langID, szOut, cdwOut, NULL);
// make one additional check before giving up. If the facility of the error is
// WIN32, then retry the call after masking out the facility code to get standard
// WIN32 errors. I.E. 80070005 is really just 5 - access denied
if (!bFound && (HRESULT_FACILITY(hrIn) == (HRESULT)FACILITY_WIN32)) {
bFound = FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, NULL, hrIn & 0xffff, langID, szOut, cdwOut, NULL); }
if (hMsgModule) FreeLibrary(hMsgModule);
if (!bFound ) { // Error not found, make a string out of the error number
CHAR *szResult = NULL; CHAR szBuf[17]; CHAR *szNumber; CHAR *szError; INT iC; // put a bunch of zeros into the buffer
for (iC = 0; iC < 16; ++iC) szBuf[iC] = L'0';
// wszNumber points half way into the buffer
szNumber = &szBuf[8];
// get the error wszNumber as a hex string
_ltoa(hrIn, szNumber, 16);
// back up szNumber to allow a total of 8 digits
szNumber -= 8 - strlen(szNumber);
// Copy the result to wszOut
strncpy(szOut, szNumber, cdwOut);
return FALSE; } else return TRUE; }
/*===================================================================
FIsResStrFormatted
Check for formatted resource string.
RETURN: TRUE/FALSE ===================================================================*/ BOOL FIsResStrFormatted(char *szIn) { BOOL freturn = FALSE; UINT cDelimiter = 0; CHAR *pch;
if(szIn) { pch = szIn; while(*pch) { if ('~' == *pch) cDelimiter++; pch = CharNextA(pch); }
if(3 == cDelimiter) return TRUE; } return freturn; }
/*===================================================================
HandleErrorMissingFilename
In several circumstances we want to report an error, but we have no filename, and the normal method for getting the filename from the template wont work because we have no line number info either (e.g. Script timeout, GPF, control GPF, etc)
Get the filename from the CIsapiReqInfo (if possible) and report the error.
Returns: Nothing ===================================================================*/ VOID HandleErrorMissingFilename ( UINT errorID, CHitObj *pHitObj, BOOL fAddlInfo /* = FALSE */, ... ) { va_list args;
if (fAddlInfo) va_start(args, fAddlInfo);
CHAR *szFileName = NULL;
if (pHitObj && pHitObj->PSzCurrTemplateVirtPath()) { #if UNICODE
szFileName = StringDupUTF8(pHitObj->PSzCurrTemplateVirtPath()); #else
szFileName = StringDupA(pHitObj->PSzCurrTemplateVirtPath()); #endif
} char szEngine[64]; CchLoadStringOfId(IDS_ENGINE, szEngine, sizeof szEngine);
char *pszEngine = new char [strlen(szEngine) + 1]; if (pszEngine) { // If the alloc failed, we will pass NULL to HandleError for pszEngine, which is fine.
// (old code used to pass NULL) All that will happen is that the AspError.Category == "". Oh Well.
// TODO: change this function to return an HRESULT.
//
strcpy(pszEngine, szEngine); }
HandleError(errorID, szFileName, NULL, pszEngine, NULL, NULL, NULL, pHitObj, fAddlInfo ? &args : NULL); }
/*===================================================================
DebugError
Handle a script error by invoking the debugger.
Returns: fails if debugger cannot be invoked. If this function fails, no other action is taken. (Therefore, the caller should make sure error is reported in some other way) ===================================================================*/ HRESULT DebugError(IActiveScriptError *pScriptError, CTemplate *pTemplate, DWORD dwEngineID, IDebugApplication *pDebugApp) { EXCEPINFO excepinfo = {0}; BSTR bstrLine = NULL; DWORD dwSourceContext = 0; ULONG ulLineError = 0; ULONG ichLineError = 0; // character offset of the line in the source
ULONG cchLineError = 0; // length of the source line
BOOLB fGuessedLine = FALSE; // see bug 379
LPTSTR szPathInfo = NULL; LPTSTR szPathTranslated = NULL; LONG ichError = -1; HRESULT hr = S_OK; IDebugDocumentContext *pDebugContext = NULL; wchar_t *wszErrNum, *wszShortDesc, *wszLongDesc; // Used to tokenize resource strings
if (pScriptError == NULL || pTemplate == NULL || pDebugApp == NULL) return E_POINTER;
if (FAILED(pScriptError->GetSourcePosition(&dwSourceContext, &ulLineError, &ichError))) return E_FAIL;
if (FAILED(pScriptError->GetExceptionInfo(&excepinfo))) return E_FAIL;
// call template object to get line number and character offset where error occurred
// (It returns both - caller discards whichever it does not want)
// bug 379: if pTemplate returns fGuessedLine == TRUE, it means we gave it a non-authored
// line, so we adjust below by not printing bstrLine in the error msg
pTemplate->GetScriptSourceInfo(dwEngineID, ulLineError, &szPathInfo, &szPathTranslated, NULL, &ichLineError, NULL);
// Create a new document context for this statement
// CONSIDER: character count that we return is bogus - however our debugging
// client (Caesar's) does not use this information anyway.
//
// If this is in the main file, create a document context based on the CTemplate compiled source
if (_tcscmp(szPathTranslated, pTemplate->GetSourceFileName()) == 0) pDebugContext = new CTemplateDocumentContext(pTemplate, ichLineError, 1);
// source refers to an include file, so create a documet context based on cached CIncFile dependency graph
else { CIncFile *pIncFile; if (FAILED(g_IncFileMap.GetIncFile(szPathTranslated, &pIncFile))) { hr = E_FAIL; goto LExit; }
pDebugContext = new CIncFileDocumentContext(pIncFile, ichLineError, 1); pIncFile->Release(); }
if (pDebugContext == NULL) { hr = E_OUTOFMEMORY; goto LExit; }
// Yes it does, bring up the debugger on this line
hr = InvokeDebuggerWithThreadSwitch ( pDebugApp, DEBUGGER_UI_BRING_DOC_CONTEXT_TO_TOP, pDebugContext ); if (FAILED(hr)) goto LExit;
// pop up a message box with the error description
// Bug 81954: Misbehaved objects may throw an exception without providing any information
wchar_t wszExceptionBuffer[256]; wchar_t *wszDescription; if (excepinfo.bstrDescription == NULL || excepinfo.bstrDescription[0] == L'\0') { HRESULT hrError;
if (0 == excepinfo.wCode) hrError = excepinfo.scode; else hrError = excepinfo.wCode;
// Bug 91847 Attempt to get a description via FormatMessage()
if (!HResultToWsz(hrError, wszExceptionBuffer, 128)) CwchLoadStringOfId(IDE_SCRIPT_UNKNOWN, wszExceptionBuffer, sizeof(wszExceptionBuffer)/sizeof(WCHAR)); wszDescription = wszExceptionBuffer; } else wszDescription = excepinfo.bstrDescription;
wchar_t wszSource[35]; CwchLoadStringOfId(IDS_SCRIPT_ERROR, wszSource, sizeof(wszSource)/sizeof(WCHAR));
// See if this is resource formatted string, and if it is, get pointers to short & long string
// resource formatted strings are delimited by '~' characters There are three '~' characters
// in the resource formatted string
//
wszErrNum = wcschr(wszDescription, L'~'); if (wszErrNum) { wszShortDesc = wcschr(wszErrNum + 1, L'~'); if (wszShortDesc) { wszLongDesc = wcschr(wszShortDesc + 1, L'~');
// OK. If all three tests succeeded, we know this is a resource formatted string,
// and we have pointers to all three segments. Replace each '~' with two newlines.
// First: Load resource strings
wchar_t wszErrorBegin[20], wszErrorEnd[5]; wchar_t *pwchEnd;
CwchLoadStringOfId(IDS_DEBUGGER_TEMPLATE_BEGIN, wszErrorBegin, sizeof(wszErrorBegin)/sizeof(WCHAR)); CwchLoadStringOfId(IDS_DEBUGGER_TEMPLATE_END, wszErrorEnd, sizeof(wszErrorEnd)/sizeof(WCHAR));
// Tokenize string by setting '~' characters to NULL and incrementing ptrs
*wszErrNum++ = *wszShortDesc++ = *wszLongDesc++ = L'\0';
// Build the string
pwchEnd = strcpyExW(wszExceptionBuffer, excepinfo.bstrSource); *pwchEnd++ = L' '; pwchEnd = strcpyExW(pwchEnd, wszErrorBegin); pwchEnd = strcpyExW(pwchEnd, wszErrNum); pwchEnd = strcpyExW(pwchEnd, wszErrorEnd); *pwchEnd++ = L'\n'; *pwchEnd++ = L'\n';
pwchEnd = strcpyExW(pwchEnd, wszShortDesc); *pwchEnd++ = L'\n'; *pwchEnd++ = L'\n';
pwchEnd = strcpyExW(pwchEnd, wszLongDesc);
wszDescription = wszExceptionBuffer; } }
MessageBoxW(NULL, wszDescription, wszSource, MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONEXCLAMATION);
LExit: if (pDebugContext) pDebugContext->Release();
if (excepinfo.bstrSource) SysFreeString(excepinfo.bstrSource);
if (excepinfo.bstrDescription) SysFreeString(excepinfo.bstrDescription);
if (excepinfo.bstrHelpFile) SysFreeString(excepinfo.bstrHelpFile);
return hr; }
|