//depot/private/jasbr/inetsrv/iis/svcs/cmp/asp/template.cpp#19 - edit change 3548 (text) /*============================================================================== Microsoft Denali Microsoft Confidential. Copyright 1996 Microsoft Corporation. All Rights Reserved. File: template.cpp Maintained by: DaveK Component: source file for Denali Compiled Template object ==============================================================================*/ #include "denpre.h" #pragma hdrstop const int SNIPPET_SIZE = 20; // # of characters in the code snippets #pragma warning( disable : 4509 ) // suppress SEH/destructor warnings #pragma warning( disable : 4355 ) // ignore: "'this' used in base member init #include "debugger.h" #include "dbgutil.h" #include "tlbcache.h" #include "ie449.h" #include "memchk.h" #include "vecimpl.h" // Include after memchk to insure that vector uses our mem manager. #include "Accctrl.h" #include "aclapi.h" // Init class statics CTemplate::CTokenList *CTemplate::gm_pTokenList = NULL; PTRACE_LOG CTemplate::gm_pTraceLog = NULL; HANDLE CTemplate::sm_hSmallHeap = NULL; HANDLE CTemplate::sm_hLargeHeap = NULL; extern BOOL g_fLazyContentPropDisabled; extern DWORD g_dwFileMonitoringTimeoutSecs; // Max # of opener tokens to look for #define TOKEN_OPENERS_MAX 8 /*=================================================================== Private non-class support functions ===================================================================*/ static void ByteRangeFromPb(BYTE* pbSource, CByteRange& brTarget); static BOOLB FByteRangesAreEqual(CByteRange& br1, CByteRange& br2); static unsigned CharAdvDBCS(WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS = FALSE); static void LineFromByteRangeAdv(CByteRange& br, CByteRange& brLine); static void LTrimWhiteSpace(CByteRange& br); static void RTrimWhiteSpace(CByteRange& br); static CByteRange BrNewLine(CByteRange br); static BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace = TRUE); static BOOLB FWhiteSpaceEx(WCHAR wch, BOOLB fSpaceIsWhiteSpace = TRUE); static BOOLB FByteRangeIsWhiteSpace(CByteRange br); static BOOLB FTagName(BYTE* pb, UINT cb); static void ByteAlignOffset(UINT* pcbOffset, UINT cbAlignment); static void GetSzFromPatternInserts(char* pszPattern, UINT cInserts, char** ppszInserts, char* szReturned); static UINT CchPathFromFilespec(LPCTSTR szFile); static void GetPathFromParentAndFilespec(LPCTSTR szParentPath, LPCTSTR szFileSpec, LPTSTR* pszPath); static void HandleAccessFailure(CHitObj* pHitObj, TCHAR* szFile); static void SendToLog(DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szShortDes, CHAR *szLongDes, CHAR *szEngine, CHitObj *pHitObj); static HRESULT GetProgLangId(CByteRange& brEngine, PROGLANG_ID* pProgLangId); inline void __cdecl DebugPrintf(LPCSTR fmt, ...) { #if DBG char msg[512]; va_list marker; va_start(marker, fmt); vsprintf(msg, fmt, marker); va_end(marker); OutputDebugStringA(msg); #endif } /* ============================================================================ ByteRangeFromPb Gets a byte range from a contiguous block of memory Returns: Nothing. Side effects: None. */ void ByteRangeFromPb ( BYTE* pbSource, CByteRange& brTarget ) { Assert(pbSource != NULL); brTarget.m_cb = *(ULONG*)pbSource; brTarget.m_pb = pbSource + sizeof(ULONG); } /* ============================================================================ FByteRangesAreEqual Compares two byte ranges Returns: BOOLB. True if byte ranges are equal, else false. Side effects: None. */ BOOLB FByteRangesAreEqual ( CByteRange& br1, CByteRange& br2 ) { if(br1.m_cb != br2.m_cb) return FALSE; return (!_strnicmp((LPCSTR)br1.m_pb, (LPCSTR)br2.m_pb, br1.m_cb)); } /* ============================================================================ CharAdvDBCS Advance "cchCharAdv" characters in a buffer SBCS: Degenerates to simple pointer arithmatic Arguments: wCodePage - code page pchStart - pointer to beginning of segment pchEnd - pointer to just past end of segment cCharAdv - # of characters to advance ppchEnd - [output], contains pointer "cCharAdv" chars past pchStart fForceDBCS - if TRUE, always use double byte algorithm. (for verifying correct behavior of func in debug mode) Returns: (int) # of characters that we actually advanced Notes: By passing INFINITE for "cCharAdv", you can use this function to count characters in a block Side effects: None. */ unsigned CharAdvDBCS ( WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS ) { CPINFO CpInfo; GetCPInfo(wCodePage, &CpInfo); if (!fForceDBCS && CpInfo.MaxCharSize == 1) { char *pchT = pchStart + min(cCharAdv, unsigned(pchEnd - pchStart)); if (ppchEnd) *ppchEnd = pchT; #if DBG // Verify DBCS algorithm (not often tested otherwise) char *pchTest; unsigned cchTest = CharAdvDBCS(wCodePage, pchStart, pchEnd, cCharAdv, &pchTest, TRUE); Assert (cchTest == unsigned(pchT - pchStart) && pchTest == pchT); #endif return DIFF(pchT - pchStart); } else { int cch = 0; char *pchNext = pchStart; // Count DBCS characters. We have to stop before pchEnd because // pchEnd may point past file map and CharNextExA AVs when advancing // past allocated memory while (cCharAdv > 0 && pchNext < pchEnd-2) { pchNext = *pchNext? AspCharNextA(wCodePage, pchNext) : pchNext + 1; --cCharAdv; ++cch; } // We could stop on the last or the before last character // depending on the DBCS char sequence if (cCharAdv > 0 && pchNext == pchEnd-1) { // Only one byte - has to be one single byte character ++pchNext; ++cch; } else if (cCharAdv > 0 && pchNext == pchEnd-2) { // 2 bytes left - either 1 2-byte char or 2 1-byte chars if (IsDBCSLeadByteEx(wCodePage, *pchNext)) { ++cch; pchNext += 2; } else { // Two characters left. If cCharAdv > 1, this means that user wants to // advance at least two more chars. Otherwise, cCharAdv == 1, and // we advance one char // if (cCharAdv > 1) { cch += 2; pchNext += 2; } else { Assert (cCharAdv == 1); ++cch; ++pchNext; } } } if (ppchEnd) *ppchEnd = pchNext; return cch; } } /* ============================================================================ LineFromByteRangeAdv Gets the first line in a byte range. Returns: Nothing Side effects: Advances source byte range to just beyond its first non-white-space line, if one is found. */ void LineFromByteRangeAdv ( CByteRange& brSource, CByteRange& brLine ) { CByteRange brTemp; if(brSource.IsNull()) { brLine.Nullify(); return; } brLine.m_pb = brSource.m_pb; brTemp = BrNewLine(brSource); if(brTemp.IsNull()) { // We found no newline in a non-empty byte range: // set line range to entire source byte range and empty source byte range brLine.m_cb = brSource.m_cb; brSource.Nullify(); } else { // We found a newline in a non-empty byte range: // set line range to portion of source byte range before new line; // set source range to portion of source range after new line brLine.m_cb = DIFF(brTemp.m_pb - brSource.m_pb); brSource.m_pb = brTemp.m_pb + brTemp.m_cb; brSource.m_cb -= (brLine.m_cb + brTemp.m_cb); } } /* ============================================================================ LTrimWhiteSpace Left-trim white space from byte-range Returns: Nothing Side effects: Advances byte range to just beyond its first non-white-space character. */ void LTrimWhiteSpace ( CByteRange& br ) { if(br.IsNull()) return; while(FWhiteSpace(*br.m_pb)) { br.m_pb++; if(--br.m_cb == 0) return; } } /* ============================================================================ RTrimWhiteSpace Right-trim white space from byte-range */ void RTrimWhiteSpace(CByteRange& br) { if(br.IsNull()) return; while(FWhiteSpace(*(br.m_pb + br.m_cb - 1))) { if(--br.m_cb == 0) return; } } /* ============================================================================ BrNewLine Returns ptr to the first newline in a byte range NOTE does not change byte range (since it is passed by value) */ CByteRange BrNewLine(CByteRange br) { while(!br.IsNull()) { if(*br.m_pb == '\r') return CByteRange(br.m_pb, (br.m_cb > 1 && br.m_pb[1] == '\n')? 2 : 1); else if (*br.m_pb == '\n') return CByteRange(br.m_pb, 1); ++br.m_pb; --br.m_cb; } return CByteRange(); } /* ============================================================================ FWhiteSpace Returns: TRUE if ch is a white-space character, else returns FALSE Certain character(s) (e.g. space) may be treated as non-white-space; to do this, caller passes FALSE for fSpaceIsWhiteSpace flag. */ BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace) { switch (ch) { case ' ': return fSpaceIsWhiteSpace; case '\0': return TRUE; case '\a': return TRUE; case '\b': return TRUE; case '\f': return TRUE; case '\n': return TRUE; case '\r': return TRUE; case '\t': return TRUE; case '\v': return TRUE; default: return FALSE; } } /* ============================================================================ FWhiteSpaceEx Returns: TRUE if ch is a white-space character, else returns FALSE Certain character(s) (e.g. space) may be treated as non-white-space; to do this, caller passes FALSE for fSpaceIsWhiteSpace flag. */ BOOLB FWhiteSpaceEx(WCHAR wch, BOOLB fSpaceIsWhiteSpace) { switch (wch) { case L' ': return fSpaceIsWhiteSpace; case L'\0': return TRUE; case L'\a': return TRUE; case L'\b': return TRUE; case L'\f': return TRUE; case L'\n': return TRUE; case L'\r': return TRUE; case L'\t': return TRUE; case L'\v': return TRUE; default: return FALSE; } } /* ============================================================================ FByteRangeIsWhiteSpace Is the entire input byte range white space? NOTE input byte range is byval; caller's copy is not changed */ BOOLB FByteRangeIsWhiteSpace(CByteRange br) { while(!br.IsNull()) { if(!FWhiteSpace(*(br.m_pb))) return FALSE; br.Advance(1); } return TRUE; } /* ============================================================================ FTagName Does pb point to a valid HTML tag name? (i.e., is *pb a valid HTML tag name and not a substring?) Returns TRUE or FALSE Side effects None */ BOOLB FTagName(BYTE* pb, UINT cb) { if((pb == NULL) || (cb == 0)) return FALSE; // a valid HTML tag name must be preceded by white space ... if( FWhiteSpace(*(pb - 1)) || *(pb - 1) == '@' ) { // ... and followed either by white space or the tag separator if(FWhiteSpace(*(pb + cb))) return TRUE; else if(*(pb + cb) == CH_ATTRIBUTE_SEPARATOR) return TRUE; } return FALSE; } /*=================================================================== ByteAlignOffset Byte-aligns an offset value, based on size of source data */ void ByteAlignOffset ( UINT* pcbOffset, // ptr to offset value UINT cbAlignment // Alignment boundary ) { // comment the below code out so that it works for 64 bit... // only byte-align for 2-, or 4-byte data // since our base pointer in only aligned to a 4 byte boundary //if(cbAlignment == 2 || cbAlignment == 4) //{ // if current offset does not fall on a byte-aligned location for current data type, // advance offset to next byte-aligned location Assert(cbAlignment > 0); --cbAlignment; if (*pcbOffset & cbAlignment) *pcbOffset = (*pcbOffset + cbAlignment + 1) & ~cbAlignment; } /* ============================================================================ GetSzFromPatternInserts Returns a 'resolved' version of a pattern string, i.e. a new string in which | characters have been replaced by caller-specified insert strings. NOTE this function allocates, but caller must free Returns: Nothing Side effects: allocates memory */ void GetSzFromPatternInserts ( char* pszPattern, // 'pattern' string UINT cInserts, // count of insert strings char** ppszInserts, // array of ptrs to insert strings char* szReturned // returned string MUST be allocated by caller ) { UINT cchRet = strlen(pszPattern); // length of return string char* pchStartCopy = pszPattern; // ptr to start of copy range in pattern char* pchEndCopy = pszPattern; // ptr to end of copy range in pattern UINT cActualInserts = 0; // count of actual insert strings // init return string to empty so we can concatenate onto it szReturned[0] = NULL; // zero out length of return string - we now use it to count actual length as we build return string cchRet = 0; while(TRUE) { // advance end-of-copy ptr through pattern looking for insertion points or end of string while ((*pchEndCopy != NULL) && (IsDBCSLeadByte(*pchEndCopy) || (*pchEndCopy != '|'))) pchEndCopy = CharNextA(pchEndCopy); // cat from start-of-copy to end-of-copy onto return string strncat(szReturned, pchStartCopy, DIFF(pchEndCopy - pchStartCopy)); // update return string length cchRet += DIFF(pchEndCopy - pchStartCopy); // if we are at end of pattern, exit if(*pchEndCopy == NULL) goto Exit; if(cActualInserts < cInserts) { // if inserts remain, cat the next one onto return string strcat(szReturned, ppszInserts[cActualInserts]); // update return string length cchRet += strlen(ppszInserts[cActualInserts]); cActualInserts++; } // advance end-of-copy and start-of-copy beyond insertion point pchEndCopy++; pchStartCopy = pchEndCopy; } Exit: // null-terminate return string szReturned[cchRet] = NULL; } /* ============================================================================ CchPathFromFilespec Returns a filespec's path length (exclusive of filespec) NOTE path string includes trailing '\' or '/' Returns: Length of path string Side effects: None */ UINT CchPathFromFilespec ( LPCTSTR szFileSpec // filespec ) { TCHAR* p1 = _tcsrchr(szFileSpec, _T('\\')); TCHAR* p2 = _tcsrchr(szFileSpec, _T('/')); // this wont be a DBCS trail byte. if (p1 == NULL && p2 == NULL) THROW(E_FAIL); return (UINT) ((((LPTSTR)max(p1,p2) - szFileSpec)) + 1); } /* ============================================================================ GetPathFromParentAndFilespec Returns an absolute path which is a 'parent' file's path concatenated with a filespec. Returns: absolute path (out-parameter) Side effects: None */ void GetPathFromParentAndFilespec ( LPCTSTR szParentPath, // parent path LPCTSTR szFileSpec, // filespec LPTSTR* pszPath // resolved path (out-parameter) ) { UINT cchParentPath = CchPathFromFilespec(szParentPath); if ((cchParentPath + _tcslen(szFileSpec)) > MAX_PATH) THROW(E_FAIL); _tcsncpy(*pszPath, szParentPath, cchParentPath); _tcscpy(*pszPath + cchParentPath, szFileSpec); } /* ============================================================================ HandleAccessFailure Handles an access-denied failure Returns: nothing Side effects: none */ void HandleAccessFailure ( CHitObj* pHitObj, // browser's hitobj TCHAR * szFile // file path of main template ) { Assert(pHitObj); // debugging diagnostic print #if DBG STACK_BUFFER( authUserBuff, 32 ); char *szAuthUser; DWORD cbAuthUser; if (SERVER_GET(pHitObj->PIReq(), "AUTH_USER", &authUserBuff, &cbAuthUser)) { szAuthUser = (char*)authUserBuff.QueryPtr(); } else { szAuthUser = "anonymous"; } #if UNICODE DBGPRINTF((DBG_CONTEXT, "No permission to read file %S\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated())); #else DBGPRINTF((DBG_CONTEXT, "No permission to read file %s\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated())); #endif DBGPRINTF((DBG_CONTEXT, " The user account is \"%s\"\n", szAuthUser)); #endif CResponse *pResponse = pHitObj->PResponse(); if (!pResponse) return; pHitObj->PIReq()->SetDwHttpStatusCode(401); HandleSysError(401,3,IDE_401_3_ACCESS_DENIED,IDH_401_3_ACCESS_DENIED,pHitObj->PIReq(),pHitObj); return; } /* ============================================================================ SendToLog Sends Error Info to Log Returns: Nothing Side effects: None. */ void SendToLog ( DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szEngine, CHAR *szErrCode, CHAR *szShortDes, CHAR *szLongDes, CHitObj *pHitObj // browser's hitobj ) { CHAR *szFileNameT; CHAR *szLineNumT; CHAR *szEngineT; CHAR *szErrCodeT; CHAR *szShortDesT; CHAR *szLongDesT; if(pHitObj) { // NOTE - szFileName is assumed to be UTF8 when UNICODE is defined szFileNameT = StringDupA(szFileName); szLineNumT = StringDupA(szLineNum); szEngineT = StringDupA(szEngine); szErrCodeT = StringDupA(szErrCode); szShortDesT = StringDupA(szShortDes); szLongDesT = StringDupA(szLongDes); HandleError(szShortDesT, szLongDesT, dwMask, szFileNameT, szLineNumT, szEngineT, szErrCodeT, NULL, pHitObj); } } /* ============================================================================ FreeNullify Frees and nullifies a ptr to memory allocated with malloc. Returns: Nothing Side effects: None */ static void FreeNullify ( void** pp ) { if(*pp != NULL) { free(*pp); *pp = NULL; } } /* ============================================================================ SmallTemplateFreeNullify Frees and nullifies a ptr to memory allocated with CTemplate::SmallMalloc. Returns: Nothing Side effects: None */ static void SmallTemplateFreeNullify ( void** pp ) { if(*pp != NULL) { CTemplate::SmallFree(*pp); *pp = NULL; } } /* ============================================================================ LargeTemplateFreeNullify Frees and nullifies a ptr to memory allocated with CTemplate::LargeMalloc. Returns: Nothing Side effects: None */ static void LargeTemplateFreeNullify ( void** pp ) { if(*pp != NULL) { CTemplate::LargeFree(*pp); *pp = NULL; } } /* ============================================================================ GetProgLangId Gets the prog lang id for a script engine Returns: Nothing Side effects: throws on error */ HRESULT GetProgLangId ( CByteRange& brEngine, // engine name PROGLANG_ID* pProgLangId // prog lang id (out-parameter) ) { STACK_BUFFER( tempEngine, 128 ); if (!tempEngine.Resize(brEngine.m_cb + 1)) { return E_OUTOFMEMORY; } LPSTR szProgLang = static_cast (tempEngine.QueryPtr()); strncpy(szProgLang, (LPCSTR)brEngine.m_pb, brEngine.m_cb); szProgLang[brEngine.m_cb] = '\0'; return g_ScriptManager.ProgLangIdOfLangName((LPCSTR) szProgLang, pProgLangId); } /* **************************************************************************** CByteRange member functions */ /* ======================================================== CByteRange::Advance Advances a byte range. */ void CByteRange::Advance(UINT i) { if(i >= m_cb) { Nullify(); } else { m_pb += i; m_cb -= i; } } /* ======================================================== CByteRange::FMatchesSz Compares a byte range with a string, case-insensitively */ BOOLB CByteRange::FMatchesSz ( LPCSTR psz ) { if(IsNull() || (psz == NULL)) return FALSE; if((ULONG)strlen(psz) != m_cb) return FALSE; return !_strnicmp((const char*)m_pb, psz, m_cb); } /* ============================================================================ CByteRange::PbString Finds a case-insensitive string within a byte range Returns: Ptr to first case-insensitive occurrence of the string in this byte range; NULL if none found. Side effects: None */ BYTE* CByteRange::PbString ( LPSTR psz, LONG lCodePage ) { UINT cch = strlen(psz); if(cch == 0) return NULL; BYTE *pbLocal = m_pb; UINT cbLocal = m_cb; char ch0 = psz[0]; BYTE *pbTemp = NULL; UINT cbAdvanced = 0; if (IsCharAlpha(ch0)) { // cannot use strchr while (cbLocal >= cch) { if (_strnicmp((const char *)pbLocal, psz, cch) == 0) return pbLocal; // The following code simply performs a DBCS-enabled ByteRange.Advance() action. pbTemp = pbLocal; pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1; cbAdvanced = DIFF(pbLocal - pbTemp); if (cbAdvanced >= cbLocal) { cbLocal = 0; pbLocal = NULL; } else cbLocal -= cbAdvanced; } } else { // can use strchr while (cbLocal >= cch) { pbTemp = (BYTE *)memchr(pbLocal, ch0, cbLocal); if (pbTemp == NULL) break; UINT cbOffset = DIFF(pbTemp - pbLocal); if (cbOffset >= cbLocal) break; pbLocal = pbTemp; cbLocal -= cbOffset; if (cch <= cbLocal && _strnicmp((const char *)pbLocal, psz, cch) == 0) return pbLocal; // The following code simply performs a DBCS-enabled ByteRange.Advance() action. pbTemp = pbLocal; pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1; cbAdvanced = DIFF(pbLocal - pbTemp); if (cbAdvanced >= cbLocal) { cbLocal = 0; pbLocal = NULL; } else cbLocal -= cbAdvanced; } } return NULL; } /* ============================================================================ CByteRange::PbOneOfAspOpenerStringTokens Finds a case-insensitive string within a byte range that matches one of the strings passed !!! WILL ONLY WORK IF THE FOLLOWING IS TRUE: 1) All the tokens start with the same charater (for example '<') 2) This character is not alpha (so that strchr() would work) !!! THE ABOVE ASSUMPTIONS MAKE THE CODE WORK FASTER Returns: Ptr to first case-insensitive occurrence of the string in this byte range; NULL if none found. *pcindex is set to the index of string found Side effects: None */ BYTE* CByteRange::PbOneOfAspOpenerStringTokens ( LPSTR rgszTokens[], UINT rgcchTokens[], UINT nTokens, UINT *pidToken ) { if (nTokens == 0) return NULL; BYTE *pb = m_pb; // pointer to unsearched remainder of the range UINT cbRemainder = m_cb; // remaining byte range length char ch0 = rgszTokens[0][0]; // first char of every token while (cbRemainder > 0) { // BUG 82331: avoid strchr() because byte range is not null-terminated while (cbRemainder > 0 && *pb != ch0) { ++pb; --cbRemainder; } if (cbRemainder == 0) break; for (UINT i = 0; i < nTokens; i++) { if ((rgcchTokens[i] <= cbRemainder) && (rgszTokens[i] != NULL) && (_strnicmp((const char *)pb, rgszTokens[i], rgcchTokens[i]) == 0)) { *pidToken = i; return pb; } } ++pb; --cbRemainder; } return NULL; } /* ============================================================================ CByteRange::FEarlierInSourceThan Does this byte range occur earlier in source than parameter byte range? Returns TRUE or FALSE Side effects None */ BOOLB CByteRange::FEarlierInSourceThan(CByteRange& br) { if(br.IsNull()) return TRUE; return(m_idSequence < br.m_idSequence); } /* **************************************************************************** CTemplate member functions */ /* ============================================================================ CTemplate::CTemplate Ctor */ CTemplate::CTemplate() : m_pWorkStore(NULL), m_fGlobalAsa(FALSE), m_fReadyForUse(FALSE), m_fDontAttach(FALSE), m_hEventReadyForUse(NULL), m_fDebuggerDetachCSInited(FALSE), m_pbStart(NULL), m_cbTemplate(0), m_cRefs(1), // NOTE ctor AddRefs implicitly m_pbErrorLocation(NULL), m_idErrMsg(0), m_cMsgInserts(0), m_ppszMsgInserts(NULL), m_cScriptEngines(0), m_rgrgSourceInfos(NULL), m_rgpDebugScripts(NULL), m_rgpFilemaps(NULL), m_cFilemaps(0), m_rgpSegmentFilemaps(NULL), m_cSegmentFilemapSlots(0), m_wCodePage(CP_ACP), m_lLCID(LOCALE_SYSTEM_DEFAULT), m_ttTransacted(ttUndefined), m_fSession(TRUE), m_fScriptless(FALSE), m_fDebuggable(FALSE), m_fIsValid(FALSE), m_fDontCache(FALSE), m_fZombie(FALSE), m_fCodePageSet(FALSE), m_fLCIDSet(FALSE), m_fIsPersisted(FALSE), m_fIsUNC(FALSE), m_fIsEncrypted(FALSE), m_szPersistTempName(NULL), m_szApplnVirtPath(NULL), m_szApplnURL(NULL), m_CPTextEvents(this, IID_IDebugDocumentTextEvents), m_pdispTypeLibWrapper(NULL), m_dwLastErrorMask(S_OK), m_hrOnNoCache(S_OK), m_cbTargetOffsetPrevT(0), m_pHashTable(NULL), m_pServicesConfig(NULL), m_cUseCount(1), m_pMostRecentImpersonatedTokenUser (NULL), m_pMostRecentImpersonatedSID(NULL), m_cbTokenUser(0), m_fNeedsMonitoring(FALSE), m_fInCheck(FALSE), m_dwLastMonitored (0), m_dwLastAccessCheck(0), m_fTemplateLockInited(FALSE), m_dwCacheTag(0) { m_wCodePage = GetACP(); for (UINT i = 0; i < ILE_MAX; i++) { m_pszLastErrorInfo[i] = NULL; } IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, m_cRefs, this); } #if PER_TEMPLATE_REFLOG m_pTraceLog = CreateRefTraceLog (100,0); WriteRefTraceLog (m_pTraceLog,m_cRefs, this); #endif } /* ============================================================================ CTemplate::~CTemplate Destructor Returns: Nothing Side effects: None */ CTemplate::~CTemplate() { DBGPRINTF(( DBG_CONTEXT, "Deleting template, m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps)); // first, remove this template from its inc-files' template lists // NOTE must do this before freeing template memory RemoveFromIncFiles(); // Remove the template from the debugger's list of documents Detach(); PersistCleanup(); if(m_rgpFilemaps) { for(UINT i = 0; i < m_cFilemaps; i++) delete m_rgpFilemaps[i]; SmallTemplateFreeNullify((void**) &m_rgpFilemaps); } FreeGoodTemplateMemory(); if (m_pWorkStore) delete m_pWorkStore; //FileName, LineNum, Engine, ErrorCode, ShortDes, LongDes for(UINT iErrInfo = 0; iErrInfo < ILE_MAX; iErrInfo++) { FreeNullify((void**) &m_pszLastErrorInfo[iErrInfo]); } if(m_hEventReadyForUse != NULL) CloseHandle(m_hEventReadyForUse); if (m_LKHashKey.szPathTranslated) free((void *)m_LKHashKey.szPathTranslated); if (m_szApplnURL) delete [] m_szApplnURL; if (m_fDebuggerDetachCSInited) DeleteCriticalSection(&m_csDebuggerDetach); if (m_fTemplateLockInited) DeleteCriticalSection(&m_csTemplateLock); if (m_pdispTypeLibWrapper) m_pdispTypeLibWrapper->Release(); if (m_szPersistTempName) CTemplate::LargeFree(m_szPersistTempName); if (m_pMostRecentImpersonatedTokenUser) CTemplate::SmallFree(m_pMostRecentImpersonatedTokenUser); if (m_pServicesConfig) m_pServicesConfig->Release(); m_pServicesConfig = NULL; #if PER_TEMPLATE_REFLOG DestroyRefTraceLog (m_pTraceLog); #endif } /* ============================================================================ CTemplate::QueryInterface Provides QueryInterface implementation for CTemplate NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo. */ HRESULT CTemplate::QueryInterface(const GUID &uidInterface, void **ppvObj) { if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider) *ppvObj = static_cast(this); else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText) *ppvObj = static_cast(this); else if (uidInterface == IID_IConnectionPointContainer) *ppvObj = static_cast(this); else *ppvObj = NULL; if (*ppvObj) { AddRef(); return S_OK; } else return E_NOINTERFACE; } /* ============================================================================ CTemplate::AddRef Adds a ref to this template, thread-safely */ ULONG CTemplate::AddRef() { LONG cRefs = InterlockedIncrement(&m_cRefs); Assert(FImplies(m_fIsValid,FImplies(cRefs > 1, m_pbStart != NULL))); IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, cRefs, this); } #if PER_TEMPLATE_REFLOG WriteRefTraceLog(m_pTraceLog, cRefs, this); #endif return cRefs; } /* ============================================================================ CTemplate::Release Releases a ref to this template, thread-safely */ ULONG CTemplate::Release() { LONG cRefs = InterlockedDecrement(&m_cRefs); IF_DEBUG(TEMPLATE) { WriteRefTraceLog(gm_pTraceLog, cRefs, this); } #if PER_TEMPLATE_REFLOG WriteRefTraceLog(m_pTraceLog, cRefs, this); #endif if (cRefs == 0) delete this; return cRefs; } /* ============================================================================ CTemplate::InitClass Initilaizes CTemplate static members Returns: hresult Side effects: allocates memory for static members */ HRESULT CTemplate::InitClass ( ) { HRESULT hr = S_OK; TRY // init heaps sm_hSmallHeap = ::HeapCreate(0, 0, 0); if (!sm_hSmallHeap) return E_OUTOFMEMORY; sm_hLargeHeap = ::HeapCreate(0, 0, 0); if (!sm_hLargeHeap) return E_OUTOFMEMORY; // Init token list gm_pTokenList = new CTokenList; if (gm_pTokenList == NULL) return E_OUTOFMEMORY; gm_pTokenList->Init(); CATCH(hrException) hr = hrException; END_TRY return hr; } /* ============================================================================ CTemplate::UnInitClass Un-initilaizes CTemplate static members Returns: Nothing Side effects: None */ void CTemplate::UnInitClass() { if (gm_pTokenList) { delete gm_pTokenList; gm_pTokenList = NULL; } if (sm_hLargeHeap) ::HeapDestroy(sm_hLargeHeap); if (sm_hLargeHeap != sm_hSmallHeap) { if (sm_hSmallHeap) ::HeapDestroy(sm_hSmallHeap); } sm_hLargeHeap = sm_hSmallHeap = NULL; } /* ============================================================================ CTemplate::Init Inits template in preparation for calling Compile Does the minimum needed Returns: Success or failure code Side effects: Allocates memory */ HRESULT CTemplate::Init ( CHitObj *pHitObj, // ptr to template's hit object BOOL fGlobalAsa, // is this the global.asa file? const CTemplateKey &rTemplateKey // hash table key ) { HRESULT hr; // Create debug critical section ErrInitCriticalSection(&m_csDebuggerDetach, hr); if (SUCCEEDED(hr)) { ErrInitCriticalSection(&m_csTemplateLock,hr); if (FAILED(hr)) { DeleteCriticalSection(&m_csDebuggerDetach); return hr; } // note critical section creation success m_fDebuggerDetachCSInited = TRUE; m_fTemplateLockInited = TRUE; } else //FAILED(hr) return hr; // Create event: manual-reset, ready-for-use event; non-signaled m_hEventReadyForUse = IIS_CREATE_EVENT( "CTemplate::m_hEventReadyForUse", this, TRUE, // flag for manual-reset event FALSE // flag for initial state ); if (!m_hEventReadyForUse) return E_OUTOFMEMORY; // cache GlobalAsp flag m_fGlobalAsa = BOOLB(fGlobalAsa); // CIsapiReqInfo better be present if (pHitObj->PIReq() == NULL) return E_POINTER; // Initialize the template's code page m_wCodePage = pHitObj->PAppln()->QueryAppConfig()->uCodePage(); m_lLCID = pHitObj->PAppln()->QueryAppConfig()->uLCID(); STACK_BUFFER( serverNameBuff, 32 ); STACK_BUFFER( serverPortBuff, 10 ); STACK_BUFFER( portSecureBuff, 8 ); DWORD cbServerName; DWORD cbServerPort; DWORD cbServerPortSecure; // Construct a URL for the application // Get the server name and port if (!SERVER_GET(pHitObj->PIReq(), "SERVER_NAME", &serverNameBuff, &cbServerName) || !SERVER_GET(pHitObj->PIReq(), "SERVER_PORT", &serverPortBuff, &cbServerPort)) { if (GetLastError() == ERROR_OUTOFMEMORY) { hr = E_OUTOFMEMORY; } else { hr = E_FAIL; } return hr; } char *szServerPort = (char *)serverPortBuff.QueryPtr(); char *szServerName = (char *)serverNameBuff.QueryPtr(); BOOL fServerPortSecure = FALSE; // determine if server port is secure if (SERVER_GET(pHitObj->PIReq(), "SERVER_PORT_SECURE", &portSecureBuff, &cbServerPortSecure)) { char *szServerPortSecure = (char *)portSecureBuff.QueryPtr(); fServerPortSecure = (szServerPortSecure[0] == '1'); } // Get the application virtual path TCHAR szApplnVirtPath[256]; if (FAILED(hr = FindApplicationPath(pHitObj->PIReq(), szApplnVirtPath, sizeof szApplnVirtPath))) return hr; TCHAR *szServerNameT; TCHAR *szServerPortT; #if UNICODE CMBCSToWChar convServer; if (FAILED(hr = convServer.Init(szServerName))) { return hr; } szServerNameT = convServer.GetString(); #else szServerNameT = szServerName; #endif #if UNICODE CMBCSToWChar convPort; if (FAILED(hr = convPort.Init(szServerPort))) { return hr; } szServerPortT = convPort.GetString(); #else szServerPortT = szServerPort; #endif // Allocate space for and construct the application URL m_szApplnURL = new TCHAR [(9 /* sizeof "https://:" */ + _tcslen(szServerNameT) + _tcslen(szServerPortT) + _tcslen(szApplnVirtPath) + 1)]; if (m_szApplnURL == NULL) return E_OUTOFMEMORY; TCHAR *pT; // start with the protocol prefix... pT = strcpyEx(m_szApplnURL, fServerPortSecure? _T("https://") : _T("http://")); // next add the servername pT = strcpyEx(pT, szServerNameT); // next the colon between the servername and the serverport pT = strcpyEx(pT, _T(":")); // next the server port pT = strcpyEx(pT, szServerPortT); // now the applURL is built up to the appln path. The next step will be to // add the virtpath. m_szApplnVirtPath = pT; _tcscpy(m_szApplnVirtPath, szApplnVirtPath); m_LKHashKey.dwInstanceID = rTemplateKey.dwInstanceID; if ((m_LKHashKey.szPathTranslated = StringDup((TCHAR *)rTemplateKey.szPathTranslated)) == NULL) return E_OUTOFMEMORY; return S_OK; } /* ============================================================================ CTemplate::Compile Compiles the template from its source file and include files, if any, by calling GetSegmentsFromFile (to populate WorkStore), followed by WriteTemplate (to create the template from WorkStore). Returns: HRESULT indicating success or type of failure Side effects: Indirectly allocates memory (via WriteTemplate) Indirectly frees memory on error (via FreeGoodTemplateMemory) */ HRESULT CTemplate::Compile ( CHitObj* pHitObj ) { HRESULT hr = S_OK; HRESULT hRes = S_OK; // The following code moved from Init() (to make Init() lighter) Assert(pHitObj); // Create and Init WorkStore if (SUCCEEDED(hr)) { // construct the workstore - bail on fail if(NULL == (m_pWorkStore = new CWorkStore)) hr = E_OUTOFMEMORY; } if (SUCCEEDED(hr)) { hr = (m_pWorkStore->m_ScriptStore).Init(pHitObj->QueryAppConfig()->szScriptLanguage(), pHitObj->QueryAppConfig()->pCLSIDDefaultEngine()); if (hr == TYPE_E_ELEMENTNOTFOUND) { // default script language in registry is bogus - send error msg to browser HandleCTemplateError( NULL, // source file map NULL, // ptr to source location where error occurred IDE_TEMPLATE_BAD_PROGLANG_IN_REGISTRY, // error message id 0, // count of insert strings for error msg NULL, // array of ptrs to error msg insert strings pHitObj // Browser Request ); } if (FAILED(hr)) { delete m_pWorkStore; m_pWorkStore = NULL; } } // Try to init the workstore and map main file - this can fail with oom, etc or user lacks permissions if (SUCCEEDED(hr)) { TRY m_pWorkStore->Init(); AppendMapFile( NULL, // file spec for this file - NULL means get filespec from pHitObj NULL, // ptr to filemap of parent file FALSE, // don't care pHitObj, // ptr to template's hit object m_fGlobalAsa // is this the global.asa file? ); CATCH(hrException) delete m_pWorkStore; m_pWorkStore = NULL; hr = hrException; if(hr == E_USER_LACKS_PERMISSIONS) HandleAccessFailure(pHitObj, (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL); if ((hr != E_COULDNT_OPEN_SOURCE_FILE) && m_rgpFilemaps && m_rgpFilemaps[0]) { // empty file will fail to map but will have a handle, so we check for it here if (0 == m_rgpFilemaps[0]->GetSize()) hr = E_SOURCE_FILE_IS_EMPTY; m_rgpFilemaps[0]->UnmapFile(); } if (SUCCEEDED(hr)) hr = E_FAIL; // make sure the error is set END_TRY } if (SUCCEEDED(hr)) { Assert(m_rgpFilemaps[0]); Assert(m_rgpFilemaps[0]->m_szPathTranslated); Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath())))); Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath())))); Assert(0 < m_rgpFilemaps[0]->GetSize()); } // Record the user who compiled this file (used specifically in UNC). Cannot proceed if there was an error (typically OOM) or Access problem. if (FAILED(hr) || FAILED (hRes = GetSIDFromTokenHandle(pHitObj->HImpersonate(), m_pMostRecentImpersonatedSID, m_pMostRecentImpersonatedTokenUser, &m_cbTokenUser) ) ) { m_fDontCache = TRUE; // OK, cache HR if m_fDontCache is true // later, another thread might find this template from the cache even if the template // has some error and marked as DontCache. m_hrOnNoCache = FAILED(hr) ? hr : hRes; m_fReadyForUse = TRUE; SetEvent(m_hEventReadyForUse); return hr; } // End of Code moved from Init() // By default we are not in a transaction m_ttTransacted = ttUndefined; // By default session is required m_fSession = TRUE; // By default assume script exists m_fScriptless = FALSE; // we assert, in effect, that template is already init'ed Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath())))); Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath())))); TRY // Get source segments from source file GetSegmentsFromFile(*(m_rgpFilemaps[0]), *m_pWorkStore, pHitObj); /* get "language equivalents" for primary languagefrom registry NOTE we do this here because the user can reset the primary language in the script file, so we must wait until after GetSegmentsFromFile() */ GetLanguageEquivalents(); // Call WriteTemplate, which writes out template components to contiguous memory, // resulting in a compiled template // CWriteTemplate...class does the processing and is freed when the Compile completes. // However, it leaves the compiled template in the heap pointed to by m_pbStart (sideeffect). CWriteTemplate writeTempl; // Initialize the WriteTemplate object writeTempl.Init (m_pWorkStore, this, pHitObj->PAppln()->QueryAppConfig()->fCalcLineNumber()); // Perform estimation and write template writeTempl.WriteTemplate(); // Record Compilation Cache Tag. m_dwCacheTag = g_TemplateCache.GetCacheTag(); m_dwLastAccessCheck = GetTickCount(); // // If any of the Include files are on a UNC or are encrypted then reflect that in the template structure // for (unsigned i = 0; i < m_cFilemaps; ++i) { m_fIsUNC |= m_rgpFilemaps[i]->m_fIsUNCPath; m_fIsEncrypted |= m_rgpFilemaps[i]->m_fIsEncryptedFile; m_rgpFilemaps[i]->CountChars((WORD)m_wCodePage); } // Wrap typelibs into single IDispatch* WrapTypeLibs(pHitObj); m_fIsValid = TRUE; CATCH(hrException) // NOTE: we used to free template memory here. Now we do not because if the // error was E_USER_LACKS_PERMISSIONS, and template is in cache, we don't want // to sabotage future requests. There's no need to decache the template. // // The template destructor will free this memory anyway. // hr = hrException; END_TRY // check if scriptless if (!m_fGlobalAsa) { // count various stuff to make the determination DWORD cScriptEngines = m_pWorkStore->m_ScriptStore.CountPreliminaryEngines(); DWORD cPrimaryScriptSegments = (cScriptEngines > 0) ? m_pWorkStore->m_ScriptStore.m_ppbufSegments[0]->Count() : 0; DWORD cObjectTags = m_pWorkStore->m_ObjectInfoStore.Count(); DWORD cHtmlSegments = m_pWorkStore->m_bufHTMLSegments.Count(); DWORD c449Cookies = m_rgp449.length(); BOOL fPageCommandsPresent = m_pWorkStore->m_fPageCommandsExecuted; if (cScriptEngines <= 1 && cPrimaryScriptSegments == 0 && cObjectTags == 0 && cHtmlSegments == 1 && c449Cookies == 0 && !fPageCommandsPresent) { m_fScriptless = TRUE; } } // free working storage - no longer needed delete m_pWorkStore; m_pWorkStore = NULL; // un-map filemaps - NOTE filemaps stay around for possible post-compile errors (e.g., script failure) UnmapFiles(); // Debugging: print data structure to debugger IF_DEBUG(SCRIPT_DEBUGGER) { if (SUCCEEDED(hr)) { DBGPRINTF((DBG_CONTEXT, "Script Compiled\n")); for (UINT i = 0; i < m_cScriptEngines; ++i) { char *szEngineName; PROGLANG_ID *pProgLangID; const wchar_t *wszScriptText; GetScriptBlock(i, &szEngineName, &pProgLangID, &wszScriptText); DBGPRINTF((DBG_CONTEXT, "Engine %d, Language=\"%s\":\n", i, szEngineName)); DBGINFO((DBG_CONTEXT, (char *) wszScriptText)); DBGINFO((DBG_CONTEXT, "\n")); } } } if (hr == E_TEMPLATE_COMPILE_FAILED_DONT_CACHE) { m_fDontCache = TRUE; m_hrOnNoCache = hr; } // Set ready-for-use flag true and event to signaled // NOTE we do this whether success or failure, since even a failed-compile template // will remain in the cache to allow template cache mgr to satisfy requests on it m_fReadyForUse = TRUE; SetEvent(m_hEventReadyForUse); // Note whether the template currently is debuggable // BUG BUG: Template is debuggable or not based on first app. If shared between a debug // & non-debug app, the first application wins. m_fDebuggable = (BOOLB)!!pHitObj->PAppln()->FDebuggable(); return hr; } /* ============================================================================ CTemplate::Deliver Delivers template to caller once template is ready for use NOTE 'compile failure' == template is 'ready for use' but did not compile successfully; this allows cache mgr to keep a failed template in cache in case it gets requested again Returns success or failure Side effects none */ HRESULT CTemplate::Deliver ( CHitObj* pHitObj ) { // NOTE: There was a compiler bug where 'ps' would not be correctly aligned, // EVEN if it was declared to be a DWORD array, if 'ps' was nested in // a block. Thus declare it here. // BYTE ps[SIZE_PRIVILEGE_SET]; // privilege set HRESULT hr = S_OK; BOOL fImpersonatedUser = FALSE; HANDLE hVirtIncImpToken = NULL; HANDLE hCurImpToken = NULL; // if ready flag is not yet set block until template is ready for use if(!m_fReadyForUse) { WaitForSingleObject(m_hEventReadyForUse, INFINITE); Assert(m_fReadyForUse); // when event unblocks, flag will be set } if (m_pbStart == NULL) { if (m_fDontCache && m_dwLastErrorMask == 0) { DBGPRINTF((DBG_CONTEXT, "template compile failed with %08x\n", m_hrOnNoCache)); DBG_ASSERT(FAILED(m_hrOnNoCache)); // Safety net: always fail, even if "m_hrOnNoCache" did not get set somehow. hr = m_hrOnNoCache; if (SUCCEEDED(m_hrOnNoCache)) hr = E_FAIL; if(hr == E_USER_LACKS_PERMISSIONS) HandleAccessFailure(pHitObj, (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL); return hr; } // template compile failed - NOTE null start-of-template ptr == template compile failed // use cached error info SendToLog( m_dwLastErrorMask, m_pszLastErrorInfo[ILE_szFileName], m_pszLastErrorInfo[ILE_szLineNum], m_pszLastErrorInfo[ILE_szEngine], m_pszLastErrorInfo[ILE_szErrorCode], m_pszLastErrorInfo[ILE_szShortDes], m_pszLastErrorInfo[ILE_szLongDes], pHitObj); hr = E_TEMPLATE_COMPILE_FAILED; } else if (!pHitObj->FIsBrowserRequest()) { return hr; } else // template compile succeeded - check user's file permissions // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project) { HANDLE hUserAccessToken = pHitObj->HImpersonate(); // current user's access token DWORD dwPS = sizeof(ps); // privilege set size DWORD dwGrantedAccess; // granted access mask BOOL fAccessGranted; // access granted flag GENERIC_MAPPING gm = { // generic mapping struct FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; ((PRIVILEGE_SET*)ps)->PrivilegeCount = 0; // set privilege count to 0 Assert(NULL != hUserAccessToken); HRESULT HRes = E_FAIL; BOOL fNeedsUpdate = FALSE; PSID pPrevSid = NULL; PSID pSid=NULL; DWORD cbTempTokenBuffer = 0; LPVOID pvTempTokenBuffer = NULL; DWORD dwAccessCheckTimeStamp = 0; // // Obtain a lock on the template so that only one Deliver will update the credentials while the others will just fall through // Only one process should write the timestamp/impersonation token. // If the File is either UNC or is Encrypted then we need to go through all the access check doors // if (m_fIsUNC) { EnterCriticalSection(&m_csTemplateLock); pPrevSid = m_pMostRecentImpersonatedSID; dwAccessCheckTimeStamp = m_dwLastAccessCheck; LeaveCriticalSection(&m_csTemplateLock); // GetCurrent Users Impersonation Token HRes = GetSIDFromTokenHandle(pHitObj->HImpersonate(), pSid,pvTempTokenBuffer, &cbTempTokenBuffer); } for(UINT i = 0; i < m_cFilemaps; i++) { if (!(m_rgpFilemaps[i]->FHasUNCPath())) { if(NULL == m_rgpFilemaps[i]->m_pSecurityDescriptor) //? Dunno why we should continue. This could be a security Issue? continue; if(!AccessCheck( m_rgpFilemaps[i]->m_pSecurityDescriptor, // pointer to security descriptor hUserAccessToken, // handle to client access token FILE_GENERIC_READ, // access mask to request &gm, // address of generic-mapping structure (PRIVILEGE_SET*)ps, // address of privilege-set structure &dwPS, // address of size of privilege-set structure &dwGrantedAccess, // address of granted access mask &fAccessGranted // address of flag indicating whether access granted )) return E_FAIL; if(!fAccessGranted) { // if access is denied on any file, handle the failure and return HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated); return E_USER_LACKS_PERMISSIONS; } } else //if it is a UNC { // // Get the SID for current thread and compare with compiled/lastAccessed sid. // If the SIDs match and we are within the TTL, skip the test. // // If either of the conditions are not met, go onto remote UNC box to refresh credentials. // if( !pPrevSid // could not store the previous SID || (FAILED (HRes)) // could not obtain SID || !EqualSid(pPrevSid, pSid) // different users || (EqualSid(pPrevSid, pSid ) && CheckTTLTimingWindow(dwAccessCheckTimeStamp, g_dwFileMonitoringTimeoutSecs)) // same user but outside TTL ) { // // Perform impersonation on the target user // fImpersonatedUser = FALSE; hVirtIncImpToken = NULL; hCurImpToken = NULL; // TODO: Could be a PERF bottleneck, if we are going to the same UNC. // TODO: Try to Somehow store the previous Impersonation token and if they are the same then dont get the token again. // TODO: Beware...this could be messy. // // We must Impersonate LoggedOnUser only in the UNC case. // In the Encrypted case the current threads credentials should be able to open the file. // if (SUCCEEDED(pHitObj->PIReq()->GetVirtualPathToken(m_rgpFilemaps[i]->m_szPathInfo, &hVirtIncImpToken))) { // set the impersonation token and note that we did so // NOTE - there is intentionally no error checking. The // assumption being that we are doing best effort at the // impersonation because throwing an error here could be // tricky for the user to interpret the problem. However, // if the impersonation fails, and ASP can still open the // file (e.g. passthru authentication), then everyone's // happy. AspDoRevertHack(&hCurImpToken); fImpersonatedUser = ImpersonateLoggedOnUser(hVirtIncImpToken); if (!fImpersonatedUser) { AspUndoRevertHack(&hCurImpToken); } } // // Check existence of file...go thru all doors // hr = AspGetFileAttributes (m_rgpFilemaps[i]->m_szPathTranslated); // // Undo impersonation if any. // if (fImpersonatedUser) AspUndoRevertHack(&hCurImpToken); if (hVirtIncImpToken) CloseHandle(hVirtIncImpToken); if (FAILED(hr)) { // Too bad...(actually maybe good) the user does not have access to the share. // if access is denied on any file, handle the failure and return HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated); return E_USER_LACKS_PERMISSIONS; } fNeedsUpdate = TRUE; } } } if (fNeedsUpdate) // This flag being set also means that there was a UNC path or encrypted file in the main/include files. Thus the first crit sec was entered { EnterCriticalSection (&m_csTemplateLock); m_pMostRecentImpersonatedTokenUser = pvTempTokenBuffer; m_pMostRecentImpersonatedSID = pSid; m_cbTokenUser = cbTempTokenBuffer; m_dwLastAccessCheck = GetTickCount(); LeaveCriticalSection (&m_csTemplateLock); } } // Reset the Session.CodePage to the script compilation-time codepage // only if a code page directive was found during compilation if (m_fCodePageSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet())) { pHitObj->SetCodePage(m_wCodePage); } // Reset the Session.LCID to the script compilation-time LCID // only if an LCID directive was found during compilation if (m_fLCIDSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FLCIDSet())) { pHitObj->SetLCID(m_lLCID); } return hr; } /* ============================================================================ CTemplate::GetSIDFromTokenHandle Takes a token handle and gets the User SID information corresponding to that token. Returns HResult Side effects none */ HRESULT CTemplate::GetSIDFromTokenHandle (HANDLE tokenHandle, PSID pSid, LPVOID pBuffer, DWORD *pcbSize) { HRESULT hr = S_OK; BOOL bRet; DWORD cbTokenUserBuffer = 0; LPVOID pvTokenUserBuffer = NULL; // Get buffer size bRet = GetTokenInformation(tokenHandle, TokenUser, NULL, 0, &cbTokenUserBuffer ); // According to MSDN this call will fail. But we will have a valid value in cbTokenUserBuffer // Allocate the space and redo the call pvTokenUserBuffer = (BYTE*) CTemplate::SmallMalloc(cbTokenUserBuffer ); if (!pvTokenUserBuffer) return E_OUTOFMEMORY; // Get TokenUser bRet = GetTokenInformation(tokenHandle, TokenUser, pvTokenUserBuffer, cbTokenUserBuffer, &cbTokenUserBuffer ); if (!bRet) { if (pvTokenUserBuffer) CTemplate::SmallFree(pvTokenUserBuffer); return HRESULT_FROM_WIN32(GetLastError()); } //Free previously allocated TokenUser buffer if (pBuffer) CTemplate::SmallFree(pBuffer); // Write the values to callers buffer pBuffer = pvTokenUserBuffer; pSid = ((TOKEN_USER *)pvTokenUserBuffer)->User.Sid; *pcbSize = cbTokenUserBuffer; return hr; } /* ============================================================================ CTemplate::RemoveIncFile Removes (by setting to null) an inc-file ptr from this template's inc-file list. Returns: Nothing Side effects: None */ void CTemplate::RemoveIncFile ( CIncFile* pIncFile ) { // If the filemap count is non-zero the pointer to // the array of filemaps has better not be null DBGPRINTF(( DBG_CONTEXT, "m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps)); Assert((m_cFilemaps <= 0) || (m_rgpFilemaps != NULL)); // find the inc-file in list for(UINT i = 1; (i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile != pIncFile); i++) ; // assert that we found the inc-file in list Assert((i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile == pIncFile)); // set inc-file ptr null m_rgpFilemaps[i]->m_pIncFile = NULL; } /*=================================================================== CTemplate::FTemplateObsolete Test to see if the files this template depends on have changed since it was compiled. We use this in cases where we may have missed a change notification, for example, when there were too many changes to record in our change notification buffer. We check the last time the file was written too, and the security descriptor, since changes to the security descriptor aren't noted in the file last write time. Parameters: None Returns: TRUE if the template is obsolete, else FALSE */ BOOL CTemplate::FTemplateObsolete(VOID) { BOOL fStatus = FALSE; for (UINT i = 0; i < m_cFilemaps; i++) { if (FFileChangedSinceCached(m_rgpFilemaps[i]->m_szPathTranslated, m_rgpFilemaps[i]->m_hFile, m_rgpFilemaps[i]->m_ftLastWriteTime)) { // If the file write time has changed we know enough // and can quit here fStatus = TRUE; break; } else { // The file hasn't been writen to, but the security descriptor may // have chagned // Assert on non-valid security descriptor if (NULL != m_rgpFilemaps[i]->m_pSecurityDescriptor) { PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL; DWORD dwSize = m_rgpFilemaps[i]->m_dwSecDescSize; if( 0 == GetSecDescriptor(m_rgpFilemaps[i]->m_szPathTranslated, &pSecurityDescriptor, &dwSize)) { if (pSecurityDescriptor) { // if the size is not the same then set fStatus to TRUE no need to compare memory blocks. if(dwSize != GetSecurityDescriptorLength(m_rgpFilemaps[i]->m_pSecurityDescriptor)) { fStatus = TRUE; } else { // The size of the security descriptor hasn't changed // but we have to compare the contents to make sure they haven't changed fStatus = !(0 == memcmp(m_rgpFilemaps[i]->m_pSecurityDescriptor, pSecurityDescriptor, dwSize)); } // We are done with the descriptor free(pSecurityDescriptor); } else { // Since we failed to get a security descriptor // assume the file has changed. fStatus = TRUE; } } } } // Quit as soon as we find a change if (fStatus) { break; } } return fStatus; } /* ============================================================================ CTemplate::GetSourceFileName Returns name of source file on which this template is based Returns source file name Side effects none */ LPTSTR CTemplate::GetSourceFileName(SOURCEPATHTYPE pathtype) { if (!m_rgpFilemaps) { return NULL; } switch (pathtype) { case SOURCEPATHTYPE_PHYSICAL: return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathTranslated : NULL)); case SOURCEPATHTYPE_VIRTUAL: return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathInfo : NULL)); default: return(NULL); } } /* ============================================================================ CTemplate::Count Returns count of components of type tcomp contained in this template Returns: Count of components of type tcomp Side effects: None */ USHORT CTemplate::Count ( TEMPLATE_COMPONENT tcomp ) { Assert(NULL != m_pbStart); // script engines and script blocks have the same count, stored in same slot if(tcomp == tcompScriptEngine) tcomp = tcompScriptBlock; // counts are stored at start of template in sequential slots, starting with script blocks count return * (USHORT*) ((USHORT*)m_pbStart + (tcomp - tcompScriptBlock)); } /* ============================================================================ CTemplate::GetScriptBlock Gets ptrs to script engine name, prog lang id and script text of i-th script block. Returns: Out-parameters; see below Side effects: None */ void CTemplate::GetScriptBlock ( UINT i, // script block id LPSTR* pszScriptEngine, // ptr to script engine name (out-parameter) PROGLANG_ID** ppProgLangId, // ptr to prog lang id (out-parameter) LPCOLESTR* pwstrScriptText // ptr to wstr script text (out-parameter) ) { CByteRange brEngine; // engine name CByteRange brScriptText; // script text UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned BYTE* pbEngineInfo = GetAddress(tcompScriptEngine, (USHORT)i); // ptr to engine info Assert(pbEngineInfo != NULL); Assert(i < CountScriptEngines()); // Get engine name from start of engine info ByteRangeFromPb(pbEngineInfo, brEngine); ByteRangeFromPb(GetAddress(tcompScriptBlock, (USHORT)i), brScriptText); Assert(!brEngine.IsNull()); Assert(!brScriptText.IsNull()); // Advance ptr past name to prog lang id // length of prefix + length of name + NULL pbEngineInfo += (sizeof(UINT) + (*pbEngineInfo) + 1); // Get prog lang id - it will be on the next pointer sized boundary cbAlignment = (UINT) (((DWORD_PTR) pbEngineInfo) % sizeof(DWORD)); if(cbAlignment > 0) {pbEngineInfo += (sizeof(DWORD) - cbAlignment);} *pszScriptEngine = (LPSTR)brEngine.m_pb; *ppProgLangId = (PROGLANG_ID*)pbEngineInfo; *pwstrScriptText = (LPCOLESTR)brScriptText.m_pb; } /* ============================================================================ CTemplate::GetObjectInfo Returns i-th object-info in template as object name and its clsid, scope, model Returns: HRESULT Out-parameters; see below Side effects: */ HRESULT CTemplate::GetObjectInfo ( UINT i, // object index LPSTR* ppszObjectName, // address of object name ptr (out-parameter) CLSID* pClsid, // address of object clsid CompScope* pcsScope, // address of object scope CompModel* pcmModel // address of object threading model ) { BYTE* pbObjectInfo = GetAddress(tcompObjectInfo, (USHORT)i); // ptr to current read location CByteRange brName; // object name UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned Assert(i < Count(tcompObjectInfo)); // Get name from start of object-info ByteRangeFromPb(pbObjectInfo, brName); Assert(!brName.IsNull()); // Advance ptr past name // length of prefix + length of name + NULL pbObjectInfo += (sizeof(UINT) + (*pbObjectInfo) + 1); // Get clsid - it will be on the next DWORD boundary cbAlignment = (UINT)(((DWORD_PTR) pbObjectInfo) % sizeof(DWORD)); if(cbAlignment > 0) pbObjectInfo += (sizeof(DWORD) - cbAlignment); *pClsid = *(CLSID*)pbObjectInfo; pbObjectInfo += sizeof(CLSID); // Get scope *pcsScope = *(CompScope*)pbObjectInfo; pbObjectInfo += sizeof(CompScope); // Get model *pcmModel = *(CompModel*)pbObjectInfo; pbObjectInfo += sizeof(CompModel); *ppszObjectName = (LPSTR)brName.m_pb; return S_OK; } /* ============================================================================ CTemplate::GetHTMLBlock Returns i-th HTML block Parameters: UINT i block number LPSTR* pszHTML [out] html text ULONG* pcbHTML [out] html text length ULONG* pcbSrcOffs [out] offset in the source file LPSTR* pszSrcIncFile [out] include source file name Returns: Nothing Side effects: None */ HRESULT CTemplate::GetHTMLBlock ( UINT i, LPSTR* pszHTML, ULONG* pcbHTML, ULONG* pcbSrcOffs, LPSTR* pszSrcIncFile ) { Assert(i < Count(tcompHTMLBlock)); // this was added due to user attempt to access the method with an invalid array offset // if ( i >= Count(tcompHTMLBlock) ) return E_FAIL; // get address of the block start in template memory BYTE *pbBlock = GetAddress(tcompHTMLBlock, (USHORT)i); Assert(pbBlock); // retrieve the byte range of the html code CByteRange brHTML; ByteRangeFromPb(pbBlock, brHTML); *pszHTML = (LPSTR)brHTML.m_pb; *pcbHTML = brHTML.m_cb; // advance to the source offset pbBlock += sizeof(ULONG); // skip prefix pbBlock += brHTML.m_cb+1; // skip html bytes (incl. '\0') // Add byte aligment which is done in ByteAlignOffset() if ((reinterpret_cast(pbBlock)) & 3) pbBlock = reinterpret_cast((reinterpret_cast(pbBlock) + 4) & ~3); *pcbSrcOffs = *((ULONG*)pbBlock); // advance to the source name length pbBlock += sizeof(ULONG); // skip source offset prefix ULONG cbSrcIncFile = *((ULONG *)pbBlock); // inc file name length pbBlock += sizeof(ULONG); // skip inc file name length *pszSrcIncFile = (cbSrcIncFile > 0) ? (LPSTR)pbBlock : NULL; return S_OK; } /* ============================================================================ CTemplate::GetScriptSourceInfo Returns line number and source file name a given target line in a given script engine. Returns line number and source file name (as out-parameters) Side effects: None */ void CTemplate::GetScriptSourceInfo ( UINT idEngine, // script engine id int iTargetLine, // target line number LPTSTR* pszPathInfo, // ptr to source file virtual path (out-parameter) LPTSTR* pszPathTranslated, // ptr to source file real path (out-parameter) ULONG* piSourceLine, // ptr to source line number (out-parameter) ULONG* pichSourceLine, // ptr to source file offset (out-parameter) BOOLB* pfGuessedLine // ptr to flag: did we guess the source line? ) { // Initialize some out parameters if (pszPathInfo) *pszPathInfo = _T("?"); // In case we don't ever find the path if (pszPathTranslated) *pszPathTranslated = _T("?"); // In case we don't ever find the path if (piSourceLine) *piSourceLine = 0; if (pichSourceLine) *pichSourceLine = 0; if (pfGuessedLine) *pfGuessedLine = FALSE; if (iTargetLine <=0) { return; } // CHANGE: The rgSourceInfo array is now ZERO based. Decrement target line // to convert. --iTargetLine; // CONSIDER: Make these assertions? if(!m_rgrgSourceInfos) return; if(idEngine > (m_cScriptEngines - 1)) // bug 375: check vs. array bound return; if(size_t(iTargetLine) >= m_rgrgSourceInfos[idEngine].length()) // bug 375: check vs. array bound return; vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // bug 379: move backwards through target lines, starting with the caller's, until we find one whose // fIsHTML flag is false. this handles the case where vbs flags a manufactured line as in error; // we assume the actual error occurred at the most recent authored line while (iTargetLine >= 0 && (*prgSourceInfos)[iTargetLine].m_fIsHTML) { --iTargetLine; if (pfGuessedLine) *pfGuessedLine = TRUE; } if (iTargetLine >= 0) { if (pszPathInfo && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL) *pszPathInfo = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathInfo; if (pszPathTranslated && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL) *pszPathTranslated = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathTranslated; if (piSourceLine) *piSourceLine = (*prgSourceInfos)[iTargetLine].m_idLine; if (pichSourceLine) *pichSourceLine = (*prgSourceInfos)[iTargetLine].m_cchSourceOffset; } } /* ============================================================================ CTemplate::GetPositionOfLine Get the character offset of a line of source (Debugger API Extended to specify a filemap) */ HRESULT CTemplate::GetPositionOfLine ( CFileMap *pFilemap, ULONG cLineNumber, ULONG *pcCharacterPosition ) { // NOTE: // The table is not binary-searchable because include files // will start a new line ordering // // Algorithm: // // Find the largest source line N across all engines, such that // N <= cLineNumber and the line corresponds to an line // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; ++cLineNumber; // Convert zero-based line # to one-based // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (pFilemap == pSourceInfo->m_pfilemap && pSourceInfo->m_idLine <= cLineNumber && (pSourceInfoLE == NULL || pSourceInfo->m_idLine > pSourceInfoLE->m_idLine)) { pSourceInfoLE = pSourceInfo; } } } // We had better be able to map all line numbers to offsets, unless they passed a bogus line // (in which case we still find an offset) // Assert (pSourceInfoLE != NULL); if (pSourceInfoLE == NULL) { return E_FAIL; } *pcCharacterPosition = pSourceInfoLE->m_cchSourceOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap, 0, 0, wszSourceText, NULL ); DBGPRINTF(( DBG_CONTEXT, "Source Line %d corresponds to source offset %d (Text: \"%S\")\n", cLineNumber - 1, pSourceInfoLE->m_cchSourceOffset, wszSourceText )); } #endif return S_OK; } /* ============================================================================ CTemplate::GetLineOfPosition Get the line # & offset in line of an arbitrary character offset in source (Debugger API Extended to specify a filemap) */ HRESULT CTemplate::GetLineOfPosition ( CFileMap *pFilemap, ULONG cCharacterPosition, ULONG *pcLineNumber, ULONG *pcCharacterOffsetInLine ) { // FAIL if source offset totally off-base if (cCharacterPosition >= pFilemap->m_cChars) return E_FAIL; // NOTE: // The table is not binary-searchable because include files // will start a new line ordering // // Algorithm: // // Find the largest source line N across all engines, such that // N <= cLineNumber and the line corresponds to an line // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (pFilemap == pSourceInfo->m_pfilemap && pSourceInfo->m_cchSourceOffset <= cCharacterPosition && (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset)) { pSourceInfoLE = pSourceInfo; } } } // We had better be able to map all offsets to line numbers, unless they passed a bogus offset // (in which case we still find a line #, but may go out of range for the offset in line. // That case is handled later) // Assert (pSourceInfoLE != NULL); if (pSourceInfoLE == NULL) { return E_FAIL; } *pcLineNumber = pSourceInfoLE->m_idLine - 1; // Convert to zero-based line # *pcCharacterOffsetInLine = cCharacterPosition - pSourceInfoLE->m_cchSourceOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap, 0, 0, wszSourceText, NULL ); DBGPRINTF(( DBG_CONTEXT, "Source offset %d corresponds to source line %d (Text: \"%S\")\n", pSourceInfoLE->m_cchSourceOffset, *pcLineNumber, wszSourceText )); } DBGPRINTF(( DBG_CONTEXT, "Source offset %d corresponds to source line %d (Text: \"%S\")\n", pSourceInfoLE->m_cchSourceOffset, *pcLineNumber, wszSourceText )); } #endif return S_OK; } /* ============================================================================ CTemplate::GetSourceOffset Convert a character offset relative to the target script to the appropriate offset in the source. NOTE: offsets in the middle of a target line are converted to the offset relative to the beginning of source line - NOT to the precise source offset. this is OK because debugger ultimately wants the offset of the beginning of line. It is a lot of work to do the precise conversion due to the translation of "=" to Response.Write & HTML to Response.WriteBlock Also, because of these translations, we return the length of the segment calculated during compilation, and throw away the length the scripting engine sent to us. */ void CTemplate::GetSourceOffset ( ULONG idEngine, ULONG cchTargetOffset, TCHAR **pszSourceFile, ULONG *pcchSourceOffset, ULONG *pcchSourceText ) { Assert (idEngine < m_cScriptEngines); vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Find the closest offset in the source // This is the largest target offset N, such that N <= cchTargetOffset CSourceInfo *pSourceInfo; GetBracketingPair( cchTargetOffset, // value to search for prgSourceInfos->begin(), prgSourceInfos->end(), // array to search CTargetOffsetOrder(), // ordering predicate &pSourceInfo, static_cast(NULL) // return values ); // Since the first offset is zero, which is less than all other conceivable offsets, // the offset must have been found or else there is a bug. Assert (pSourceInfo != NULL); Assert (cchTargetOffset >= pSourceInfo->m_cchTargetOffset); #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap, cchTargetOffset, idEngine, wszSourceText, wszTargetText ); DBGPRINTF(( DBG_CONTEXT, "Target offset %d (Text: \"%S\") corresponds to source offset %d (Text: \"%S\") (Length is %d)\n", cchTargetOffset, wszTargetText, pSourceInfo->m_cchSourceOffset, wszSourceText, pSourceInfo->m_cchSourceText )); } #endif *pszSourceFile = pSourceInfo->m_pfilemap->m_szPathTranslated; *pcchSourceOffset = pSourceInfo->m_cchSourceOffset; *pcchSourceText = pSourceInfo->m_cchSourceText; } /* ============================================================================ CTemplate::GetTargetOffset Convert a character offset relative to the source script to the appropriate offset in the target. Returns: TRUE - source offset corresponds to script FALSE - source offset corresponds to HTML NOTES: 1. This function is very slow. consider caching the value of this function (The CTemplateDocumentContext class does this.) 2. This function returns the source offset in the master include file - if the target offset corresponds to an offset in a header file, then the offset to the #include line in the source is returned. 3. offsets in the middle of a target line are converted to the offset relative to the beginning of source line - NOT to the precise source offset. this is OK because the debugger ultimately wants the offset of the beginning of line. It is a lot of work to do the precise conversion due to the translation of "=" to Response.Write & HTML to Response.WriteBlock CONSIDER: Figure out a better way to do this */ BOOL CTemplate::GetTargetOffset ( TCHAR *szSourceFile, ULONG cchSourceOffset, /* [out] */ ULONG *pidEngine, /* [out] */ ULONG *pcchTargetOffset ) { // NOTE: // The table is not binary-searchable because of two factors: // 1. Include files will start a new line ordering // 2. For engine 0, tagged scripts will be re-arranged in // the target code to reside after all primary script in // engine 0. // // Algorithm: // // Find the largest source offset N across all engines, such that // N <= cchSourceOffset and the offset corresponds to an offset // in the appropriate file. // CSourceInfo *pSourceInfoLE = NULL; unsigned idEngineLE = 0; // Find the correct offset for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine) { vector *prgSourceInfos = &m_rgrgSourceInfos[idEngine]; // Loop through all lines EXCEPT the EOF line for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j) { CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j]; if (_tcscmp(pSourceInfo->m_pfilemap->m_szPathTranslated, szSourceFile) == 0 && pSourceInfo->m_cchSourceOffset <= cchSourceOffset && (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset)) { pSourceInfoLE = pSourceInfo; idEngineLE = idEngine; } } } // There won't be a valid offset in the case where there is no // code corresponding to the first line in the file (this only // occurs when the first line is whitespace, because there is no // corresponding "Response.WriteBlock" call there) // // In that case, return FALSE, which will cause the caller to fail // if (pSourceInfoLE == NULL) { *pidEngine = 0; *pcchTargetOffset = 0; return FALSE; } *pidEngine = idEngineLE; *pcchTargetOffset = pSourceInfoLE->m_cchTargetOffset; #if 0 IF_DEBUG(SCRIPT_DEBUGGER) { wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256]; GetScriptSnippets( cchSourceOffset, pSourceInfoLE->m_pfilemap, *pcchTargetOffset, *pidEngine, wszSourceText, wszTargetText ); DBGPRINTF(( DBG_CONTEXT, "Source offset %d (Text: \"%S\") corresponds to target offset %d (Text: \"%S\")\n", cchSourceOffset, wszSourceText, *pcchTargetOffset, wszTargetText )); } #endif return !pSourceInfoLE->m_fIsHTML; } /* ============================================================================ CTemplate::GetActiveScript Return a cached script from the template - only used in debug mode */ CActiveScriptEngine *CTemplate::GetActiveScript(ULONG idEngine) { if (m_rgpDebugScripts == NULL) return NULL; else { Assert (idEngine < m_cScriptEngines); CActiveScriptEngine *pEng = m_rgpDebugScripts[idEngine]; if (pEng) pEng->AddRef(); return pEng; } } /* ============================================================================ CTemplate::AddScript add an active script to the template object */ HRESULT CTemplate::AddScript(ULONG idEngine, CActiveScriptEngine *pScriptEngine) { if (m_rgpDebugScripts == NULL) { if ( (m_rgpDebugScripts = new CActiveScriptEngine *[m_cScriptEngines]) == NULL ) { return E_OUTOFMEMORY; } memset(m_rgpDebugScripts, 0, m_cScriptEngines * sizeof(CActiveScriptEngine *)); } Assert (idEngine < m_cScriptEngines); CActiveScriptEngine **ppScriptElem = &m_rgpDebugScripts[idEngine]; if (*ppScriptElem != NULL) (*ppScriptElem)->Release(); *ppScriptElem = pScriptEngine; pScriptEngine->AddRef(); // Initialize the script engine now (is currently uninitialized) // so that the debugger user can set breakpoints. IActiveScript *pActiveScript = pScriptEngine->GetActiveScript(); HRESULT hr; TRY hr = pActiveScript->SetScriptSite(static_cast(pScriptEngine)); CATCH(nExcept) HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF, NULL, TRUE, nExcept, "IActiveScript::SetScriptSite()", "CTemplate::AddScript()"); hr = nExcept; END_TRY if (FAILED(hr)) { *ppScriptElem = NULL; return E_FAIL; } TRY hr = pActiveScript->SetScriptState(SCRIPTSTATE_INITIALIZED); CATCH(nExcept) HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF, NULL, TRUE, nExcept, "IActiveScript::SetScriptState()", "CTemplate::AddScript()"); hr = nExcept; END_TRY if (FAILED(hr)) return E_FAIL; return S_OK; } /* ============================================================================ CTemplate::AppendMapFile Appends a filemap to the workstore and memory-maps its file Returns: Nothing Side effects: Allocates memory; throws exception on error */ void CTemplate::AppendMapFile ( LPCTSTR szFileSpec, // file spec for this file CFileMap* pfilemapCurrent, // ptr to filemap of parent file BOOLB fVirtual, // is file spec virtual or relative? CHitObj* pHitObj, // ptr to template's hit object BOOLB fGlobalAsa // is this file the global.asa file? ) { // alloc or realloc as needed if(m_cFilemaps++ == 0) m_rgpFilemaps = (CFileMap**) CTemplate::SmallMalloc(sizeof(CFileMap*)); else m_rgpFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpFilemaps, m_cFilemaps * sizeof(CFileMap*)); if(NULL == m_rgpFilemaps) THROW(E_OUTOFMEMORY); if(NULL == (m_rgpFilemaps[m_cFilemaps - 1] = new CFileMap)) THROW(E_OUTOFMEMORY); // map the file m_rgpFilemaps[m_cFilemaps - 1]->MapFile( szFileSpec, m_szApplnVirtPath, pfilemapCurrent, fVirtual, pHitObj, fGlobalAsa ); } /* ============================================================================ CTemplate::GetSegmentsFromFile Gets source segments from a source file by calling ExtractAndProcessSegment until there are no more segments; populates WorkStore with info on source segments. Returns: Nothing Side effects: None */ void CTemplate::GetSegmentsFromFile ( CFileMap& filemap, // this file's file map CWorkStore& WorkStore, // working storage for source segments CHitObj* pHitObj, // Browser request object BOOL fIsHTML ) { CByteRange brSearch; // byte range to search for source segments _TOKEN rgtknOpeners[TOKEN_OPENERS_MAX]; // array of permitted open tokens UINT ctknOpeners; // count of permitted open tokens SOURCE_SEGMENT ssegThisFile = ssegHTML; // Either HTML or "); AppendToken(tknCloseObject, ""); AppendToken(tknCloseHTMLComment, "-->"); AppendToken(tknEscapedClosePrimaryScript, "%\\>"); AppendToken(tknCloseTag, ">"); AppendToken(tknCommandINCLUDE, "#INCLUDE"); AppendToken(tknTagRunat, "RUNAT"); AppendToken(tknTagLanguage, "LANGUAGE"); AppendToken(tknTagCodePage, "CODEPAGE"); AppendToken(tknTagCodePage, "LCID"); AppendToken(tknTagTransacted, "TRANSACTION"); AppendToken(tknTagSession, "ENABLESESSIONSTATE"); AppendToken(tknTagID, "ID"); AppendToken(tknTagClassID, "CLASSID"); AppendToken(tknTagProgID, "PROGID"); AppendToken(tknTagScope, "SCOPE"); AppendToken(tknTagVirtual, "VIRTUAL"); AppendToken(tknTagFile, "FILE"); AppendToken(tknTagMETADATA, "METADATA"); // AppendToken(tknTagSetPriScriptLang, "@"); AppendToken(tknTagName, "NAME"); AppendToken(tknValueTypeLib, "TYPELIB"); AppendToken(tknTagType, "TYPE"); AppendToken(tknTagUUID, "UUID"); AppendToken(tknTagVersion, "VERSION"); AppendToken(tknTagStartspan, "STARTSPAN"); AppendToken(tknTagEndspan, "ENDSPAN"); AppendToken(tknValueCookie, "COOKIE"); AppendToken(tknTagSrc, "SRC"); AppendToken(tknValueServer, "Server"); AppendToken(tknValueApplication, "Application"); AppendToken(tknValueSession, "Session"); AppendToken(tknValuePage, "Page"); AppendToken(tknVBSCommentSQuote, "'"); AppendToken(tknVBSCommentRem, "REM "); // NOTE ends with space character AppendToken(tknTagFPBot, "webbot"); AppendToken(tknEOF, ""); AppendToken(tkncAll, ""); } /* ============================================================================ CTemplate::CTokenList::AppendToken Appends a string to tokens buffer NOTE we keep the unused tkn parameter because it enforces consistency and readability in CTemplate::CTokenList::Init(), e.g. AppendToken(tknOpenPrimaryScript, "<%"); rather than AppendToken("<%"); Returns: Nothing Side effects: None */ void CTemplate::CTokenList::AppendToken ( _TOKEN tkn, // token value char* sz // token string ) { // construct byte range from token string CByteRange br; br.m_pb = (BYTE*) sz; br.m_cb = strlen(sz); // append to tokens buffer as local string m_bufTokens.Append(br, TRUE, 0, NULL, TRUE); } /* ============================================================================ CTemplate::CTokenList::NextOpenToken Returns value of next open token in search range Returns token value of next open token in search range; ptr to ptr to open token (out-parameter) Side effects None */ _TOKEN CTemplate::CTokenList::NextOpenToken ( CByteRange& brSearch, // search byte range TOKEN* rgtknOpeners, // array of permitted open tokens UINT ctknOpeners, // count of permitted open tokens BYTE** ppbToken, // ptr to ptr to open token (out-parameter) LONG lCodePage ) { BYTE* pbTemp = NULL; // temp pointer _TOKEN tkn = tknEOF; // return value USHORT i; // loop index // Init caller's token ptr to null *ppbToken = NULL; // If input is empty, return if (brSearch.IsNull()) return tkn; // Prepare array of LPSTR pointers to tokens. // Do it here once, because to get LPSTR is not free. LPSTR rgszTokens[TOKEN_OPENERS_MAX]; UINT rgcchTokens[TOKEN_OPENERS_MAX]; Assert(ctknOpeners <= TOKEN_OPENERS_MAX); for (i = 0; i < ctknOpeners; i++) { LPSTR pszStr = m_bufTokens.PszLocal((UINT)(rgtknOpeners[i])); rgszTokens[i] = pszStr; rgcchTokens[i] = (pszStr != NULL) ? strlen(pszStr) : 0; } // Call a method to find one of the strings in the range UINT idToken; pbTemp = brSearch.PbOneOfAspOpenerStringTokens( rgszTokens, rgcchTokens, ctknOpeners, &idToken); if (pbTemp != NULL) { *ppbToken = pbTemp; tkn = rgtknOpeners[idToken]; } // If we found no open token, position token pointer at end of search range if (tkn == tknEOF) *ppbToken = brSearch.m_pb + brSearch.m_cb; return tkn; } /* ============================================================================ CTemplate::CTokenList::MovePastToken Moves a byte range past a token contained within it */ void CTemplate::CTokenList::MovePastToken ( _TOKEN tkn, BYTE* pbToken, CByteRange& brSearch ) { Assert(pbToken >= brSearch.m_pb); Assert(brSearch.m_cb >= (DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn))); brSearch.Advance(DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn)); } /* ============================================================================ CTemplate::CTokenList::GetToken Gets the next occurrence of a token within a byte range. Returns: ptr to token Side effects none */ BYTE* CTemplate::CTokenList::GetToken ( TOKEN tkn, CByteRange& brSearch, LONG lCodePage ) { return brSearch.PbString(m_bufTokens.PszLocal((UINT)tkn), lCodePage); } /* ============================================================================ The Big Three for CTemplateConnPt NOTES: Since this interface is embedded in CTemplate, AddRef() and Release() delegate to the container object (because that is the CTemplate pointer) */ HRESULT CTemplateConnPt::QueryInterface(const GUID &uidInterface, void **ppvObj) { if (uidInterface == IID_IUnknown || uidInterface == IID_IConnectionPoint) { *ppvObj = this; AddRef(); return S_OK; } else { *ppvObj = NULL; return E_NOINTERFACE; } } ULONG CTemplateConnPt::AddRef() { return m_pUnkContainer->AddRef(); } ULONG CTemplateConnPt::Release() { return m_pUnkContainer->Release(); } /* ============================================================================ Constructor for CDocNode */ CTemplate::CDocNodeElem::CDocNodeElem(CAppln *pAppln, IDebugApplicationNode *pDocRoot) { Assert (pAppln != NULL); Assert (pDocRoot != NULL); (m_pAppln = pAppln)->AddRef(); (m_pDocRoot = pDocRoot)->AddRef(); } /* ============================================================================ Destructor for CDocNode */ CTemplate::CDocNodeElem::~CDocNodeElem() { m_pAppln->Release(); DestroyDocumentTree(m_pDocRoot); } /* ============================================================================ CTemplate::fIsLangVBScriptOrJScript(USHORT idEngine) This function returns T/F to determine if the requested script engine is VBScript or JScript. This function is used as an indicator to determin if spaces need to be preserved for non MS Scripting languages There is an assumption here that the GUIDs for VBScript and JScript will not change Inputs Index to a script engine Returns BOOL */ BOOLB CTemplate::FIsLangVBScriptOrJScript(USHORT idEngine) { // {b54f3741-5b07-11cf-a4b0-00aa004a55e8} VBScript static const GUID uid_VBScript = {0xb54f3741, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}}; // {f414c260-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript static const GUID uid_JScript = {0xf414c260, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}}; // {b54f3743-5b07-11cf-a4b0-00aa004a55e8} VBScript.Encode static const GUID uid_VBScriptEncode = {0xb54f3743, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}}; // {f414c262-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript.Encode static const GUID uid_JScriptEncode = {0xf414c262, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}}; GUID &uidLang = m_pWorkStore->m_ScriptStore.m_rgProgLangId[idEngine]; return uidLang == uid_VBScript || uidLang == uid_VBScriptEncode || uidLang == uid_JScript || uidLang == uid_JScriptEncode; } SIZE_T _RoundUp( SIZE_T dwBytes) { #if 1 // 16KB <= dwBytes? Round up to next multiple of 4KB if (16*1024 <= dwBytes) dwBytes = ((dwBytes + (1<<12) - 1) >> 12) << 12; // 4KB <= dwBytes < 16KB? Round up to next multiple of 1KB else if (4*1024 <= dwBytes) dwBytes = ((dwBytes + (1<<10) - 1) >> 10) << 10; // 1KB <= dwBytes < 4KB? Round up to next multiple of 256 bytes else if (1024 <= dwBytes) dwBytes = ((dwBytes + (1<<8) - 1) >> 8) << 8; // dwBytes < 1KB? Round up to next multiple of 32 bytes else dwBytes = ((dwBytes + (1<<5) - 1) >> 5) << 5; #endif return dwBytes; } void* CTemplate::SmallMalloc(SIZE_T dwBytes) { DBG_ASSERT(sm_hSmallHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapAlloc(sm_hSmallHeap, 0, dwBytes); } void* CTemplate::SmallReAlloc(void* pvMem, SIZE_T dwBytes) { DBG_ASSERT(sm_hSmallHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapReAlloc(sm_hSmallHeap, 0, pvMem, dwBytes); } void CTemplate::SmallFree(void* pvMem) { DBG_ASSERT(sm_hSmallHeap != NULL); ::HeapFree(sm_hSmallHeap, 0, pvMem); } void* CTemplate::LargeMalloc(SIZE_T dwBytes) { DBG_ASSERT(sm_hLargeHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapAlloc(sm_hLargeHeap, 0, dwBytes); } void* CTemplate::LargeReAlloc(void* pvMem, SIZE_T dwBytes) { DBG_ASSERT(sm_hLargeHeap != NULL); dwBytes = _RoundUp(dwBytes); return ::HeapReAlloc(sm_hLargeHeap, 0, pvMem, dwBytes); } void CTemplate::LargeFree(void* pvMem) { DBG_ASSERT(sm_hLargeHeap != NULL); ::HeapFree(sm_hLargeHeap, 0, pvMem); } // WriteTemplate Class. /* ============================================================================ CTemplate::CWriteTemplate::CWriteTemplate Constructor for WriteTemplate Side effects none */ CTemplate::CWriteTemplate::CWriteTemplate () : m_pworkStore(NULL), m_cbMemRequired (0L), m_pTemplate(NULL), m_fWriteScript(FALSE), m_fCalcLineNumber(TRUE), m_pbHeader(NULL) { } /* ============================================================================ CTemplate::CWriteTemplate::~CWriteTemplate Destructor for WriteTemplate. Side effects none */ CTemplate::CWriteTemplate::~CWriteTemplate () { if (m_pbHeader) { // Dont free up m_pbHeader..Rather let CTemplate free it up during its cleanup. // As m_pbStart = m_pbHeader and memory has been alocated from heap. m_pbHeader = NULL; } } /* ============================================================================ CTemplate::CWriteTemplate::Init Initializes the write Template by copying the workstore object and the pointer to the template. Returns: HRESULT Side effects none */ void CTemplate::CWriteTemplate::Init ( CWorkStore* pworkStore, CTemplate* pTemplate, BOOL fCalcLineNumber ) { //Store pointers to the workstore and template and metabase flag for line number calculation. m_pworkStore = pworkStore; m_pTemplate = pTemplate; m_fCalcLineNumber = fCalcLineNumber; // Get CodePage Information if (!GetCPInfo(pTemplate->m_wCodePage, &m_codePageInfo)) { // Cant GetCPInfo? Set the codePage such that the call to MultiByteToWideChar is forced. m_codePageInfo.MaxCharSize = 0; // This will fail (MaxCharSize == 1) } } /* ============================================================================ CTemplate::CWriteTemplate::WriteTemplate Control routine that calls EstimateMemory and then write script/HTML/object blocks to template memory. Returns: HRESULT Side effects none */ void CTemplate::CWriteTemplate::WriteTemplate () { m_fWriteScript = FALSE; // Set state to ESTIMATE WriteTemplateComponents(); // Write the blocks into template memory m_fWriteScript = TRUE; // Set state to WRITE WriteTemplateComponents(); // This assert is no longer holds as the trailing comment could send this for a spin while it is valid. // Assert (m_cbMemRequired == m_pTemplate->m_cbTemplate); Assert (m_cbMemRequired >= m_pTemplate->m_cbTemplate); } /* ============================================================================ CTemplate::CWriteTemplate::WriteTemplateComponents Runs thru the workstore object the second time.. this time telling routines to write to the template memory and not just calculate the memory required. Writes the template out to a contiguous block of memory. Returns: nothing Side effects: Allocates memory. Its CTemplate's responsibility to free it when it done with it. HERE IS HOW IT WORKS -------------------- - an 'offset' is the count of bytes from start-of-template to a location within template memory - at the start of the template are 3 USHORTs, the counts of script blocks, object-infos and HTML blocks, respectively - next are 4 ULONGs, each an offset to a block of offsets; in order, these are: offset-to-offset to first script engine name offset-to-offset to first script block (the script text itself) offset-to-offset to first object-info offset-to-offset to first HTML block - next are a variable number of ULONGs, each an offset to a particular template component. In order these ULONGs are: Offsets to Count of offsets ---------- ---------------- script engine names cScriptBlocks script blocks cScriptBlocks object-infos cObjectInfos HTML blocks cHTMLBlocks - next are the template components themselves, stored sequentially in the following order: script engine names script blocks object-infos HTML blocks HERE IS HOW IT LOOKS -------------------- |--|--|--| 3 template component counts (USHORTs) |-- --|-- --| 4 offsets to template component offsets (ULONGs) |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs) |-- --| ............... |-- --| |-- --|-- --|-- --|-- --|-- --| | ........................... | template components | ........................... | | ........................... | | ........................... | or, mnemonically: cS cO cH 3 template component counts (USHORTs) offSE offSB offOb offHT 4 offsets to template component offsets (ULONGs) |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs) |-- --| ............... |-- --| |-- --|-- --|-- --|-- --|-- --| | ........................... | template components | ........................... | | ........................... | | ........................... | */ void CTemplate::CWriteTemplate::WriteTemplateComponents ( ) { USHORT i; CByteRange brWrite; // SourceFilename :: Only if include file. BYTE* pbIncFilename; ULONG cbIncFilename; ULONG cbSourceOffset; // Offset in the Source file (FOR HTML BLOCKS) // Block counts USHORT cScriptBlocks = m_pworkStore->CRequiredScriptEngines (m_pTemplate->m_fGlobalAsa); USHORT cObjectInfos = m_pworkStore->m_ObjectInfoStore.Count(); USHORT cHTMLBlocks = m_pworkStore->m_bufHTMLSegments.Count(); // Count the total blocks in the ASP file that will be written to a template. USHORT cBlockPtrs = (2 * cScriptBlocks) + cObjectInfos + cHTMLBlocks; // Total number of memory required for header = required header + memory for blocks in ASP file. UINT cbRequiredHeader = (C_COUNTS_IN_HEADER * sizeof(USHORT)) + (C_OFFOFFS_IN_HEADER * sizeof(DWORD)); UINT cbRequiredBlockPtrs = cBlockPtrs * sizeof (DWORD); // Adjust offsets. UINT cbHeaderOffset = 0; UINT cbOffsetToOffset = 0; UINT cbDataOffset = cbRequiredHeader + cbRequiredBlockPtrs; UINT *pcbDataOffset; // Pointer to the counter = Points to m_cbMemRequired during Estimation phase and cbDataOffset during write phase. if (m_fWriteScript) { // Allocate space for the template (resize the header field.) if(NULL == (m_pbHeader = (BYTE*) CTemplate::LargeMalloc(m_cbMemRequired))) THROW(E_OUTOFMEMORY); /* AppendSourceInfo Uses m_pbStart...So updating m_pbStart to use the same memory. Care should be taken to allow CTemplate::~CTemplate to do the clean up. So no cleanup is performed when the CTemplate::CWriteTemplate object is distroyed. */ m_pTemplate->m_pbStart = m_pbHeader; // The following 3 DWORDs are the number of script, object and HTML blocks respectively. // Write out basic headers MemCopyAlign (&cbHeaderOffset, &cScriptBlocks, sizeof(USHORT), sizeof(USHORT)); MemCopyAlign (&cbHeaderOffset, &cObjectInfos, sizeof(USHORT), sizeof(USHORT)); MemCopyAlign (&cbHeaderOffset, &cHTMLBlocks, sizeof(USHORT), sizeof(USHORT)); /* The next 4 ULONGs are offsets to within the template memory to the start of script engine names block, the start of script block, the start of object block and the start of the HTML block. At each of these memory locations where these offsets point there will be offsets to individual blocks (script,object,HTML). */ // Write out Offset pointers UINT fillerVar = 0; // Dummy variable.. used to write 0 to memory. cbOffsetToOffset = cbRequiredHeader; fillerVar = cScriptBlocks ? cbOffsetToOffset : 0; MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG)); cbOffsetToOffset += cScriptBlocks * sizeof(ULONG); fillerVar = cScriptBlocks ? cbOffsetToOffset : 0; MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG)); cbOffsetToOffset += cScriptBlocks * sizeof(ULONG); fillerVar = cObjectInfos ? cbOffsetToOffset : 0; MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG)); cbOffsetToOffset += cObjectInfos * sizeof(ULONG); fillerVar = cHTMLBlocks ? cbOffsetToOffset : 0; MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG)); // Start calculating the memory taken for the template while writing out the header. // At this point cbRequiredHeader == cbHeaderOffset // Reset cbOffsetToOffset. It will not be used after this as start of cbOffsetToOffset == cbHeaderOffset Assert (cbHeaderOffset == cbRequiredHeader); pcbDataOffset = &cbDataOffset; } else { m_cbMemRequired = cbRequiredHeader + cbRequiredBlockPtrs; pcbDataOffset = &m_cbMemRequired; } // Write the Script Engine names. for (i = 0;im_ScriptStore.CountPreliminaryEngines(); i++) { if (m_pworkStore->FScriptEngineRequired (i, m_pTemplate->m_fGlobalAsa)) { m_pworkStore->m_ScriptStore.m_bufEngineNames.GetItem(i, brWrite); if (m_fWriteScript) // Write out current pointer to the Header (OffsetOfOffsets) MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG)); WriteBSTRToMem (brWrite, pcbDataOffset); // Write out the ProgLangID MemCopyAlign (pcbDataOffset, &(m_pworkStore->m_ScriptStore.m_rgProgLangId[i]),sizeof(PROGLANG_ID),sizeof(DWORD)); } } // Write Scripts USHORT idEngine = 0; // Think abt storing this in CWriteTemplate for (i = 0;im_ScriptStore.CountPreliminaryEngines(); i++) { if (m_pworkStore->FScriptEngineRequired (i, m_pTemplate->m_fGlobalAsa)) { // Calc memory of the script block. WriteScriptBlocks (i, idEngine, pcbDataOffset, &cbHeaderOffset, m_pTemplate->m_fGlobalAsa); idEngine ++; } } // Write ObjectInfos for (i=0;im_ObjectInfoStore.m_bufObjectNames.GetItem(i, brWrite); if (m_fWriteScript) { // Align cbDataOffset to the place where we will write out the Object blocks // Alignment is required before writing the offset to header. ByteAlignOffset (pcbDataOffset, sizeof(ULONG)); MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG)); } WriteBSTRToMem (brWrite, pcbDataOffset); // Write CLSID, scope and model MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_clsid) , sizeof(CLSID), sizeof(DWORD)); MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_scope) , sizeof(CompScope), sizeof(CompScope)); MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_model) , sizeof(CompModel), sizeof(CompModel)); } // Write out the the HTML BLocks if (!m_pTemplate->m_fGlobalAsa) for (i=0;im_bufHTMLSegments.GetItem(i, brWrite); if (m_fWriteScript) { // Align cbDataOffset to the place where we will write out the HTML blocks // Alignment is required before writing the offset to header. ByteAlignOffset (pcbDataOffset, sizeof(ULONG)); MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG)); } WriteBSTRToMem (brWrite, pcbDataOffset); //Source offset and include file: cbSourceOffset = 0; pbIncFilename = NULL; cbIncFilename = 0; if (brWrite.m_pfilemap) { // Calculate offset from filemap CFileMap *pFileMap = (CFileMap *) brWrite.m_pfilemap; if (pFileMap->m_pbStartOfFile) // mapped? { cbSourceOffset = DIFF (brWrite.m_pb - pFileMap->m_pbStartOfFile) + 1; if (pFileMap->GetParent() != NULL && // is Include file ? pFileMap->m_szPathInfo ) // path exists { pbIncFilename = (BYTE *) pFileMap->m_szPathInfo ; cbIncFilename = _tcslen(pFileMap->m_szPathInfo) * sizeof(TCHAR); } } } // Calculate memory required to write them. MemCopyAlign (pcbDataOffset, &cbSourceOffset ,sizeof(ULONG), 0); MemCopyAlign (pcbDataOffset, &cbIncFilename,sizeof(ULONG), 0); if (cbIncFilename > 0) { MemCopyAlign(pcbDataOffset, pbIncFilename, cbIncFilename+sizeof(TCHAR), 0); } } // If this is Write Mode then update the template size counter. if (m_fWriteScript) m_pTemplate->m_cbTemplate = cbDataOffset; } /* ============================================================================ CTemplate::CWriteTemplate::WriteScriptBlocks Writes out script block for idEngine-th script engine. NOTE segment buffer [0] contains primary script segments segment buffer [1] contains tagged script segments of default engine segment buffer [i] contains tagged script segments of (i-1)th engine, for i >= 2 During the ESTIMATE phase this routine just skims over the blocks calculating memory required. In most cases the estimate phase will follow the exact same code path that the WRITE phase and will stop short of actually writing to memory. Returns: HRESULT Side effects none */ void CTemplate::CWriteTemplate::WriteScriptBlocks ( USHORT idEnginePrelim, USHORT idEngine, UINT *pcbDataOffset, UINT *pcbOffsetToOffset, BOOLB m_fGlobalAsa ) { UINT i = 0; CByteRange brSegment; // current Script Segment UINT cbScriptBlockOffset; // Offset to script Block write location. UINT scriptStartBlockOffset; USHORT iTSegBuffer = idEnginePrelim + 1; // Index of tagged segment buffer. // Number of tagged script segements UINT cTaggedSegments = m_pworkStore->m_ScriptStore.m_ppbufSegments[iTSegBuffer]->Count(); //Align Offset For script Block and update header. (align on ULONG) ByteAlignOffset ((UINT*)pcbDataOffset,sizeof(ULONG)); cbScriptBlockOffset = *pcbDataOffset; if (!m_fWriteScript) // Calculate memory for the size of this script segment Right now assume 0 is written. Part of the design. // 'i' used here as a filler, The code will just add sizeof (ULONG) to pcbDataOffset after aligning it if necessary MemCopyAlign (pcbDataOffset, &i, sizeof(ULONG), sizeof(ULONG)); else { //Update the header to write the start of the script block. MemCopyAlign (pcbOffsetToOffset, &cbScriptBlockOffset, sizeof(ULONG), sizeof(ULONG)); // Save the offset to where the size of the script has to be written later. scriptStartBlockOffset = *pcbDataOffset; // Advance Offset pointer by ULONG to make space for writing Lenght in the end. *pcbDataOffset += sizeof(ULONG); } // 0 out the m_cbTargetOffsetPrevT variable which append source info uses. m_pTemplate->m_cbTargetOffsetPrevT = 0; // Now begin calculating the amount of memory the script segments will take. This has two parts.. // The primary script segment and the tagged script segments. if (!m_fGlobalAsa) // This is not Global.asa if (idEnginePrelim == 0) // This is the primary script WritePrimaryScript(0, pcbDataOffset, cbScriptBlockOffset + sizeof(ULONG)); // Calculate memory for the tagged segments. for (i=0;im_ScriptStore.m_ppbufSegments[iTSegBuffer]->GetItem(i,brSegment); WriteTaggedScript( idEngine, m_pTemplate->m_rgpSegmentFilemaps[brSegment.m_idSequence], brSegment, pcbDataOffset, cbScriptBlockOffset + sizeof(ULONG), FALSE ); } // check if the last few characters were --> and take appropriate action. // Do this only during the write phase as this code will write to script memory. if (m_fWriteScript) RemoveHTMLCommentSuffix(scriptStartBlockOffset, pcbDataOffset); // We need to write the null terminator MemCopyAlign (pcbDataOffset, WSTR_NULL, sizeof(WCHAR), 0); if (m_fWriteScript) { // Calculate size of script = difference in offsets - size of the length pointer - size of null terminator UINT cbScript = *pcbDataOffset - scriptStartBlockOffset - sizeof (ULONG) - sizeof (WCHAR); MemCopyAlign (&scriptStartBlockOffset, &cbScript, sizeof(ULONG), 0); /* We need to append one extra "pseudo-line" to the end of source info's array to cover the case where the script engine reports back an error line number which falls after the end of the script. We always want the "pseudo-line" to point to the main file, so the debugger displays something reasonable. We pass it m_rgpFilemaps[0] which is the main file. We need to call this function only during the write phase and not the estimation phase. We need to call AppendSourceInfo before the script engine executes the script. */ m_pTemplate->AppendSourceInfo (idEngine, m_pTemplate->m_rgpFilemaps[0], NULL, UINT_MAX, UINT_MAX, UINT_MAX, 0, TRUE); } } /* ============================================================================ CTemplate::CWriteTemplate::WritePrimaryScript Writes (if fWriteScript is TRUE, calculates memory requirement otherwise) out default-engine primary script procedure. If VBScript is default-engine, the primary script procedure contains interleaved script commands and HTML block-writes, like this: Sub Main ... [script segment] Response.WriteBlock([HTML block id]) ... [script segment] Response.WriteBlock([HTML block id]) ... [script segment] Response.WriteBlock([HTML block id]) ... End Sub NOTE segment buffer [0] == primary script segments This routine is called for the main script. Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::WritePrimaryScript ( USHORT idEngine, UINT * pcbDataOffset, UINT cbScriptBlockOffset ) { USHORT cScriptSegmentsProcessed = 0; USHORT cHTMLBlocksProcessed = 0; CByteRange brScriptNext; CByteRange brHTMLNext; char szHTMLBlockID[6]; USHORT cPrimaryScriptSegments = m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->Count(); USHORT cHTMLBlocks = m_pworkStore->m_bufHTMLSegments.Count(); CFileMap * pfilemap; // Get Initial script and HTML segments. if (cPrimaryScriptSegments) m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->GetItem(0,brScriptNext); if (cHTMLBlocks) m_pworkStore->m_bufHTMLSegments.GetItem(0,brHTMLNext); //While there are HTML or primary scripts present... while ((cHTMLBlocksProcessed < cHTMLBlocks) || (cScriptSegmentsProcessed < cPrimaryScriptSegments)) { //If HTML block(s) remain to be processed if (cHTMLBlocksProcessed < cHTMLBlocks) while (TRUE) { if (brHTMLNext.FEarlierInSourceThan(brScriptNext) || (cScriptSegmentsProcessed >= cPrimaryScriptSegments)) { //append source-info for the target script line we just manufactured. pfilemap = m_pTemplate->m_rgpSegmentFilemaps[brHTMLNext.m_idSequence]; // We can call Append Source info here as it does not write any information to the template // rather it reads information from the workstore/scriptstore and appends the source info to // the workstore. Provided this function is called just once. we call it during the WRITE phase if (m_fWriteScript) { m_pTemplate->AppendSourceInfo ( idEngine, pfilemap, // Filemap NULL, // Dont do linenumber calculation DIFF(brHTMLNext.m_pb - pfilemap->m_pbStartOfFile), // offset of line in source cbScriptBlockOffset, // ptr to start of scripts (*pcbDataOffset - cbScriptBlockOffset)/sizeof(WCHAR), // line in target file CharAdvDBCS ((WORD) m_pTemplate->m_wCodePage, reinterpret_cast (brHTMLNext.m_pb), reinterpret_cast (brHTMLNext.m_pb + brHTMLNext.m_cb), INFINITE, NULL), // Exact number of characters in source file TRUE); // It is HTML } // Proceed to calculate the blocknumber and tags in the template. // Convert blockID to string. _itoa (cHTMLBlocksProcessed, szHTMLBlockID,10); // Write out block opener, the block number and the block closer followed by a newline. MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteBlockOpen, m_pworkStore->m_cchWriteBlockOpen, 0); MemCopyWithWideChar (pcbDataOffset, szHTMLBlockID, strlen(szHTMLBlockID), 0); MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteBlockClose, m_pworkStore->m_cchWriteBlockClose, 0); MemCopyWithWideChar (pcbDataOffset, SZ_NEWLINE, CB_NEWLINE, 0); if (++cHTMLBlocksProcessed >= cHTMLBlocks) break; //get next HTMLBlock. m_pworkStore->m_bufHTMLSegments.GetItem (cHTMLBlocksProcessed,brHTMLNext); } else break; } //if primary Script Segment remains to be processed; if (cScriptSegmentsProcessed < cPrimaryScriptSegments) while (TRUE) { // Write out each primary script segment earlier in the source file than the next HTML block if (brScriptNext.FEarlierInSourceThan(brHTMLNext) || (cHTMLBlocksProcessed >= cHTMLBlocks)) { WriteTaggedScript ( idEngine, m_pTemplate->m_rgpSegmentFilemaps[brScriptNext.m_idSequence], brScriptNext, pcbDataOffset, cbScriptBlockOffset, TRUE); if (++cScriptSegmentsProcessed >= cPrimaryScriptSegments) break; // Get next script Segment m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->GetItem(cScriptSegmentsProcessed, brScriptNext); } else break; } } } /* ============================================================================ CTemplate::CTemplate::WriteTaggedScript Writes a script segment to template memory line-by-line if fWriteScript is TRUE else just calculates memory its memory requirement. This routine could be called for both blocks and <% and %> blocks Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::WriteTaggedScript ( USHORT idEngine, CFileMap* pfilemap, CByteRange brScript, UINT* pcbDataOffset, UINT cbScriptBlockOffset, BOOL fAllowExprWrite ) { CByteRange brLine; UINT cbPtrOffset = 0; BOOL fExpression = FALSE; BOOL fFirstLine = TRUE; // Metabase setting (m_fCalcLineNumber) will override if it is set to FALSE else it will have no effect. BOOL fCalcLineNumber = m_fCalcLineNumber; // should line number be calculated. if (FByteRangeIsWhiteSpace(brScript)) return; // Trim white space from begining of script segment if (m_pTemplate->FIsLangVBScriptOrJScript (idEngine)) LTrimWhiteSpace (brScript); while (!(brScript.IsNull())) { //fetch next line from byte range until brScript is NULL LineFromByteRangeAdv (brScript, brLine); if (FByteRangeIsWhiteSpace (brLine)) { fCalcLineNumber = m_fCalcLineNumber ; // if m_fCalcLineNumber is set to true then fCalcLineNumber is true else // just m_fCalcLineNumber will just mask of fCalcLineNumber to FALSE continue; } // If the line is not blank..trim the white spaces. if (m_pTemplate->FIsLangVBScriptOrJScript (idEngine)) LTrimWhiteSpace (brLine); RTrimWhiteSpace (brLine); /* We need to call AppendSourceInfo so that the script engine has a corelation with the line number..and other source information such as which file the script belongs to. If the line number flag is not NULL then AppendSourceInfo will force recalculation of the line numbers. If however it is NULL then AppendSourceInfo just appends 1 to the current line number without calling into the Line number calculation routine. All AppendSourceInfo calls are made only during the WRITE phase. NOTE: AppendSourceInfo will always take the NULL as a parameter if m_fCalcLineNumber is set to FALSE */ if (m_fWriteScript) { m_pTemplate->AppendSourceInfo ( idEngine, pfilemap, fCalcLineNumber ? brLine.m_pb : NULL, DIFF (brLine.m_pb - pfilemap->m_pbStartOfFile), cbScriptBlockOffset, (*pcbDataOffset - cbScriptBlockOffset)/sizeof(WCHAR), CharAdvDBCS ((WORD) m_pTemplate->m_wCodePage, reinterpret_cast (brLine.m_pb), reinterpret_cast (brLine.m_pb + brLine.m_cb), INFINITE, NULL), FALSE); } /* fCalcLineNumber is set to TRUE if the parser hits a blank line or an close token. It instructs the Append source info to begin line number calculation from the start. Setting it to FALSE will just tell it to ignore recalculation of the line number and it just appends 1 to previous line number to get the current count. On return from AppendSourceInfo..we just reset the line number calculation flag. */ fCalcLineNumber = FALSE; if (fAllowExprWrite && fFirstLine) { // Test for remainder of script segment null on temp copy of script byte range, not on actual CByteRange brTemp = brScript; LTrimWhiteSpace (brTemp); if (brTemp.IsNull()) { /* if (a) expr-write is allowed AND (b) this is only script line in this segment (first and remainder is NULL. then test this line to see if it is an expression. if this like is an expression, create a script command that reads Response.Write([line contents]) */ if (fExpression = m_pTemplate->FExpression (brLine)) { MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteOpen, m_pworkStore->m_cchWriteOpen, 0); } brScript = brTemp; } } // write out line contents ScriptMemoryMinusEscapeChars(brLine, pcbDataOffset, cbPtrOffset); // If this line is an expression, close script command. if (fExpression) MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteClose, m_pworkStore->m_cchWriteClose, 0); // Write NEW_LINE and set first line flag to false. MemCopyWithWideChar (pcbDataOffset, SZ_NEWLINE, CB_NEWLINE, 0); fFirstLine = FALSE; } } /* ============================================================================ CTemplate::CWriteTemplate::ScriptMemoryMinusEscapeChars if fWriteWideCharStr is FALSE it calculates a script byte range to memory requirement, minus its escape characters, if any. if fWriteWideCharStr is TRUE it Writes a script byte range to memory, minus its escape characters, if any. Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::ScriptMemoryMinusEscapeChars ( CByteRange brScript, UINT *pcbDataOffset, UINT cbPtrOffset ) { BYTE * pbToken; while (NULL != (pbToken = gm_pTokenList->GetToken (CTokenList::tknEscapedClosePrimaryScript, brScript, m_pTemplate->m_wCodePage))) { CByteRange brTemp = brScript; // Set temp range to source range up to escaped-token brTemp.m_cb = DIFF (pbToken - brTemp.m_pb); // WriteByteRangeAdv with no BSTR = MemCopyWithWideChar // Write out temp range and actual-token - this replaces escaped-token with actual-token. MemCopyWithWideChar (pcbDataOffset, brTemp.m_pb, brTemp.m_cb, 0); MemCopyWithWideChar (pcbDataOffset, SZ_TOKEN(CTokenList::tknClosePrimaryScript), CCH_TOKEN(CTokenList::tknClosePrimaryScript), 0); //Advance source range past escaped token brScript.Advance (DIFF(pbToken - brScript.m_pb) + CCH_TOKEN(CTokenList::tknEscapedClosePrimaryScript)); } // write remainder of source range. MemCopyWithWideChar (pcbDataOffset, brScript.m_pb, brScript.m_cb, 0); } /* ============================================================================ CTemplate::CWriteTemplate::CalcMemoryAndUpdateHeader Calculate the memory required to write a BSTR into template memory Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::WriteBSTRToMem ( CByteRange & brWrite, UINT *pcbOffset ) { MemCopyAlign (pcbOffset, &brWrite.m_cb, sizeof(ULONG), sizeof(ULONG)); // Length of BSTR MemCopyAlign (pcbOffset, brWrite.m_pb, brWrite.m_cb, 0); // the BSTR itself. MemCopyAlign (pcbOffset, SZ_NULL, 1, 0); // Teminating NULL } /* ============================================================================ CTemplate::CWriteTemplate::MemCopyAlign Writes to target/calculates memory required after adjusting byte alignment if necessary. Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::MemCopyAlign ( UINT *pcbOffset, void *pbSource, ULONG cbSource, UINT cbByteAlign ) { // If byte alignment is necessary then align. if (cbByteAlign > 0) ByteAlignOffset (pcbOffset, cbByteAlign); // Copy memory if in WRITE phase and adjust offset else just adjust offset. if (m_fWriteScript) memcpy(m_pbHeader + *pcbOffset,pbSource,cbSource); *pcbOffset += cbSource; } /* ============================================================================ CTemplate::CWriteTemplate::MemCopyEstimateWithWideChar If the m_fWriteScript flag is FALSE then this routine calculates the memory that will be required when the source in converted into WideChar and stored. If the m_fWriteScript flag is TRUE then the routine copies the wide string into Template memory. Parameters: pcbOffset : pointer to offset where data will be written pbSource : pointer to the source string cbSource : count of bytes in source. cbByteAlign : what boundry should pcbOffset be aligned on 0, 1, 2, 4 Returns: nothing Side effects none */ void CTemplate::CWriteTemplate::MemCopyWithWideChar ( UINT *pcbOffset, void *pbSource, ULONG cbSource, UINT cbByteAlign ) { ULONG cchWstr; // If byte alignment is necessary then align. Assert(cbSource); if (cbByteAlign > 0) ByteAlignOffset ((UINT *)pcbOffset, cbByteAlign); /* If this is the estimation phase and the code page MaxCharSize is 1 (ASCII) then we just make the calculate cbWstr to be cbSource (WideChars). This is to reduce the overhead of calling into MultiByteToWideChar. If this is the write phase then it calls MultiByteToWideChar. */ if (!m_fWriteScript) { if (m_codePageInfo.MaxCharSize == 1) cchWstr = cbSource; // cbWstr is in wide chars. else cchWstr = MultiByteToWideChar (m_pTemplate->m_wCodePage, 0, (LPCSTR)pbSource, cbSource, NULL, 0); } else { cchWstr = MultiByteToWideChar (m_pTemplate->m_wCodePage, 0, (LPCSTR)pbSource, cbSource,(LPWSTR) (m_pbHeader + *pcbOffset), m_cbMemRequired - *pcbOffset); Assert(FImplies ((m_codePageInfo.MaxCharSize == 1), (cchWstr == cbSource))); } // Adjust offset. if (cchWstr) *pcbOffset += cchWstr *2; } /* ============================================================================ CTemplate::CWriteTemplate::RemoveHTMLCommentSuffix Strip of a HTML comment within script blocks Parameters: Returns: nothing Side effects strips trailing HTML comments from the Script. IMPORTANT: This function is similar to the one in VBSCRIPT scripting engine. The only difference is that the scripting team places a \0 at the end of the script because they parse the scripts independently. By doing the same thing we will inadvertently be terminating the script at the first point we notice a --> followed by a %> or a . We have two alternatives (a) Overwrite the entire comment with a space. Effects: Error Reporting will miss information (b) Modify the pointer in the template to place the pbCurrentOffset to the start of the Comment block. Effects: Yet to learn. Probarbly affect Line Number calculation Another Side Effect = We will be allocating more memory than we need (memory pertaining to the comment). */ void CTemplate::CWriteTemplate::RemoveHTMLCommentSuffix ( UINT cbStartOffset, UINT *pcbCurrentOffset ) { UINT cbTempDataOffset = *pcbCurrentOffset; UINT len = (*pcbCurrentOffset -cbStartOffset)/sizeof(WCHAR); WCHAR * pwszSrc = (WCHAR*) (m_pTemplate->m_pbStart + cbStartOffset); while (len > 3 && FWhiteSpaceEx(pwszSrc[len])) { len --; cbTempDataOffset -= sizeof (WCHAR); } if (len < 3 || (WCHAR) L'>' != (WCHAR) pwszSrc[len--] || (WCHAR) L'-' != pwszSrc[len--]|| (WCHAR) L'-' != pwszSrc[len--]) return ; // Saw a -->delimiter // Now run back until there is an EOL , a // or a