Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

11462 lines
386 KiB

  1. //depot/private/jasbr/inetsrv/iis/svcs/cmp/asp/template.cpp#19 - edit change 3548 (text)
  2. /*==============================================================================
  3. Microsoft Denali
  4. Microsoft Confidential.
  5. Copyright 1996 Microsoft Corporation. All Rights Reserved.
  6. File: template.cpp
  7. Maintained by: DaveK
  8. Component: source file for Denali Compiled Template object
  9. ==============================================================================*/
  10. #include "denpre.h"
  11. #pragma hdrstop
  12. const int SNIPPET_SIZE = 20; // # of characters in the code snippets
  13. #pragma warning( disable : 4509 ) // suppress SEH/destructor warnings
  14. #pragma warning( disable : 4355 ) // ignore: "'this' used in base member init
  15. #include "debugger.h"
  16. #include "dbgutil.h"
  17. #include "tlbcache.h"
  18. #include "ie449.h"
  19. #include "memchk.h"
  20. #include "vecimpl.h" // Include after memchk to insure that vector uses our mem manager.
  21. #include "Accctrl.h"
  22. #include "aclapi.h"
  23. // Init class statics
  24. CTemplate::CTokenList *CTemplate::gm_pTokenList = NULL;
  25. PTRACE_LOG CTemplate::gm_pTraceLog = NULL;
  26. HANDLE CTemplate::sm_hSmallHeap = NULL;
  27. HANDLE CTemplate::sm_hLargeHeap = NULL;
  28. extern BOOL g_fLazyContentPropDisabled;
  29. extern DWORD g_dwFileMonitoringTimeoutSecs;
  30. // Max # of opener tokens to look for
  31. #define TOKEN_OPENERS_MAX 8
  32. /*===================================================================
  33. Private non-class support functions
  34. ===================================================================*/
  35. static void ByteRangeFromPb(BYTE* pbSource, CByteRange& brTarget);
  36. static BOOLB FByteRangesAreEqual(CByteRange& br1, CByteRange& br2);
  37. static unsigned CharAdvDBCS(WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS = FALSE);
  38. static void LineFromByteRangeAdv(CByteRange& br, CByteRange& brLine);
  39. static void LTrimWhiteSpace(CByteRange& br);
  40. static void RTrimWhiteSpace(CByteRange& br);
  41. static CByteRange BrNewLine(CByteRange br);
  42. static BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace = TRUE);
  43. static BOOLB FWhiteSpaceEx(WCHAR wch, BOOLB fSpaceIsWhiteSpace = TRUE);
  44. static BOOLB FByteRangeIsWhiteSpace(CByteRange br);
  45. static BOOLB FTagName(BYTE* pb, UINT cb);
  46. static void ByteAlignOffset(UINT* pcbOffset, UINT cbAlignment);
  47. static void GetSzFromPatternInserts(char* pszPattern, UINT cInserts, char** ppszInserts, char* szReturned);
  48. static UINT CchPathFromFilespec(LPCTSTR szFile);
  49. static void GetPathFromParentAndFilespec(LPCTSTR szParentPath, LPCTSTR szFileSpec, LPTSTR* pszPath);
  50. static void HandleAccessFailure(CHitObj* pHitObj, TCHAR* szFile);
  51. static void SendToLog(DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szShortDes, CHAR *szLongDes, CHAR *szEngine, CHitObj *pHitObj);
  52. static HRESULT GetProgLangId(CByteRange& brEngine, PROGLANG_ID* pProgLangId);
  53. inline
  54. void __cdecl DebugPrintf(LPCSTR fmt, ...)
  55. {
  56. #if DBG
  57. char msg[512];
  58. va_list marker;
  59. va_start(marker, fmt);
  60. vsprintf(msg, fmt, marker);
  61. va_end(marker);
  62. OutputDebugStringA(msg);
  63. #endif
  64. }
  65. /* ============================================================================
  66. ByteRangeFromPb
  67. Gets a byte range from a contiguous block of memory
  68. Returns:
  69. Nothing.
  70. Side effects:
  71. None.
  72. */
  73. void
  74. ByteRangeFromPb
  75. (
  76. BYTE* pbSource,
  77. CByteRange& brTarget
  78. )
  79. {
  80. Assert(pbSource != NULL);
  81. brTarget.m_cb = *(ULONG*)pbSource;
  82. brTarget.m_pb = pbSource + sizeof(ULONG);
  83. }
  84. /* ============================================================================
  85. FByteRangesAreEqual
  86. Compares two byte ranges
  87. Returns:
  88. BOOLB. True if byte ranges are equal, else false.
  89. Side effects:
  90. None.
  91. */
  92. BOOLB
  93. FByteRangesAreEqual
  94. (
  95. CByteRange& br1,
  96. CByteRange& br2
  97. )
  98. {
  99. if(br1.m_cb != br2.m_cb)
  100. return FALSE;
  101. return (!_strnicmp((LPCSTR)br1.m_pb, (LPCSTR)br2.m_pb, br1.m_cb));
  102. }
  103. /* ============================================================================
  104. CharAdvDBCS
  105. Advance "cchCharAdv" characters in a buffer
  106. SBCS: Degenerates to simple pointer arithmatic
  107. Arguments:
  108. wCodePage - code page
  109. pchStart - pointer to beginning of segment
  110. pchEnd - pointer to just past end of segment
  111. cCharAdv - # of characters to advance
  112. ppchEnd - [output], contains pointer "cCharAdv" chars past pchStart
  113. fForceDBCS - if TRUE, always use double byte algorithm.
  114. (for verifying correct behavior of func in debug mode)
  115. Returns:
  116. (int) # of characters that we actually advanced
  117. Notes:
  118. By passing INFINITE for "cCharAdv", you can use this function to count characters
  119. in a block
  120. Side effects:
  121. None.
  122. */
  123. unsigned
  124. CharAdvDBCS
  125. (
  126. WORD wCodePage,
  127. char *pchStart,
  128. char *pchEnd,
  129. unsigned cCharAdv,
  130. char **ppchEnd,
  131. BOOL fForceDBCS
  132. )
  133. {
  134. CPINFO CpInfo;
  135. GetCPInfo(wCodePage, &CpInfo);
  136. if (!fForceDBCS && CpInfo.MaxCharSize == 1)
  137. {
  138. char *pchT = pchStart + min(cCharAdv, unsigned(pchEnd - pchStart));
  139. if (ppchEnd)
  140. *ppchEnd = pchT;
  141. #if DBG
  142. // Verify DBCS algorithm (not often tested otherwise)
  143. char *pchTest;
  144. unsigned cchTest = CharAdvDBCS(wCodePage, pchStart, pchEnd, cCharAdv, &pchTest, TRUE);
  145. Assert (cchTest == unsigned(pchT - pchStart) && pchTest == pchT);
  146. #endif
  147. return DIFF(pchT - pchStart);
  148. }
  149. else
  150. {
  151. int cch = 0;
  152. char *pchNext = pchStart;
  153. // Count DBCS characters. We have to stop before pchEnd because
  154. // pchEnd may point past file map and CharNextExA AVs when advancing
  155. // past allocated memory
  156. while (cCharAdv > 0 && pchNext < pchEnd-2)
  157. {
  158. pchNext = *pchNext? AspCharNextA(wCodePage, pchNext) : pchNext + 1;
  159. --cCharAdv;
  160. ++cch;
  161. }
  162. // We could stop on the last or the before last character
  163. // depending on the DBCS char sequence
  164. if (cCharAdv > 0 && pchNext == pchEnd-1)
  165. {
  166. // Only one byte - has to be one single byte character
  167. ++pchNext;
  168. ++cch;
  169. }
  170. else if (cCharAdv > 0 && pchNext == pchEnd-2)
  171. {
  172. // 2 bytes left - either 1 2-byte char or 2 1-byte chars
  173. if (IsDBCSLeadByteEx(wCodePage, *pchNext))
  174. {
  175. ++cch;
  176. pchNext += 2;
  177. }
  178. else
  179. {
  180. // Two characters left. If cCharAdv > 1, this means that user wants to
  181. // advance at least two more chars. Otherwise, cCharAdv == 1, and
  182. // we advance one char
  183. //
  184. if (cCharAdv > 1)
  185. {
  186. cch += 2;
  187. pchNext += 2;
  188. }
  189. else
  190. {
  191. Assert (cCharAdv == 1);
  192. ++cch;
  193. ++pchNext;
  194. }
  195. }
  196. }
  197. if (ppchEnd)
  198. *ppchEnd = pchNext;
  199. return cch;
  200. }
  201. }
  202. /* ============================================================================
  203. LineFromByteRangeAdv
  204. Gets the first line in a byte range.
  205. Returns:
  206. Nothing
  207. Side effects:
  208. Advances source byte range to just beyond its first non-white-space line,
  209. if one is found.
  210. */
  211. void
  212. LineFromByteRangeAdv
  213. (
  214. CByteRange& brSource,
  215. CByteRange& brLine
  216. )
  217. {
  218. CByteRange brTemp;
  219. if(brSource.IsNull())
  220. {
  221. brLine.Nullify();
  222. return;
  223. }
  224. brLine.m_pb = brSource.m_pb;
  225. brTemp = BrNewLine(brSource);
  226. if(brTemp.IsNull())
  227. {
  228. // We found no newline in a non-empty byte range:
  229. // set line range to entire source byte range and empty source byte range
  230. brLine.m_cb = brSource.m_cb;
  231. brSource.Nullify();
  232. }
  233. else
  234. {
  235. // We found a newline in a non-empty byte range:
  236. // set line range to portion of source byte range before new line;
  237. // set source range to portion of source range after new line
  238. brLine.m_cb = DIFF(brTemp.m_pb - brSource.m_pb);
  239. brSource.m_pb = brTemp.m_pb + brTemp.m_cb;
  240. brSource.m_cb -= (brLine.m_cb + brTemp.m_cb);
  241. }
  242. }
  243. /* ============================================================================
  244. LTrimWhiteSpace
  245. Left-trim white space from byte-range
  246. Returns:
  247. Nothing
  248. Side effects:
  249. Advances byte range to just beyond its first non-white-space character.
  250. */
  251. void
  252. LTrimWhiteSpace
  253. (
  254. CByteRange& br
  255. )
  256. {
  257. if(br.IsNull())
  258. return;
  259. while(FWhiteSpace(*br.m_pb))
  260. {
  261. br.m_pb++;
  262. if(--br.m_cb == 0)
  263. return;
  264. }
  265. }
  266. /* ============================================================================
  267. RTrimWhiteSpace
  268. Right-trim white space from byte-range
  269. */
  270. void
  271. RTrimWhiteSpace(CByteRange& br)
  272. {
  273. if(br.IsNull())
  274. return;
  275. while(FWhiteSpace(*(br.m_pb + br.m_cb - 1)))
  276. {
  277. if(--br.m_cb == 0)
  278. return;
  279. }
  280. }
  281. /* ============================================================================
  282. BrNewLine
  283. Returns ptr to the first newline in a byte range
  284. NOTE does not change byte range (since it is passed by value)
  285. */
  286. CByteRange
  287. BrNewLine(CByteRange br)
  288. {
  289. while(!br.IsNull())
  290. {
  291. if(*br.m_pb == '\r')
  292. return CByteRange(br.m_pb, (br.m_cb > 1 && br.m_pb[1] == '\n')? 2 : 1);
  293. else if (*br.m_pb == '\n')
  294. return CByteRange(br.m_pb, 1);
  295. ++br.m_pb;
  296. --br.m_cb;
  297. }
  298. return CByteRange();
  299. }
  300. /* ============================================================================
  301. FWhiteSpace
  302. Returns:
  303. TRUE if ch is a white-space character, else returns FALSE
  304. Certain character(s) (e.g. space) may be treated as
  305. non-white-space; to do this, caller passes FALSE for
  306. fSpaceIsWhiteSpace flag.
  307. */
  308. BOOLB
  309. FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace)
  310. {
  311. switch (ch)
  312. {
  313. case ' ':
  314. return fSpaceIsWhiteSpace;
  315. case '\0':
  316. return TRUE;
  317. case '\a':
  318. return TRUE;
  319. case '\b':
  320. return TRUE;
  321. case '\f':
  322. return TRUE;
  323. case '\n':
  324. return TRUE;
  325. case '\r':
  326. return TRUE;
  327. case '\t':
  328. return TRUE;
  329. case '\v':
  330. return TRUE;
  331. default:
  332. return FALSE;
  333. }
  334. }
  335. /* ============================================================================
  336. FWhiteSpaceEx
  337. Returns:
  338. TRUE if ch is a white-space character, else returns FALSE
  339. Certain character(s) (e.g. space) may be treated as
  340. non-white-space; to do this, caller passes FALSE for
  341. fSpaceIsWhiteSpace flag.
  342. */
  343. BOOLB
  344. FWhiteSpaceEx(WCHAR wch, BOOLB fSpaceIsWhiteSpace)
  345. {
  346. switch (wch)
  347. {
  348. case L' ':
  349. return fSpaceIsWhiteSpace;
  350. case L'\0':
  351. return TRUE;
  352. case L'\a':
  353. return TRUE;
  354. case L'\b':
  355. return TRUE;
  356. case L'\f':
  357. return TRUE;
  358. case L'\n':
  359. return TRUE;
  360. case L'\r':
  361. return TRUE;
  362. case L'\t':
  363. return TRUE;
  364. case L'\v':
  365. return TRUE;
  366. default:
  367. return FALSE;
  368. }
  369. }
  370. /* ============================================================================
  371. FByteRangeIsWhiteSpace
  372. Is the entire input byte range white space?
  373. NOTE input byte range is byval; caller's copy is not changed
  374. */
  375. BOOLB
  376. FByteRangeIsWhiteSpace(CByteRange br)
  377. {
  378. while(!br.IsNull())
  379. {
  380. if(!FWhiteSpace(*(br.m_pb)))
  381. return FALSE;
  382. br.Advance(1);
  383. }
  384. return TRUE;
  385. }
  386. /* ============================================================================
  387. FTagName
  388. Does pb point to a valid HTML tag name?
  389. (i.e., is *pb a valid HTML tag name and not a substring?)
  390. Returns
  391. TRUE or FALSE
  392. Side effects
  393. None
  394. */
  395. BOOLB
  396. FTagName(BYTE* pb, UINT cb)
  397. {
  398. if((pb == NULL) || (cb == 0))
  399. return FALSE;
  400. // a valid HTML tag name must be preceded by white space ...
  401. if( FWhiteSpace(*(pb - 1)) || *(pb - 1) == '@' )
  402. {
  403. // ... and followed either by white space or the tag separator
  404. if(FWhiteSpace(*(pb + cb)))
  405. return TRUE;
  406. else if(*(pb + cb) == CH_ATTRIBUTE_SEPARATOR)
  407. return TRUE;
  408. }
  409. return FALSE;
  410. }
  411. /*===================================================================
  412. ByteAlignOffset
  413. Byte-aligns an offset value, based on size of source data
  414. */
  415. void
  416. ByteAlignOffset
  417. (
  418. UINT* pcbOffset, // ptr to offset value
  419. UINT cbAlignment // Alignment boundary
  420. )
  421. {
  422. // comment the below code out so that it works for 64 bit...
  423. // only byte-align for 2-, or 4-byte data
  424. // since our base pointer in only aligned to a 4 byte boundary
  425. //if(cbAlignment == 2 || cbAlignment == 4)
  426. //{
  427. // if current offset does not fall on a byte-aligned location for current data type,
  428. // advance offset to next byte-aligned location
  429. Assert(cbAlignment > 0);
  430. --cbAlignment;
  431. if (*pcbOffset & cbAlignment)
  432. *pcbOffset = (*pcbOffset + cbAlignment + 1) & ~cbAlignment;
  433. }
  434. /* ============================================================================
  435. GetSzFromPatternInserts
  436. Returns a 'resolved' version of a pattern string, i.e. a new string in which
  437. | characters have been replaced by caller-specified insert strings.
  438. NOTE this function allocates, but caller must free
  439. Returns:
  440. Nothing
  441. Side effects:
  442. allocates memory
  443. */
  444. void
  445. GetSzFromPatternInserts
  446. (
  447. char* pszPattern, // 'pattern' string
  448. UINT cInserts, // count of insert strings
  449. char** ppszInserts, // array of ptrs to insert strings
  450. char* szReturned // returned string MUST be allocated by caller
  451. )
  452. {
  453. UINT cchRet = strlen(pszPattern); // length of return string
  454. char* pchStartCopy = pszPattern; // ptr to start of copy range in pattern
  455. char* pchEndCopy = pszPattern; // ptr to end of copy range in pattern
  456. UINT cActualInserts = 0; // count of actual insert strings
  457. // init return string to empty so we can concatenate onto it
  458. szReturned[0] = NULL;
  459. // zero out length of return string - we now use it to count actual length as we build return string
  460. cchRet = 0;
  461. while(TRUE)
  462. {
  463. // advance end-of-copy ptr through pattern looking for insertion points or end of string
  464. while ((*pchEndCopy != NULL) && (IsDBCSLeadByte(*pchEndCopy) || (*pchEndCopy != '|')))
  465. pchEndCopy = CharNextA(pchEndCopy);
  466. // cat from start-of-copy to end-of-copy onto return string
  467. strncat(szReturned, pchStartCopy, DIFF(pchEndCopy - pchStartCopy));
  468. // update return string length
  469. cchRet += DIFF(pchEndCopy - pchStartCopy);
  470. // if we are at end of pattern, exit
  471. if(*pchEndCopy == NULL)
  472. goto Exit;
  473. if(cActualInserts < cInserts)
  474. {
  475. // if inserts remain, cat the next one onto return string
  476. strcat(szReturned, ppszInserts[cActualInserts]);
  477. // update return string length
  478. cchRet += strlen(ppszInserts[cActualInserts]);
  479. cActualInserts++;
  480. }
  481. // advance end-of-copy and start-of-copy beyond insertion point
  482. pchEndCopy++;
  483. pchStartCopy = pchEndCopy;
  484. }
  485. Exit:
  486. // null-terminate return string
  487. szReturned[cchRet] = NULL;
  488. }
  489. /* ============================================================================
  490. CchPathFromFilespec
  491. Returns a filespec's path length (exclusive of filespec)
  492. NOTE path string includes trailing '\' or '/'
  493. Returns:
  494. Length of path string
  495. Side effects:
  496. None
  497. */
  498. UINT
  499. CchPathFromFilespec
  500. (
  501. LPCTSTR szFileSpec // filespec
  502. )
  503. {
  504. TCHAR* p1 = _tcsrchr(szFileSpec, _T('\\'));
  505. TCHAR* p2 = _tcsrchr(szFileSpec, _T('/')); // this wont be a DBCS trail byte.
  506. if (p1 == NULL && p2 == NULL)
  507. THROW(E_FAIL);
  508. return (UINT) ((((LPTSTR)max(p1,p2) - szFileSpec)) + 1);
  509. }
  510. /* ============================================================================
  511. GetPathFromParentAndFilespec
  512. Returns an absolute path which is a 'parent' file's path concatenated with a filespec.
  513. Returns:
  514. absolute path (out-parameter)
  515. Side effects:
  516. None
  517. */
  518. void
  519. GetPathFromParentAndFilespec
  520. (
  521. LPCTSTR szParentPath, // parent path
  522. LPCTSTR szFileSpec, // filespec
  523. LPTSTR* pszPath // resolved path (out-parameter)
  524. )
  525. {
  526. UINT cchParentPath = CchPathFromFilespec(szParentPath);
  527. if ((cchParentPath + _tcslen(szFileSpec)) > MAX_PATH)
  528. THROW(E_FAIL);
  529. _tcsncpy(*pszPath, szParentPath, cchParentPath);
  530. _tcscpy(*pszPath + cchParentPath, szFileSpec);
  531. }
  532. /* ============================================================================
  533. HandleAccessFailure
  534. Handles an access-denied failure
  535. Returns:
  536. nothing
  537. Side effects:
  538. none
  539. */
  540. void
  541. HandleAccessFailure
  542. (
  543. CHitObj* pHitObj, // browser's hitobj
  544. TCHAR * szFile // file path of main template
  545. )
  546. {
  547. Assert(pHitObj);
  548. // debugging diagnostic print
  549. #if DBG
  550. STACK_BUFFER( authUserBuff, 32 );
  551. char *szAuthUser;
  552. DWORD cbAuthUser;
  553. if (SERVER_GET(pHitObj->PIReq(), "AUTH_USER", &authUserBuff, &cbAuthUser)) {
  554. szAuthUser = (char*)authUserBuff.QueryPtr();
  555. }
  556. else {
  557. szAuthUser = "anonymous";
  558. }
  559. #if UNICODE
  560. DBGPRINTF((DBG_CONTEXT, "No permission to read file %S\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated()));
  561. #else
  562. DBGPRINTF((DBG_CONTEXT, "No permission to read file %s\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated()));
  563. #endif
  564. DBGPRINTF((DBG_CONTEXT, " The user account is \"%s\"\n", szAuthUser));
  565. #endif
  566. CResponse *pResponse = pHitObj->PResponse();
  567. if (!pResponse)
  568. return;
  569. pHitObj->PIReq()->SetDwHttpStatusCode(401);
  570. HandleSysError(401,3,IDE_401_3_ACCESS_DENIED,IDH_401_3_ACCESS_DENIED,pHitObj->PIReq(),pHitObj);
  571. return;
  572. }
  573. /* ============================================================================
  574. SendToLog
  575. Sends Error Info to Log
  576. Returns:
  577. Nothing
  578. Side effects:
  579. None.
  580. */
  581. void
  582. SendToLog
  583. (
  584. DWORD dwMask,
  585. CHAR *szFileName,
  586. CHAR *szLineNum,
  587. CHAR *szEngine,
  588. CHAR *szErrCode,
  589. CHAR *szShortDes,
  590. CHAR *szLongDes,
  591. CHitObj *pHitObj // browser's hitobj
  592. )
  593. {
  594. CHAR *szFileNameT;
  595. CHAR *szLineNumT;
  596. CHAR *szEngineT;
  597. CHAR *szErrCodeT;
  598. CHAR *szShortDesT;
  599. CHAR *szLongDesT;
  600. if(pHitObj) {
  601. // NOTE - szFileName is assumed to be UTF8 when UNICODE is defined
  602. szFileNameT = StringDupA(szFileName);
  603. szLineNumT = StringDupA(szLineNum);
  604. szEngineT = StringDupA(szEngine);
  605. szErrCodeT = StringDupA(szErrCode);
  606. szShortDesT = StringDupA(szShortDes);
  607. szLongDesT = StringDupA(szLongDes);
  608. HandleError(szShortDesT, szLongDesT, dwMask, szFileNameT, szLineNumT, szEngineT, szErrCodeT, NULL, pHitObj);
  609. }
  610. }
  611. /* ============================================================================
  612. FreeNullify
  613. Frees and nullifies a ptr to memory allocated with malloc.
  614. Returns:
  615. Nothing
  616. Side effects:
  617. None
  618. */
  619. static void
  620. FreeNullify
  621. (
  622. void** pp
  623. )
  624. {
  625. if(*pp != NULL)
  626. {
  627. free(*pp);
  628. *pp = NULL;
  629. }
  630. }
  631. /* ============================================================================
  632. SmallTemplateFreeNullify
  633. Frees and nullifies a ptr to memory allocated with CTemplate::SmallMalloc.
  634. Returns:
  635. Nothing
  636. Side effects:
  637. None
  638. */
  639. static void
  640. SmallTemplateFreeNullify
  641. (
  642. void** pp
  643. )
  644. {
  645. if(*pp != NULL)
  646. {
  647. CTemplate::SmallFree(*pp);
  648. *pp = NULL;
  649. }
  650. }
  651. /* ============================================================================
  652. LargeTemplateFreeNullify
  653. Frees and nullifies a ptr to memory allocated with CTemplate::LargeMalloc.
  654. Returns:
  655. Nothing
  656. Side effects:
  657. None
  658. */
  659. static void
  660. LargeTemplateFreeNullify
  661. (
  662. void** pp
  663. )
  664. {
  665. if(*pp != NULL)
  666. {
  667. CTemplate::LargeFree(*pp);
  668. *pp = NULL;
  669. }
  670. }
  671. /* ============================================================================
  672. GetProgLangId
  673. Gets the prog lang id for a script engine
  674. Returns:
  675. Nothing
  676. Side effects:
  677. throws on error
  678. */
  679. HRESULT
  680. GetProgLangId
  681. (
  682. CByteRange& brEngine, // engine name
  683. PROGLANG_ID* pProgLangId // prog lang id (out-parameter)
  684. )
  685. {
  686. STACK_BUFFER( tempEngine, 128 );
  687. if (!tempEngine.Resize(brEngine.m_cb + 1)) {
  688. return E_OUTOFMEMORY;
  689. }
  690. LPSTR szProgLang = static_cast<LPSTR> (tempEngine.QueryPtr());
  691. strncpy(szProgLang, (LPCSTR)brEngine.m_pb, brEngine.m_cb);
  692. szProgLang[brEngine.m_cb] = '\0';
  693. return g_ScriptManager.ProgLangIdOfLangName((LPCSTR) szProgLang, pProgLangId);
  694. }
  695. /* ****************************************************************************
  696. CByteRange member functions
  697. */
  698. /* ========================================================
  699. CByteRange::Advance
  700. Advances a byte range.
  701. */
  702. void
  703. CByteRange::Advance(UINT i)
  704. {
  705. if(i >= m_cb)
  706. {
  707. Nullify();
  708. }
  709. else
  710. {
  711. m_pb += i;
  712. m_cb -= i;
  713. }
  714. }
  715. /* ========================================================
  716. CByteRange::FMatchesSz
  717. Compares a byte range with a string, case-insensitively
  718. */
  719. BOOLB
  720. CByteRange::FMatchesSz
  721. (
  722. LPCSTR psz
  723. )
  724. {
  725. if(IsNull() || (psz == NULL))
  726. return FALSE;
  727. if((ULONG)strlen(psz) != m_cb)
  728. return FALSE;
  729. return !_strnicmp((const char*)m_pb, psz, m_cb);
  730. }
  731. /* ============================================================================
  732. CByteRange::PbString
  733. Finds a case-insensitive string within a byte range
  734. Returns:
  735. Ptr to first case-insensitive occurrence of the string in this byte range;
  736. NULL if none found.
  737. Side effects:
  738. None
  739. */
  740. BYTE*
  741. CByteRange::PbString
  742. (
  743. LPSTR psz,
  744. LONG lCodePage
  745. )
  746. {
  747. UINT cch = strlen(psz);
  748. if(cch == 0)
  749. return NULL;
  750. BYTE *pbLocal = m_pb;
  751. UINT cbLocal = m_cb;
  752. char ch0 = psz[0];
  753. BYTE *pbTemp = NULL;
  754. UINT cbAdvanced = 0;
  755. if (IsCharAlpha(ch0))
  756. {
  757. // cannot use strchr
  758. while (cbLocal >= cch)
  759. {
  760. if (_strnicmp((const char *)pbLocal, psz, cch) == 0)
  761. return pbLocal;
  762. // The following code simply performs a DBCS-enabled ByteRange.Advance() action.
  763. pbTemp = pbLocal;
  764. pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1;
  765. cbAdvanced = DIFF(pbLocal - pbTemp);
  766. if (cbAdvanced >= cbLocal)
  767. {
  768. cbLocal = 0;
  769. pbLocal = NULL;
  770. }
  771. else
  772. cbLocal -= cbAdvanced;
  773. }
  774. }
  775. else
  776. {
  777. // can use strchr
  778. while (cbLocal >= cch)
  779. {
  780. pbTemp = (BYTE *)memchr(pbLocal, ch0, cbLocal);
  781. if (pbTemp == NULL)
  782. break;
  783. UINT cbOffset = DIFF(pbTemp - pbLocal);
  784. if (cbOffset >= cbLocal)
  785. break;
  786. pbLocal = pbTemp;
  787. cbLocal -= cbOffset;
  788. if (cch <= cbLocal && _strnicmp((const char *)pbLocal, psz, cch) == 0)
  789. return pbLocal;
  790. // The following code simply performs a DBCS-enabled ByteRange.Advance() action.
  791. pbTemp = pbLocal;
  792. pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1;
  793. cbAdvanced = DIFF(pbLocal - pbTemp);
  794. if (cbAdvanced >= cbLocal)
  795. {
  796. cbLocal = 0;
  797. pbLocal = NULL;
  798. }
  799. else
  800. cbLocal -= cbAdvanced;
  801. }
  802. }
  803. return NULL;
  804. }
  805. /* ============================================================================
  806. CByteRange::PbOneOfAspOpenerStringTokens
  807. Finds a case-insensitive string within a byte range
  808. that matches one of the strings passed
  809. !!! WILL ONLY WORK IF THE FOLLOWING IS TRUE:
  810. 1) All the tokens start with the same charater (for example '<')
  811. 2) This character is not alpha (so that strchr() would work)
  812. !!! THE ABOVE ASSUMPTIONS MAKE THE CODE WORK FASTER
  813. Returns:
  814. Ptr to first case-insensitive occurrence of the string in this byte range;
  815. NULL if none found.
  816. *pcindex is set to the index of string found
  817. Side effects:
  818. None
  819. */
  820. BYTE*
  821. CByteRange::PbOneOfAspOpenerStringTokens
  822. (
  823. LPSTR rgszTokens[],
  824. UINT rgcchTokens[],
  825. UINT nTokens,
  826. UINT *pidToken
  827. )
  828. {
  829. if (nTokens == 0)
  830. return NULL;
  831. BYTE *pb = m_pb; // pointer to unsearched remainder of the range
  832. UINT cbRemainder = m_cb; // remaining byte range length
  833. char ch0 = rgszTokens[0][0]; // first char of every token
  834. while (cbRemainder > 0) {
  835. // BUG 82331: avoid strchr() because byte range is not null-terminated
  836. while (cbRemainder > 0 && *pb != ch0)
  837. {
  838. ++pb;
  839. --cbRemainder;
  840. }
  841. if (cbRemainder == 0)
  842. break;
  843. for (UINT i = 0; i < nTokens; i++) {
  844. if ((rgcchTokens[i] <= cbRemainder)
  845. && (rgszTokens[i] != NULL)
  846. && (_strnicmp((const char *)pb, rgszTokens[i], rgcchTokens[i]) == 0)) {
  847. *pidToken = i;
  848. return pb;
  849. }
  850. }
  851. ++pb;
  852. --cbRemainder;
  853. }
  854. return NULL;
  855. }
  856. /* ============================================================================
  857. CByteRange::FEarlierInSourceThan
  858. Does this byte range occur earlier in source than parameter byte range?
  859. Returns
  860. TRUE or FALSE
  861. Side effects
  862. None
  863. */
  864. BOOLB
  865. CByteRange::FEarlierInSourceThan(CByteRange& br)
  866. {
  867. if(br.IsNull())
  868. return TRUE;
  869. return(m_idSequence < br.m_idSequence);
  870. }
  871. /* ****************************************************************************
  872. CTemplate member functions
  873. */
  874. /* ============================================================================
  875. CTemplate::CTemplate
  876. Ctor
  877. */
  878. CTemplate::CTemplate()
  879. : m_pWorkStore(NULL),
  880. m_fGlobalAsa(FALSE),
  881. m_fReadyForUse(FALSE),
  882. m_fDontAttach(FALSE),
  883. m_hEventReadyForUse(NULL),
  884. m_fDebuggerDetachCSInited(FALSE),
  885. m_pbStart(NULL),
  886. m_cbTemplate(0),
  887. m_cRefs(1), // NOTE ctor AddRefs implicitly
  888. m_pbErrorLocation(NULL),
  889. m_idErrMsg(0),
  890. m_cMsgInserts(0),
  891. m_ppszMsgInserts(NULL),
  892. m_cScriptEngines(0),
  893. m_rgrgSourceInfos(NULL),
  894. m_rgpDebugScripts(NULL),
  895. m_rgpFilemaps(NULL),
  896. m_cFilemaps(0),
  897. m_rgpSegmentFilemaps(NULL),
  898. m_cSegmentFilemapSlots(0),
  899. m_wCodePage(CP_ACP),
  900. m_lLCID(LOCALE_SYSTEM_DEFAULT),
  901. m_ttTransacted(ttUndefined),
  902. m_fSession(TRUE),
  903. m_fScriptless(FALSE),
  904. m_fDebuggable(FALSE),
  905. m_fIsValid(FALSE),
  906. m_fDontCache(FALSE),
  907. m_fZombie(FALSE),
  908. m_fCodePageSet(FALSE),
  909. m_fLCIDSet(FALSE),
  910. m_fIsPersisted(FALSE),
  911. m_fIsUNC(FALSE),
  912. m_fIsEncrypted(FALSE),
  913. m_szPersistTempName(NULL),
  914. m_szApplnVirtPath(NULL),
  915. m_szApplnURL(NULL),
  916. m_CPTextEvents(this, IID_IDebugDocumentTextEvents),
  917. m_pdispTypeLibWrapper(NULL),
  918. m_dwLastErrorMask(S_OK),
  919. m_hrOnNoCache(S_OK),
  920. m_cbTargetOffsetPrevT(0),
  921. m_pHashTable(NULL),
  922. m_pServicesConfig(NULL),
  923. m_cUseCount(1),
  924. m_pMostRecentImpersonatedTokenUser (NULL),
  925. m_pMostRecentImpersonatedSID(NULL),
  926. m_cbTokenUser(0),
  927. m_fNeedsMonitoring(FALSE),
  928. m_fInCheck(FALSE),
  929. m_dwLastMonitored (0),
  930. m_dwLastAccessCheck(0),
  931. m_fTemplateLockInited(FALSE),
  932. m_dwCacheTag(0)
  933. {
  934. m_wCodePage = GetACP();
  935. for (UINT i = 0; i < ILE_MAX; i++)
  936. {
  937. m_pszLastErrorInfo[i] = NULL;
  938. }
  939. IF_DEBUG(TEMPLATE)
  940. {
  941. WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
  942. }
  943. #if PER_TEMPLATE_REFLOG
  944. m_pTraceLog = CreateRefTraceLog (100,0);
  945. WriteRefTraceLog (m_pTraceLog,m_cRefs, this);
  946. #endif
  947. }
  948. /* ============================================================================
  949. CTemplate::~CTemplate
  950. Destructor
  951. Returns:
  952. Nothing
  953. Side effects:
  954. None
  955. */
  956. CTemplate::~CTemplate()
  957. {
  958. DBGPRINTF(( DBG_CONTEXT, "Deleting template, m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps));
  959. // first, remove this template from its inc-files' template lists
  960. // NOTE must do this before freeing template memory
  961. RemoveFromIncFiles();
  962. // Remove the template from the debugger's list of documents
  963. Detach();
  964. PersistCleanup();
  965. if(m_rgpFilemaps)
  966. {
  967. for(UINT i = 0; i < m_cFilemaps; i++)
  968. delete m_rgpFilemaps[i];
  969. SmallTemplateFreeNullify((void**) &m_rgpFilemaps);
  970. }
  971. FreeGoodTemplateMemory();
  972. if (m_pWorkStore)
  973. delete m_pWorkStore;
  974. //FileName, LineNum, Engine, ErrorCode, ShortDes, LongDes
  975. for(UINT iErrInfo = 0; iErrInfo < ILE_MAX; iErrInfo++)
  976. {
  977. FreeNullify((void**) &m_pszLastErrorInfo[iErrInfo]);
  978. }
  979. if(m_hEventReadyForUse != NULL)
  980. CloseHandle(m_hEventReadyForUse);
  981. if (m_LKHashKey.szPathTranslated)
  982. free((void *)m_LKHashKey.szPathTranslated);
  983. if (m_szApplnURL)
  984. delete [] m_szApplnURL;
  985. if (m_fDebuggerDetachCSInited)
  986. DeleteCriticalSection(&m_csDebuggerDetach);
  987. if (m_fTemplateLockInited)
  988. DeleteCriticalSection(&m_csTemplateLock);
  989. if (m_pdispTypeLibWrapper)
  990. m_pdispTypeLibWrapper->Release();
  991. if (m_szPersistTempName)
  992. CTemplate::LargeFree(m_szPersistTempName);
  993. if (m_pMostRecentImpersonatedTokenUser)
  994. CTemplate::SmallFree(m_pMostRecentImpersonatedTokenUser);
  995. if (m_pServicesConfig)
  996. m_pServicesConfig->Release();
  997. m_pServicesConfig = NULL;
  998. #if PER_TEMPLATE_REFLOG
  999. DestroyRefTraceLog (m_pTraceLog);
  1000. #endif
  1001. }
  1002. /* ============================================================================
  1003. CTemplate::QueryInterface
  1004. Provides QueryInterface implementation for CTemplate
  1005. NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo.
  1006. */
  1007. HRESULT
  1008. CTemplate::QueryInterface(const GUID &uidInterface, void **ppvObj)
  1009. {
  1010. if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider)
  1011. *ppvObj = static_cast<IDebugDocumentProvider *>(this);
  1012. else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText)
  1013. *ppvObj = static_cast<IDebugDocumentText *>(this);
  1014. else if (uidInterface == IID_IConnectionPointContainer)
  1015. *ppvObj = static_cast<IConnectionPointContainer *>(this);
  1016. else
  1017. *ppvObj = NULL;
  1018. if (*ppvObj)
  1019. {
  1020. AddRef();
  1021. return S_OK;
  1022. }
  1023. else
  1024. return E_NOINTERFACE;
  1025. }
  1026. /* ============================================================================
  1027. CTemplate::AddRef
  1028. Adds a ref to this template, thread-safely
  1029. */
  1030. ULONG
  1031. CTemplate::AddRef()
  1032. {
  1033. LONG cRefs = InterlockedIncrement(&m_cRefs);
  1034. Assert(FImplies(m_fIsValid,FImplies(cRefs > 1, m_pbStart != NULL)));
  1035. IF_DEBUG(TEMPLATE)
  1036. {
  1037. WriteRefTraceLog(gm_pTraceLog, cRefs, this);
  1038. }
  1039. #if PER_TEMPLATE_REFLOG
  1040. WriteRefTraceLog(m_pTraceLog, cRefs, this);
  1041. #endif
  1042. return cRefs;
  1043. }
  1044. /* ============================================================================
  1045. CTemplate::Release
  1046. Releases a ref to this template, thread-safely
  1047. */
  1048. ULONG
  1049. CTemplate::Release()
  1050. {
  1051. LONG cRefs = InterlockedDecrement(&m_cRefs);
  1052. IF_DEBUG(TEMPLATE)
  1053. {
  1054. WriteRefTraceLog(gm_pTraceLog, cRefs, this);
  1055. }
  1056. #if PER_TEMPLATE_REFLOG
  1057. WriteRefTraceLog(m_pTraceLog, cRefs, this);
  1058. #endif
  1059. if (cRefs == 0)
  1060. delete this;
  1061. return cRefs;
  1062. }
  1063. /* ============================================================================
  1064. CTemplate::InitClass
  1065. Initilaizes CTemplate static members
  1066. Returns:
  1067. hresult
  1068. Side effects:
  1069. allocates memory for static members
  1070. */
  1071. HRESULT
  1072. CTemplate::InitClass
  1073. (
  1074. )
  1075. {
  1076. HRESULT hr = S_OK;
  1077. TRY
  1078. // init heaps
  1079. sm_hSmallHeap = ::HeapCreate(0, 0, 0);
  1080. if (!sm_hSmallHeap)
  1081. return E_OUTOFMEMORY;
  1082. sm_hLargeHeap = ::HeapCreate(0, 0, 0);
  1083. if (!sm_hLargeHeap)
  1084. return E_OUTOFMEMORY;
  1085. // Init token list
  1086. gm_pTokenList = new CTokenList;
  1087. if (gm_pTokenList == NULL)
  1088. return E_OUTOFMEMORY;
  1089. gm_pTokenList->Init();
  1090. CATCH(hrException)
  1091. hr = hrException;
  1092. END_TRY
  1093. return hr;
  1094. }
  1095. /* ============================================================================
  1096. CTemplate::UnInitClass
  1097. Un-initilaizes CTemplate static members
  1098. Returns:
  1099. Nothing
  1100. Side effects:
  1101. None
  1102. */
  1103. void
  1104. CTemplate::UnInitClass()
  1105. {
  1106. if (gm_pTokenList)
  1107. {
  1108. delete gm_pTokenList;
  1109. gm_pTokenList = NULL;
  1110. }
  1111. if (sm_hLargeHeap)
  1112. ::HeapDestroy(sm_hLargeHeap);
  1113. if (sm_hLargeHeap != sm_hSmallHeap)
  1114. {
  1115. if (sm_hSmallHeap)
  1116. ::HeapDestroy(sm_hSmallHeap);
  1117. }
  1118. sm_hLargeHeap = sm_hSmallHeap = NULL;
  1119. }
  1120. /* ============================================================================
  1121. CTemplate::Init
  1122. Inits template in preparation for calling Compile
  1123. Does the minimum needed
  1124. Returns:
  1125. Success or failure code
  1126. Side effects:
  1127. Allocates memory
  1128. */
  1129. HRESULT
  1130. CTemplate::Init
  1131. (
  1132. CHitObj *pHitObj, // ptr to template's hit object
  1133. BOOL fGlobalAsa, // is this the global.asa file?
  1134. const CTemplateKey &rTemplateKey // hash table key
  1135. )
  1136. {
  1137. HRESULT hr;
  1138. // Create debug critical section
  1139. ErrInitCriticalSection(&m_csDebuggerDetach, hr);
  1140. if (SUCCEEDED(hr))
  1141. {
  1142. ErrInitCriticalSection(&m_csTemplateLock,hr);
  1143. if (FAILED(hr))
  1144. {
  1145. DeleteCriticalSection(&m_csDebuggerDetach);
  1146. return hr;
  1147. }
  1148. // note critical section creation success
  1149. m_fDebuggerDetachCSInited = TRUE;
  1150. m_fTemplateLockInited = TRUE;
  1151. }
  1152. else //FAILED(hr)
  1153. return hr;
  1154. // Create event: manual-reset, ready-for-use event; non-signaled
  1155. m_hEventReadyForUse = IIS_CREATE_EVENT(
  1156. "CTemplate::m_hEventReadyForUse",
  1157. this,
  1158. TRUE, // flag for manual-reset event
  1159. FALSE // flag for initial state
  1160. );
  1161. if (!m_hEventReadyForUse)
  1162. return E_OUTOFMEMORY;
  1163. // cache GlobalAsp flag
  1164. m_fGlobalAsa = BOOLB(fGlobalAsa);
  1165. // CIsapiReqInfo better be present
  1166. if (pHitObj->PIReq() == NULL)
  1167. return E_POINTER;
  1168. // Initialize the template's code page
  1169. m_wCodePage = pHitObj->PAppln()->QueryAppConfig()->uCodePage();
  1170. m_lLCID = pHitObj->PAppln()->QueryAppConfig()->uLCID();
  1171. STACK_BUFFER( serverNameBuff, 32 );
  1172. STACK_BUFFER( serverPortBuff, 10 );
  1173. STACK_BUFFER( portSecureBuff, 8 );
  1174. DWORD cbServerName;
  1175. DWORD cbServerPort;
  1176. DWORD cbServerPortSecure;
  1177. // Construct a URL for the application
  1178. // Get the server name and port
  1179. if (!SERVER_GET(pHitObj->PIReq(), "SERVER_NAME", &serverNameBuff, &cbServerName)
  1180. || !SERVER_GET(pHitObj->PIReq(), "SERVER_PORT", &serverPortBuff, &cbServerPort)) {
  1181. if (GetLastError() == ERROR_OUTOFMEMORY) {
  1182. hr = E_OUTOFMEMORY;
  1183. }
  1184. else {
  1185. hr = E_FAIL;
  1186. }
  1187. return hr;
  1188. }
  1189. char *szServerPort = (char *)serverPortBuff.QueryPtr();
  1190. char *szServerName = (char *)serverNameBuff.QueryPtr();
  1191. BOOL fServerPortSecure = FALSE;
  1192. // determine if server port is secure
  1193. if (SERVER_GET(pHitObj->PIReq(), "SERVER_PORT_SECURE", &portSecureBuff, &cbServerPortSecure)) {
  1194. char *szServerPortSecure = (char *)portSecureBuff.QueryPtr();
  1195. fServerPortSecure = (szServerPortSecure[0] == '1');
  1196. }
  1197. // Get the application virtual path
  1198. TCHAR szApplnVirtPath[256];
  1199. if (FAILED(hr = FindApplicationPath(pHitObj->PIReq(), szApplnVirtPath, sizeof szApplnVirtPath)))
  1200. return hr;
  1201. TCHAR *szServerNameT;
  1202. TCHAR *szServerPortT;
  1203. #if UNICODE
  1204. CMBCSToWChar convServer;
  1205. if (FAILED(hr = convServer.Init(szServerName))) {
  1206. return hr;
  1207. }
  1208. szServerNameT = convServer.GetString();
  1209. #else
  1210. szServerNameT = szServerName;
  1211. #endif
  1212. #if UNICODE
  1213. CMBCSToWChar convPort;
  1214. if (FAILED(hr = convPort.Init(szServerPort))) {
  1215. return hr;
  1216. }
  1217. szServerPortT = convPort.GetString();
  1218. #else
  1219. szServerPortT = szServerPort;
  1220. #endif
  1221. // Allocate space for and construct the application URL
  1222. m_szApplnURL = new TCHAR [(9 /* sizeof "https://:" */ + _tcslen(szServerNameT) + _tcslen(szServerPortT) + _tcslen(szApplnVirtPath) + 1)];
  1223. if (m_szApplnURL == NULL)
  1224. return E_OUTOFMEMORY;
  1225. TCHAR *pT;
  1226. // start with the protocol prefix...
  1227. pT = strcpyEx(m_szApplnURL, fServerPortSecure? _T("https://") : _T("http://"));
  1228. // next add the servername
  1229. pT = strcpyEx(pT, szServerNameT);
  1230. // next the colon between the servername and the serverport
  1231. pT = strcpyEx(pT, _T(":"));
  1232. // next the server port
  1233. pT = strcpyEx(pT, szServerPortT);
  1234. // now the applURL is built up to the appln path. The next step will be to
  1235. // add the virtpath.
  1236. m_szApplnVirtPath = pT;
  1237. _tcscpy(m_szApplnVirtPath, szApplnVirtPath);
  1238. m_LKHashKey.dwInstanceID = rTemplateKey.dwInstanceID;
  1239. if ((m_LKHashKey.szPathTranslated = StringDup((TCHAR *)rTemplateKey.szPathTranslated)) == NULL)
  1240. return E_OUTOFMEMORY;
  1241. return S_OK;
  1242. }
  1243. /* ============================================================================
  1244. CTemplate::Compile
  1245. Compiles the template from its source file and include files, if any,
  1246. by calling GetSegmentsFromFile (to populate WorkStore),
  1247. followed by WriteTemplate (to create the template from WorkStore).
  1248. Returns:
  1249. HRESULT indicating success or type of failure
  1250. Side effects:
  1251. Indirectly allocates memory (via WriteTemplate)
  1252. Indirectly frees memory on error (via FreeGoodTemplateMemory)
  1253. */
  1254. HRESULT
  1255. CTemplate::Compile
  1256. (
  1257. CHitObj* pHitObj
  1258. )
  1259. {
  1260. HRESULT hr = S_OK;
  1261. HRESULT hRes = S_OK;
  1262. // The following code moved from Init() (to make Init() lighter)
  1263. Assert(pHitObj);
  1264. // Create and Init WorkStore
  1265. if (SUCCEEDED(hr))
  1266. {
  1267. // construct the workstore - bail on fail
  1268. if(NULL == (m_pWorkStore = new CWorkStore))
  1269. hr = E_OUTOFMEMORY;
  1270. }
  1271. if (SUCCEEDED(hr))
  1272. {
  1273. hr = (m_pWorkStore->m_ScriptStore).Init(pHitObj->QueryAppConfig()->szScriptLanguage(),
  1274. pHitObj->QueryAppConfig()->pCLSIDDefaultEngine());
  1275. if (hr == TYPE_E_ELEMENTNOTFOUND)
  1276. {
  1277. // default script language in registry is bogus - send error msg to browser
  1278. HandleCTemplateError(
  1279. NULL, // source file map
  1280. NULL, // ptr to source location where error occurred
  1281. IDE_TEMPLATE_BAD_PROGLANG_IN_REGISTRY, // error message id
  1282. 0, // count of insert strings for error msg
  1283. NULL, // array of ptrs to error msg insert strings
  1284. pHitObj // Browser Request
  1285. );
  1286. }
  1287. if (FAILED(hr))
  1288. {
  1289. delete m_pWorkStore;
  1290. m_pWorkStore = NULL;
  1291. }
  1292. }
  1293. // Try to init the workstore and map main file - this can fail with oom, etc or user lacks permissions
  1294. if (SUCCEEDED(hr))
  1295. {
  1296. TRY
  1297. m_pWorkStore->Init();
  1298. AppendMapFile(
  1299. NULL, // file spec for this file - NULL means get filespec from pHitObj
  1300. NULL, // ptr to filemap of parent file
  1301. FALSE, // don't care
  1302. pHitObj, // ptr to template's hit object
  1303. m_fGlobalAsa // is this the global.asa file?
  1304. );
  1305. CATCH(hrException)
  1306. delete m_pWorkStore;
  1307. m_pWorkStore = NULL;
  1308. hr = hrException;
  1309. if(hr == E_USER_LACKS_PERMISSIONS)
  1310. HandleAccessFailure(pHitObj,
  1311. (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  1312. if ((hr != E_COULDNT_OPEN_SOURCE_FILE) && m_rgpFilemaps && m_rgpFilemaps[0])
  1313. {
  1314. // empty file will fail to map but will have a handle, so we check for it here
  1315. if (0 == m_rgpFilemaps[0]->GetSize())
  1316. hr = E_SOURCE_FILE_IS_EMPTY;
  1317. m_rgpFilemaps[0]->UnmapFile();
  1318. }
  1319. if (SUCCEEDED(hr))
  1320. hr = E_FAIL; // make sure the error is set
  1321. END_TRY
  1322. }
  1323. if (SUCCEEDED(hr))
  1324. {
  1325. Assert(m_rgpFilemaps[0]);
  1326. Assert(m_rgpFilemaps[0]->m_szPathTranslated);
  1327. Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath()))));
  1328. Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath()))));
  1329. Assert(0 < m_rgpFilemaps[0]->GetSize());
  1330. }
  1331. // Record the user who compiled this file (used specifically in UNC). Cannot proceed if there was an error (typically OOM) or Access problem.
  1332. if (FAILED(hr) || FAILED (hRes = GetSIDFromTokenHandle(pHitObj->HImpersonate(),
  1333. m_pMostRecentImpersonatedSID,
  1334. m_pMostRecentImpersonatedTokenUser,
  1335. &m_cbTokenUser)
  1336. )
  1337. )
  1338. {
  1339. m_fDontCache = TRUE;
  1340. // OK, cache HR if m_fDontCache is true
  1341. // later, another thread might find this template from the cache even if the template
  1342. // has some error and marked as DontCache.
  1343. m_hrOnNoCache = FAILED(hr) ? hr : hRes;
  1344. m_fReadyForUse = TRUE;
  1345. SetEvent(m_hEventReadyForUse);
  1346. return hr;
  1347. }
  1348. // End of Code moved from Init()
  1349. // By default we are not in a transaction
  1350. m_ttTransacted = ttUndefined;
  1351. // By default session is required
  1352. m_fSession = TRUE;
  1353. // By default assume script exists
  1354. m_fScriptless = FALSE;
  1355. // we assert, in effect, that template is already init'ed
  1356. Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath()))));
  1357. Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath()))));
  1358. TRY
  1359. // Get source segments from source file
  1360. GetSegmentsFromFile(*(m_rgpFilemaps[0]), *m_pWorkStore, pHitObj);
  1361. /* get "language equivalents" for primary languagefrom registry
  1362. NOTE we do this here because the user can reset the primary language in the script file,
  1363. so we must wait until after GetSegmentsFromFile()
  1364. */
  1365. GetLanguageEquivalents();
  1366. // Call WriteTemplate, which writes out template components to contiguous memory,
  1367. // resulting in a compiled template
  1368. // CWriteTemplate...class does the processing and is freed when the Compile completes.
  1369. // However, it leaves the compiled template in the heap pointed to by m_pbStart (sideeffect).
  1370. CWriteTemplate writeTempl;
  1371. // Initialize the WriteTemplate object
  1372. writeTempl.Init (m_pWorkStore, this, pHitObj->PAppln()->QueryAppConfig()->fCalcLineNumber());
  1373. // Perform estimation and write template
  1374. writeTempl.WriteTemplate();
  1375. // Record Compilation Cache Tag.
  1376. m_dwCacheTag = g_TemplateCache.GetCacheTag();
  1377. m_dwLastAccessCheck = GetTickCount();
  1378. //
  1379. // If any of the Include files are on a UNC or are encrypted then reflect that in the template structure
  1380. //
  1381. for (unsigned i = 0; i < m_cFilemaps; ++i)
  1382. {
  1383. m_fIsUNC |= m_rgpFilemaps[i]->m_fIsUNCPath;
  1384. m_fIsEncrypted |= m_rgpFilemaps[i]->m_fIsEncryptedFile;
  1385. m_rgpFilemaps[i]->CountChars((WORD)m_wCodePage);
  1386. }
  1387. // Wrap typelibs into single IDispatch*
  1388. WrapTypeLibs(pHitObj);
  1389. m_fIsValid = TRUE;
  1390. CATCH(hrException)
  1391. // NOTE: we used to free template memory here. Now we do not because if the
  1392. // error was E_USER_LACKS_PERMISSIONS, and template is in cache, we don't want
  1393. // to sabotage future requests. There's no need to decache the template.
  1394. //
  1395. // The template destructor will free this memory anyway.
  1396. //
  1397. hr = hrException;
  1398. END_TRY
  1399. // check if scriptless
  1400. if (!m_fGlobalAsa)
  1401. {
  1402. // count various stuff to make the determination
  1403. DWORD cScriptEngines = m_pWorkStore->m_ScriptStore.CountPreliminaryEngines();
  1404. DWORD cPrimaryScriptSegments = (cScriptEngines > 0) ? m_pWorkStore->m_ScriptStore.m_ppbufSegments[0]->Count() : 0;
  1405. DWORD cObjectTags = m_pWorkStore->m_ObjectInfoStore.Count();
  1406. DWORD cHtmlSegments = m_pWorkStore->m_bufHTMLSegments.Count();
  1407. DWORD c449Cookies = m_rgp449.length();
  1408. BOOL fPageCommandsPresent = m_pWorkStore->m_fPageCommandsExecuted;
  1409. if (cScriptEngines <= 1 &&
  1410. cPrimaryScriptSegments == 0 &&
  1411. cObjectTags == 0 &&
  1412. cHtmlSegments == 1 &&
  1413. c449Cookies == 0 &&
  1414. !fPageCommandsPresent)
  1415. {
  1416. m_fScriptless = TRUE;
  1417. }
  1418. }
  1419. // free working storage - no longer needed
  1420. delete m_pWorkStore;
  1421. m_pWorkStore = NULL;
  1422. // un-map filemaps - NOTE filemaps stay around for possible post-compile errors (e.g., script failure)
  1423. UnmapFiles();
  1424. // Debugging: print data structure to debugger
  1425. IF_DEBUG(SCRIPT_DEBUGGER)
  1426. {
  1427. if (SUCCEEDED(hr))
  1428. {
  1429. DBGPRINTF((DBG_CONTEXT, "Script Compiled\n"));
  1430. for (UINT i = 0; i < m_cScriptEngines; ++i)
  1431. {
  1432. char *szEngineName;
  1433. PROGLANG_ID *pProgLangID;
  1434. const wchar_t *wszScriptText;
  1435. GetScriptBlock(i, &szEngineName, &pProgLangID, &wszScriptText);
  1436. DBGPRINTF((DBG_CONTEXT, "Engine %d, Language=\"%s\":\n", i, szEngineName));
  1437. DBGINFO((DBG_CONTEXT, (char *) wszScriptText));
  1438. DBGINFO((DBG_CONTEXT, "\n"));
  1439. }
  1440. }
  1441. }
  1442. if (hr == E_TEMPLATE_COMPILE_FAILED_DONT_CACHE)
  1443. {
  1444. m_fDontCache = TRUE;
  1445. m_hrOnNoCache = hr;
  1446. }
  1447. // Set ready-for-use flag true and event to signaled
  1448. // NOTE we do this whether success or failure, since even a failed-compile template
  1449. // will remain in the cache to allow template cache mgr to satisfy requests on it
  1450. m_fReadyForUse = TRUE;
  1451. SetEvent(m_hEventReadyForUse);
  1452. // Note whether the template currently is debuggable
  1453. // BUG BUG: Template is debuggable or not based on first app. If shared between a debug
  1454. // & non-debug app, the first application wins.
  1455. m_fDebuggable = (BOOLB)!!pHitObj->PAppln()->FDebuggable();
  1456. return hr;
  1457. }
  1458. /* ============================================================================
  1459. CTemplate::Deliver
  1460. Delivers template to caller once template is ready for use
  1461. NOTE 'compile failure' == template is 'ready for use' but did not compile successfully;
  1462. this allows cache mgr to keep a failed template in cache in case it gets requested again
  1463. Returns
  1464. success or failure
  1465. Side effects
  1466. none
  1467. */
  1468. HRESULT
  1469. CTemplate::Deliver
  1470. (
  1471. CHitObj* pHitObj
  1472. )
  1473. {
  1474. // NOTE: There was a compiler bug where 'ps' would not be correctly aligned,
  1475. // EVEN if it was declared to be a DWORD array, if 'ps' was nested in
  1476. // a block. Thus declare it here.
  1477. //
  1478. BYTE ps[SIZE_PRIVILEGE_SET]; // privilege set
  1479. HRESULT hr = S_OK;
  1480. BOOL fImpersonatedUser = FALSE;
  1481. HANDLE hVirtIncImpToken = NULL;
  1482. HANDLE hCurImpToken = NULL;
  1483. // if ready flag is not yet set block until template is ready for use
  1484. if(!m_fReadyForUse)
  1485. {
  1486. WaitForSingleObject(m_hEventReadyForUse, INFINITE);
  1487. Assert(m_fReadyForUse); // when event unblocks, flag will be set
  1488. }
  1489. if (m_pbStart == NULL)
  1490. {
  1491. if (m_fDontCache && m_dwLastErrorMask == 0)
  1492. {
  1493. DBGPRINTF((DBG_CONTEXT, "template compile failed with %08x\n", m_hrOnNoCache));
  1494. DBG_ASSERT(FAILED(m_hrOnNoCache));
  1495. // Safety net: always fail, even if "m_hrOnNoCache" did not get set somehow.
  1496. hr = m_hrOnNoCache;
  1497. if (SUCCEEDED(m_hrOnNoCache))
  1498. hr = E_FAIL;
  1499. if(hr == E_USER_LACKS_PERMISSIONS)
  1500. HandleAccessFailure(pHitObj, (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  1501. return hr;
  1502. }
  1503. // template compile failed - NOTE null start-of-template ptr == template compile failed
  1504. // use cached error info
  1505. SendToLog( m_dwLastErrorMask,
  1506. m_pszLastErrorInfo[ILE_szFileName],
  1507. m_pszLastErrorInfo[ILE_szLineNum],
  1508. m_pszLastErrorInfo[ILE_szEngine],
  1509. m_pszLastErrorInfo[ILE_szErrorCode],
  1510. m_pszLastErrorInfo[ILE_szShortDes],
  1511. m_pszLastErrorInfo[ILE_szLongDes],
  1512. pHitObj);
  1513. hr = E_TEMPLATE_COMPILE_FAILED;
  1514. }
  1515. else if (!pHitObj->FIsBrowserRequest())
  1516. {
  1517. return hr;
  1518. }
  1519. else
  1520. // template compile succeeded - check user's file permissions
  1521. // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project)
  1522. {
  1523. HANDLE hUserAccessToken = pHitObj->HImpersonate(); // current user's access token
  1524. DWORD dwPS = sizeof(ps); // privilege set size
  1525. DWORD dwGrantedAccess; // granted access mask
  1526. BOOL fAccessGranted; // access granted flag
  1527. GENERIC_MAPPING gm = { // generic mapping struct
  1528. FILE_GENERIC_READ,
  1529. FILE_GENERIC_WRITE,
  1530. FILE_GENERIC_EXECUTE,
  1531. FILE_ALL_ACCESS
  1532. };
  1533. ((PRIVILEGE_SET*)ps)->PrivilegeCount = 0; // set privilege count to 0
  1534. Assert(NULL != hUserAccessToken);
  1535. HRESULT HRes = E_FAIL;
  1536. BOOL fNeedsUpdate = FALSE;
  1537. PSID pPrevSid = NULL;
  1538. PSID pSid=NULL;
  1539. DWORD cbTempTokenBuffer = 0;
  1540. LPVOID pvTempTokenBuffer = NULL;
  1541. DWORD dwAccessCheckTimeStamp = 0;
  1542. //
  1543. // Obtain a lock on the template so that only one Deliver will update the credentials while the others will just fall through
  1544. // Only one process should write the timestamp/impersonation token.
  1545. // If the File is either UNC or is Encrypted then we need to go through all the access check doors
  1546. //
  1547. if (m_fIsUNC)
  1548. {
  1549. EnterCriticalSection(&m_csTemplateLock);
  1550. pPrevSid = m_pMostRecentImpersonatedSID;
  1551. dwAccessCheckTimeStamp = m_dwLastAccessCheck;
  1552. LeaveCriticalSection(&m_csTemplateLock);
  1553. // GetCurrent Users Impersonation Token
  1554. HRes = GetSIDFromTokenHandle(pHitObj->HImpersonate(), pSid,pvTempTokenBuffer, &cbTempTokenBuffer);
  1555. }
  1556. for(UINT i = 0; i < m_cFilemaps; i++)
  1557. {
  1558. if (!(m_rgpFilemaps[i]->FHasUNCPath()))
  1559. {
  1560. if(NULL == m_rgpFilemaps[i]->m_pSecurityDescriptor) //? Dunno why we should continue. This could be a security Issue?
  1561. continue;
  1562. if(!AccessCheck(
  1563. m_rgpFilemaps[i]->m_pSecurityDescriptor, // pointer to security descriptor
  1564. hUserAccessToken, // handle to client access token
  1565. FILE_GENERIC_READ, // access mask to request
  1566. &gm, // address of generic-mapping structure
  1567. (PRIVILEGE_SET*)ps, // address of privilege-set structure
  1568. &dwPS, // address of size of privilege-set structure
  1569. &dwGrantedAccess, // address of granted access mask
  1570. &fAccessGranted // address of flag indicating whether access granted
  1571. ))
  1572. return E_FAIL;
  1573. if(!fAccessGranted)
  1574. {
  1575. // if access is denied on any file, handle the failure and return
  1576. HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated);
  1577. return E_USER_LACKS_PERMISSIONS;
  1578. }
  1579. }
  1580. else //if it is a UNC
  1581. {
  1582. //
  1583. // Get the SID for current thread and compare with compiled/lastAccessed sid.
  1584. // If the SIDs match and we are within the TTL, skip the test.
  1585. //
  1586. // If either of the conditions are not met, go onto remote UNC box to refresh credentials.
  1587. //
  1588. if( !pPrevSid // could not store the previous SID
  1589. || (FAILED (HRes)) // could not obtain SID
  1590. || !EqualSid(pPrevSid, pSid) // different users
  1591. || (EqualSid(pPrevSid, pSid ) && CheckTTLTimingWindow(dwAccessCheckTimeStamp, g_dwFileMonitoringTimeoutSecs))
  1592. // same user but outside TTL
  1593. )
  1594. {
  1595. //
  1596. // Perform impersonation on the target user
  1597. //
  1598. fImpersonatedUser = FALSE;
  1599. hVirtIncImpToken = NULL;
  1600. hCurImpToken = NULL;
  1601. // TODO: Could be a PERF bottleneck, if we are going to the same UNC.
  1602. // TODO: Try to Somehow store the previous Impersonation token and if they are the same then dont get the token again.
  1603. // TODO: Beware...this could be messy.
  1604. //
  1605. // We must Impersonate LoggedOnUser only in the UNC case.
  1606. // In the Encrypted case the current threads credentials should be able to open the file.
  1607. //
  1608. if (SUCCEEDED(pHitObj->PIReq()->GetVirtualPathToken(m_rgpFilemaps[i]->m_szPathInfo, &hVirtIncImpToken)))
  1609. {
  1610. // set the impersonation token and note that we did so
  1611. // NOTE - there is intentionally no error checking. The
  1612. // assumption being that we are doing best effort at the
  1613. // impersonation because throwing an error here could be
  1614. // tricky for the user to interpret the problem. However,
  1615. // if the impersonation fails, and ASP can still open the
  1616. // file (e.g. passthru authentication), then everyone's
  1617. // happy.
  1618. AspDoRevertHack(&hCurImpToken);
  1619. fImpersonatedUser = ImpersonateLoggedOnUser(hVirtIncImpToken);
  1620. if (!fImpersonatedUser)
  1621. {
  1622. AspUndoRevertHack(&hCurImpToken);
  1623. }
  1624. }
  1625. //
  1626. // Check existence of file...go thru all doors
  1627. //
  1628. hr = AspGetFileAttributes (m_rgpFilemaps[i]->m_szPathTranslated);
  1629. //
  1630. // Undo impersonation if any.
  1631. //
  1632. if (fImpersonatedUser)
  1633. AspUndoRevertHack(&hCurImpToken);
  1634. if (hVirtIncImpToken)
  1635. CloseHandle(hVirtIncImpToken);
  1636. if (FAILED(hr))
  1637. {
  1638. // Too bad...(actually maybe good) the user does not have access to the share.
  1639. // if access is denied on any file, handle the failure and return
  1640. HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated);
  1641. return E_USER_LACKS_PERMISSIONS;
  1642. }
  1643. fNeedsUpdate = TRUE;
  1644. }
  1645. }
  1646. }
  1647. 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
  1648. {
  1649. EnterCriticalSection (&m_csTemplateLock);
  1650. m_pMostRecentImpersonatedTokenUser = pvTempTokenBuffer;
  1651. m_pMostRecentImpersonatedSID = pSid;
  1652. m_cbTokenUser = cbTempTokenBuffer;
  1653. m_dwLastAccessCheck = GetTickCount();
  1654. LeaveCriticalSection (&m_csTemplateLock);
  1655. }
  1656. }
  1657. // Reset the Session.CodePage to the script compilation-time codepage
  1658. // only if a code page directive was found during compilation
  1659. if (m_fCodePageSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet()))
  1660. {
  1661. pHitObj->SetCodePage(m_wCodePage);
  1662. }
  1663. // Reset the Session.LCID to the script compilation-time LCID
  1664. // only if an LCID directive was found during compilation
  1665. if (m_fLCIDSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FLCIDSet()))
  1666. {
  1667. pHitObj->SetLCID(m_lLCID);
  1668. }
  1669. return hr;
  1670. }
  1671. /* ============================================================================
  1672. CTemplate::GetSIDFromTokenHandle
  1673. Takes a token handle and gets the User SID information corresponding to that token.
  1674. Returns
  1675. HResult
  1676. Side effects
  1677. none
  1678. */
  1679. HRESULT CTemplate::GetSIDFromTokenHandle (HANDLE tokenHandle, PSID pSid, LPVOID pBuffer, DWORD *pcbSize)
  1680. {
  1681. HRESULT hr = S_OK;
  1682. BOOL bRet;
  1683. DWORD cbTokenUserBuffer = 0;
  1684. LPVOID pvTokenUserBuffer = NULL;
  1685. // Get buffer size
  1686. bRet = GetTokenInformation(tokenHandle,
  1687. TokenUser,
  1688. NULL,
  1689. 0,
  1690. &cbTokenUserBuffer
  1691. );
  1692. // According to MSDN this call will fail. But we will have a valid value in cbTokenUserBuffer
  1693. // Allocate the space and redo the call
  1694. pvTokenUserBuffer = (BYTE*) CTemplate::SmallMalloc(cbTokenUserBuffer );
  1695. if (!pvTokenUserBuffer)
  1696. return E_OUTOFMEMORY;
  1697. // Get TokenUser
  1698. bRet = GetTokenInformation(tokenHandle,
  1699. TokenUser,
  1700. pvTokenUserBuffer,
  1701. cbTokenUserBuffer,
  1702. &cbTokenUserBuffer
  1703. );
  1704. if (!bRet)
  1705. {
  1706. if (pvTokenUserBuffer)
  1707. CTemplate::SmallFree(pvTokenUserBuffer);
  1708. return HRESULT_FROM_WIN32(GetLastError());
  1709. }
  1710. //Free previously allocated TokenUser buffer
  1711. if (pBuffer)
  1712. CTemplate::SmallFree(pBuffer);
  1713. // Write the values to callers buffer
  1714. pBuffer = pvTokenUserBuffer;
  1715. pSid = ((TOKEN_USER *)pvTokenUserBuffer)->User.Sid;
  1716. *pcbSize = cbTokenUserBuffer;
  1717. return hr;
  1718. }
  1719. /* ============================================================================
  1720. CTemplate::RemoveIncFile
  1721. Removes (by setting to null) an inc-file ptr from this template's inc-file list.
  1722. Returns:
  1723. Nothing
  1724. Side effects:
  1725. None
  1726. */
  1727. void
  1728. CTemplate::RemoveIncFile
  1729. (
  1730. CIncFile* pIncFile
  1731. )
  1732. {
  1733. // If the filemap count is non-zero the pointer to
  1734. // the array of filemaps has better not be null
  1735. DBGPRINTF(( DBG_CONTEXT, "m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps));
  1736. Assert((m_cFilemaps <= 0) || (m_rgpFilemaps != NULL));
  1737. // find the inc-file in list
  1738. for(UINT i = 1; (i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile != pIncFile); i++)
  1739. ;
  1740. // assert that we found the inc-file in list
  1741. Assert((i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile == pIncFile));
  1742. // set inc-file ptr null
  1743. m_rgpFilemaps[i]->m_pIncFile = NULL;
  1744. }
  1745. /*===================================================================
  1746. CTemplate::FTemplateObsolete
  1747. Test to see if the files this template depends on have changed since it
  1748. was compiled.
  1749. We use this in cases where we may have missed a change notification,
  1750. for example, when there were too many changes to record in our change
  1751. notification buffer. We check the last time the file was written too,
  1752. and the security descriptor, since changes to the security descriptor
  1753. aren't noted in the file last write time.
  1754. Parameters:
  1755. None
  1756. Returns:
  1757. TRUE if the template is obsolete, else FALSE
  1758. */
  1759. BOOL CTemplate::FTemplateObsolete(VOID)
  1760. {
  1761. BOOL fStatus = FALSE;
  1762. for (UINT i = 0; i < m_cFilemaps; i++)
  1763. {
  1764. if (FFileChangedSinceCached(m_rgpFilemaps[i]->m_szPathTranslated,
  1765. m_rgpFilemaps[i]->m_hFile,
  1766. m_rgpFilemaps[i]->m_ftLastWriteTime))
  1767. {
  1768. // If the file write time has changed we know enough
  1769. // and can quit here
  1770. fStatus = TRUE;
  1771. break;
  1772. }
  1773. else
  1774. {
  1775. // The file hasn't been writen to, but the security descriptor may
  1776. // have chagned
  1777. // Assert on non-valid security descriptor
  1778. if (NULL != m_rgpFilemaps[i]->m_pSecurityDescriptor)
  1779. {
  1780. PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  1781. DWORD dwSize = m_rgpFilemaps[i]->m_dwSecDescSize;
  1782. if( 0 == GetSecDescriptor(m_rgpFilemaps[i]->m_szPathTranslated, &pSecurityDescriptor, &dwSize))
  1783. {
  1784. if (pSecurityDescriptor)
  1785. {
  1786. // if the size is not the same then set fStatus to TRUE no need to compare memory blocks.
  1787. if(dwSize != GetSecurityDescriptorLength(m_rgpFilemaps[i]->m_pSecurityDescriptor))
  1788. {
  1789. fStatus = TRUE;
  1790. }
  1791. else
  1792. {
  1793. // The size of the security descriptor hasn't changed
  1794. // but we have to compare the contents to make sure they haven't changed
  1795. fStatus = !(0 == memcmp(m_rgpFilemaps[i]->m_pSecurityDescriptor, pSecurityDescriptor, dwSize));
  1796. }
  1797. // We are done with the descriptor
  1798. free(pSecurityDescriptor);
  1799. }
  1800. else
  1801. {
  1802. // Since we failed to get a security descriptor
  1803. // assume the file has changed.
  1804. fStatus = TRUE;
  1805. }
  1806. }
  1807. }
  1808. }
  1809. // Quit as soon as we find a change
  1810. if (fStatus)
  1811. {
  1812. break;
  1813. }
  1814. }
  1815. return fStatus;
  1816. }
  1817. /* ============================================================================
  1818. CTemplate::GetSourceFileName
  1819. Returns name of source file on which this template is based
  1820. Returns
  1821. source file name
  1822. Side effects
  1823. none
  1824. */
  1825. LPTSTR
  1826. CTemplate::GetSourceFileName(SOURCEPATHTYPE pathtype)
  1827. {
  1828. if (!m_rgpFilemaps)
  1829. {
  1830. return NULL;
  1831. }
  1832. switch (pathtype)
  1833. {
  1834. case SOURCEPATHTYPE_PHYSICAL:
  1835. return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathTranslated : NULL));
  1836. case SOURCEPATHTYPE_VIRTUAL:
  1837. return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathInfo : NULL));
  1838. default:
  1839. return(NULL);
  1840. }
  1841. }
  1842. /* ============================================================================
  1843. CTemplate::Count
  1844. Returns count of components of type tcomp contained in this template
  1845. Returns:
  1846. Count of components of type tcomp
  1847. Side effects:
  1848. None
  1849. */
  1850. USHORT
  1851. CTemplate::Count
  1852. (
  1853. TEMPLATE_COMPONENT tcomp
  1854. )
  1855. {
  1856. Assert(NULL != m_pbStart);
  1857. // script engines and script blocks have the same count, stored in same slot
  1858. if(tcomp == tcompScriptEngine)
  1859. tcomp = tcompScriptBlock;
  1860. // counts are stored at start of template in sequential slots, starting with script blocks count
  1861. return * (USHORT*) ((USHORT*)m_pbStart + (tcomp - tcompScriptBlock));
  1862. }
  1863. /* ============================================================================
  1864. CTemplate::GetScriptBlock
  1865. Gets ptrs to script engine name, prog lang id and script text of i-th script block.
  1866. Returns:
  1867. Out-parameters; see below
  1868. Side effects:
  1869. None
  1870. */
  1871. void
  1872. CTemplate::GetScriptBlock
  1873. (
  1874. UINT i, // script block id
  1875. LPSTR* pszScriptEngine, // ptr to script engine name (out-parameter)
  1876. PROGLANG_ID** ppProgLangId, // ptr to prog lang id (out-parameter)
  1877. LPCOLESTR* pwstrScriptText // ptr to wstr script text (out-parameter)
  1878. )
  1879. {
  1880. CByteRange brEngine; // engine name
  1881. CByteRange brScriptText; // script text
  1882. UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned
  1883. BYTE* pbEngineInfo = GetAddress(tcompScriptEngine, (USHORT)i); // ptr to engine info
  1884. Assert(pbEngineInfo != NULL);
  1885. Assert(i < CountScriptEngines());
  1886. // Get engine name from start of engine info
  1887. ByteRangeFromPb(pbEngineInfo, brEngine);
  1888. ByteRangeFromPb(GetAddress(tcompScriptBlock, (USHORT)i), brScriptText);
  1889. Assert(!brEngine.IsNull());
  1890. Assert(!brScriptText.IsNull());
  1891. // Advance ptr past name to prog lang id
  1892. // length of prefix + length of name + NULL
  1893. pbEngineInfo += (sizeof(UINT) + (*pbEngineInfo) + 1);
  1894. // Get prog lang id - it will be on the next pointer sized boundary
  1895. cbAlignment = (UINT) (((DWORD_PTR) pbEngineInfo) % sizeof(DWORD));
  1896. if(cbAlignment > 0)
  1897. {pbEngineInfo += (sizeof(DWORD) - cbAlignment);}
  1898. *pszScriptEngine = (LPSTR)brEngine.m_pb;
  1899. *ppProgLangId = (PROGLANG_ID*)pbEngineInfo;
  1900. *pwstrScriptText = (LPCOLESTR)brScriptText.m_pb;
  1901. }
  1902. /* ============================================================================
  1903. CTemplate::GetObjectInfo
  1904. Returns i-th object-info in template as object name and
  1905. its clsid, scope, model
  1906. Returns:
  1907. HRESULT
  1908. Out-parameters; see below
  1909. Side effects:
  1910. */
  1911. HRESULT
  1912. CTemplate::GetObjectInfo
  1913. (
  1914. UINT i, // object index
  1915. LPSTR* ppszObjectName, // address of object name ptr (out-parameter)
  1916. CLSID* pClsid, // address of object clsid
  1917. CompScope* pcsScope, // address of object scope
  1918. CompModel* pcmModel // address of object threading model
  1919. )
  1920. {
  1921. BYTE* pbObjectInfo = GetAddress(tcompObjectInfo, (USHORT)i); // ptr to current read location
  1922. CByteRange brName; // object name
  1923. UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned
  1924. Assert(i < Count(tcompObjectInfo));
  1925. // Get name from start of object-info
  1926. ByteRangeFromPb(pbObjectInfo, brName);
  1927. Assert(!brName.IsNull());
  1928. // Advance ptr past name
  1929. // length of prefix + length of name + NULL
  1930. pbObjectInfo += (sizeof(UINT) + (*pbObjectInfo) + 1);
  1931. // Get clsid - it will be on the next DWORD boundary
  1932. cbAlignment = (UINT)(((DWORD_PTR) pbObjectInfo) % sizeof(DWORD));
  1933. if(cbAlignment > 0)
  1934. pbObjectInfo += (sizeof(DWORD) - cbAlignment);
  1935. *pClsid = *(CLSID*)pbObjectInfo;
  1936. pbObjectInfo += sizeof(CLSID);
  1937. // Get scope
  1938. *pcsScope = *(CompScope*)pbObjectInfo;
  1939. pbObjectInfo += sizeof(CompScope);
  1940. // Get model
  1941. *pcmModel = *(CompModel*)pbObjectInfo;
  1942. pbObjectInfo += sizeof(CompModel);
  1943. *ppszObjectName = (LPSTR)brName.m_pb;
  1944. return S_OK;
  1945. }
  1946. /* ============================================================================
  1947. CTemplate::GetHTMLBlock
  1948. Returns i-th HTML block
  1949. Parameters:
  1950. UINT i block number
  1951. LPSTR* pszHTML [out] html text
  1952. ULONG* pcbHTML [out] html text length
  1953. ULONG* pcbSrcOffs [out] offset in the source file
  1954. LPSTR* pszSrcIncFile [out] include source file name
  1955. Returns:
  1956. Nothing
  1957. Side effects:
  1958. None
  1959. */
  1960. HRESULT
  1961. CTemplate::GetHTMLBlock
  1962. (
  1963. UINT i,
  1964. LPSTR* pszHTML,
  1965. ULONG* pcbHTML,
  1966. ULONG* pcbSrcOffs,
  1967. LPSTR* pszSrcIncFile
  1968. )
  1969. {
  1970. Assert(i < Count(tcompHTMLBlock));
  1971. // this was added due to user attempt to access the method with an invalid array offset
  1972. //
  1973. if ( i >= Count(tcompHTMLBlock) )
  1974. return E_FAIL;
  1975. // get address of the block start in template memory
  1976. BYTE *pbBlock = GetAddress(tcompHTMLBlock, (USHORT)i);
  1977. Assert(pbBlock);
  1978. // retrieve the byte range of the html code
  1979. CByteRange brHTML;
  1980. ByteRangeFromPb(pbBlock, brHTML);
  1981. *pszHTML = (LPSTR)brHTML.m_pb;
  1982. *pcbHTML = brHTML.m_cb;
  1983. // advance to the source offset
  1984. pbBlock += sizeof(ULONG); // skip prefix
  1985. pbBlock += brHTML.m_cb+1; // skip html bytes (incl. '\0')
  1986. // Add byte aligment which is done in ByteAlignOffset()
  1987. if ((reinterpret_cast<ULONG_PTR>(pbBlock)) & 3)
  1988. pbBlock = reinterpret_cast<BYTE *>((reinterpret_cast<ULONG_PTR>(pbBlock) + 4) & ~3);
  1989. *pcbSrcOffs = *((ULONG*)pbBlock);
  1990. // advance to the source name length
  1991. pbBlock += sizeof(ULONG); // skip source offset prefix
  1992. ULONG cbSrcIncFile = *((ULONG *)pbBlock); // inc file name length
  1993. pbBlock += sizeof(ULONG); // skip inc file name length
  1994. *pszSrcIncFile = (cbSrcIncFile > 0) ? (LPSTR)pbBlock : NULL;
  1995. return S_OK;
  1996. }
  1997. /* ============================================================================
  1998. CTemplate::GetScriptSourceInfo
  1999. Returns line number and source file name a given target line in a given script engine.
  2000. Returns
  2001. line number and source file name (as out-parameters)
  2002. Side effects:
  2003. None
  2004. */
  2005. void
  2006. CTemplate::GetScriptSourceInfo
  2007. (
  2008. UINT idEngine, // script engine id
  2009. int iTargetLine, // target line number
  2010. LPTSTR* pszPathInfo, // ptr to source file virtual path (out-parameter)
  2011. LPTSTR* pszPathTranslated, // ptr to source file real path (out-parameter)
  2012. ULONG* piSourceLine, // ptr to source line number (out-parameter)
  2013. ULONG* pichSourceLine, // ptr to source file offset (out-parameter)
  2014. BOOLB* pfGuessedLine // ptr to flag: did we guess the source line?
  2015. )
  2016. {
  2017. // Initialize some out parameters
  2018. if (pszPathInfo)
  2019. *pszPathInfo = _T("?"); // In case we don't ever find the path
  2020. if (pszPathTranslated)
  2021. *pszPathTranslated = _T("?"); // In case we don't ever find the path
  2022. if (piSourceLine)
  2023. *piSourceLine = 0;
  2024. if (pichSourceLine)
  2025. *pichSourceLine = 0;
  2026. if (pfGuessedLine)
  2027. *pfGuessedLine = FALSE;
  2028. if (iTargetLine <=0)
  2029. {
  2030. return;
  2031. }
  2032. // CHANGE: The rgSourceInfo array is now ZERO based. Decrement target line
  2033. // to convert.
  2034. --iTargetLine;
  2035. // CONSIDER: Make these assertions?
  2036. if(!m_rgrgSourceInfos)
  2037. return;
  2038. if(idEngine > (m_cScriptEngines - 1)) // bug 375: check vs. array bound
  2039. return;
  2040. if(size_t(iTargetLine) >= m_rgrgSourceInfos[idEngine].length()) // bug 375: check vs. array bound
  2041. return;
  2042. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2043. // bug 379: move backwards through target lines, starting with the caller's, until we find one whose
  2044. // fIsHTML flag is false. this handles the case where vbs flags a manufactured line as in error;
  2045. // we assume the actual error occurred at the most recent authored line
  2046. while (iTargetLine >= 0 && (*prgSourceInfos)[iTargetLine].m_fIsHTML)
  2047. {
  2048. --iTargetLine;
  2049. if (pfGuessedLine)
  2050. *pfGuessedLine = TRUE;
  2051. }
  2052. if (iTargetLine >= 0)
  2053. {
  2054. if (pszPathInfo && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL)
  2055. *pszPathInfo = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathInfo;
  2056. if (pszPathTranslated && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL)
  2057. *pszPathTranslated = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathTranslated;
  2058. if (piSourceLine)
  2059. *piSourceLine = (*prgSourceInfos)[iTargetLine].m_idLine;
  2060. if (pichSourceLine)
  2061. *pichSourceLine = (*prgSourceInfos)[iTargetLine].m_cchSourceOffset;
  2062. }
  2063. }
  2064. /* ============================================================================
  2065. CTemplate::GetPositionOfLine
  2066. Get the character offset of a line of source
  2067. (Debugger API Extended to specify a filemap)
  2068. */
  2069. HRESULT
  2070. CTemplate::GetPositionOfLine
  2071. (
  2072. CFileMap *pFilemap,
  2073. ULONG cLineNumber,
  2074. ULONG *pcCharacterPosition
  2075. )
  2076. {
  2077. // NOTE:
  2078. // The table is not binary-searchable because include files
  2079. // will start a new line ordering
  2080. //
  2081. // Algorithm:
  2082. //
  2083. // Find the largest source line N across all engines, such that
  2084. // N <= cLineNumber and the line corresponds to an line
  2085. // in the appropriate file.
  2086. //
  2087. CSourceInfo *pSourceInfoLE = NULL;
  2088. ++cLineNumber; // Convert zero-based line # to one-based
  2089. // Find the correct offset
  2090. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  2091. {
  2092. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2093. // Loop through all lines EXCEPT the EOF line
  2094. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  2095. {
  2096. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  2097. if (pFilemap == pSourceInfo->m_pfilemap &&
  2098. pSourceInfo->m_idLine <= cLineNumber &&
  2099. (pSourceInfoLE == NULL || pSourceInfo->m_idLine > pSourceInfoLE->m_idLine))
  2100. {
  2101. pSourceInfoLE = pSourceInfo;
  2102. }
  2103. }
  2104. }
  2105. // We had better be able to map all line numbers to offsets, unless they passed a bogus line
  2106. // (in which case we still find an offset)
  2107. //
  2108. Assert (pSourceInfoLE != NULL);
  2109. if (pSourceInfoLE == NULL) {
  2110. return E_FAIL;
  2111. }
  2112. *pcCharacterPosition = pSourceInfoLE->m_cchSourceOffset;
  2113. #if 0
  2114. IF_DEBUG(SCRIPT_DEBUGGER)
  2115. {
  2116. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2117. GetScriptSnippets(
  2118. pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap,
  2119. 0, 0,
  2120. wszSourceText, NULL
  2121. );
  2122. DBGPRINTF((
  2123. DBG_CONTEXT,
  2124. "Source Line %d corresponds to source offset %d (Text: \"%S\")\n",
  2125. cLineNumber - 1, pSourceInfoLE->m_cchSourceOffset,
  2126. wszSourceText
  2127. ));
  2128. }
  2129. #endif
  2130. return S_OK;
  2131. }
  2132. /* ============================================================================
  2133. CTemplate::GetLineOfPosition
  2134. Get the line # & offset in line of an arbitrary character offset in source
  2135. (Debugger API Extended to specify a filemap)
  2136. */
  2137. HRESULT CTemplate::GetLineOfPosition
  2138. (
  2139. CFileMap *pFilemap,
  2140. ULONG cCharacterPosition,
  2141. ULONG *pcLineNumber,
  2142. ULONG *pcCharacterOffsetInLine
  2143. )
  2144. {
  2145. // FAIL if source offset totally off-base
  2146. if (cCharacterPosition >= pFilemap->m_cChars)
  2147. return E_FAIL;
  2148. // NOTE:
  2149. // The table is not binary-searchable because include files
  2150. // will start a new line ordering
  2151. //
  2152. // Algorithm:
  2153. //
  2154. // Find the largest source line N across all engines, such that
  2155. // N <= cLineNumber and the line corresponds to an line
  2156. // in the appropriate file.
  2157. //
  2158. CSourceInfo *pSourceInfoLE = NULL;
  2159. // Find the correct offset
  2160. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  2161. {
  2162. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2163. // Loop through all lines EXCEPT the EOF line
  2164. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  2165. {
  2166. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  2167. if (pFilemap == pSourceInfo->m_pfilemap &&
  2168. pSourceInfo->m_cchSourceOffset <= cCharacterPosition &&
  2169. (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset))
  2170. {
  2171. pSourceInfoLE = pSourceInfo;
  2172. }
  2173. }
  2174. }
  2175. // We had better be able to map all offsets to line numbers, unless they passed a bogus offset
  2176. // (in which case we still find a line #, but may go out of range for the offset in line.
  2177. // That case is handled later)
  2178. //
  2179. Assert (pSourceInfoLE != NULL);
  2180. if (pSourceInfoLE == NULL) {
  2181. return E_FAIL;
  2182. }
  2183. *pcLineNumber = pSourceInfoLE->m_idLine - 1; // Convert to zero-based line #
  2184. *pcCharacterOffsetInLine = cCharacterPosition - pSourceInfoLE->m_cchSourceOffset;
  2185. #if 0
  2186. IF_DEBUG(SCRIPT_DEBUGGER)
  2187. {
  2188. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2189. GetScriptSnippets(
  2190. pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap,
  2191. 0, 0,
  2192. wszSourceText, NULL
  2193. );
  2194. DBGPRINTF((
  2195. DBG_CONTEXT,
  2196. "Source offset %d corresponds to source line %d (Text: \"%S\")\n",
  2197. pSourceInfoLE->m_cchSourceOffset, *pcLineNumber,
  2198. wszSourceText
  2199. ));
  2200. }
  2201. DBGPRINTF((
  2202. DBG_CONTEXT,
  2203. "Source offset %d corresponds to source line %d (Text: \"%S\")\n",
  2204. pSourceInfoLE->m_cchSourceOffset, *pcLineNumber,
  2205. wszSourceText
  2206. ));
  2207. }
  2208. #endif
  2209. return S_OK;
  2210. }
  2211. /* ============================================================================
  2212. CTemplate::GetSourceOffset
  2213. Convert a character offset relative to the target script to the appropriate
  2214. offset in the source.
  2215. NOTE: offsets in the middle of a target line are converted to the
  2216. offset relative to the beginning of source line - NOT to the
  2217. precise source offset.
  2218. this is OK because debugger ultimately wants the offset of the
  2219. beginning of line. It is a lot of work to do the precise conversion
  2220. due to the translation of "=" to Response.Write & HTML to
  2221. Response.WriteBlock
  2222. Also, because of these translations, we return the length of the segment
  2223. calculated during compilation, and throw away the length the scripting
  2224. engine sent to us.
  2225. */
  2226. void
  2227. CTemplate::GetSourceOffset
  2228. (
  2229. ULONG idEngine,
  2230. ULONG cchTargetOffset,
  2231. TCHAR **pszSourceFile,
  2232. ULONG *pcchSourceOffset,
  2233. ULONG *pcchSourceText
  2234. )
  2235. {
  2236. Assert (idEngine < m_cScriptEngines);
  2237. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2238. // Find the closest offset in the source
  2239. // This is the largest target offset N, such that N <= cchTargetOffset
  2240. CSourceInfo *pSourceInfo;
  2241. GetBracketingPair(
  2242. cchTargetOffset, // value to search for
  2243. prgSourceInfos->begin(), prgSourceInfos->end(), // array to search
  2244. CTargetOffsetOrder(), // ordering predicate
  2245. &pSourceInfo, static_cast<CSourceInfo **>(NULL) // return values
  2246. );
  2247. // Since the first offset is zero, which is less than all other conceivable offsets,
  2248. // the offset must have been found or else there is a bug.
  2249. Assert (pSourceInfo != NULL);
  2250. Assert (cchTargetOffset >= pSourceInfo->m_cchTargetOffset);
  2251. #if 0
  2252. IF_DEBUG(SCRIPT_DEBUGGER)
  2253. {
  2254. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2255. GetScriptSnippets(
  2256. pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap,
  2257. cchTargetOffset, idEngine,
  2258. wszSourceText, wszTargetText
  2259. );
  2260. DBGPRINTF((
  2261. DBG_CONTEXT,
  2262. "Target offset %d (Text: \"%S\") corresponds to source offset %d (Text: \"%S\") (Length is %d)\n",
  2263. cchTargetOffset, wszTargetText,
  2264. pSourceInfo->m_cchSourceOffset, wszSourceText,
  2265. pSourceInfo->m_cchSourceText
  2266. ));
  2267. }
  2268. #endif
  2269. *pszSourceFile = pSourceInfo->m_pfilemap->m_szPathTranslated;
  2270. *pcchSourceOffset = pSourceInfo->m_cchSourceOffset;
  2271. *pcchSourceText = pSourceInfo->m_cchSourceText;
  2272. }
  2273. /* ============================================================================
  2274. CTemplate::GetTargetOffset
  2275. Convert a character offset relative to the source script to the appropriate
  2276. offset in the target.
  2277. Returns:
  2278. TRUE - source offset corresponds to script
  2279. FALSE - source offset corresponds to HTML
  2280. NOTES:
  2281. 1. This function is very slow. consider caching the value of this function
  2282. (The CTemplateDocumentContext class does this.)
  2283. 2. This function returns the source offset in the master include file -
  2284. if the target offset corresponds to an offset in a header file, then
  2285. the offset to the #include line in the source is returned.
  2286. 3. offsets in the middle of a target line are converted to the
  2287. offset relative to the beginning of source line - NOT to the
  2288. precise source offset.
  2289. this is OK because the debugger ultimately wants the offset of the
  2290. beginning of line. It is a lot of work to do the precise conversion
  2291. due to the translation of "=" to Response.Write & HTML to
  2292. Response.WriteBlock
  2293. CONSIDER:
  2294. Figure out a better way to do this
  2295. */
  2296. BOOL CTemplate::GetTargetOffset
  2297. (
  2298. TCHAR *szSourceFile,
  2299. ULONG cchSourceOffset,
  2300. /* [out] */ ULONG *pidEngine,
  2301. /* [out] */ ULONG *pcchTargetOffset
  2302. )
  2303. {
  2304. // NOTE:
  2305. // The table is not binary-searchable because of two factors:
  2306. // 1. Include files will start a new line ordering
  2307. // 2. For engine 0, tagged scripts will be re-arranged in
  2308. // the target code to reside after all primary script in
  2309. // engine 0.
  2310. //
  2311. // Algorithm:
  2312. //
  2313. // Find the largest source offset N across all engines, such that
  2314. // N <= cchSourceOffset and the offset corresponds to an offset
  2315. // in the appropriate file.
  2316. //
  2317. CSourceInfo *pSourceInfoLE = NULL;
  2318. unsigned idEngineLE = 0;
  2319. // Find the correct offset
  2320. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  2321. {
  2322. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2323. // Loop through all lines EXCEPT the EOF line
  2324. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  2325. {
  2326. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  2327. if (_tcscmp(pSourceInfo->m_pfilemap->m_szPathTranslated, szSourceFile) == 0 &&
  2328. pSourceInfo->m_cchSourceOffset <= cchSourceOffset &&
  2329. (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset))
  2330. {
  2331. pSourceInfoLE = pSourceInfo;
  2332. idEngineLE = idEngine;
  2333. }
  2334. }
  2335. }
  2336. // There won't be a valid offset in the case where there is no
  2337. // code corresponding to the first line in the file (this only
  2338. // occurs when the first line is whitespace, because there is no
  2339. // corresponding "Response.WriteBlock" call there)
  2340. //
  2341. // In that case, return FALSE, which will cause the caller to fail
  2342. //
  2343. if (pSourceInfoLE == NULL)
  2344. {
  2345. *pidEngine = 0;
  2346. *pcchTargetOffset = 0;
  2347. return FALSE;
  2348. }
  2349. *pidEngine = idEngineLE;
  2350. *pcchTargetOffset = pSourceInfoLE->m_cchTargetOffset;
  2351. #if 0
  2352. IF_DEBUG(SCRIPT_DEBUGGER)
  2353. {
  2354. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2355. GetScriptSnippets(
  2356. cchSourceOffset, pSourceInfoLE->m_pfilemap,
  2357. *pcchTargetOffset, *pidEngine,
  2358. wszSourceText, wszTargetText
  2359. );
  2360. DBGPRINTF((
  2361. DBG_CONTEXT,
  2362. "Source offset %d (Text: \"%S\") corresponds to target offset %d (Text: \"%S\")\n",
  2363. cchSourceOffset, wszSourceText,
  2364. *pcchTargetOffset, wszTargetText
  2365. ));
  2366. }
  2367. #endif
  2368. return !pSourceInfoLE->m_fIsHTML;
  2369. }
  2370. /* ============================================================================
  2371. CTemplate::GetActiveScript
  2372. Return a cached script from the template - only used in debug mode
  2373. */
  2374. CActiveScriptEngine *CTemplate::GetActiveScript(ULONG idEngine)
  2375. {
  2376. if (m_rgpDebugScripts == NULL)
  2377. return NULL;
  2378. else
  2379. {
  2380. Assert (idEngine < m_cScriptEngines);
  2381. CActiveScriptEngine *pEng = m_rgpDebugScripts[idEngine];
  2382. if (pEng)
  2383. pEng->AddRef();
  2384. return pEng;
  2385. }
  2386. }
  2387. /* ============================================================================
  2388. CTemplate::AddScript
  2389. add an active script to the template object
  2390. */
  2391. HRESULT CTemplate::AddScript(ULONG idEngine, CActiveScriptEngine *pScriptEngine)
  2392. {
  2393. if (m_rgpDebugScripts == NULL)
  2394. {
  2395. if (
  2396. (m_rgpDebugScripts = new CActiveScriptEngine *[m_cScriptEngines])
  2397. == NULL
  2398. )
  2399. {
  2400. return E_OUTOFMEMORY;
  2401. }
  2402. memset(m_rgpDebugScripts, 0, m_cScriptEngines * sizeof(CActiveScriptEngine *));
  2403. }
  2404. Assert (idEngine < m_cScriptEngines);
  2405. CActiveScriptEngine **ppScriptElem = &m_rgpDebugScripts[idEngine];
  2406. if (*ppScriptElem != NULL)
  2407. (*ppScriptElem)->Release();
  2408. *ppScriptElem = pScriptEngine;
  2409. pScriptEngine->AddRef();
  2410. // Initialize the script engine now (is currently uninitialized)
  2411. // so that the debugger user can set breakpoints.
  2412. IActiveScript *pActiveScript = pScriptEngine->GetActiveScript();
  2413. HRESULT hr;
  2414. TRY
  2415. hr = pActiveScript->SetScriptSite(static_cast<IActiveScriptSite *>(pScriptEngine));
  2416. CATCH(nExcept)
  2417. HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF,
  2418. NULL,
  2419. TRUE,
  2420. nExcept,
  2421. "IActiveScript::SetScriptSite()",
  2422. "CTemplate::AddScript()");
  2423. hr = nExcept;
  2424. END_TRY
  2425. if (FAILED(hr))
  2426. {
  2427. *ppScriptElem = NULL;
  2428. return E_FAIL;
  2429. }
  2430. TRY
  2431. hr = pActiveScript->SetScriptState(SCRIPTSTATE_INITIALIZED);
  2432. CATCH(nExcept)
  2433. HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF,
  2434. NULL,
  2435. TRUE,
  2436. nExcept,
  2437. "IActiveScript::SetScriptState()",
  2438. "CTemplate::AddScript()");
  2439. hr = nExcept;
  2440. END_TRY
  2441. if (FAILED(hr))
  2442. return E_FAIL;
  2443. return S_OK;
  2444. }
  2445. /* ============================================================================
  2446. CTemplate::AppendMapFile
  2447. Appends a filemap to the workstore and memory-maps its file
  2448. Returns:
  2449. Nothing
  2450. Side effects:
  2451. Allocates memory; throws exception on error
  2452. */
  2453. void
  2454. CTemplate::AppendMapFile
  2455. (
  2456. LPCTSTR szFileSpec, // file spec for this file
  2457. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  2458. BOOLB fVirtual, // is file spec virtual or relative?
  2459. CHitObj* pHitObj, // ptr to template's hit object
  2460. BOOLB fGlobalAsa // is this file the global.asa file?
  2461. )
  2462. {
  2463. // alloc or realloc as needed
  2464. if(m_cFilemaps++ == 0)
  2465. m_rgpFilemaps = (CFileMap**) CTemplate::SmallMalloc(sizeof(CFileMap*));
  2466. else
  2467. m_rgpFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpFilemaps, m_cFilemaps * sizeof(CFileMap*));
  2468. if(NULL == m_rgpFilemaps)
  2469. THROW(E_OUTOFMEMORY);
  2470. if(NULL == (m_rgpFilemaps[m_cFilemaps - 1] = new CFileMap))
  2471. THROW(E_OUTOFMEMORY);
  2472. // map the file
  2473. m_rgpFilemaps[m_cFilemaps - 1]->MapFile(
  2474. szFileSpec,
  2475. m_szApplnVirtPath,
  2476. pfilemapCurrent,
  2477. fVirtual,
  2478. pHitObj,
  2479. fGlobalAsa
  2480. );
  2481. }
  2482. /* ============================================================================
  2483. CTemplate::GetSegmentsFromFile
  2484. Gets source segments from a source file by calling ExtractAndProcessSegment
  2485. until there are no more segments; populates WorkStore with info on source segments.
  2486. Returns:
  2487. Nothing
  2488. Side effects:
  2489. None
  2490. */
  2491. void
  2492. CTemplate::GetSegmentsFromFile
  2493. (
  2494. CFileMap& filemap, // this file's file map
  2495. CWorkStore& WorkStore, // working storage for source segments
  2496. CHitObj* pHitObj, // Browser request object
  2497. BOOL fIsHTML
  2498. )
  2499. {
  2500. CByteRange brSearch; // byte range to search for source segments
  2501. _TOKEN rgtknOpeners[TOKEN_OPENERS_MAX]; // array of permitted open tokens
  2502. UINT ctknOpeners; // count of permitted open tokens
  2503. SOURCE_SEGMENT ssegThisFile = ssegHTML; // Either HTML or <SCRIPT> segment
  2504. BOOL fPrevCodePageSet = FALSE;
  2505. UINT wPrevCodePage;
  2506. // init search range to all of file - NOTE we ignore high dword of file size
  2507. brSearch.m_pb = filemap.m_pbStartOfFile;
  2508. brSearch.m_cb = filemap.GetSize();
  2509. if (fIsHTML)
  2510. {
  2511. // populate array of permitted open tokens
  2512. ctknOpeners = 4;
  2513. rgtknOpeners[0] = CTokenList::tknOpenPrimaryScript;
  2514. rgtknOpeners[1] = CTokenList::tknOpenTaggedScript;
  2515. rgtknOpeners[2] = CTokenList::tknOpenObject;
  2516. rgtknOpeners[3] = CTokenList::tknOpenHTMLComment;
  2517. }
  2518. else
  2519. {
  2520. ctknOpeners = 1;
  2521. rgtknOpeners[0] = CTokenList::tknOpenHTMLComment;
  2522. ssegThisFile = ssegTaggedScript;
  2523. }
  2524. TRY
  2525. if ((brSearch.m_cb >= 2)
  2526. && (((brSearch.m_pb[0] == 0xff) && (brSearch.m_pb[1] == 0xfe))
  2527. || ((brSearch.m_pb[0] == 0xfe) && (brSearch.m_pb[1] == 0xff)))) {
  2528. ThrowError(brSearch.m_pb,IDE_TEMPLATE_UNICODE_NOTSUP);
  2529. return;
  2530. }
  2531. // check for the UTF-8 BOM mark. If present, then treat this similar to
  2532. // seeing @CODEPAGE=65001. Note that previous values are retained in the
  2533. // event that there are differing @CODEPAGE settings. This probably should
  2534. // be an error in itself, but I can imagine that this might break a lot of
  2535. // apps as more and more UTF8 files are put into use.
  2536. if ((brSearch.m_cb >= 3)
  2537. && (brSearch.m_pb[0] == 0xEF)
  2538. && (brSearch.m_pb[1] == 0xBB)
  2539. && (brSearch.m_pb[2] == 0xBF)) {
  2540. pHitObj->SetCodePage(65001);
  2541. fPrevCodePageSet = m_fCodePageSet;
  2542. wPrevCodePage = m_wCodePage;
  2543. m_fCodePageSet = TRUE;
  2544. m_wCodePage = 65001;
  2545. brSearch.Advance(3);
  2546. }
  2547. // Process source segments until we run out of them, i.e. until search segment is empty
  2548. // NOTE we pass current filemap as 'parent file' to ExtractAndProcessSegment
  2549. // NOTE ExtractAndProcessSegment appends source segments to WorkStore, advancing brSearch as it goes
  2550. while(!brSearch.IsNull())
  2551. ExtractAndProcessSegment(
  2552. brSearch,
  2553. ssegThisFile,
  2554. rgtknOpeners,
  2555. ctknOpeners,
  2556. &filemap,
  2557. WorkStore,
  2558. pHitObj,
  2559. ssegThisFile == ssegTaggedScript,
  2560. fIsHTML
  2561. );
  2562. CATCH(hrException)
  2563. /*
  2564. NOTE we indicate 'generic error' by m_idErrMsg == 0; this happens as we move
  2565. up the 'include file stack' after processing a specific error (m_idErrMsg != 0).
  2566. Only the specific error is processed; generic error, we simply re-throw exception.
  2567. */
  2568. if(m_idErrMsg != 0)
  2569. {
  2570. // process specific error
  2571. ProcessSpecificError(filemap, pHitObj);
  2572. // reset err message so next msg will be generic as we move up the stack
  2573. m_idErrMsg = 0;
  2574. }
  2575. THROW(hrException);
  2576. END_TRY
  2577. if (fPrevCodePageSet){
  2578. m_wCodePage = wPrevCodePage;
  2579. pHitObj->SetCodePage(wPrevCodePage);
  2580. }
  2581. }
  2582. #define SZ_REG_LANGUAGE_ENGINES "SYSTEM\\CurrentControlSet\\Services\\W3SVC\\ASP\\LanguageEngines\\"
  2583. /* ============================================================================
  2584. CTemplate::GetLanguageEquivalents
  2585. Gets the "Write", "WriteBlock", etc. equivalents from registry for primary scripting language
  2586. Returns
  2587. Nothing
  2588. Side effects
  2589. Throws on error
  2590. */
  2591. void
  2592. CTemplate::GetLanguageEquivalents
  2593. (
  2594. )
  2595. {
  2596. CByteRange brPrimaryEngine;
  2597. m_pWorkStore->m_ScriptStore.m_bufEngineNames.GetItem(0, brPrimaryEngine); // 0-th engine is primary
  2598. // if the primary language is one of the big two, return; we don't need to look up equivalents
  2599. if(brPrimaryEngine.FMatchesSz("VBScript"))
  2600. return;
  2601. if(brPrimaryEngine.FMatchesSz("JScript"))
  2602. return;
  2603. if(brPrimaryEngine.FMatchesSz("JavaScript"))
  2604. return;
  2605. if(brPrimaryEngine.FMatchesSz("LiveScript"))
  2606. return;
  2607. /* query the registry; language equivalents are stored in:
  2608. HKEY_LOCAL_MACHINE
  2609. key: SYSTEM
  2610. key: CurrentControlSet
  2611. key: Services
  2612. key: W3SVC
  2613. key: ASP
  2614. key: LanguageEngines
  2615. key: <LanguageName>
  2616. value: Write data: <replacement syntax for Response.Write(|)>
  2617. value: WriteBlock data: <replacement syntax for Response.WriteBlock(|)>
  2618. */
  2619. STACK_BUFFER( tempRegKeyPath, 512 );
  2620. UINT cchRegKeyPath = strlen(SZ_REG_LANGUAGE_ENGINES);
  2621. if (!tempRegKeyPath.Resize(cchRegKeyPath + brPrimaryEngine.m_cb + 1)) {
  2622. SetLastError(E_OUTOFMEMORY);
  2623. return;
  2624. }
  2625. LPSTR szRegKeyPath = static_cast<LPSTR> (tempRegKeyPath.QueryPtr());
  2626. LPSTR pch = szRegKeyPath;
  2627. strcpy(pch, SZ_REG_LANGUAGE_ENGINES);
  2628. pch += cchRegKeyPath;
  2629. strncpy(pch, (const char *) brPrimaryEngine.m_pb, brPrimaryEngine.m_cb);
  2630. pch += brPrimaryEngine.m_cb;
  2631. *pch = '\0';
  2632. HANDLE hKeyScriptLanguage; // handle of script language reg key
  2633. if(ERROR_SUCCESS == RegOpenKeyExA(
  2634. HKEY_LOCAL_MACHINE, // handle constant
  2635. (const char*) szRegKeyPath, // LPCSTR lpSubKey subkey to open
  2636. 0, // DWORD ulOptions reserved; must be zero
  2637. KEY_QUERY_VALUE, // REGSAM samDesired security access mask
  2638. (PHKEY) &hKeyScriptLanguage // PHKEY phkResult address of handle of open key
  2639. ))
  2640. {
  2641. SetLanguageEquivalent(hKeyScriptLanguage, "Write", &(m_pWorkStore->m_szWriteOpen), &m_pWorkStore->m_cchWriteOpen, &(m_pWorkStore->m_szWriteClose), &m_pWorkStore->m_cchWriteClose);
  2642. SetLanguageEquivalent(hKeyScriptLanguage, "WriteBlock", &(m_pWorkStore->m_szWriteBlockOpen), &m_pWorkStore->m_cchWriteBlockOpen, &(m_pWorkStore->m_szWriteBlockClose), &m_pWorkStore->m_cchWriteBlockClose);
  2643. RegCloseKey((HKEY) hKeyScriptLanguage);
  2644. }
  2645. }
  2646. /* ============================================================================
  2647. CTemplate::SetLanguageEquivalent
  2648. Sets a "language equivalent" from the registry.
  2649. Returns:
  2650. language item opener and closer as out-parameters
  2651. Ex: "Response.Write(" and ")"
  2652. Side effects:
  2653. Throws on error
  2654. */
  2655. void
  2656. CTemplate::SetLanguageEquivalent
  2657. (
  2658. HANDLE hKeyScriptLanguage, // reg key
  2659. LPSTR szLanguageItem, // reg value name - "Write", "WriteBlock", etc.
  2660. LPSTR* pszOpen, // ptr to language item opener, e.g. "Response.Write(" (out-parameter)
  2661. UINT* pcchOpen, // Length for the Open Token
  2662. LPSTR* pszClose, // ptr to language item closer, e.g. ")" (out-parameter)
  2663. UINT* pcchClose // Length for the Close Token
  2664. )
  2665. {
  2666. LONG lError;
  2667. DWORD cbSyntax;
  2668. LPSTR szSyntax;
  2669. char* pchInsert;
  2670. UINT cchOpen;
  2671. UINT cchClose;
  2672. // query registry to get buffer size
  2673. lError = RegQueryValueExA(
  2674. (HKEY) hKeyScriptLanguage, // handle of key to query
  2675. szLanguageItem, // name of value to query
  2676. NULL, // reserved; must be NULL
  2677. NULL, // ptr to value type; not required
  2678. NULL, // ptr to data buffer
  2679. &cbSyntax // ptr to data buffer size
  2680. );
  2681. if(ERROR_FILE_NOT_FOUND == lError)
  2682. // if we don't find szLanguageItem in registry, return silently, leaving *pszOpen and *pszClose unchanged
  2683. return;
  2684. else if((ERROR_MORE_DATA != lError) && (ERROR_SUCCESS != lError))
  2685. THROW(lError);
  2686. Assert(cbSyntax > 0);
  2687. // allocate buffer and re-query registry to get syntax string
  2688. // NOTE RegQueryValueEx returns cbSyntax that includes room for '\0' terminator
  2689. STACK_BUFFER(tempSyntax, 64);
  2690. if (!tempSyntax.Resize(cbSyntax)) {
  2691. THROW(E_OUTOFMEMORY);
  2692. }
  2693. szSyntax = static_cast<LPSTR> (tempSyntax.QueryPtr());
  2694. lError = RegQueryValueExA(
  2695. (HKEY) hKeyScriptLanguage, // handle of key to query
  2696. szLanguageItem, // name of value to query
  2697. NULL, // reserved; must be NULL
  2698. NULL, // ptr to value type; not required
  2699. (LPBYTE) szSyntax, // ptr to data buffer
  2700. &cbSyntax // ptr to data buffer size
  2701. );
  2702. /* NOTE there is the slight possibility of ERROR_FILE_NOT_FOUND or ERROR_MORE_DATA
  2703. if the registry value was deleted or changed between the first and second calls to RegQueryValueEx.
  2704. Since this occurs with vanishingly small probability, we throw (instead of coding the re-try logic).
  2705. */
  2706. if(ERROR_SUCCESS != lError)
  2707. THROW(lError);
  2708. pchInsert = szSyntax;
  2709. while(*pchInsert != '|' && *pchInsert != '\0')
  2710. pchInsert++;
  2711. cchOpen = DIFF(pchInsert - szSyntax);
  2712. cchClose = *pchInsert == '|'
  2713. ? cbSyntax - cchOpen - 2 // found insert symbol: deduct 2 chars, 1 for insert symbol, 1 for '\0'
  2714. : cbSyntax - cchOpen - 1; // didn't find insert symbol: deduct 1 char for '\0'
  2715. Assert(FImplies(cchOpen == 0, *szSyntax == '|'));
  2716. Assert(FImplies(*pchInsert == '\0', cchClose == 0));
  2717. if(cchOpen == 0)
  2718. // opener is empty - set caller's opener ptr null
  2719. *pszOpen = NULL;
  2720. else if(cchOpen > 0)
  2721. {
  2722. // opener is non-empty - set caller's opener to opener in registry
  2723. if(NULL == (*pszOpen = (LPSTR) CTemplate::SmallMalloc(cchOpen + 1)))
  2724. THROW(E_OUTOFMEMORY);
  2725. strncpy(*pszOpen, szSyntax, cchOpen);
  2726. (*pszOpen)[cchOpen] = '\0';
  2727. }
  2728. if(cchClose == 0)
  2729. // closer is empty - set caller's closer ptr null
  2730. *pszClose = NULL;
  2731. else if(cchClose > 0)
  2732. {
  2733. // closer is non-empty - set caller's closer to closer in registry
  2734. if(NULL == (*pszClose = (LPSTR) CTemplate::SmallMalloc(cchClose + 1)))
  2735. THROW(E_OUTOFMEMORY);
  2736. strncpy(*pszClose, (pchInsert + 1), cchClose);
  2737. (*pszClose)[cchClose] = '\0';
  2738. }
  2739. *pcchOpen = cchOpen;
  2740. *pcchClose = cchClose;
  2741. }
  2742. /* ============================================================================
  2743. CTemplate::ThrowError
  2744. Sets up for processing a compile failure.
  2745. Returns:
  2746. Nothing
  2747. Side effects:
  2748. Throws error
  2749. */
  2750. void
  2751. CTemplate::ThrowError
  2752. (
  2753. BYTE* pbErrorLocation, // ptr to error location in source file
  2754. UINT idErrMsg // error id
  2755. )
  2756. {
  2757. m_pbErrorLocation = pbErrorLocation;
  2758. m_idErrMsg = idErrMsg;
  2759. // bug 80745: always throw compile-failed-don't-cache
  2760. THROW(E_TEMPLATE_COMPILE_FAILED_DONT_CACHE);
  2761. }
  2762. /* ============================================================================
  2763. CTemplate::AppendErrorMessageInsert
  2764. Appends an error message insert to member array.
  2765. Returns:
  2766. Nothing
  2767. Side effects:
  2768. Appends to inserts array
  2769. */
  2770. void
  2771. CTemplate::AppendErrorMessageInsert
  2772. (
  2773. BYTE* pbInsert, // ptr to insert
  2774. UINT cbInsert // length of insert
  2775. )
  2776. {
  2777. if (m_ppszMsgInserts == NULL)
  2778. {
  2779. m_ppszMsgInserts = new char*;
  2780. m_cMsgInserts = 0;
  2781. if (m_ppszMsgInserts == NULL)
  2782. return;
  2783. }
  2784. m_ppszMsgInserts[m_cMsgInserts] = new char[cbInsert + 1];
  2785. if (m_ppszMsgInserts[m_cMsgInserts] == NULL)
  2786. return;
  2787. strncpy(m_ppszMsgInserts[m_cMsgInserts], (const char*)pbInsert, cbInsert);
  2788. m_ppszMsgInserts[m_cMsgInserts++][cbInsert] = NULL;
  2789. }
  2790. /* ============================================================================
  2791. CTemplate::ThrowErrorSingleInsert
  2792. Appends a single message insert to member array and throws a compile error.
  2793. Returns:
  2794. Nothing
  2795. Side effects:
  2796. Throws error indirectly
  2797. */
  2798. void
  2799. CTemplate::ThrowErrorSingleInsert
  2800. (
  2801. BYTE* pbErrorLocation, // ptr to error location in source file
  2802. UINT idErrMsg, // error id
  2803. BYTE* pbInsert, // ptr to insert
  2804. UINT cbInsert // length of insert
  2805. )
  2806. {
  2807. AppendErrorMessageInsert(pbInsert, cbInsert);
  2808. ThrowError(pbErrorLocation, idErrMsg);
  2809. }
  2810. /* ============================================================================
  2811. CTemplate::ProcessSpecificError
  2812. Processes a specific compile failure.
  2813. Returns:
  2814. Nothing
  2815. Side effects:
  2816. None
  2817. */
  2818. void
  2819. CTemplate::ProcessSpecificError
  2820. (
  2821. CFileMap& filemap, // source file map
  2822. CHitObj* pHitObj // Browser request object
  2823. )
  2824. {
  2825. // no error msg for generic failures
  2826. if(m_idErrMsg == E_FAIL || m_idErrMsg == E_OUTOFMEMORY)
  2827. return;
  2828. HandleCTemplateError(
  2829. &filemap,
  2830. m_pbErrorLocation,
  2831. m_idErrMsg,
  2832. m_cMsgInserts,
  2833. m_ppszMsgInserts,
  2834. pHitObj
  2835. );
  2836. }
  2837. /* ============================================================================
  2838. CTemplate::ShowErrorInDebugger
  2839. Display a runtime error by invoking the JIT debugger
  2840. Returns:
  2841. failure if debugger won't start
  2842. Side effects:
  2843. None.
  2844. */
  2845. HRESULT
  2846. CTemplate::ShowErrorInDebugger
  2847. (
  2848. CFileMap* pfilemap,
  2849. UINT cchErrorLocation,
  2850. char* szDescription,
  2851. CHitObj *pHitObj,
  2852. BOOL fAttachDocument
  2853. )
  2854. {
  2855. HRESULT hr = S_OK;
  2856. char szDebugTitle[64];
  2857. if (pfilemap == NULL || szDescription == NULL || pHitObj == NULL)
  2858. return E_POINTER;
  2859. // Create a new document context for this statement
  2860. // CONSIDER: character count that we return is bogus - however our debugging
  2861. // client (Caesar's) does not use this information anyway.
  2862. //
  2863. CTemplateDocumentContext *pDebugContext = new CTemplateDocumentContext(this, cchErrorLocation, 1);
  2864. if (pDebugContext == NULL)
  2865. return E_OUTOFMEMORY;
  2866. // Make sure debug document is attached to debugger
  2867. if (fAttachDocument)
  2868. AttachTo(pHitObj->PAppln());
  2869. // Yes it does, bring up the debugger on this line
  2870. hr = InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_UI_BRING_DOC_CONTEXT_TO_TOP, pDebugContext);
  2871. if (FAILED(hr))
  2872. goto LExit;
  2873. // Load the compiler message string
  2874. CchLoadStringOfId(IDE_TEMPLATE_ERRMSG_TITLE, szDebugTitle, sizeof szDebugTitle);
  2875. // pop up a message box with the error description
  2876. MessageBoxA(NULL, szDescription, szDebugTitle, MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONEXCLAMATION);
  2877. LExit:
  2878. if (pDebugContext)
  2879. pDebugContext->Release();
  2880. return hr;
  2881. }
  2882. /* ============================================================================
  2883. CTemplate::HandleCTemplateError
  2884. Handles template compilation errors
  2885. Returns:
  2886. Nothing
  2887. Side effects:
  2888. None.
  2889. */
  2890. void
  2891. CTemplate::HandleCTemplateError
  2892. (
  2893. CFileMap* pfilemap, // ptr to source file map
  2894. BYTE* pbErrorLocation, // ptr to source location where error occurred
  2895. UINT idErrMsg, // error message id
  2896. UINT cMsgInserts, // count of insert strings for error msg
  2897. char** ppszMsgInserts, // array of ptrs to error msg insert strings
  2898. CHitObj* pHitObj // Browser Request
  2899. )
  2900. {
  2901. char szErrMsgPattern[MAX_RESSTRINGSIZE]; // error message pattern
  2902. CHAR szLineNum[12];
  2903. TCHAR szFileName[512];
  2904. CHAR szShortDes[256];
  2905. CHAR szEngine[256];
  2906. CHAR szErrCode[20];
  2907. CHAR szLongDes[MAX_RESSTRINGSIZE];
  2908. CHAR szCombinedDes[sizeof szShortDes + sizeof szLongDes]; // long & short desc
  2909. DWORD dwMask;
  2910. UINT cch;
  2911. // if request ptr or ecb ptr is null, bail; we won't be able to write error msg anyway
  2912. if(pHitObj == NULL)
  2913. return;
  2914. /* if this was a security error, process it specially and bail
  2915. NOTE security error causes exception, rather than true error id
  2916. NOTE template will be destroyed anyway in this case, so no need to maintain m_pszLastErrorMessage
  2917. */
  2918. if(idErrMsg == E_USER_LACKS_PERMISSIONS)
  2919. {
  2920. Assert(cMsgInserts == 1);
  2921. HandleAccessFailure(pHitObj,
  2922. (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  2923. return;
  2924. }
  2925. // get error resource message
  2926. LoadErrResString(idErrMsg, &dwMask, szErrCode, szShortDes, szLongDes);
  2927. // if we have a specific error location, construct msg prefix
  2928. if(pbErrorLocation != NULL) {
  2929. Assert(pfilemap != NULL);
  2930. // get line number of error location as string
  2931. _itoa(SourceLineNumberFromPb(pfilemap, pbErrorLocation), szLineNum, 10);
  2932. }
  2933. else {
  2934. szLineNum[0] = NULL;
  2935. }
  2936. if(pfilemap != NULL) {
  2937. cch = _tcslen(pfilemap->m_szPathInfo);
  2938. _tcsncpy(szFileName, pfilemap->m_szPathInfo, cch);
  2939. }
  2940. else {
  2941. cch = 0;
  2942. }
  2943. szFileName[cch] = '\0';
  2944. //Load Default Engine from resource
  2945. cch = CchLoadStringOfId(IDS_ENGINE, szEngine, sizeof szEngine);
  2946. szEngine[cch] = '\0';
  2947. // resolve error msg pattern and inserts into actual error msg
  2948. cch = strlen(szLongDes);
  2949. memcpy(szErrMsgPattern, szLongDes, cch);
  2950. szErrMsgPattern[cch] = '\0';
  2951. // get an idea of the possibility of a buffer overrunn
  2952. UINT dwTotalLen=0;
  2953. BOOL fTooBig = FALSE;
  2954. if (cMsgInserts) {
  2955. // allow 32 characters for space, etc.
  2956. dwTotalLen = 32 + strlen(szErrMsgPattern);
  2957. for (UINT i = 0; i < cMsgInserts; i++)
  2958. dwTotalLen += strlen(ppszMsgInserts[i]);
  2959. if (dwTotalLen > sizeof szLongDes) {
  2960. cch = CchLoadStringOfId(IDE_TOOBIG, szLongDes, sizeof szLongDes);
  2961. szLongDes[cch] = '\0';
  2962. fTooBig = TRUE;
  2963. }
  2964. }
  2965. if (!fTooBig)
  2966. GetSzFromPatternInserts(szErrMsgPattern, cMsgInserts, ppszMsgInserts, szLongDes);
  2967. // attempt to bring up debugger to display the error - if we cannot then log the error
  2968. /* Find the character offset closest to cbErrorLocation. This will be
  2969. * the place where we start looping with CharNext() to get the full
  2970. * character offset.
  2971. *
  2972. * NOTE: compilation is done in two phases.
  2973. * Errors are detected and reported in phase 1.
  2974. * The DBCS mapping is created in phase 2.
  2975. *
  2976. * Therefore, we don't have the benefit of the rgByte2DBCS table
  2977. * because it doesn't exist yet. Therefore we are left with a SLOW
  2978. * loop starting at BOF. To make things not so abysmal, we don't
  2979. * do the loop on SBCS charsets. We also don't do this conversion
  2980. * unless debugging is enabled.
  2981. */
  2982. if (FCaesars() && pHitObj->PAppln()->FDebuggable()) {
  2983. unsigned cchErrorLocation = CharAdvDBCS(
  2984. (WORD)m_wCodePage,
  2985. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile),
  2986. reinterpret_cast<char *>(pbErrorLocation),
  2987. INFINITE,
  2988. NULL);
  2989. // Create the description string
  2990. char *szEnd = strcpyExA(szCombinedDes, szShortDes);
  2991. *szEnd++ = '\n';
  2992. *szEnd++ = '\n';
  2993. strcpy(szEnd, szLongDes);
  2994. ShowErrorInDebugger(pfilemap, cchErrorLocation, szCombinedDes, pHitObj, idErrMsg != IDE_TEMPLATE_CYCLIC_INCLUDE);
  2995. }
  2996. //cache the info in case we need to use later.
  2997. m_dwLastErrorMask = dwMask;
  2998. //delay NULL check to caller who use this info.
  2999. #if UNICODE
  3000. m_pszLastErrorInfo[ILE_szFileName] = StringDupUTF8(szFileName);
  3001. #else
  3002. m_pszLastErrorInfo[ILE_szFileName] = StringDupA(szFileName);
  3003. #endif
  3004. m_pszLastErrorInfo[ILE_szLineNum] = StringDupA(szLineNum);
  3005. m_pszLastErrorInfo[ILE_szEngine] = StringDupA(szEngine);
  3006. m_pszLastErrorInfo[ILE_szErrorCode] = StringDupA(szErrCode);
  3007. m_pszLastErrorInfo[ILE_szShortDes] = StringDupA(szShortDes);
  3008. m_pszLastErrorInfo[ILE_szLongDes] = StringDupA(szLongDes);
  3009. SendToLog( m_dwLastErrorMask,
  3010. m_pszLastErrorInfo[ILE_szFileName],
  3011. m_pszLastErrorInfo[ILE_szLineNum],
  3012. m_pszLastErrorInfo[ILE_szEngine],
  3013. m_pszLastErrorInfo[ILE_szErrorCode],
  3014. m_pszLastErrorInfo[ILE_szShortDes],
  3015. m_pszLastErrorInfo[ILE_szLongDes],
  3016. pHitObj);
  3017. }
  3018. /* ============================================================================
  3019. CTemplate::FreeGoodTemplateMemory
  3020. Frees memory allocated for a 'good' (successfully compiled) template.
  3021. This includes the template itself, memory to support compile-time errors
  3022. (since the entire concatenated compile-time error message is cached in
  3023. last-err-msg member), and memory to support run-time errors (since if the
  3024. template didn't compile, it can't run).
  3025. Returns
  3026. Nothing
  3027. Side effects
  3028. None
  3029. */
  3030. void
  3031. CTemplate::FreeGoodTemplateMemory
  3032. (
  3033. )
  3034. {
  3035. UINT i;
  3036. LargeTemplateFreeNullify((void**) &m_pbStart);
  3037. SmallTemplateFreeNullify((void**) &m_rgpSegmentFilemaps);
  3038. delete[] m_rgrgSourceInfos;
  3039. m_rgrgSourceInfos = NULL;
  3040. if(m_ppszMsgInserts)
  3041. {
  3042. for(i = 0; i < m_cMsgInserts; i++)
  3043. delete m_ppszMsgInserts[i];
  3044. delete m_ppszMsgInserts;
  3045. m_ppszMsgInserts = NULL;
  3046. }
  3047. // release the collected type libs
  3048. ReleaseTypeLibs();
  3049. // release any 449-echo-cookie objects
  3050. Release449();
  3051. }
  3052. /* ============================================================================
  3053. CTemplate::UnmapFiles
  3054. Unmaps the template's filemaps.
  3055. NOTE: we keep filemap objects around so that filenames will be available for runtime errors
  3056. Returns
  3057. Nothing
  3058. Side effects
  3059. Unmaps template's filemaps
  3060. */
  3061. void
  3062. CTemplate::UnmapFiles
  3063. (
  3064. )
  3065. {
  3066. UINT i;
  3067. for(i = 0; i < m_cFilemaps; i++)
  3068. m_rgpFilemaps[i]->UnmapFile();
  3069. }
  3070. /*===================================================================
  3071. CTemplate::ExtractAndProcessSegment
  3072. Extracts and processes leading source segment and first contained
  3073. source segment from search range.
  3074. Returns
  3075. Nothing
  3076. Side effects
  3077. None
  3078. */
  3079. void
  3080. CTemplate::ExtractAndProcessSegment
  3081. (
  3082. CByteRange& brSearch, // byte range to search for next segment-opening token
  3083. const SOURCE_SEGMENT& ssegLeading, // type of 'leading', i.e. pre-token, source segment
  3084. _TOKEN* rgtknOpeners, // array of permitted open tokens
  3085. UINT ctknOpeners, // count of permitted open tokens
  3086. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3087. CWorkStore& WorkStore, // working storage for source segments
  3088. CHitObj* pHitObj, // Browser request object
  3089. BOOL fScriptTagProcessed,// has script tag been processed?
  3090. BOOL fIsHTML // are we in HTML segment?
  3091. )
  3092. {
  3093. CByteRange brLeadSegment; // byte range of leading source segment
  3094. SOURCE_SEGMENT ssegContained; // type of 'contained', i.e. post-token, source segment
  3095. CByteRange brContainedSegment; // byte range of contained source segment
  3096. _TOKEN tknOpen; // opening token
  3097. BYTE* pbTokenOpen; // ptr to opening token
  3098. _TOKEN tknClose; // closing token
  3099. BYTE* pbTokenClose; // ptr to closing token
  3100. // NOTE: If "fScriptTagProcessed" is TRUE, then "fIsHTML" must be FALSE. The reason for
  3101. // both flags is that if "fScriptTagProcessed" is FALSE, then "fIsHTML" may be either TRUE
  3102. // or FALSE (indeterminate)
  3103. //
  3104. Assert (FImplies(fScriptTagProcessed, !fIsHTML));
  3105. // If search range is empty, return
  3106. if(brSearch.IsNull())
  3107. return;
  3108. // Set ptr of leading segment to start of search segment
  3109. brLeadSegment.m_pb = brSearch.m_pb;
  3110. // get open token for contained segment
  3111. pbTokenOpen = GetOpenToken(
  3112. brSearch,
  3113. ssegLeading,
  3114. rgtknOpeners,
  3115. ctknOpeners,
  3116. &tknOpen
  3117. );
  3118. // Set count of leading segment to distance between start of search range and token
  3119. brLeadSegment.m_cb = DIFF(pbTokenOpen - brSearch.m_pb);
  3120. // Process leading segment
  3121. ProcessSegment(ssegLeading, brLeadSegment, pfilemapCurrent, WorkStore, fScriptTagProcessed, pHitObj, fIsHTML);
  3122. // If open token was 'EOF', empty out search range and return
  3123. if(tknOpen == CTokenList::tknEOF)
  3124. {
  3125. brSearch.Nullify();
  3126. return;
  3127. }
  3128. // Set contained segment type and close token based upon the opener we found
  3129. tknClose = GetComplementToken(tknOpen);
  3130. ssegContained = GetSegmentOfOpenToken(tknOpen);
  3131. if(ssegContained == ssegHTMLComment)
  3132. // for html comment segments, advance search range to open token
  3133. // NOTE keep html comment tags in segment because they must be sent to client
  3134. brSearch.Advance(DIFF(pbTokenOpen - brSearch.m_pb));
  3135. else
  3136. // for all but html comment segments, advance search range to just past open token
  3137. gm_pTokenList->MovePastToken(tknOpen, pbTokenOpen, brSearch);
  3138. // Get closing token - if none, throw error
  3139. if(NULL == (pbTokenClose = GetCloseToken(brSearch, tknClose)))
  3140. {
  3141. if(tknOpen == CTokenList::tknOpenPrimaryScript)
  3142. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_PSCRIPT);
  3143. else if(tknOpen == CTokenList::tknOpenTaggedScript)
  3144. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  3145. else if(tknOpen == CTokenList::tknOpenObject)
  3146. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  3147. else if(tknOpen == CTokenList::tknOpenHTMLComment)
  3148. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_HTML_COMMENT);
  3149. }
  3150. // calc contained segment
  3151. brContainedSegment.m_pb = brSearch.m_pb;
  3152. brContainedSegment.m_cb = DIFF(pbTokenClose - brSearch.m_pb);
  3153. // advance search range to just past close token
  3154. gm_pTokenList->MovePastToken(tknClose, pbTokenClose, brSearch);
  3155. // if an html comment segment, get actual segment type (e.g. might be a server-side include command)
  3156. // NOTE call may also change contained segment byte range
  3157. if(ssegContained == ssegHTMLComment)
  3158. ssegContained = SsegFromHTMLComment(brContainedSegment);
  3159. // if an html comment segment, add its close tag to contained segment
  3160. // NOTE we keep html comment tags as part of segment so we can process like any other html segment
  3161. if(ssegContained == ssegHTMLComment)
  3162. brContainedSegment.m_cb += CCH_TOKEN(tknClose);
  3163. if(ssegContained == ssegMetadata)
  3164. {
  3165. // METADATA comments are used by DESIGN time controls and we don't send
  3166. // them to the client.
  3167. // We process metadata to get to the typelib info
  3168. UINT idError = 0;
  3169. HRESULT hr = ProcessMetadataSegment(brContainedSegment, &idError, pHitObj);
  3170. if (FAILED(hr))
  3171. ThrowError(brContainedSegment.m_pb, idError);
  3172. }
  3173. else if (ssegContained == ssegFPBot)
  3174. {
  3175. }
  3176. else
  3177. {
  3178. // process contained segment
  3179. ProcessSegment(ssegContained, brContainedSegment, pfilemapCurrent, WorkStore, fScriptTagProcessed, pHitObj, fIsHTML);
  3180. }
  3181. }
  3182. /* ============================================================================
  3183. CTemplate::SsegFromHTMLComment
  3184. Determines source segment type of HTML comment.
  3185. Returns
  3186. Source segment type
  3187. Side effects
  3188. May advance segment byte range
  3189. */
  3190. CTemplate::SOURCE_SEGMENT
  3191. CTemplate::SsegFromHTMLComment
  3192. (
  3193. CByteRange& brSegment // source segment
  3194. )
  3195. {
  3196. SOURCE_SEGMENT ssegRet = ssegHTMLComment; // return value
  3197. BYTE* pbToken; // ptr to token
  3198. if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknCommandINCLUDE, brSegment, m_wCodePage)))
  3199. {
  3200. gm_pTokenList->MovePastToken(CTokenList::tknCommandINCLUDE, pbToken, brSegment);
  3201. ssegRet = ssegInclude;
  3202. }
  3203. else if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknTagMETADATA, brSegment, m_wCodePage)))
  3204. {
  3205. gm_pTokenList->MovePastToken(CTokenList::tknTagMETADATA, pbToken, brSegment);
  3206. ssegRet = ssegMetadata;
  3207. }
  3208. else if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknTagFPBot, brSegment, m_wCodePage)))
  3209. {
  3210. gm_pTokenList->MovePastToken(CTokenList::tknTagFPBot, pbToken, brSegment);
  3211. ssegRet = ssegFPBot;
  3212. }
  3213. return ssegRet;
  3214. }
  3215. /* ============================================================================
  3216. CTemplate::ProcessSegment
  3217. Processes a source segment based on its type.
  3218. Returns
  3219. Nothing
  3220. Side effects
  3221. None
  3222. */
  3223. void
  3224. CTemplate::ProcessSegment
  3225. (
  3226. SOURCE_SEGMENT sseg, // segment type
  3227. CByteRange& brSegment, // segment byte range
  3228. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3229. CWorkStore& WorkStore, // working storage for source segments
  3230. BOOL fScriptTagProcessed, // has script tag been processed?
  3231. CHitObj* pHitObj, // Browser request object
  3232. BOOL fIsHTML // Is segment in HTML block or script?
  3233. )
  3234. {
  3235. UINT idSequence; // sequence id for this segment
  3236. // if segment is entirely white space, silently return
  3237. if(FByteRangeIsWhiteSpace(brSegment))
  3238. return;
  3239. // set local sequence id and increment member
  3240. idSequence = WorkStore.m_idCurSequence++;
  3241. // Process segment based on its type
  3242. if(sseg == ssegHTML)
  3243. ProcessHTMLSegment(brSegment, WorkStore.m_bufHTMLSegments, idSequence, pfilemapCurrent);
  3244. else if(sseg == ssegHTMLComment)
  3245. ProcessHTMLCommentSegment(brSegment, pfilemapCurrent, WorkStore, pHitObj);
  3246. else if(sseg == ssegPrimaryScript || sseg == ssegTaggedScript)
  3247. ProcessScriptSegment(sseg, brSegment, pfilemapCurrent, WorkStore, idSequence, (BOOLB)!!fScriptTagProcessed, pHitObj);
  3248. else if(sseg == ssegObject)
  3249. ProcessObjectSegment(brSegment, pfilemapCurrent, WorkStore, idSequence);
  3250. else if(sseg == ssegInclude)
  3251. {
  3252. if (! fIsHTML)
  3253. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SSI_COMMAND);
  3254. ProcessIncludeFile(brSegment, pfilemapCurrent, WorkStore, idSequence, pHitObj, fIsHTML);
  3255. }
  3256. // malloc/realloc array if needed
  3257. if(m_cSegmentFilemapSlots == 0)
  3258. {
  3259. m_cSegmentFilemapSlots = C_SCRIPTSEGMENTSDEFAULT + C_HTMLSEGMENTSDEFAULT;
  3260. if(NULL == (m_rgpSegmentFilemaps = (CFileMap**) CTemplate::SmallMalloc(m_cSegmentFilemapSlots * sizeof(CFileMap*))))
  3261. THROW(E_OUTOFMEMORY);
  3262. }
  3263. else if(idSequence >= m_cSegmentFilemapSlots)
  3264. {
  3265. // grab twice what we had before
  3266. m_cSegmentFilemapSlots *= 2;
  3267. if(NULL == (m_rgpSegmentFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpSegmentFilemaps,
  3268. m_cSegmentFilemapSlots * sizeof(CFileMap*))))
  3269. THROW(E_OUTOFMEMORY);
  3270. }
  3271. // set filemap ptr for this segment - NOTE 'parent' filemap is also current file map
  3272. m_rgpSegmentFilemaps[idSequence] = pfilemapCurrent;
  3273. }
  3274. /* ========================================================
  3275. CTemplate::ProcessHTMLSegment
  3276. Processes an HTML segment.
  3277. Returns
  3278. Nothing
  3279. Side effects
  3280. None
  3281. */
  3282. void
  3283. CTemplate::ProcessHTMLSegment
  3284. (
  3285. CByteRange& brHTML, // html segment
  3286. CBuffer& bufHTMLBlocks, // working storage for html blocks
  3287. UINT idSequence, // segment sequence id
  3288. CFileMap* pfilemapCurrent // current filemap
  3289. )
  3290. {
  3291. if(!(brHTML.IsNull()))
  3292. // If byte range is non-empty, store it in html buffer (non-local)
  3293. bufHTMLBlocks.Append(brHTML, FALSE, idSequence, pfilemapCurrent);
  3294. }
  3295. /* ========================================================
  3296. CTemplate::ProcessHTMLCommentSegment
  3297. Processes an HTML comment segment: within an HTML comment we
  3298. honor plain text (passed through as HTML comment) and primary script.
  3299. See bug 182 for istudio scenarios that require this behavior.
  3300. Returns
  3301. Nothing
  3302. Side effects
  3303. None
  3304. */
  3305. void
  3306. CTemplate::ProcessHTMLCommentSegment
  3307. (
  3308. CByteRange& brSegment, // segment byte range
  3309. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3310. CWorkStore& WorkStore, // working storage for source segments
  3311. CHitObj* pHitObj // Browser request object
  3312. )
  3313. {
  3314. _TOKEN* rgtknOpeners; // array of permitted open tokens
  3315. UINT ctknOpeners; // count of permitted open tokens
  3316. // populate array of permitted open tokens
  3317. ctknOpeners = 1;
  3318. _TOKEN tknOpeners[1];
  3319. rgtknOpeners = tknOpeners;
  3320. rgtknOpeners[0] = CTokenList::tknOpenPrimaryScript;
  3321. // Process source segments embedded within HTML comment segment
  3322. while(!brSegment.IsNull())
  3323. ExtractAndProcessSegment(
  3324. brSegment, // byte range to search for next segment-opening token
  3325. ssegHTML, // type of 'leading', i.e. pre-token, source segment
  3326. rgtknOpeners, // array of permitted open tokens
  3327. ctknOpeners, // count of permitted open tokens
  3328. pfilemapCurrent,// ptr to filemap of parent file
  3329. WorkStore, // working storage for source segments
  3330. pHitObj // Browser request object
  3331. );
  3332. }
  3333. /* ============================================================================
  3334. CTemplate::ProcessScriptSegment
  3335. Processes a script segment.
  3336. Returns
  3337. Nothing
  3338. Side effects
  3339. None
  3340. */
  3341. void
  3342. CTemplate::ProcessScriptSegment
  3343. (
  3344. SOURCE_SEGMENT sseg, // segment type
  3345. CByteRange& brSegment, // segment byte range
  3346. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3347. CWorkStore& WorkStore, // working storage for scripts
  3348. UINT idSequence, // segment sequence id
  3349. BOOLB fScriptTagProcessed,// has script tag been processed?
  3350. CHitObj* pHitObj // Browser request object
  3351. )
  3352. {
  3353. CByteRange brEngine; // script engine name - NOTE constructed null
  3354. if(m_fGlobalAsa)
  3355. if(sseg == ssegPrimaryScript)
  3356. // error out on primary script if we are processing global.asa
  3357. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_GLOBAL_PSCRIPT);
  3358. if(sseg == ssegPrimaryScript)
  3359. {
  3360. CByteRange brTemp = brSegment;
  3361. LTrimWhiteSpace(brTemp);
  3362. if(*brTemp.m_pb == '@') // CONSIDER: tknTagSetPriScriptLang
  3363. {
  3364. // impossible condition: page-level @ commands can't be allowed if they have already been executed
  3365. Assert(!(WorkStore.m_fPageCommandsAllowed && WorkStore.m_fPageCommandsExecuted));
  3366. if(!WorkStore.m_fPageCommandsAllowed)
  3367. {
  3368. if(WorkStore.m_fPageCommandsExecuted)
  3369. // error out if trying to re-execute page-level @ commands
  3370. ThrowError(brSegment.m_pb, IDE_TEMPLATE_PAGE_COMMAND_REPEATED);
  3371. else
  3372. // error out if trying to execute page-level @ commands when not allowed
  3373. ThrowError(brSegment.m_pb, IDE_TEMPLATE_PAGE_COMMAND_NOT_FIRST);
  3374. }
  3375. // if we made it here, must be allowed to execute page-level @ commands AND they have not been executed
  3376. Assert((WorkStore.m_fPageCommandsAllowed && !WorkStore.m_fPageCommandsExecuted));
  3377. /* set primary script language if required
  3378. NOTE we call GetTagName to see if LANGUAGE tag occurs in tags segment; this is somewhat wasteful,
  3379. since BrValueOfTag must simply call GetTagName again. However, this scheme is easier than changing
  3380. BrValueOfTag to return a BOOL and amending all its other callers, who don't need this info.
  3381. */
  3382. // Flags and counters used to track and validate the @ command directive
  3383. //
  3384. int nFirstPass = 1;
  3385. int nOffset = 0;
  3386. BOOLB fTagLanguage = TRUE;
  3387. BOOLB fTagCodePage = TRUE;
  3388. BOOLB fTagLCID = TRUE;
  3389. BOOLB fTagTransacted = TRUE;
  3390. BOOLB fTagSession = TRUE;
  3391. while( GetTag( brSegment, nFirstPass) )
  3392. {
  3393. nFirstPass =2;
  3394. nOffset = 0;
  3395. if ( fTagLanguage && CompTagName( brSegment, CTokenList::tknTagLanguage ) )
  3396. {
  3397. fTagLanguage = FALSE;
  3398. brEngine = BrValueOfTag(brSegment, CTokenList::tknTagLanguage);
  3399. if ( brEngine.IsNull() )
  3400. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_ENGINE_NAME);
  3401. // get prog lang id
  3402. PROGLANG_ID ProgLangId;
  3403. HRESULT hr = GetProgLangId(brEngine, &ProgLangId);
  3404. if(hr == TYPE_E_ELEMENTNOTFOUND)
  3405. // if prog lang not found, throw error
  3406. ThrowErrorSingleInsert(
  3407. brEngine.m_pb,
  3408. IDE_TEMPLATE_BAD_PROGLANG,
  3409. brEngine.m_pb,
  3410. brEngine.m_cb
  3411. );
  3412. else if(FAILED(hr))
  3413. // other failure: re-throw exception code
  3414. THROW(hr);
  3415. Assert(WorkStore.m_ScriptStore.CountPreliminaryEngines() >= 1);
  3416. // Set 0-th (primary) script engine to user-specified value
  3417. WorkStore.m_ScriptStore.m_bufEngineNames.SetItem(
  3418. 0, // index of item to set
  3419. brEngine, // engine name
  3420. FALSE, // item is non-local
  3421. 0, // sequence id (don't care)
  3422. NULL // filemap ptr (don't care)
  3423. );
  3424. // Set 0-th (primary) prog lang id to engine's
  3425. WorkStore.m_ScriptStore.m_rgProgLangId[0] = ProgLangId;
  3426. brSegment.Advance(DIFF(brEngine.m_pb - brSegment.m_pb));
  3427. }
  3428. /* set code page if required
  3429. see NOTE above for why we call we call GetTagName.
  3430. */
  3431. else if ( fTagCodePage && CompTagName( brSegment, CTokenList::tknTagCodePage ) )
  3432. {
  3433. fTagCodePage = FALSE;
  3434. CByteRange brCodePage = BrValueOfTag( brSegment, CTokenList::tknTagCodePage );
  3435. if ( brCodePage.IsNull() )
  3436. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CODEPAGE);
  3437. if ( brCodePage.m_cb > 10 )
  3438. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3439. char szCodePage[31];
  3440. strncpy( szCodePage, (char*) brCodePage.m_pb, brCodePage.m_cb );
  3441. szCodePage[ brCodePage.m_cb ] = '\0';
  3442. char *pchEnd;
  3443. UINT uCodePage = UINT( strtoul( szCodePage, &pchEnd, 10 ) );
  3444. // verify that pchEnd is the NULL
  3445. if (*pchEnd != 0)
  3446. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3447. if ((m_fCodePageSet == TRUE) && (uCodePage != pHitObj->GetCodePage()))
  3448. ThrowError(brSegment.m_pb, IDE_TEMPLATE_MIXED_CODEPAGE_USAGE);
  3449. if ( FAILED( pHitObj->SetCodePage( uCodePage ) ) )
  3450. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3451. else
  3452. {
  3453. m_wCodePage = uCodePage;
  3454. m_fCodePageSet = TRUE;
  3455. }
  3456. brSegment.Advance(DIFF(brCodePage.m_pb - brSegment.m_pb));
  3457. }
  3458. /* set LCID if required
  3459. see NOTE above for why we call we call GetTagName.
  3460. */
  3461. else if ( fTagLCID && CompTagName( brSegment, CTokenList::tknTagLCID ) )
  3462. {
  3463. fTagLCID = FALSE;
  3464. CByteRange brLCID = BrValueOfTag( brSegment, CTokenList::tknTagLCID );
  3465. if ( brLCID.IsNull() )
  3466. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_LCID);
  3467. if ( brLCID.m_cb > 10 ) // cannot be greater than 4G 4,000,000,000
  3468. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_LCID);
  3469. char szLCID[31];
  3470. strncpy( szLCID, (char*) brLCID.m_pb, brLCID.m_cb );
  3471. szLCID[ brLCID.m_cb ] = '\0';
  3472. char *pchEnd;
  3473. UINT uLCID = UINT( strtoul( szLCID, &pchEnd, 10 ) );
  3474. // verify that pchEnd is the NULL
  3475. if (*pchEnd != 0)
  3476. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_LCID);
  3477. if ( FAILED( pHitObj->SetLCID( uLCID ) ) )
  3478. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_LCID);
  3479. else
  3480. {
  3481. m_lLCID = uLCID;
  3482. m_fLCIDSet = TRUE;
  3483. }
  3484. brSegment.Advance(DIFF(brLCID.m_pb - brSegment.m_pb));
  3485. }
  3486. /* Set transacted if requiured
  3487. see NOTE above for why we call GetTagName
  3488. */
  3489. else if ( fTagTransacted && CompTagName( brSegment, CTokenList::tknTagTransacted ) )
  3490. {
  3491. STACK_BUFFER( tempTransValue, 32 );
  3492. fTagTransacted = FALSE;
  3493. CByteRange brTransacted = BrValueOfTag( brSegment, CTokenList::tknTagTransacted );
  3494. if ( brTransacted.IsNull() )
  3495. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_TRANSACTED_VALUE);
  3496. if (!tempTransValue.Resize(brTransacted.m_cb + 1)) {
  3497. ThrowError(brSegment.m_pb, IDE_OOM);
  3498. }
  3499. LPSTR szTransacted = static_cast<LPSTR> (tempTransValue.QueryPtr());
  3500. strncpy(szTransacted, (LPCSTR)brTransacted.m_pb, brTransacted.m_cb);
  3501. szTransacted[brTransacted.m_cb]='\0';
  3502. if (!strcmpi(szTransacted, "REQUIRED"))
  3503. m_ttTransacted = ttRequired;
  3504. else if (!strcmpi(szTransacted, "REQUIRES_NEW"))
  3505. m_ttTransacted = ttRequiresNew;
  3506. else if (!strcmpi(szTransacted, "SUPPORTED"))
  3507. m_ttTransacted = ttSupported;
  3508. else if (!strcmpi(szTransacted, "NOT_SUPPORTED"))
  3509. m_ttTransacted = ttNotSupported;
  3510. else
  3511. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_TRANSACTED_VALUE);
  3512. brSegment.Advance(DIFF(brTransacted.m_pb - brSegment.m_pb));
  3513. }
  3514. /* Set session flag
  3515. see NOTE above for why we call GetTagName
  3516. */
  3517. else if ( fTagSession && CompTagName( brSegment, CTokenList::tknTagSession ) )
  3518. {
  3519. STACK_BUFFER( tempSession, 16 );
  3520. fTagSession = FALSE;
  3521. CByteRange brSession = BrValueOfTag( brSegment, CTokenList::tknTagSession );
  3522. if ( brSession.IsNull() )
  3523. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SESSION_VALUE);
  3524. if (!tempSession.Resize(brSession.m_cb + 1))
  3525. ThrowError(brSegment.m_pb, IDE_OOM);
  3526. LPSTR szSession = static_cast<LPSTR> (tempSession.QueryPtr());
  3527. strncpy(szSession, (LPCSTR)brSession.m_pb, brSession.m_cb);
  3528. szSession[brSession.m_cb]='\0';
  3529. if (strcmpi(szSession, "TRUE") == 0)
  3530. {
  3531. m_fSession = TRUE;
  3532. if (!pHitObj->PAppln()->QueryAppConfig()->fAllowSessionState())
  3533. ThrowError(brSegment.m_pb, IDE_TEMPLATE_CANT_ENABLE_SESSIONS);
  3534. }
  3535. else if (strcmpi(szSession, "FALSE") == 0)
  3536. m_fSession = FALSE;
  3537. else
  3538. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SESSION_VALUE);
  3539. brSegment.Advance(DIFF(brSession.m_pb - brSegment.m_pb));
  3540. }
  3541. else
  3542. ThrowErrorSingleInsert( brSegment.m_pb,
  3543. IDE_TEMPLATE_BAD_AT_COMMAND,
  3544. brSegment.m_pb,
  3545. brSegment.m_cb
  3546. );
  3547. }
  3548. if (nFirstPass == 1)
  3549. ThrowErrorSingleInsert( brSegment.m_pb,
  3550. IDE_TEMPLATE_BAD_AT_COMMAND,
  3551. brSegment.m_pb,
  3552. brSegment.m_cb
  3553. );
  3554. // set flag true and ignore remainder of segment, since we only use this segment for page-level @ commands
  3555. WorkStore.m_fPageCommandsExecuted = TRUE;
  3556. goto LExit;
  3557. }
  3558. }
  3559. if(sseg == ssegTaggedScript)
  3560. {
  3561. if(!fScriptTagProcessed)
  3562. {
  3563. /* semantics of script-tag-processed flag:
  3564. - if false, we have a 'fresh' tagged script block, so we need to get its engine name
  3565. (which also advances past the script tag header) and then process the tagged segment
  3566. via indirect recursive call
  3567. - if true, we have aleady been called recursively, so we bypass further recursion
  3568. and simply append to store
  3569. */
  3570. CByteRange brIncludeFile;
  3571. GetScriptEngineOfSegment(brSegment, WorkStore.m_brCurEngine, brIncludeFile);
  3572. if (! brIncludeFile.IsNull())
  3573. {
  3574. STACK_BUFFER( tempInclude, 256 );
  3575. if (!tempInclude.Resize(brIncludeFile.m_cb + 1)) {
  3576. ThrowError(brSegment.m_pb, IDE_OOM);
  3577. }
  3578. // Create Null-terminated string from brIncludeFile
  3579. char *szFileSpec = reinterpret_cast<char *>(tempInclude.QueryPtr());
  3580. memcpy(szFileSpec, brIncludeFile.m_pb, brIncludeFile.m_cb);
  3581. szFileSpec[brIncludeFile.m_cb] = 0;
  3582. if (szFileSpec[0] == '\\') // metabase stuff chokes on initial '\' char
  3583. szFileSpec[0] = '/';
  3584. // read the include file (szFileSpec & brIncludeFile in this case point to same string contents.
  3585. // however, "brIncludeFile" is used as an error location.
  3586. //
  3587. TRY
  3588. ProcessIncludeFile2(szFileSpec, brIncludeFile, pfilemapCurrent, WorkStore, idSequence, pHitObj, FALSE);
  3589. CATCH(hrException)
  3590. // The TRY/CATCH below may re-throw a IDE_TEMPLATE_BAD_PROGLANG when the
  3591. // segment being processed is tagged script with a SRC file. The reason being
  3592. // that to properly report the error, the ThrowErrorSingleInsert must be called
  3593. // from the template which contained the script tag with the bad prog lang. If
  3594. // called from the template created containing the included script, then the
  3595. // brEngine assigned below is not pointing into the included script's filemap
  3596. // which results in AVs because we can't do the pointer math to determine the
  3597. // line number.
  3598. if(hrException == IDE_TEMPLATE_BAD_PROGLANG)
  3599. // exception code is really an error message id: set err id to it
  3600. ThrowErrorSingleInsert(
  3601. WorkStore.m_brCurEngine.m_pb,
  3602. IDE_TEMPLATE_BAD_PROGLANG,
  3603. WorkStore.m_brCurEngine.m_pb,
  3604. WorkStore.m_brCurEngine.m_cb
  3605. );
  3606. else
  3607. // other exception: re-throw
  3608. THROW(hrException);
  3609. END_TRY
  3610. // done - don't process script text
  3611. return;
  3612. }
  3613. else
  3614. ProcessTaggedScriptSegment(brSegment, pfilemapCurrent, WorkStore, pHitObj);
  3615. }
  3616. brEngine = WorkStore.m_brCurEngine;
  3617. }
  3618. TRY
  3619. // append script segment to store
  3620. WorkStore.m_ScriptStore.AppendScript(brSegment, brEngine, (sseg == ssegPrimaryScript), idSequence, pfilemapCurrent);
  3621. CATCH(hrException)
  3622. // NOTE exception code from AppendScript() is overloaded: it can be an error message id or a true exception
  3623. // if the brEngine does not point to memory within the current filemap, then
  3624. // we must have come into here because of a tagged script statement with a SRC=
  3625. // attrib. In which case, we won't call ThrowError from here but will re-throw
  3626. // the error to be caught above.
  3627. if((hrException == IDE_TEMPLATE_BAD_PROGLANG)
  3628. && (brEngine.m_pb >= pfilemapCurrent->m_pbStartOfFile)
  3629. && (brEngine.m_pb < (pfilemapCurrent->m_pbStartOfFile + pfilemapCurrent->GetSize()))) {
  3630. // exception code is really an error message id: set err id to it
  3631. ThrowErrorSingleInsert(
  3632. brEngine.m_pb,
  3633. IDE_TEMPLATE_BAD_PROGLANG,
  3634. brEngine.m_pb,
  3635. brEngine.m_cb
  3636. );
  3637. }
  3638. else
  3639. // other exception: re-throw
  3640. THROW(hrException);
  3641. END_TRY
  3642. LExit:
  3643. // set flag to say we can no longer set primary language (must be in first script segment, if at all)
  3644. WorkStore.m_fPageCommandsAllowed = FALSE;
  3645. }
  3646. /* ========================================================
  3647. CTemplate::ProcessMetadataSegment
  3648. Parses the metadata comment for typelib information.
  3649. Returns
  3650. HRESULT
  3651. */
  3652. HRESULT
  3653. CTemplate::ProcessMetadataSegment
  3654. (
  3655. const CByteRange& brSegment,
  3656. UINT *pidError,
  3657. CHitObj *pHitObj
  3658. )
  3659. {
  3660. // TYPELIB
  3661. if (FTagHasValue(brSegment,
  3662. CTokenList::tknTagType,
  3663. CTokenList::tknValueTypeLib))
  3664. {
  3665. return ProcessMetadataTypelibSegment(brSegment, pidError, pHitObj);
  3666. }
  3667. // METADATA INVALID in Global.asa
  3668. else if (m_fGlobalAsa)
  3669. {
  3670. ThrowError(brSegment.m_pb, IDE_TEMPLATE_METADATA_IN_GLOBAL_ASA);
  3671. return E_TEMPLATE_COMPILE_FAILED_DONT_CACHE; // to keep compiler happy; in reality doesn't return.
  3672. }
  3673. // COOKIE
  3674. else if (FTagHasValue(brSegment,
  3675. CTokenList::tknTagType,
  3676. CTokenList::tknValueCookie))
  3677. {
  3678. return ProcessMetadataCookieSegment(brSegment, pidError, pHitObj);
  3679. }
  3680. // Ignore everything else
  3681. else
  3682. {
  3683. return S_OK;
  3684. }
  3685. }
  3686. /* ========================================================
  3687. CTemplate::ProcessMetadataTypelibSegment
  3688. Parses the metadata comment for typelib information.
  3689. Returns
  3690. HRESULT
  3691. */
  3692. HRESULT
  3693. CTemplate::ProcessMetadataTypelibSegment
  3694. (
  3695. const CByteRange& brSegment,
  3696. UINT *pidError,
  3697. CHitObj *pHitObj
  3698. )
  3699. {
  3700. // Ignore ENDSPAN segments
  3701. if (GetTagName(brSegment, CTokenList::tknTagEndspan))
  3702. {
  3703. // ENDSPAN found - ignore
  3704. return S_OK;
  3705. }
  3706. HRESULT hr;
  3707. char szFile[MAX_PATH+1];
  3708. DWORD cbFile;
  3709. // Try to get the filename
  3710. CByteRange br = BrValueOfTag(brSegment, CTokenList::tknTagFile);
  3711. if (!br.IsNull())
  3712. {
  3713. // filename present
  3714. if (br.m_cb > MAX_PATH)
  3715. {
  3716. // file too long
  3717. *pidError = IDE_TEMPLATE_BAD_TYPELIB_SPEC;
  3718. return E_FAIL;
  3719. }
  3720. memcpy(szFile, br.m_pb, br.m_cb);
  3721. cbFile = br.m_cb;
  3722. szFile[cbFile] = '\0';
  3723. }
  3724. else
  3725. {
  3726. // No filename - use GUID, version, LCID to get file
  3727. char szUUID[44]; // {} + hex chars + dashes
  3728. char szVers[16]; // "1.0", etc
  3729. char szLCID[16]; // locale id - a number
  3730. br = BrValueOfTag(brSegment, CTokenList::tknTagUUID);
  3731. if (br.IsNull() || br.m_cb > sizeof(szUUID)-3)
  3732. {
  3733. // no filename and no uuid -> invalid typelib spec
  3734. *pidError = IDE_TEMPLATE_BAD_TYPELIB_SPEC;
  3735. return E_FAIL;
  3736. }
  3737. if (br.m_pb[0] == '{')
  3738. {
  3739. // already in braces
  3740. memcpy(szUUID, br.m_pb, br.m_cb);
  3741. szUUID[br.m_cb] = '\0';
  3742. }
  3743. else
  3744. {
  3745. // enclose in {}
  3746. szUUID[0] = '{';
  3747. memcpy(szUUID+1, br.m_pb, br.m_cb);
  3748. szUUID[br.m_cb+1] = '}';
  3749. szUUID[br.m_cb+2] = '\0';
  3750. }
  3751. // Optional Version
  3752. szVers[0] = '\0';
  3753. br = BrValueOfTag(brSegment, CTokenList::tknTagVersion);
  3754. if (!br.IsNull() && br.m_cb < sizeof(szVers)-1)
  3755. {
  3756. memcpy(szVers, br.m_pb, br.m_cb);
  3757. szVers[br.m_cb] = '\0';
  3758. }
  3759. // Optional LCID
  3760. LCID lcid;
  3761. br = BrValueOfTag(brSegment, CTokenList::tknTagLCID);
  3762. if (!br.IsNull() && br.m_cb < sizeof(szLCID)-1)
  3763. {
  3764. memcpy(szLCID, br.m_pb, br.m_cb);
  3765. szLCID[br.m_cb] = '\0';
  3766. lcid = strtoul(szLCID, NULL, 16);
  3767. }
  3768. else
  3769. {
  3770. // if the LCID is not defined -> use system's
  3771. lcid = GetSystemDefaultLCID();
  3772. }
  3773. // Get TYPELIB filename from registry
  3774. hr = GetTypelibFilenameFromRegistry
  3775. (
  3776. szUUID,
  3777. szVers,
  3778. lcid,
  3779. szFile,
  3780. MAX_PATH
  3781. );
  3782. if (FAILED(hr))
  3783. {
  3784. *pidError = IDE_TEMPLATE_BAD_TYPELIB_REG_SPEC;
  3785. return hr;
  3786. }
  3787. cbFile = strlen(szFile);
  3788. }
  3789. // Convert filename to double-byte to call LoadTypeLib()
  3790. STACK_BUFFER( tempFile, MAX_PATH * sizeof(WCHAR) );
  3791. if (!tempFile.Resize((cbFile+1) * sizeof(WCHAR))) {
  3792. *pidError = IDE_OOM;
  3793. return E_FAIL;
  3794. }
  3795. LPWSTR wszFile = (LPWSTR)tempFile.QueryPtr();
  3796. if (MultiByteToWideChar(pHitObj->GetCodePage(), MB_ERR_INVALID_CHARS,
  3797. szFile, cbFile, wszFile, cbFile) == 0)
  3798. {
  3799. *pidError = IDE_OOM;
  3800. return E_FAIL;
  3801. }
  3802. wszFile[cbFile] = L'\0';
  3803. // LoadTypeLib() to get ITypeLib*
  3804. ITypeLib *ptlb = NULL;
  3805. hr = LoadTypeLib(wszFile, &ptlb);
  3806. if (FAILED(hr))
  3807. {
  3808. *pidError = IDE_TEMPLATE_LOAD_TYPELIB_FAILED;
  3809. return hr;
  3810. }
  3811. // Remember ITypeLib* in the array
  3812. Assert(ptlb);
  3813. hr = m_rgpTypeLibs.append(ptlb);
  3814. if (FAILED(hr))
  3815. {
  3816. *pidError = IDE_TEMPLATE_LOAD_TYPELIB_FAILED;
  3817. return hr;
  3818. }
  3819. return S_OK;
  3820. }
  3821. /* ========================================================
  3822. CTemplate::ProcessMetadataCookieSegment
  3823. Parses the metadata comment for cookie information.
  3824. Returns
  3825. HRESULT
  3826. */
  3827. HRESULT
  3828. CTemplate::ProcessMetadataCookieSegment
  3829. (
  3830. const CByteRange& brSegment,
  3831. UINT *pidError,
  3832. CHitObj *pHitObj
  3833. )
  3834. {
  3835. HRESULT hr;
  3836. CByteRange br;
  3837. char *pszName;
  3838. char szFile[MAX_PATH+1];
  3839. TCHAR sztFile[MAX_PATH+1];
  3840. CMBCSToWChar convStr;
  3841. STACK_BUFFER( tempCookie, 64 );
  3842. STACK_BUFFER( tempFile, 64 );
  3843. // Try to get the cookie name
  3844. br = BrValueOfTag(brSegment, CTokenList::tknTagName);
  3845. if (br.IsNull() || (br.m_cb == 0)) {
  3846. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_NAME;
  3847. return E_FAIL;
  3848. }
  3849. if (!tempCookie.Resize(br.m_cb + 1)) {
  3850. *pidError = IDE_OOM;
  3851. return E_FAIL;
  3852. }
  3853. pszName = (char *)tempCookie.QueryPtr();
  3854. if (!pszName)
  3855. {
  3856. *pidError = IDE_OOM;
  3857. return E_FAIL;
  3858. }
  3859. memcpy(pszName, br.m_pb, br.m_cb);
  3860. pszName[br.m_cb] = '\0';
  3861. // Try to get the path to the script
  3862. br = BrValueOfTag(brSegment, CTokenList::tknTagSrc);
  3863. if (br.IsNull() || (br.m_cb >= MAX_PATH) || (br.m_cb == 0))
  3864. {
  3865. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_SRC;
  3866. return E_FAIL;
  3867. }
  3868. memcpy(szFile, br.m_pb, br.m_cb);
  3869. szFile[br.m_cb] = '\0';
  3870. // Convert file to physical path
  3871. Assert(pHitObj->PServer());
  3872. WCHAR *pCookieFile;
  3873. // 6.0 can handle UNICODE. Convert using script code page
  3874. if (FAILED (convStr.Init (szFile,pHitObj->GetCodePage()))) {
  3875. *pidError = IDE_OOM;
  3876. return E_FAIL;
  3877. }
  3878. pCookieFile = convStr.GetString();
  3879. if (FAILED(pHitObj->PServer()->MapPathInternal(0, pCookieFile, sztFile)))
  3880. {
  3881. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_SRC;
  3882. return E_FAIL;
  3883. }
  3884. Normalize(sztFile);
  3885. // Construct 449-echo-cookie object
  3886. C449Cookie *p449 = NULL;
  3887. hr = Create449Cookie(pszName, sztFile, &p449);
  3888. if (FAILED(hr))
  3889. {
  3890. *pidError = IDE_TEMPLATE_LOAD_COOKIESCRIPT_FAILED;
  3891. return hr;
  3892. }
  3893. // Remember 449 cookie in the array
  3894. Assert(p449);
  3895. hr = m_rgp449.append(p449);
  3896. if (FAILED(hr)) {
  3897. *pidError = IDE_TEMPLATE_LOAD_COOKIESCRIPT_FAILED;
  3898. return hr;
  3899. }
  3900. return S_OK;
  3901. }
  3902. /* ========================================================
  3903. CTemplate::GetScriptEngineOfSegment
  3904. Returns script engine name for a script segment.
  3905. Returns
  3906. Byte range containing script engine name
  3907. Side effects
  3908. Advances segment byte range past close tag token
  3909. */
  3910. void
  3911. CTemplate::GetScriptEngineOfSegment
  3912. (
  3913. CByteRange& brSegment, // segment byte range
  3914. CByteRange& brEngine, // script engine name
  3915. CByteRange& brInclude // value of SRC tag
  3916. )
  3917. {
  3918. BYTE* pbCloseTag; // ptr to close of start tag
  3919. // tags contained in start tag
  3920. CByteRange brTags = BrTagsFromSegment(brSegment, CTokenList::tknCloseTaggedScript, &pbCloseTag);
  3921. // if no close found, throw error
  3922. if(pbCloseTag == NULL)
  3923. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  3924. Assert(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer));
  3925. // get engine name from tags
  3926. brEngine = BrValueOfTag(brTags, CTokenList::tknTagLanguage);
  3927. if(brEngine.IsNull())
  3928. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_ENGINE_NAME);
  3929. // Get SRC attribute from tags
  3930. brInclude = BrValueOfTag(brTags, CTokenList::tknTagSrc);
  3931. // advance segment past close tag token
  3932. gm_pTokenList->MovePastToken(CTokenList::tknCloseTag, pbCloseTag, brSegment);
  3933. }
  3934. /* ========================================================
  3935. CTemplate::ProcessTaggedScriptSegment
  3936. Processes a tagged script segment: within tagged script we
  3937. honor plain text (passed through as script text) and HTML comments.
  3938. See bug 423 for istudio scenarios that require this behavior.
  3939. Returns
  3940. Nothing
  3941. Side effects
  3942. None
  3943. */
  3944. void
  3945. CTemplate::ProcessTaggedScriptSegment
  3946. (
  3947. CByteRange& brSegment, // segment byte range
  3948. CFileMap* pfilemapCurrent,// ptr to filemap of parent file
  3949. CWorkStore& WorkStore, // working storage for source segments
  3950. CHitObj* pHitObj // Browser request object
  3951. )
  3952. {
  3953. _TOKEN* rgtknOpeners; // array of permitted open tokens
  3954. _TOKEN tknOpeners[1];
  3955. UINT ctknOpeners; // count of permitted open tokens
  3956. // populate array of permitted open tokens
  3957. ctknOpeners = 1;
  3958. rgtknOpeners = tknOpeners;
  3959. rgtknOpeners[0] = CTokenList::tknOpenHTMLComment;
  3960. // Process source segments embedded within tagged script segment
  3961. while(!brSegment.IsNull())
  3962. ExtractAndProcessSegment(
  3963. brSegment, // byte range to search for next segment-opening token
  3964. ssegTaggedScript, // type of 'leading', i.e. pre-token, source segment
  3965. rgtknOpeners, // array of permitted open tokens
  3966. ctknOpeners, // count of permitted open tokens
  3967. pfilemapCurrent, // ptr to filemap of parent file
  3968. WorkStore, // working storage for source segments
  3969. pHitObj, // Browser request object
  3970. TRUE, // script tag has been processed
  3971. FALSE // NOT in HTML segment
  3972. );
  3973. }
  3974. /* ============================================================================
  3975. CTemplate::ProcessObjectSegment
  3976. Processes an object segment.
  3977. Returns
  3978. Nothing
  3979. Side effects
  3980. throws on error
  3981. */
  3982. void
  3983. CTemplate::ProcessObjectSegment
  3984. (
  3985. CByteRange& brSegment, // segment byte range
  3986. CFileMap* pfilemapCurrent,// ptr to filemap of parent file
  3987. CWorkStore& WorkStore, // working storage for source segments
  3988. UINT idSequence // segment sequence id
  3989. )
  3990. {
  3991. BYTE* pbCloseTag; // ptr to close of start tag
  3992. // tags contained in start tag
  3993. CByteRange brTags = BrTagsFromSegment(brSegment, CTokenList::tknCloseObject, &pbCloseTag);
  3994. // if no close found, bail on error
  3995. if(pbCloseTag == NULL)
  3996. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  3997. // if this is a server object (RUNAT=Server), process its tags
  3998. if(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer))
  3999. {
  4000. CLSID ClsId; // clsid
  4001. // get name value
  4002. CByteRange brName = BrValueOfTag(brTags, CTokenList::tknTagID);
  4003. // if name is null, error out
  4004. if(brName.IsNull())
  4005. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_OBJECT_NAME);
  4006. if(!FValidObjectName(brName))
  4007. ThrowErrorSingleInsert(brName.m_pb, IDE_TEMPLATE_INVALID_OBJECT_NAME, brName.m_pb, brName.m_cb);
  4008. // get values for ClassID and ProgID tags
  4009. CByteRange brClassIDText = BrValueOfTag(brTags, CTokenList::tknTagClassID);
  4010. CByteRange brProgIDText = BrValueOfTag(brTags, CTokenList::tknTagProgID);
  4011. if(!brClassIDText.IsNull())
  4012. // if we find a text class id, set clsid with it
  4013. // NOTE progid tag is ignored if classid tag exists
  4014. GetCLSIDFromBrClassIDText(brClassIDText, &ClsId);
  4015. else if(!brProgIDText.IsNull())
  4016. // else if we find a prog id, resolve it into a class id
  4017. GetCLSIDFromBrProgIDText(brProgIDText, &ClsId);
  4018. else
  4019. // else, throw error; can't create an object without at least one of classid or progid
  4020. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_NO_CLASSID_PROGID, brName.m_pb, brName.m_cb);
  4021. // set scope; bail if bogus
  4022. CompScope csScope = csUnknown;
  4023. CByteRange brScope = BrValueOfTag(brTags, CTokenList::tknTagScope);
  4024. if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValuePage)) || brScope.IsNull())
  4025. // non-existent scope tag defaults to page scope
  4026. csScope = csPage;
  4027. else if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValueApplication)))
  4028. csScope = csAppln;
  4029. else if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValueSession)))
  4030. csScope = csSession;
  4031. else
  4032. ThrowError(brTags.m_pb, IDE_TEMPLATE_BAD_OBJECT_SCOPE);
  4033. if(!m_fGlobalAsa && csScope != csPage)
  4034. // error out on non-page-level object if we are processing anything but global.asa
  4035. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_BAD_PAGE_OBJECT_SCOPE, brName.m_pb, brName.m_cb);
  4036. else if(m_fGlobalAsa && csScope == csPage)
  4037. // error out on page-level object if we are processing global.asa
  4038. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_BAD_GLOBAL_OBJECT_SCOPE, brName.m_pb, brName.m_cb);
  4039. // set threading model
  4040. CompModel cmModel = cmSingle;
  4041. CompModelFromCLSID(ClsId, &cmModel);
  4042. // append object-info to store
  4043. WorkStore.m_ObjectInfoStore.AppendObject(brName, ClsId, csScope, cmModel, idSequence);
  4044. }
  4045. }
  4046. /* ============================================================================
  4047. CTemplate::GetCLSIDFromBrClassIDText
  4048. Sets a clsid from a byte range containing an ANSI text version of a class id
  4049. Returns
  4050. ptr to clsid (out-parameter)
  4051. Side effects
  4052. throws on error
  4053. */
  4054. void
  4055. CTemplate::GetCLSIDFromBrClassIDText
  4056. (
  4057. CByteRange& brClassIDText,
  4058. LPCLSID pclsid
  4059. )
  4060. {
  4061. // if class id text starts with its standard object tag prefix, advance past it
  4062. if(!_strnicmp((char*)brClassIDText.m_pb, "clsid:", 6))
  4063. brClassIDText.Advance(6);
  4064. // if class id text is bracketed with {}, adjust byte range to strip them
  4065. // NOTE we always add {} below, because normal case is that they are missing from input text
  4066. if(*brClassIDText.m_pb == '{')
  4067. brClassIDText.Advance(1);
  4068. if(*(brClassIDText.m_pb + brClassIDText.m_cb - 1) == '}')
  4069. brClassIDText.m_cb--;
  4070. // Allocate a wide char string for the string version of class id
  4071. // NOTE we add 3 characters to hold {} and null terminator
  4072. OLECHAR* pszWideClassID = new WCHAR[brClassIDText.m_cb + 3];
  4073. if (NULL == pszWideClassID)
  4074. THROW(E_OUTOFMEMORY);
  4075. // start wide string class id with left brace
  4076. pszWideClassID[0] = '{';
  4077. // Convert the string class id to wide chars
  4078. if (0 == MultiByteToWideChar( CP_ACP, // ANSI code page
  4079. MB_ERR_INVALID_CHARS, // err on invalid chars
  4080. (LPCSTR)brClassIDText.m_pb, // input ANSI string version of class id
  4081. brClassIDText.m_cb, // length of input string
  4082. pszWideClassID + 1, // location for output wide string class id
  4083. brClassIDText.m_cb // size of output buffer
  4084. ))
  4085. {
  4086. delete [] pszWideClassID;
  4087. THROW(E_FAIL);
  4088. }
  4089. // append right brace to wide string
  4090. pszWideClassID[brClassIDText.m_cb + 1] = '}';
  4091. // Null terminate the wide string
  4092. pszWideClassID[brClassIDText.m_cb + 2] = NULL;
  4093. // Now get the clsid from wide string class id
  4094. if(FAILED(CLSIDFromString(pszWideClassID, pclsid)))
  4095. {
  4096. delete [] pszWideClassID;
  4097. ThrowErrorSingleInsert(brClassIDText.m_pb, IDE_TEMPLATE_BAD_CLASSID, brClassIDText.m_pb, brClassIDText.m_cb);
  4098. }
  4099. if(NULL != pszWideClassID)
  4100. delete [] pszWideClassID;
  4101. }
  4102. /* ===================================================================
  4103. CTemplate::GetCLSIDFromBrProgIDText
  4104. Gets a clsid from the registry given a ProgID
  4105. Returns
  4106. ptr to clsid (out-parameter)
  4107. Side effects
  4108. throws on error
  4109. */
  4110. void
  4111. CTemplate::GetCLSIDFromBrProgIDText
  4112. (
  4113. CByteRange& brProgIDText,
  4114. LPCLSID pclsid
  4115. )
  4116. {
  4117. // allocate a wide char string for ProgID plus null terminator
  4118. OLECHAR* pszWideProgID = new WCHAR[brProgIDText.m_cb + 1];
  4119. if (NULL == pszWideProgID)
  4120. THROW(E_OUTOFMEMORY);
  4121. // Convert the string class id to wide chars
  4122. if (0 == MultiByteToWideChar( CP_ACP, // ANSI code page
  4123. MB_ERR_INVALID_CHARS, // err on invalid chars
  4124. (LPCSTR)brProgIDText.m_pb, // input ANSI string version of prog id
  4125. brProgIDText.m_cb, // length of input string
  4126. pszWideProgID, // location for output wide string prog id
  4127. brProgIDText.m_cb // size of output buffer
  4128. ))
  4129. {
  4130. delete [] pszWideProgID; pszWideProgID = NULL;
  4131. THROW(E_FAIL);
  4132. }
  4133. // Null terminate the wide string
  4134. pszWideProgID[brProgIDText.m_cb] = NULL;
  4135. // Now get clsid from ProgID
  4136. if(FAILED(CLSIDFromProgID(pszWideProgID, pclsid)))
  4137. {
  4138. delete [] pszWideProgID; pszWideProgID = NULL;
  4139. ThrowErrorSingleInsert(brProgIDText.m_pb, IDE_TEMPLATE_BAD_PROGID, brProgIDText.m_pb, brProgIDText.m_cb);
  4140. }
  4141. // Cache ProgId to CLSID mapping
  4142. g_TypelibCache.RememberProgidToCLSIDMapping(pszWideProgID, *pclsid);
  4143. if (NULL != pszWideProgID)
  4144. delete [] pszWideProgID;
  4145. }
  4146. /* ============================================================================
  4147. CTemplate::FValidObjectName
  4148. Determines whether an object name clashes with a Denali intrinsic object name.
  4149. Returns
  4150. TRUE or FALSE
  4151. Side effects
  4152. None
  4153. */
  4154. BOOLB
  4155. CTemplate::FValidObjectName
  4156. (
  4157. CByteRange& brName // object name
  4158. )
  4159. {
  4160. if(brName.FMatchesSz(SZ_OBJ_APPLICATION))
  4161. return FALSE;
  4162. if(brName.FMatchesSz(SZ_OBJ_REQUEST))
  4163. return FALSE;
  4164. if(brName.FMatchesSz(SZ_OBJ_RESPONSE))
  4165. return FALSE;
  4166. if(brName.FMatchesSz(SZ_OBJ_SERVER))
  4167. return FALSE;
  4168. if(brName.FMatchesSz(SZ_OBJ_CERTIFICATE))
  4169. return FALSE;
  4170. if(brName.FMatchesSz(SZ_OBJ_SESSION))
  4171. return FALSE;
  4172. if(brName.FMatchesSz(SZ_OBJ_SCRIPTINGNAMESPACE))
  4173. return FALSE;
  4174. return TRUE;
  4175. }
  4176. /* ============================================================================
  4177. CTemplate::ProcessIncludeFile
  4178. Processes an include file.
  4179. Returns
  4180. Nothing
  4181. */
  4182. void
  4183. CTemplate::ProcessIncludeFile
  4184. (
  4185. CByteRange& brSegment, // segment byte range
  4186. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  4187. CWorkStore& WorkStore, // current working storage
  4188. UINT idSequence, // sequence #
  4189. CHitObj* pHitObj, // Browser request object pointer
  4190. BOOL fIsHTML
  4191. )
  4192. {
  4193. CByteRange brFileSpec; // filespec of include file
  4194. BOOLB fVirtual = FALSE; // is include filespec virtual?
  4195. // filespec of include file (sz)
  4196. CHAR szFileSpec[MAX_PATH + 1];
  4197. LPSTR szTemp = szFileSpec; // temp ptr to filespec
  4198. // get value of FILE tag
  4199. brFileSpec = BrValueOfTag(brSegment, CTokenList::tknTagFile);
  4200. if(brFileSpec.IsNull())
  4201. {
  4202. // if we found no FILE tag, get value of VIRTUAL tag
  4203. brFileSpec = BrValueOfTag(brSegment, CTokenList::tknTagVirtual);
  4204. fVirtual = TRUE;
  4205. }
  4206. if(brFileSpec.IsNull())
  4207. // if we found neither, error out
  4208. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_INCLUDE_NAME);
  4209. if (brFileSpec.m_cb>MAX_PATH)
  4210. {
  4211. // return the last MAX_PATH chars of the file name. This is done
  4212. // this way to avoid a Error Too Long message when the include
  4213. // file spec is exceedingly long.
  4214. char fileNameLast[MAX_PATH+4];
  4215. strcpy(fileNameLast, "...");
  4216. strcpy(fileNameLast+3, (LPSTR)(brFileSpec.m_pb+brFileSpec.m_cb-MAX_PATH));
  4217. ThrowErrorSingleInsert(brFileSpec.m_pb,
  4218. IDE_TEMPLATE_BAD_INCLUDE,
  4219. brFileSpec.m_pb,
  4220. brFileSpec.m_cb);
  4221. }
  4222. // NOTE we manipulate temp sz to preserve szFileSpec
  4223. if(fVirtual)
  4224. {
  4225. if(*brFileSpec.m_pb == '\\')
  4226. {
  4227. // if VIRTUAL path starts with backslash, replace it with fwd slash
  4228. *szTemp++ = '/';
  4229. brFileSpec.Advance(1);
  4230. }
  4231. else if(*brFileSpec.m_pb != '/')
  4232. // if VIRTUAL path starts with anything other than fwd slash or backslash, prepend fwd slash
  4233. *szTemp++ = '/';
  4234. }
  4235. // append supplied path to temp sz
  4236. strncpy(szTemp, (LPCSTR) brFileSpec.m_pb, brFileSpec.m_cb);
  4237. szTemp[brFileSpec.m_cb] = NULL;
  4238. if(!fVirtual)
  4239. {
  4240. // if FILE filespec starts with \ or /, hurl
  4241. if(*szFileSpec == '\\' || *szFileSpec == '/')
  4242. ThrowErrorSingleInsert(
  4243. brFileSpec.m_pb,
  4244. IDE_TEMPLATE_BAD_FILE_TAG,
  4245. brFileSpec.m_pb,
  4246. brFileSpec.m_cb
  4247. );
  4248. }
  4249. // NOTE: szFileSpec is the doctored path (it possibly has "/" prepended.
  4250. // brFileSpec is used as the error location.
  4251. //
  4252. ProcessIncludeFile2(szFileSpec, brFileSpec, pfilemapCurrent, WorkStore, idSequence, pHitObj, fIsHTML);
  4253. }
  4254. /* ============================================================================
  4255. CTemplate::ProcessIncludeFile2
  4256. adds a #include file to the CTemplate and starts the template to processing
  4257. the file.
  4258. Returns
  4259. Nothing
  4260. Side effects
  4261. Calls GetSegmentsFromFile recursively
  4262. NOTE - kind of an oddball thing here. The szFileSpec in this case is
  4263. intentionally ANSI as it came from the ASP script content. It may need
  4264. to be converted to UNICODE.
  4265. */
  4266. void
  4267. CTemplate::ProcessIncludeFile2
  4268. (
  4269. CHAR * szAnsiFileSpec, // file to include
  4270. CByteRange& brErrorLocation, // ByteRange in source where errors should be reported
  4271. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  4272. CWorkStore& WorkStore, // current working storage
  4273. UINT idSequence, // sequence #
  4274. CHitObj* pHitObj, // Browser request object pointer
  4275. BOOL fIsHTML
  4276. )
  4277. {
  4278. HRESULT hr;
  4279. TCHAR *szFileSpec;
  4280. #if UNICODE
  4281. CMBCSToWChar convFileSpec;
  4282. if (FAILED(hr = convFileSpec.Init(szAnsiFileSpec, pHitObj->GetCodePage()))) {
  4283. THROW(hr);
  4284. }
  4285. szFileSpec = convFileSpec.GetString();
  4286. #else
  4287. szFileSpec = szAnsiFileSpec;
  4288. #endif
  4289. // if parent paths are disallowed and filespec contains parent dir reference, hurl
  4290. if (!pHitObj->QueryAppConfig()->fEnableParentPaths() && _tcsstr(szFileSpec, _T("..")))
  4291. ThrowErrorSingleInsert(
  4292. brErrorLocation.m_pb,
  4293. IDE_TEMPLATE_DISALLOWED_PARENT_PATH,
  4294. brErrorLocation.m_pb,
  4295. brErrorLocation.m_cb
  4296. );
  4297. TRY
  4298. AppendMapFile(
  4299. szFileSpec,
  4300. pfilemapCurrent,
  4301. (szFileSpec[0] == _T('/')) || (szFileSpec[0] == _T('\\')), // fVirtual
  4302. pHitObj, // main file's hit object
  4303. FALSE // not the global.asa file
  4304. );
  4305. CATCH(hrException)
  4306. // MapFile() threw an exception: delete last filemap's memory and decrement filemap counter
  4307. // NOTE this is a bit hokey, but we need to do it here rather than AppendMapFile (where we allocated)
  4308. // because its other caller(s) may not want this behavior
  4309. delete m_rgpFilemaps[m_cFilemaps-- - 1];
  4310. /* NOTE exception code from MapFile() is overloaded: it can sometimes
  4311. be an error message id, sometimes a true exception
  4312. NOTE security error causes exception E_USER_LACKS_PERMISSIONS, rather than error id,
  4313. but we pass it thru as if it were an error id because the various error-catch routines
  4314. know how to handle E_USER_LACKS_PERMISSIONS specially.
  4315. */
  4316. UINT idErrMsg;
  4317. if(hrException == IDE_TEMPLATE_CYCLIC_INCLUDE || hrException == E_USER_LACKS_PERMISSIONS)
  4318. // exception code is really an error message id: set err id to it
  4319. idErrMsg = hrException;
  4320. else if(hrException == E_COULDNT_OPEN_SOURCE_FILE)
  4321. // exception is generic couldn't-open-file : set err id to generic bad-file error
  4322. idErrMsg = IDE_TEMPLATE_BAD_INCLUDE;
  4323. else
  4324. // other exception: re-throw
  4325. THROW(hrException);
  4326. ThrowErrorSingleInsert(
  4327. brErrorLocation.m_pb,
  4328. idErrMsg,
  4329. brErrorLocation.m_pb,
  4330. brErrorLocation.m_cb
  4331. );
  4332. END_TRY
  4333. // store ptr to current file map in local before recursive call (which may increment m_cFilemaps)
  4334. CFileMap* pfilemap = m_rgpFilemaps[m_cFilemaps - 1];
  4335. // get inc-file object from cache
  4336. CIncFile* pIncFile;
  4337. if(FAILED(hr = g_IncFileMap.GetIncFile(pfilemap->m_szPathTranslated, &pIncFile)))
  4338. THROW(hr);
  4339. // add this template to inc-file's template list
  4340. if (FAILED(hr = pIncFile->AddTemplate(this)))
  4341. THROW(hr);
  4342. // set filemap's inc-file ptr
  4343. pfilemap->m_pIncFile = pIncFile;
  4344. // get source segments from include file
  4345. // bugs 1363, 1364: process include file only after we establish dependencies;
  4346. // required for cache flushing to work correctly after compile errors
  4347. GetSegmentsFromFile(*pfilemap, WorkStore, pHitObj, fIsHTML);
  4348. }
  4349. /* ===================================================================
  4350. CTemplate::GetOpenToken
  4351. Returns the token index of and a ptr to the first valid open token
  4352. in search range. For the open token to be valid, we must bypass
  4353. segments we should not process, e.g. scripts or objects not tagged as 'server'
  4354. Returns
  4355. ptr to open token; ptr to open token enum value (out-parameter)
  4356. Side effects
  4357. None
  4358. */
  4359. BYTE*
  4360. CTemplate::GetOpenToken
  4361. (
  4362. CByteRange brSearch, // (ByVal) byte range to search for next segment-opening token
  4363. SOURCE_SEGMENT ssegLeading, // type of 'leading', i.e. pre-token, source segment
  4364. // (only used when deciding to ignore non-SSI comments)
  4365. _TOKEN* rgtknOpeners, // array of permitted open tokens
  4366. UINT ctknOpeners, // count of permitted open tokens
  4367. _TOKEN* ptknOpen // ptr to open token enum value (out-parameter)
  4368. )
  4369. {
  4370. BYTE* pbTokenOpen = NULL; // ptr to opening token
  4371. // keep getting segment-opening tokens until we find one that we need to process
  4372. while(TRUE)
  4373. {
  4374. // Get next open token in search range
  4375. *ptknOpen = gm_pTokenList->NextOpenToken(
  4376. brSearch,
  4377. rgtknOpeners,
  4378. ctknOpeners,
  4379. &pbTokenOpen,
  4380. m_wCodePage
  4381. );
  4382. /* Certain tokens must be followed immediately by white space; others need not.
  4383. NOTE it is pure coincidence that the 'white-space tokens' are also those that
  4384. get special processing below; hence we handle white space issue there.
  4385. If we ever add another 'white-space token' that doesn't require the special processing,
  4386. we will need to handle the white space issue here.
  4387. */
  4388. /* Similar thing applies to non-include and non-metadata HTML
  4389. comments. We really don't care for them to generate their
  4390. own segments -- we can reduce number of Response.WriteBlock()
  4391. calls by considering them part of the preceding HTML segment.
  4392. */
  4393. if (*ptknOpen == CTokenList::tknOpenHTMLComment)
  4394. {
  4395. if (ssegLeading != ssegHTML) // if not inside HTML
  4396. break; // generate a new segment
  4397. // for HTML comments check if it is an include or metadata
  4398. // and if not, this is not a separate segment - keep on looking
  4399. // for the next opener
  4400. // advance search range to just past open token
  4401. gm_pTokenList->MovePastToken(*ptknOpen, pbTokenOpen, brSearch);
  4402. // find end of comment
  4403. BYTE *pbClose = gm_pTokenList->GetToken(CTokenList::tknCloseHTMLComment,
  4404. brSearch, m_wCodePage);
  4405. if (pbClose == NULL)
  4406. {
  4407. // Error -- let other code handle this
  4408. break;
  4409. }
  4410. // construct comment byte range to limit search to it
  4411. CByteRange brComment = brSearch;
  4412. brComment.m_cb = DIFF(pbClose - brSearch.m_pb);
  4413. // look for metadata and include (only inside the comment)
  4414. if (gm_pTokenList->GetToken(CTokenList::tknCommandINCLUDE,
  4415. brComment, m_wCodePage))
  4416. {
  4417. // SSI inclide -- keep it
  4418. break;
  4419. }
  4420. else if (gm_pTokenList->GetToken(CTokenList::tknTagMETADATA,
  4421. brComment, m_wCodePage))
  4422. {
  4423. // METADATA -- keep it
  4424. break;
  4425. }
  4426. else if (gm_pTokenList->GetToken(CTokenList::tknTagFPBot,
  4427. brComment, m_wCodePage))
  4428. {
  4429. // METADATA -- keep it
  4430. break;
  4431. }
  4432. else
  4433. {
  4434. // Regular comment - ignore it
  4435. goto LKeepLooking;
  4436. }
  4437. }
  4438. else if (*ptknOpen == CTokenList::tknOpenTaggedScript || *ptknOpen == CTokenList::tknOpenObject)
  4439. {
  4440. /* if token was script or object tag, check to see if:
  4441. a) it is followed immediately by white space; if not keep looking
  4442. b) it opens a well-formed segment, i.e. one with a proper close tag; if not, throw error
  4443. c) it is designated runat-server; if not keep looking
  4444. */
  4445. // advance search range to just past open token
  4446. gm_pTokenList->MovePastToken(*ptknOpen, pbTokenOpen, brSearch);
  4447. // bug 760: if token is not followed immediately by white space, keep looking
  4448. if(!brSearch.IsNull())
  4449. if(!FWhiteSpace(*brSearch.m_pb))
  4450. goto LKeepLooking;
  4451. // ptr to close of start tag
  4452. BYTE* pbCloseTag;
  4453. // tags contained in start tag
  4454. CByteRange brTags = BrTagsFromSegment(
  4455. brSearch,
  4456. GetComplementToken(*ptknOpen),
  4457. &pbCloseTag
  4458. );
  4459. if(pbCloseTag == NULL)
  4460. {
  4461. // if no close tag, throw error
  4462. if(*ptknOpen == CTokenList::tknOpenObject)
  4463. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  4464. else if(*ptknOpen == CTokenList::tknOpenTaggedScript)
  4465. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  4466. }
  4467. // if this is a server object (RUNAT=Server), we will process it; else keep looking
  4468. if(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer))
  4469. break;
  4470. }
  4471. else
  4472. {
  4473. // if token was other than script or object tag, or comment
  4474. // we should process segment;
  4475. // hence we have found our open token, so break
  4476. break;
  4477. }
  4478. LKeepLooking: ;
  4479. }
  4480. return pbTokenOpen;
  4481. }
  4482. /* ===================================================================
  4483. CTemplate::GetCloseToken
  4484. Returns a ptr to the next token of type tknClose.
  4485. Returns
  4486. ptr to close token
  4487. Side effects
  4488. Detects and errors out on attempt to nest tagged script or object blocks.
  4489. */
  4490. BYTE*
  4491. CTemplate::GetCloseToken
  4492. (
  4493. CByteRange brSearch, // (ByVal) byte range to search
  4494. _TOKEN tknClose // close token
  4495. )
  4496. {
  4497. BYTE* pbTokenClose = gm_pTokenList->GetToken(tknClose, brSearch, m_wCodePage);
  4498. if(pbTokenClose != NULL)
  4499. if(tknClose == CTokenList::tknCloseTaggedScript || tknClose == CTokenList::tknCloseObject)
  4500. {
  4501. CByteRange brSegment;
  4502. BYTE* pbTokenOpen;
  4503. brSegment.m_pb = brSearch.m_pb;
  4504. brSegment.m_cb = DIFF(pbTokenClose - brSearch.m_pb);
  4505. if(NULL != (pbTokenOpen = gm_pTokenList->GetToken(GetComplementToken(tknClose), brSegment, m_wCodePage)))
  4506. {
  4507. if(tknClose == CTokenList::tknCloseTaggedScript)
  4508. ThrowError(pbTokenOpen, IDE_TEMPLATE_NESTED_TSCRIPT);
  4509. else if(tknClose == CTokenList::tknCloseObject)
  4510. ThrowError(pbTokenOpen, IDE_TEMPLATE_NESTED_OBJECT);
  4511. }
  4512. }
  4513. return pbTokenClose;
  4514. }
  4515. /*===================================================================
  4516. CTemplate::GetComplementToken
  4517. Returns a token's compement token.
  4518. Returns
  4519. Complement token
  4520. Side effects
  4521. None
  4522. */
  4523. _TOKEN
  4524. CTemplate::GetComplementToken
  4525. (
  4526. _TOKEN tkn
  4527. )
  4528. {
  4529. switch(tkn)
  4530. {
  4531. // open tokens
  4532. case CTokenList::tknOpenPrimaryScript:
  4533. return CTokenList::tknClosePrimaryScript;
  4534. case CTokenList::tknOpenTaggedScript:
  4535. return CTokenList::tknCloseTaggedScript;
  4536. case CTokenList::tknOpenObject:
  4537. return CTokenList::tknCloseObject;
  4538. case CTokenList::tknOpenHTMLComment:
  4539. return CTokenList::tknCloseHTMLComment;
  4540. // close tokens
  4541. case CTokenList::tknClosePrimaryScript:
  4542. return CTokenList::tknOpenPrimaryScript;
  4543. case CTokenList::tknCloseTaggedScript:
  4544. return CTokenList::tknOpenTaggedScript;
  4545. case CTokenList::tknCloseObject:
  4546. return CTokenList::tknOpenObject;
  4547. case CTokenList::tknCloseHTMLComment:
  4548. return CTokenList::tknOpenHTMLComment;
  4549. }
  4550. Assert(FALSE);
  4551. return CTokenList::tknEOF;
  4552. }
  4553. /*===================================================================
  4554. CTemplate::GetSegmentOfOpenToken
  4555. Returns the segment type of an open token.
  4556. Returns
  4557. source segment type of open token
  4558. Side effects
  4559. None
  4560. */
  4561. CTemplate::SOURCE_SEGMENT
  4562. CTemplate::GetSegmentOfOpenToken
  4563. (
  4564. _TOKEN tknOpen
  4565. )
  4566. {
  4567. switch(tknOpen)
  4568. {
  4569. case CTokenList::tknOpenPrimaryScript:
  4570. return ssegPrimaryScript;
  4571. case CTokenList::tknOpenTaggedScript:
  4572. return ssegTaggedScript;
  4573. case CTokenList::tknOpenObject:
  4574. return ssegObject;
  4575. case CTokenList::tknOpenHTMLComment:
  4576. return ssegHTMLComment;
  4577. }
  4578. return ssegHTML;
  4579. }
  4580. /* ========================================================
  4581. CTemplate::BrTagsFromSegment
  4582. Returns the tag range from an HTML start tag
  4583. Returns
  4584. tag byte range
  4585. Side effects
  4586. none
  4587. */
  4588. CByteRange
  4589. CTemplate::BrTagsFromSegment
  4590. (
  4591. CByteRange brSegment, // segment byte range
  4592. _TOKEN tknClose, // close token
  4593. BYTE** ppbCloseTag // ptr-to-ptr to close tag - returned to caller
  4594. )
  4595. {
  4596. CByteRange brTags; // tags return value - NOTE constructed null
  4597. // ptr to close token - NOTE null if none within segment byte range
  4598. BYTE* pbTokenClose = GetCloseToken(brSegment, tknClose);
  4599. // if no close tag found, return null tags
  4600. if(NULL == (*ppbCloseTag = gm_pTokenList->GetToken(CTokenList::tknCloseTag, brSegment, m_wCodePage)))
  4601. goto Exit;
  4602. // if next non-null close token occurs before close tag, close tag is invalid; return nulls
  4603. if((pbTokenClose != NULL) && (*ppbCloseTag > pbTokenClose ))
  4604. {
  4605. *ppbCloseTag = NULL;
  4606. goto Exit;
  4607. }
  4608. // crack tags from header tag
  4609. brTags.m_pb = brSegment.m_pb;
  4610. brTags.m_cb = DIFF(*ppbCloseTag - brSegment.m_pb);
  4611. Exit:
  4612. return brTags;
  4613. }
  4614. /* ========================================================
  4615. CTemplate::BrValueOfTag
  4616. Returns a tag's value from a byte range; null if tag is not found
  4617. NOTE value search algorithm per W3 HTML spec - see www.w3.org
  4618. Returns
  4619. byte range of tag's value
  4620. pfTagExists - does the tag exist in tags byte range? (out-parameter)
  4621. NOTE we default *pfTagExists = TRUE; most callers don't care and omit this parameter
  4622. Side effects
  4623. none
  4624. */
  4625. CByteRange
  4626. CTemplate::BrValueOfTag
  4627. (
  4628. CByteRange brTags, // tags byte range
  4629. _TOKEN tknTagName // tag name token
  4630. )
  4631. {
  4632. CByteRange brTemp = brTags; // temp byte range
  4633. CByteRange brValue; // byte range of value for the given tag - NOTE constructed null
  4634. char chDelimiter = NULL; // value delimiter
  4635. // ptr to tag name
  4636. BYTE* pbTagName = GetTagName(brTags, tknTagName);
  4637. // If we did not find tag, return
  4638. if(pbTagName == NULL)
  4639. return brValue;
  4640. // Move past tag name token and pre-separator white space
  4641. brTemp.Advance(DIFF(pbTagName - brTags.m_pb) + CCH_TOKEN(tknTagName));
  4642. LTrimWhiteSpace(brTemp);
  4643. if(brTemp.IsNull())
  4644. goto Exit;
  4645. // If we did not find separator, return
  4646. if(*brTemp.m_pb != CH_ATTRIBUTE_SEPARATOR)
  4647. goto Exit;
  4648. // Move past separator and post-separator white space
  4649. brTemp.Advance(sizeof(CH_ATTRIBUTE_SEPARATOR));
  4650. LTrimWhiteSpace(brTemp);
  4651. if(brTemp.IsNull())
  4652. goto Exit;
  4653. // If value begins with a quote mark, cache it as delimiter
  4654. if((*brTemp.m_pb == CH_SINGLE_QUOTE) || (*brTemp.m_pb == CH_DOUBLE_QUOTE))
  4655. chDelimiter = *brTemp.m_pb;
  4656. if(chDelimiter)
  4657. {
  4658. // move past delimiter
  4659. brTemp.Advance(sizeof(chDelimiter));
  4660. if(brTemp.IsNull())
  4661. goto Exit;
  4662. }
  4663. // provisionally set value to temp byte range
  4664. brValue = brTemp;
  4665. // advance temp byte range to end of value range
  4666. while(
  4667. (chDelimiter && (*brTemp.m_pb != chDelimiter)) // if we have a delimiter, find next delimiter
  4668. || (!chDelimiter && (!FWhiteSpace(*brTemp.m_pb))) // if we have no delimiter, find next white space
  4669. )
  4670. {
  4671. // advance temp byte range
  4672. brTemp.Advance(1);
  4673. if(brTemp.IsNull())
  4674. {
  4675. if(chDelimiter)
  4676. // we found no closing delimiter, so error out
  4677. ThrowErrorSingleInsert(brValue.m_pb, IDE_TEMPLATE_NO_ATTRIBUTE_DELIMITER,
  4678. pbTagName, CCH_TOKEN(tknTagName));
  4679. else
  4680. // value runs to end of temp byte range, so exit (since we already init'ed to temp)
  4681. goto Exit;
  4682. }
  4683. }
  4684. // set byte count so that value points to delimited range
  4685. brValue.m_cb = DIFF(brTemp.m_pb - brValue.m_pb);
  4686. Exit:
  4687. // if tag is empty, raise an error
  4688. if (brValue.IsNull())
  4689. {
  4690. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_VALUE_REQUIRED, pbTagName, CCH_TOKEN(tknTagName));
  4691. }
  4692. // enforce mandatory tag values if required
  4693. if(tknTagName == CTokenList::tknTagRunat)
  4694. {
  4695. if(!brValue.FMatchesSz(SZ_TOKEN(CTokenList::tknValueServer)))
  4696. ThrowError(brTags.m_pb, IDE_TEMPLATE_RUNAT_NOT_SERVER);
  4697. }
  4698. return brValue;
  4699. }
  4700. /* ============================================================================
  4701. CTemplate::CompTagName
  4702. Compares characters in two buffers (case-insensitive) and returns TRUE or FALSE
  4703. Side effects
  4704. none
  4705. */
  4706. BOOL
  4707. CTemplate::CompTagName
  4708. (
  4709. CByteRange &brTags, // tags byte range
  4710. _TOKEN tknTagName // tag name token
  4711. )
  4712. {
  4713. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4714. UINT cbAttributeName = CCH_TOKEN(tknTagName); // length of tag name
  4715. LPSTR pszAttributeName = SZ_TOKEN(tknTagName); // tag name string
  4716. // search for potential matches on tag name string, case-insensitive
  4717. if(!brTemp.IsNull())
  4718. if( 0 == _memicmp( brTemp.m_pb, pszAttributeName, cbAttributeName ))
  4719. return TRUE;
  4720. return FALSE;
  4721. }
  4722. /* ============================================================================
  4723. CTemplate::GetTagName
  4724. Returns a ptr to a tag name in a byte range; null if not found
  4725. Returns
  4726. ptr to tag name
  4727. Side effects
  4728. none
  4729. */
  4730. BYTE*
  4731. CTemplate::GetTagName
  4732. (
  4733. CByteRange brTags, // tags byte range
  4734. _TOKEN tknTagName // tag name token
  4735. )
  4736. {
  4737. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4738. UINT cbAttributeName = CCH_TOKEN(tknTagName); // length of tag name
  4739. LPSTR pszAttributeName = SZ_TOKEN(tknTagName); // tag name string
  4740. // PREFIX: pszAttributeName could be NULL, though I don't think that can happen.
  4741. Assert (pszAttributeName != NULL);
  4742. while(TRUE)
  4743. {
  4744. // search for potential matches on tag name string, case-insensitive
  4745. while(!brTemp.IsNull())
  4746. {
  4747. if(0 == _strnicmp((char*)brTemp.m_pb, pszAttributeName, cbAttributeName ))
  4748. break;
  4749. brTemp.Advance(1);
  4750. }
  4751. // if we did not find tag name string at all, return 'not found'
  4752. if(brTemp.IsNull())
  4753. goto NotFound;
  4754. // if it is a valid HTML tag name, return it
  4755. if(FTagName(brTemp.m_pb, cbAttributeName))
  4756. goto Exit;
  4757. // if we found a matching but invalid substring, advance beyond it so we can search again
  4758. brTemp.Advance(cbAttributeName);
  4759. // if we have exhausted search range, return 'not found'
  4760. if(brTemp.IsNull())
  4761. goto NotFound;
  4762. }
  4763. Exit:
  4764. return brTemp.m_pb;
  4765. NotFound:
  4766. return NULL;
  4767. }
  4768. /* ============================================================================
  4769. CTemplate::GetTag
  4770. Returns a ptr to a tag name in a byte range; null if not found
  4771. Returns
  4772. ptr to tag name
  4773. Side effects
  4774. none
  4775. */
  4776. BOOL
  4777. CTemplate::GetTag
  4778. (
  4779. CByteRange &brTags, // tags byte range
  4780. int nIndex
  4781. )
  4782. {
  4783. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4784. int nTIndex = 0;
  4785. while(TRUE)
  4786. {
  4787. // locate the start of a tag by skipping past the script tag "<%" and any leading white space
  4788. //
  4789. while(!brTemp.IsNull())
  4790. {
  4791. if( *brTemp.m_pb == '<' ||
  4792. *brTemp.m_pb == '%' ||
  4793. *brTemp.m_pb == '@' ||
  4794. FWhiteSpace(*brTemp.m_pb))
  4795. {
  4796. brTemp.Advance(1);
  4797. brTags.Advance(1);
  4798. }
  4799. else
  4800. break;
  4801. }
  4802. // search for potential matches on tag name string, case-insensitive
  4803. //
  4804. while(!brTemp.IsNull())
  4805. {
  4806. if( *brTemp.m_pb == '=' || FWhiteSpace(*brTemp.m_pb))
  4807. {
  4808. nTIndex++;
  4809. break;
  4810. }
  4811. brTemp.Advance(1);
  4812. }
  4813. // if we did not find tag name string at all, return 'not found'
  4814. if(brTemp.IsNull())
  4815. goto NotFound;
  4816. // if it is a valid HTML tag name, return it
  4817. if(FTagName(brTags.m_pb, DIFF(brTemp.m_pb - brTags.m_pb)))
  4818. if(nTIndex >= nIndex)
  4819. goto Exit;
  4820. // position past named pair data and reset start and if end of byte range then
  4821. // goto NotFound
  4822. //
  4823. while(!brTemp.IsNull() && !FWhiteSpace(*brTemp.m_pb))
  4824. brTemp.Advance(1);
  4825. if(brTemp.IsNull())
  4826. goto NotFound;
  4827. else
  4828. brTags.Advance(DIFF(brTemp.m_pb - brTags.m_pb));
  4829. }
  4830. Exit:
  4831. return TRUE;
  4832. NotFound:
  4833. return FALSE;
  4834. }
  4835. /* ============================================================================
  4836. CTemplate::FTagHasValue
  4837. Do tags include tknTag=tknValue?
  4838. Returns
  4839. TRUE if tags include value, else FALSE
  4840. Side effects
  4841. none
  4842. */
  4843. BOOLB
  4844. CTemplate::FTagHasValue
  4845. (
  4846. const CByteRange& brTags, // tags byte range to search
  4847. _TOKEN tknTag, // tag token
  4848. _TOKEN tknValue // value token
  4849. )
  4850. {
  4851. return (BrValueOfTag(brTags, tknTag) // byte range of value
  4852. .FMatchesSz(SZ_TOKEN(tknValue)));
  4853. }
  4854. /* =========================
  4855. CTemplate::CopySzAdv
  4856. Copies a string to a ptr and advances the ptr just beyond the copied string.
  4857. Returns
  4858. Nothing
  4859. Side effects
  4860. advances ptr beyond copied string
  4861. */
  4862. void
  4863. CTemplate::CopySzAdv
  4864. (
  4865. char* pchWrite, // write location ptr
  4866. LPSTR psz // string to copy
  4867. )
  4868. {
  4869. strcpy(pchWrite, psz);
  4870. pchWrite += strlen(psz);
  4871. }
  4872. /* ============================================================================
  4873. CTemplate::ValidateSourceFiles
  4874. Returns
  4875. Side effects
  4876. none
  4877. */
  4878. BOOL CTemplate::ValidateSourceFiles
  4879. (
  4880. CIsapiReqInfo* pIReq
  4881. )
  4882. {
  4883. FILETIME ftLastWriteTime;
  4884. HRESULT hr = S_OK;
  4885. BOOL fImpersonatedUser = FALSE;
  4886. HANDLE hVirtIncImpToken = NULL;
  4887. HANDLE hCurImpToken = NULL;
  4888. for (UINT i=0;i<m_cFilemaps;i++)
  4889. {
  4890. fImpersonatedUser = FALSE;
  4891. hVirtIncImpToken = NULL;
  4892. hCurImpToken = NULL;
  4893. //
  4894. // If it target file is a UNC , then we need to Impersonate LoggedOnUser.
  4895. //
  4896. if (m_rgpFilemaps[i]->FHasUNCPath())
  4897. {
  4898. if (SUCCEEDED(pIReq->GetVirtualPathToken(m_rgpFilemaps[i]->m_szPathInfo, &hVirtIncImpToken)))
  4899. {
  4900. AspDoRevertHack(&hCurImpToken);
  4901. fImpersonatedUser = ImpersonateLoggedOnUser(hVirtIncImpToken);
  4902. if (!fImpersonatedUser)
  4903. AspUndoRevertHack(&hCurImpToken);
  4904. }
  4905. }
  4906. hr = AspGetFileAttributes (m_rgpFilemaps[i]->m_szPathTranslated,
  4907. m_rgpFilemaps[i]->m_hFile,
  4908. &ftLastWriteTime);
  4909. //
  4910. // Undo impersonation if any.
  4911. //
  4912. if (fImpersonatedUser)
  4913. AspUndoRevertHack(&hCurImpToken);
  4914. if (hVirtIncImpToken)
  4915. CloseHandle(hVirtIncImpToken);
  4916. //
  4917. // Check if the Files timestamp changed
  4918. //
  4919. if (SUCCEEDED(hr))
  4920. {
  4921. if (CompareFileTime(&(m_rgpFilemaps[i]->m_ftLastWriteTime), &ftLastWriteTime) != 0)
  4922. {
  4923. DBGPRINTF ((DBG_CONTEXT,"VALIDATE SOUCE FILES : Template is Invalid\n"));
  4924. return FALSE;
  4925. }
  4926. }
  4927. else
  4928. return FALSE;
  4929. }
  4930. m_dwLastMonitored = GetTickCount();
  4931. m_dwCacheTag = g_TemplateCache.GetCacheTag();
  4932. m_fInCheck = FALSE;
  4933. return TRUE;
  4934. }
  4935. /* ============================================================================
  4936. CTemplate::FNeedsValidation
  4937. Returns
  4938. Side effects
  4939. none
  4940. */
  4941. BOOL CTemplate::FNeedsValidation ( )
  4942. {
  4943. if (m_fNeedsMonitoring)
  4944. {
  4945. if (m_fInCheck == FALSE)
  4946. {
  4947. if ( m_dwCacheTag != g_TemplateCache.GetCacheTag() && !g_fLazyContentPropDisabled)
  4948. {
  4949. m_fInCheck = TRUE;
  4950. return TRUE;
  4951. }
  4952. BOOL bRet = CheckTTLTimingWindow (m_dwLastMonitored, g_dwFileMonitoringTimeoutSecs);
  4953. if (bRet)
  4954. m_fInCheck=TRUE;
  4955. return bRet;
  4956. }
  4957. }
  4958. return FALSE;
  4959. }
  4960. /* ============================================================================
  4961. CTemplate::CheckTTLTimingWindow
  4962. Checks if the current time is withing Timeout seconds from the dwLastMonitored timestamp.
  4963. If it is within the window it returns false. And if its outside the timeing window then it will return TRUE
  4964. Returns
  4965. TRUE = Current Time is beyond TTL (timeoutsecs) from lastmod time.
  4966. FALSE = Current Time is within TTL (timeoutsecs) of lastmod time.
  4967. Side effects
  4968. none
  4969. */
  4970. BOOL CTemplate::CheckTTLTimingWindow(DWORD dwLastMonitored, DWORD timeoutSecs)
  4971. {
  4972. BOOL bRet = FALSE;
  4973. DWORD currentTime= GetTickCount();
  4974. if ( currentTime > (dwLastMonitored + (timeoutSecs *1000)) || currentTime < dwLastMonitored)
  4975. {
  4976. // This could also be a false Positive that will last just for a couple of minutes
  4977. // around the 47 day period when getTickCount wraps around.
  4978. if (((dwLastMonitored +(timeoutSecs *1000)) < dwLastMonitored) &&
  4979. (currentTime > dwLastMonitored))
  4980. {
  4981. // Found that tiny-tiny case where we would get a false positive.
  4982. bRet = FALSE;
  4983. }
  4984. // This file could not have been checked in the last dwTimeoutSecs time period.
  4985. bRet = TRUE;
  4986. }
  4987. return bRet;
  4988. }
  4989. /* ============================================================================
  4990. CTemplate::FVbsComment
  4991. Determines whether a script line is a VBS comment.
  4992. NOTE caller must ensure that brLine is non-blank and has no leading white space
  4993. Returns
  4994. TRUE if the line is a VBS comment, else FALSE
  4995. Side effects
  4996. none
  4997. */
  4998. BOOLB
  4999. CTemplate::FVbsComment(CByteRange& brLine)
  5000. {
  5001. // CONSIDER: SCRIPTLANG generic comment token
  5002. if(!_strnicmp((LPCSTR)brLine.m_pb, SZ_TOKEN(CTokenList::tknVBSCommentSQuote), CCH_TOKEN(CTokenList::tknVBSCommentSQuote)))
  5003. return TRUE;
  5004. if(!_strnicmp((LPCSTR)brLine.m_pb, SZ_TOKEN(CTokenList::tknVBSCommentRem), CCH_TOKEN(CTokenList::tknVBSCommentRem)))
  5005. return TRUE;
  5006. return FALSE;
  5007. }
  5008. /* ============================================================================
  5009. CTemplate::FExpression
  5010. Determines whether a script line is an expression, and if so returns
  5011. just the expression in brLine.
  5012. NOTE caller must ensure that brLine has no leading white space
  5013. Returns
  5014. TRUE if the line is an expression, else FALSE
  5015. Side effects
  5016. none
  5017. */
  5018. BOOLB
  5019. CTemplate::FExpression(CByteRange& brLine)
  5020. {
  5021. // may be whitespace (other languages besides VB & JScript will have whitespace)
  5022. char *pchLine = reinterpret_cast<char *>(brLine.m_pb);
  5023. int cchLine = brLine.m_cb;
  5024. while (cchLine > 0 && FWhiteSpace(*pchLine))
  5025. {
  5026. --cchLine;
  5027. ++pchLine;
  5028. }
  5029. // if line starts with =, it is an expression: bypass =, left-trim whitespace and return true
  5030. if(cchLine > 0 && *pchLine == '=')
  5031. {
  5032. brLine.Advance(1 + DIFF(reinterpret_cast<BYTE *>(pchLine) - brLine.m_pb)); // OK to advance past whitespace now.
  5033. LTrimWhiteSpace(brLine);
  5034. return TRUE;
  5035. }
  5036. // else return false
  5037. return FALSE;
  5038. }
  5039. /**
  5040. ** In the following function names:
  5041. ** 'Adv' == 'advance offset after writing'
  5042. **/
  5043. /* ============================================================================
  5044. CTemplate::WriteOffsetToOffset
  5045. Writes a offset-to-offset offset (0 if no blocks) into header,
  5046. and advances header offset and offset-to-offset.
  5047. Returns:
  5048. Nothing.
  5049. Side effects:
  5050. Advances offsets.
  5051. */
  5052. void
  5053. CTemplate::WriteOffsetToOffset
  5054. (
  5055. USHORT cBlocks, // count of blocks
  5056. UINT* pcbHeaderOffset, // ptr to header offset value
  5057. UINT* pcbOffsetToOffset // ptr to offset-to-offset value
  5058. )
  5059. {
  5060. // if blocks of this type, write offset to first of them into header;
  5061. // if no blocks of this type, write 0 into header
  5062. WriteLongAdv((cBlocks > 0) ? *pcbOffsetToOffset : 0, pcbHeaderOffset);
  5063. // advance offset-to-offset offset
  5064. *pcbOffsetToOffset += cBlocks * sizeof(ULONG);
  5065. }
  5066. /* ============================================================================
  5067. CTemplate::WriteSzAsBytesAdv
  5068. Writes a null-terminated string as bytes, i.e. without its null terminator
  5069. and advances offset
  5070. Returns:
  5071. Nothing.
  5072. Side effects:
  5073. Advances offset.
  5074. */
  5075. void
  5076. CTemplate::WriteSzAsBytesAdv
  5077. (
  5078. LPCSTR szSource, // source string
  5079. UINT* pcbDataOffset // ptr to offset value
  5080. )
  5081. {
  5082. if((szSource == NULL) || (*szSource == '\0'))
  5083. return;
  5084. MemCpyAdv(pcbDataOffset, (void*) szSource, strlen(szSource));
  5085. }
  5086. /* ============================================================================
  5087. CTemplate::WriteByteRangeAdv
  5088. Writes a byte range to memory at template offset location *pcbDataOffset and, optionally,
  5089. writes a ptr to the written data at template offset location *pcbPtrOffset
  5090. (pass *pcbPtrOffset == 0 to avoid this)
  5091. fWriteAsBsz == FALSE --> write only byte range's data
  5092. fWriteAsBsz == TRUE --> write length, followed by data, followed by NULL
  5093. NOTE bsz == length-prefixed, null-terminated string
  5094. Returns:
  5095. Nothing.
  5096. Side effects:
  5097. Advances offset(s).
  5098. */
  5099. void
  5100. CTemplate::WriteByteRangeAdv
  5101. (
  5102. CByteRange& brSource, // source data
  5103. BOOLB fWriteAsBsz, // write as bsz?
  5104. UINT* pcbDataOffset, // offset where data will be written
  5105. UINT* pcbPtrOffset // offset where ptr will be written
  5106. )
  5107. {
  5108. // bail if source is empty
  5109. if(brSource.IsNull())
  5110. return;
  5111. // If writing as a bsz, write length prefix
  5112. if(fWriteAsBsz)
  5113. WriteLongAdv(brSource.m_cb, pcbDataOffset);
  5114. // Write data
  5115. MemCpyAdv(pcbDataOffset, brSource.m_pb, brSource.m_cb);
  5116. // If writing as a bsz, write null terminator and advance target ptr
  5117. if(fWriteAsBsz)
  5118. MemCpyAdv(pcbDataOffset, SZ_NULL, 1);
  5119. // If caller passed a non-zero ptr offset, write offset to data there
  5120. if(*pcbPtrOffset > 0)
  5121. {
  5122. if(fWriteAsBsz)
  5123. /* if writing as a bsz ...
  5124. offset to start of data == current data offset
  5125. - null terminator
  5126. - data length
  5127. - sizeof length prefix
  5128. */
  5129. WriteLongAdv(*pcbDataOffset - 1 - brSource.m_cb - sizeof(brSource.m_cb), pcbPtrOffset);
  5130. else
  5131. // else, offset to start of data == current data offset - data length
  5132. WriteLongAdv(*pcbDataOffset - brSource.m_cb, pcbPtrOffset);
  5133. }
  5134. }
  5135. /*===================================================================
  5136. CTemplate::MemCpyAdv
  5137. Copies from a memory location to a template offset location,
  5138. and advances offset.
  5139. Returns:
  5140. Nothing.
  5141. Side effects:
  5142. Advances offset.
  5143. Re-allocates memory if required.
  5144. */
  5145. void
  5146. CTemplate::MemCpyAdv
  5147. (
  5148. UINT* pcbOffset, // ptr to offset value
  5149. void* pbSource, // ptr to source
  5150. ULONG cbSource, // length of source
  5151. UINT cbByteAlign // align bytes on short/long/dword boundary?
  5152. )
  5153. {
  5154. // byte-align offset location before write, if specified by caller
  5155. if(cbByteAlign > 0)
  5156. ByteAlignOffset(pcbOffset, cbByteAlign);
  5157. // calc number of bytes by which to grow allocated template memory:
  5158. // if projected reach exceeds current reach, we need to grow by the difference;
  5159. // else, no need to grow
  5160. if((*pcbOffset + cbSource) > m_cbTemplate)
  5161. {
  5162. // Reallocate space for storing local data - we grab twice what we had before
  5163. // or twice current growth requirement, whichever is more
  5164. m_cbTemplate = 2 * max(m_cbTemplate, (*pcbOffset + cbSource) - m_cbTemplate);
  5165. if(NULL == (m_pbStart = (BYTE*) CTemplate::LargeReAlloc(m_pbStart, m_cbTemplate)))
  5166. THROW(E_OUTOFMEMORY);
  5167. }
  5168. // copy source to template offset location
  5169. memcpy(m_pbStart + *pcbOffset, pbSource, cbSource);
  5170. // advance offset location
  5171. *pcbOffset += cbSource;
  5172. }
  5173. /* ============================================================================
  5174. CTemplate::GetAddress
  5175. Returns a ptr to the i-th object of type tcomp
  5176. */
  5177. BYTE*
  5178. CTemplate::GetAddress
  5179. (
  5180. TEMPLATE_COMPONENT tcomp,
  5181. USHORT i
  5182. )
  5183. {
  5184. DWORD* pdwBase;
  5185. Assert(NULL != m_pbStart);
  5186. // refer to CTemplate::WriteTemplate comments for the structure of what this is dealing with
  5187. pdwBase = (DWORD*)(m_pbStart + (C_COUNTS_IN_HEADER * sizeof(USHORT)));
  5188. // tcomp types are ptr-to-ptrs
  5189. DWORD* pdwTcompBase = (DWORD *) (m_pbStart + pdwBase[tcomp]);
  5190. return m_pbStart + pdwTcompBase[i];
  5191. }
  5192. /* ============================================================================
  5193. CTemplate::AppendSourceInfo
  5194. Appends a source line number for the current target line
  5195. NOTE if caller passes null source ptr, we append prev source line number + 1
  5196. Returns
  5197. Nothing
  5198. Side effects
  5199. allocates memory first time thru; may realloc
  5200. */
  5201. void
  5202. CTemplate::AppendSourceInfo
  5203. (
  5204. USHORT idEngine, // script engine id
  5205. CFileMap* pfilemap, // ptr to source file map
  5206. BYTE* pbSource, // ptr to current location in source file
  5207. ULONG cbSourceOffset, // byte offset of line in source file
  5208. ULONG cbScriptBlockOffset, // pointer to start of script text
  5209. ULONG cbTargetOffset, // character offset of line in target file
  5210. ULONG cchSourceText, // # of characters in source text
  5211. BOOL fIsHTML // TRUE if manufactured line
  5212. )
  5213. {
  5214. UINT i; // loop index
  5215. CSourceInfo si; // temporary CSourceInfo structure
  5216. vector<CSourceInfo> *prgSourceInfos; // pointer to line mapping table for the engine
  5217. ULONG cchSourceOffset = 0;// cch corresponding to cbSourceOffset
  5218. HRESULT hr = S_OK;
  5219. // if arrays are not yet allocated, allocate them
  5220. if (m_rgrgSourceInfos == NULL)
  5221. {
  5222. // transfer count of script engines from workstore to template
  5223. m_cScriptEngines = m_pWorkStore->CRequiredScriptEngines(m_fGlobalAsa);
  5224. // one source-info array per engine
  5225. if ((m_rgrgSourceInfos = new vector<CSourceInfo>[m_cScriptEngines]) == NULL)
  5226. THROW (E_OUTOFMEMORY);
  5227. }
  5228. // new script engine must be allocated in IdEngineFromBr (way upstream of this point),
  5229. // so we assert that current engine must already be covered
  5230. Assert(idEngine < m_pWorkStore->CRequiredScriptEngines(m_fGlobalAsa));
  5231. /* set current target line's source line number (SLN):
  5232. a) if caller passed a source ptr, calc SLN from the source ptr;
  5233. b) else if caller passed a filemap ptr, set SLN to prev target line's SLN plus one;
  5234. c) else set SLN to 0
  5235. semantics:
  5236. a) we have a source file location, but must calc a line # for that location
  5237. b) caller tells us (by passing NULL source file location) that this target line
  5238. immediately follows prev target line. This is an optimization because
  5239. SourceLineNumberFromPb is very slow.
  5240. change:
  5241. caller used to pass NULL filemap ptr that target line is 'manufactured'
  5242. i.e. has no corresponding authored line in source file
  5243. HOWEVER - now filemap ptr must NOT be NULL because 'manufactured' lines
  5244. are also stored in the file map array
  5245. */
  5246. Assert (pfilemap != NULL);
  5247. prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  5248. if (pbSource == NULL)
  5249. {
  5250. if (prgSourceInfos->length() == 0)
  5251. si.m_idLine = 1;
  5252. else
  5253. si.m_idLine = (*prgSourceInfos)[prgSourceInfos->length() - 1].m_idLine + 1;
  5254. }
  5255. else
  5256. si.m_idLine = SourceLineNumberFromPb(pfilemap, pbSource);
  5257. // The EOF line does not have a source offset (caller passes -1 (UINT_MAX)). For this case, no
  5258. // DBCS calculations etc. should be done. (set cchSourceOffset to UINT_MAX).
  5259. if (cbSourceOffset == UINT_MAX)
  5260. cchSourceOffset = UINT_MAX;
  5261. else
  5262. {
  5263. // BUG 80901: Source offset needs to point to the beginning of leading white space on the line
  5264. // Adjust source length by one as we decrement source offset
  5265. // Note: whitepsace is never trailing byte, so loop will work with DBCS encoded character sets
  5266. while (cbSourceOffset > 0 && strchr(" \t\v\a\f", pfilemap->m_pbStartOfFile[cbSourceOffset - 1]))
  5267. {
  5268. --cbSourceOffset;
  5269. ++cchSourceText;
  5270. }
  5271. // BUG 95859
  5272. // If the cursor is on the opening token of a script block (the "<%" part of a line), the
  5273. // BP is set in the previous HTML, not in the script block, as is desired.
  5274. //
  5275. // To correct this, if we are in a script block, scan back two characters, see if it is the open
  5276. // token. If it is, set the offset back two, and add two to the length.
  5277. //
  5278. if (!fIsHTML)
  5279. {
  5280. // Skip whitespace (including newlines -- the previous step did not skip newlines)
  5281. //
  5282. ULONG cbOpen = cbSourceOffset;
  5283. while (cbOpen > 0 && strchr(" \t\v\a\f\r\n", pfilemap->m_pbStartOfFile[cbOpen - 1]))
  5284. --cbOpen;
  5285. if (cbOpen >= 2 && strncmp(reinterpret_cast<char *>(&pfilemap->m_pbStartOfFile[cbOpen - 2]), "<%", 2) == 0)
  5286. {
  5287. cbOpen -= 2;
  5288. cchSourceText += cbSourceOffset - cbOpen;
  5289. cbSourceOffset = cbOpen;
  5290. }
  5291. // Look for trailing "%>" in this snippet, and if it exists then include the end delimiter in
  5292. // the length. NOTE: No DBCS awareness needed here - if we find a lead byte we just get out
  5293. // of the loop. We are looking for <whitespace>*"%>" which is totally SBCS chars.
  5294. //
  5295. ULONG cbClose = cbSourceOffset + cchSourceText;
  5296. ULONG cbFile = pfilemap->GetSize();
  5297. while (cbClose < cbFile && strchr(" \t\v\a\f\r\n", pfilemap->m_pbStartOfFile[cbClose]))
  5298. ++cbClose;
  5299. if (cbClose < cbFile && strncmp(reinterpret_cast<char *>(&pfilemap->m_pbStartOfFile[cbClose]), "%>", 2) == 0)
  5300. cchSourceText += cbClose - (cbSourceOffset + cchSourceText) + 2;
  5301. }
  5302. // BUG 82222, 85584
  5303. // Compiler marks HTML segments starting with the newline on the previous line
  5304. // if the line ends with %>.
  5305. //
  5306. // This screws up the debugger, becasue when you press <F9>, the pointer is placed
  5307. // on the line above when it should point to the start of the whitespace on the next line.
  5308. if (fIsHTML)
  5309. {
  5310. UINT cbEOF = pfilemap->GetSize(), cbRover = cbSourceOffset;
  5311. // Skip initial whitespace
  5312. while (cbRover < cbEOF && strchr(" \t\a\f", pfilemap->m_pbStartOfFile[cbRover]))
  5313. ++cbRover;
  5314. // If what's left is a CR/LF pair, then advance cbSourceOffset to next line
  5315. BOOL fCR = FALSE, fLF = FALSE;
  5316. if (cbRover < cbEOF && strchr("\r\n", pfilemap->m_pbStartOfFile[cbRover]))
  5317. {
  5318. fCR = pfilemap->m_pbStartOfFile[cbRover] == '\r';
  5319. fLF = pfilemap->m_pbStartOfFile[cbRover] == '\n';
  5320. ++cbRover;
  5321. Assert (fCR || fLF);
  5322. }
  5323. // we allow either <CR>, <LF>, <CR><LF>, or <LF><CR> to terminate a line,
  5324. // so look for its opposite terminator if one is found (but don't require it)
  5325. if (fCR && cbRover < cbEOF && pfilemap->m_pbStartOfFile[cbRover] == '\n')
  5326. ++cbRover;
  5327. if (fLF && cbRover < cbEOF && pfilemap->m_pbStartOfFile[cbRover] == '\r')
  5328. ++cbRover;
  5329. // OK, adjust cbSourceOffset now
  5330. if ((fCR || fLF) && cbRover < cbEOF)
  5331. {
  5332. cchSourceText -= cbRover - cbSourceOffset; // adjust # of chars to select
  5333. cbSourceOffset = cbRover;
  5334. }
  5335. }
  5336. // Now that we have the source offset, calculate its CCH by finding
  5337. // the last time we sampled the value, then add that to the number
  5338. // of DBCS characters from that point to the current offset.
  5339. //
  5340. // For the case of includes, it's possible offset already exists
  5341. // (if the entry was previously generated by another instance of
  5342. // #include - therefore we have to search)
  5343. COffsetInfo *pOffsetInfoLE, *pOffsetInfoGE;
  5344. GetBracketingPair(
  5345. cbSourceOffset, // value to find
  5346. pfilemap->m_rgByte2DBCS.begin(), // beginning of array
  5347. pfilemap->m_rgByte2DBCS.end(), // end of array
  5348. CByteOffsetOrder(), // search for byte offset
  5349. &pOffsetInfoLE, &pOffsetInfoGE // return values
  5350. );
  5351. // If we find an equal match, don't insert any duplicates
  5352. if (pOffsetInfoLE == NULL || pOffsetInfoLE->m_cbOffset < cbSourceOffset)
  5353. {
  5354. // if pOffsetInfoLE is NULL, it means that the array is empty -
  5355. // create the mapping of offset 0 to offset 0.
  5356. //
  5357. // In the case of the first line of a file being an include directive,
  5358. // the first executable line from the file may not start at offset zero,
  5359. // so in this case we need to create this entry AND execute the next "if"
  5360. // block.
  5361. //
  5362. if (pOffsetInfoLE == NULL)
  5363. {
  5364. COffsetInfo oiZero; // ctor will init
  5365. if (FAILED(hr = pfilemap->m_rgByte2DBCS.append(oiZero)))
  5366. THROW(hr);
  5367. pOffsetInfoLE = pfilemap->m_rgByte2DBCS.begin();
  5368. Assert (pOffsetInfoLE != NULL);
  5369. }
  5370. // If cbSourceOffset is zero, we handled it above
  5371. if (cbSourceOffset != 0)
  5372. {
  5373. cchSourceOffset = pOffsetInfoLE->m_cchOffset +
  5374. CharAdvDBCS
  5375. (
  5376. (WORD)m_wCodePage,
  5377. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile + pOffsetInfoLE->m_cbOffset),
  5378. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile + cbSourceOffset),
  5379. INFINITE,
  5380. NULL
  5381. );
  5382. // Now add the value to the table
  5383. COffsetInfo oi;
  5384. oi.m_cchOffset = cchSourceOffset;
  5385. oi.m_cbOffset = cbSourceOffset;
  5386. if (pOffsetInfoGE == NULL) // No offset greater
  5387. hr = pfilemap->m_rgByte2DBCS.append(oi);
  5388. else
  5389. hr = pfilemap->m_rgByte2DBCS.insertAt(DIFF(pOffsetInfoGE - pfilemap->m_rgByte2DBCS.begin()), oi);
  5390. if (FAILED(hr))
  5391. THROW(hr);
  5392. }
  5393. }
  5394. else
  5395. {
  5396. // If we're not adding anything for the table, Assert it's because there's
  5397. // a duplicate item
  5398. Assert (cbSourceOffset == pOffsetInfoLE->m_cbOffset);
  5399. cchSourceOffset = pOffsetInfoLE->m_cchOffset;
  5400. }
  5401. }
  5402. UINT cchTargetOffset = UINT_MAX;
  5403. if (cbTargetOffset != UINT_MAX)
  5404. {
  5405. // ptr to start of script is:
  5406. // ptr start of template + offset to script + size of script length
  5407. LPSTR szScript = (LPSTR) m_pbStart + cbScriptBlockOffset;
  5408. // Calculate cchTargetOffset (have the cb). The cch is the number of characters since the
  5409. // last cch calculated in the end of the array.
  5410. //
  5411. if (prgSourceInfos->length() > 0)
  5412. cchTargetOffset = (*prgSourceInfos)[prgSourceInfos->length() - 1].m_cchTargetOffset;
  5413. else
  5414. cchTargetOffset = 0;
  5415. cchTargetOffset += CharAdvDBCS
  5416. (
  5417. (WORD) m_wCodePage,
  5418. &szScript[m_cbTargetOffsetPrevT],
  5419. &szScript[cbTargetOffset],
  5420. INFINITE,
  5421. NULL
  5422. );
  5423. // Keeps track of offsets during compilation
  5424. //
  5425. m_cbTargetOffsetPrevT = cbTargetOffset;
  5426. }
  5427. // Store this record and move on.
  5428. //
  5429. si.m_pfilemap = pfilemap;
  5430. si.m_fIsHTML = fIsHTML;
  5431. si.m_cchSourceOffset = cchSourceOffset;
  5432. si.m_cchTargetOffset = cchTargetOffset;
  5433. si.m_cchSourceText = cchSourceText;
  5434. if (FAILED(prgSourceInfos->append(si)))
  5435. THROW(hr);
  5436. }
  5437. /* ============================================================================
  5438. CTemplate::SourceLineNumberFromPb
  5439. Returns the starting source line number for the given source file location
  5440. */
  5441. UINT
  5442. CTemplate::SourceLineNumberFromPb
  5443. (
  5444. CFileMap* pfilemap, // ptr to source file map
  5445. BYTE* pbSource // ptr to current location in source file
  5446. )
  5447. {
  5448. UINT cSourceLines = 1; // count of lines into source file
  5449. CByteRange brScan; // byte range to scan for newlines
  5450. CByteRange brSOL; // start-of-line ptr
  5451. if(pbSource == NULL || pfilemap == NULL)
  5452. return 0;
  5453. // Determine if there was a state stored on this ASP script earlier. If yes, then restore state.
  5454. // If the line being requested is prior to the saved state...Bail out and restart from the begining.
  5455. if(m_pWorkStore->m_cPrevSourceLines &&
  5456. (m_pWorkStore->m_pbPrevSource && (m_pWorkStore->m_pbPrevSource < pbSource)) &&
  5457. (m_pWorkStore->m_hPrevFile && (pfilemap->m_hFile==m_pWorkStore->m_hPrevFile)))
  5458. {
  5459. // The file handles match:with means that we are evaluating the current file. Restore state.
  5460. brScan.m_pb = m_pWorkStore->m_pbPrevSource;
  5461. brScan.m_cb = max(DIFF(pbSource - brScan.m_pb), 0);
  5462. cSourceLines = m_pWorkStore->m_cPrevSourceLines;
  5463. }
  5464. else
  5465. {
  5466. // set scan range to run from start-of-template to caller's ptr
  5467. brScan.m_pb = pfilemap->m_pbStartOfFile;
  5468. brScan.m_cb = max(DIFF(pbSource - brScan.m_pb), 0);
  5469. }
  5470. // get newlines in scan range
  5471. brSOL = BrNewLine(brScan);
  5472. while(!brSOL.IsNull())
  5473. {
  5474. // advance start-of-line ptr and scan byte range
  5475. brScan.Advance(DIFF((brSOL.m_pb + brSOL.m_cb) - brScan.m_pb));
  5476. // increment source line counter
  5477. cSourceLines++;
  5478. // find next newline
  5479. brSOL = BrNewLine(brScan);
  5480. }
  5481. // Store the state for the next call.
  5482. m_pWorkStore->m_pbPrevSource = pbSource;
  5483. m_pWorkStore->m_cPrevSourceLines = cSourceLines;
  5484. m_pWorkStore->m_hPrevFile = pfilemap->m_hFile;
  5485. return cSourceLines;
  5486. }
  5487. /* ============================================================================
  5488. CTemplate::RemoveFromIncFiles
  5489. Removes this template from inc-files on which it depends
  5490. Returns:
  5491. Nothing
  5492. Side effects:
  5493. None
  5494. */
  5495. void
  5496. CTemplate::RemoveFromIncFiles
  5497. (
  5498. )
  5499. {
  5500. // NOTE we loop from 1 to count, since 0-th filemap is for main file
  5501. for(UINT i = 1; i < m_cFilemaps; i++)
  5502. {
  5503. if(NULL != m_rgpFilemaps[i]->m_pIncFile)
  5504. m_rgpFilemaps[i]->m_pIncFile->RemoveTemplate(this);
  5505. }
  5506. }
  5507. /* ****************************************************************************
  5508. IDebugDocumentProvider implementation
  5509. */
  5510. /* ============================================================================
  5511. CTemplate::GetDocument
  5512. Return a pointer to the IDebugDocument implementation. (same object in this case)
  5513. Returns:
  5514. *ppDebugDoc is set to "this".
  5515. Notes:
  5516. always succeeds
  5517. */
  5518. HRESULT CTemplate::GetDocument
  5519. (
  5520. IDebugDocument **ppDebugDoc
  5521. )
  5522. {
  5523. return QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDoc));
  5524. }
  5525. /* ============================================================================
  5526. CTemplate::GetName
  5527. Return the various names of a document.
  5528. */
  5529. HRESULT CTemplate::GetName
  5530. (
  5531. /* [in] */ DOCUMENTNAMETYPE doctype,
  5532. /* [out] */ BSTR *pbstrName
  5533. )
  5534. {
  5535. TCHAR *szPathInfo = m_rgpFilemaps[0]->m_szPathInfo;
  5536. switch (doctype) {
  5537. case DOCUMENTNAMETYPE_APPNODE:
  5538. case DOCUMENTNAMETYPE_FILE_TAIL:
  5539. case DOCUMENTNAMETYPE_TITLE:
  5540. // Skip application path portion of the filename
  5541. {
  5542. // Make sure the template remembers the virtual path
  5543. // from the same application (it could be different
  5544. // if template is shared between two applications)
  5545. //
  5546. int cch = _tcslen(m_szApplnVirtPath);
  5547. if (_tcsncicmp(szPathInfo, m_szApplnVirtPath, cch) == 0)
  5548. szPathInfo += cch;
  5549. // Strip leading '/'
  5550. if (*szPathInfo == _T('/'))
  5551. szPathInfo++;
  5552. #if UNICODE
  5553. *pbstrName = SysAllocString(szPathInfo);
  5554. if (*pbstrName == NULL)
  5555. return E_OUTOFMEMORY;
  5556. return S_OK;
  5557. #else
  5558. return SysAllocStringFromSz(szPathInfo, 0, pbstrName, m_wCodePage);
  5559. #endif
  5560. }
  5561. case DOCUMENTNAMETYPE_URL:
  5562. // prefix with the URL, use szPathInfo for the rest of the path
  5563. {
  5564. STACK_BUFFER( tempName, MAX_PATH );
  5565. int cbURLPrefix = DIFF(m_szApplnVirtPath - m_szApplnURL) * sizeof (TCHAR);
  5566. if (!tempName.Resize(cbURLPrefix + (_tcslen(szPathInfo)*sizeof(TCHAR)) + sizeof(TCHAR))) {
  5567. return E_OUTOFMEMORY;
  5568. }
  5569. TCHAR *szURL = (TCHAR *)tempName.QueryPtr();
  5570. memcpy(szURL, m_szApplnURL, cbURLPrefix);
  5571. _tcscpy(&szURL[cbURLPrefix/sizeof(TCHAR)], szPathInfo);
  5572. #if UNICODE
  5573. *pbstrName = SysAllocString(szURL);
  5574. if (*pbstrName == NULL)
  5575. return E_OUTOFMEMORY;
  5576. return S_OK;
  5577. #else
  5578. return SysAllocStringFromSz(szURL, 0, pbstrName, m_wCodePage);
  5579. #endif
  5580. }
  5581. default:
  5582. return E_FAIL;
  5583. }
  5584. }
  5585. /* ****************************************************************************
  5586. IDebugDocumentText implementation
  5587. */
  5588. /* ============================================================================
  5589. CTemplate::GetSize
  5590. Return the number of lines & characters in the document
  5591. */
  5592. HRESULT CTemplate::GetSize
  5593. (
  5594. /* [out] */ ULONG *pcLines,
  5595. /* [out] */ ULONG *pcChars
  5596. )
  5597. {
  5598. /*
  5599. * NOTE: compilation is done in two phases.
  5600. * Errors are detected and reported in phase 1.
  5601. * The DBCS mapping is created in phase 2.
  5602. *
  5603. * If an error occurred during compilation, m_cChars will be equal to zero
  5604. * (Since zero length files are not compiled, m_cChars == 0 means "size
  5605. * is unknown", not "size is zero").
  5606. */
  5607. if (m_rgpFilemaps[0]->m_cChars == 0)
  5608. {
  5609. // Likely need to remap the file, then count
  5610. BOOL fRemapTemplate = !m_rgpFilemaps[0]->FIsMapped();
  5611. if (fRemapTemplate)
  5612. TRY
  5613. m_rgpFilemaps[0]->RemapFile();
  5614. CATCH (dwException)
  5615. return E_FAIL;
  5616. END_TRY
  5617. m_rgpFilemaps[0]->CountChars((WORD)m_wCodePage);
  5618. if (fRemapTemplate)
  5619. TRY
  5620. m_rgpFilemaps[0]->UnmapFile();
  5621. CATCH (dwException)
  5622. return E_FAIL;
  5623. END_TRY
  5624. // let's hope client is not relying on # of lines - expensive to compute
  5625. *pcChars = m_rgpFilemaps[0]->m_cChars;
  5626. *pcLines = ULONG_MAX;
  5627. }
  5628. else
  5629. {
  5630. /* The last line in the line mapping array of each engine is the <<EOF>> line
  5631. * for that engine. Therefore, the # of lines is the largest <<EOF>> line
  5632. * number - 1. The EOF line always points into the main file, so there are no
  5633. * include file glitches here.
  5634. */
  5635. ULONG cLinesMax = 0;
  5636. for (UINT i = 0; i < m_cScriptEngines; ++i)
  5637. {
  5638. ULONG cLinesCurrentEngine = m_rgrgSourceInfos[0][m_rgrgSourceInfos[0].length() - 1].m_idLine - 1;
  5639. if (cLinesCurrentEngine > cLinesMax)
  5640. cLinesMax = cLinesCurrentEngine;
  5641. }
  5642. *pcLines = cLinesMax;
  5643. *pcChars = m_rgpFilemaps[0]->m_cChars;
  5644. }
  5645. IF_DEBUG(SCRIPT_DEBUGGER) {
  5646. #if UNICODE
  5647. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%S\") returns %lu characters (%lu lines)\n", m_rgpFilemaps[0]->m_szPathTranslated, *pcChars, *pcLines));
  5648. #else
  5649. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%s\") returns %lu characters (%lu lines)\n", m_rgpFilemaps[0]->m_szPathTranslated, *pcChars, *pcLines));
  5650. #endif
  5651. }
  5652. return S_OK;
  5653. }
  5654. /* ============================================================================
  5655. CTemplate::GetDocumentAttributes
  5656. Return doc attributes
  5657. */
  5658. HRESULT CTemplate::GetDocumentAttributes
  5659. (
  5660. /* [out] */ TEXT_DOC_ATTR *ptextdocattr
  5661. )
  5662. {
  5663. // Easy way to tell debugger that we don't support editing.
  5664. *ptextdocattr = TEXT_DOC_ATTR_READONLY;
  5665. return S_OK;
  5666. }
  5667. /* ============================================================================
  5668. CTemplate::GetPositionOfLine
  5669. From a line number, return the character offset of the beginning
  5670. */
  5671. HRESULT CTemplate::GetPositionOfLine
  5672. (
  5673. /* [in] */ ULONG cLineNumber,
  5674. /* [out] */ ULONG *pcCharacterPosition
  5675. )
  5676. {
  5677. return GetPositionOfLine(m_rgpFilemaps[0], cLineNumber, pcCharacterPosition);
  5678. }
  5679. /* ============================================================================
  5680. CTemplate::GetLineOfPosition
  5681. From a character offset, return the line number and offset within the line
  5682. */
  5683. HRESULT CTemplate::GetLineOfPosition
  5684. (
  5685. /* [in] */ ULONG cCharacterPosition,
  5686. /* [out] */ ULONG *pcLineNumber,
  5687. /* [out] */ ULONG *pcCharacterOffsetInLine
  5688. )
  5689. {
  5690. return GetLineOfPosition(m_rgpFilemaps[0], cCharacterPosition, pcLineNumber, pcCharacterOffsetInLine);
  5691. }
  5692. /* ============================================================================
  5693. CTemplate::GetText
  5694. From a character offset and length, return the document text
  5695. */
  5696. HRESULT CTemplate::GetText
  5697. (
  5698. ULONG cchSourceOffset,
  5699. WCHAR *pwchText,
  5700. SOURCE_TEXT_ATTR *pTextAttr,
  5701. ULONG *pcChars,
  5702. ULONG cMaxChars
  5703. )
  5704. {
  5705. return m_rgpFilemaps[0]->GetText((WORD)m_wCodePage, cchSourceOffset, pwchText, pTextAttr, pcChars, cMaxChars);
  5706. }
  5707. /* ============================================================================
  5708. CTemplate::GetPositionOfContext
  5709. Decompose a document context into the document offset & length
  5710. */
  5711. HRESULT CTemplate::GetPositionOfContext
  5712. (
  5713. /* [in] */ IDebugDocumentContext *pUnknownDocumentContext,
  5714. /* [out] */ ULONG *pcchSourceOffset,
  5715. /* [out] */ ULONG *pcchText
  5716. )
  5717. {
  5718. // Make sure that the context is one of ours
  5719. CTemplateDocumentContext *pDocumentContext;
  5720. if (FAILED(pUnknownDocumentContext->QueryInterface(IID_IDenaliTemplateDocumentContext, reinterpret_cast<void **>(&pDocumentContext))))
  5721. return E_FAIL;
  5722. if (pcchSourceOffset)
  5723. *pcchSourceOffset = pDocumentContext->m_cchSourceOffset;
  5724. if (pcchText)
  5725. *pcchText = pDocumentContext->m_cchText;
  5726. pDocumentContext->Release();
  5727. return S_OK;
  5728. }
  5729. /* ============================================================================
  5730. CTemplate::GetContextOfPosition
  5731. Given the character position & number of characters in the document,
  5732. encapsulate this into a document context object.
  5733. */
  5734. HRESULT CTemplate::GetContextOfPosition
  5735. (
  5736. /* [in] */ ULONG cchSourceOffset,
  5737. /* [in] */ ULONG cchText,
  5738. /* [out] */ IDebugDocumentContext **ppDocumentContext
  5739. )
  5740. {
  5741. if (
  5742. (*ppDocumentContext = new CTemplateDocumentContext(this, cchSourceOffset, cchText))
  5743. == NULL
  5744. )
  5745. return E_OUTOFMEMORY;
  5746. return S_OK;
  5747. }
  5748. /* ****************************************************************************
  5749. IConnectionPointContainer implementation
  5750. */
  5751. /* ============================================================================
  5752. CTemplate::FindConnectionPoint
  5753. From a character offset and length, return the document text
  5754. */
  5755. HRESULT CTemplate::FindConnectionPoint
  5756. (
  5757. const GUID &uidConnection,
  5758. IConnectionPoint **ppCP
  5759. )
  5760. {
  5761. if (uidConnection == IID_IDebugDocumentTextEvents)
  5762. return m_CPTextEvents.QueryInterface(IID_IConnectionPoint, reinterpret_cast<void **>(ppCP));
  5763. else
  5764. {
  5765. *ppCP = NULL;
  5766. return E_NOINTERFACE;
  5767. }
  5768. }
  5769. /* ============================================================================
  5770. CTemplate::AttachTo
  5771. attach this to the debugger UI tree view.
  5772. */
  5773. HRESULT CTemplate::AttachTo
  5774. (
  5775. CAppln *pAppln
  5776. )
  5777. {
  5778. if (!m_fDontAttach && pAppln->FDebuggable())
  5779. {
  5780. // If we are already attached to this application, then ignore 2nd request
  5781. CDblLink *pNodeCurr = m_listDocNodes.PNext();
  5782. while (pNodeCurr != &m_listDocNodes)
  5783. {
  5784. if (pAppln == static_cast<CDocNodeElem *>(pNodeCurr)->m_pAppln)
  5785. return S_OK;
  5786. pNodeCurr = pNodeCurr->PNext();
  5787. }
  5788. // Create the node and store it in the linked list.
  5789. HRESULT hr;
  5790. IDebugApplicationNode *pDocRoot;
  5791. CDocNodeElem *pDocNodeElem;
  5792. // Create a document tree, showing the include file hierarchy
  5793. if (FAILED(hr = CreateDocumentTree(m_rgpFilemaps[0], &pDocRoot)))
  5794. return hr;
  5795. if (FAILED(hr = pDocRoot->Attach(pAppln->PAppRoot())))
  5796. return hr;
  5797. if ((pDocNodeElem = new CDocNodeElem(pAppln, pDocRoot)) == NULL)
  5798. return E_OUTOFMEMORY;
  5799. pDocNodeElem->AppendTo(m_listDocNodes);
  5800. pDocRoot->Release();
  5801. m_fDebuggable = TRUE;
  5802. }
  5803. return S_OK;
  5804. }
  5805. /* ============================================================================
  5806. CTemplate::DetachFrom
  5807. detach this from the debugger UI tree view.
  5808. */
  5809. HRESULT CTemplate::DetachFrom
  5810. (
  5811. CAppln *pAppln
  5812. )
  5813. {
  5814. // Enter the CS to prevent Detach() from detaching while we are scanning
  5815. // the list (causes application ptr to be deleted twice if this occurs)
  5816. DBG_ASSERT(m_fDebuggerDetachCSInited);
  5817. EnterCriticalSection(&m_csDebuggerDetach);
  5818. // Look for the node that has this application
  5819. CDblLink *pNodeCurr = m_listDocNodes.PNext();
  5820. while (pNodeCurr != &m_listDocNodes)
  5821. {
  5822. if (pAppln == static_cast<CDocNodeElem *>(pNodeCurr)->m_pAppln)
  5823. break;
  5824. pNodeCurr = pNodeCurr->PNext();
  5825. }
  5826. // If not found (pNodeCurr points back to head), then fail
  5827. if (pNodeCurr == &m_listDocNodes)
  5828. {
  5829. LeaveCriticalSection(&m_csDebuggerDetach);
  5830. return E_FAIL;
  5831. }
  5832. // Detach the node by deleting the current element
  5833. delete pNodeCurr;
  5834. // Turn off "Debuggable" flag if last application is detached
  5835. m_fDebuggable = !m_listDocNodes.FIsEmpty();
  5836. // At this point CS not needed
  5837. LeaveCriticalSection(&m_csDebuggerDetach);
  5838. // If we have just removed ourselves from the last application,
  5839. // then we call Detach(), to remove all cached script engines now.
  5840. if (!m_fDebuggable)
  5841. Detach();
  5842. return S_OK;
  5843. }
  5844. /* ============================================================================
  5845. CTemplate::Detach
  5846. detach this from the debugger UI tree view.
  5847. */
  5848. HRESULT CTemplate::Detach
  5849. (
  5850. )
  5851. {
  5852. // Enter the CS to prevent DetachFrom() from detaching while we are clearing
  5853. // the list (causes application ptr to be deleted twice if this occurs)
  5854. if (m_fDebuggerDetachCSInited)
  5855. EnterCriticalSection(&m_csDebuggerDetach);
  5856. // Detach all nodes
  5857. while (! m_listDocNodes.FIsEmpty())
  5858. delete m_listDocNodes.PNext();
  5859. // Done with CS
  5860. if (m_fDebuggerDetachCSInited)
  5861. LeaveCriticalSection(&m_csDebuggerDetach);
  5862. // Since we are not debuggable now, remove any script engines we may
  5863. // be holding on to. If we are detaching from change notification
  5864. // thread, queue engines to be released from debugger thread.
  5865. //
  5866. if (m_rgpDebugScripts)
  5867. {
  5868. Assert (g_dwDebugThreadId != 0);
  5869. BOOL fCalledFromDebugActivity = GetCurrentThreadId() == g_dwDebugThreadId;
  5870. for (UINT i = 0; i < m_cScriptEngines; i++)
  5871. {
  5872. CActiveScriptEngine *pEngine = m_rgpDebugScripts[i];
  5873. if (pEngine)
  5874. {
  5875. if (fCalledFromDebugActivity)
  5876. {
  5877. pEngine->FinalRelease();
  5878. }
  5879. else
  5880. {
  5881. g_ApplnMgr.AddEngine(pEngine);
  5882. pEngine->Release();
  5883. }
  5884. }
  5885. }
  5886. delete[] m_rgpDebugScripts;
  5887. m_rgpDebugScripts = NULL;
  5888. }
  5889. m_fDebuggable = FALSE;
  5890. return S_OK;
  5891. }
  5892. /* ============================================================================
  5893. CTemplate::CreateDocumentTree
  5894. Traverse the tree that we have embedded in the filemap structures,
  5895. and use it to create the include file structure
  5896. */
  5897. HRESULT CTemplate::CreateDocumentTree
  5898. (
  5899. CFileMap *pfilemapRoot,
  5900. IDebugApplicationNode **ppDocRoot
  5901. )
  5902. {
  5903. IDebugApplicationNode *pDocNode;
  5904. HRESULT hr = S_OK;
  5905. if (pfilemapRoot == NULL || ppDocRoot == NULL)
  5906. return E_POINTER;
  5907. // Create the root node
  5908. if (FAILED(hr = g_pDebugApp->CreateApplicationNode(ppDocRoot)))
  5909. return hr;
  5910. // From the filemap information, match it up with the correct provider
  5911. // "This" is the provider for the root document, others come from Inc file cache
  5912. if (pfilemapRoot == m_rgpFilemaps[0])
  5913. {
  5914. if (FAILED(hr = (*ppDocRoot)->SetDocumentProvider(this)))
  5915. return hr;
  5916. }
  5917. else
  5918. {
  5919. CIncFile *pIncFile;
  5920. if (FAILED(hr = g_IncFileMap.GetIncFile(pfilemapRoot->m_szPathTranslated, &pIncFile)))
  5921. return hr;
  5922. if (FAILED(hr = (*ppDocRoot)->SetDocumentProvider(pIncFile)))
  5923. return hr;
  5924. // SetDocumentProvider AddRef'ed
  5925. pIncFile->Release();
  5926. }
  5927. // Create a node from all of the children and attach it to this node
  5928. CFileMap *pfilemapChild = pfilemapRoot->m_pfilemapChild;
  5929. while (pfilemapChild != NULL)
  5930. {
  5931. IDebugApplicationNode *pDocChild;
  5932. if (FAILED(hr = CreateDocumentTree(pfilemapChild, &pDocChild)))
  5933. return hr;
  5934. if (FAILED(hr = pDocChild->Attach(*ppDocRoot)))
  5935. return hr;
  5936. pfilemapChild = pfilemapChild->m_fHasSibling? pfilemapChild->m_pfilemapSibling : NULL;
  5937. }
  5938. return S_OK;
  5939. }
  5940. /* ============================================================================
  5941. CTemplate::End
  5942. Place template in non-usable state (after this is called, last ref. should
  5943. be the any currently executing scripts. The count will naturally vanish
  5944. as the scripts finish. The template should never be recycled in cache after
  5945. this call.)
  5946. REF COUNTING NOTE:
  5947. Since debugging client has a reference to the template, the template needs
  5948. to dis-associate with the debugger at a point in time before destruction.
  5949. Otherwise, the reference will never go to zero.
  5950. */
  5951. ULONG
  5952. CTemplate::End
  5953. (
  5954. )
  5955. {
  5956. // Flag template as non-usable (for debugging)
  5957. m_fIsValid = FALSE;
  5958. Detach();
  5959. if (!m_CPTextEvents.FIsEmpty() && g_pDebugApp != NULL)
  5960. {
  5961. IEnumConnections *pConnIterator;
  5962. if (SUCCEEDED(m_CPTextEvents.EnumConnections(&pConnIterator)))
  5963. {
  5964. CONNECTDATA ConnectData;
  5965. while (pConnIterator->Next(1, &ConnectData, NULL) == S_OK)
  5966. {
  5967. IDebugDocumentTextEvents *pTextEventSink;
  5968. if (SUCCEEDED(ConnectData.pUnk->QueryInterface(IID_IDebugDocumentTextEvents, reinterpret_cast<void **>(&pTextEventSink))))
  5969. {
  5970. InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_ON_DESTROY, pTextEventSink);
  5971. pTextEventSink->Release();
  5972. }
  5973. ConnectData.pUnk->Release();
  5974. }
  5975. pConnIterator->Release();
  5976. }
  5977. }
  5978. return Release();
  5979. }
  5980. /* ============================================================================
  5981. CTemplate::NotifyDebuggerOnPageEvent
  5982. Let debugger know about page start/end
  5983. */
  5984. HRESULT
  5985. CTemplate::NotifyDebuggerOnPageEvent
  5986. (
  5987. BOOL fStart // TRUE = StartPage, FALSE = EndPage
  5988. )
  5989. {
  5990. CTemplateDocumentContext *pDebugContext = new CTemplateDocumentContext(this, 0, 0);
  5991. if (pDebugContext == NULL)
  5992. return E_OUTOFMEMORY;
  5993. HRESULT hr = S_OK;
  5994. if (g_pDebugApp)
  5995. hr = InvokeDebuggerWithThreadSwitch
  5996. (
  5997. g_pDebugApp,
  5998. fStart ? DEBUGGER_EVENT_ON_PAGEBEGIN : DEBUGGER_EVENT_ON_PAGEEND,
  5999. static_cast<IUnknown *>(pDebugContext)
  6000. );
  6001. pDebugContext->Release();
  6002. return hr;
  6003. }
  6004. /* ============================================================================
  6005. CTemplate::ReleaseTypeLibs
  6006. Release all typelibs collected from metadata
  6007. */
  6008. void
  6009. CTemplate::ReleaseTypeLibs()
  6010. {
  6011. if (m_rgpTypeLibs.length() > 0)
  6012. {
  6013. for (UINT i = 0; i < m_rgpTypeLibs.length(); i++)
  6014. {
  6015. m_rgpTypeLibs[i]->Release();
  6016. }
  6017. m_rgpTypeLibs.reshape(0);
  6018. }
  6019. }
  6020. /* ============================================================================
  6021. CTemplate::WrapTypeLibs
  6022. Wrap all typelibs collected from metadata into single IDispatch *
  6023. */
  6024. void
  6025. CTemplate::WrapTypeLibs(CHitObj *pHitObj)
  6026. {
  6027. HRESULT hr = S_OK;
  6028. Assert(m_pdispTypeLibWrapper == NULL);
  6029. if (m_rgpTypeLibs.length() > 0)
  6030. {
  6031. hr = ::WrapTypeLibs
  6032. (
  6033. m_rgpTypeLibs.begin(),
  6034. m_rgpTypeLibs.length(),
  6035. &m_pdispTypeLibWrapper
  6036. );
  6037. ReleaseTypeLibs();
  6038. }
  6039. if (FAILED(hr))
  6040. {
  6041. m_pbErrorLocation = NULL;
  6042. m_idErrMsg = IDE_TEMPLATE_WRAP_TYPELIB_FAILED;
  6043. ProcessSpecificError(*(m_rgpFilemaps[0]), pHitObj);
  6044. THROW(E_TEMPLATE_COMPILE_FAILED_DONT_CACHE);
  6045. }
  6046. }
  6047. /* ============================================================================
  6048. CTemplate::Release449
  6049. Release all 449-echo-cookie objects collected from metadata
  6050. */
  6051. void
  6052. CTemplate::Release449()
  6053. {
  6054. if (m_rgp449.length() > 0)
  6055. {
  6056. for (UINT i = 0; i < m_rgp449.length(); i++)
  6057. {
  6058. m_rgp449[i]->Release();
  6059. }
  6060. m_rgp449.reshape(0);
  6061. }
  6062. }
  6063. /* ============================================================================
  6064. CTemplate::Do449Processing
  6065. Generate 449 response in cookie negotiations with IE when needed
  6066. */
  6067. HRESULT
  6068. CTemplate::Do449Processing
  6069. (
  6070. CHitObj *pHitObj
  6071. )
  6072. {
  6073. if (m_rgp449.length() == 0 || pHitObj->F449Done())
  6074. return S_OK;
  6075. HRESULT hr = ::Do449Processing
  6076. (
  6077. pHitObj,
  6078. m_rgp449.begin(),
  6079. m_rgp449.length()
  6080. );
  6081. pHitObj->Set449Done();
  6082. return hr;
  6083. }
  6084. #if 0
  6085. /* ============================================================================
  6086. CTemplate::OutputDebugTables
  6087. print the debugging data structures to the debug window
  6088. */
  6089. void
  6090. CTemplate::OutputDebugTables()
  6091. {
  6092. unsigned i, j;
  6093. wchar_t wszDebugLine[256];
  6094. CWCharToMBCS convTarget;
  6095. CWCharToMBCS convSource;
  6096. // print line mapping table
  6097. DBGPRINTF((DBG_CONTEXT, "\nEngine HTML? Line# SourceOffset Length TargetOffset TargetText__________ SourceText__________ File\n"));
  6098. for (i = 0; i < m_cScriptEngines; ++i)
  6099. for (j = 0; j < m_rgrgSourceInfos[i].length(); ++j)
  6100. {
  6101. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1];
  6102. CSourceInfo *pSourceInfo = &m_rgrgSourceInfos[i][j];
  6103. // DON'T display sample script text on last line of each engine
  6104. if (j == m_rgrgSourceInfos[i].length() - 1)
  6105. {
  6106. wszTargetText[0] = 0;
  6107. wszSourceText[0] = 0;
  6108. }
  6109. else
  6110. {
  6111. // Get source & target text sample
  6112. GetScriptSnippets(
  6113. pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap,
  6114. pSourceInfo->m_cchTargetOffset, i,
  6115. wszSourceText, wszTargetText
  6116. );
  6117. // Actually display each line
  6118. #if 0
  6119. #ifndef _NO_TRACING_
  6120. convTarget.Init(wszTargetText);
  6121. convSource.Init(wszSourceText);
  6122. DBGINFO((DBG_CONTEXT,
  6123. "%-6d %-5s %-5d %-12d %-6d %-12d %-20s %-20s %s\n",
  6124. i,
  6125. pSourceInfo->m_fIsHTML? "Yes" : "No",
  6126. pSourceInfo->m_idLine,
  6127. pSourceInfo->m_cchSourceOffset,
  6128. pSourceInfo->m_cchSourceText,
  6129. pSourceInfo->m_cchTargetOffset,
  6130. convTarget.GetString(),
  6131. convSource.GetString(),
  6132. pSourceInfo->m_pfilemap->m_szPathTranslated));
  6133. #else
  6134. CMBCSToWChar convPath;
  6135. convPath.Init(pSourceInfo->m_pfilemap->m_szPathTranslated);
  6136. wsprintfW(
  6137. wszDebugLine,
  6138. L"%-6d %-5s %-5d %-12d %-6d %-12d %-20s %-20s %s\n",
  6139. i,
  6140. pSourceInfo->m_fIsHTML? L"Yes" : L"No",
  6141. pSourceInfo->m_idLine,
  6142. pSourceInfo->m_cchSourceOffset,
  6143. pSourceInfo->m_cchSourceText,
  6144. pSourceInfo->m_cchTargetOffset,
  6145. wszTargetText,
  6146. wszSourceText,
  6147. convPath.GetString());
  6148. OutputDebugStringW(wszDebugLine);
  6149. #endif
  6150. #endif
  6151. }
  6152. }
  6153. OutputDebugStringA("\n\n");
  6154. for (i = 0; i < m_cFilemaps; ++i)
  6155. {
  6156. CFileMap *pFilemap = m_rgpFilemaps[i];
  6157. #if UNICODE
  6158. DBGPRINTF((DBG_CONTEXT, "DBCS mapping table for File %S:\n", pFilemap->m_szPathTranslated));
  6159. #else
  6160. DBGPRINTF((DBG_CONTEXT, "DBCS mapping table for File %s:\n", pFilemap->m_szPathTranslated));
  6161. #endif
  6162. DBGPRINTF((DBG_CONTEXT, "ByteOffset CharOffset\n"));
  6163. for (COffsetInfo *pOffsetInfo = pFilemap->m_rgByte2DBCS.begin();
  6164. pOffsetInfo < pFilemap->m_rgByte2DBCS.end();
  6165. ++pOffsetInfo)
  6166. DebugPrintf("%-10d %-10d\n", pOffsetInfo->m_cbOffset, pOffsetInfo->m_cchOffset);
  6167. DBGPRINTF((DBG_CONTEXT, "\n\n"));
  6168. }
  6169. DBGPRINTF((DBG_CONTEXT, "Include File Hierarchy\n"));
  6170. OutputIncludeHierarchy(m_rgpFilemaps[0], 0);
  6171. DBGPRINTF((DBG_CONTEXT, "\n"));
  6172. }
  6173. /* ============================================================================
  6174. CTemplate::OutputIncludeHierarchy
  6175. print the lineage information that we keep around for include files.
  6176. Print all nodes on one level at the current indentation, then descend for
  6177. nested includes.
  6178. */
  6179. void
  6180. CTemplate::OutputIncludeHierarchy
  6181. (
  6182. CFileMap* pfilemap,
  6183. int cchIndent
  6184. )
  6185. {
  6186. TCHAR szDebugString[256], *pchEnd;
  6187. for (;;)
  6188. {
  6189. pchEnd = szDebugString;
  6190. for (int i = 0; i < cchIndent; ++i)
  6191. *pchEnd++ = _T(' ');
  6192. pchEnd = strcpyEx(pchEnd, pfilemap->m_szPathTranslated);
  6193. *pchEnd++ = _T('\n');
  6194. *pchEnd = _T('\0');
  6195. DBGPRINTF((DBG_CONTEXT, szDebugString));
  6196. // Print anything that this file includes
  6197. if (pfilemap->m_pfilemapChild)
  6198. OutputIncludeHierarchy(pfilemap->m_pfilemapChild, cchIndent + 3);
  6199. // Stop when there are no more siblings on this level
  6200. if (! pfilemap->m_fHasSibling)
  6201. break;
  6202. // Advance to next sibling
  6203. pfilemap = pfilemap->m_pfilemapSibling;
  6204. }
  6205. }
  6206. /* ============================================================================
  6207. CTemplate::OutputScriptSnippets
  6208. print some script from both the source offset & its corresponding target.
  6209. Good way to visually see if the offset conversions are working.
  6210. */
  6211. void
  6212. CTemplate::GetScriptSnippets
  6213. (
  6214. ULONG cchSourceOffset,
  6215. CFileMap *pFilemapSource,
  6216. ULONG cchTargetOffset,
  6217. ULONG idTargetEngine,
  6218. wchar_t *wszSourceText,
  6219. wchar_t *wszTargetText
  6220. )
  6221. {
  6222. // Get target text sample
  6223. if (wszTargetText)
  6224. {
  6225. char *szEngineName;
  6226. PROGLANG_ID *pProgLangID;
  6227. const wchar_t *wszScriptText;
  6228. GetScriptBlock(idTargetEngine, &szEngineName, &pProgLangID, &wszScriptText);
  6229. wszScriptText += cchTargetOffset;
  6230. int cch = wcslen(wszScriptText);
  6231. wcsncpy(wszTargetText, wszScriptText, min(cch, SNIPPET_SIZE) + 1);
  6232. wszTargetText[min(cch, SNIPPET_SIZE)] = 0;
  6233. // Convert newlines to space
  6234. wchar_t *pwch = wszTargetText;
  6235. while (*pwch != 0)
  6236. if (iswspace(*pwch++))
  6237. pwch[-1] = ' ';
  6238. }
  6239. // Get source text sample
  6240. if (wszSourceText)
  6241. {
  6242. ULONG cchMax = 0;
  6243. pFilemapSource->GetText((WORD)m_wCodePage, cchSourceOffset, wszSourceText, NULL, &cchMax, SNIPPET_SIZE);
  6244. wszSourceText[cchMax] = 0;
  6245. // Convert newlines to space
  6246. wchar_t *pwch = wszSourceText;
  6247. while (*pwch != 0)
  6248. if (iswspace(*pwch++))
  6249. pwch[-1] = ' ';
  6250. }
  6251. }
  6252. #endif
  6253. /* ============================================================================
  6254. CTemplate::BuildPersistedDACL
  6255. Builds a DACL based on the SECURITY_DESCRIPTOR already
  6256. associated with the template. The PersistedDACL is modified to include
  6257. full access for administrators and delete access for everyone.
  6258. */
  6259. HRESULT CTemplate::BuildPersistedDACL(PACL *ppRetDACL)
  6260. {
  6261. HRESULT hr = S_OK;
  6262. BOOL bDaclPresent;
  6263. BOOL bDaclDefaulted;
  6264. PACL pSrcDACL = NULL;
  6265. EXPLICIT_ACCESS ea;
  6266. SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
  6267. *ppRetDACL = NULL;
  6268. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
  6269. ea.grfAccessPermissions = SYNCHRONIZE | DELETE;
  6270. ea.grfAccessMode = GRANT_ACCESS;
  6271. ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  6272. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  6273. if (m_rgpFilemaps[0]->m_pSecurityDescriptor == NULL) {
  6274. return S_OK;
  6275. }
  6276. if (!AllocateAndInitializeSid(&WorldAuthority,
  6277. 1,
  6278. SECURITY_WORLD_RID,
  6279. 0,0,0,0,0,0,0,
  6280. (PSID *)(&ea.Trustee.ptstrName)))
  6281. hr = HRESULT_FROM_WIN32(GetLastError());
  6282. else if (!GetSecurityDescriptorDacl(m_rgpFilemaps[0]->m_pSecurityDescriptor,
  6283. &bDaclPresent,
  6284. &pSrcDACL,
  6285. &bDaclDefaulted))
  6286. hr = HRESULT_FROM_WIN32(GetLastError());
  6287. else if ((hr = SetEntriesInAcl(1,
  6288. &ea,
  6289. bDaclPresent ? pSrcDACL : NULL,
  6290. ppRetDACL)) != ERROR_SUCCESS)
  6291. hr = HRESULT_FROM_WIN32(hr);
  6292. if (ea.Trustee.ptstrName)
  6293. FreeSid(ea.Trustee.ptstrName);
  6294. return hr;
  6295. }
  6296. /* ============================================================================
  6297. CTemplate::PersistData
  6298. Attempts to write the contents of the template memory to disk. Note that
  6299. the memory isn't freed here but later when the template ref count falls to
  6300. 1 (indicating that the only reference to the template is the one that the
  6301. cache has on it).
  6302. */
  6303. HRESULT CTemplate::PersistData(char *pszTempFilePath)
  6304. {
  6305. HRESULT hr = S_OK;
  6306. DWORD winErr = 0;
  6307. HANDLE hFile = NULL;
  6308. DWORD dwWritten;
  6309. HANDLE hImpersonationToken = NULL;
  6310. HANDLE hThread;
  6311. PACL pPersistDACL = NULL;
  6312. #if DBG_PERSTEMPL
  6313. DBGPRINTF((DBG_CONTEXT,
  6314. "CTemplate::PersistData() enterred.\n\tTemplate is %s\n\tPersistTempName is %s\n",
  6315. GetSourceFileName(),
  6316. m_szPersistTempName ? m_szPersistTempName : "<none>"));
  6317. #endif
  6318. //
  6319. // if for some reason this template has been marked as invalid, then it is not persistable
  6320. //
  6321. if (m_fIsValid == FALSE)
  6322. {
  6323. hr = E_FAIL;
  6324. goto end;
  6325. }
  6326. // check to see if we already have a persist temp name. If a template moves
  6327. // from the persisted cache back to the memory cache, then the persisted flag
  6328. // will have been lifted but the cache name will remain as an optimization for
  6329. // future persisting.
  6330. if (m_szPersistTempName == NULL) {
  6331. hThread = GetCurrentThread();
  6332. if (OpenThreadToken( hThread,
  6333. TOKEN_READ | TOKEN_IMPERSONATE,
  6334. TRUE,
  6335. &hImpersonationToken )) {
  6336. RevertToSelf();
  6337. }
  6338. // allocate memory for this temp path
  6339. if (!(m_szPersistTempName = (LPSTR)CTemplate::LargeMalloc(MAX_PATH))) {
  6340. hr = E_OUTOFMEMORY;
  6341. }
  6342. // create the temp file. The location of the temp directory was passed
  6343. // in as an argument. The resulting tempfile name in m_szPersistTempName
  6344. // will include this path.
  6345. else if (GetTempFileNameA(pszTempFilePath,
  6346. "ASPTemplate",
  6347. 0,
  6348. m_szPersistTempName) == 0) {
  6349. hr = HRESULT_FROM_WIN32(GetLastError());
  6350. }
  6351. // build a security descriptor to use with this persisted file. It is
  6352. // comprised of the .asp's security descriptor plus a couple of DACLs
  6353. // to allow administrators full access and everyone delete access.
  6354. else if (FAILED(hr = BuildPersistedDACL(&pPersistDACL)));
  6355. else if (pPersistDACL
  6356. && (winErr = SetNamedSecurityInfoA((LPSTR)m_szPersistTempName,
  6357. SE_FILE_OBJECT,
  6358. DACL_SECURITY_INFORMATION,
  6359. NULL,
  6360. NULL,
  6361. pPersistDACL,
  6362. NULL)))
  6363. hr = HRESULT_FROM_WIN32(winErr);
  6364. // create the file
  6365. else if ((hFile = CreateFileA(m_szPersistTempName,
  6366. GENERIC_WRITE,
  6367. 0,
  6368. NULL,
  6369. CREATE_ALWAYS,
  6370. FILE_ATTRIBUTE_NORMAL,
  6371. NULL)) == INVALID_HANDLE_VALUE) {
  6372. hr = HRESULT_FROM_WIN32(GetLastError());
  6373. }
  6374. // slam out the entire contents of the template memory to the file
  6375. else if (WriteFile(hFile,
  6376. m_pbStart,
  6377. m_cbTemplate,
  6378. &dwWritten,
  6379. NULL) == 0) {
  6380. hr = HRESULT_FROM_WIN32(GetLastError());
  6381. }
  6382. // close
  6383. else if (CloseHandle(hFile) == 0) {
  6384. hr = HRESULT_FROM_WIN32(GetLastError());
  6385. }
  6386. else {
  6387. hFile = NULL;
  6388. }
  6389. if (FAILED(hr));
  6390. // make sure that the entire amount was written out
  6391. else if (dwWritten != m_cbTemplate) {
  6392. hr = E_FAIL;
  6393. }
  6394. if (hImpersonationToken) {
  6395. SetThreadToken(&hThread, hImpersonationToken);
  6396. CloseHandle(hImpersonationToken);
  6397. }
  6398. }
  6399. if (FAILED(hr));
  6400. else {
  6401. // if successfull, then note that the template is now persisted.
  6402. // Do an AddRef and Release as a safe way to check to see if the
  6403. // template memory can be freed.
  6404. m_fIsPersisted = TRUE;
  6405. AddRef();
  6406. Release();
  6407. }
  6408. // if errors occurred, clean up any resources.
  6409. if (hr != S_OK) {
  6410. if (hFile)
  6411. CloseHandle(hFile);
  6412. if (m_szPersistTempName)
  6413. CTemplate::LargeFree(m_szPersistTempName);
  6414. m_szPersistTempName = NULL;
  6415. }
  6416. // free the persisted SECURITY_DESCRIPTOR if allocated
  6417. if (pPersistDACL) {
  6418. LocalFree(pPersistDACL);
  6419. }
  6420. end:
  6421. #if DBG_PERSTEMPL
  6422. if (hr == S_OK) {
  6423. DBGPRINTF((DBG_CONTEXT,
  6424. "Persist Successful. TempName is %s\n",
  6425. m_szPersistTempName));
  6426. }
  6427. else {
  6428. DBGPRINTF((DBG_CONTEXT,
  6429. "Persist failed. hr = %x",
  6430. hr));
  6431. }
  6432. #endif
  6433. return hr;
  6434. }
  6435. /* ============================================================================
  6436. CTemplate::UnPersistData
  6437. Restores the template memory from disk.
  6438. */
  6439. HRESULT CTemplate::UnPersistData()
  6440. {
  6441. HRESULT hr = S_OK;
  6442. HANDLE hFile = NULL;
  6443. DWORD dwRead;
  6444. HANDLE hImpersonationToken = NULL;
  6445. HANDLE hThread;
  6446. #if DEB_PERSTEMPL
  6447. DBGPRINTF((DBG_CONTEXT,
  6448. "CTemplate::UnPersistData() enterred.\n\tTemplate is %s\n\tTempName is %s\n",
  6449. m_rgpFilemaps[0]->m_szPathTranslated,
  6450. m_szPersistTempName));
  6451. #endif
  6452. // check to see if the template is already loaded into memory. If so, then
  6453. // all this routine needs to do is lift the IsPersisted flag.
  6454. if (m_pbStart != NULL) {
  6455. m_fIsPersisted = FALSE;
  6456. goto end;
  6457. }
  6458. hThread = GetCurrentThread();
  6459. if (OpenThreadToken( hThread,
  6460. TOKEN_READ | TOKEN_IMPERSONATE,
  6461. TRUE,
  6462. &hImpersonationToken )) {
  6463. RevertToSelf();
  6464. }
  6465. // open the temp file for read
  6466. if ((hFile = CreateFileA(m_szPersistTempName,
  6467. GENERIC_READ,
  6468. 0,
  6469. NULL,
  6470. OPEN_EXISTING,
  6471. 0,
  6472. NULL)) == INVALID_HANDLE_VALUE) {
  6473. hr = HRESULT_FROM_WIN32(GetLastError());
  6474. }
  6475. // allocate the template memory
  6476. else if (!(m_pbStart = (BYTE *)CTemplate::LargeMalloc(m_cbTemplate))) {
  6477. hr = E_OUTOFMEMORY;
  6478. }
  6479. // read in the entire file
  6480. else if (ReadFile(hFile,
  6481. m_pbStart,
  6482. m_cbTemplate,
  6483. &dwRead,
  6484. NULL) == 0) {
  6485. hr = HRESULT_FROM_WIN32(GetLastError());
  6486. }
  6487. // we're done with the file
  6488. else if (CloseHandle(hFile) == 0) {
  6489. hr = HRESULT_FROM_WIN32(GetLastError());
  6490. }
  6491. else {
  6492. hFile = NULL;
  6493. }
  6494. if (FAILED(hr));
  6495. // check to make sure we got everything
  6496. else if (m_cbTemplate != dwRead) {
  6497. hr = E_FAIL;
  6498. }
  6499. else {
  6500. // if not, pretend like this is no longer persisted. Prevents errors
  6501. // in the future.
  6502. m_fIsPersisted = FALSE;
  6503. }
  6504. if (hr != S_OK) {
  6505. // make sure that the file handle was cleaned up
  6506. if (hFile)
  6507. CloseHandle(hFile);
  6508. }
  6509. end:
  6510. if (hImpersonationToken) {
  6511. SetThreadToken(&hThread, hImpersonationToken);
  6512. CloseHandle(hImpersonationToken);
  6513. }
  6514. #if DBG_PERSTEMPL
  6515. if (hr == S_OK) {
  6516. DBGPRINTF((DBG_CONTEXT,
  6517. "UnPersist Successful\n"));
  6518. }
  6519. else {
  6520. DBGPRINTF((DBG_CONTEXT,
  6521. "UnPersist failed. hr = %x",
  6522. hr));
  6523. }
  6524. #endif
  6525. return hr;
  6526. }
  6527. /* ============================================================================
  6528. CTemplate::PersistCleanup
  6529. Cleans up the temp file and the memory holding the temp file name.
  6530. */
  6531. HRESULT CTemplate::PersistCleanup()
  6532. {
  6533. HRESULT hr = S_OK;
  6534. HANDLE hImpersonationToken = NULL;
  6535. HANDLE hThread;
  6536. if (m_szPersistTempName == NULL) {
  6537. return (S_OK);
  6538. }
  6539. hThread = GetCurrentThread();
  6540. if (OpenThreadToken( hThread,
  6541. TOKEN_READ | TOKEN_IMPERSONATE,
  6542. TRUE,
  6543. &hImpersonationToken )) {
  6544. RevertToSelf();
  6545. }
  6546. if (DeleteFileA(m_szPersistTempName) == 0) {
  6547. hr = GetLastError();
  6548. }
  6549. else {
  6550. m_fIsPersisted = FALSE;
  6551. CTemplate::LargeFree(m_szPersistTempName);
  6552. m_szPersistTempName = NULL;
  6553. }
  6554. if (hImpersonationToken) {
  6555. SetThreadToken(&hThread, hImpersonationToken);
  6556. CloseHandle(hImpersonationToken);
  6557. }
  6558. return hr;
  6559. }
  6560. /* ============================================================================
  6561. CTemplate::CreateTransServiceConfig
  6562. Creates the ServicesConfig object for a transacted page
  6563. */
  6564. HRESULT CTemplate::CreateTransServiceConfig(BOOL fEnableTracker)
  6565. {
  6566. HRESULT hr;
  6567. IServiceInheritanceConfig *pIInheritConfig = NULL;
  6568. IServiceTransactionConfig *pITransConfig = NULL;
  6569. IServiceTrackerConfig *pITracker = NULL;
  6570. // see if there is any reason to create the object...
  6571. if ((fEnableTracker == FALSE) && (m_ttTransacted == ttUndefined)) {
  6572. return S_OK;
  6573. }
  6574. hr = CoCreateInstance(CLSID_CServiceConfig,
  6575. NULL,
  6576. CLSCTX_INPROC_SERVER,
  6577. IID_IUnknown,
  6578. (void **)&m_pServicesConfig);
  6579. if (FAILED(hr)) {
  6580. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not CCI ServicesConfig, hr = %#08x\n", hr));
  6581. goto LCleanup;
  6582. }
  6583. hr = m_pServicesConfig->QueryInterface(IID_IServiceInheritanceConfig, (void **)&pIInheritConfig);
  6584. if (FAILED(hr)) {
  6585. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IServiceInheritanceConfig, hr = %#08x\n", hr));
  6586. goto LCleanup;
  6587. }
  6588. hr = pIInheritConfig->ContainingContextTreatment(CSC_Inherit);
  6589. if (FAILED(hr)) {
  6590. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set Inherit mode, hr = %#08x\n", hr));
  6591. goto LCleanup;
  6592. }
  6593. if (m_ttTransacted != ttUndefined) {
  6594. CSC_TransactionConfig transConfig;
  6595. switch (m_ttTransacted) {
  6596. case ttNotSupported:
  6597. transConfig = CSC_NoTransaction;
  6598. break;
  6599. case ttSupported:
  6600. transConfig = CSC_IfContainerIsTransactional;
  6601. break;
  6602. case ttRequired:
  6603. transConfig = CSC_CreateTransactionIfNecessary;
  6604. break;
  6605. case ttRequiresNew:
  6606. transConfig = CSC_NewTransaction;
  6607. break;
  6608. }
  6609. hr = m_pServicesConfig->QueryInterface(IID_IServiceTransactionConfig, (void **)&pITransConfig);
  6610. if (FAILED(hr)) {
  6611. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IID_IServiceTransactionConfig, hr = %#08x\n", hr));
  6612. goto LCleanup;
  6613. }
  6614. hr = pITransConfig->ConfigureTransaction(transConfig);
  6615. if (FAILED(hr)) {
  6616. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set transaction type, hr = %#08x\n", hr));
  6617. goto LCleanup;
  6618. }
  6619. }
  6620. if (fEnableTracker) {
  6621. hr = m_pServicesConfig->QueryInterface(IID_IServiceTrackerConfig, (void **)&pITracker);
  6622. if (FAILED(hr)) {
  6623. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IID_IServiceTrackerConfig, hr = %#08x\n", hr));
  6624. goto LCleanup;
  6625. }
  6626. LPWSTR pwszAppURL;
  6627. LPWSTR pwszASPName;
  6628. #if UNICODE
  6629. pwszAppURL = m_szApplnVirtPath;
  6630. pwszASPName = GetSourceFileName(SOURCEPATHTYPE_VIRTUAL);
  6631. #else
  6632. CMBCSToWChar convAppln;
  6633. CMBCSToWChar convASPName;
  6634. hr = convAppln.Init(m_szApplnVirtPath);
  6635. if (FAILED(hr)) {
  6636. goto LCleanup;
  6637. }
  6638. hr = convASPName.Init(GetSourceFileName(SOURCEPATHTYPE_VIRTUAL));
  6639. if (FAILED(hr)) {
  6640. goto LCleanup;
  6641. }
  6642. pwszAppURL = convAppln.GetString();
  6643. pwszASPName = convASPName.GetString();
  6644. #endif
  6645. pwszASPName += wcslen(pwszAppURL) + 1;
  6646. hr = pITracker->TrackerConfig(CSC_UseTracker, pwszAppURL, pwszASPName);
  6647. if (FAILED(hr)) {
  6648. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set Inherit mode, hr = %#08x\n", hr));
  6649. goto LCleanup;
  6650. }
  6651. }
  6652. LCleanup:
  6653. if (pIInheritConfig)
  6654. pIInheritConfig->Release();
  6655. if (pITransConfig)
  6656. pITransConfig->Release();
  6657. return hr;
  6658. }
  6659. /* ****************************************************************************
  6660. CIncFile member functions
  6661. */
  6662. /* ============================================================================
  6663. CIncFile::CIncFile
  6664. Constructor
  6665. Returns:
  6666. Nothing
  6667. Side effects:
  6668. None
  6669. */
  6670. CIncFile::CIncFile
  6671. (
  6672. )
  6673. : m_szIncFile(NULL),
  6674. m_fCsInited(FALSE),
  6675. m_CPTextEvents(this, IID_IDebugDocumentTextEvents),
  6676. m_cRefs(0)
  6677. { }
  6678. /* ============================================================================
  6679. CIncFile::Init
  6680. Inits the CIncFile object
  6681. Returns:
  6682. HRESULT
  6683. Side effects:
  6684. None
  6685. */
  6686. HRESULT
  6687. CIncFile::Init
  6688. (
  6689. const TCHAR* szIncFile // file name
  6690. )
  6691. {
  6692. HRESULT hr = S_OK;
  6693. WIN32_FILE_ATTRIBUTE_DATA fad; // win32 file attributes data structure
  6694. ErrInitCriticalSection(&m_csUpdate, hr);
  6695. m_fCsInited = TRUE;
  6696. if(NULL == (m_szIncFile = (LPTSTR) CTemplate::SmallMalloc((_tcslen(szIncFile) + 1)*sizeof(TCHAR)))) {
  6697. hr = E_OUTOFMEMORY;
  6698. goto LExit;
  6699. }
  6700. _tcscpy(m_szIncFile, szIncFile);
  6701. // init hash table element base class
  6702. if(FAILED(hr = CLinkElem::Init(m_szIncFile, _tcslen(m_szIncFile)*sizeof(TCHAR))))
  6703. goto LExit;
  6704. LExit:
  6705. return hr;
  6706. }
  6707. /* ============================================================================
  6708. CIncFile::~CIncFile
  6709. Destructor
  6710. Returns:
  6711. Nothing
  6712. Side effects:
  6713. None
  6714. */
  6715. CIncFile::~CIncFile
  6716. (
  6717. )
  6718. {
  6719. #if UNICODE
  6720. DBGPRINTF((DBG_CONTEXT, "Include file deleted: %S\n", m_szIncFile));
  6721. #else
  6722. DBGPRINTF((DBG_CONTEXT, "Include file deleted: %s\n", m_szIncFile));
  6723. #endif
  6724. Assert(m_cRefs == 0);
  6725. SmallTemplateFreeNullify((void**) &m_szIncFile);
  6726. if(m_fCsInited)
  6727. DeleteCriticalSection(&m_csUpdate);
  6728. }
  6729. /* ============================================================================
  6730. CIncFile::GetTemplate
  6731. Get i'th template user from CIncFile
  6732. Returns:
  6733. NULL if "iTemplate" is out of range, m_rgpTemplates[iTemplate] otherwise
  6734. Side effects:
  6735. None
  6736. */
  6737. CTemplate*
  6738. CIncFile::GetTemplate
  6739. (
  6740. int iTemplate
  6741. )
  6742. {
  6743. if (iTemplate < 0 || iTemplate >= (signed int) m_rgpTemplates.length())
  6744. return NULL;
  6745. else
  6746. return m_rgpTemplates[iTemplate];
  6747. }
  6748. /* ============================================================================
  6749. CIncFile::QueryInterface
  6750. Provides QueryInterface implementation for CIncFile
  6751. NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo.
  6752. */
  6753. HRESULT
  6754. CIncFile::QueryInterface(const GUID &uidInterface, void **ppvObj)
  6755. {
  6756. if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider)
  6757. *ppvObj = static_cast<IDebugDocumentProvider *>(this);
  6758. else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText)
  6759. *ppvObj = static_cast<IDebugDocumentText *>(this);
  6760. else if (uidInterface == IID_IConnectionPointContainer)
  6761. *ppvObj = static_cast<IConnectionPointContainer *>(this);
  6762. else
  6763. *ppvObj = NULL;
  6764. if (*ppvObj)
  6765. {
  6766. AddRef();
  6767. return S_OK;
  6768. }
  6769. else
  6770. return E_NOINTERFACE;
  6771. }
  6772. /* ============================================================================
  6773. CIncFile::AddRef
  6774. Adds a ref to this IncFile, thread-safely
  6775. */
  6776. ULONG
  6777. CIncFile::AddRef()
  6778. {
  6779. InterlockedIncrement(&m_cRefs);
  6780. return m_cRefs;
  6781. }
  6782. /* ============================================================================
  6783. CIncFile::Release
  6784. Releases a ref to this IncFile, thread-safely
  6785. */
  6786. ULONG
  6787. CIncFile::Release()
  6788. {
  6789. LONG cRef = InterlockedDecrement(&m_cRefs);
  6790. if (cRef == 0)
  6791. {
  6792. delete this;
  6793. }
  6794. return cRef;
  6795. }
  6796. /* ****************************************************************************
  6797. IDebugDocumentProvider implementation for includes
  6798. */
  6799. /* ============================================================================
  6800. CIncFile::GetDocument
  6801. Return a pointer to the IDebugDocument implementation. (same object in this case)
  6802. Returns:
  6803. *ppDebugDoc is set to "this".
  6804. Notes:
  6805. always succeeds
  6806. */
  6807. HRESULT CIncFile::GetDocument
  6808. (
  6809. IDebugDocument **ppDebugDoc
  6810. )
  6811. {
  6812. return QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDoc));
  6813. }
  6814. /* ============================================================================
  6815. CIncFile::GetName
  6816. Return the various names of a document.
  6817. */
  6818. HRESULT CIncFile::GetName
  6819. (
  6820. /* [in] */ DOCUMENTNAMETYPE doctype,
  6821. /* [out] */ BSTR *pbstrName
  6822. )
  6823. {
  6824. switch (doctype) {
  6825. case DOCUMENTNAMETYPE_APPNODE:
  6826. case DOCUMENTNAMETYPE_FILE_TAIL:
  6827. case DOCUMENTNAMETYPE_TITLE:
  6828. // Use the name of the include file (char after last back-slash) converted to lower case.
  6829. {
  6830. TCHAR *szFilePart = _tcsrchr(m_szIncFile, _T('\\'));
  6831. Assert (szFilePart != NULL);
  6832. #if UNICODE
  6833. *pbstrName = SysAllocString(szFilePart + 1);
  6834. if (*pbstrName == NULL) {
  6835. return E_OUTOFMEMORY;
  6836. }
  6837. #else
  6838. if (FAILED(SysAllocStringFromSz(szFilePart + 1, 0, pbstrName, CP_ACP)))
  6839. return E_FAIL;
  6840. #endif
  6841. if (*pbstrName != NULL)
  6842. _wcslwr(*pbstrName);
  6843. return S_OK;
  6844. }
  6845. case DOCUMENTNAMETYPE_URL:
  6846. // prefix with the URL, use szPathInfo for the rest of the path
  6847. {
  6848. CTemplate::CFileMap *pFilemap = GetFilemap();
  6849. if (pFilemap->FHasVirtPath()) {
  6850. STACK_BUFFER( tempName, MAX_PATH );
  6851. CTemplate *pTemplate = m_rgpTemplates[0];
  6852. int cbURLPrefix = DIFF(pTemplate->m_szApplnVirtPath - pTemplate->m_szApplnURL)*sizeof(TCHAR);
  6853. if (!tempName.Resize(cbURLPrefix + ((_tcslen(pFilemap->m_szPathInfo) + 1)*sizeof(TCHAR)))) {
  6854. return E_OUTOFMEMORY;
  6855. }
  6856. TCHAR *szURL = (TCHAR *)tempName.QueryPtr();
  6857. memcpy(szURL, pTemplate->m_szApplnURL, cbURLPrefix);
  6858. _tcscpy(&szURL[cbURLPrefix/sizeof(TCHAR)], pFilemap->m_szPathInfo);
  6859. #if UNICODE
  6860. *pbstrName = SysAllocString(szURL);
  6861. if (*pbstrName == NULL) {
  6862. return (E_OUTOFMEMORY);
  6863. }
  6864. return S_OK;
  6865. #else
  6866. return SysAllocStringFromSz(szURL, 0, pbstrName, pTemplate->m_wCodePage);
  6867. #endif
  6868. }
  6869. else {
  6870. *pbstrName = NULL;
  6871. return E_FAIL;
  6872. }
  6873. }
  6874. default:
  6875. return E_FAIL;
  6876. }
  6877. }
  6878. /* ****************************************************************************
  6879. IDebugDocumentText implementation
  6880. */
  6881. /* ============================================================================
  6882. CIncFile::GetSize
  6883. Return the number of lines & characters in the document
  6884. */
  6885. HRESULT CIncFile::GetSize
  6886. (
  6887. /* [out] */ ULONG *pcLines,
  6888. /* [out] */ ULONG *pcChars
  6889. )
  6890. {
  6891. CTemplate::CFileMap *pfilemap = GetFilemap();
  6892. *pcLines = ULONG_MAX;
  6893. *pcChars = pfilemap->m_cChars;
  6894. #if UNICODE
  6895. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%S\") returns %lu characters (%lu lines)\n", pfilemap->m_szPathTranslated, *pcChars, *pcLines));
  6896. #else
  6897. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%s\") returns %lu characters (%lu lines)\n", pfilemap->m_szPathTranslated, *pcChars, *pcLines));
  6898. #endif
  6899. return S_OK;
  6900. }
  6901. /* ============================================================================
  6902. CTemplate::GetDocumentAttributes
  6903. Return doc attributes
  6904. */
  6905. HRESULT CIncFile::GetDocumentAttributes
  6906. (
  6907. /* [out] */ TEXT_DOC_ATTR *ptextdocattr
  6908. )
  6909. {
  6910. // Easy way to tell debugger that we don't support editing.
  6911. *ptextdocattr = TEXT_DOC_ATTR_READONLY;
  6912. return S_OK;
  6913. }
  6914. /* ============================================================================
  6915. CIncFile::GetPositionOfLine
  6916. From a line number, return the character offset of the beginning
  6917. I don't think we need this function. It is meant to support line oriented
  6918. debuggers, of which Caesar is not one.
  6919. */
  6920. HRESULT CIncFile::GetPositionOfLine
  6921. (
  6922. /* [in] */ ULONG cLineNumber,
  6923. /* [out] */ ULONG *pcCharacterPosition
  6924. )
  6925. {
  6926. return m_rgpTemplates[0]->GetPositionOfLine(GetFilemap(), cLineNumber, pcCharacterPosition);
  6927. }
  6928. /* ============================================================================
  6929. CIncFile::GetLineOfPosition
  6930. From a character offset, return the line number and offset within the line
  6931. I don't think we need this function. It is meant to support line oriented
  6932. debuggers, of which Caesar is not one.
  6933. */
  6934. HRESULT CIncFile::GetLineOfPosition
  6935. (
  6936. /* [in] */ ULONG cCharacterPosition,
  6937. /* [out] */ ULONG *pcLineNumber,
  6938. /* [out] */ ULONG *pcCharacterOffsetInLine
  6939. )
  6940. {
  6941. return m_rgpTemplates[0]->GetLineOfPosition(GetFilemap(), cCharacterPosition, pcLineNumber, pcCharacterOffsetInLine);
  6942. }
  6943. /* ============================================================================
  6944. CIncFile::GetText
  6945. From a character offset and length, return the document text
  6946. */
  6947. HRESULT CIncFile::GetText
  6948. (
  6949. ULONG cchSourceOffset,
  6950. WCHAR *pwchText,
  6951. SOURCE_TEXT_ATTR *pTextAttr,
  6952. ULONG *pcChars,
  6953. ULONG cMaxChars
  6954. )
  6955. {
  6956. return GetFilemap()->GetText((WORD)m_rgpTemplates[0]->m_wCodePage, cchSourceOffset, pwchText, pTextAttr, pcChars, cMaxChars);
  6957. }
  6958. /* ============================================================================
  6959. CIncFile::GetPositionOfContext
  6960. Decompose a document context into the document offset & length
  6961. */
  6962. HRESULT CIncFile::GetPositionOfContext
  6963. (
  6964. /* [in] */ IDebugDocumentContext *pUnknownDocumentContext,
  6965. /* [out] */ ULONG *pcchSourceOffset,
  6966. /* [out] */ ULONG *pcchText
  6967. )
  6968. {
  6969. // Make sure that the context is one of ours
  6970. CIncFileDocumentContext *pDocumentContext;
  6971. if (FAILED(pUnknownDocumentContext->QueryInterface(IID_IDenaliIncFileDocumentContext, reinterpret_cast<void **>(&pDocumentContext))))
  6972. return E_FAIL;
  6973. if (pcchSourceOffset)
  6974. *pcchSourceOffset = pDocumentContext->m_cchSourceOffset;
  6975. if (pcchText)
  6976. *pcchText = pDocumentContext->m_cchText;
  6977. pDocumentContext->Release();
  6978. return S_OK;
  6979. }
  6980. /* ============================================================================
  6981. CIncFile::GetContextOfPosition
  6982. Given the character position & number of characters in the document,
  6983. encapsulate this into a document context object.
  6984. */
  6985. HRESULT CIncFile::GetContextOfPosition
  6986. (
  6987. /* [in] */ ULONG cchSourceOffset,
  6988. /* [in] */ ULONG cchText,
  6989. /* [out] */ IDebugDocumentContext **ppDocumentContext
  6990. )
  6991. {
  6992. if (
  6993. (*ppDocumentContext = new CIncFileDocumentContext(this, cchSourceOffset, cchText))
  6994. == NULL
  6995. )
  6996. return E_OUTOFMEMORY;
  6997. return S_OK;
  6998. }
  6999. /* ****************************************************************************
  7000. IConnectionPointContainer implementation
  7001. */
  7002. /* ============================================================================
  7003. CIncFile::FindConnectionPoint
  7004. From a character offset and length, return the document text
  7005. */
  7006. HRESULT CIncFile::FindConnectionPoint
  7007. (
  7008. const GUID &uidConnection,
  7009. IConnectionPoint **ppCP
  7010. )
  7011. {
  7012. if (uidConnection == IID_IDebugDocumentTextEvents)
  7013. return m_CPTextEvents.QueryInterface(IID_IConnectionPoint, reinterpret_cast<void **>(ppCP));
  7014. else
  7015. {
  7016. *ppCP = NULL;
  7017. return E_NOINTERFACE;
  7018. }
  7019. }
  7020. /* ============================================================================
  7021. CIncFile::GetFilemap
  7022. Returns a CFileMap pointer for this include file. (Note: There are several
  7023. CFileMaps that may be used, corresponding to each template. This function
  7024. selects one of them.)
  7025. Returns:
  7026. Corresponding CFileMap
  7027. Side effects:
  7028. None
  7029. */
  7030. CTemplate::CFileMap *
  7031. CIncFile::GetFilemap
  7032. (
  7033. )
  7034. {
  7035. // Get pointer to first template's filemaps
  7036. CTemplate::CFileMap **ppFilemapInc = &m_rgpTemplates[0]->m_rgpFilemaps[1];
  7037. BOOL fFoundInc = FALSE;
  7038. // Look for the filemap whose name corresponds to this IncFile. It had better exist
  7039. // in all template filemaps.
  7040. // NOTE: Start searching at position 1, because position 0 is the template itself.
  7041. //
  7042. for (unsigned i = 1; i < m_rgpTemplates[0]->m_cFilemaps && !fFoundInc; ++i)
  7043. if (_tcscmp(m_szIncFile, (*ppFilemapInc++)->m_szPathTranslated) == 0)
  7044. fFoundInc = TRUE;
  7045. Assert (fFoundInc);
  7046. return ppFilemapInc[-1];
  7047. }
  7048. /* ============================================================================
  7049. CIncFile::AddTemplate
  7050. Adds a template to the list of templates that include this inc-file
  7051. Returns:
  7052. HRESULT
  7053. Side effects:
  7054. None
  7055. */
  7056. HRESULT
  7057. CIncFile::AddTemplate
  7058. (
  7059. CTemplate* pTemplate
  7060. )
  7061. {
  7062. EnterCriticalSection(&m_csUpdate);
  7063. // Add the template to the list only if it does not exist
  7064. if (m_rgpTemplates.find(pTemplate) == -1)
  7065. {
  7066. if (FAILED(m_rgpTemplates.append(pTemplate)))
  7067. {
  7068. LeaveCriticalSection(&m_csUpdate);
  7069. return E_OUTOFMEMORY;
  7070. }
  7071. // Notify the debugger that template dependency has changed
  7072. // (Ignore failure)
  7073. //
  7074. if (g_pDebugApp)
  7075. {
  7076. IF_DEBUG(SCRIPT_DEBUGGER)
  7077. DBGPRINTF((DBG_CONTEXT, "AddTemplate: Notifying debugger to refresh breakpoints\n"));
  7078. InvokeDebuggerWithThreadSwitch
  7079. (
  7080. g_pDebugApp,
  7081. DEBUGGER_EVENT_ON_REFRESH_BREAKPOINT,
  7082. static_cast<IDebugDocument *>(this)
  7083. );
  7084. }
  7085. }
  7086. LeaveCriticalSection(&m_csUpdate);
  7087. return S_OK;
  7088. }
  7089. /* ============================================================================
  7090. CIncFile::RemoveTemplate
  7091. Removes a template from the template list
  7092. Returns:
  7093. Nothing
  7094. Side effects:
  7095. Compresses the removed template's ptr out of template ptrs array (see "back-copy", below)
  7096. Decrements template count
  7097. */
  7098. void
  7099. CIncFile::RemoveTemplate
  7100. (
  7101. CTemplate* pTemplate
  7102. )
  7103. {
  7104. EnterCriticalSection(&m_csUpdate);
  7105. // find the template in list
  7106. int i = m_rgpTemplates.find(pTemplate);
  7107. // Remove the element (If we found it - possible that this is 2nd instance of #include and was previously removed)
  7108. if (i != -1)
  7109. {
  7110. m_rgpTemplates.removeAt(i);
  7111. // Notify the debugger that template dependency has changed
  7112. // (Ignore failure)
  7113. //
  7114. if (g_pDebugApp)
  7115. {
  7116. IF_DEBUG(SCRIPT_DEBUGGER)
  7117. DBGPRINTF((DBG_CONTEXT, "RemoveTemplate: Notifying debugger to refresh breakpoints\n"));
  7118. InvokeDebuggerWithThreadSwitch
  7119. (
  7120. g_pDebugApp,
  7121. DEBUGGER_EVENT_ON_REFRESH_BREAKPOINT,
  7122. static_cast<IDebugDocument *>(this)
  7123. );
  7124. }
  7125. }
  7126. LeaveCriticalSection(&m_csUpdate);
  7127. }
  7128. /* ============================================================================
  7129. CIncFile::FlushTemplates
  7130. Flushes all of this inc-file's templates from the global template cache
  7131. Returns:
  7132. TRUE if all templates flushed, FALSE if some left
  7133. Side effects:
  7134. None
  7135. */
  7136. BOOL
  7137. CIncFile::FlushTemplates
  7138. (
  7139. )
  7140. {
  7141. /* NOTE we have a cross-dependency with RemoveTemplate() because the following call chain
  7142. occurs when an inc-file gets flushed:
  7143. CIncFileMap::Flush
  7144. CIncFile::FlushTemplates
  7145. CTemplateCacheManager::Flush
  7146. CTemplate::RemoveFromIncFiles
  7147. CIncFile::RemoveTemplate
  7148. The problem is that RemoveTemplate() updates m_cTemplates and m_rgTemplates, so these members
  7149. will not be stable during the loop within FlushTemplates.
  7150. To get around this, we make a local copy of m_rgTemplates.
  7151. */
  7152. EnterCriticalSection(&m_csUpdate);
  7153. STACK_BUFFER( tempTemplates, 128 );
  7154. STACK_BUFFER( tempFile, MAX_PATH );
  7155. UINT cTemplates = m_rgpTemplates.length();
  7156. if (!tempTemplates.Resize(cTemplates * sizeof(CTemplate*))) {
  7157. // failed to get memory. The best we can do is return FALSE to indicate
  7158. // that not all templates where flushed.
  7159. LeaveCriticalSection(&m_csUpdate);
  7160. return FALSE;
  7161. }
  7162. CTemplate** rgpTemplates = static_cast<CTemplate**> (tempTemplates.QueryPtr());
  7163. memcpy(rgpTemplates, m_rgpTemplates.vec(), sizeof(CTemplate *) * cTemplates);
  7164. UINT cTemplatesFlushed = 0;
  7165. for(UINT i = 0; i < cTemplates; i++)
  7166. {
  7167. // If the template is ready now, flush it
  7168. if(rgpTemplates[i]->m_fReadyForUse && !(rgpTemplates[i]->m_fDontCache))
  7169. {
  7170. // bug 917: make a local copy of template file name, since the member gets freed part way through g_TemplateCache.Flush
  7171. TCHAR* szTemp = NULL;
  7172. szTemp = rgpTemplates[i]->GetSourceFileName();
  7173. if (szTemp)
  7174. {
  7175. if (!tempFile.Resize((_tcslen(szTemp) + 1)*sizeof(TCHAR))) {
  7176. // failed on this one. Continue and try to flush as many
  7177. // as we can.
  7178. continue;
  7179. }
  7180. TCHAR *szTemplateFile = (TCHAR *)tempFile.QueryPtr();
  7181. _tcscpy(szTemplateFile, szTemp);
  7182. g_TemplateCache.Flush(szTemplateFile, MATCH_ALL_INSTANCE_IDS);
  7183. cTemplatesFlushed++;
  7184. }
  7185. }
  7186. // If the template was not ready, we don't flush. It will probably
  7187. // pick up the current include file anyway
  7188. }
  7189. LeaveCriticalSection(&m_csUpdate);
  7190. return (cTemplates == cTemplatesFlushed);
  7191. }
  7192. /* ============================================================================
  7193. CIncFile::OnIncFileDecache
  7194. Callback which we use to call onDestroy events in the debugger just before
  7195. we are removed from the IncFile cache.
  7196. REF COUNTING NOTE:
  7197. Since debugging client has a reference to the IDebugDocument, the include needs
  7198. to dis-associate with the debugger at a point in time before destruction.
  7199. Otherwise, the reference will never go to zero.
  7200. */
  7201. void
  7202. CIncFile::OnIncFileDecache
  7203. (
  7204. )
  7205. {
  7206. if (m_CPTextEvents.FIsEmpty() || g_pDebugApp == NULL)
  7207. return;
  7208. IEnumConnections *pConnIterator;
  7209. if (SUCCEEDED(m_CPTextEvents.EnumConnections(&pConnIterator)))
  7210. {
  7211. CONNECTDATA ConnectData;
  7212. while (pConnIterator->Next(1, &ConnectData, NULL) == S_OK)
  7213. {
  7214. IDebugDocumentTextEvents *pTextEventSink;
  7215. if (SUCCEEDED(ConnectData.pUnk->QueryInterface(IID_IDebugDocumentTextEvents, reinterpret_cast<void **>(&pTextEventSink))))
  7216. {
  7217. InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_ON_DESTROY, pTextEventSink);
  7218. pTextEventSink->Release();
  7219. }
  7220. ConnectData.pUnk->Release();
  7221. }
  7222. pConnIterator->Release();
  7223. }
  7224. }
  7225. /* ****************************************************************************
  7226. CTemplate::CBuffer member functions
  7227. */
  7228. /* ============================================================================
  7229. CTemplate::CBuffer::CBuffer
  7230. Ctor
  7231. */
  7232. CTemplate::CBuffer::CBuffer()
  7233. :
  7234. m_pItems(NULL),
  7235. m_cSlots(0),
  7236. m_cItems(0),
  7237. m_pbData(NULL),
  7238. m_cbData(0),
  7239. m_cbDataUsed(0)
  7240. {
  7241. }
  7242. /* ============================================================================
  7243. CTemplate::CBuffer::~CBuffer
  7244. Dtor
  7245. */
  7246. CTemplate::CBuffer::~CBuffer()
  7247. {
  7248. if(m_pItems)
  7249. CTemplate::SmallFree(m_pItems);
  7250. if(m_pbData)
  7251. CTemplate::LargeFree(m_pbData);
  7252. }
  7253. /* ============================================================================
  7254. CTemplate::CBuffer::Init
  7255. Inits a CBuffer
  7256. */
  7257. void
  7258. CTemplate::CBuffer::Init
  7259. (
  7260. USHORT cSlots,
  7261. ULONG cbData
  7262. )
  7263. {
  7264. m_cSlots = cSlots;
  7265. m_cbData = cbData;
  7266. // Allocate space for storing byte range items
  7267. if(!(m_pItems = (CByteRange*) CTemplate::SmallMalloc(m_cSlots * sizeof(CByteRange))))
  7268. THROW(E_OUTOFMEMORY);
  7269. // Allocate space for storing local data, if there is any
  7270. if(m_cbData > 0)
  7271. {
  7272. if(!(m_pbData = (BYTE*) CTemplate::LargeMalloc(m_cbData)))
  7273. THROW(E_OUTOFMEMORY);
  7274. }
  7275. }
  7276. /* ============================================================================
  7277. CTemplate::CBuffer::Append
  7278. Appends to a CBuffer
  7279. */
  7280. void
  7281. CTemplate::CBuffer::Append
  7282. (
  7283. const CByteRange& br, // byte range to append
  7284. BOOL fLocal, // append local?
  7285. UINT idSequence, // segment sequence id
  7286. CFileMap* pfilemap,
  7287. BOOL fLocalString // append local as a string? (length-prefixed, null-terminated)
  7288. )
  7289. {
  7290. // calc bytes required to store byte range; allow for length prefix and null if a local string
  7291. ULONG cbRequired = (ULONG)(br.m_cb + (fLocalString ? sizeof(br.m_cb) + 1 : 0));
  7292. // If caller passed a non-local zero-length byte range, no-op and return;
  7293. // allows callers to ignore byte range size
  7294. // NOTE we store empty local byte ranges - required by token list
  7295. if(!fLocal && br.m_cb == 0)
  7296. return;
  7297. if(fLocal)
  7298. {
  7299. if((m_cbData - m_cbDataUsed) < cbRequired)
  7300. {
  7301. // Reallocate space for storing local data - we grab twice what we had before
  7302. // or twice current requirement, whichever is more
  7303. m_cbData = 2 * (m_cbData > cbRequired ? m_cbData : cbRequired);
  7304. if(!(m_pbData = (BYTE*) CTemplate::LargeReAlloc(m_pbData, m_cbData)))
  7305. THROW(E_OUTOFMEMORY);
  7306. }
  7307. // if appending as a local string, copy length-prefix to buffer
  7308. if(fLocalString)
  7309. {
  7310. memcpy(m_pbData + m_cbDataUsed, &(br.m_cb), sizeof(br.m_cb));
  7311. m_cbDataUsed += sizeof(br.m_cb);
  7312. }
  7313. // copy data to buffer
  7314. memcpy(m_pbData + m_cbDataUsed, br.m_pb, br.m_cb);
  7315. m_cbDataUsed += br.m_cb;
  7316. // if appending as a local string, copy null terminator to buffer
  7317. if(fLocalString)
  7318. *(m_pbData + m_cbDataUsed++) = NULL;
  7319. }
  7320. if(m_cItems >= m_cSlots)
  7321. {
  7322. // Reallocate space for storing byte range items - we grab twice what we had before
  7323. m_cSlots *= 2;
  7324. if(!(m_pItems = (CByteRange*) CTemplate::SmallReAlloc(m_pItems, m_cSlots * sizeof(*m_pItems))))
  7325. THROW(E_OUTOFMEMORY);
  7326. }
  7327. // Set the (new) last item to this byte range
  7328. SetItem(m_cItems++, br, fLocal, idSequence, pfilemap, fLocalString);
  7329. }
  7330. /* ============================================================================
  7331. CTemplate::CBuffer::GetItem
  7332. Gets an item from a CBuffer, as a byte range
  7333. Returns:
  7334. Nothing
  7335. Side effects:
  7336. None
  7337. */
  7338. void
  7339. CTemplate::CBuffer::GetItem
  7340. (
  7341. UINT i, // index of item
  7342. CByteRange& br // byte range containing returned item (out-parameter)
  7343. )
  7344. {
  7345. Assert(i < m_cItems);
  7346. // for local data, ptr is offset only; must add it to base ptr
  7347. br.m_pb = m_pItems[i].m_pb + (m_pItems[i].m_fLocal ? (DWORD_PTR) m_pbData : 0);
  7348. br.m_cb = m_pItems[i].m_cb;
  7349. br.m_fLocal = m_pItems[i].m_fLocal;
  7350. br.m_idSequence = m_pItems[i].m_idSequence;
  7351. br.m_pfilemap = m_pItems[i].m_pfilemap;
  7352. }
  7353. /* ============================================================================
  7354. CTemplate::CBuffer::SetItem
  7355. Sets a CBuffer item to a new value
  7356. Returns
  7357. Nothing
  7358. Side effects
  7359. Throws error on non-existent item index
  7360. */
  7361. void
  7362. CTemplate::CBuffer::SetItem
  7363. (
  7364. UINT i,
  7365. const CByteRange& br, // byte range to set item to
  7366. BOOL fLocal, // is item local in buffer?
  7367. UINT idSequence, // segment sequence id
  7368. CFileMap * pfilemap, // file where segment came from
  7369. BOOL fLocalString // append local as a string? (length-prefixed, null-terminated)
  7370. )
  7371. {
  7372. // If buffer item i does not exist, fail
  7373. if(i >= m_cSlots)
  7374. THROW(E_FAIL);
  7375. // for local data, store ptr as offset only - avoids fixup after realloc
  7376. // NOTE offset == data used offset - length of data - null terminator (if local string)
  7377. m_pItems[i].m_pb = (fLocal
  7378. ? (BYTE*)(m_cbDataUsed - br.m_cb -
  7379. (fLocalString
  7380. ? sizeof(BYTE)
  7381. : 0
  7382. ))
  7383. : (BYTE*)br.m_pb);
  7384. m_pItems[i].m_cb = br.m_cb;
  7385. m_pItems[i].m_fLocal = fLocal;
  7386. m_pItems[i].m_idSequence = idSequence;
  7387. m_pItems[i].m_pfilemap = pfilemap;
  7388. }
  7389. /* ============================================================================
  7390. CTemplate::CBuffer::PszLocal
  7391. Gets i-th locally-buffered string within the buffer.
  7392. Returns:
  7393. Ptr to locally-buffered string; NULL if not found
  7394. Side effects:
  7395. None
  7396. */
  7397. LPSTR
  7398. CTemplate::CBuffer::PszLocal
  7399. (
  7400. UINT i // index of item to retrieve
  7401. )
  7402. {
  7403. CByteRange br;
  7404. GetItem(i, br);
  7405. if(!br.m_fLocal)
  7406. return NULL;
  7407. return (LPSTR) br.m_pb;
  7408. }
  7409. /* ****************************************************************************
  7410. CTemplate::CScriptStore member functions
  7411. */
  7412. /* ============================================================================
  7413. CTemplate::CScriptStore::~CScriptStore
  7414. Destructor - frees memory
  7415. Returns:
  7416. nothing
  7417. Side effects:
  7418. none
  7419. */
  7420. CTemplate::CScriptStore::~CScriptStore()
  7421. {
  7422. UINT i;
  7423. for(i = 0; i < m_cSegmentBuffers; i++)
  7424. delete m_ppbufSegments[i];
  7425. if(m_ppbufSegments != NULL)
  7426. CTemplate::SmallFree(m_ppbufSegments);
  7427. if(m_rgProgLangId != NULL)
  7428. CTemplate::SmallFree(m_rgProgLangId);
  7429. }
  7430. /* ============================================================================
  7431. CTemplate::CScriptStore::Init
  7432. Inits the script store
  7433. Returns:
  7434. nothing
  7435. Side effects:
  7436. allocates memory
  7437. */
  7438. HRESULT
  7439. CTemplate::CScriptStore::Init
  7440. (
  7441. LPCSTR szDefaultScriptLanguage,
  7442. CLSID *pCLSIDDefaultEngine
  7443. )
  7444. {
  7445. HRESULT hr = S_OK;
  7446. UINT i;
  7447. CByteRange brDefaultScriptLanguage;
  7448. // Check for NULL pointers - can happen if Application has invalid default lang
  7449. if (szDefaultScriptLanguage == NULL || pCLSIDDefaultEngine == NULL)
  7450. return TYPE_E_ELEMENTNOTFOUND;
  7451. /* init segment buffers count based on:
  7452. - two for default engine (one primary, one tagged)
  7453. - one each for other engines (tagged only)
  7454. */
  7455. m_cSegmentBuffers = C_SCRIPTENGINESDEFAULT + 1;
  7456. // init segments buffers
  7457. if(NULL == (m_ppbufSegments = (CBuffer**) CTemplate::SmallMalloc(m_cSegmentBuffers * sizeof(CBuffer*))))
  7458. {
  7459. hr = E_OUTOFMEMORY;
  7460. goto LExit;
  7461. }
  7462. for(i = 0; i < m_cSegmentBuffers; i++)
  7463. {
  7464. if(NULL == (m_ppbufSegments[i] = new CBuffer))
  7465. {
  7466. hr = E_OUTOFMEMORY;
  7467. goto LExit;
  7468. }
  7469. m_ppbufSegments[i]->Init((C_SCRIPTSEGMENTSDEFAULT), 0);
  7470. }
  7471. // Append default engine to script store
  7472. brDefaultScriptLanguage.m_cb = strlen(szDefaultScriptLanguage);
  7473. brDefaultScriptLanguage.m_pb = (unsigned char *)szDefaultScriptLanguage;
  7474. hr = AppendEngine(brDefaultScriptLanguage, pCLSIDDefaultEngine, /* idSequence */ 0);
  7475. LExit:
  7476. return hr;
  7477. }
  7478. /* ============================================================================
  7479. CTemplate::CScriptStore::AppendEngine
  7480. Appends a script engine to the script store
  7481. Returns:
  7482. HRESULT
  7483. Side effects:
  7484. None
  7485. */
  7486. HRESULT
  7487. CTemplate::CScriptStore::AppendEngine
  7488. (
  7489. CByteRange& brEngine, // engine name
  7490. PROGLANG_ID* pProgLangId, // ptr to prog lang id - pass NULL to have this function get proglangid from registry
  7491. UINT idSequence // segment sequence id
  7492. )
  7493. {
  7494. HRESULT hr = S_OK;
  7495. USHORT cEngines; // count of engines
  7496. TRY
  7497. // if no engines yet, init engine names buffer
  7498. if(CountPreliminaryEngines() == 0)
  7499. m_bufEngineNames.Init(C_SCRIPTENGINESDEFAULT, 0);
  7500. // Append engine name to buffer
  7501. m_bufEngineNames.Append(brEngine, FALSE, idSequence, NULL);
  7502. CATCH(hrException)
  7503. hr = hrException;
  7504. goto LExit;
  7505. END_TRY
  7506. Assert(CountPreliminaryEngines() >= 1);
  7507. // malloc or realloc prog lang ids array
  7508. if((cEngines = CountPreliminaryEngines()) == 1)
  7509. m_rgProgLangId = (PROGLANG_ID*) CTemplate::SmallMalloc(cEngines * sizeof(PROGLANG_ID));
  7510. else
  7511. m_rgProgLangId = (PROGLANG_ID*) CTemplate::SmallReAlloc(m_rgProgLangId, cEngines * sizeof(PROGLANG_ID));
  7512. if(NULL == m_rgProgLangId)
  7513. {
  7514. hr = E_OUTOFMEMORY;
  7515. goto LExit;
  7516. }
  7517. if(NULL == pProgLangId)
  7518. // caller passed null progid ptr - get prog id from registry
  7519. hr = GetProgLangId(brEngine, &(m_rgProgLangId[cEngines - 1]));
  7520. else
  7521. // caller passed non-null progid ptr - set prog id from it
  7522. m_rgProgLangId[cEngines - 1] = *pProgLangId;
  7523. LExit:
  7524. return hr;
  7525. }
  7526. /* ============================================================================
  7527. CTemplate::CScriptStore::IdEngineFromBr
  7528. Determines the id of a script engine from its engine name
  7529. Returns:
  7530. id of script engine whose name is passed in
  7531. Side effects:
  7532. appends a new script engine name to engine names buffer
  7533. */
  7534. USHORT
  7535. CTemplate::CScriptStore::IdEngineFromBr
  7536. (
  7537. CByteRange& brEngine, // engine name
  7538. UINT idSequence // segment sequence id
  7539. )
  7540. {
  7541. Assert(!brEngine.IsNull()); // NOTE we trap/error null engine name earlier
  7542. USHORT cKnownEngines = CountPreliminaryEngines();
  7543. // search existing names for a match; return id if found
  7544. for(USHORT i = 0; i < cKnownEngines; i++)
  7545. {
  7546. Assert(m_bufEngineNames[i]);
  7547. Assert(m_bufEngineNames[i]->m_pb);
  7548. if(FByteRangesAreEqual(*(m_bufEngineNames[i]), brEngine))
  7549. return i;
  7550. }
  7551. // if not found by name try to find by engine id
  7552. // (some engines with different names share the same id, like J[ava]Script)
  7553. if (cKnownEngines > 0)
  7554. {
  7555. PROGLANG_ID ProgLandId;
  7556. // we will get the prog lang id again inside AppendEngine() but
  7557. // because it's cached and this only happens when > 1 engine, it's alright
  7558. if (SUCCEEDED(GetProgLangId(brEngine, &ProgLandId)))
  7559. {
  7560. for(i = 0; i < cKnownEngines; i++)
  7561. {
  7562. // If matches don't append -- just return the index
  7563. if (m_rgProgLangId[i] == ProgLandId)
  7564. return i;
  7565. }
  7566. }
  7567. }
  7568. /* if we did not find engine among those already buffered
  7569. - append engine to script store
  7570. - realloc segment buffers array if necessary
  7571. - return index of last engine (the one we just appended)
  7572. */
  7573. // append engine to script store
  7574. HRESULT hr = AppendEngine(brEngine, NULL, idSequence);
  7575. if(hr == TYPE_E_ELEMENTNOTFOUND)
  7576. // if prog lang not found, throw bad prog lang error id
  7577. THROW(IDE_TEMPLATE_BAD_PROGLANG);
  7578. else if(FAILED(hr))
  7579. // other failure: re-throw hresult
  7580. THROW(hr);
  7581. // realloc segment buffers array if necessary
  7582. if(CountPreliminaryEngines() > (m_cSegmentBuffers - 1))
  7583. {
  7584. // increment count of segment buffers
  7585. m_cSegmentBuffers++;
  7586. Assert(CountPreliminaryEngines() == m_cSegmentBuffers - 1);
  7587. // realloc array of ptrs
  7588. if(NULL == (m_ppbufSegments = (CBuffer**) CTemplate::SmallReAlloc(m_ppbufSegments, m_cSegmentBuffers * sizeof(CBuffer*))))
  7589. THROW(E_OUTOFMEMORY);
  7590. // allocate the new buffer
  7591. if(NULL == (m_ppbufSegments[m_cSegmentBuffers - 1] = new CBuffer))
  7592. THROW(E_OUTOFMEMORY);
  7593. // init the new buffer
  7594. m_ppbufSegments[m_cSegmentBuffers - 1]->Init(C_SCRIPTSEGMENTSDEFAULT, 0);
  7595. }
  7596. // return index of last engine (the one we just appended)
  7597. return (CountPreliminaryEngines() - 1);
  7598. }
  7599. /* ============================================================================
  7600. CTemplate::CScriptStore::AppendScript
  7601. Appends a script/engine pair to the store.
  7602. Returns:
  7603. Nothing
  7604. Side effects:
  7605. None
  7606. */
  7607. void
  7608. CTemplate::CScriptStore::AppendScript
  7609. (
  7610. CByteRange& brScript, // script text
  7611. CByteRange& brEngine, // script engine name
  7612. BOOLB fPrimary, // primary or tagged script?
  7613. UINT idSequence, // segment sequence id
  7614. CFileMap* pfilemapCurrent
  7615. )
  7616. {
  7617. USHORT iBuffer; // buffer id
  7618. Assert(fPrimary || !brEngine.IsNull()); // NOTE we trap/error null engine name earlier
  7619. Assert(m_bufEngineNames[0]); // page's primary engine must be known by this point
  7620. Assert(m_bufEngineNames[0]->m_pb);
  7621. if(fPrimary)
  7622. // if primary script (not tagged), buffer id is 0
  7623. iBuffer = 0;
  7624. else if((!fPrimary) && FByteRangesAreEqual(brEngine, /* bug 1008: primary script engine name */ *(m_bufEngineNames[0])))
  7625. // if tagged script and engine is primary, buffer id is 1
  7626. iBuffer = 1;
  7627. else
  7628. // else, buffer id is engine id plus 1
  7629. iBuffer = IdEngineFromBr(brEngine, idSequence) + 1;
  7630. // append script segment to iBuffer-th segments buffer
  7631. m_ppbufSegments[iBuffer]->Append(brScript, FALSE, idSequence, pfilemapCurrent);
  7632. }
  7633. /* ****************************************************************************
  7634. CTemplate::CObjectInfoStore member functions
  7635. */
  7636. /* ============================================================================
  7637. CTemplate::CObjectInfoStore::~CObjectInfoStore
  7638. */
  7639. CTemplate::CObjectInfoStore::~CObjectInfoStore
  7640. (
  7641. )
  7642. {
  7643. if(m_pObjectInfos)
  7644. CTemplate::SmallFree(m_pObjectInfos);
  7645. }
  7646. /* ============================================================================
  7647. CTemplate::CObjectInfoStore::Init
  7648. Inits the object-info store
  7649. */
  7650. void
  7651. CTemplate::CObjectInfoStore::Init()
  7652. {
  7653. m_bufObjectNames.Init(C_OBJECTINFOS_DEFAULT, 0);
  7654. // init object-infos array
  7655. if(NULL == (m_pObjectInfos = (CObjectInfo*) CTemplate::SmallMalloc(m_bufObjectNames.CountSlots() * sizeof(CObjectInfo))))
  7656. THROW(E_OUTOFMEMORY);
  7657. }
  7658. /* ============================================================================
  7659. CTemplate::CObjectInfoStore::AppendObject
  7660. Appends an object-info to the object-info store
  7661. */
  7662. void
  7663. CTemplate::CObjectInfoStore::AppendObject
  7664. (
  7665. CByteRange& brObjectName,
  7666. CLSID clsid,
  7667. CompScope scope,
  7668. CompModel model,
  7669. UINT idSequence
  7670. )
  7671. {
  7672. USHORT iObject = m_bufObjectNames.Count();
  7673. if(iObject >= m_bufObjectNames.CountSlots())
  7674. {
  7675. // Reallocate space for storing object-infos - we grab twice what we had before
  7676. // NOTE we keep no object count in CObjectInfoStore, but instead use count in object names buffer
  7677. (m_pObjectInfos = (CObjectInfo*)CTemplate::SmallReAlloc(m_pObjectInfos,
  7678. 2 * m_bufObjectNames.CountSlots() * sizeof(CObjectInfo)));
  7679. if (m_pObjectInfos == NULL)
  7680. THROW(E_OUTOFMEMORY);
  7681. }
  7682. m_pObjectInfos[iObject].m_clsid = clsid;
  7683. m_pObjectInfos[iObject].m_scope = scope;
  7684. m_pObjectInfos[iObject].m_model = model;
  7685. m_bufObjectNames.Append(brObjectName, FALSE, idSequence, NULL);
  7686. }
  7687. /* ****************************************************************************
  7688. CTemplate::CWorkStore member functions
  7689. */
  7690. /* ============================================================================
  7691. CTemplate::CWorkStore::CWorkStore
  7692. Constructor
  7693. Returns:
  7694. Nothing
  7695. Side effects:
  7696. None
  7697. */
  7698. CTemplate::CWorkStore::CWorkStore
  7699. (
  7700. )
  7701. :
  7702. m_idCurSequence(0),
  7703. m_fPageCommandsExecuted(FALSE),
  7704. m_fPageCommandsAllowed(TRUE),
  7705. m_szWriteBlockOpen(g_szWriteBlockOpen),
  7706. m_szWriteBlockClose(g_szWriteBlockClose),
  7707. m_szWriteOpen(g_szWriteOpen),
  7708. m_szWriteClose(g_szWriteClose),
  7709. m_pbPrevSource(NULL),
  7710. m_cPrevSourceLines(0),
  7711. m_hPrevFile (NULL)
  7712. {
  7713. m_cchWriteBlockOpen = strlen (m_szWriteBlockOpen);
  7714. m_cchWriteBlockClose = strlen(m_szWriteBlockClose);
  7715. m_cchWriteOpen = strlen(m_szWriteOpen);
  7716. m_cchWriteClose = strlen(m_szWriteClose);
  7717. }
  7718. /* ============================================================================
  7719. CTemplate::CWorkStore::~CWorkStore
  7720. Destructor
  7721. Returns:
  7722. Nothing
  7723. Side effects:
  7724. None
  7725. */
  7726. CTemplate::CWorkStore::~CWorkStore
  7727. (
  7728. )
  7729. {
  7730. /* if language element ptrs are anything but their constant defaults or null,
  7731. they must have been allocated during compilation - free them now
  7732. */
  7733. if(m_szWriteBlockOpen != g_szWriteBlockOpen && m_szWriteBlockOpen != NULL)
  7734. CTemplate::SmallFree(m_szWriteBlockOpen);
  7735. if(m_szWriteBlockClose != g_szWriteBlockClose && m_szWriteBlockClose != NULL)
  7736. CTemplate::SmallFree(m_szWriteBlockClose);
  7737. if(m_szWriteOpen != g_szWriteOpen && m_szWriteOpen != NULL)
  7738. CTemplate::SmallFree(m_szWriteOpen);
  7739. if(m_szWriteClose != g_szWriteClose && m_szWriteClose != NULL)
  7740. CTemplate::SmallFree(m_szWriteClose);
  7741. }
  7742. /* ============================================================================
  7743. CTemplate::CWorkStore::Init
  7744. Inits the workstore
  7745. Returns:
  7746. Nothing
  7747. Side effects:
  7748. None
  7749. */
  7750. void
  7751. CTemplate::CWorkStore::Init
  7752. (
  7753. )
  7754. {
  7755. /*
  7756. NOTE init we the scriptstore separately from rest of workstore
  7757. because try-catch in CTemplate::Init() apparently doesn't work to detect
  7758. bogus script engine name; we need to get an hr back instead.
  7759. m_ScriptStore.Init(brDefaultEngine);
  7760. */
  7761. m_ObjectInfoStore.Init();
  7762. m_bufHTMLSegments.Init(C_HTMLSEGMENTSDEFAULT, 0);
  7763. }
  7764. /* ============================================================================
  7765. CTemplate::CWorkStore::CRequiredScriptEngines
  7766. Returns the count of script engines in the script store that are required
  7767. to run the template.
  7768. NOTE this function is part of the fix for bug 933
  7769. Returns:
  7770. Count of non-empty script engines
  7771. Side effects:
  7772. None
  7773. */
  7774. USHORT
  7775. CTemplate::CWorkStore::CRequiredScriptEngines
  7776. (
  7777. BOOL fGlobalAsa // bug 1394: is template global.asa?
  7778. )
  7779. {
  7780. USHORT cPreliminaryEngines = m_ScriptStore.CountPreliminaryEngines();
  7781. USHORT cRequiredEngines = 0;
  7782. for(USHORT i = 0; i < cPreliminaryEngines; i++)
  7783. {
  7784. if(FScriptEngineRequired(i, fGlobalAsa))
  7785. cRequiredEngines++;
  7786. }
  7787. return cRequiredEngines;
  7788. }
  7789. /* ============================================================================
  7790. CTemplate::CWorkStore::FScriptEngineRequired
  7791. Is a given preliminary script engine required to run the template?
  7792. NOTE this function is part of the fix for bug 933
  7793. Returns:
  7794. TRUE or FALSE
  7795. Side effects:
  7796. None
  7797. */
  7798. BOOLB
  7799. CTemplate::CWorkStore::FScriptEngineRequired
  7800. (
  7801. USHORT idEnginePrelim,
  7802. BOOL fGlobalAsa // bug 1394: is template global.asa?
  7803. )
  7804. {
  7805. if(idEnginePrelim == 0)
  7806. return ( // primary engine (id 0) required if
  7807. (m_ScriptStore.m_ppbufSegments[0]->Count() > 0) // ... script buffer 0 has segments
  7808. || (m_ScriptStore.m_ppbufSegments[1]->Count() > 0) // ... or script buffer 1 has segments
  7809. || ((m_bufHTMLSegments.Count() > 0) && !fGlobalAsa) // ... or html buffer has segments and (bug 1394) template is not global.asa
  7810. );
  7811. // non-primary engine required if script buffer id+1 has segments
  7812. return (m_ScriptStore.m_ppbufSegments[idEnginePrelim + 1]->Count() > 0);
  7813. }
  7814. /* ****************************************************************************
  7815. CTemplate::CFileMap member functions
  7816. */
  7817. /* ============================================================================
  7818. CTemplate::CFileMap::CFileMap
  7819. Constructor
  7820. Returns
  7821. Nothing
  7822. Side effects
  7823. None
  7824. */
  7825. CTemplate::CFileMap::CFileMap()
  7826. :
  7827. m_szPathInfo(NULL),
  7828. m_szPathTranslated(NULL),
  7829. m_pfilemapSibling(NULL),
  7830. m_pfilemapChild(NULL),
  7831. m_hFile(NULL),
  7832. m_hMap(NULL),
  7833. m_pbStartOfFile(NULL),
  7834. m_pIncFile(NULL),
  7835. m_pSecurityDescriptor(NULL),
  7836. m_dwSecDescSize(0),
  7837. m_fIsUNCPath(FALSE),
  7838. m_fIsEncryptedFile(FALSE),
  7839. m_cChars(0),
  7840. m_pDME(NULL),
  7841. m_dwFileSize(0)
  7842. {
  7843. m_ftLastWriteTime.dwLowDateTime = 0;
  7844. m_ftLastWriteTime.dwHighDateTime = 0;
  7845. }
  7846. /* ============================================================================
  7847. CTemplate::CFileMap::~CFileMap
  7848. Destructor
  7849. Returns
  7850. Nothing
  7851. Side effects
  7852. None
  7853. */
  7854. CTemplate::CFileMap::~CFileMap()
  7855. {
  7856. if (m_pDME)
  7857. {
  7858. m_pDME->Release();
  7859. m_pDME = NULL;
  7860. }
  7861. if(m_szPathInfo != NULL)
  7862. CTemplate::SmallFree(m_szPathInfo);
  7863. if(m_szPathTranslated != NULL)
  7864. CTemplate::SmallFree(m_szPathTranslated);
  7865. if(m_pSecurityDescriptor != NULL)
  7866. CTemplate::SmallFree(m_pSecurityDescriptor);
  7867. if (m_pIncFile != NULL)
  7868. m_pIncFile->Release();
  7869. }
  7870. /* ============================================================================
  7871. CTemplate::CFileMap::MapFile
  7872. Memory-maps a file.
  7873. Returns
  7874. Nothing
  7875. Side effects
  7876. Throws **overloaded** exception on error: exception code can sometimes be
  7877. an error message id, sometimes a true exception. Caller must handle.
  7878. */
  7879. void
  7880. CTemplate::CFileMap::MapFile
  7881. (
  7882. LPCTSTR szFileSpec, // file spec for this file
  7883. LPCTSTR szApplnPath, // application path (in case its global.asa)
  7884. CFileMap* pfilemapParent, // ptr to filemap of parent file
  7885. BOOL fVirtual, // is file spec virtual or relative?
  7886. CHitObj* pHitObj, // ptr to template's hit object
  7887. BOOL fGlobalAsa // is this file the global.asa file?
  7888. )
  7889. {
  7890. BOOL fMustNormalize = TRUE;
  7891. BOOL fImpersonatedUser = FALSE;
  7892. HANDLE hVirtIncImpToken = NULL;
  7893. HANDLE hCurImpToken = NULL;
  7894. Assert((pfilemapParent != NULL) || (pHitObj->PIReq() != NULL) || fGlobalAsa);
  7895. /* three possible cases:
  7896. 1) we are processing global.asa file
  7897. 2) we are processing the "main" .asp file
  7898. 3) we are processing an include file
  7899. */
  7900. if(fGlobalAsa)
  7901. {
  7902. // case 1) we are processing global.asa file
  7903. Assert(pHitObj->GlobalAspPath());
  7904. DWORD cchPathTranslated = _tcslen(pHitObj->GlobalAspPath());
  7905. m_szPathTranslated = (TCHAR *)CTemplate::SmallMalloc((cchPathTranslated+1)*sizeof(TCHAR));
  7906. if (!m_szPathTranslated)
  7907. THROW(E_OUTOFMEMORY);
  7908. _tcscpy(m_szPathTranslated, pHitObj->GlobalAspPath());
  7909. //
  7910. // In case of Global.asa copy ApplnMDPath to pathinfo. This is because when VBScript throws an exception for errors in global.asa to the eventlog.
  7911. // it becomes impossible to determine which global.asa failed. For this reason, we copy the ApplnMDPath to PathInfo so that the nt log will contain
  7912. // a pointer to the locatoin of the faulty global.asa
  7913. //
  7914. DWORD cchPathInfo = _tcslen(pHitObj->PIReq()->QueryPszApplnMDPath()) + 11; // 11 = /global.asa
  7915. m_szPathInfo = (TCHAR *)CTemplate::SmallMalloc((cchPathInfo+1) * sizeof(TCHAR));
  7916. if (!m_szPathInfo)
  7917. THROW(E_OUTOFMEMORY);
  7918. _tcscpy(strcpyEx(m_szPathInfo, pHitObj->PIReq()->QueryPszApplnMDPath()), _T("/global.asa"));
  7919. // no need to normalize in this case, since global.asa path is already normalized
  7920. Assert(IsNormalized((const TCHAR*)m_szPathTranslated));
  7921. fMustNormalize = FALSE;
  7922. m_fHasVirtPath = TRUE;
  7923. }
  7924. else if(pfilemapParent == NULL)
  7925. {
  7926. // case 2) we are processing the "main" .asp file: get path-info and path-tran from ecb
  7927. Assert(pHitObj->PIReq());
  7928. TCHAR *szVirtPath = pHitObj->PSzCurrTemplateVirtPath();
  7929. TCHAR *szPhysPath = pHitObj->PSzCurrTemplatePhysPath();
  7930. m_szPathInfo = static_cast<LPTSTR>(CTemplate::SmallMalloc((_tcslen(szVirtPath) + 1)*sizeof(TCHAR)));
  7931. m_szPathTranslated = static_cast<LPTSTR>(CTemplate::SmallMalloc((_tcslen(szPhysPath) + 1)*sizeof(TCHAR)));
  7932. if (!m_szPathInfo || !m_szPathTranslated)
  7933. THROW(E_OUTOFMEMORY);
  7934. _tcscpy(m_szPathInfo, szVirtPath);
  7935. _tcscpy(m_szPathTranslated, szPhysPath);
  7936. // no need to normalize in this case, since ecb's path-tran is already normalized
  7937. Assert(IsNormalized((const TCHAR*)m_szPathTranslated));
  7938. fMustNormalize = FALSE;
  7939. m_fHasVirtPath = TRUE;
  7940. }
  7941. else
  7942. {
  7943. /* case 3) we are processing an include file: resolve filespec into path-info and path-tran
  7944. based on whether file was included with VIRTUAL tag or FILE tag
  7945. */
  7946. Assert(szFileSpec);
  7947. // in this case, we don't know path lengths up front so we alloc the max and realloc below
  7948. m_szPathInfo = static_cast<LPTSTR> (CTemplate::SmallMalloc((MAX_PATH + 1)*sizeof(TCHAR)));
  7949. m_szPathTranslated = static_cast<LPTSTR> (CTemplate::SmallMalloc((MAX_PATH + 1)*sizeof(TCHAR)));
  7950. if (!m_szPathInfo || !m_szPathTranslated)
  7951. THROW(E_OUTOFMEMORY);
  7952. STACK_BUFFER(tempPathT, (MAX_PATH+1)*sizeof(TCHAR) );
  7953. if (!tempPathT.Resize((_tcslen(szFileSpec) + 1)*sizeof(TCHAR))) {
  7954. THROW(E_OUTOFMEMORY);
  7955. }
  7956. LPTSTR szPathTranslatedT = (TCHAR *)tempPathT.QueryPtr(); // temp path-tran
  7957. if(fVirtual) {
  7958. DWORD dwSzLength = tempPathT.QuerySize(); // length of path string buffer
  7959. if (_tcslen(szFileSpec) > MAX_PATH)
  7960. THROW(E_FAIL);
  7961. // VIRTUAL: path-info is simply virtual filespec
  7962. _tcscpy(m_szPathInfo, szFileSpec);
  7963. // VIRTUAL: path-tran is translation of path-info
  7964. _tcscpy(szPathTranslatedT, m_szPathInfo);
  7965. if (!pHitObj->PIReq()->MapUrlToPath(szPathTranslatedT, &dwSzLength))
  7966. THROW(E_FAIL);
  7967. // Check the translated path for a UNC specified path . Ignore Hr
  7968. HRESULT hr = S_OK;
  7969. if (IsFileUNC (szPathTranslatedT, hr))
  7970. {
  7971. // if UNC, then ask WAM for the impersonation token for
  7972. // this UNC VRoot. Silently fail.
  7973. if (SUCCEEDED(pHitObj->PIReq()->GetVirtualPathToken(szFileSpec,
  7974. &hVirtIncImpToken))) {
  7975. // set the impersonation token and note that we did so
  7976. // NOTE - there is intentionally no error checking. The
  7977. // assumption being that we are doing best effort at the
  7978. // impersonation because throwing an error here could be
  7979. // tricky for the user to interpret the problem. However,
  7980. // if the impersonation fails, and ASP can still open the
  7981. // file (e.g. passthru authentication), then everyone's
  7982. // happy.
  7983. AspDoRevertHack(&hCurImpToken);
  7984. fImpersonatedUser = ImpersonateLoggedOnUser(hVirtIncImpToken);
  7985. if (!fImpersonatedUser)
  7986. {
  7987. AspUndoRevertHack(&hCurImpToken);
  7988. }
  7989. }
  7990. }
  7991. m_fHasVirtPath = TRUE;
  7992. }
  7993. else
  7994. {
  7995. if (_tcslen(pfilemapParent->m_szPathInfo) >= MAX_PATH)
  7996. THROW(E_FAIL);
  7997. TCHAR szParentDir[MAX_PATH], *szT;
  7998. _tcscpy(szParentDir, pfilemapParent->m_szPathInfo);
  7999. //force null termination.
  8000. szParentDir[MAX_PATH-1] = _T('\0');
  8001. DWORD strlen_szParentDir = _tcslen(szParentDir);
  8002. if ((szT = _tcsrchr(szParentDir, _T('/'))) != NULL)
  8003. {
  8004. *szT = _T('\0');
  8005. strlen_szParentDir = (int)(szT - szParentDir);
  8006. }
  8007. // If we don't allow parent paths, we can save lots of time (Always have a valid virtual path)
  8008. if (!pHitObj->QueryAppConfig()->fEnableParentPaths())
  8009. {
  8010. //int strlen_szParentDir = (int)(szT - szParentDir);
  8011. if ((strlen_szParentDir + 1 + _tcslen(szFileSpec)) >= MAX_PATH)
  8012. THROW(E_FAIL);
  8013. strcpyEx(strcpyEx(strcpyEx(m_szPathInfo, szParentDir), _T("/")), szFileSpec);
  8014. m_fHasVirtPath = TRUE;
  8015. }
  8016. else
  8017. {
  8018. // NOTE: If we must translate ".." paths, there is no need to verify them (by remapping)
  8019. // because: If the file does not exist, that case will show up when the file is mapped
  8020. // If we ".." ourselves out of the vroot space, (out of the app or into another app)
  8021. // DotPathToPath will detect this.
  8022. //
  8023. if (DotPathToPath(m_szPathInfo, szFileSpec, szParentDir))
  8024. m_fHasVirtPath = TRUE;
  8025. else
  8026. {
  8027. GetPathFromParentAndFilespec(pfilemapParent->m_szPathTranslated, szFileSpec, &m_szPathInfo);
  8028. m_fHasVirtPath = FALSE;
  8029. }
  8030. }
  8031. GetPathFromParentAndFilespec(pfilemapParent->m_szPathTranslated, szFileSpec, &szPathTranslatedT);
  8032. }
  8033. // bug 1214: get canonical path-tran, without . and ..
  8034. // CONSIDER check for . or .. in name before calling GetFullPathName? UNCs? what else?
  8035. GetFullPathName(
  8036. szPathTranslatedT, // LPCSTR lpFileName, // address of name of file to find path for
  8037. MAX_PATH + 1, // DWORD nBufferLength, // size, in characters, of path buffer
  8038. m_szPathTranslated, // LPSTR lpBuffer, // address of path buffer
  8039. NULL // LPSTR *lpFilePart // address of filename in path
  8040. );
  8041. // realloc path strings to only use required memory (see note above)
  8042. m_szPathInfo = static_cast<LPTSTR> (CTemplate::SmallReAlloc(m_szPathInfo, (_tcslen(m_szPathInfo) + 1)*sizeof(TCHAR)));
  8043. m_szPathTranslated = static_cast<LPTSTR> (CTemplate::SmallReAlloc(m_szPathTranslated, (_tcslen(m_szPathTranslated) + 1)*sizeof(TCHAR)));
  8044. if (!m_szPathInfo || !m_szPathTranslated) {
  8045. if (fImpersonatedUser)
  8046. AspUndoRevertHack(&hCurImpToken);
  8047. if (hVirtIncImpToken)
  8048. CloseHandle(hVirtIncImpToken);
  8049. THROW(E_OUTOFMEMORY);
  8050. }
  8051. }
  8052. // if required, normalize path-tran so that
  8053. // a) cyclic include check can ignore case; b) inc-file cache lookups will work
  8054. if(fMustNormalize)
  8055. Normalize(m_szPathTranslated);
  8056. Assert(IsNormalized(m_szPathTranslated));
  8057. // Bug 99071: Attempt to open the file **BEFORE** we add it to the tree of file
  8058. // dependencies. Otherwise if it fails to open, we will have
  8059. // dangling references. Since FCyclicInclude depends on us adding
  8060. // to the tree, if it is cyclic, we need to unmap then. Since that
  8061. // is a very uncommon error case, the extra overhead is probably OK
  8062. //
  8063. // RemapFile will throw if it fails. If the exception is that the source file is empty
  8064. // and we are trying to process an include file, we will handle the exception here.
  8065. // in all other cases, rethrow the exception. We do this so that an empty include file
  8066. // will be harmless, but an empty primary file will fail.
  8067. TRY
  8068. RemapFile();
  8069. CATCH(hrException)
  8070. if (hrException != E_SOURCE_FILE_IS_EMPTY || pfilemapParent == NULL) {
  8071. if (fImpersonatedUser)
  8072. AspUndoRevertHack(&hCurImpToken);
  8073. if (hVirtIncImpToken)
  8074. CloseHandle(hVirtIncImpToken);
  8075. THROW(hrException);
  8076. }
  8077. END_TRY
  8078. if (fImpersonatedUser)
  8079. AspUndoRevertHack(&hCurImpToken);
  8080. if (hVirtIncImpToken)
  8081. CloseHandle(hVirtIncImpToken);
  8082. // Create the tree structure for this file
  8083. if (pfilemapParent != NULL)
  8084. {
  8085. // See if this file is already included once on this level. (Don't show duplicates in the
  8086. // debugger tree view)
  8087. //
  8088. BOOL fDuplicateExists = FALSE;
  8089. CFileMap *pFilemap = pfilemapParent->m_pfilemapChild;
  8090. while (pFilemap != NULL && !fDuplicateExists)
  8091. {
  8092. if (_tcscmp(pFilemap->m_szPathTranslated, m_szPathTranslated) == 0)
  8093. fDuplicateExists = TRUE;
  8094. pFilemap = pFilemap->m_fHasSibling? pFilemap->m_pfilemapSibling : NULL;
  8095. }
  8096. // If the include file is #include'd more than once, don't add it as a sibling.
  8097. // Rather orphan the pfilemap and just set the parent pointer.
  8098. //
  8099. if (!fDuplicateExists)
  8100. {
  8101. if (pfilemapParent->m_pfilemapChild == NULL)
  8102. pfilemapParent->m_pfilemapChild = this;
  8103. else
  8104. pfilemapParent->m_pfilemapChild->AddSibling(this);
  8105. }
  8106. }
  8107. // in both of the above code paths, we are always added as the LAST child, (or we are an orphan node)
  8108. // so it is safe to set the parent without calling SetParent()
  8109. m_fHasSibling = FALSE; // Paranoia
  8110. m_pfilemapParent = pfilemapParent;
  8111. // hurl if this file is being included by itself (perhaps indirectly)
  8112. if(FCyclicInclude(m_szPathTranslated)) {
  8113. UnmapFile();
  8114. THROW(IDE_TEMPLATE_CYCLIC_INCLUDE);
  8115. }
  8116. }
  8117. /* ============================================================================
  8118. CTemplate::CFileMap::RemapFile
  8119. map a file that was previously mapped.
  8120. Returns
  8121. Nothing
  8122. Side effects
  8123. Throws **overloaded** exception on error: exception code can sometimes be
  8124. an error message id, sometimes a true exception. Caller must handle.
  8125. Does not decrypt EASPs on remapping. Caller must decrypt if required. This
  8126. function is called by the debugger, and the debugger does not allow access
  8127. to decrypted files, so decryption is a waste of time.
  8128. */
  8129. void
  8130. CTemplate::CFileMap::RemapFile
  8131. (
  8132. )
  8133. {
  8134. HRESULT hr = S_OK;
  8135. if (FIsMapped())
  8136. return;
  8137. // Limit the size of the file that can be opened by ASP to MAX_PATH until we remove all assumptions ASP
  8138. // makes about MAX_PATH
  8139. if (_tcslen(m_szPathTranslated) >= MAX_PATH)
  8140. THROW (E_COULDNT_OPEN_SOURCE_FILE);
  8141. //
  8142. // Check if the File is on a UNC.
  8143. // IGNORE the HRESULT value as whatever it was will/should also show up in the call to ASPCreateFile (MakePathCanonicalizationProof)
  8144. //
  8145. m_fIsUNCPath = IsFileUNC(m_szPathTranslated, hr);
  8146. if(INVALID_HANDLE_VALUE == (m_hFile =
  8147. AspCreateFile(
  8148. m_szPathTranslated, // file name
  8149. GENERIC_READ, // access (read-write) mode
  8150. FILE_SHARE_READ, // share mode
  8151. NULL, // pointer to security descriptor
  8152. OPEN_EXISTING, // how to create
  8153. FILE_ATTRIBUTE_NORMAL, // file attributes
  8154. NULL // handle to file with attributes to copy
  8155. )))
  8156. {
  8157. DWORD dwLastError = GetLastError();
  8158. if(dwLastError == ERROR_ACCESS_DENIED)
  8159. {
  8160. // typically, we end up here if the user has no permissions on the file
  8161. // bug 1007: however, we also end up here if the user gave us a directory name, instead of a file name
  8162. WIN32_FILE_ATTRIBUTE_DATA fad;
  8163. if(GetFileAttributesEx(m_szPathTranslated, GetFileExInfoStandard, &fad) == 0)
  8164. {
  8165. // bug 1495: file in a secured directory will end up here - we need to re-GetLastError to see if access is denied
  8166. dwLastError = GetLastError();
  8167. if(dwLastError == ERROR_ACCESS_DENIED)
  8168. {
  8169. THROW(E_USER_LACKS_PERMISSIONS);
  8170. }
  8171. // GetFileAttributes call failed; don't know why
  8172. THROW(E_FAIL);
  8173. }
  8174. else if(FILE_ATTRIBUTE_DIRECTORY & fad.dwFileAttributes)
  8175. {
  8176. // bug 1007: the user gave us a directory name
  8177. #if UNICODE
  8178. DBGPRINTF((DBG_CONTEXT, "Failed to open file %S because it is a directory.\n", m_szPathTranslated));
  8179. #else
  8180. DBGPRINTF((DBG_CONTEXT, "Failed to open file %s because it is a directory.\n", m_szPathTranslated));
  8181. #endif
  8182. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8183. }
  8184. else
  8185. {
  8186. THROW(E_USER_LACKS_PERMISSIONS);
  8187. }
  8188. }
  8189. else
  8190. {
  8191. #if DBG
  8192. char szError[128];
  8193. if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
  8194. NULL,
  8195. dwLastError,
  8196. 0L, // lang ID - defaults to LANG_NEUTRAL
  8197. szError,
  8198. sizeof szError,
  8199. NULL) )
  8200. {
  8201. sprintf(szError, "%d", dwLastError);
  8202. }
  8203. #if UNICODE
  8204. DBGPRINTF((DBG_CONTEXT, "Failed to open file %S\n", m_szPathTranslated));
  8205. #else
  8206. DBGPRINTF((DBG_CONTEXT, "Failed to open file %s\n", m_szPathTranslated));
  8207. #endif
  8208. DBGPRINTF((DBG_CONTEXT, " The error returned was: %s\n", szError));
  8209. #endif
  8210. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8211. }
  8212. }
  8213. //
  8214. // Get LastWriteTime, FileSize and its Attributes
  8215. //
  8216. DWORD dwFileAttributes;
  8217. if (FAILED(AspGetFileAttributes(m_szPathTranslated, m_hFile, &m_ftLastWriteTime, &m_dwFileSize, &dwFileAttributes)))
  8218. {
  8219. DBGPRINTF((DBG_CONTEXT,"ASP could not retrieve file attributes even though it could open the file.\n"));
  8220. // THROW Server 500 Error
  8221. // Quick and dirty way: THROW E_FAIL
  8222. // Correct way: Call Handle500Error/HandleError with all parameters and corresponding string.
  8223. THROW (E_FAIL);
  8224. }
  8225. //
  8226. // Does the File have the Encrypted attribute bit set
  8227. //
  8228. m_fIsEncryptedFile = (dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED ) ? TRUE : FALSE;
  8229. // get file's security descriptor (only if not UNC or encrypted)
  8230. if (!m_fIsUNCPath)
  8231. {
  8232. if (!GetSecurityDescriptor())
  8233. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8234. }
  8235. // map the file
  8236. if(NULL == (m_hMap =
  8237. CreateFileMapping(
  8238. m_hFile, // handle to file to map
  8239. NULL, // optional security attributes
  8240. PAGE_READONLY, // protection for mapping object
  8241. 0, // high-order 32 bits of object size
  8242. 0, // low-order 32 bits of object size
  8243. NULL // name of file-mapping object
  8244. )))
  8245. {
  8246. DWORD nFileSize;
  8247. if (SUCCEEDED(AspGetFileAttributes(m_szPathTranslated, m_hFile, NULL, &nFileSize)))
  8248. {
  8249. if(nFileSize == 0)
  8250. {
  8251. #if UNICODE
  8252. DBGPRINTF((DBG_CONTEXT, "Empty source file %S\n", m_szPathTranslated));
  8253. #else
  8254. DBGPRINTF((DBG_CONTEXT, "Empty source file %s\n", m_szPathTranslated));
  8255. #endif
  8256. THROW(E_SOURCE_FILE_IS_EMPTY);
  8257. }
  8258. }
  8259. else
  8260. {
  8261. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8262. }
  8263. }
  8264. // set file's start-of-file ptr
  8265. if(NULL == (m_pbStartOfFile =
  8266. (PBYTE) MapViewOfFile(
  8267. m_hMap, // file-mapping object to map into address space
  8268. FILE_MAP_READ, // access mode
  8269. 0, // high-order 32 bits of file offset
  8270. 0, // low-order 32 bits of file offset
  8271. 0 // number of bytes to map
  8272. )))
  8273. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8274. }
  8275. /* ============================================================================
  8276. CTemplate::CFileMap::SetParent
  8277. Set the parent for this filemap
  8278. */
  8279. void
  8280. CTemplate::CFileMap::SetParent
  8281. (
  8282. CFileMap* pfilemapParent
  8283. )
  8284. {
  8285. CFileMap *pfilemap = this;
  8286. while (pfilemap->m_fHasSibling)
  8287. pfilemap = pfilemap->m_pfilemapSibling;
  8288. pfilemap->m_pfilemapParent = pfilemapParent;
  8289. }
  8290. /* ============================================================================
  8291. CTemplate::CFileMap::GetParent
  8292. Get the parent for this filemap
  8293. */
  8294. CTemplate::CFileMap*
  8295. CTemplate::CFileMap::GetParent
  8296. (
  8297. )
  8298. {
  8299. register CFileMap *pfilemap = this;
  8300. while (pfilemap->m_fHasSibling)
  8301. pfilemap = pfilemap->m_pfilemapSibling;
  8302. return pfilemap->m_pfilemapParent;
  8303. }
  8304. /* ============================================================================
  8305. CTemplate::CFileMap::AddSibling
  8306. Add a new node as a sibling of this
  8307. */
  8308. void
  8309. CTemplate::CFileMap::AddSibling
  8310. (
  8311. register CFileMap* pfilemapSibling
  8312. )
  8313. {
  8314. register CFileMap *pfilemap = this;
  8315. while (pfilemap->m_fHasSibling)
  8316. pfilemap = pfilemap->m_pfilemapSibling;
  8317. pfilemapSibling->m_fHasSibling = FALSE;
  8318. pfilemapSibling->m_pfilemapParent = pfilemap->m_pfilemapParent;
  8319. pfilemap->m_fHasSibling = TRUE;
  8320. pfilemap->m_pfilemapSibling = pfilemapSibling;
  8321. }
  8322. /* ============================================================================
  8323. CTemplate::CFileMap::FCyclicInclude
  8324. Is a file among this filemap's ancestors? (i.e. does it occur anywhere
  8325. in the filemap's parent chain?)
  8326. Returns
  8327. TRUE or FALSE
  8328. Side effects
  8329. None
  8330. */
  8331. BOOL
  8332. CTemplate::CFileMap::FCyclicInclude
  8333. (
  8334. LPCTSTR szPathTranslated
  8335. )
  8336. {
  8337. CFileMap *pfilemapParent = GetParent();
  8338. if(pfilemapParent == NULL)
  8339. return FALSE;
  8340. // NOTE we ignore case because path-tran was normalized
  8341. if(_tcscmp(szPathTranslated, pfilemapParent->m_szPathTranslated) == 0)
  8342. return TRUE;
  8343. return pfilemapParent->FCyclicInclude(szPathTranslated);
  8344. }
  8345. /* ============================================================================
  8346. CTemplate::CFileMap::GetSecurityDescriptor
  8347. Gets a file's security descriptor
  8348. Returns
  8349. TRUE if we got security descriptor, else FALSE
  8350. Side effects
  8351. allocates memory
  8352. */
  8353. BOOL
  8354. CTemplate::CFileMap::GetSecurityDescriptor
  8355. (
  8356. )
  8357. // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project)
  8358. {
  8359. BOOL fRet = TRUE; // return value
  8360. BOOL fGotSecurityDescriptor; // did we get a security descriptor?
  8361. DWORD dwSecDescSizeNeeded = 0; // required size of security descriptor
  8362. DWORD dwLastError; // last error code
  8363. const SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION // security info struct
  8364. | GROUP_SECURITY_INFORMATION
  8365. | DACL_SECURITY_INFORMATION;
  8366. // get required buffer size before malloc
  8367. // NOTE this costs us an extra system call, but means the cached template will often use less memory
  8368. // we must do this up front by passing 0 buffer size because when the call succeeds it returns
  8369. // dwSecDescSizeNeeded == 0 (i.e. we can't realloc to shrink after successful call)
  8370. GetKernelObjectSecurity(
  8371. m_hFile, // handle of object to query
  8372. si, // requested information
  8373. NULL, // address of security descriptor
  8374. 0, // size of buffer for security descriptor
  8375. &dwSecDescSizeNeeded // address of required size of buffer
  8376. );
  8377. if((dwLastError = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
  8378. {
  8379. // pretend everything's fine -- just NULL security descriptor
  8380. if(m_pSecurityDescriptor != NULL)
  8381. CTemplate::SmallFree(m_pSecurityDescriptor);
  8382. m_pSecurityDescriptor = NULL;
  8383. m_dwSecDescSize = 0;
  8384. if (dwLastError == ERROR_NOT_SUPPORTED)
  8385. return TRUE;
  8386. else
  8387. return FALSE;
  8388. }
  8389. // set member buffer size to just enough chunks to accommodate security descriptor size needed
  8390. m_dwSecDescSize = ((dwSecDescSizeNeeded + SECURITY_DESC_GRANULARITY - 1) / SECURITY_DESC_GRANULARITY)
  8391. * SECURITY_DESC_GRANULARITY;
  8392. // allocate memory for security descriptor
  8393. // (Note: security descriptor may already be allocated if this is a remap)
  8394. if (m_pSecurityDescriptor == NULL)
  8395. if(NULL == (m_pSecurityDescriptor = (PSECURITY_DESCRIPTOR) CTemplate::SmallMalloc(m_dwSecDescSize)))
  8396. THROW(E_OUTOFMEMORY);
  8397. // try to get security descriptor until we succeed, or until we fail for some reason other than buffer-too-small
  8398. while(TRUE)
  8399. {
  8400. // attempt to get security descriptor
  8401. fGotSecurityDescriptor = GetKernelObjectSecurity(
  8402. m_hFile, // handle of object to query
  8403. si, // requested information
  8404. m_pSecurityDescriptor, // address of security descriptor
  8405. m_dwSecDescSize, // size of buffer for security descriptor
  8406. &dwSecDescSizeNeeded // address of required size of buffer
  8407. );
  8408. // get last error immediately after call
  8409. dwLastError = fGotSecurityDescriptor
  8410. ? 0 // we got a security descriptor: set last error to 0
  8411. : GetLastError(); // we didn't get a security descriptor: get last error
  8412. if(fGotSecurityDescriptor)
  8413. // we got a security descriptor, so break
  8414. // NOTE we can't realloc m_pSecurityDescriptor to free its unused memory
  8415. // because dwSecDescSizeNeeded is 0 after successful call
  8416. break;
  8417. else if(dwLastError == ERROR_INSUFFICIENT_BUFFER)
  8418. {
  8419. // we didn't get a security descriptor because buffer was too small: increase buffer size, realloc and continue.
  8420. Assert(m_dwSecDescSize < dwSecDescSizeNeeded);
  8421. // set member buffer size to just enough chunks to accommodate security descriptor size needed
  8422. m_dwSecDescSize = ((dwSecDescSizeNeeded + SECURITY_DESC_GRANULARITY - 1) / SECURITY_DESC_GRANULARITY)
  8423. * SECURITY_DESC_GRANULARITY;
  8424. if(NULL == (m_pSecurityDescriptor = (PSECURITY_DESCRIPTOR) CTemplate::SmallReAlloc(m_pSecurityDescriptor, m_dwSecDescSize)))
  8425. THROW(E_OUTOFMEMORY);
  8426. }
  8427. else
  8428. {
  8429. // we didn't get a security descriptor for some random reason: return failure
  8430. fRet = FALSE;
  8431. break;
  8432. }
  8433. }
  8434. return fRet;
  8435. }
  8436. /* ============================================================================
  8437. CTemplate::CFileMap::UnmapFile
  8438. Unmaps a memory-mapped file
  8439. NOTE this leaves the filemap's path-info and path-tran members intact
  8440. Returns
  8441. Nothing
  8442. Side effects
  8443. None
  8444. */
  8445. void
  8446. CTemplate::CFileMap::UnmapFile
  8447. (
  8448. )
  8449. {
  8450. if(m_pbStartOfFile != NULL)
  8451. if(!UnmapViewOfFile(m_pbStartOfFile)) THROW(E_FAIL);
  8452. if(m_hMap!= NULL)
  8453. if(!CloseHandle(m_hMap)) THROW(E_FAIL);
  8454. if(m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
  8455. if(!CloseHandle(m_hFile)) THROW(E_FAIL);
  8456. // Null-ify ptr and handles, since MapFile checks for non-null
  8457. m_pbStartOfFile = NULL;
  8458. m_hMap = NULL;
  8459. m_hFile = NULL;
  8460. }
  8461. /* ============================================================================
  8462. CTemplate::CFileMap::CountChars
  8463. Count the number of characters in the (open) filemap
  8464. Returns:
  8465. # of characters in the file
  8466. */
  8467. DWORD
  8468. CTemplate::CFileMap::CountChars
  8469. (
  8470. WORD wCodePage
  8471. )
  8472. {
  8473. // Bug 84284: Scripts containing object tags only do not have the DBCS table built
  8474. // (Because there is no line mapping table to base it from)
  8475. //
  8476. CTemplate::COffsetInfo *pOffsetInfoLast, oiZero;
  8477. pOffsetInfoLast = (m_rgByte2DBCS.length() == 0)
  8478. ? &oiZero
  8479. : &m_rgByte2DBCS[m_rgByte2DBCS.length() - 1];
  8480. // If GetSize() fails don't count the remaining DBCS characters - otherwise an AV
  8481. DWORD cchFilemap = GetSize();
  8482. if (cchFilemap != 0xFFFFFFFF && cchFilemap != 0)
  8483. {
  8484. // Count DBCS characters
  8485. m_cChars = pOffsetInfoLast->m_cchOffset +
  8486. CharAdvDBCS(wCodePage,
  8487. reinterpret_cast<char *>(m_pbStartOfFile + pOffsetInfoLast->m_cbOffset),
  8488. reinterpret_cast<char *>(m_pbStartOfFile + cchFilemap),
  8489. INFINITE, NULL);
  8490. }
  8491. else
  8492. {
  8493. m_cChars = 0;
  8494. }
  8495. // Done counting DBCS characters
  8496. return m_cChars;
  8497. }
  8498. /* ============================================================================
  8499. CTemplate::CFileMap::GetText
  8500. From a character offset and length, return the document text
  8501. File must be mapped
  8502. */
  8503. HRESULT CTemplate::CFileMap::GetText
  8504. (
  8505. WORD wCodePage,
  8506. ULONG cchSourceOffset,
  8507. WCHAR *pwchText,
  8508. SOURCE_TEXT_ATTR *pTextAttr,
  8509. ULONG *pcChars,
  8510. ULONG cMaxChars
  8511. )
  8512. {
  8513. ULONG cCharsCopied;
  8514. if (pcChars == NULL)
  8515. pcChars = &cCharsCopied;
  8516. // Map the file (temporarily) if not mapped
  8517. BOOL fRemapFile = !FIsMapped();
  8518. TRY
  8519. RemapFile();
  8520. CATCH (dwException)
  8521. return E_FAIL;
  8522. END_TRY
  8523. /* Find the byte offset closest to cchSourceOffset. This will be
  8524. * the place where we start looping with CharNext() to get the full
  8525. * byte offset.
  8526. */
  8527. COffsetInfo *pOffsetInfoLE = NULL, OffsetInfoT;
  8528. /*
  8529. * NOTE: compilation is done in two phases.
  8530. * Errors are detected and reported in phase 1.
  8531. * The DBCS mapping is created in phase 2.
  8532. *
  8533. * If an error occurred during compilation, the DBCS table does not exist.
  8534. * If there is no DBCS mapping table, then pretend like we found entry with
  8535. * nearest offset == 0. (unless this is SBCS in which case nearest
  8536. * offset == cchSourceOffset)
  8537. */
  8538. if (m_rgByte2DBCS.length() == 0)
  8539. {
  8540. CPINFO CpInfo;
  8541. GetCPInfo(wCodePage, &CpInfo);
  8542. OffsetInfoT.m_cbOffset = OffsetInfoT.m_cchOffset = (CpInfo.MaxCharSize == 1)? cchSourceOffset : 0;
  8543. pOffsetInfoLE = &OffsetInfoT;
  8544. }
  8545. else
  8546. GetBracketingPair(
  8547. cchSourceOffset, // value to search for
  8548. m_rgByte2DBCS.begin(), // beginning of array to search
  8549. m_rgByte2DBCS.end(), // end of array
  8550. CCharOffsetOrder(), // order by character offsets
  8551. &pOffsetInfoLE, // desired offset
  8552. static_cast<COffsetInfo **>(NULL) // don't care
  8553. );
  8554. /* OK - pOffsetLE->cbOffset contains the closest offset not exceeding
  8555. * cchSourceOffset. Iterate over the remainder of the characters
  8556. * to convert the cch to a cb. It had better exist!
  8557. */
  8558. Assert (pOffsetInfoLE != NULL);
  8559. // Advance over remaining characters
  8560. char *pchStart;
  8561. CharAdvDBCS(wCodePage,
  8562. reinterpret_cast<char *>(m_pbStartOfFile + pOffsetInfoLE->m_cbOffset),
  8563. reinterpret_cast<char *>(m_pbStartOfFile + GetSize()),
  8564. cchSourceOffset - pOffsetInfoLE->m_cchOffset,
  8565. &pchStart
  8566. );
  8567. // Compute # of Characters to copy
  8568. Assert (m_cChars >= cchSourceOffset);
  8569. *pcChars = min(cMaxChars, m_cChars - cchSourceOffset);
  8570. // Compute # of Bytes to copy
  8571. char *pchEnd;
  8572. CharAdvDBCS(wCodePage,
  8573. pchStart,
  8574. reinterpret_cast<char *>(m_pbStartOfFile + GetSize()),
  8575. *pcChars,
  8576. &pchEnd
  8577. );
  8578. if (pwchText)
  8579. MultiByteToWideChar(
  8580. (WORD)wCodePage,
  8581. 0,
  8582. pchStart,
  8583. DIFF(pchEnd - pchStart),
  8584. pwchText,
  8585. cMaxChars
  8586. );
  8587. // We don't support syntax coloring, so set all the character attributes to
  8588. // default color (black)
  8589. if (pTextAttr)
  8590. memset(pTextAttr, 0, *pcChars);
  8591. // Unmap the file (but only if we previously remapped it)
  8592. if (fRemapFile)
  8593. TRY
  8594. UnmapFile();
  8595. CATCH (dwException)
  8596. return E_FAIL;
  8597. END_TRY
  8598. return S_OK;
  8599. }
  8600. /* ****************************************************************************
  8601. CTemplate::CTokenList member functions
  8602. */
  8603. /* ============================================================================
  8604. CTemplate::CTokenList::Init
  8605. Populates token list with tokens
  8606. Returns
  8607. Nothing
  8608. Side effects
  8609. None
  8610. */
  8611. void
  8612. CTemplate::CTokenList::Init
  8613. (
  8614. )
  8615. {
  8616. // Init tokens buffer for local storage
  8617. m_bufTokens.Init(tkncAll, CB_TOKENS_DEFAULT);
  8618. // append tokens to buffer
  8619. // NOTE *** TOKENS MUST BE IN SAME ORDER AS ENUM TYPE VALUES ***
  8620. // NOTE 'superset' token must precede 'subset' token (e.g. <!--#INCLUDE before <!--)
  8621. AppendToken(tknOpenPrimaryScript, "<%");
  8622. AppendToken(tknOpenTaggedScript, "<SCRIPT");
  8623. AppendToken(tknOpenObject, "<OBJECT");
  8624. AppendToken(tknOpenHTMLComment, "<!--");
  8625. AppendToken(tknNewLine, SZ_NEWLINE);
  8626. AppendToken(tknClosePrimaryScript, "%>");
  8627. AppendToken(tknCloseTaggedScript, "</SCRIPT>");
  8628. AppendToken(tknCloseObject, "</OBJECT>");
  8629. AppendToken(tknCloseHTMLComment, "-->");
  8630. AppendToken(tknEscapedClosePrimaryScript, "%\\>");
  8631. AppendToken(tknCloseTag, ">");
  8632. AppendToken(tknCommandINCLUDE, "#INCLUDE");
  8633. AppendToken(tknTagRunat, "RUNAT");
  8634. AppendToken(tknTagLanguage, "LANGUAGE");
  8635. AppendToken(tknTagCodePage, "CODEPAGE");
  8636. AppendToken(tknTagCodePage, "LCID");
  8637. AppendToken(tknTagTransacted, "TRANSACTION");
  8638. AppendToken(tknTagSession, "ENABLESESSIONSTATE");
  8639. AppendToken(tknTagID, "ID");
  8640. AppendToken(tknTagClassID, "CLASSID");
  8641. AppendToken(tknTagProgID, "PROGID");
  8642. AppendToken(tknTagScope, "SCOPE");
  8643. AppendToken(tknTagVirtual, "VIRTUAL");
  8644. AppendToken(tknTagFile, "FILE");
  8645. AppendToken(tknTagMETADATA, "METADATA");
  8646. // AppendToken(tknTagSetPriScriptLang, "@");
  8647. AppendToken(tknTagName, "NAME");
  8648. AppendToken(tknValueTypeLib, "TYPELIB");
  8649. AppendToken(tknTagType, "TYPE");
  8650. AppendToken(tknTagUUID, "UUID");
  8651. AppendToken(tknTagVersion, "VERSION");
  8652. AppendToken(tknTagStartspan, "STARTSPAN");
  8653. AppendToken(tknTagEndspan, "ENDSPAN");
  8654. AppendToken(tknValueCookie, "COOKIE");
  8655. AppendToken(tknTagSrc, "SRC");
  8656. AppendToken(tknValueServer, "Server");
  8657. AppendToken(tknValueApplication, "Application");
  8658. AppendToken(tknValueSession, "Session");
  8659. AppendToken(tknValuePage, "Page");
  8660. AppendToken(tknVBSCommentSQuote, "'");
  8661. AppendToken(tknVBSCommentRem, "REM "); // NOTE ends with space character
  8662. AppendToken(tknTagFPBot, "webbot");
  8663. AppendToken(tknEOF, "");
  8664. AppendToken(tkncAll, "");
  8665. }
  8666. /* ============================================================================
  8667. CTemplate::CTokenList::AppendToken
  8668. Appends a string to tokens buffer
  8669. NOTE we keep the unused tkn parameter because it enforces consistency and
  8670. readability in CTemplate::CTokenList::Init(), e.g.
  8671. AppendToken(tknOpenPrimaryScript, "<%");
  8672. rather than
  8673. AppendToken("<%");
  8674. Returns:
  8675. Nothing
  8676. Side effects:
  8677. None
  8678. */
  8679. void
  8680. CTemplate::CTokenList::AppendToken
  8681. (
  8682. _TOKEN tkn, // token value
  8683. char* sz // token string
  8684. )
  8685. {
  8686. // construct byte range from token string
  8687. CByteRange br;
  8688. br.m_pb = (BYTE*) sz;
  8689. br.m_cb = strlen(sz);
  8690. // append to tokens buffer as local string
  8691. m_bufTokens.Append(br, TRUE, 0, NULL, TRUE);
  8692. }
  8693. /* ============================================================================
  8694. CTemplate::CTokenList::NextOpenToken
  8695. Returns value of next open token in search range
  8696. Returns
  8697. token value of next open token in search range; ptr to ptr to open token (out-parameter)
  8698. Side effects
  8699. None
  8700. */
  8701. _TOKEN
  8702. CTemplate::CTokenList::NextOpenToken
  8703. (
  8704. CByteRange& brSearch, // search byte range
  8705. TOKEN* rgtknOpeners, // array of permitted open tokens
  8706. UINT ctknOpeners, // count of permitted open tokens
  8707. BYTE** ppbToken, // ptr to ptr to open token (out-parameter)
  8708. LONG lCodePage
  8709. )
  8710. {
  8711. BYTE* pbTemp = NULL; // temp pointer
  8712. _TOKEN tkn = tknEOF; // return value
  8713. USHORT i; // loop index
  8714. // Init caller's token ptr to null
  8715. *ppbToken = NULL;
  8716. // If input is empty, return
  8717. if (brSearch.IsNull())
  8718. return tkn;
  8719. // Prepare array of LPSTR pointers to tokens.
  8720. // Do it here once, because to get LPSTR is not free.
  8721. LPSTR rgszTokens[TOKEN_OPENERS_MAX];
  8722. UINT rgcchTokens[TOKEN_OPENERS_MAX];
  8723. Assert(ctknOpeners <= TOKEN_OPENERS_MAX);
  8724. for (i = 0; i < ctknOpeners; i++)
  8725. {
  8726. LPSTR pszStr = m_bufTokens.PszLocal((UINT)(rgtknOpeners[i]));
  8727. rgszTokens[i] = pszStr;
  8728. rgcchTokens[i] = (pszStr != NULL) ? strlen(pszStr) : 0;
  8729. }
  8730. // Call a method to find one of the strings in the range
  8731. UINT idToken;
  8732. pbTemp = brSearch.PbOneOfAspOpenerStringTokens(
  8733. rgszTokens, rgcchTokens, ctknOpeners, &idToken);
  8734. if (pbTemp != NULL)
  8735. {
  8736. *ppbToken = pbTemp;
  8737. tkn = rgtknOpeners[idToken];
  8738. }
  8739. // If we found no open token, position token pointer at end of search range
  8740. if (tkn == tknEOF)
  8741. *ppbToken = brSearch.m_pb + brSearch.m_cb;
  8742. return tkn;
  8743. }
  8744. /* ============================================================================
  8745. CTemplate::CTokenList::MovePastToken
  8746. Moves a byte range past a token contained within it
  8747. */
  8748. void
  8749. CTemplate::CTokenList::MovePastToken
  8750. (
  8751. _TOKEN tkn,
  8752. BYTE* pbToken,
  8753. CByteRange& brSearch
  8754. )
  8755. {
  8756. Assert(pbToken >= brSearch.m_pb);
  8757. Assert(brSearch.m_cb >= (DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn)));
  8758. brSearch.Advance(DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn));
  8759. }
  8760. /* ============================================================================
  8761. CTemplate::CTokenList::GetToken
  8762. Gets the next occurrence of a token within a byte range.
  8763. Returns:
  8764. ptr to token
  8765. Side effects
  8766. none
  8767. */
  8768. BYTE*
  8769. CTemplate::CTokenList::GetToken
  8770. (
  8771. TOKEN tkn,
  8772. CByteRange& brSearch,
  8773. LONG lCodePage
  8774. )
  8775. {
  8776. return brSearch.PbString(m_bufTokens.PszLocal((UINT)tkn), lCodePage);
  8777. }
  8778. /* ============================================================================
  8779. The Big Three for CTemplateConnPt
  8780. NOTES:
  8781. Since this interface is embedded in CTemplate,
  8782. AddRef() and Release() delegate to the container object (because that
  8783. is the CTemplate pointer)
  8784. */
  8785. HRESULT
  8786. CTemplateConnPt::QueryInterface(const GUID &uidInterface, void **ppvObj)
  8787. {
  8788. if (uidInterface == IID_IUnknown || uidInterface == IID_IConnectionPoint)
  8789. {
  8790. *ppvObj = this;
  8791. AddRef();
  8792. return S_OK;
  8793. }
  8794. else
  8795. {
  8796. *ppvObj = NULL;
  8797. return E_NOINTERFACE;
  8798. }
  8799. }
  8800. ULONG
  8801. CTemplateConnPt::AddRef()
  8802. {
  8803. return m_pUnkContainer->AddRef();
  8804. }
  8805. ULONG
  8806. CTemplateConnPt::Release()
  8807. {
  8808. return m_pUnkContainer->Release();
  8809. }
  8810. /* ============================================================================
  8811. Constructor for CDocNode
  8812. */
  8813. CTemplate::CDocNodeElem::CDocNodeElem(CAppln *pAppln, IDebugApplicationNode *pDocRoot)
  8814. {
  8815. Assert (pAppln != NULL);
  8816. Assert (pDocRoot != NULL);
  8817. (m_pAppln = pAppln)->AddRef();
  8818. (m_pDocRoot = pDocRoot)->AddRef();
  8819. }
  8820. /* ============================================================================
  8821. Destructor for CDocNode
  8822. */
  8823. CTemplate::CDocNodeElem::~CDocNodeElem()
  8824. {
  8825. m_pAppln->Release();
  8826. DestroyDocumentTree(m_pDocRoot);
  8827. }
  8828. /* ============================================================================
  8829. CTemplate::fIsLangVBScriptOrJScript(USHORT idEngine)
  8830. This function returns T/F to determine if the requested script engine
  8831. is VBScript or JScript. This function is used as an indicator to determin
  8832. if spaces need to be preserved for non MS Scripting languages
  8833. There is an assumption here that the GUIDs for VBScript and JScript will not change
  8834. Inputs
  8835. Index to a script engine
  8836. Returns
  8837. BOOL
  8838. */
  8839. BOOLB CTemplate::FIsLangVBScriptOrJScript(USHORT idEngine)
  8840. {
  8841. // {b54f3741-5b07-11cf-a4b0-00aa004a55e8} VBScript
  8842. static const GUID uid_VBScript = {0xb54f3741, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}};
  8843. // {f414c260-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript
  8844. static const GUID uid_JScript = {0xf414c260, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}};
  8845. // {b54f3743-5b07-11cf-a4b0-00aa004a55e8} VBScript.Encode
  8846. static const GUID uid_VBScriptEncode = {0xb54f3743, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}};
  8847. // {f414c262-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript.Encode
  8848. static const GUID uid_JScriptEncode = {0xf414c262, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}};
  8849. GUID &uidLang = m_pWorkStore->m_ScriptStore.m_rgProgLangId[idEngine];
  8850. return
  8851. uidLang == uid_VBScript || uidLang == uid_VBScriptEncode ||
  8852. uidLang == uid_JScript || uidLang == uid_JScriptEncode;
  8853. }
  8854. SIZE_T
  8855. _RoundUp(
  8856. SIZE_T dwBytes)
  8857. {
  8858. #if 1
  8859. // 16KB <= dwBytes? Round up to next multiple of 4KB
  8860. if (16*1024 <= dwBytes)
  8861. dwBytes = ((dwBytes + (1<<12) - 1) >> 12) << 12;
  8862. // 4KB <= dwBytes < 16KB? Round up to next multiple of 1KB
  8863. else if (4*1024 <= dwBytes)
  8864. dwBytes = ((dwBytes + (1<<10) - 1) >> 10) << 10;
  8865. // 1KB <= dwBytes < 4KB? Round up to next multiple of 256 bytes
  8866. else if (1024 <= dwBytes)
  8867. dwBytes = ((dwBytes + (1<<8) - 1) >> 8) << 8;
  8868. // dwBytes < 1KB? Round up to next multiple of 32 bytes
  8869. else
  8870. dwBytes = ((dwBytes + (1<<5) - 1) >> 5) << 5;
  8871. #endif
  8872. return dwBytes;
  8873. }
  8874. void*
  8875. CTemplate::SmallMalloc(SIZE_T dwBytes)
  8876. {
  8877. DBG_ASSERT(sm_hSmallHeap != NULL);
  8878. dwBytes = _RoundUp(dwBytes);
  8879. return ::HeapAlloc(sm_hSmallHeap, 0, dwBytes);
  8880. }
  8881. void*
  8882. CTemplate::SmallReAlloc(void* pvMem, SIZE_T dwBytes)
  8883. {
  8884. DBG_ASSERT(sm_hSmallHeap != NULL);
  8885. dwBytes = _RoundUp(dwBytes);
  8886. return ::HeapReAlloc(sm_hSmallHeap, 0, pvMem, dwBytes);
  8887. }
  8888. void
  8889. CTemplate::SmallFree(void* pvMem)
  8890. {
  8891. DBG_ASSERT(sm_hSmallHeap != NULL);
  8892. ::HeapFree(sm_hSmallHeap, 0, pvMem);
  8893. }
  8894. void*
  8895. CTemplate::LargeMalloc(SIZE_T dwBytes)
  8896. {
  8897. DBG_ASSERT(sm_hLargeHeap != NULL);
  8898. dwBytes = _RoundUp(dwBytes);
  8899. return ::HeapAlloc(sm_hLargeHeap, 0, dwBytes);
  8900. }
  8901. void*
  8902. CTemplate::LargeReAlloc(void* pvMem, SIZE_T dwBytes)
  8903. {
  8904. DBG_ASSERT(sm_hLargeHeap != NULL);
  8905. dwBytes = _RoundUp(dwBytes);
  8906. return ::HeapReAlloc(sm_hLargeHeap, 0, pvMem, dwBytes);
  8907. }
  8908. void
  8909. CTemplate::LargeFree(void* pvMem)
  8910. {
  8911. DBG_ASSERT(sm_hLargeHeap != NULL);
  8912. ::HeapFree(sm_hLargeHeap, 0, pvMem);
  8913. }
  8914. // WriteTemplate Class.
  8915. /* ============================================================================
  8916. CTemplate::CWriteTemplate::CWriteTemplate
  8917. Constructor for WriteTemplate
  8918. Side effects
  8919. none
  8920. */
  8921. CTemplate::CWriteTemplate::CWriteTemplate () :
  8922. m_pworkStore(NULL),
  8923. m_cbMemRequired (0L),
  8924. m_pTemplate(NULL),
  8925. m_fWriteScript(FALSE),
  8926. m_fCalcLineNumber(TRUE),
  8927. m_pbHeader(NULL)
  8928. {
  8929. }
  8930. /* ============================================================================
  8931. CTemplate::CWriteTemplate::~CWriteTemplate
  8932. Destructor for WriteTemplate.
  8933. Side effects
  8934. none
  8935. */
  8936. CTemplate::CWriteTemplate::~CWriteTemplate ()
  8937. {
  8938. if (m_pbHeader)
  8939. {
  8940. // Dont free up m_pbHeader..Rather let CTemplate free it up during its cleanup.
  8941. // As m_pbStart = m_pbHeader and memory has been alocated from heap.
  8942. m_pbHeader = NULL;
  8943. }
  8944. }
  8945. /* ============================================================================
  8946. CTemplate::CWriteTemplate::Init
  8947. Initializes the write Template by copying the workstore object and the pointer to the template.
  8948. Returns:
  8949. HRESULT
  8950. Side effects
  8951. none
  8952. */
  8953. void CTemplate::CWriteTemplate::Init
  8954. (
  8955. CWorkStore* pworkStore,
  8956. CTemplate* pTemplate,
  8957. BOOL fCalcLineNumber
  8958. )
  8959. {
  8960. //Store pointers to the workstore and template and metabase flag for line number calculation.
  8961. m_pworkStore = pworkStore;
  8962. m_pTemplate = pTemplate;
  8963. m_fCalcLineNumber = fCalcLineNumber;
  8964. // Get CodePage Information
  8965. if (!GetCPInfo(pTemplate->m_wCodePage, &m_codePageInfo))
  8966. {
  8967. // Cant GetCPInfo? Set the codePage such that the call to MultiByteToWideChar is forced.
  8968. m_codePageInfo.MaxCharSize = 0; // This will fail (MaxCharSize == 1)
  8969. }
  8970. }
  8971. /* ============================================================================
  8972. CTemplate::CWriteTemplate::WriteTemplate
  8973. Control routine that calls EstimateMemory and then write script/HTML/object blocks to template memory.
  8974. Returns:
  8975. HRESULT
  8976. Side effects
  8977. none
  8978. */
  8979. void CTemplate::CWriteTemplate::WriteTemplate ()
  8980. {
  8981. m_fWriteScript = FALSE; // Set state to ESTIMATE
  8982. WriteTemplateComponents();
  8983. // Write the blocks into template memory
  8984. m_fWriteScript = TRUE; // Set state to WRITE
  8985. WriteTemplateComponents();
  8986. // This assert is no longer holds as the trailing comment could send this for a spin while it is valid.
  8987. // Assert (m_cbMemRequired == m_pTemplate->m_cbTemplate);
  8988. Assert (m_cbMemRequired >= m_pTemplate->m_cbTemplate);
  8989. }
  8990. /* ============================================================================
  8991. CTemplate::CWriteTemplate::WriteTemplateComponents
  8992. Runs thru the workstore object the second time.. this time telling routines to write to the template
  8993. memory and not just calculate the memory required.
  8994. Writes the template out to a contiguous block of memory.
  8995. Returns:
  8996. nothing
  8997. Side effects:
  8998. Allocates memory. Its CTemplate's responsibility to free it when it done with it.
  8999. HERE IS HOW IT WORKS
  9000. --------------------
  9001. - an 'offset' is the count of bytes from start-of-template to a location
  9002. within template memory
  9003. - at the start of the template are 3 USHORTs, the counts of script blocks,
  9004. object-infos and HTML blocks, respectively
  9005. - next are 4 ULONGs, each an offset to a block of offsets; in order, these are:
  9006. offset-to-offset to first script engine name
  9007. offset-to-offset to first script block (the script text itself)
  9008. offset-to-offset to first object-info
  9009. offset-to-offset to first HTML block
  9010. - next are a variable number of ULONGs, each an offset to a particular
  9011. template component. In order these ULONGs are:
  9012. Offsets to Count of offsets
  9013. ---------- ----------------
  9014. script engine names cScriptBlocks
  9015. script blocks cScriptBlocks
  9016. object-infos cObjectInfos
  9017. HTML blocks cHTMLBlocks
  9018. - next are the template components themselves, stored sequentially
  9019. in the following order:
  9020. script engine names
  9021. script blocks
  9022. object-infos
  9023. HTML blocks
  9024. HERE IS HOW IT LOOKS
  9025. --------------------
  9026. |--|--|--| 3 template component counts (USHORTs)
  9027. |-- --|-- --| 4 offsets to template component offsets (ULONGs)
  9028. |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs)
  9029. |-- --| ............... |-- --|
  9030. |-- --|-- --|-- --|-- --|-- --|
  9031. | ........................... | template components
  9032. | ........................... |
  9033. | ........................... |
  9034. | ........................... |
  9035. or, mnemonically:
  9036. cS cO cH 3 template component counts (USHORTs)
  9037. offSE offSB offOb offHT 4 offsets to template component offsets (ULONGs)
  9038. |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs)
  9039. |-- --| ............... |-- --|
  9040. |-- --|-- --|-- --|-- --|-- --|
  9041. | ........................... | template components
  9042. | ........................... |
  9043. | ........................... |
  9044. | ........................... |
  9045. */
  9046. void CTemplate::CWriteTemplate::WriteTemplateComponents
  9047. (
  9048. )
  9049. {
  9050. USHORT i;
  9051. CByteRange brWrite;
  9052. // SourceFilename :: Only if include file.
  9053. BYTE* pbIncFilename;
  9054. ULONG cbIncFilename;
  9055. ULONG cbSourceOffset; // Offset in the Source file (FOR HTML BLOCKS)
  9056. // Block counts
  9057. USHORT cScriptBlocks = m_pworkStore->CRequiredScriptEngines (m_pTemplate->m_fGlobalAsa);
  9058. USHORT cObjectInfos = m_pworkStore->m_ObjectInfoStore.Count();
  9059. USHORT cHTMLBlocks = m_pworkStore->m_bufHTMLSegments.Count();
  9060. // Count the total blocks in the ASP file that will be written to a template.
  9061. USHORT cBlockPtrs = (2 * cScriptBlocks) + cObjectInfos + cHTMLBlocks;
  9062. // Total number of memory required for header = required header + memory for blocks in ASP file.
  9063. UINT cbRequiredHeader = (C_COUNTS_IN_HEADER * sizeof(USHORT)) + (C_OFFOFFS_IN_HEADER * sizeof(DWORD));
  9064. UINT cbRequiredBlockPtrs = cBlockPtrs * sizeof (DWORD);
  9065. // Adjust offsets.
  9066. UINT cbHeaderOffset = 0;
  9067. UINT cbOffsetToOffset = 0;
  9068. UINT cbDataOffset = cbRequiredHeader + cbRequiredBlockPtrs;
  9069. UINT *pcbDataOffset; // Pointer to the counter = Points to m_cbMemRequired during Estimation phase and cbDataOffset during write phase.
  9070. if (m_fWriteScript)
  9071. {
  9072. // Allocate space for the template (resize the header field.)
  9073. if(NULL == (m_pbHeader = (BYTE*) CTemplate::LargeMalloc(m_cbMemRequired)))
  9074. THROW(E_OUTOFMEMORY);
  9075. /* AppendSourceInfo Uses m_pbStart...So updating m_pbStart to use the same memory.
  9076. Care should be taken to allow CTemplate::~CTemplate to do the clean up. So no cleanup is
  9077. performed when the CTemplate::CWriteTemplate object is distroyed.
  9078. */
  9079. m_pTemplate->m_pbStart = m_pbHeader;
  9080. // The following 3 DWORDs are the number of script, object and HTML blocks respectively.
  9081. // Write out basic headers
  9082. MemCopyAlign (&cbHeaderOffset, &cScriptBlocks, sizeof(USHORT), sizeof(USHORT));
  9083. MemCopyAlign (&cbHeaderOffset, &cObjectInfos, sizeof(USHORT), sizeof(USHORT));
  9084. MemCopyAlign (&cbHeaderOffset, &cHTMLBlocks, sizeof(USHORT), sizeof(USHORT));
  9085. /* The next 4 ULONGs are offsets to within the template memory to the start of script engine names block,
  9086. the start of script block, the start of object block and the start of the HTML block.
  9087. At each of these memory locations where these offsets point there will be offsets to individual
  9088. blocks (script,object,HTML).
  9089. */
  9090. // Write out Offset pointers
  9091. UINT fillerVar = 0; // Dummy variable.. used to write 0 to memory.
  9092. cbOffsetToOffset = cbRequiredHeader;
  9093. fillerVar = cScriptBlocks ? cbOffsetToOffset : 0;
  9094. MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG));
  9095. cbOffsetToOffset += cScriptBlocks * sizeof(ULONG);
  9096. fillerVar = cScriptBlocks ? cbOffsetToOffset : 0;
  9097. MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG));
  9098. cbOffsetToOffset += cScriptBlocks * sizeof(ULONG);
  9099. fillerVar = cObjectInfos ? cbOffsetToOffset : 0;
  9100. MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG));
  9101. cbOffsetToOffset += cObjectInfos * sizeof(ULONG);
  9102. fillerVar = cHTMLBlocks ? cbOffsetToOffset : 0;
  9103. MemCopyAlign (&cbHeaderOffset, &fillerVar, sizeof(ULONG), sizeof(ULONG));
  9104. // Start calculating the memory taken for the template while writing out the header.
  9105. // At this point cbRequiredHeader == cbHeaderOffset
  9106. // Reset cbOffsetToOffset. It will not be used after this as start of cbOffsetToOffset == cbHeaderOffset
  9107. Assert (cbHeaderOffset == cbRequiredHeader);
  9108. pcbDataOffset = &cbDataOffset;
  9109. }
  9110. else
  9111. {
  9112. m_cbMemRequired = cbRequiredHeader + cbRequiredBlockPtrs;
  9113. pcbDataOffset = &m_cbMemRequired;
  9114. }
  9115. // Write the Script Engine names.
  9116. for (i = 0;i<m_pworkStore->m_ScriptStore.CountPreliminaryEngines(); i++)
  9117. {
  9118. if (m_pworkStore->FScriptEngineRequired (i, m_pTemplate->m_fGlobalAsa))
  9119. {
  9120. m_pworkStore->m_ScriptStore.m_bufEngineNames.GetItem(i, brWrite);
  9121. if (m_fWriteScript) // Write out current pointer to the Header (OffsetOfOffsets)
  9122. MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG));
  9123. WriteBSTRToMem (brWrite, pcbDataOffset);
  9124. // Write out the ProgLangID
  9125. MemCopyAlign (pcbDataOffset, &(m_pworkStore->m_ScriptStore.m_rgProgLangId[i]),sizeof(PROGLANG_ID),sizeof(DWORD));
  9126. }
  9127. }
  9128. // Write Scripts
  9129. USHORT idEngine = 0; // Think abt storing this in CWriteTemplate
  9130. for (i = 0;i<m_pworkStore->m_ScriptStore.CountPreliminaryEngines(); i++)
  9131. {
  9132. if (m_pworkStore->FScriptEngineRequired (i, m_pTemplate->m_fGlobalAsa))
  9133. {
  9134. // Calc memory of the script block.
  9135. WriteScriptBlocks (i, idEngine, pcbDataOffset, &cbHeaderOffset, m_pTemplate->m_fGlobalAsa);
  9136. idEngine ++;
  9137. }
  9138. }
  9139. // Write ObjectInfos
  9140. for (i=0;i<cObjectInfos;i++)
  9141. {
  9142. // Get object from workstore
  9143. m_pworkStore->m_ObjectInfoStore.m_bufObjectNames.GetItem(i, brWrite);
  9144. if (m_fWriteScript)
  9145. {
  9146. // Align cbDataOffset to the place where we will write out the Object blocks
  9147. // Alignment is required before writing the offset to header.
  9148. ByteAlignOffset (pcbDataOffset, sizeof(ULONG));
  9149. MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG));
  9150. }
  9151. WriteBSTRToMem (brWrite, pcbDataOffset);
  9152. // Write CLSID, scope and model
  9153. MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_clsid) , sizeof(CLSID), sizeof(DWORD));
  9154. MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_scope) , sizeof(CompScope), sizeof(CompScope));
  9155. MemCopyAlign(pcbDataOffset, &(m_pworkStore->m_ObjectInfoStore.m_pObjectInfos[i].m_model) , sizeof(CompModel), sizeof(CompModel));
  9156. }
  9157. // Write out the the HTML BLocks
  9158. if (!m_pTemplate->m_fGlobalAsa)
  9159. for (i=0;i<cHTMLBlocks;i++)
  9160. {
  9161. m_pworkStore->m_bufHTMLSegments.GetItem(i, brWrite);
  9162. if (m_fWriteScript)
  9163. {
  9164. // Align cbDataOffset to the place where we will write out the HTML blocks
  9165. // Alignment is required before writing the offset to header.
  9166. ByteAlignOffset (pcbDataOffset, sizeof(ULONG));
  9167. MemCopyAlign (&cbHeaderOffset, pcbDataOffset, sizeof(ULONG), sizeof(ULONG));
  9168. }
  9169. WriteBSTRToMem (brWrite, pcbDataOffset);
  9170. //Source offset and include file:
  9171. cbSourceOffset = 0;
  9172. pbIncFilename = NULL;
  9173. cbIncFilename = 0;
  9174. if (brWrite.m_pfilemap)
  9175. {
  9176. // Calculate offset from filemap
  9177. CFileMap *pFileMap = (CFileMap *) brWrite.m_pfilemap;
  9178. if (pFileMap->m_pbStartOfFile) // mapped?
  9179. {
  9180. cbSourceOffset = DIFF (brWrite.m_pb - pFileMap->m_pbStartOfFile) + 1;
  9181. if (pFileMap->GetParent() != NULL && // is Include file ?
  9182. pFileMap->m_szPathInfo ) // path exists
  9183. {
  9184. pbIncFilename = (BYTE *) pFileMap->m_szPathInfo ;
  9185. cbIncFilename = _tcslen(pFileMap->m_szPathInfo) * sizeof(TCHAR);
  9186. }
  9187. }
  9188. }
  9189. // Calculate memory required to write them.
  9190. MemCopyAlign (pcbDataOffset, &cbSourceOffset ,sizeof(ULONG), 0);
  9191. MemCopyAlign (pcbDataOffset, &cbIncFilename,sizeof(ULONG), 0);
  9192. if (cbIncFilename > 0)
  9193. {
  9194. MemCopyAlign(pcbDataOffset, pbIncFilename, cbIncFilename+sizeof(TCHAR), 0);
  9195. }
  9196. }
  9197. // If this is Write Mode then update the template size counter.
  9198. if (m_fWriteScript)
  9199. m_pTemplate->m_cbTemplate = cbDataOffset;
  9200. }
  9201. /* ============================================================================
  9202. CTemplate::CWriteTemplate::WriteScriptBlocks
  9203. Writes out script block for idEngine-th script engine.
  9204. NOTE segment buffer [0] contains primary script segments
  9205. segment buffer [1] contains tagged script segments of default engine
  9206. segment buffer [i] contains tagged script segments of (i-1)th engine, for i >= 2
  9207. During the ESTIMATE phase this routine just skims over the blocks calculating memory required. In most
  9208. cases the estimate phase will follow the exact same code path that the WRITE phase and will stop short of
  9209. actually writing to memory.
  9210. Returns:
  9211. HRESULT
  9212. Side effects
  9213. none
  9214. */
  9215. void CTemplate::CWriteTemplate::WriteScriptBlocks
  9216. (
  9217. USHORT idEnginePrelim,
  9218. USHORT idEngine,
  9219. UINT *pcbDataOffset,
  9220. UINT *pcbOffsetToOffset,
  9221. BOOLB m_fGlobalAsa
  9222. )
  9223. {
  9224. UINT i = 0;
  9225. CByteRange brSegment; // current Script Segment
  9226. UINT cbScriptBlockOffset; // Offset to script Block write location.
  9227. UINT scriptStartBlockOffset;
  9228. USHORT iTSegBuffer = idEnginePrelim + 1; // Index of tagged segment buffer.
  9229. // Number of tagged script segements
  9230. UINT cTaggedSegments = m_pworkStore->m_ScriptStore.m_ppbufSegments[iTSegBuffer]->Count();
  9231. //Align Offset For script Block and update header. (align on ULONG)
  9232. ByteAlignOffset ((UINT*)pcbDataOffset,sizeof(ULONG));
  9233. cbScriptBlockOffset = *pcbDataOffset;
  9234. if (!m_fWriteScript)
  9235. // Calculate memory for the size of this script segment Right now assume 0 is written. Part of the design.
  9236. // 'i' used here as a filler, The code will just add sizeof (ULONG) to pcbDataOffset after aligning it if necessary
  9237. MemCopyAlign (pcbDataOffset, &i, sizeof(ULONG), sizeof(ULONG));
  9238. else
  9239. {
  9240. //Update the header to write the start of the script block.
  9241. MemCopyAlign (pcbOffsetToOffset, &cbScriptBlockOffset, sizeof(ULONG), sizeof(ULONG));
  9242. // Save the offset to where the size of the script has to be written later.
  9243. scriptStartBlockOffset = *pcbDataOffset;
  9244. // Advance Offset pointer by ULONG to make space for writing Lenght in the end.
  9245. *pcbDataOffset += sizeof(ULONG);
  9246. }
  9247. // 0 out the m_cbTargetOffsetPrevT variable which append source info uses.
  9248. m_pTemplate->m_cbTargetOffsetPrevT = 0;
  9249. // Now begin calculating the amount of memory the script segments will take. This has two parts..
  9250. // The primary script segment and the tagged script segments.
  9251. if (!m_fGlobalAsa) // This is not Global.asa
  9252. if (idEnginePrelim == 0) // This is the primary script
  9253. WritePrimaryScript(0, pcbDataOffset, cbScriptBlockOffset + sizeof(ULONG));
  9254. // Calculate memory for the tagged segments.
  9255. for (i=0;i<cTaggedSegments;i++)
  9256. {
  9257. m_pworkStore->m_ScriptStore.m_ppbufSegments[iTSegBuffer]->GetItem(i,brSegment);
  9258. WriteTaggedScript(
  9259. idEngine,
  9260. m_pTemplate->m_rgpSegmentFilemaps[brSegment.m_idSequence],
  9261. brSegment,
  9262. pcbDataOffset,
  9263. cbScriptBlockOffset + sizeof(ULONG),
  9264. FALSE
  9265. );
  9266. }
  9267. // check if the last few characters were --> and take appropriate action.
  9268. // Do this only during the write phase as this code will write to script memory.
  9269. if (m_fWriteScript)
  9270. RemoveHTMLCommentSuffix(scriptStartBlockOffset, pcbDataOffset);
  9271. // We need to write the null terminator
  9272. MemCopyAlign (pcbDataOffset, WSTR_NULL, sizeof(WCHAR), 0);
  9273. if (m_fWriteScript)
  9274. {
  9275. // Calculate size of script = difference in offsets - size of the length pointer - size of null terminator
  9276. UINT cbScript = *pcbDataOffset - scriptStartBlockOffset - sizeof (ULONG) - sizeof (WCHAR);
  9277. MemCopyAlign (&scriptStartBlockOffset, &cbScript, sizeof(ULONG), 0);
  9278. /* We need to append one extra "pseudo-line" to the end of source info's array to cover
  9279. the case where the script engine reports back an error line number which falls after the
  9280. end of the script. We always want the "pseudo-line" to point to the main file, so the
  9281. debugger displays something reasonable. We pass it m_rgpFilemaps[0] which is the main file.
  9282. We need to call this function only during the write phase and not the estimation phase.
  9283. We need to call AppendSourceInfo before the script engine executes the script.
  9284. */
  9285. m_pTemplate->AppendSourceInfo (idEngine,
  9286. m_pTemplate->m_rgpFilemaps[0],
  9287. NULL,
  9288. UINT_MAX,
  9289. UINT_MAX,
  9290. UINT_MAX,
  9291. 0, TRUE);
  9292. }
  9293. }
  9294. /* ============================================================================
  9295. CTemplate::CWriteTemplate::WritePrimaryScript
  9296. Writes (if fWriteScript is TRUE, calculates memory requirement otherwise) out default-engine primary script procedure.
  9297. If VBScript is default-engine, the primary script procedure contains
  9298. interleaved script commands and HTML block-writes, like this:
  9299. Sub Main
  9300. ...
  9301. [script segment]
  9302. Response.WriteBlock([HTML block id])
  9303. ...
  9304. [script segment]
  9305. Response.WriteBlock([HTML block id])
  9306. ...
  9307. [script segment]
  9308. Response.WriteBlock([HTML block id])
  9309. ...
  9310. End Sub
  9311. NOTE segment buffer [0] == primary script segments
  9312. This routine is called for the main script.
  9313. Returns:
  9314. nothing
  9315. Side effects
  9316. none
  9317. */
  9318. void CTemplate::CWriteTemplate::WritePrimaryScript
  9319. (
  9320. USHORT idEngine,
  9321. UINT * pcbDataOffset,
  9322. UINT cbScriptBlockOffset
  9323. )
  9324. {
  9325. USHORT cScriptSegmentsProcessed = 0;
  9326. USHORT cHTMLBlocksProcessed = 0;
  9327. CByteRange brScriptNext;
  9328. CByteRange brHTMLNext;
  9329. char szHTMLBlockID[6];
  9330. USHORT cPrimaryScriptSegments = m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->Count();
  9331. USHORT cHTMLBlocks = m_pworkStore->m_bufHTMLSegments.Count();
  9332. CFileMap * pfilemap;
  9333. // Get Initial script and HTML segments.
  9334. if (cPrimaryScriptSegments)
  9335. m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->GetItem(0,brScriptNext);
  9336. if (cHTMLBlocks)
  9337. m_pworkStore->m_bufHTMLSegments.GetItem(0,brHTMLNext);
  9338. //While there are HTML or primary scripts present...
  9339. while ((cHTMLBlocksProcessed < cHTMLBlocks) || (cScriptSegmentsProcessed < cPrimaryScriptSegments))
  9340. {
  9341. //If HTML block(s) remain to be processed
  9342. if (cHTMLBlocksProcessed < cHTMLBlocks)
  9343. while (TRUE)
  9344. {
  9345. if (brHTMLNext.FEarlierInSourceThan(brScriptNext) || (cScriptSegmentsProcessed >= cPrimaryScriptSegments))
  9346. {
  9347. //append source-info for the target script line we just manufactured.
  9348. pfilemap = m_pTemplate->m_rgpSegmentFilemaps[brHTMLNext.m_idSequence];
  9349. // We can call Append Source info here as it does not write any information to the template
  9350. // rather it reads information from the workstore/scriptstore and appends the source info to
  9351. // the workstore. Provided this function is called just once. we call it during the WRITE phase
  9352. if (m_fWriteScript)
  9353. {
  9354. m_pTemplate->AppendSourceInfo ( idEngine,
  9355. pfilemap, // Filemap
  9356. NULL, // Dont do linenumber calculation
  9357. DIFF(brHTMLNext.m_pb - pfilemap->m_pbStartOfFile), // offset of line in source
  9358. cbScriptBlockOffset, // ptr to start of scripts
  9359. (*pcbDataOffset - cbScriptBlockOffset)/sizeof(WCHAR), // line in target file
  9360. CharAdvDBCS ((WORD) m_pTemplate->m_wCodePage,
  9361. reinterpret_cast <char *> (brHTMLNext.m_pb),
  9362. reinterpret_cast <char *> (brHTMLNext.m_pb + brHTMLNext.m_cb),
  9363. INFINITE,
  9364. NULL), // Exact number of characters in source file
  9365. TRUE); // It is HTML
  9366. }
  9367. // Proceed to calculate the blocknumber and tags in the template.
  9368. // Convert blockID to string.
  9369. _itoa (cHTMLBlocksProcessed, szHTMLBlockID,10);
  9370. // Write out block opener, the block number and the block closer followed by a newline.
  9371. MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteBlockOpen, m_pworkStore->m_cchWriteBlockOpen, 0);
  9372. MemCopyWithWideChar (pcbDataOffset, szHTMLBlockID, strlen(szHTMLBlockID), 0);
  9373. MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteBlockClose, m_pworkStore->m_cchWriteBlockClose, 0);
  9374. MemCopyWithWideChar (pcbDataOffset, SZ_NEWLINE, CB_NEWLINE, 0);
  9375. if (++cHTMLBlocksProcessed >= cHTMLBlocks)
  9376. break;
  9377. //get next HTMLBlock.
  9378. m_pworkStore->m_bufHTMLSegments.GetItem (cHTMLBlocksProcessed,brHTMLNext);
  9379. }
  9380. else
  9381. break;
  9382. }
  9383. //if primary Script Segment remains to be processed;
  9384. if (cScriptSegmentsProcessed < cPrimaryScriptSegments)
  9385. while (TRUE)
  9386. {
  9387. // Write out each primary script segment earlier in the source file than the next HTML block
  9388. if (brScriptNext.FEarlierInSourceThan(brHTMLNext) || (cHTMLBlocksProcessed >= cHTMLBlocks))
  9389. {
  9390. WriteTaggedScript ( idEngine,
  9391. m_pTemplate->m_rgpSegmentFilemaps[brScriptNext.m_idSequence],
  9392. brScriptNext,
  9393. pcbDataOffset,
  9394. cbScriptBlockOffset,
  9395. TRUE);
  9396. if (++cScriptSegmentsProcessed >= cPrimaryScriptSegments)
  9397. break;
  9398. // Get next script Segment
  9399. m_pworkStore->m_ScriptStore.m_ppbufSegments[0]->GetItem(cScriptSegmentsProcessed, brScriptNext);
  9400. }
  9401. else
  9402. break;
  9403. }
  9404. }
  9405. }
  9406. /* ============================================================================
  9407. CTemplate::CTemplate::WriteTaggedScript
  9408. Writes a script segment to template memory line-by-line if fWriteScript is TRUE else just calculates memory
  9409. its memory requirement.
  9410. This routine could be called for both <SCRIPT> </SCRIPT> blocks and <% and %> blocks
  9411. Returns:
  9412. nothing
  9413. Side effects
  9414. none
  9415. */
  9416. void CTemplate::CWriteTemplate::WriteTaggedScript
  9417. (
  9418. USHORT idEngine,
  9419. CFileMap* pfilemap,
  9420. CByteRange brScript,
  9421. UINT* pcbDataOffset,
  9422. UINT cbScriptBlockOffset,
  9423. BOOL fAllowExprWrite
  9424. )
  9425. {
  9426. CByteRange brLine;
  9427. UINT cbPtrOffset = 0;
  9428. BOOL fExpression = FALSE;
  9429. BOOL fFirstLine = TRUE;
  9430. // Metabase setting (m_fCalcLineNumber) will override if it is set to FALSE else it will have no effect.
  9431. BOOL fCalcLineNumber = m_fCalcLineNumber; // should line number be calculated.
  9432. if (FByteRangeIsWhiteSpace(brScript))
  9433. return;
  9434. // Trim white space from begining of script segment
  9435. if (m_pTemplate->FIsLangVBScriptOrJScript (idEngine))
  9436. LTrimWhiteSpace (brScript);
  9437. while (!(brScript.IsNull()))
  9438. {
  9439. //fetch next line from byte range until brScript is NULL
  9440. LineFromByteRangeAdv (brScript, brLine);
  9441. if (FByteRangeIsWhiteSpace (brLine))
  9442. {
  9443. fCalcLineNumber = m_fCalcLineNumber ; // if m_fCalcLineNumber is set to true then fCalcLineNumber is true else
  9444. // just m_fCalcLineNumber will just mask of fCalcLineNumber to FALSE
  9445. continue;
  9446. }
  9447. // If the line is not blank..trim the white spaces.
  9448. if (m_pTemplate->FIsLangVBScriptOrJScript (idEngine))
  9449. LTrimWhiteSpace (brLine);
  9450. RTrimWhiteSpace (brLine);
  9451. /* We need to call AppendSourceInfo so that the script engine has a corelation
  9452. with the line number..and other source information such as which file the script belongs to.
  9453. If the line number flag is not NULL then AppendSourceInfo will force recalculation of the
  9454. line numbers. If however it is NULL then AppendSourceInfo just appends 1 to the current line number
  9455. without calling into the Line number calculation routine.
  9456. All AppendSourceInfo calls are made only during the WRITE phase.
  9457. NOTE: AppendSourceInfo will always take the NULL as a parameter if m_fCalcLineNumber is set to FALSE
  9458. */
  9459. if (m_fWriteScript)
  9460. {
  9461. m_pTemplate->AppendSourceInfo ( idEngine,
  9462. pfilemap,
  9463. fCalcLineNumber ? brLine.m_pb : NULL,
  9464. DIFF (brLine.m_pb - pfilemap->m_pbStartOfFile),
  9465. cbScriptBlockOffset,
  9466. (*pcbDataOffset - cbScriptBlockOffset)/sizeof(WCHAR),
  9467. CharAdvDBCS ((WORD) m_pTemplate->m_wCodePage,
  9468. reinterpret_cast <char *> (brLine.m_pb),
  9469. reinterpret_cast <char *> (brLine.m_pb + brLine.m_cb),
  9470. INFINITE, NULL),
  9471. FALSE);
  9472. }
  9473. /* fCalcLineNumber is set to TRUE if the parser hits a blank line or an close token.
  9474. It instructs the Append source info to begin line number calculation from the start.
  9475. Setting it to FALSE will just tell it to ignore recalculation of the line number and it
  9476. just appends 1 to previous line number to get the current count.
  9477. On return from AppendSourceInfo..we just reset the line number calculation flag.
  9478. */
  9479. fCalcLineNumber = FALSE;
  9480. if (fAllowExprWrite && fFirstLine)
  9481. {
  9482. // Test for remainder of script segment null on temp copy of script byte range, not on actual
  9483. CByteRange brTemp = brScript;
  9484. LTrimWhiteSpace (brTemp);
  9485. if (brTemp.IsNull())
  9486. {
  9487. /* if (a) expr-write is allowed AND (b) this is only script line in this segment (first and remainder
  9488. is NULL. then test this line to see if it is an expression.
  9489. if this like is an expression, create a script command that reads
  9490. Response.Write([line contents])
  9491. */
  9492. if (fExpression = m_pTemplate->FExpression (brLine))
  9493. {
  9494. MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteOpen,
  9495. m_pworkStore->m_cchWriteOpen, 0);
  9496. }
  9497. brScript = brTemp;
  9498. }
  9499. }
  9500. // write out line contents
  9501. ScriptMemoryMinusEscapeChars(brLine, pcbDataOffset, cbPtrOffset);
  9502. // If this line is an expression, close script command.
  9503. if (fExpression)
  9504. MemCopyWithWideChar (pcbDataOffset, m_pworkStore->m_szWriteClose,
  9505. m_pworkStore->m_cchWriteClose, 0);
  9506. // Write NEW_LINE and set first line flag to false.
  9507. MemCopyWithWideChar (pcbDataOffset, SZ_NEWLINE, CB_NEWLINE, 0);
  9508. fFirstLine = FALSE;
  9509. }
  9510. }
  9511. /* ============================================================================
  9512. CTemplate::CWriteTemplate::ScriptMemoryMinusEscapeChars
  9513. if fWriteWideCharStr is FALSE it calculates a script byte range to memory requirement, minus its escape characters, if any.
  9514. if fWriteWideCharStr is TRUE it Writes a script byte range to memory, minus its escape characters, if any.
  9515. Returns:
  9516. nothing
  9517. Side effects
  9518. none
  9519. */
  9520. void CTemplate::CWriteTemplate::ScriptMemoryMinusEscapeChars
  9521. (
  9522. CByteRange brScript,
  9523. UINT *pcbDataOffset,
  9524. UINT cbPtrOffset
  9525. )
  9526. {
  9527. BYTE * pbToken;
  9528. while (NULL != (pbToken = gm_pTokenList->GetToken (CTokenList::tknEscapedClosePrimaryScript, brScript,
  9529. m_pTemplate->m_wCodePage)))
  9530. {
  9531. CByteRange brTemp = brScript;
  9532. // Set temp range to source range up to escaped-token
  9533. brTemp.m_cb = DIFF (pbToken - brTemp.m_pb);
  9534. // WriteByteRangeAdv with no BSTR = MemCopyWithWideChar
  9535. // Write out temp range and actual-token - this replaces escaped-token with actual-token.
  9536. MemCopyWithWideChar (pcbDataOffset, brTemp.m_pb, brTemp.m_cb, 0);
  9537. MemCopyWithWideChar (pcbDataOffset, SZ_TOKEN(CTokenList::tknClosePrimaryScript), CCH_TOKEN(CTokenList::tknClosePrimaryScript), 0);
  9538. //Advance source range past escaped token
  9539. brScript.Advance (DIFF(pbToken - brScript.m_pb) + CCH_TOKEN(CTokenList::tknEscapedClosePrimaryScript));
  9540. }
  9541. // write remainder of source range.
  9542. MemCopyWithWideChar (pcbDataOffset, brScript.m_pb, brScript.m_cb, 0);
  9543. }
  9544. /* ============================================================================
  9545. CTemplate::CWriteTemplate::CalcMemoryAndUpdateHeader
  9546. Calculate the memory required to write a BSTR into template memory
  9547. Returns:
  9548. nothing
  9549. Side effects
  9550. none
  9551. */
  9552. void CTemplate::CWriteTemplate::WriteBSTRToMem
  9553. (
  9554. CByteRange & brWrite,
  9555. UINT *pcbOffset
  9556. )
  9557. {
  9558. MemCopyAlign (pcbOffset, &brWrite.m_cb, sizeof(ULONG), sizeof(ULONG)); // Length of BSTR
  9559. MemCopyAlign (pcbOffset, brWrite.m_pb, brWrite.m_cb, 0); // the BSTR itself.
  9560. MemCopyAlign (pcbOffset, SZ_NULL, 1, 0); // Teminating NULL
  9561. }
  9562. /* ============================================================================
  9563. CTemplate::CWriteTemplate::MemCopyAlign
  9564. Writes to target/calculates memory required after adjusting byte alignment if necessary.
  9565. Returns:
  9566. nothing
  9567. Side effects
  9568. none
  9569. */
  9570. void CTemplate::CWriteTemplate::MemCopyAlign
  9571. (
  9572. UINT *pcbOffset,
  9573. void *pbSource,
  9574. ULONG cbSource,
  9575. UINT cbByteAlign
  9576. )
  9577. {
  9578. // If byte alignment is necessary then align.
  9579. if (cbByteAlign > 0)
  9580. ByteAlignOffset (pcbOffset, cbByteAlign);
  9581. // Copy memory if in WRITE phase and adjust offset else just adjust offset.
  9582. if (m_fWriteScript)
  9583. memcpy(m_pbHeader + *pcbOffset,pbSource,cbSource);
  9584. *pcbOffset += cbSource;
  9585. }
  9586. /* ============================================================================
  9587. CTemplate::CWriteTemplate::MemCopyEstimateWithWideChar
  9588. If the m_fWriteScript flag is FALSE then this routine calculates the memory that will be required when
  9589. the source in converted into WideChar and stored. If the m_fWriteScript flag is TRUE then the routine
  9590. copies the wide string into Template memory.
  9591. Parameters:
  9592. pcbOffset : pointer to offset where data will be written
  9593. pbSource : pointer to the source string
  9594. cbSource : count of bytes in source.
  9595. cbByteAlign : what boundry should pcbOffset be aligned on 0, 1, 2, 4
  9596. Returns:
  9597. nothing
  9598. Side effects
  9599. none
  9600. */
  9601. void CTemplate::CWriteTemplate::MemCopyWithWideChar
  9602. (
  9603. UINT *pcbOffset,
  9604. void *pbSource,
  9605. ULONG cbSource,
  9606. UINT cbByteAlign
  9607. )
  9608. {
  9609. ULONG cchWstr;
  9610. // If byte alignment is necessary then align.
  9611. Assert(cbSource);
  9612. if (cbByteAlign > 0)
  9613. ByteAlignOffset ((UINT *)pcbOffset, cbByteAlign);
  9614. /* If this is the estimation phase and the code page MaxCharSize is 1 (ASCII) then we just make the
  9615. calculate cbWstr to be cbSource (WideChars). This is to reduce the overhead of calling into
  9616. MultiByteToWideChar. If this is the write phase then it calls MultiByteToWideChar.
  9617. */
  9618. if (!m_fWriteScript)
  9619. {
  9620. if (m_codePageInfo.MaxCharSize == 1)
  9621. cchWstr = cbSource; // cbWstr is in wide chars.
  9622. else
  9623. cchWstr = MultiByteToWideChar (m_pTemplate->m_wCodePage, 0, (LPCSTR)pbSource, cbSource, NULL, 0);
  9624. }
  9625. else
  9626. {
  9627. cchWstr = MultiByteToWideChar (m_pTemplate->m_wCodePage, 0, (LPCSTR)pbSource, cbSource,(LPWSTR) (m_pbHeader + *pcbOffset), m_cbMemRequired - *pcbOffset);
  9628. Assert(FImplies ((m_codePageInfo.MaxCharSize == 1), (cchWstr == cbSource)));
  9629. }
  9630. // Adjust offset.
  9631. if (cchWstr)
  9632. *pcbOffset += cchWstr *2;
  9633. }
  9634. /* ============================================================================
  9635. CTemplate::CWriteTemplate::RemoveHTMLCommentSuffix
  9636. Strip of a HTML comment within script blocks
  9637. Parameters:
  9638. Returns:
  9639. nothing
  9640. Side effects
  9641. strips trailing HTML comments from the Script.
  9642. IMPORTANT:
  9643. This function is similar to the one in VBSCRIPT scripting engine. The only difference is that
  9644. the scripting team places a \0 at the end of the script because they parse the scripts independently.
  9645. By doing the same thing we will inadvertently be terminating the script at the first point we
  9646. notice a --> followed by a %> or a </SCRIPT>. We have two alternatives
  9647. (a) Overwrite the entire comment with a space.
  9648. Effects: Error Reporting will miss information
  9649. (b) Modify the pointer in the template to place the pbCurrentOffset to the start of the Comment block.
  9650. Effects: Yet to learn. Probarbly affect Line Number calculation
  9651. Another Side Effect = We will be allocating more memory than we need (memory pertaining to the comment).
  9652. */
  9653. void CTemplate::CWriteTemplate::RemoveHTMLCommentSuffix
  9654. (
  9655. UINT cbStartOffset,
  9656. UINT *pcbCurrentOffset
  9657. )
  9658. {
  9659. UINT cbTempDataOffset = *pcbCurrentOffset;
  9660. UINT len = (*pcbCurrentOffset -cbStartOffset)/sizeof(WCHAR);
  9661. WCHAR * pwszSrc = (WCHAR*) (m_pTemplate->m_pbStart + cbStartOffset);
  9662. while (len > 3 && FWhiteSpaceEx(pwszSrc[len]))
  9663. {
  9664. len --;
  9665. cbTempDataOffset -= sizeof (WCHAR);
  9666. }
  9667. if (len < 3 || (WCHAR) L'>' != (WCHAR) pwszSrc[len--]
  9668. || (WCHAR) L'-' != pwszSrc[len--]|| (WCHAR) L'-' != pwszSrc[len--])
  9669. return ;
  9670. // Saw a -->delimiter
  9671. // Now run back until there is an EOL , a // or a <!--
  9672. // Also there were 3 decrements of len which went unnoticed. Take them into account.
  9673. cbTempDataOffset -= (3 * sizeof(WCHAR));
  9674. while (len > 0)
  9675. {
  9676. if ((WCHAR) L'\n' == pwszSrc[len] || (WCHAR) L'\r' == pwszSrc[len])
  9677. {
  9678. len ++; // arrange for the character following the EOL to be overwritten with a 0
  9679. cbTempDataOffset += sizeof (WCHAR);
  9680. break;
  9681. }
  9682. if (len >= 1 && (WCHAR) L'/' == pwszSrc[len-1] && (WCHAR)L'/' == pwszSrc[len])
  9683. {
  9684. len --; // arrange for the first // to be overwritten with a 0
  9685. cbTempDataOffset -= sizeof (WCHAR);
  9686. break;
  9687. }
  9688. if (len >= 3 && (WCHAR) L'<' == pwszSrc[len -3] && (WCHAR) L'!' == pwszSrc[len -2]
  9689. &&(WCHAR) L'-' == pwszSrc[len -1] && (WCHAR) L'-' == pwszSrc[len])
  9690. {
  9691. len -=3; // arrange for the first < to be over written by the 0
  9692. cbTempDataOffset -= (3* sizeof (WCHAR));
  9693. break;
  9694. }
  9695. len --;
  9696. cbTempDataOffset -= sizeof(WCHAR);
  9697. }
  9698. // We should add a \r\n to keep the line number calculation truthful.
  9699. MemCopyAlign(&cbTempDataOffset, WSZ_NEWLINE, CB_NEWLINE * sizeof(WCHAR));
  9700. //Set start of comment as currentDataOffset
  9701. *pcbCurrentOffset = cbTempDataOffset;
  9702. return ;
  9703. }