Source code of Windows XP (NT5)
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.

10711 lines
351 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. // Max # of opener tokens to look for
  29. #define TOKEN_OPENERS_MAX 8
  30. /*===================================================================
  31. Private non-class support functions
  32. ===================================================================*/
  33. static void ByteRangeFromPb(BYTE* pbSource, CByteRange& brTarget);
  34. static BOOLB FByteRangesAreEqual(CByteRange& br1, CByteRange& br2);
  35. static unsigned CharAdvDBCS(WORD wCodePage, char *pchStart, char *pchEnd, unsigned cCharAdv, char **ppchEnd, BOOL fForceDBCS = FALSE);
  36. static void LineFromByteRangeAdv(CByteRange& br, CByteRange& brLine);
  37. static void LTrimWhiteSpace(CByteRange& br);
  38. static void RTrimWhiteSpace(CByteRange& br);
  39. static CByteRange BrNewLine(CByteRange br);
  40. static BOOLB FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace = TRUE);
  41. static BOOLB FByteRangeIsWhiteSpace(CByteRange br);
  42. static BOOLB FTagName(BYTE* pb, UINT cb);
  43. static void ByteAlignOffset(UINT* pcbOffset, UINT cbAlignment);
  44. static void GetSzFromPatternInserts(char* pszPattern, UINT cInserts, char** ppszInserts, char* szReturned);
  45. static UINT CchPathFromFilespec(LPCTSTR szFile);
  46. static void GetPathFromParentAndFilespec(LPCTSTR szParentPath, LPCTSTR szFileSpec, LPTSTR* pszPath);
  47. static void HandleAccessFailure(CHitObj* pHitObj, TCHAR* szFile);
  48. static void SendToLog(DWORD dwMask, CHAR *szFileName, CHAR *szLineNum, CHAR *szShortDes, CHAR *szLongDes, CHAR *szEngine, CHitObj *pHitObj);
  49. static HRESULT GetProgLangId(CByteRange& brEngine, PROGLANG_ID* pProgLangId);
  50. inline
  51. void __cdecl DebugPrintf(LPCSTR fmt, ...)
  52. {
  53. #if DBG
  54. char msg[512];
  55. va_list marker;
  56. va_start(marker, fmt);
  57. vsprintf(msg, fmt, marker);
  58. va_end(marker);
  59. OutputDebugStringA(msg);
  60. #endif
  61. }
  62. /* ============================================================================
  63. ByteRangeFromPb
  64. Gets a byte range from a contiguous block of memory
  65. Returns:
  66. Nothing.
  67. Side effects:
  68. None.
  69. */
  70. void
  71. ByteRangeFromPb
  72. (
  73. BYTE* pbSource,
  74. CByteRange& brTarget
  75. )
  76. {
  77. Assert(pbSource != NULL);
  78. brTarget.m_cb = *(ULONG*)pbSource;
  79. brTarget.m_pb = pbSource + sizeof(ULONG);
  80. }
  81. /* ============================================================================
  82. FByteRangesAreEqual
  83. Compares two byte ranges
  84. Returns:
  85. BOOLB. True if byte ranges are equal, else false.
  86. Side effects:
  87. None.
  88. */
  89. BOOLB
  90. FByteRangesAreEqual
  91. (
  92. CByteRange& br1,
  93. CByteRange& br2
  94. )
  95. {
  96. if(br1.m_cb != br2.m_cb)
  97. return FALSE;
  98. return (!_strnicmp((LPCSTR)br1.m_pb, (LPCSTR)br2.m_pb, br1.m_cb));
  99. }
  100. /* ============================================================================
  101. CharAdvDBCS
  102. Advance "cchCharAdv" characters in a buffer
  103. SBCS: Degenerates to simple pointer arithmatic
  104. Arguments:
  105. wCodePage - code page
  106. pchStart - pointer to beginning of segment
  107. pchEnd - pointer to just past end of segment
  108. cCharAdv - # of characters to advance
  109. ppchEnd - [output], contains pointer "cCharAdv" chars past pchStart
  110. fForceDBCS - if TRUE, always use double byte algorithm.
  111. (for verifying correct behavior of func in debug mode)
  112. Returns:
  113. (int) # of characters that we actually advanced
  114. Notes:
  115. By passing INFINITE for "cCharAdv", you can use this function to count characters
  116. in a block
  117. Side effects:
  118. None.
  119. */
  120. unsigned
  121. CharAdvDBCS
  122. (
  123. WORD wCodePage,
  124. char *pchStart,
  125. char *pchEnd,
  126. unsigned cCharAdv,
  127. char **ppchEnd,
  128. BOOL fForceDBCS
  129. )
  130. {
  131. CPINFO CpInfo;
  132. GetCPInfo(wCodePage, &CpInfo);
  133. if (!fForceDBCS && CpInfo.MaxCharSize == 1)
  134. {
  135. char *pchT = pchStart + min(cCharAdv, unsigned(pchEnd - pchStart));
  136. if (ppchEnd)
  137. *ppchEnd = pchT;
  138. #if DBG
  139. // Verify DBCS algorithm (not often tested otherwise)
  140. char *pchTest;
  141. unsigned cchTest = CharAdvDBCS(wCodePage, pchStart, pchEnd, cCharAdv, &pchTest, TRUE);
  142. Assert (cchTest == unsigned(pchT - pchStart) && pchTest == pchT);
  143. #endif
  144. return DIFF(pchT - pchStart);
  145. }
  146. else
  147. {
  148. int cch = 0;
  149. char *pchNext = pchStart;
  150. // Count DBCS characters. We have to stop before pchEnd because
  151. // pchEnd may point past file map and CharNextExA AVs when advancing
  152. // past allocated memory
  153. while (cCharAdv > 0 && pchNext < pchEnd-2)
  154. {
  155. pchNext = *pchNext? AspCharNextA(wCodePage, pchNext) : pchNext + 1;
  156. --cCharAdv;
  157. ++cch;
  158. }
  159. // We could stop on the last or the before last character
  160. // depending on the DBCS char sequence
  161. if (cCharAdv > 0 && pchNext == pchEnd-1)
  162. {
  163. // Only one byte - has to be one single byte character
  164. ++pchNext;
  165. ++cch;
  166. }
  167. else if (cCharAdv > 0 && pchNext == pchEnd-2)
  168. {
  169. // 2 bytes left - either 1 2-byte char or 2 1-byte chars
  170. if (IsDBCSLeadByteEx(wCodePage, *pchNext))
  171. {
  172. ++cch;
  173. pchNext += 2;
  174. }
  175. else
  176. {
  177. // Two characters left. If cCharAdv > 1, this means that user wants to
  178. // advance at least two more chars. Otherwise, cCharAdv == 1, and
  179. // we advance one char
  180. //
  181. if (cCharAdv > 1)
  182. {
  183. cch += 2;
  184. pchNext += 2;
  185. }
  186. else
  187. {
  188. Assert (cCharAdv == 1);
  189. ++cch;
  190. ++pchNext;
  191. }
  192. }
  193. }
  194. if (ppchEnd)
  195. *ppchEnd = pchNext;
  196. return cch;
  197. }
  198. }
  199. /* ============================================================================
  200. LineFromByteRangeAdv
  201. Gets the first line in a byte range.
  202. Returns:
  203. Nothing
  204. Side effects:
  205. Advances source byte range to just beyond its first non-white-space line,
  206. if one is found.
  207. */
  208. void
  209. LineFromByteRangeAdv
  210. (
  211. CByteRange& brSource,
  212. CByteRange& brLine
  213. )
  214. {
  215. CByteRange brTemp;
  216. if(brSource.IsNull())
  217. {
  218. brLine.Nullify();
  219. return;
  220. }
  221. brLine.m_pb = brSource.m_pb;
  222. brTemp = BrNewLine(brSource);
  223. if(brTemp.IsNull())
  224. {
  225. // We found no newline in a non-empty byte range:
  226. // set line range to entire source byte range and empty source byte range
  227. brLine.m_cb = brSource.m_cb;
  228. brSource.Nullify();
  229. }
  230. else
  231. {
  232. // We found a newline in a non-empty byte range:
  233. // set line range to portion of source byte range before new line;
  234. // set source range to portion of source range after new line
  235. brLine.m_cb = DIFF(brTemp.m_pb - brSource.m_pb);
  236. brSource.m_pb = brTemp.m_pb + brTemp.m_cb;
  237. brSource.m_cb -= (brLine.m_cb + brTemp.m_cb);
  238. }
  239. }
  240. /* ============================================================================
  241. LTrimWhiteSpace
  242. Left-trim white space from byte-range
  243. Returns:
  244. Nothing
  245. Side effects:
  246. Advances byte range to just beyond its first non-white-space character.
  247. */
  248. void
  249. LTrimWhiteSpace
  250. (
  251. CByteRange& br
  252. )
  253. {
  254. if(br.IsNull())
  255. return;
  256. while(FWhiteSpace(*br.m_pb))
  257. {
  258. br.m_pb++;
  259. if(--br.m_cb == 0)
  260. return;
  261. }
  262. }
  263. /* ============================================================================
  264. RTrimWhiteSpace
  265. Right-trim white space from byte-range
  266. */
  267. void
  268. RTrimWhiteSpace(CByteRange& br)
  269. {
  270. if(br.IsNull())
  271. return;
  272. while(FWhiteSpace(*(br.m_pb + br.m_cb - 1)))
  273. {
  274. if(--br.m_cb == 0)
  275. return;
  276. }
  277. }
  278. /* ============================================================================
  279. BrNewLine
  280. Returns ptr to the first newline in a byte range
  281. NOTE does not change byte range (since it is passed by value)
  282. */
  283. CByteRange
  284. BrNewLine(CByteRange br)
  285. {
  286. while(!br.IsNull())
  287. {
  288. if(*br.m_pb == '\r')
  289. return CByteRange(br.m_pb, (br.m_cb > 1 && br.m_pb[1] == '\n')? 2 : 1);
  290. else if (*br.m_pb == '\n')
  291. return CByteRange(br.m_pb, 1);
  292. ++br.m_pb;
  293. --br.m_cb;
  294. }
  295. return CByteRange();
  296. }
  297. /* ============================================================================
  298. FWhiteSpace
  299. Returns:
  300. TRUE if ch is a white-space character, else returns FALSE
  301. Certain character(s) (e.g. space) may be treated as
  302. non-white-space; to do this, caller passes FALSE for
  303. fSpaceIsWhiteSpace flag.
  304. */
  305. BOOLB
  306. FWhiteSpace(char ch, BOOLB fSpaceIsWhiteSpace)
  307. {
  308. switch (ch)
  309. {
  310. case ' ':
  311. return fSpaceIsWhiteSpace;
  312. case '\0':
  313. return TRUE;
  314. case '\a':
  315. return TRUE;
  316. case '\b':
  317. return TRUE;
  318. case '\f':
  319. return TRUE;
  320. case '\n':
  321. return TRUE;
  322. case '\r':
  323. return TRUE;
  324. case '\t':
  325. return TRUE;
  326. case '\v':
  327. return TRUE;
  328. default:
  329. return FALSE;
  330. }
  331. }
  332. /* ============================================================================
  333. FByteRangeIsWhiteSpace
  334. Is the entire input byte range white space?
  335. NOTE input byte range is byval; caller's copy is not changed
  336. */
  337. BOOLB
  338. FByteRangeIsWhiteSpace(CByteRange br)
  339. {
  340. while(!br.IsNull())
  341. {
  342. if(!FWhiteSpace(*(br.m_pb)))
  343. return FALSE;
  344. br.Advance(1);
  345. }
  346. return TRUE;
  347. }
  348. /* ============================================================================
  349. FTagName
  350. Does pb point to a valid HTML tag name?
  351. (i.e., is *pb a valid HTML tag name and not a substring?)
  352. Returns
  353. TRUE or FALSE
  354. Side effects
  355. None
  356. */
  357. BOOLB
  358. FTagName(BYTE* pb, UINT cb)
  359. {
  360. if((pb == NULL) || (cb == 0))
  361. return FALSE;
  362. // a valid HTML tag name must be preceded by white space ...
  363. if( FWhiteSpace(*(pb - 1)) || *(pb - 1) == '@' )
  364. {
  365. // ... and followed either by white space or the tag separator
  366. if(FWhiteSpace(*(pb + cb)))
  367. return TRUE;
  368. else if(*(pb + cb) == CH_ATTRIBUTE_SEPARATOR)
  369. return TRUE;
  370. }
  371. return FALSE;
  372. }
  373. /*===================================================================
  374. ByteAlignOffset
  375. Byte-aligns an offset value, based on size of source data
  376. */
  377. void
  378. ByteAlignOffset
  379. (
  380. UINT* pcbOffset, // ptr to offset value
  381. UINT cbAlignment // Alignment boundary
  382. )
  383. {
  384. // comment the below code out so that it works for 64 bit...
  385. // only byte-align for 2-, or 4-byte data
  386. // since our base pointer in only aligned to a 4 byte boundary
  387. //if(cbAlignment == 2 || cbAlignment == 4)
  388. //{
  389. // if current offset does not fall on a byte-aligned location for current data type,
  390. // advance offset to next byte-aligned location
  391. Assert(cbAlignment > 0);
  392. --cbAlignment;
  393. if (*pcbOffset & cbAlignment)
  394. *pcbOffset = (*pcbOffset + cbAlignment + 1) & ~cbAlignment;
  395. }
  396. /* ============================================================================
  397. GetSzFromPatternInserts
  398. Returns a 'resolved' version of a pattern string, i.e. a new string in which
  399. | characters have been replaced by caller-specified insert strings.
  400. NOTE this function allocates, but caller must free
  401. Returns:
  402. Nothing
  403. Side effects:
  404. allocates memory
  405. */
  406. void
  407. GetSzFromPatternInserts
  408. (
  409. char* pszPattern, // 'pattern' string
  410. UINT cInserts, // count of insert strings
  411. char** ppszInserts, // array of ptrs to insert strings
  412. char* szReturned // returned string MUST be allocated by caller
  413. )
  414. {
  415. UINT cchRet = strlen(pszPattern); // length of return string
  416. char* pchStartCopy = pszPattern; // ptr to start of copy range in pattern
  417. char* pchEndCopy = pszPattern; // ptr to end of copy range in pattern
  418. UINT cActualInserts = 0; // count of actual insert strings
  419. // init return string to empty so we can concatenate onto it
  420. szReturned[0] = NULL;
  421. // zero out length of return string - we now use it to count actual length as we build return string
  422. cchRet = 0;
  423. while(TRUE)
  424. {
  425. // advance end-of-copy ptr through pattern looking for insertion points or end of string
  426. while ((*pchEndCopy != NULL) && (IsDBCSLeadByte(*pchEndCopy) || (*pchEndCopy != '|')))
  427. pchEndCopy = CharNextA(pchEndCopy);
  428. // cat from start-of-copy to end-of-copy onto return string
  429. strncat(szReturned, pchStartCopy, DIFF(pchEndCopy - pchStartCopy));
  430. // update return string length
  431. cchRet += DIFF(pchEndCopy - pchStartCopy);
  432. // if we are at end of pattern, exit
  433. if(*pchEndCopy == NULL)
  434. goto Exit;
  435. if(cActualInserts < cInserts)
  436. {
  437. // if inserts remain, cat the next one onto return string
  438. strcat(szReturned, ppszInserts[cActualInserts]);
  439. // update return string length
  440. cchRet += strlen(ppszInserts[cActualInserts]);
  441. cActualInserts++;
  442. }
  443. // advance end-of-copy and start-of-copy beyond insertion point
  444. pchEndCopy++;
  445. pchStartCopy = pchEndCopy;
  446. }
  447. Exit:
  448. // null-terminate return string
  449. szReturned[cchRet] = NULL;
  450. }
  451. /* ============================================================================
  452. CchPathFromFilespec
  453. Returns a filespec's path length (exclusive of filespec)
  454. NOTE path string includes trailing '\' or '/'
  455. Returns:
  456. Length of path string
  457. Side effects:
  458. None
  459. */
  460. UINT
  461. CchPathFromFilespec
  462. (
  463. LPCTSTR szFileSpec // filespec
  464. )
  465. {
  466. // BUG FIX 102010 DBCS fixes
  467. //int ich = lstrlen(szFileSpec) - 1; // index of char to compare
  468. //
  469. //while(*(szFileSpec + ich) != '\\' && *(szFileSpec + ich) != '/')
  470. // {
  471. // if(--ich < 0)
  472. // THROW(E_FAIL);
  473. // }
  474. //return (UINT) (ich + 1); // path length, including trailing '\' or '/', is char index + 1
  475. TCHAR* p1 = _tcsrchr(szFileSpec, _T('\\'));
  476. TCHAR* p2 = _tcsrchr(szFileSpec, _T('/')); // this wont be a DBCS trail byte.
  477. if (p1 == NULL && p2 == NULL)
  478. THROW(E_FAIL);
  479. return (UINT) ((((LPTSTR)max(p1,p2) - szFileSpec)) + 1);
  480. }
  481. /* ============================================================================
  482. GetPathFromParentAndFilespec
  483. Returns an absolute path which is a 'parent' file's path concatenated with a filespec.
  484. Returns:
  485. absolute path (out-parameter)
  486. Side effects:
  487. None
  488. */
  489. void
  490. GetPathFromParentAndFilespec
  491. (
  492. LPCTSTR szParentPath, // parent path
  493. LPCTSTR szFileSpec, // filespec
  494. LPTSTR* pszPath // resolved path (out-parameter)
  495. )
  496. {
  497. UINT cchParentPath = CchPathFromFilespec(szParentPath);
  498. if ((cchParentPath + _tcslen(szFileSpec)) > MAX_PATH)
  499. THROW(E_FAIL);
  500. _tcsncpy(*pszPath, szParentPath, cchParentPath);
  501. _tcscpy(*pszPath + cchParentPath, szFileSpec);
  502. }
  503. /* ============================================================================
  504. HandleAccessFailure
  505. Handles an access-denied failure
  506. Returns:
  507. nothing
  508. Side effects:
  509. none
  510. */
  511. void
  512. HandleAccessFailure
  513. (
  514. CHitObj* pHitObj, // browser's hitobj
  515. TCHAR * szFile // file path of main template
  516. )
  517. {
  518. Assert(pHitObj);
  519. // debugging diagnostic print
  520. #if DBG
  521. STACK_BUFFER( authUserBuff, 32 );
  522. char *szAuthUser;
  523. DWORD cbAuthUser;
  524. if (SERVER_GET(pHitObj->PIReq(), "AUTH_USER", &authUserBuff, &cbAuthUser)) {
  525. szAuthUser = (char*)authUserBuff.QueryPtr();
  526. }
  527. else {
  528. szAuthUser = "anonymous";
  529. }
  530. #if UNICODE
  531. DBGPRINTF((DBG_CONTEXT, "No permission to read file %S\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated()));
  532. #else
  533. DBGPRINTF((DBG_CONTEXT, "No permission to read file %s\n", szFile != NULL? szFile : pHitObj->PIReq()->QueryPszPathTranslated()));
  534. #endif
  535. DBGPRINTF((DBG_CONTEXT, " The user account is \"%s\"\n", szAuthUser));
  536. #endif
  537. CResponse *pResponse = pHitObj->PResponse();
  538. if (!pResponse)
  539. return;
  540. HandleSysError(401,3,IDE_401_3_ACCESS_DENIED,IDH_401_3_ACCESS_DENIED,pHitObj->PIReq(),pHitObj);
  541. return;
  542. }
  543. /* ============================================================================
  544. SendToLog
  545. Sends Error Info to Log
  546. Returns:
  547. Nothing
  548. Side effects:
  549. None.
  550. */
  551. void
  552. SendToLog
  553. (
  554. DWORD dwMask,
  555. CHAR *szFileName,
  556. CHAR *szLineNum,
  557. CHAR *szEngine,
  558. CHAR *szErrCode,
  559. CHAR *szShortDes,
  560. CHAR *szLongDes,
  561. CHitObj *pHitObj // browser's hitobj
  562. )
  563. {
  564. CHAR *szFileNameT;
  565. CHAR *szLineNumT;
  566. CHAR *szEngineT;
  567. CHAR *szErrCodeT;
  568. CHAR *szShortDesT;
  569. CHAR *szLongDesT;
  570. if(pHitObj) {
  571. // NOTE - szFileName is assumed to be UTF8 when UNICODE is defined
  572. szFileNameT = StringDupA(szFileName);
  573. szLineNumT = StringDupA(szLineNum);
  574. szEngineT = StringDupA(szEngine);
  575. szErrCodeT = StringDupA(szErrCode);
  576. szShortDesT = StringDupA(szShortDes);
  577. szLongDesT = StringDupA(szLongDes);
  578. HandleError(szShortDesT, szLongDesT, dwMask, szFileNameT, szLineNumT, szEngineT, szErrCodeT, NULL, pHitObj);
  579. }
  580. }
  581. /* ============================================================================
  582. FreeNullify
  583. Frees and nullifies a ptr to memory allocated with malloc.
  584. Returns:
  585. Nothing
  586. Side effects:
  587. None
  588. */
  589. static void
  590. FreeNullify
  591. (
  592. void** pp
  593. )
  594. {
  595. if(*pp != NULL)
  596. {
  597. free(*pp);
  598. *pp = NULL;
  599. }
  600. }
  601. /* ============================================================================
  602. SmallTemplateFreeNullify
  603. Frees and nullifies a ptr to memory allocated with CTemplate::SmallMalloc.
  604. Returns:
  605. Nothing
  606. Side effects:
  607. None
  608. */
  609. static void
  610. SmallTemplateFreeNullify
  611. (
  612. void** pp
  613. )
  614. {
  615. if(*pp != NULL)
  616. {
  617. CTemplate::SmallFree(*pp);
  618. *pp = NULL;
  619. }
  620. }
  621. /* ============================================================================
  622. LargeTemplateFreeNullify
  623. Frees and nullifies a ptr to memory allocated with CTemplate::LargeMalloc.
  624. Returns:
  625. Nothing
  626. Side effects:
  627. None
  628. */
  629. static void
  630. LargeTemplateFreeNullify
  631. (
  632. void** pp
  633. )
  634. {
  635. if(*pp != NULL)
  636. {
  637. CTemplate::LargeFree(*pp);
  638. *pp = NULL;
  639. }
  640. }
  641. /* ============================================================================
  642. GetProgLangId
  643. Gets the prog lang id for a script engine
  644. Returns:
  645. Nothing
  646. Side effects:
  647. throws on error
  648. */
  649. HRESULT
  650. GetProgLangId
  651. (
  652. CByteRange& brEngine, // engine name
  653. PROGLANG_ID* pProgLangId // prog lang id (out-parameter)
  654. )
  655. {
  656. STACK_BUFFER( tempEngine, 128 );
  657. if (!tempEngine.Resize(brEngine.m_cb + 1)) {
  658. return E_OUTOFMEMORY;
  659. }
  660. LPSTR szProgLang = static_cast<LPSTR> (tempEngine.QueryPtr());
  661. strncpy(szProgLang, (LPCSTR)brEngine.m_pb, brEngine.m_cb);
  662. szProgLang[brEngine.m_cb] = '\0';
  663. return g_ScriptManager.ProgLangIdOfLangName((LPCSTR) szProgLang, pProgLangId);
  664. }
  665. /* ****************************************************************************
  666. CByteRange member functions
  667. */
  668. /* ========================================================
  669. CByteRange::Advance
  670. Advances a byte range.
  671. */
  672. void
  673. CByteRange::Advance(UINT i)
  674. {
  675. if(i >= m_cb)
  676. {
  677. Nullify();
  678. }
  679. else
  680. {
  681. m_pb += i;
  682. m_cb -= i;
  683. }
  684. }
  685. /* ========================================================
  686. CByteRange::FMatchesSz
  687. Compares a byte range with a string, case-insensitively
  688. */
  689. BOOLB
  690. CByteRange::FMatchesSz
  691. (
  692. LPCSTR psz
  693. )
  694. {
  695. if(IsNull() || (psz == NULL))
  696. return FALSE;
  697. if((ULONG)strlen(psz) != m_cb)
  698. return FALSE;
  699. return !_strnicmp((const char*)m_pb, psz, m_cb);
  700. }
  701. /* ============================================================================
  702. CByteRange::PbString
  703. Finds a case-insensitive string within a byte range
  704. Returns:
  705. Ptr to first case-insensitive occurrence of the string in this byte range;
  706. NULL if none found.
  707. Side effects:
  708. None
  709. */
  710. BYTE*
  711. CByteRange::PbString
  712. (
  713. LPSTR psz,
  714. LONG lCodePage
  715. )
  716. {
  717. UINT cch = strlen(psz);
  718. if(cch == 0)
  719. return NULL;
  720. BYTE *pbLocal = m_pb;
  721. UINT cbLocal = m_cb;
  722. char ch0 = psz[0];
  723. BYTE *pbTemp = NULL;
  724. UINT cbAdvanced = 0;
  725. if (IsCharAlpha(ch0))
  726. {
  727. // cannot use strchr
  728. while (cbLocal >= cch)
  729. {
  730. if (_strnicmp((const char *)pbLocal, psz, cch) == 0)
  731. return pbLocal;
  732. // The following code simply performs a DBCS-enabled ByteRange.Advance() action.
  733. pbTemp = pbLocal;
  734. pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1;
  735. cbAdvanced = DIFF(pbLocal - pbTemp);
  736. if (cbAdvanced >= cbLocal)
  737. {
  738. cbLocal = 0;
  739. pbLocal = NULL;
  740. }
  741. else
  742. cbLocal -= cbAdvanced;
  743. }
  744. }
  745. else
  746. {
  747. // can use strchr
  748. while (cbLocal >= cch)
  749. {
  750. pbTemp = (BYTE *)memchr(pbLocal, ch0, cbLocal);
  751. if (pbTemp == NULL)
  752. break;
  753. UINT cbOffset = DIFF(pbTemp - pbLocal);
  754. if (cbOffset >= cbLocal)
  755. break;
  756. pbLocal = pbTemp;
  757. cbLocal -= cbOffset;
  758. if (cch <= cbLocal && _strnicmp((const char *)pbLocal, psz, cch) == 0)
  759. return pbLocal;
  760. // The following code simply performs a DBCS-enabled ByteRange.Advance() action.
  761. pbTemp = pbLocal;
  762. pbLocal = *pbLocal? (BYTE *)AspCharNextA((WORD)lCodePage, (const char *)pbLocal) : pbLocal + 1;
  763. cbAdvanced = DIFF(pbLocal - pbTemp);
  764. if (cbAdvanced >= cbLocal)
  765. {
  766. cbLocal = 0;
  767. pbLocal = NULL;
  768. }
  769. else
  770. cbLocal -= cbAdvanced;
  771. }
  772. }
  773. return NULL;
  774. }
  775. /* ============================================================================
  776. CByteRange::PbOneOfAspOpenerStringTokens
  777. Finds a case-insensitive string within a byte range
  778. that matches one of the strings passed
  779. !!! WILL ONLY WORK IF THE FOLLOWING IS TRUE:
  780. 1) All the tokens start with the same charater (for example '<')
  781. 2) This character is not alpha (so that strchr() would work)
  782. !!! THE ABOVE ASSUMPTIONS MAKE THE CODE WORK FASTER
  783. Returns:
  784. Ptr to first case-insensitive occurrence of the string in this byte range;
  785. NULL if none found.
  786. *pcindex is set to the index of string found
  787. Side effects:
  788. None
  789. */
  790. BYTE*
  791. CByteRange::PbOneOfAspOpenerStringTokens
  792. (
  793. LPSTR rgszTokens[],
  794. UINT rgcchTokens[],
  795. UINT nTokens,
  796. UINT *pidToken
  797. )
  798. {
  799. if (nTokens == 0)
  800. return NULL;
  801. BYTE *pb = m_pb; // pointer to unsearched remainder of the range
  802. UINT cbRemainder = m_cb; // remaining byte range length
  803. char ch0 = rgszTokens[0][0]; // first char of every token
  804. while (cbRemainder > 0) {
  805. // BUG 82331: avoid strchr() because byte range is not null-terminated
  806. while (cbRemainder > 0 && *pb != ch0)
  807. {
  808. ++pb;
  809. --cbRemainder;
  810. }
  811. if (cbRemainder == 0)
  812. break;
  813. for (UINT i = 0; i < nTokens; i++) {
  814. if ((rgcchTokens[i] <= cbRemainder)
  815. && (rgszTokens[i] != NULL)
  816. && (_strnicmp((const char *)pb, rgszTokens[i], rgcchTokens[i]) == 0)) {
  817. *pidToken = i;
  818. return pb;
  819. }
  820. }
  821. ++pb;
  822. --cbRemainder;
  823. }
  824. return NULL;
  825. }
  826. /* ============================================================================
  827. CByteRange::FEarlierInSourceThan
  828. Does this byte range occur earlier in source than parameter byte range?
  829. Returns
  830. TRUE or FALSE
  831. Side effects
  832. None
  833. */
  834. BOOLB
  835. CByteRange::FEarlierInSourceThan(CByteRange& br)
  836. {
  837. if(br.IsNull())
  838. return TRUE;
  839. return(m_idSequence < br.m_idSequence);
  840. }
  841. /* ****************************************************************************
  842. CTemplate member functions
  843. */
  844. /* ============================================================================
  845. CTemplate::InitClass
  846. Initilaizes CTemplate static members
  847. Returns:
  848. hresult
  849. Side effects:
  850. allocates memory for static members
  851. */
  852. HRESULT
  853. CTemplate::InitClass
  854. (
  855. )
  856. {
  857. HRESULT hr = S_OK;
  858. TRY
  859. // init heaps
  860. sm_hSmallHeap = ::HeapCreate(0, 0, 0);
  861. sm_hLargeHeap = ::HeapCreate(0, 0, 0);
  862. // Init token list
  863. gm_pTokenList = new CTokenList;
  864. if (gm_pTokenList == NULL)
  865. return E_OUTOFMEMORY;
  866. gm_pTokenList->Init();
  867. CATCH(hrException)
  868. hr = hrException;
  869. END_TRY
  870. return hr;
  871. }
  872. /* ============================================================================
  873. CTemplate::UnInitClass
  874. Un-initilaizes CTemplate static members
  875. Returns:
  876. Nothing
  877. Side effects:
  878. None
  879. */
  880. void
  881. CTemplate::UnInitClass()
  882. {
  883. delete gm_pTokenList;
  884. gm_pTokenList = NULL;
  885. ::HeapDestroy(sm_hLargeHeap);
  886. if (sm_hLargeHeap != sm_hSmallHeap)
  887. ::HeapDestroy(sm_hSmallHeap);
  888. sm_hLargeHeap = sm_hSmallHeap = NULL;
  889. }
  890. /* ============================================================================
  891. CTemplate::Init
  892. Inits template in preparation for calling Compile
  893. Does the minimum needed
  894. Returns:
  895. Success or failure code
  896. Side effects:
  897. Allocates memory
  898. */
  899. HRESULT
  900. CTemplate::Init
  901. (
  902. CHitObj *pHitObj, // ptr to template's hit object
  903. BOOL fGlobalAsa, // is this the global.asa file?
  904. const CTemplateKey &rTemplateKey // hash table key
  905. )
  906. {
  907. HRESULT hr;
  908. // Create debug critical section
  909. ErrInitCriticalSection(&m_csDebuggerDetach, hr);
  910. if (FAILED(hr))
  911. return hr;
  912. // note critical section creation success
  913. m_fDebuggerDetachCSInited = TRUE;
  914. // Create event: manual-reset, ready-for-use event; non-signaled
  915. m_hEventReadyForUse = IIS_CREATE_EVENT(
  916. "CTemplate::m_hEventReadyForUse",
  917. this,
  918. TRUE, // flag for manual-reset event
  919. FALSE // flag for initial state
  920. );
  921. if (!m_hEventReadyForUse)
  922. return E_OUTOFMEMORY;
  923. // cache GlobalAsp flag
  924. m_fGlobalAsa = BOOLB(fGlobalAsa);
  925. // CIsapiReqInfo better be present
  926. if (pHitObj->PIReq() == NULL)
  927. return E_POINTER;
  928. // Initialize the template's code page
  929. m_wCodePage = pHitObj->PAppln()->QueryAppConfig()->uCodePage();
  930. m_lLCID = pHitObj->PAppln()->QueryAppConfig()->uLCID();
  931. STACK_BUFFER( serverNameBuff, 32 );
  932. STACK_BUFFER( serverPortBuff, 10 );
  933. STACK_BUFFER( portSecureBuff, 8 );
  934. DWORD cbServerName;
  935. DWORD cbServerPort;
  936. DWORD cbServerPortSecure;
  937. // Construct a URL for the application
  938. // Get the server name and port
  939. if (!SERVER_GET(pHitObj->PIReq(), "SERVER_NAME", &serverNameBuff, &cbServerName)
  940. || !SERVER_GET(pHitObj->PIReq(), "SERVER_PORT", &serverPortBuff, &cbServerPort)) {
  941. if (GetLastError() == E_OUTOFMEMORY) {
  942. hr = E_OUTOFMEMORY;
  943. }
  944. else {
  945. hr = E_FAIL;
  946. }
  947. return hr;
  948. }
  949. char *szServerPort = (char *)serverPortBuff.QueryPtr();
  950. char *szServerName = (char *)serverNameBuff.QueryPtr();
  951. BOOL fServerPortSecure = FALSE;
  952. // determine if server port is secure
  953. if (SERVER_GET(pHitObj->PIReq(), "SERVER_PORT_SECURE", &portSecureBuff, &cbServerPortSecure)) {
  954. char *szServerPortSecure = (char *)portSecureBuff.QueryPtr();
  955. fServerPortSecure = (szServerPortSecure[0] == '1');
  956. }
  957. // Get the application virtual path
  958. TCHAR szApplnVirtPath[256];
  959. if (FAILED(hr = FindApplicationPath(pHitObj->PIReq(), szApplnVirtPath, sizeof szApplnVirtPath)))
  960. return hr;
  961. TCHAR *szServerNameT;
  962. TCHAR *szServerPortT;
  963. #if UNICODE
  964. CMBCSToWChar convServer;
  965. if (FAILED(hr = convServer.Init(szServerName))) {
  966. return hr;
  967. }
  968. szServerNameT = convServer.GetString();
  969. #else
  970. szServerNameT = szServerName;
  971. #endif
  972. #if UNICODE
  973. CMBCSToWChar convPort;
  974. if (FAILED(hr = convPort.Init(szServerPort))) {
  975. return hr;
  976. }
  977. szServerPortT = convPort.GetString();
  978. #else
  979. szServerPortT = szServerPort;
  980. #endif
  981. // Allocate space for and construct the application URL
  982. m_szApplnURL = new TCHAR [(9 /* sizeof "https://:" */ + _tcslen(szServerNameT) + _tcslen(szServerPortT) + _tcslen(szApplnVirtPath) + 1)];
  983. if (m_szApplnURL == NULL)
  984. return E_OUTOFMEMORY;
  985. TCHAR *pT;
  986. // start with the protocol prefix...
  987. pT = strcpyEx(m_szApplnURL, fServerPortSecure? _T("https://") : _T("http://"));
  988. // next add the servername
  989. pT = strcpyEx(pT, szServerNameT);
  990. // next the colon between the servername and the serverport
  991. pT = strcpyEx(pT, _T(":"));
  992. // next the server port
  993. pT = strcpyEx(pT, szServerPortT);
  994. // now the applURL is built up to the appln path. The next step will be to
  995. // add the virtpath.
  996. m_szApplnVirtPath = pT;
  997. _tcscpy(m_szApplnVirtPath, szApplnVirtPath);
  998. m_LKHashKey.dwInstanceID = rTemplateKey.dwInstanceID;
  999. if ((m_LKHashKey.szPathTranslated = StringDup((TCHAR *)rTemplateKey.szPathTranslated)) == NULL)
  1000. return E_OUTOFMEMORY;
  1001. return S_OK;
  1002. }
  1003. /* ============================================================================
  1004. CTemplate::Compile
  1005. Compiles the template from its source file and include files, if any,
  1006. by calling GetSegmentsFromFile (to populate WorkStore),
  1007. followed by WriteTemplate (to create the template from WorkStore).
  1008. Returns:
  1009. HRESULT indicating success or type of failure
  1010. Side effects:
  1011. Indirectly allocates memory (via WriteTemplate)
  1012. Indirectly frees memory on error (via FreeGoodTemplateMemory)
  1013. */
  1014. HRESULT
  1015. CTemplate::Compile
  1016. (
  1017. CHitObj* pHitObj
  1018. )
  1019. {
  1020. HRESULT hr = S_OK;
  1021. // The following code moved from Init() (to make Init() lighter)
  1022. Assert(pHitObj);
  1023. // Create and Init WorkStore
  1024. if (SUCCEEDED(hr))
  1025. {
  1026. // construct the workstore - bail on fail
  1027. if(NULL == (m_pWorkStore = new CWorkStore))
  1028. hr = E_OUTOFMEMORY;
  1029. }
  1030. if (SUCCEEDED(hr))
  1031. {
  1032. hr = (m_pWorkStore->m_ScriptStore).Init(pHitObj->QueryAppConfig()->szScriptLanguage(),
  1033. pHitObj->QueryAppConfig()->pCLSIDDefaultEngine());
  1034. if (hr == TYPE_E_ELEMENTNOTFOUND)
  1035. {
  1036. // default script language in registry is bogus - send error msg to browser
  1037. HandleCTemplateError(
  1038. NULL, // source file map
  1039. NULL, // ptr to source location where error occurred
  1040. IDE_TEMPLATE_BAD_PROGLANG_IN_REGISTRY, // error message id
  1041. 0, // count of insert strings for error msg
  1042. NULL, // array of ptrs to error msg insert strings
  1043. pHitObj // Browser Request
  1044. );
  1045. }
  1046. if (FAILED(hr))
  1047. {
  1048. delete m_pWorkStore;
  1049. m_pWorkStore = NULL;
  1050. }
  1051. }
  1052. // Try to init the workstore and map main file - this can fail with oom, etc or user lacks permissions
  1053. if (SUCCEEDED(hr))
  1054. {
  1055. TRY
  1056. m_pWorkStore->Init();
  1057. AppendMapFile(
  1058. NULL, // file spec for this file - NULL means get filespec from pHitObj
  1059. NULL, // ptr to filemap of parent file
  1060. FALSE, // don't care
  1061. pHitObj, // ptr to template's hit object
  1062. m_fGlobalAsa // is this the global.asa file?
  1063. );
  1064. CATCH(hrException)
  1065. delete m_pWorkStore;
  1066. m_pWorkStore = NULL;
  1067. hr = hrException;
  1068. if(hr == E_USER_LACKS_PERMISSIONS)
  1069. HandleAccessFailure(pHitObj,
  1070. (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  1071. if (m_rgpFilemaps && m_rgpFilemaps[0])
  1072. {
  1073. // empty file will fail to map but will have a handle, so we check for it here
  1074. if (0 == GetFileSize(m_rgpFilemaps[0]->m_hFile, NULL))
  1075. hr = E_SOURCE_FILE_IS_EMPTY;
  1076. m_rgpFilemaps[0]->UnmapFile();
  1077. }
  1078. if (SUCCEEDED(hr))
  1079. hr = E_FAIL; // make sure the error is set
  1080. END_TRY
  1081. }
  1082. if (SUCCEEDED(hr))
  1083. {
  1084. Assert(m_rgpFilemaps[0]);
  1085. Assert(m_rgpFilemaps[0]->m_szPathTranslated);
  1086. Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath()))));
  1087. Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath()))));
  1088. Assert(0 < m_rgpFilemaps[0]->GetSize());
  1089. }
  1090. if (FAILED(hr))
  1091. {
  1092. m_fDontCache = TRUE;
  1093. // OK, cache HR if m_fDontCache is true
  1094. // later, another thread might find this template from the cache even if the template
  1095. // has some error and marked as DontCache.
  1096. m_hrOnNoCache = hr;
  1097. m_fReadyForUse = TRUE;
  1098. SetEvent(m_hEventReadyForUse);
  1099. return hr;
  1100. }
  1101. // End of Code moved from Init()
  1102. // By default we are not in a transaction
  1103. m_ttTransacted = ttUndefined;
  1104. // By default session is required
  1105. m_fSession = TRUE;
  1106. // By default assume script exists
  1107. m_fScriptless = FALSE;
  1108. // we assert, in effect, that template is already init'ed
  1109. Assert(FImplies(!m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->PSzCurrTemplatePhysPath()))));
  1110. Assert(FImplies(m_fGlobalAsa, (0 == _tcscmp(m_rgpFilemaps[0]->m_szPathTranslated, pHitObj->GlobalAspPath()))));
  1111. TRY
  1112. // Get source segments from source file
  1113. GetSegmentsFromFile(*(m_rgpFilemaps[0]), *m_pWorkStore, pHitObj);
  1114. /* get "language equivalents" for primary languagefrom registry
  1115. NOTE we do this here because the user can reset the primary language in the script file,
  1116. so we must wait until after GetSegmentsFromFile()
  1117. */
  1118. GetLanguageEquivalents();
  1119. // Call WriteTemplate, which writes out template components to contiguous memory,
  1120. // resulting in a compiled template
  1121. WriteTemplate(*m_pWorkStore, pHitObj);
  1122. // Calculate the # of characters in a filemap before we unmap the file for all time.
  1123. for (unsigned i = 0; i < m_cFilemaps; ++i)
  1124. m_rgpFilemaps[i]->CountChars((WORD)m_wCodePage);
  1125. // Wrap typelibs into single IDispatch*
  1126. WrapTypeLibs(pHitObj);
  1127. m_fIsValid = TRUE;
  1128. CATCH(hrException)
  1129. // NOTE: we used to free template memory here. Now we do not because if the
  1130. // error was E_USER_LACKS_PERMISSIONS, and template is in cache, we don't want
  1131. // to sabotage future requests. There's no need to decache the template.
  1132. //
  1133. // The template destructor will free this memory anyway.
  1134. //
  1135. hr = hrException;
  1136. END_TRY
  1137. // check if scriptless
  1138. if (!m_fGlobalAsa)
  1139. {
  1140. // count various stuff to make the determination
  1141. DWORD cScriptEngines = m_pWorkStore->m_ScriptStore.CountPreliminaryEngines();
  1142. DWORD cPrimaryScriptSegments = (cScriptEngines > 0) ? m_pWorkStore->m_ScriptStore.m_ppbufSegments[0]->Count() : 0;
  1143. DWORD cObjectTags = m_pWorkStore->m_ObjectInfoStore.Count();
  1144. DWORD cHtmlSegments = m_pWorkStore->m_bufHTMLSegments.Count();
  1145. DWORD c449Cookies = m_rgp449.length();
  1146. BOOL fPageCommandsPresent = m_pWorkStore->m_fPageCommandsExecuted;
  1147. if (cScriptEngines <= 1 &&
  1148. cPrimaryScriptSegments == 0 &&
  1149. cObjectTags == 0 &&
  1150. cHtmlSegments == 1 &&
  1151. c449Cookies == 0 &&
  1152. !fPageCommandsPresent)
  1153. {
  1154. m_fScriptless = TRUE;
  1155. }
  1156. }
  1157. // free working storage - no longer needed
  1158. delete m_pWorkStore;
  1159. m_pWorkStore = NULL;
  1160. // un-map filemaps - NOTE filemaps stay around for possible post-compile errors (e.g., script failure)
  1161. UnmapFiles();
  1162. // Debugging: print data structure to debugger
  1163. IF_DEBUG(SCRIPT_DEBUGGER)
  1164. {
  1165. if (SUCCEEDED(hr))
  1166. {
  1167. DBGPRINTF((DBG_CONTEXT, "Script Compiled\n"));
  1168. for (UINT i = 0; i < m_cScriptEngines; ++i)
  1169. {
  1170. char *szEngineName;
  1171. PROGLANG_ID *pProgLangID;
  1172. const wchar_t *wszScriptText;
  1173. GetScriptBlock(i, &szEngineName, &pProgLangID, &wszScriptText);
  1174. DBGPRINTF((DBG_CONTEXT, "Engine %d, Language=\"%s\":\n", i, szEngineName));
  1175. #ifndef _NO_TRACING_
  1176. DBGINFO((DBG_CONTEXT, (char *) wszScriptText));
  1177. DBGINFO((DBG_CONTEXT, "\n"));
  1178. #else
  1179. OutputDebugStringW(wszScriptText);
  1180. OutputDebugStringA("\n");
  1181. #endif
  1182. }
  1183. #if 0
  1184. OutputDebugTables();
  1185. #endif
  1186. }
  1187. }
  1188. if (hr == E_TEMPLATE_COMPILE_FAILED_DONT_CACHE)
  1189. {
  1190. m_fDontCache = TRUE;
  1191. m_hrOnNoCache = hr;
  1192. }
  1193. // Set ready-for-use flag true and event to signaled
  1194. // NOTE we do this whether success or failure, since even a failed-compile template
  1195. // will remain in the cache to allow template cache mgr to satisfy requests on it
  1196. m_fReadyForUse = TRUE;
  1197. SetEvent(m_hEventReadyForUse);
  1198. // Note whether the template currently is debuggable
  1199. // BUG BUG: Template is debuggable or not based on first app. If shared between a debug
  1200. // & non-debug app, the first application wins.
  1201. m_fDebuggable = (BOOLB)!!pHitObj->PAppln()->FDebuggable();
  1202. return hr;
  1203. }
  1204. /* ============================================================================
  1205. CTemplate::Deliver
  1206. Delivers template to caller once template is ready for use
  1207. NOTE 'compile failure' == template is 'ready for use' but did not compile successfully;
  1208. this allows cache mgr to keep a failed template in cache in case it gets requested again
  1209. Returns
  1210. success or failure
  1211. Side effects
  1212. none
  1213. */
  1214. HRESULT
  1215. CTemplate::Deliver
  1216. (
  1217. CHitObj* pHitObj
  1218. )
  1219. {
  1220. // NOTE: There was a compiler bug where 'ps' would not be correctly aligned,
  1221. // EVEN if it was declared to be a DWORD array, if 'ps' was nested in
  1222. // a block. Thus declare it here.
  1223. //
  1224. BYTE ps[SIZE_PRIVILEGE_SET]; // privilege set
  1225. HRESULT hr = S_OK;
  1226. // if ready flag is not yet set block until template is ready for use
  1227. if(!m_fReadyForUse)
  1228. {
  1229. WaitForSingleObject(m_hEventReadyForUse, INFINITE);
  1230. Assert(m_fReadyForUse); // when event unblocks, flag will be set
  1231. }
  1232. if (m_pbStart == NULL)
  1233. {
  1234. if (m_fDontCache && m_dwLastErrorMask == 0)
  1235. {
  1236. DBGPRINTF((DBG_CONTEXT, "template compile failed with %08x\n", m_hrOnNoCache));
  1237. DBG_ASSERT(FAILED(m_hrOnNoCache));
  1238. // Safety net: always fail, even if "m_hrOnNoCache" did not get set somehow.
  1239. hr = m_hrOnNoCache;
  1240. if (SUCCEEDED(m_hrOnNoCache))
  1241. hr = E_FAIL;
  1242. if(hr == E_USER_LACKS_PERMISSIONS)
  1243. HandleAccessFailure(pHitObj,
  1244. (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  1245. return hr;
  1246. }
  1247. // template compile failed - NOTE null start-of-template ptr == template compile failed
  1248. // use cached error info
  1249. SendToLog( m_dwLastErrorMask,
  1250. m_pszLastErrorInfo[ILE_szFileName],
  1251. m_pszLastErrorInfo[ILE_szLineNum],
  1252. m_pszLastErrorInfo[ILE_szEngine],
  1253. m_pszLastErrorInfo[ILE_szErrorCode],
  1254. m_pszLastErrorInfo[ILE_szShortDes],
  1255. m_pszLastErrorInfo[ILE_szLongDes],
  1256. pHitObj);
  1257. hr = E_TEMPLATE_COMPILE_FAILED;
  1258. }
  1259. else if (!pHitObj->FIsBrowserRequest())
  1260. {
  1261. return hr;
  1262. }
  1263. else if (Glob(fWinNT))
  1264. // template compile succeeded - check user's file permissions
  1265. // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project)
  1266. {
  1267. HANDLE hUserAccessToken = pHitObj->HImpersonate(); // current user's access token
  1268. DWORD dwPS = sizeof(ps); // privilege set size
  1269. DWORD dwGrantedAccess; // granted access mask
  1270. BOOL fAccessGranted; // access granted flag
  1271. GENERIC_MAPPING gm = { // generic mapping struct
  1272. FILE_GENERIC_READ,
  1273. FILE_GENERIC_WRITE,
  1274. FILE_GENERIC_EXECUTE,
  1275. FILE_ALL_ACCESS
  1276. };
  1277. ((PRIVILEGE_SET*)&ps)->PrivilegeCount = 0; // set privilege count to 0
  1278. Assert(NULL != hUserAccessToken);
  1279. for(UINT i = 0; i < m_cFilemaps; i++)
  1280. {
  1281. if(NULL == m_rgpFilemaps[i]->m_pSecurityDescriptor)
  1282. continue;
  1283. if(!AccessCheck(
  1284. m_rgpFilemaps[i]->m_pSecurityDescriptor, // pointer to security descriptor
  1285. hUserAccessToken, // handle to client access token
  1286. FILE_GENERIC_READ, // access mask to request
  1287. &gm, // address of generic-mapping structure
  1288. (PRIVILEGE_SET*)ps, // address of privilege-set structure
  1289. &dwPS, // address of size of privilege-set structure
  1290. &dwGrantedAccess, // address of granted access mask
  1291. &fAccessGranted // address of flag indicating whether access granted
  1292. ))
  1293. return E_FAIL;
  1294. if(!fAccessGranted)
  1295. {
  1296. // if access is denied on any file, handle the failure and return
  1297. HandleAccessFailure(pHitObj, m_rgpFilemaps[0]->m_szPathTranslated);
  1298. return E_USER_LACKS_PERMISSIONS;
  1299. }
  1300. }
  1301. }
  1302. // Reset the Session.CodePage to the script compilation-time codepage
  1303. // only if a code page directive was found during compilation
  1304. if (m_fCodePageSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FCodePageSet()))
  1305. {
  1306. pHitObj->SetCodePage(m_wCodePage);
  1307. }
  1308. // Reset the Session.LCID to the script compilation-time LCID
  1309. // only if an LCID directive was found during compilation
  1310. if (m_fLCIDSet && (!pHitObj->FHasSession() || !pHitObj->PSession()->FLCIDSet()))
  1311. {
  1312. pHitObj->SetLCID(m_lLCID);
  1313. }
  1314. return hr;
  1315. }
  1316. /* ============================================================================
  1317. CTemplate::CTemplate
  1318. Ctor
  1319. */
  1320. CTemplate::CTemplate()
  1321. : m_pWorkStore(NULL),
  1322. m_fGlobalAsa(FALSE),
  1323. m_fReadyForUse(FALSE),
  1324. m_fDontAttach(FALSE),
  1325. m_hEventReadyForUse(NULL),
  1326. m_fDebuggerDetachCSInited(FALSE),
  1327. m_pbStart(NULL),
  1328. m_cbTemplate(0),
  1329. m_cRefs(1), // NOTE ctor AddRefs implicitly
  1330. m_pbErrorLocation(NULL),
  1331. m_idErrMsg(0),
  1332. m_cMsgInserts(0),
  1333. m_ppszMsgInserts(NULL),
  1334. m_cScriptEngines(0),
  1335. m_rgrgSourceInfos(NULL),
  1336. m_rgpDebugScripts(NULL),
  1337. m_rgpFilemaps(NULL),
  1338. m_cFilemaps(0),
  1339. m_rgpSegmentFilemaps(NULL),
  1340. m_cSegmentFilemapSlots(0),
  1341. m_wCodePage(CP_ACP),
  1342. m_lLCID(LOCALE_SYSTEM_DEFAULT),
  1343. m_ttTransacted(ttUndefined),
  1344. m_fSession(TRUE),
  1345. m_fScriptless(FALSE),
  1346. m_fDebuggable(FALSE),
  1347. m_fIsValid(FALSE),
  1348. m_fDontCache(FALSE),
  1349. m_fZombie(FALSE),
  1350. m_fCodePageSet(FALSE),
  1351. m_fLCIDSet(FALSE),
  1352. m_fIsPersisted(FALSE),
  1353. m_szPersistTempName(NULL),
  1354. m_szApplnVirtPath(NULL),
  1355. m_szApplnURL(NULL),
  1356. m_CPTextEvents(this, IID_IDebugDocumentTextEvents),
  1357. m_pdispTypeLibWrapper(NULL),
  1358. m_dwLastErrorMask(S_OK),
  1359. m_hrOnNoCache(S_OK),
  1360. m_cbTargetOffsetPrevT(0),
  1361. m_pHashTable(NULL),
  1362. m_pServicesConfig(NULL) {
  1363. m_wCodePage = GetACP();
  1364. for (UINT i = 0; i < ILE_MAX; i++)
  1365. {
  1366. m_pszLastErrorInfo[i] = NULL;
  1367. }
  1368. IF_DEBUG(TEMPLATE)
  1369. {
  1370. WriteRefTraceLog(gm_pTraceLog, m_cRefs, this);
  1371. }
  1372. #if PER_TEMPLATE_REFLOG
  1373. m_pTraceLog = CreateRefTraceLog (100,0);
  1374. WriteRefTraceLog (m_pTraceLog,m_cRefs, this);
  1375. #endif
  1376. }
  1377. /* ============================================================================
  1378. CTemplate::~CTemplate
  1379. Destructor
  1380. Returns:
  1381. Nothing
  1382. Side effects:
  1383. None
  1384. */
  1385. CTemplate::~CTemplate()
  1386. {
  1387. DBGPRINTF(( DBG_CONTEXT, "Deleting template, m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps));
  1388. // first, remove this template from its inc-files' template lists
  1389. // NOTE must do this before freeing template memory
  1390. RemoveFromIncFiles();
  1391. // Remove the template from the debugger's list of documents
  1392. Detach();
  1393. PersistCleanup();
  1394. if(m_rgpFilemaps)
  1395. {
  1396. for(UINT i = 0; i < m_cFilemaps; i++)
  1397. delete m_rgpFilemaps[i];
  1398. SmallTemplateFreeNullify((void**) &m_rgpFilemaps);
  1399. }
  1400. FreeGoodTemplateMemory();
  1401. if (m_pWorkStore)
  1402. delete m_pWorkStore;
  1403. //FileName, LineNum, Engine, ErrorCode, ShortDes, LongDes
  1404. for(UINT iErrInfo = 0; iErrInfo < ILE_MAX; iErrInfo++)
  1405. {
  1406. FreeNullify((void**) &m_pszLastErrorInfo[iErrInfo]);
  1407. }
  1408. if(m_hEventReadyForUse != NULL)
  1409. CloseHandle(m_hEventReadyForUse);
  1410. if (m_LKHashKey.szPathTranslated)
  1411. free((void *)m_LKHashKey.szPathTranslated);
  1412. if (m_szApplnURL)
  1413. delete [] m_szApplnURL;
  1414. if (m_fDebuggerDetachCSInited)
  1415. DeleteCriticalSection(&m_csDebuggerDetach);
  1416. if (m_pdispTypeLibWrapper)
  1417. m_pdispTypeLibWrapper->Release();
  1418. if (m_szPersistTempName)
  1419. CTemplate::LargeFree(m_szPersistTempName);
  1420. if (m_pServicesConfig)
  1421. m_pServicesConfig->Release();
  1422. m_pServicesConfig = NULL;
  1423. #if PER_TEMPLATE_REFLOG
  1424. DestroyRefTraceLog (m_pTraceLog);
  1425. #endif
  1426. }
  1427. /* ============================================================================
  1428. CTemplate::QueryInterface
  1429. Provides QueryInterface implementation for CTemplate
  1430. NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo.
  1431. */
  1432. HRESULT
  1433. CTemplate::QueryInterface(const GUID &uidInterface, void **ppvObj)
  1434. {
  1435. if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider)
  1436. *ppvObj = static_cast<IDebugDocumentProvider *>(this);
  1437. else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText)
  1438. *ppvObj = static_cast<IDebugDocumentText *>(this);
  1439. else if (uidInterface == IID_IConnectionPointContainer)
  1440. *ppvObj = static_cast<IConnectionPointContainer *>(this);
  1441. else
  1442. *ppvObj = NULL;
  1443. if (*ppvObj)
  1444. {
  1445. AddRef();
  1446. return S_OK;
  1447. }
  1448. else
  1449. return E_NOINTERFACE;
  1450. }
  1451. /* ============================================================================
  1452. CTemplate::AddRef
  1453. Adds a ref to this template, thread-safely
  1454. */
  1455. ULONG
  1456. CTemplate::AddRef()
  1457. {
  1458. LONG cRefs = InterlockedIncrement(&m_cRefs);
  1459. Assert(FImplies(m_fIsValid,FImplies(cRefs > 1, m_pbStart != NULL)));
  1460. IF_DEBUG(TEMPLATE)
  1461. {
  1462. WriteRefTraceLog(gm_pTraceLog, cRefs, this);
  1463. }
  1464. #if PER_TEMPLATE_REFLOG
  1465. WriteRefTraceLog(m_pTraceLog, cRefs, this);
  1466. #endif
  1467. return cRefs;
  1468. }
  1469. /* ============================================================================
  1470. CTemplate::Release
  1471. Releases a ref to this template, thread-safely
  1472. */
  1473. ULONG
  1474. CTemplate::Release()
  1475. {
  1476. LONG cRefs = InterlockedDecrement(&m_cRefs);
  1477. IF_DEBUG(TEMPLATE)
  1478. {
  1479. WriteRefTraceLog(gm_pTraceLog, cRefs, this);
  1480. }
  1481. #if PER_TEMPLATE_REFLOG
  1482. WriteRefTraceLog(m_pTraceLog, cRefs, this);
  1483. #endif
  1484. if (cRefs == 0)
  1485. delete this;
  1486. return cRefs;
  1487. }
  1488. /* ============================================================================
  1489. CTemplate::RemoveIncFile
  1490. Removes (by setting to null) an inc-file ptr from this template's inc-file list.
  1491. Returns:
  1492. Nothing
  1493. Side effects:
  1494. None
  1495. */
  1496. void
  1497. CTemplate::RemoveIncFile
  1498. (
  1499. CIncFile* pIncFile
  1500. )
  1501. {
  1502. // If the filemap count is non-zero the pointer to
  1503. // the array of filemaps has better not be null
  1504. DBGPRINTF(( DBG_CONTEXT, "m_cFilemaps = %d, m_rgpFilemaps %p\n", m_cFilemaps, m_rgpFilemaps));
  1505. Assert((m_cFilemaps <= 0) || (m_rgpFilemaps != NULL));
  1506. // find the inc-file in list
  1507. for(UINT i = 1; (i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile != pIncFile); i++)
  1508. ;
  1509. // assert that we found the inc-file in list
  1510. Assert((i < m_cFilemaps) && (m_rgpFilemaps[i]->m_pIncFile == pIncFile));
  1511. // set inc-file ptr null
  1512. m_rgpFilemaps[i]->m_pIncFile = NULL;
  1513. }
  1514. /*===================================================================
  1515. CTemplate::FTemplateObsolete
  1516. Test to see if the files this template depends on have changed since it
  1517. was compiled.
  1518. We use this in cases where we may have missed a change notification,
  1519. for example, when there were too many changes to record in our change
  1520. notification buffer. We check the last time the file was written too,
  1521. and the security descriptor, since changes to the security descriptor
  1522. aren't noted in the file last write time.
  1523. Parameters:
  1524. None
  1525. Returns:
  1526. TRUE if the template is obsolete, else FALSE
  1527. */
  1528. BOOL CTemplate::FTemplateObsolete(VOID)
  1529. {
  1530. BOOL fStatus = FALSE;
  1531. // On Windows 95 files should not be cached
  1532. // so assume the template has changed
  1533. if (!FIsWinNT())
  1534. {
  1535. return TRUE;
  1536. }
  1537. for (UINT i = 0; i < m_cFilemaps; i++)
  1538. {
  1539. if (FFileChangedSinceCached(m_rgpFilemaps[i]->m_szPathTranslated, m_rgpFilemaps[i]->m_ftLastWriteTime))
  1540. {
  1541. // If the file write time has changed we know enough
  1542. // and can quit here
  1543. fStatus = TRUE;
  1544. break;
  1545. }
  1546. else
  1547. {
  1548. // The file hasn't been writen to, but the security descriptor may
  1549. // have chagned
  1550. // Assert on non-valid security descriptor
  1551. if (NULL != m_rgpFilemaps[i]->m_pSecurityDescriptor)
  1552. {
  1553. PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
  1554. DWORD dwSize = m_rgpFilemaps[i]->m_dwSecDescSize;
  1555. if( 0 == GetSecDescriptor(m_rgpFilemaps[i]->m_szPathTranslated, &pSecurityDescriptor, &dwSize))
  1556. {
  1557. if (pSecurityDescriptor)
  1558. {
  1559. // if the size is not the same then set fStatus to TRUE no need to compare memory blocks.
  1560. if(dwSize != GetSecurityDescriptorLength(m_rgpFilemaps[i]->m_pSecurityDescriptor))
  1561. {
  1562. fStatus = TRUE;
  1563. }
  1564. else
  1565. {
  1566. // The size of the security descriptor hasn't changed
  1567. // but we have to compare the contents to make sure they haven't changed
  1568. fStatus = !(0 == memcmp(m_rgpFilemaps[i]->m_pSecurityDescriptor, pSecurityDescriptor, dwSize));
  1569. }
  1570. // We are done with the descriptor
  1571. free(pSecurityDescriptor);
  1572. }
  1573. else
  1574. {
  1575. // Since we failed to get a security descriptor
  1576. // assume the file has changed.
  1577. fStatus = TRUE;
  1578. }
  1579. }
  1580. }
  1581. }
  1582. // Quit as soon as we find a change
  1583. if (fStatus)
  1584. {
  1585. break;
  1586. }
  1587. }
  1588. return fStatus;
  1589. }
  1590. /* ============================================================================
  1591. CTemplate::GetSourceFileName
  1592. Returns name of source file on which this template is based
  1593. Returns
  1594. source file name
  1595. Side effects
  1596. none
  1597. */
  1598. LPTSTR
  1599. CTemplate::GetSourceFileName(SOURCEPATHTYPE pathtype)
  1600. {
  1601. if (!m_rgpFilemaps)
  1602. {
  1603. return NULL;
  1604. }
  1605. switch (pathtype)
  1606. {
  1607. case SOURCEPATHTYPE_PHYSICAL:
  1608. return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathTranslated : NULL));
  1609. case SOURCEPATHTYPE_VIRTUAL:
  1610. return((m_rgpFilemaps[0] ? m_rgpFilemaps[0]->m_szPathInfo : NULL));
  1611. default:
  1612. return(NULL);
  1613. }
  1614. }
  1615. /* ============================================================================
  1616. CTemplate::Count
  1617. Returns count of components of type tcomp contained in this template
  1618. Returns:
  1619. Count of components of type tcomp
  1620. Side effects:
  1621. None
  1622. */
  1623. USHORT
  1624. CTemplate::Count
  1625. (
  1626. TEMPLATE_COMPONENT tcomp
  1627. )
  1628. {
  1629. Assert(NULL != m_pbStart);
  1630. // script engines and script blocks have the same count, stored in same slot
  1631. if(tcomp == tcompScriptEngine)
  1632. tcomp = tcompScriptBlock;
  1633. // counts are stored at start of template in sequential slots, starting with script blocks count
  1634. return * (USHORT*) ((USHORT*)m_pbStart + (tcomp - tcompScriptBlock));
  1635. }
  1636. /* ============================================================================
  1637. CTemplate::GetScriptBlock
  1638. Gets ptrs to script engine name, prog lang id and script text of i-th script block.
  1639. Returns:
  1640. Out-parameters; see below
  1641. Side effects:
  1642. None
  1643. */
  1644. void
  1645. CTemplate::GetScriptBlock
  1646. (
  1647. UINT i, // script block id
  1648. LPSTR* pszScriptEngine, // ptr to script engine name (out-parameter)
  1649. PROGLANG_ID** ppProgLangId, // ptr to prog lang id (out-parameter)
  1650. LPCOLESTR* pwstrScriptText // ptr to wstr script text (out-parameter)
  1651. )
  1652. {
  1653. CByteRange brEngine; // engine name
  1654. CByteRange brScriptText; // script text
  1655. UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned
  1656. BYTE* pbEngineInfo = GetAddress(tcompScriptEngine, (USHORT)i); // ptr to engine info
  1657. Assert(pbEngineInfo != NULL);
  1658. Assert(i < CountScriptEngines());
  1659. // Get engine name from start of engine info
  1660. ByteRangeFromPb(pbEngineInfo, brEngine);
  1661. ByteRangeFromPb(GetAddress(tcompScriptBlock, (USHORT)i), brScriptText);
  1662. Assert(!brEngine.IsNull());
  1663. Assert(!brScriptText.IsNull());
  1664. // Advance ptr past name to prog lang id
  1665. // length of prefix + length of name + NULL
  1666. pbEngineInfo += (sizeof(UINT) + (*pbEngineInfo) + 1);
  1667. // Get prog lang id - it will be on the next pointer sized boundary
  1668. cbAlignment = (UINT) (((DWORD_PTR) pbEngineInfo) % sizeof(DWORD));
  1669. if(cbAlignment > 0)
  1670. {pbEngineInfo += (sizeof(DWORD) - cbAlignment);}
  1671. *pszScriptEngine = (LPSTR)brEngine.m_pb;
  1672. *ppProgLangId = (PROGLANG_ID*)pbEngineInfo;
  1673. *pwstrScriptText = (LPCOLESTR)brScriptText.m_pb;
  1674. }
  1675. /* ============================================================================
  1676. CTemplate::GetObjectInfo
  1677. Returns i-th object-info in template as object name and
  1678. its clsid, scope, model
  1679. Returns:
  1680. HRESULT
  1681. Out-parameters; see below
  1682. Side effects:
  1683. */
  1684. HRESULT
  1685. CTemplate::GetObjectInfo
  1686. (
  1687. UINT i, // object index
  1688. LPSTR* ppszObjectName, // address of object name ptr (out-parameter)
  1689. CLSID* pClsid, // address of object clsid
  1690. CompScope* pcsScope, // address of object scope
  1691. CompModel* pcmModel // address of object threading model
  1692. )
  1693. {
  1694. BYTE* pbObjectInfo = GetAddress(tcompObjectInfo, (USHORT)i); // ptr to current read location
  1695. CByteRange brName; // object name
  1696. UINT cbAlignment; // count of bytes guid was shifted in WriteTemplate() to make it dword-aligned
  1697. Assert(i < Count(tcompObjectInfo));
  1698. // Get name from start of object-info
  1699. ByteRangeFromPb(pbObjectInfo, brName);
  1700. Assert(!brName.IsNull());
  1701. // Advance ptr past name
  1702. // length of prefix + length of name + NULL
  1703. pbObjectInfo += (sizeof(UINT) + (*pbObjectInfo) + 1);
  1704. // Get clsid - it will be on the next DWORD boundary
  1705. cbAlignment = (UINT)(((DWORD_PTR) pbObjectInfo) % sizeof(DWORD));
  1706. if(cbAlignment > 0)
  1707. pbObjectInfo += (sizeof(DWORD) - cbAlignment);
  1708. *pClsid = *(CLSID*)pbObjectInfo;
  1709. pbObjectInfo += sizeof(CLSID);
  1710. // Get scope
  1711. *pcsScope = *(CompScope*)pbObjectInfo;
  1712. pbObjectInfo += sizeof(CompScope);
  1713. // Get model
  1714. *pcmModel = *(CompModel*)pbObjectInfo;
  1715. pbObjectInfo += sizeof(CompModel);
  1716. *ppszObjectName = (LPSTR)brName.m_pb;
  1717. return S_OK;
  1718. }
  1719. /* ============================================================================
  1720. CTemplate::GetHTMLBlock
  1721. Returns i-th HTML block
  1722. Parameters:
  1723. UINT i block number
  1724. LPSTR* pszHTML [out] html text
  1725. ULONG* pcbHTML [out] html text length
  1726. ULONG* pcbSrcOffs [out] offset in the source file
  1727. LPSTR* pszSrcIncFile [out] include source file name
  1728. Returns:
  1729. Nothing
  1730. Side effects:
  1731. None
  1732. */
  1733. HRESULT
  1734. CTemplate::GetHTMLBlock
  1735. (
  1736. UINT i,
  1737. LPSTR* pszHTML,
  1738. ULONG* pcbHTML,
  1739. ULONG* pcbSrcOffs,
  1740. LPSTR* pszSrcIncFile
  1741. )
  1742. {
  1743. Assert(i < Count(tcompHTMLBlock));
  1744. // this was added due to user attempt to access the method with an invalid array offset
  1745. //
  1746. if ( i >= Count(tcompHTMLBlock) )
  1747. return E_FAIL;
  1748. // get address of the block start in template memory
  1749. BYTE *pbBlock = GetAddress(tcompHTMLBlock, (USHORT)i);
  1750. Assert(pbBlock);
  1751. // retrieve the byte range of the html code
  1752. CByteRange brHTML;
  1753. ByteRangeFromPb(pbBlock, brHTML);
  1754. *pszHTML = (LPSTR)brHTML.m_pb;
  1755. *pcbHTML = brHTML.m_cb;
  1756. // advance to the source offset
  1757. pbBlock += sizeof(ULONG); // skip prefix
  1758. pbBlock += brHTML.m_cb+1; // skip html bytes (incl. '\0')
  1759. // Add byte aligment which is done in ByteAlignOffset()
  1760. if ((reinterpret_cast<ULONG_PTR>(pbBlock)) & 3)
  1761. pbBlock = reinterpret_cast<BYTE *>((reinterpret_cast<ULONG_PTR>(pbBlock) + 4) & ~3);
  1762. *pcbSrcOffs = *((ULONG*)pbBlock);
  1763. // advance to the source name length
  1764. pbBlock += sizeof(ULONG); // skip source offset prefix
  1765. ULONG cbSrcIncFile = *((ULONG *)pbBlock); // inc file name length
  1766. pbBlock += sizeof(ULONG); // skip inc file name length
  1767. *pszSrcIncFile = (cbSrcIncFile > 0) ? (LPSTR)pbBlock : NULL;
  1768. return S_OK;
  1769. }
  1770. /* ============================================================================
  1771. CTemplate::GetScriptSourceInfo
  1772. Returns line number and source file name a given target line in a given script engine.
  1773. Returns
  1774. line number and source file name (as out-parameters)
  1775. Side effects:
  1776. None
  1777. */
  1778. void
  1779. CTemplate::GetScriptSourceInfo
  1780. (
  1781. UINT idEngine, // script engine id
  1782. int iTargetLine, // target line number
  1783. LPTSTR* pszPathInfo, // ptr to source file virtual path (out-parameter)
  1784. LPTSTR* pszPathTranslated, // ptr to source file real path (out-parameter)
  1785. ULONG* piSourceLine, // ptr to source line number (out-parameter)
  1786. ULONG* pichSourceLine, // ptr to source file offset (out-parameter)
  1787. BOOLB* pfGuessedLine // ptr to flag: did we guess the source line?
  1788. )
  1789. {
  1790. // Initialize some out parameters
  1791. if (pszPathInfo)
  1792. *pszPathInfo = _T("?"); // In case we don't ever find the path
  1793. if (pszPathTranslated)
  1794. *pszPathTranslated = _T("?"); // In case we don't ever find the path
  1795. if (piSourceLine)
  1796. *piSourceLine = 0;
  1797. if (pichSourceLine)
  1798. *pichSourceLine = 0;
  1799. if (pfGuessedLine)
  1800. *pfGuessedLine = FALSE;
  1801. if (iTargetLine <=0)
  1802. {
  1803. return;
  1804. }
  1805. // CHANGE: The rgSourceInfo array is now ZERO based. Decrement target line
  1806. // to convert.
  1807. --iTargetLine;
  1808. // CONSIDER: Make these assertions?
  1809. if(!m_rgrgSourceInfos)
  1810. return;
  1811. if(idEngine > (m_cScriptEngines - 1)) // bug 375: check vs. array bound
  1812. return;
  1813. if(size_t(iTargetLine) >= m_rgrgSourceInfos[idEngine].length()) // bug 375: check vs. array bound
  1814. return;
  1815. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  1816. // bug 379: move backwards through target lines, starting with the caller's, until we find one whose
  1817. // fIsHTML flag is false. this handles the case where vbs flags a manufactured line as in error;
  1818. // we assume the actual error occurred at the most recent authored line
  1819. while (iTargetLine >= 0 && (*prgSourceInfos)[iTargetLine].m_fIsHTML)
  1820. {
  1821. --iTargetLine;
  1822. if (pfGuessedLine)
  1823. *pfGuessedLine = TRUE;
  1824. }
  1825. if (iTargetLine >= 0)
  1826. {
  1827. if (pszPathInfo && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL)
  1828. *pszPathInfo = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathInfo;
  1829. if (pszPathTranslated && (*prgSourceInfos)[iTargetLine].m_pfilemap != NULL)
  1830. *pszPathTranslated = (*prgSourceInfos)[iTargetLine].m_pfilemap->m_szPathTranslated;
  1831. if (piSourceLine)
  1832. *piSourceLine = (*prgSourceInfos)[iTargetLine].m_idLine;
  1833. if (pichSourceLine)
  1834. *pichSourceLine = (*prgSourceInfos)[iTargetLine].m_cchSourceOffset;
  1835. }
  1836. }
  1837. /* ============================================================================
  1838. CTemplate::GetPositionOfLine
  1839. Get the character offset of a line of source
  1840. (Debugger API Extended to specify a filemap)
  1841. */
  1842. HRESULT
  1843. CTemplate::GetPositionOfLine
  1844. (
  1845. CFileMap *pFilemap,
  1846. ULONG cLineNumber,
  1847. ULONG *pcCharacterPosition
  1848. )
  1849. {
  1850. // NOTE:
  1851. // The table is not binary-searchable because include files
  1852. // will start a new line ordering
  1853. //
  1854. // Algorithm:
  1855. //
  1856. // Find the largest source line N across all engines, such that
  1857. // N <= cLineNumber and the line corresponds to an line
  1858. // in the appropriate file.
  1859. //
  1860. CSourceInfo *pSourceInfoLE = NULL;
  1861. ++cLineNumber; // Convert zero-based line # to one-based
  1862. // Find the correct offset
  1863. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  1864. {
  1865. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  1866. // Loop through all lines EXCEPT the EOF line
  1867. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  1868. {
  1869. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  1870. if (pFilemap == pSourceInfo->m_pfilemap &&
  1871. pSourceInfo->m_idLine <= cLineNumber &&
  1872. (pSourceInfoLE == NULL || pSourceInfo->m_idLine > pSourceInfoLE->m_idLine))
  1873. {
  1874. pSourceInfoLE = pSourceInfo;
  1875. }
  1876. }
  1877. }
  1878. // We had better be able to map all line numbers to offsets, unless they passed a bogus line
  1879. // (in which case we still find an offset)
  1880. //
  1881. Assert (pSourceInfoLE != NULL);
  1882. if (pSourceInfoLE == NULL) {
  1883. return E_FAIL;
  1884. }
  1885. *pcCharacterPosition = pSourceInfoLE->m_cchSourceOffset;
  1886. #if 0
  1887. IF_DEBUG(SCRIPT_DEBUGGER)
  1888. {
  1889. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  1890. GetScriptSnippets(
  1891. pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap,
  1892. 0, 0,
  1893. wszSourceText, NULL
  1894. );
  1895. DBGPRINTF((
  1896. DBG_CONTEXT,
  1897. "Source Line %d corresponds to source offset %d (Text: \"%S\")\n",
  1898. cLineNumber - 1, pSourceInfoLE->m_cchSourceOffset,
  1899. wszSourceText
  1900. ));
  1901. }
  1902. #endif
  1903. return S_OK;
  1904. }
  1905. /* ============================================================================
  1906. CTemplate::GetLineOfPosition
  1907. Get the line # & offset in line of an arbitrary character offset in source
  1908. (Debugger API Extended to specify a filemap)
  1909. */
  1910. HRESULT CTemplate::GetLineOfPosition
  1911. (
  1912. CFileMap *pFilemap,
  1913. ULONG cCharacterPosition,
  1914. ULONG *pcLineNumber,
  1915. ULONG *pcCharacterOffsetInLine
  1916. )
  1917. {
  1918. // FAIL if source offset totally off-base
  1919. if (cCharacterPosition >= pFilemap->m_cChars)
  1920. return E_FAIL;
  1921. // NOTE:
  1922. // The table is not binary-searchable because include files
  1923. // will start a new line ordering
  1924. //
  1925. // Algorithm:
  1926. //
  1927. // Find the largest source line N across all engines, such that
  1928. // N <= cLineNumber and the line corresponds to an line
  1929. // in the appropriate file.
  1930. //
  1931. CSourceInfo *pSourceInfoLE = NULL;
  1932. // Find the correct offset
  1933. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  1934. {
  1935. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  1936. // Loop through all lines EXCEPT the EOF line
  1937. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  1938. {
  1939. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  1940. if (pFilemap == pSourceInfo->m_pfilemap &&
  1941. pSourceInfo->m_cchSourceOffset <= cCharacterPosition &&
  1942. (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset))
  1943. {
  1944. pSourceInfoLE = pSourceInfo;
  1945. }
  1946. }
  1947. }
  1948. // We had better be able to map all offsets to line numbers, unless they passed a bogus offset
  1949. // (in which case we still find a line #, but may go out of range for the offset in line.
  1950. // That case is handled later)
  1951. //
  1952. Assert (pSourceInfoLE != NULL);
  1953. if (pSourceInfoLE == NULL) {
  1954. return E_FAIL;
  1955. }
  1956. *pcLineNumber = pSourceInfoLE->m_idLine - 1; // Convert to zero-based line #
  1957. *pcCharacterOffsetInLine = cCharacterPosition - pSourceInfoLE->m_cchSourceOffset;
  1958. #if 0
  1959. IF_DEBUG(SCRIPT_DEBUGGER)
  1960. {
  1961. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  1962. GetScriptSnippets(
  1963. pSourceInfoLE->m_cchSourceOffset, pSourceInfoLE->m_pfilemap,
  1964. 0, 0,
  1965. wszSourceText, NULL
  1966. );
  1967. DBGPRINTF((
  1968. DBG_CONTEXT,
  1969. "Source offset %d corresponds to source line %d (Text: \"%S\")\n",
  1970. pSourceInfoLE->m_cchSourceOffset, *pcLineNumber,
  1971. wszSourceText
  1972. ));
  1973. }
  1974. DBGPRINTF((
  1975. DBG_CONTEXT,
  1976. "Source offset %d corresponds to source line %d (Text: \"%S\")\n",
  1977. pSourceInfoLE->m_cchSourceOffset, *pcLineNumber,
  1978. wszSourceText
  1979. ));
  1980. }
  1981. #endif
  1982. return S_OK;
  1983. }
  1984. /* ============================================================================
  1985. CTemplate::GetSourceOffset
  1986. Convert a character offset relative to the target script to the appropriate
  1987. offset in the source.
  1988. NOTE: offsets in the middle of a target line are converted to the
  1989. offset relative to the beginning of source line - NOT to the
  1990. precise source offset.
  1991. this is OK because debugger ultimately wants the offset of the
  1992. beginning of line. It is a lot of work to do the precise conversion
  1993. due to the translation of "=" to Response.Write & HTML to
  1994. Response.WriteBlock
  1995. Also, because of these translations, we return the length of the segment
  1996. calculated during compilation, and throw away the length the scripting
  1997. engine sent to us.
  1998. */
  1999. void
  2000. CTemplate::GetSourceOffset
  2001. (
  2002. ULONG idEngine,
  2003. ULONG cchTargetOffset,
  2004. TCHAR **pszSourceFile,
  2005. ULONG *pcchSourceOffset,
  2006. ULONG *pcchSourceText
  2007. )
  2008. {
  2009. Assert (idEngine < m_cScriptEngines);
  2010. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2011. // Find the closest offset in the source
  2012. // This is the largest target offset N, such that N <= cchTargetOffset
  2013. CSourceInfo *pSourceInfo;
  2014. GetBracketingPair(
  2015. cchTargetOffset, // value to search for
  2016. prgSourceInfos->begin(), prgSourceInfos->end(), // array to search
  2017. CTargetOffsetOrder(), // ordering predicate
  2018. &pSourceInfo, static_cast<CSourceInfo **>(NULL) // return values
  2019. );
  2020. // Since the first offset is zero, which is less than all other conceivable offsets,
  2021. // the offset must have been found or else there is a bug.
  2022. Assert (pSourceInfo != NULL);
  2023. Assert (cchTargetOffset >= pSourceInfo->m_cchTargetOffset);
  2024. #if 0
  2025. IF_DEBUG(SCRIPT_DEBUGGER)
  2026. {
  2027. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2028. GetScriptSnippets(
  2029. pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap,
  2030. cchTargetOffset, idEngine,
  2031. wszSourceText, wszTargetText
  2032. );
  2033. DBGPRINTF((
  2034. DBG_CONTEXT,
  2035. "Target offset %d (Text: \"%S\") corresponds to source offset %d (Text: \"%S\") (Length is %d)\n",
  2036. cchTargetOffset, wszTargetText,
  2037. pSourceInfo->m_cchSourceOffset, wszSourceText,
  2038. pSourceInfo->m_cchSourceText
  2039. ));
  2040. }
  2041. #endif
  2042. *pszSourceFile = pSourceInfo->m_pfilemap->m_szPathTranslated;
  2043. *pcchSourceOffset = pSourceInfo->m_cchSourceOffset;
  2044. *pcchSourceText = pSourceInfo->m_cchSourceText;
  2045. }
  2046. /* ============================================================================
  2047. CTemplate::GetTargetOffset
  2048. Convert a character offset relative to the source script to the appropriate
  2049. offset in the target.
  2050. Returns:
  2051. TRUE - source offset corresponds to script
  2052. FALSE - source offset corresponds to HTML
  2053. NOTES:
  2054. 1. This function is very slow. consider caching the value of this function
  2055. (The CTemplateDocumentContext class does this.)
  2056. 2. This function returns the source offset in the master include file -
  2057. if the target offset corresponds to an offset in a header file, then
  2058. the offset to the #include line in the source is returned.
  2059. 3. offsets in the middle of a target line are converted to the
  2060. offset relative to the beginning of source line - NOT to the
  2061. precise source offset.
  2062. this is OK because the debugger ultimately wants the offset of the
  2063. beginning of line. It is a lot of work to do the precise conversion
  2064. due to the translation of "=" to Response.Write & HTML to
  2065. Response.WriteBlock
  2066. CONSIDER:
  2067. Figure out a better way to do this
  2068. */
  2069. BOOL CTemplate::GetTargetOffset
  2070. (
  2071. TCHAR *szSourceFile,
  2072. ULONG cchSourceOffset,
  2073. /* [out] */ ULONG *pidEngine,
  2074. /* [out] */ ULONG *pcchTargetOffset
  2075. )
  2076. {
  2077. // NOTE:
  2078. // The table is not binary-searchable because of two factors:
  2079. // 1. Include files will start a new line ordering
  2080. // 2. For engine 0, tagged scripts will be re-arranged in
  2081. // the target code to reside after all primary script in
  2082. // engine 0.
  2083. //
  2084. // Algorithm:
  2085. //
  2086. // Find the largest source offset N across all engines, such that
  2087. // N <= cchSourceOffset and the offset corresponds to an offset
  2088. // in the appropriate file.
  2089. //
  2090. CSourceInfo *pSourceInfoLE = NULL;
  2091. unsigned idEngineLE = 0;
  2092. // Find the correct offset
  2093. for (unsigned idEngine = 0; idEngine < m_cScriptEngines; ++idEngine)
  2094. {
  2095. vector<CSourceInfo> *prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  2096. // Loop through all lines EXCEPT the EOF line
  2097. for (unsigned j = 0; j < prgSourceInfos->length() - 1; ++j)
  2098. {
  2099. CSourceInfo *pSourceInfo = &(*prgSourceInfos)[j];
  2100. if (_tcscmp(pSourceInfo->m_pfilemap->m_szPathTranslated, szSourceFile) == 0 &&
  2101. pSourceInfo->m_cchSourceOffset <= cchSourceOffset &&
  2102. (pSourceInfoLE == NULL || pSourceInfo->m_cchSourceOffset > pSourceInfoLE->m_cchSourceOffset))
  2103. {
  2104. pSourceInfoLE = pSourceInfo;
  2105. idEngineLE = idEngine;
  2106. }
  2107. }
  2108. }
  2109. // There won't be a valid offset in the case where there is no
  2110. // code corresponding to the first line in the file (this only
  2111. // occurs when the first line is whitespace, because there is no
  2112. // corresponding "Response.WriteBlock" call there)
  2113. //
  2114. // In that case, return FALSE, which will cause the caller to fail
  2115. //
  2116. if (pSourceInfoLE == NULL)
  2117. {
  2118. *pidEngine = 0;
  2119. *pcchTargetOffset = 0;
  2120. return FALSE;
  2121. }
  2122. *pidEngine = idEngineLE;
  2123. *pcchTargetOffset = pSourceInfoLE->m_cchTargetOffset;
  2124. #if 0
  2125. IF_DEBUG(SCRIPT_DEBUGGER)
  2126. {
  2127. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1], wszDebugMessage[256];
  2128. GetScriptSnippets(
  2129. cchSourceOffset, pSourceInfoLE->m_pfilemap,
  2130. *pcchTargetOffset, *pidEngine,
  2131. wszSourceText, wszTargetText
  2132. );
  2133. DBGPRINTF((
  2134. DBG_CONTEXT,
  2135. "Source offset %d (Text: \"%S\") corresponds to target offset %d (Text: \"%S\")\n",
  2136. cchSourceOffset, wszSourceText,
  2137. *pcchTargetOffset, wszTargetText
  2138. ));
  2139. }
  2140. #endif
  2141. return !pSourceInfoLE->m_fIsHTML;
  2142. }
  2143. /* ============================================================================
  2144. CTemplate::GetActiveScript
  2145. Return a cached script from the template - only used in debug mode
  2146. */
  2147. CActiveScriptEngine *CTemplate::GetActiveScript(ULONG idEngine)
  2148. {
  2149. if (m_rgpDebugScripts == NULL)
  2150. return NULL;
  2151. else
  2152. {
  2153. Assert (idEngine < m_cScriptEngines);
  2154. CActiveScriptEngine *pEng = m_rgpDebugScripts[idEngine];
  2155. if (pEng)
  2156. pEng->AddRef();
  2157. return pEng;
  2158. }
  2159. }
  2160. /* ============================================================================
  2161. CTemplate::AddScript
  2162. add an active script to the template object
  2163. */
  2164. HRESULT CTemplate::AddScript(ULONG idEngine, CActiveScriptEngine *pScriptEngine)
  2165. {
  2166. if (m_rgpDebugScripts == NULL)
  2167. {
  2168. if (
  2169. (m_rgpDebugScripts = new CActiveScriptEngine *[m_cScriptEngines])
  2170. == NULL
  2171. )
  2172. {
  2173. return E_OUTOFMEMORY;
  2174. }
  2175. memset(m_rgpDebugScripts, 0, m_cScriptEngines * sizeof(CActiveScriptEngine *));
  2176. }
  2177. Assert (idEngine < m_cScriptEngines);
  2178. CActiveScriptEngine **ppScriptElem = &m_rgpDebugScripts[idEngine];
  2179. if (*ppScriptElem != NULL)
  2180. (*ppScriptElem)->Release();
  2181. *ppScriptElem = pScriptEngine;
  2182. pScriptEngine->AddRef();
  2183. // Initialize the script engine now (is currently uninitialized)
  2184. // so that the debugger user can set breakpoints.
  2185. IActiveScript *pActiveScript = pScriptEngine->GetActiveScript();
  2186. HRESULT hr;
  2187. TRY
  2188. hr = pActiveScript->SetScriptSite(static_cast<IActiveScriptSite *>(pScriptEngine));
  2189. CATCH(nExcept)
  2190. HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF,
  2191. NULL,
  2192. TRUE,
  2193. nExcept,
  2194. "IActiveScript::SetScriptSite()",
  2195. "CTemplate::AddScript()");
  2196. hr = nExcept;
  2197. END_TRY
  2198. if (FAILED(hr))
  2199. {
  2200. *ppScriptElem = NULL;
  2201. return E_FAIL;
  2202. }
  2203. TRY
  2204. hr = pActiveScript->SetScriptState(SCRIPTSTATE_INITIALIZED);
  2205. CATCH(nExcept)
  2206. HandleErrorMissingFilename(IDE_SCRIPT_ENGINE_GPF,
  2207. NULL,
  2208. TRUE,
  2209. nExcept,
  2210. "IActiveScript::SetScriptState()",
  2211. "CTemplate::AddScript()");
  2212. hr = nExcept;
  2213. END_TRY
  2214. if (FAILED(hr))
  2215. return E_FAIL;
  2216. return S_OK;
  2217. }
  2218. /* ============================================================================
  2219. CTemplate::AppendMapFile
  2220. Appends a filemap to the workstore and memory-maps its file
  2221. Returns:
  2222. Nothing
  2223. Side effects:
  2224. Allocates memory; throws exception on error
  2225. */
  2226. void
  2227. CTemplate::AppendMapFile
  2228. (
  2229. LPCTSTR szFileSpec, // file spec for this file
  2230. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  2231. BOOLB fVirtual, // is file spec virtual or relative?
  2232. CHitObj* pHitObj, // ptr to template's hit object
  2233. BOOLB fGlobalAsa // is this file the global.asa file?
  2234. )
  2235. {
  2236. // alloc or realloc as needed
  2237. if(m_cFilemaps++ == 0)
  2238. m_rgpFilemaps = (CFileMap**) CTemplate::SmallMalloc(sizeof(CFileMap*));
  2239. else
  2240. m_rgpFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpFilemaps, m_cFilemaps * sizeof(CFileMap*));
  2241. if(NULL == m_rgpFilemaps)
  2242. THROW(E_OUTOFMEMORY);
  2243. if(NULL == (m_rgpFilemaps[m_cFilemaps - 1] = new CFileMap))
  2244. THROW(E_OUTOFMEMORY);
  2245. // map the file
  2246. m_rgpFilemaps[m_cFilemaps - 1]->MapFile(
  2247. szFileSpec,
  2248. m_szApplnVirtPath,
  2249. pfilemapCurrent,
  2250. fVirtual,
  2251. pHitObj,
  2252. fGlobalAsa
  2253. );
  2254. }
  2255. /* ============================================================================
  2256. CTemplate::GetSegmentsFromFile
  2257. Gets source segments from a source file by calling ExtractAndProcessSegment
  2258. until there are no more segments; populates WorkStore with info on source segments.
  2259. Returns:
  2260. Nothing
  2261. Side effects:
  2262. None
  2263. */
  2264. void
  2265. CTemplate::GetSegmentsFromFile
  2266. (
  2267. CFileMap& filemap, // this file's file map
  2268. CWorkStore& WorkStore, // working storage for source segments
  2269. CHitObj* pHitObj, // Browser request object
  2270. BOOL fIsHTML
  2271. )
  2272. {
  2273. CByteRange brSearch; // byte range to search for source segments
  2274. _TOKEN rgtknOpeners[TOKEN_OPENERS_MAX]; // array of permitted open tokens
  2275. UINT ctknOpeners; // count of permitted open tokens
  2276. SOURCE_SEGMENT ssegThisFile = ssegHTML; // Either HTML or <SCRIPT> segment
  2277. BOOL fPrevCodePageSet = FALSE;
  2278. UINT wPrevCodePage;
  2279. // init search range to all of file - NOTE we ignore high dword of file size
  2280. brSearch.m_pb = filemap.m_pbStartOfFile;
  2281. brSearch.m_cb = filemap.GetSize();
  2282. if (fIsHTML)
  2283. {
  2284. // populate array of permitted open tokens
  2285. ctknOpeners = 4;
  2286. rgtknOpeners[0] = CTokenList::tknOpenPrimaryScript;
  2287. rgtknOpeners[1] = CTokenList::tknOpenTaggedScript;
  2288. rgtknOpeners[2] = CTokenList::tknOpenObject;
  2289. rgtknOpeners[3] = CTokenList::tknOpenHTMLComment;
  2290. }
  2291. else
  2292. {
  2293. ctknOpeners = 1;
  2294. rgtknOpeners[0] = CTokenList::tknOpenHTMLComment;
  2295. ssegThisFile = ssegTaggedScript;
  2296. }
  2297. TRY
  2298. if ((brSearch.m_cb >= 2)
  2299. && (((brSearch.m_pb[0] == 0xff) && (brSearch.m_pb[1] == 0xfe))
  2300. || ((brSearch.m_pb[0] == 0xfe) && (brSearch.m_pb[1] == 0xff)))) {
  2301. ThrowError(brSearch.m_pb,IDE_TEMPLATE_UNICODE_NOTSUP);
  2302. return;
  2303. }
  2304. // check for the UTF-8 BOM mark. If present, then treat this similar to
  2305. // seeing @CODEPAGE=65001. Note that previous values are retained in the
  2306. // event that there are differing @CODEPAGE settings. This probably should
  2307. // be an error in itself, but I can imagine that this might break a lot of
  2308. // apps as more and more UTF8 files are put into use.
  2309. if ((brSearch.m_cb >= 3)
  2310. && (brSearch.m_pb[0] == 0xEF)
  2311. && (brSearch.m_pb[1] == 0xBB)
  2312. && (brSearch.m_pb[2] == 0xBF)) {
  2313. pHitObj->SetCodePage(65001);
  2314. fPrevCodePageSet = m_fCodePageSet;
  2315. wPrevCodePage = m_wCodePage;
  2316. m_fCodePageSet = TRUE;
  2317. m_wCodePage = 65001;
  2318. brSearch.Advance(3);
  2319. }
  2320. // Process source segments until we run out of them, i.e. until search segment is empty
  2321. // NOTE we pass current filemap as 'parent file' to ExtractAndProcessSegment
  2322. // NOTE ExtractAndProcessSegment appends source segments to WorkStore, advancing brSearch as it goes
  2323. while(!brSearch.IsNull())
  2324. ExtractAndProcessSegment(
  2325. brSearch,
  2326. ssegThisFile,
  2327. rgtknOpeners,
  2328. ctknOpeners,
  2329. &filemap,
  2330. WorkStore,
  2331. pHitObj,
  2332. ssegThisFile == ssegTaggedScript,
  2333. fIsHTML
  2334. );
  2335. CATCH(hrException)
  2336. /*
  2337. NOTE we indicate 'generic error' by m_idErrMsg == 0; this happens as we move
  2338. up the 'include file stack' after processing a specific error (m_idErrMsg != 0).
  2339. Only the specific error is processed; generic error, we simply re-throw exception.
  2340. */
  2341. if(m_idErrMsg != 0)
  2342. {
  2343. // process specific error
  2344. ProcessSpecificError(filemap, pHitObj);
  2345. // reset err message so next msg will be generic as we move up the stack
  2346. m_idErrMsg = 0;
  2347. }
  2348. THROW(hrException);
  2349. END_TRY
  2350. if (fPrevCodePageSet){
  2351. m_wCodePage = wPrevCodePage;
  2352. pHitObj->SetCodePage(wPrevCodePage);
  2353. }
  2354. }
  2355. #define SZ_REG_LANGUAGE_ENGINES "SYSTEM\\CurrentControlSet\\Services\\W3SVC\\ASP\\LanguageEngines\\"
  2356. /* ============================================================================
  2357. CTemplate::GetLanguageEquivalents
  2358. Gets the "Write", "WriteBlock", etc. equivalents from registry for primary scripting language
  2359. Returns
  2360. Nothing
  2361. Side effects
  2362. Throws on error
  2363. */
  2364. void
  2365. CTemplate::GetLanguageEquivalents
  2366. (
  2367. )
  2368. {
  2369. CByteRange brPrimaryEngine;
  2370. m_pWorkStore->m_ScriptStore.m_bufEngineNames.GetItem(0, brPrimaryEngine); // 0-th engine is primary
  2371. #if DBG
  2372. /* DEBUG ONLY - to test the reg lookup code you must:
  2373. 1) create the key HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W3SVC\ASP\LanguageEngines\Debug
  2374. 2) put the language reg values for WriteBlock and Write under the language you want to test
  2375. */
  2376. HANDLE hKey;
  2377. if(ERROR_SUCCESS != RegOpenKeyExA(
  2378. HKEY_LOCAL_MACHINE,
  2379. "SYSTEM\\CurrentControlSet\\Services\\W3SVC\\ASp\\LanguageEngines\\Debug",
  2380. 0,
  2381. KEY_READ,
  2382. (PHKEY)&hKey
  2383. ))
  2384. {
  2385. return;
  2386. }
  2387. RegCloseKey((HKEY)hKey);
  2388. #else
  2389. // if the primary language is one of the big two, return; we don't need to look up equivalents
  2390. if(brPrimaryEngine.FMatchesSz("VBScript"))
  2391. return;
  2392. if(brPrimaryEngine.FMatchesSz("JScript"))
  2393. return;
  2394. if(brPrimaryEngine.FMatchesSz("JavaScript"))
  2395. return;
  2396. if(brPrimaryEngine.FMatchesSz("LiveScript"))
  2397. return;
  2398. #endif // DBG
  2399. /* query the registry; language equivalents are stored in:
  2400. HKEY_LOCAL_MACHINE
  2401. key: SYSTEM
  2402. key: CurrentControlSet
  2403. key: Services
  2404. key: W3SVC
  2405. key: ASP
  2406. key: LanguageEngines
  2407. key: <LanguageName>
  2408. value: Write data: <replacement syntax for Response.Write(|)>
  2409. value: WriteBlock data: <replacement syntax for Response.WriteBlock(|)>
  2410. */
  2411. STACK_BUFFER( tempRegKeyPath, 512 );
  2412. UINT cchRegKeyPath = strlen(SZ_REG_LANGUAGE_ENGINES);
  2413. if (!tempRegKeyPath.Resize(cchRegKeyPath + brPrimaryEngine.m_cb + 1)) {
  2414. SetLastError(E_OUTOFMEMORY);
  2415. return;
  2416. }
  2417. LPSTR szRegKeyPath = static_cast<LPSTR> (tempRegKeyPath.QueryPtr());
  2418. LPSTR pch = szRegKeyPath;
  2419. strcpy(pch, SZ_REG_LANGUAGE_ENGINES);
  2420. pch += cchRegKeyPath;
  2421. strncpy(pch, (const char *) brPrimaryEngine.m_pb, brPrimaryEngine.m_cb);
  2422. pch += brPrimaryEngine.m_cb;
  2423. *pch = '\0';
  2424. HANDLE hKeyScriptLanguage; // handle of script language reg key
  2425. if(ERROR_SUCCESS == RegOpenKeyExA(
  2426. HKEY_LOCAL_MACHINE, // handle constant
  2427. (const char*) szRegKeyPath, // LPCSTR lpSubKey subkey to open
  2428. 0, // DWORD ulOptions reserved; must be zero
  2429. KEY_QUERY_VALUE, // REGSAM samDesired security access mask
  2430. (PHKEY) &hKeyScriptLanguage // PHKEY phkResult address of handle of open key
  2431. ))
  2432. {
  2433. SetLanguageEquivalent(hKeyScriptLanguage, "Write", &(m_pWorkStore->m_szWriteOpen), &(m_pWorkStore->m_szWriteClose));
  2434. SetLanguageEquivalent(hKeyScriptLanguage, "WriteBlock", &(m_pWorkStore->m_szWriteBlockOpen), &(m_pWorkStore->m_szWriteBlockClose));
  2435. RegCloseKey((HKEY) hKeyScriptLanguage);
  2436. }
  2437. }
  2438. /* ============================================================================
  2439. CTemplate::SetLanguageEquivalent
  2440. Sets a "language equivalent" from the registry.
  2441. Returns:
  2442. language item opener and closer as out-parameters
  2443. Ex: "Response.Write(" and ")"
  2444. Side effects:
  2445. Throws on error
  2446. */
  2447. void
  2448. CTemplate::SetLanguageEquivalent
  2449. (
  2450. HANDLE hKeyScriptLanguage, // reg key
  2451. LPSTR szLanguageItem, // reg value name - "Write", "WriteBlock", etc.
  2452. LPSTR* pszOpen, // ptr to language item opener, e.g. "Response.Write(" (out-parameter)
  2453. LPSTR* pszClose // ptr to language item closer, e.g. ")" (out-parameter)
  2454. )
  2455. {
  2456. LONG lError;
  2457. DWORD cbSyntax;
  2458. LPSTR szSyntax;
  2459. char* pchInsert;
  2460. UINT cchOpen;
  2461. UINT cchClose;
  2462. // query registry to get buffer size
  2463. lError = RegQueryValueExA(
  2464. (HKEY) hKeyScriptLanguage, // handle of key to query
  2465. szLanguageItem, // name of value to query
  2466. NULL, // reserved; must be NULL
  2467. NULL, // ptr to value type; not required
  2468. NULL, // ptr to data buffer
  2469. &cbSyntax // ptr to data buffer size
  2470. );
  2471. if(ERROR_FILE_NOT_FOUND == lError)
  2472. // if we don't find szLanguageItem in registry, return silently, leaving *pszOpen and *pszClose unchanged
  2473. return;
  2474. else if((ERROR_MORE_DATA != lError) && (ERROR_SUCCESS != lError))
  2475. THROW(lError);
  2476. Assert(cbSyntax > 0);
  2477. // allocate buffer and re-query registry to get syntax string
  2478. // NOTE RegQueryValueEx returns cbSyntax that includes room for '\0' terminator
  2479. STACK_BUFFER(tempSyntax, 64);
  2480. if (!tempSyntax.Resize(cbSyntax)) {
  2481. THROW(E_OUTOFMEMORY);
  2482. }
  2483. szSyntax = static_cast<LPSTR> (tempSyntax.QueryPtr());
  2484. lError = RegQueryValueExA(
  2485. (HKEY) hKeyScriptLanguage, // handle of key to query
  2486. szLanguageItem, // name of value to query
  2487. NULL, // reserved; must be NULL
  2488. NULL, // ptr to value type; not required
  2489. (LPBYTE) szSyntax, // ptr to data buffer
  2490. &cbSyntax // ptr to data buffer size
  2491. );
  2492. /* NOTE there is the slight possibility of ERROR_FILE_NOT_FOUND or ERROR_MORE_DATA
  2493. if the registry value was deleted or changed between the first and second calls to RegQueryValueEx.
  2494. Since this occurs with vanishingly small probability, we throw (instead of coding the re-try logic).
  2495. */
  2496. if(ERROR_SUCCESS != lError)
  2497. THROW(lError);
  2498. pchInsert = szSyntax;
  2499. while(*pchInsert != '|' && *pchInsert != '\0')
  2500. pchInsert++;
  2501. cchOpen = DIFF(pchInsert - szSyntax);
  2502. cchClose = *pchInsert == '|'
  2503. ? cbSyntax - cchOpen - 2 // found insert symbol: deduct 2 chars, 1 for insert symbol, 1 for '\0'
  2504. : cbSyntax - cchOpen - 1; // didn't find insert symbol: deduct 1 char for '\0'
  2505. Assert(FImplies(cchOpen == 0, *szSyntax == '|'));
  2506. Assert(FImplies(*pchInsert == '\0', cchClose == 0));
  2507. if(cchOpen == 0)
  2508. // opener is empty - set caller's opener ptr null
  2509. *pszOpen = NULL;
  2510. else if(cchOpen > 0)
  2511. {
  2512. // opener is non-empty - set caller's opener to opener in registry
  2513. if(NULL == (*pszOpen = (LPSTR) CTemplate::SmallMalloc(cchOpen + 1)))
  2514. THROW(E_OUTOFMEMORY);
  2515. strncpy(*pszOpen, szSyntax, cchOpen);
  2516. (*pszOpen)[cchOpen] = '\0';
  2517. }
  2518. if(cchClose == 0)
  2519. // closer is empty - set caller's closer ptr null
  2520. *pszClose = NULL;
  2521. else if(cchClose > 0)
  2522. {
  2523. // closer is non-empty - set caller's closer to closer in registry
  2524. if(NULL == (*pszClose = (LPSTR) CTemplate::SmallMalloc(cchClose + 1)))
  2525. THROW(E_OUTOFMEMORY);
  2526. strncpy(*pszClose, (pchInsert + 1), cchClose);
  2527. (*pszClose)[cchClose] = '\0';
  2528. }
  2529. }
  2530. /* ============================================================================
  2531. CTemplate::ThrowError
  2532. Sets up for processing a compile failure.
  2533. Returns:
  2534. Nothing
  2535. Side effects:
  2536. Throws error
  2537. */
  2538. void
  2539. CTemplate::ThrowError
  2540. (
  2541. BYTE* pbErrorLocation, // ptr to error location in source file
  2542. UINT idErrMsg // error id
  2543. )
  2544. {
  2545. m_pbErrorLocation = pbErrorLocation;
  2546. m_idErrMsg = idErrMsg;
  2547. // bug 80745: always throw compile-failed-don't-cache
  2548. THROW(E_TEMPLATE_COMPILE_FAILED_DONT_CACHE);
  2549. }
  2550. /* ============================================================================
  2551. CTemplate::AppendErrorMessageInsert
  2552. Appends an error message insert to member array.
  2553. Returns:
  2554. Nothing
  2555. Side effects:
  2556. Appends to inserts array
  2557. */
  2558. void
  2559. CTemplate::AppendErrorMessageInsert
  2560. (
  2561. BYTE* pbInsert, // ptr to insert
  2562. UINT cbInsert // length of insert
  2563. )
  2564. {
  2565. if (m_ppszMsgInserts == NULL)
  2566. {
  2567. m_ppszMsgInserts = new char*;
  2568. m_cMsgInserts = 0;
  2569. if (m_ppszMsgInserts == NULL)
  2570. return;
  2571. }
  2572. m_ppszMsgInserts[m_cMsgInserts] = new char[cbInsert + 1];
  2573. if (m_ppszMsgInserts[m_cMsgInserts] == NULL)
  2574. return;
  2575. strncpy(m_ppszMsgInserts[m_cMsgInserts], (const char*)pbInsert, cbInsert);
  2576. m_ppszMsgInserts[m_cMsgInserts++][cbInsert] = NULL;
  2577. }
  2578. /* ============================================================================
  2579. CTemplate::ThrowErrorSingleInsert
  2580. Appends a single message insert to member array and throws a compile error.
  2581. Returns:
  2582. Nothing
  2583. Side effects:
  2584. Throws error indirectly
  2585. */
  2586. void
  2587. CTemplate::ThrowErrorSingleInsert
  2588. (
  2589. BYTE* pbErrorLocation, // ptr to error location in source file
  2590. UINT idErrMsg, // error id
  2591. BYTE* pbInsert, // ptr to insert
  2592. UINT cbInsert // length of insert
  2593. )
  2594. {
  2595. AppendErrorMessageInsert(pbInsert, cbInsert);
  2596. ThrowError(pbErrorLocation, idErrMsg);
  2597. }
  2598. /* ============================================================================
  2599. CTemplate::ProcessSpecificError
  2600. Processes a specific compile failure.
  2601. Returns:
  2602. Nothing
  2603. Side effects:
  2604. None
  2605. */
  2606. void
  2607. CTemplate::ProcessSpecificError
  2608. (
  2609. CFileMap& filemap, // source file map
  2610. CHitObj* pHitObj // Browser request object
  2611. )
  2612. {
  2613. // no error msg for generic failures
  2614. if(m_idErrMsg == E_FAIL || m_idErrMsg == E_OUTOFMEMORY)
  2615. return;
  2616. HandleCTemplateError(
  2617. &filemap,
  2618. m_pbErrorLocation,
  2619. m_idErrMsg,
  2620. m_cMsgInserts,
  2621. m_ppszMsgInserts,
  2622. pHitObj
  2623. );
  2624. }
  2625. /* ============================================================================
  2626. CTemplate::ShowErrorInDebugger
  2627. Display a runtime error by invoking the JIT debugger
  2628. Returns:
  2629. failure if debugger won't start
  2630. Side effects:
  2631. None.
  2632. */
  2633. HRESULT
  2634. CTemplate::ShowErrorInDebugger
  2635. (
  2636. CFileMap* pfilemap,
  2637. UINT cchErrorLocation,
  2638. char* szDescription,
  2639. CHitObj *pHitObj,
  2640. BOOL fAttachDocument
  2641. )
  2642. {
  2643. HRESULT hr = S_OK;
  2644. char szDebugTitle[64];
  2645. if (pfilemap == NULL || szDescription == NULL || pHitObj == NULL)
  2646. return E_POINTER;
  2647. // Create a new document context for this statement
  2648. // CONSIDER: character count that we return is bogus - however our debugging
  2649. // client (Caesar's) does not use this information anyway.
  2650. //
  2651. CTemplateDocumentContext *pDebugContext = new CTemplateDocumentContext(this, cchErrorLocation, 1);
  2652. if (pDebugContext == NULL)
  2653. return E_OUTOFMEMORY;
  2654. // Make sure debug document is attached to debugger
  2655. if (fAttachDocument)
  2656. AttachTo(pHitObj->PAppln());
  2657. // Yes it does, bring up the debugger on this line
  2658. hr = InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_UI_BRING_DOC_CONTEXT_TO_TOP, pDebugContext);
  2659. if (FAILED(hr))
  2660. goto LExit;
  2661. // Load the compiler message string
  2662. CchLoadStringOfId(IDE_TEMPLATE_ERRMSG_TITLE, szDebugTitle, sizeof szDebugTitle);
  2663. // pop up a message box with the error description
  2664. MessageBoxA(NULL, szDescription, szDebugTitle, MB_SERVICE_NOTIFICATION | MB_TOPMOST | MB_OK | MB_ICONEXCLAMATION);
  2665. LExit:
  2666. if (pDebugContext)
  2667. pDebugContext->Release();
  2668. return hr;
  2669. }
  2670. /* ============================================================================
  2671. CTemplate::HandleCTemplateError
  2672. Handles template compilation errors
  2673. Returns:
  2674. Nothing
  2675. Side effects:
  2676. None.
  2677. */
  2678. void
  2679. CTemplate::HandleCTemplateError
  2680. (
  2681. CFileMap* pfilemap, // ptr to source file map
  2682. BYTE* pbErrorLocation, // ptr to source location where error occurred
  2683. UINT idErrMsg, // error message id
  2684. UINT cMsgInserts, // count of insert strings for error msg
  2685. char** ppszMsgInserts, // array of ptrs to error msg insert strings
  2686. CHitObj* pHitObj // Browser Request
  2687. )
  2688. {
  2689. char szErrMsgPattern[MAX_RESSTRINGSIZE]; // error message pattern
  2690. CHAR szLineNum[12];
  2691. TCHAR szFileName[512];
  2692. CHAR szShortDes[256];
  2693. CHAR szEngine[256];
  2694. CHAR szErrCode[20];
  2695. CHAR szLongDes[MAX_RESSTRINGSIZE];
  2696. CHAR szCombinedDes[sizeof szShortDes + sizeof szLongDes]; // long & short desc
  2697. DWORD dwMask;
  2698. UINT cch;
  2699. // if request ptr or ecb ptr is null, bail; we won't be able to write error msg anyway
  2700. if(pHitObj == NULL)
  2701. return;
  2702. /* if this was a security error, process it specially and bail
  2703. NOTE security error causes exception, rather than true error id
  2704. NOTE template will be destroyed anyway in this case, so no need to maintain m_pszLastErrorMessage
  2705. */
  2706. if(idErrMsg == E_USER_LACKS_PERMISSIONS)
  2707. {
  2708. Assert(cMsgInserts == 1);
  2709. HandleAccessFailure(pHitObj,
  2710. (m_rgpFilemaps && m_rgpFilemaps[0])? m_rgpFilemaps[0]->m_szPathTranslated : NULL);
  2711. return;
  2712. }
  2713. // get error resource message
  2714. LoadErrResString(idErrMsg, &dwMask, szErrCode, szShortDes, szLongDes);
  2715. // if we have a specific error location, construct msg prefix
  2716. if(pbErrorLocation != NULL) {
  2717. Assert(pfilemap != NULL);
  2718. // get line number of error location as string
  2719. _itoa(SourceLineNumberFromPb(pfilemap, pbErrorLocation), szLineNum, 10);
  2720. }
  2721. else {
  2722. szLineNum[0] = NULL;
  2723. }
  2724. if(pfilemap != NULL) {
  2725. cch = _tcslen(pfilemap->m_szPathInfo);
  2726. _tcsncpy(szFileName, pfilemap->m_szPathInfo, cch);
  2727. }
  2728. else {
  2729. cch = 0;
  2730. }
  2731. szFileName[cch] = '\0';
  2732. //Load Default Engine from resource
  2733. cch = CchLoadStringOfId(IDS_ENGINE, szEngine, sizeof szEngine);
  2734. szEngine[cch] = '\0';
  2735. // resolve error msg pattern and inserts into actual error msg
  2736. cch = strlen(szLongDes);
  2737. memcpy(szErrMsgPattern, szLongDes, cch);
  2738. szErrMsgPattern[cch] = '\0';
  2739. // get an idea of the possibility of a buffer overrunn
  2740. UINT dwTotalLen=0;
  2741. BOOL fTooBig = FALSE;
  2742. if (cMsgInserts) {
  2743. // allow 32 characters for space, etc.
  2744. dwTotalLen = 32 + strlen(szErrMsgPattern);
  2745. for (UINT i = 0; i < cMsgInserts; i++)
  2746. dwTotalLen += strlen(ppszMsgInserts[i]);
  2747. if (dwTotalLen > sizeof szLongDes) {
  2748. cch = CchLoadStringOfId(IDE_TOOBIG, szLongDes, sizeof szLongDes);
  2749. szLongDes[cch] = '\0';
  2750. fTooBig = TRUE;
  2751. }
  2752. }
  2753. if (!fTooBig)
  2754. GetSzFromPatternInserts(szErrMsgPattern, cMsgInserts, ppszMsgInserts, szLongDes);
  2755. // attempt to bring up debugger to display the error - if we cannot then log the error
  2756. /* Find the character offset closest to cbErrorLocation. This will be
  2757. * the place where we start looping with CharNext() to get the full
  2758. * character offset.
  2759. *
  2760. * NOTE: compilation is done in two phases.
  2761. * Errors are detected and reported in phase 1.
  2762. * The DBCS mapping is created in phase 2.
  2763. *
  2764. * Therefore, we don't have the benefit of the rgByte2DBCS table
  2765. * because it doesn't exist yet. Therefore we are left with a SLOW
  2766. * loop starting at BOF. To make things not so abysmal, we don't
  2767. * do the loop on SBCS charsets. We also don't do this conversion
  2768. * unless debugging is enabled.
  2769. */
  2770. if (FCaesars() && pHitObj->PAppln()->FDebuggable()) {
  2771. unsigned cchErrorLocation = CharAdvDBCS(
  2772. (WORD)m_wCodePage,
  2773. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile),
  2774. reinterpret_cast<char *>(pbErrorLocation),
  2775. INFINITE,
  2776. NULL);
  2777. // Create the description string
  2778. char *szEnd = strcpyExA(szCombinedDes, szShortDes);
  2779. *szEnd++ = '\n';
  2780. *szEnd++ = '\n';
  2781. strcpy(szEnd, szLongDes);
  2782. ShowErrorInDebugger(pfilemap, cchErrorLocation, szCombinedDes, pHitObj, idErrMsg != IDE_TEMPLATE_CYCLIC_INCLUDE);
  2783. }
  2784. //cache the info in case we need to use later.
  2785. m_dwLastErrorMask = dwMask;
  2786. //delay NULL check to caller who use this info.
  2787. #if UNICODE
  2788. m_pszLastErrorInfo[ILE_szFileName] = StringDupUTF8(szFileName);
  2789. #else
  2790. m_pszLastErrorInfo[ILE_szFileName] = StringDupA(szFileName);
  2791. #endif
  2792. m_pszLastErrorInfo[ILE_szLineNum] = StringDupA(szLineNum);
  2793. m_pszLastErrorInfo[ILE_szEngine] = StringDupA(szEngine);
  2794. m_pszLastErrorInfo[ILE_szErrorCode] = StringDupA(szErrCode);
  2795. m_pszLastErrorInfo[ILE_szShortDes] = StringDupA(szShortDes);
  2796. m_pszLastErrorInfo[ILE_szLongDes] = StringDupA(szLongDes);
  2797. SendToLog( m_dwLastErrorMask,
  2798. m_pszLastErrorInfo[ILE_szFileName],
  2799. m_pszLastErrorInfo[ILE_szLineNum],
  2800. m_pszLastErrorInfo[ILE_szEngine],
  2801. m_pszLastErrorInfo[ILE_szErrorCode],
  2802. m_pszLastErrorInfo[ILE_szShortDes],
  2803. m_pszLastErrorInfo[ILE_szLongDes],
  2804. pHitObj);
  2805. }
  2806. /* ============================================================================
  2807. CTemplate::FreeGoodTemplateMemory
  2808. Frees memory allocated for a 'good' (successfully compiled) template.
  2809. This includes the template itself, memory to support compile-time errors
  2810. (since the entire concatenated compile-time error message is cached in
  2811. last-err-msg member), and memory to support run-time errors (since if the
  2812. template didn't compile, it can't run).
  2813. Returns
  2814. Nothing
  2815. Side effects
  2816. None
  2817. */
  2818. void
  2819. CTemplate::FreeGoodTemplateMemory
  2820. (
  2821. )
  2822. {
  2823. UINT i;
  2824. LargeTemplateFreeNullify((void**) &m_pbStart);
  2825. SmallTemplateFreeNullify((void**) &m_rgpSegmentFilemaps);
  2826. delete[] m_rgrgSourceInfos;
  2827. m_rgrgSourceInfos = NULL;
  2828. if(m_ppszMsgInserts)
  2829. {
  2830. for(i = 0; i < m_cMsgInserts; i++)
  2831. delete m_ppszMsgInserts[i];
  2832. delete m_ppszMsgInserts;
  2833. m_ppszMsgInserts = NULL;
  2834. }
  2835. // release the collected type libs
  2836. ReleaseTypeLibs();
  2837. // release any 449-echo-cookie objects
  2838. Release449();
  2839. }
  2840. /* ============================================================================
  2841. CTemplate::UnmapFiles
  2842. Unmaps the template's filemaps.
  2843. NOTE: we keep filemap objects around so that filenames will be available for runtime errors
  2844. Returns
  2845. Nothing
  2846. Side effects
  2847. Unmaps template's filemaps
  2848. */
  2849. void
  2850. CTemplate::UnmapFiles
  2851. (
  2852. )
  2853. {
  2854. UINT i;
  2855. for(i = 0; i < m_cFilemaps; i++)
  2856. m_rgpFilemaps[i]->UnmapFile();
  2857. }
  2858. /*===================================================================
  2859. CTemplate::ExtractAndProcessSegment
  2860. Extracts and processes leading source segment and first contained
  2861. source segment from search range.
  2862. Returns
  2863. Nothing
  2864. Side effects
  2865. None
  2866. */
  2867. void
  2868. CTemplate::ExtractAndProcessSegment
  2869. (
  2870. CByteRange& brSearch, // byte range to search for next segment-opening token
  2871. const SOURCE_SEGMENT& ssegLeading, // type of 'leading', i.e. pre-token, source segment
  2872. _TOKEN* rgtknOpeners, // array of permitted open tokens
  2873. UINT ctknOpeners, // count of permitted open tokens
  2874. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  2875. CWorkStore& WorkStore, // working storage for source segments
  2876. CHitObj* pHitObj, // Browser request object
  2877. BOOL fScriptTagProcessed,// has script tag been processed?
  2878. BOOL fIsHTML // are we in HTML segment?
  2879. )
  2880. {
  2881. CByteRange brLeadSegment; // byte range of leading source segment
  2882. SOURCE_SEGMENT ssegContained; // type of 'contained', i.e. post-token, source segment
  2883. CByteRange brContainedSegment; // byte range of contained source segment
  2884. _TOKEN tknOpen; // opening token
  2885. BYTE* pbTokenOpen; // ptr to opening token
  2886. _TOKEN tknClose; // closing token
  2887. BYTE* pbTokenClose; // ptr to closing token
  2888. // NOTE: If "fScriptTagProcessed" is TRUE, then "fIsHTML" must be FALSE. The reason for
  2889. // both flags is that if "fScriptTagProcessed" is FALSE, then "fIsHTML" may be either TRUE
  2890. // or FALSE (indeterminate)
  2891. //
  2892. Assert (FImplies(fScriptTagProcessed, !fIsHTML));
  2893. // If search range is empty, return
  2894. if(brSearch.IsNull())
  2895. return;
  2896. // Set ptr of leading segment to start of search segment
  2897. brLeadSegment.m_pb = brSearch.m_pb;
  2898. // get open token for contained segment
  2899. pbTokenOpen = GetOpenToken(
  2900. brSearch,
  2901. ssegLeading,
  2902. rgtknOpeners,
  2903. ctknOpeners,
  2904. &tknOpen
  2905. );
  2906. // Set count of leading segment to distance between start of search range and token
  2907. brLeadSegment.m_cb = DIFF(pbTokenOpen - brSearch.m_pb);
  2908. // Process leading segment
  2909. ProcessSegment(ssegLeading, brLeadSegment, pfilemapCurrent, WorkStore, fScriptTagProcessed, pHitObj, fIsHTML);
  2910. // If open token was 'EOF', empty out search range and return
  2911. if(tknOpen == CTokenList::tknEOF)
  2912. {
  2913. brSearch.Nullify();
  2914. return;
  2915. }
  2916. // Set contained segment type and close token based upon the opener we found
  2917. tknClose = GetComplementToken(tknOpen);
  2918. ssegContained = GetSegmentOfOpenToken(tknOpen);
  2919. if(ssegContained == ssegHTMLComment)
  2920. // for html comment segments, advance search range to open token
  2921. // NOTE keep html comment tags in segment because they must be sent to client
  2922. brSearch.Advance(DIFF(pbTokenOpen - brSearch.m_pb));
  2923. else
  2924. // for all but html comment segments, advance search range to just past open token
  2925. gm_pTokenList->MovePastToken(tknOpen, pbTokenOpen, brSearch);
  2926. // Get closing token - if none, throw error
  2927. if(NULL == (pbTokenClose = GetCloseToken(brSearch, tknClose)))
  2928. {
  2929. if(tknOpen == CTokenList::tknOpenPrimaryScript)
  2930. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_PSCRIPT);
  2931. else if(tknOpen == CTokenList::tknOpenTaggedScript)
  2932. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  2933. else if(tknOpen == CTokenList::tknOpenObject)
  2934. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  2935. else if(tknOpen == CTokenList::tknOpenHTMLComment)
  2936. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_HTML_COMMENT);
  2937. }
  2938. // calc contained segment
  2939. brContainedSegment.m_pb = brSearch.m_pb;
  2940. brContainedSegment.m_cb = DIFF(pbTokenClose - brSearch.m_pb);
  2941. // advance search range to just past close token
  2942. gm_pTokenList->MovePastToken(tknClose, pbTokenClose, brSearch);
  2943. // if an html comment segment, get actual segment type (e.g. might be a server-side include command)
  2944. // NOTE call may also change contained segment byte range
  2945. if(ssegContained == ssegHTMLComment)
  2946. ssegContained = SsegFromHTMLComment(brContainedSegment);
  2947. // if an html comment segment, add its close tag to contained segment
  2948. // NOTE we keep html comment tags as part of segment so we can process like any other html segment
  2949. if(ssegContained == ssegHTMLComment)
  2950. brContainedSegment.m_cb += CCH_TOKEN(tknClose);
  2951. if(ssegContained == ssegMetadata)
  2952. {
  2953. // METADATA comments are used by DESIGN time controls and we don't send
  2954. // them to the client.
  2955. // We process metadata to get to the typelib info
  2956. UINT idError = 0;
  2957. HRESULT hr = ProcessMetadataSegment(brContainedSegment, &idError, pHitObj);
  2958. if (FAILED(hr))
  2959. ThrowError(brContainedSegment.m_pb, idError);
  2960. }
  2961. else if (ssegContained == ssegFPBot)
  2962. {
  2963. }
  2964. else
  2965. {
  2966. // process contained segment
  2967. ProcessSegment(ssegContained, brContainedSegment, pfilemapCurrent, WorkStore, fScriptTagProcessed, pHitObj, fIsHTML);
  2968. }
  2969. }
  2970. /* ============================================================================
  2971. CTemplate::SsegFromHTMLComment
  2972. Determines source segment type of HTML comment.
  2973. Returns
  2974. Source segment type
  2975. Side effects
  2976. May advance segment byte range
  2977. */
  2978. CTemplate::SOURCE_SEGMENT
  2979. CTemplate::SsegFromHTMLComment
  2980. (
  2981. CByteRange& brSegment // source segment
  2982. )
  2983. {
  2984. SOURCE_SEGMENT ssegRet = ssegHTMLComment; // return value
  2985. BYTE* pbToken; // ptr to token
  2986. if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknCommandINCLUDE, brSegment, m_wCodePage)))
  2987. {
  2988. gm_pTokenList->MovePastToken(CTokenList::tknCommandINCLUDE, pbToken, brSegment);
  2989. ssegRet = ssegInclude;
  2990. }
  2991. else if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknTagMETADATA, brSegment, m_wCodePage)))
  2992. {
  2993. gm_pTokenList->MovePastToken(CTokenList::tknTagMETADATA, pbToken, brSegment);
  2994. ssegRet = ssegMetadata;
  2995. }
  2996. else if(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknTagFPBot, brSegment, m_wCodePage)))
  2997. {
  2998. gm_pTokenList->MovePastToken(CTokenList::tknTagFPBot, pbToken, brSegment);
  2999. ssegRet = ssegFPBot;
  3000. }
  3001. return ssegRet;
  3002. }
  3003. /* ============================================================================
  3004. CTemplate::ProcessSegment
  3005. Processes a source segment based on its type.
  3006. Returns
  3007. Nothing
  3008. Side effects
  3009. None
  3010. */
  3011. void
  3012. CTemplate::ProcessSegment
  3013. (
  3014. SOURCE_SEGMENT sseg, // segment type
  3015. CByteRange& brSegment, // segment byte range
  3016. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3017. CWorkStore& WorkStore, // working storage for source segments
  3018. BOOL fScriptTagProcessed, // has script tag been processed?
  3019. CHitObj* pHitObj, // Browser request object
  3020. BOOL fIsHTML // Is segment in HTML block or script?
  3021. )
  3022. {
  3023. UINT idSequence; // sequence id for this segment
  3024. // if segment is entirely white space, silently return
  3025. if(FByteRangeIsWhiteSpace(brSegment))
  3026. return;
  3027. // set local sequence id and increment member
  3028. idSequence = WorkStore.m_idCurSequence++;
  3029. // Process segment based on its type
  3030. if(sseg == ssegHTML)
  3031. ProcessHTMLSegment(brSegment, WorkStore.m_bufHTMLSegments, idSequence, pfilemapCurrent);
  3032. else if(sseg == ssegHTMLComment)
  3033. ProcessHTMLCommentSegment(brSegment, pfilemapCurrent, WorkStore, pHitObj);
  3034. else if(sseg == ssegPrimaryScript || sseg == ssegTaggedScript)
  3035. ProcessScriptSegment(sseg, brSegment, pfilemapCurrent, WorkStore, idSequence, (BOOLB)!!fScriptTagProcessed, pHitObj);
  3036. else if(sseg == ssegObject)
  3037. ProcessObjectSegment(brSegment, pfilemapCurrent, WorkStore, idSequence);
  3038. else if(sseg == ssegInclude)
  3039. {
  3040. if (! fIsHTML)
  3041. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SSI_COMMAND);
  3042. ProcessIncludeFile(brSegment, pfilemapCurrent, WorkStore, idSequence, pHitObj, fIsHTML);
  3043. }
  3044. // malloc/realloc array if needed
  3045. if(m_cSegmentFilemapSlots == 0)
  3046. {
  3047. m_cSegmentFilemapSlots = C_SCRIPTSEGMENTSDEFAULT + C_HTMLSEGMENTSDEFAULT;
  3048. if(NULL == (m_rgpSegmentFilemaps = (CFileMap**) CTemplate::SmallMalloc(m_cSegmentFilemapSlots * sizeof(CFileMap*))))
  3049. THROW(E_OUTOFMEMORY);
  3050. }
  3051. else if(idSequence >= m_cSegmentFilemapSlots)
  3052. {
  3053. // grab twice what we had before
  3054. m_cSegmentFilemapSlots *= 2;
  3055. if(NULL == (m_rgpSegmentFilemaps = (CFileMap**) CTemplate::SmallReAlloc(m_rgpSegmentFilemaps,
  3056. m_cSegmentFilemapSlots * sizeof(CFileMap*))))
  3057. THROW(E_OUTOFMEMORY);
  3058. }
  3059. // set filemap ptr for this segment - NOTE 'parent' filemap is also current file map
  3060. m_rgpSegmentFilemaps[idSequence] = pfilemapCurrent;
  3061. }
  3062. /* ========================================================
  3063. CTemplate::ProcessHTMLSegment
  3064. Processes an HTML segment.
  3065. Returns
  3066. Nothing
  3067. Side effects
  3068. None
  3069. */
  3070. void
  3071. CTemplate::ProcessHTMLSegment
  3072. (
  3073. CByteRange& brHTML, // html segment
  3074. CBuffer& bufHTMLBlocks, // working storage for html blocks
  3075. UINT idSequence, // segment sequence id
  3076. CFileMap* pfilemapCurrent // current filemap
  3077. )
  3078. {
  3079. if(!(brHTML.IsNull()))
  3080. // If byte range is non-empty, store it in html buffer (non-local)
  3081. bufHTMLBlocks.Append(brHTML, FALSE, idSequence, pfilemapCurrent);
  3082. }
  3083. /* ========================================================
  3084. CTemplate::ProcessHTMLCommentSegment
  3085. Processes an HTML comment segment: within an HTML comment we
  3086. honor plain text (passed through as HTML comment) and primary script.
  3087. See bug 182 for istudio scenarios that require this behavior.
  3088. Returns
  3089. Nothing
  3090. Side effects
  3091. None
  3092. */
  3093. void
  3094. CTemplate::ProcessHTMLCommentSegment
  3095. (
  3096. CByteRange& brSegment, // segment byte range
  3097. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3098. CWorkStore& WorkStore, // working storage for source segments
  3099. CHitObj* pHitObj // Browser request object
  3100. )
  3101. {
  3102. _TOKEN* rgtknOpeners; // array of permitted open tokens
  3103. UINT ctknOpeners; // count of permitted open tokens
  3104. // populate array of permitted open tokens
  3105. ctknOpeners = 1;
  3106. _TOKEN tknOpeners[1];
  3107. rgtknOpeners = tknOpeners;
  3108. rgtknOpeners[0] = CTokenList::tknOpenPrimaryScript;
  3109. // Process source segments embedded within HTML comment segment
  3110. while(!brSegment.IsNull())
  3111. ExtractAndProcessSegment(
  3112. brSegment, // byte range to search for next segment-opening token
  3113. ssegHTML, // type of 'leading', i.e. pre-token, source segment
  3114. rgtknOpeners, // array of permitted open tokens
  3115. ctknOpeners, // count of permitted open tokens
  3116. pfilemapCurrent,// ptr to filemap of parent file
  3117. WorkStore, // working storage for source segments
  3118. pHitObj // Browser request object
  3119. );
  3120. }
  3121. /* ============================================================================
  3122. CTemplate::ProcessScriptSegment
  3123. Processes a script segment.
  3124. Returns
  3125. Nothing
  3126. Side effects
  3127. None
  3128. */
  3129. void
  3130. CTemplate::ProcessScriptSegment
  3131. (
  3132. SOURCE_SEGMENT sseg, // segment type
  3133. CByteRange& brSegment, // segment byte range
  3134. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3135. CWorkStore& WorkStore, // working storage for scripts
  3136. UINT idSequence, // segment sequence id
  3137. BOOLB fScriptTagProcessed,// has script tag been processed?
  3138. CHitObj* pHitObj // Browser request object
  3139. )
  3140. {
  3141. CByteRange brEngine; // script engine name - NOTE constructed null
  3142. if(m_fGlobalAsa)
  3143. if(sseg == ssegPrimaryScript)
  3144. // error out on primary script if we are processing global.asa
  3145. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_GLOBAL_PSCRIPT);
  3146. if(sseg == ssegPrimaryScript)
  3147. {
  3148. CByteRange brTemp = brSegment;
  3149. LTrimWhiteSpace(brTemp);
  3150. if(*brTemp.m_pb == '@') // CONSIDER: tknTagSetPriScriptLang
  3151. {
  3152. // impossible condition: page-level @ commands can't be allowed if they have already been executed
  3153. Assert(!(WorkStore.m_fPageCommandsAllowed && WorkStore.m_fPageCommandsExecuted));
  3154. if(!WorkStore.m_fPageCommandsAllowed)
  3155. {
  3156. if(WorkStore.m_fPageCommandsExecuted)
  3157. // error out if trying to re-execute page-level @ commands
  3158. ThrowError(brSegment.m_pb, IDE_TEMPLATE_PAGE_COMMAND_REPEATED);
  3159. else
  3160. // error out if trying to execute page-level @ commands when not allowed
  3161. ThrowError(brSegment.m_pb, IDE_TEMPLATE_PAGE_COMMAND_NOT_FIRST);
  3162. }
  3163. // if we made it here, must be allowed to execute page-level @ commands AND they have not been executed
  3164. Assert((WorkStore.m_fPageCommandsAllowed && !WorkStore.m_fPageCommandsExecuted));
  3165. /* set primary script language if required
  3166. NOTE we call GetTagName to see if LANGUAGE tag occurs in tags segment; this is somewhat wasteful,
  3167. since BrValueOfTag must simply call GetTagName again. However, this scheme is easier than changing
  3168. BrValueOfTag to return a BOOL and amending all its other callers, who don't need this info.
  3169. */
  3170. // Flags and counters used to track and validate the @ command directive
  3171. //
  3172. int nFirstPass = 1;
  3173. int nOffset = 0;
  3174. BOOLB fTagLanguage = TRUE;
  3175. BOOLB fTagCodePage = TRUE;
  3176. BOOLB fTagLCID = TRUE;
  3177. BOOLB fTagTransacted = TRUE;
  3178. BOOLB fTagSession = TRUE;
  3179. while( GetTag( brSegment, nFirstPass) )
  3180. {
  3181. nFirstPass =2;
  3182. nOffset = 0;
  3183. if ( fTagLanguage && CompTagName( brSegment, CTokenList::tknTagLanguage ) )
  3184. {
  3185. fTagLanguage = FALSE;
  3186. brEngine = BrValueOfTag(brSegment, CTokenList::tknTagLanguage);
  3187. if ( brEngine.IsNull() )
  3188. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_ENGINE_NAME);
  3189. // get prog lang id
  3190. PROGLANG_ID ProgLangId;
  3191. HRESULT hr = GetProgLangId(brEngine, &ProgLangId);
  3192. if(hr == TYPE_E_ELEMENTNOTFOUND)
  3193. // if prog lang not found, throw error
  3194. ThrowErrorSingleInsert(
  3195. brEngine.m_pb,
  3196. IDE_TEMPLATE_BAD_PROGLANG,
  3197. brEngine.m_pb,
  3198. brEngine.m_cb
  3199. );
  3200. else if(FAILED(hr))
  3201. // other failure: re-throw exception code
  3202. THROW(hr);
  3203. Assert(WorkStore.m_ScriptStore.CountPreliminaryEngines() >= 1);
  3204. // Set 0-th (primary) script engine to user-specified value
  3205. WorkStore.m_ScriptStore.m_bufEngineNames.SetItem(
  3206. 0, // index of item to set
  3207. brEngine, // engine name
  3208. FALSE, // item is non-local
  3209. 0, // sequence id (don't care)
  3210. NULL // filemap ptr (don't care)
  3211. );
  3212. // Set 0-th (primary) prog lang id to engine's
  3213. WorkStore.m_ScriptStore.m_rgProgLangId[0] = ProgLangId;
  3214. brSegment.Advance(DIFF(brEngine.m_pb - brSegment.m_pb));
  3215. }
  3216. /* set code page if required
  3217. see NOTE above for why we call we call GetTagName.
  3218. */
  3219. else if ( fTagCodePage && CompTagName( brSegment, CTokenList::tknTagCodePage ) )
  3220. {
  3221. fTagCodePage = FALSE;
  3222. CByteRange brCodePage = BrValueOfTag( brSegment, CTokenList::tknTagCodePage );
  3223. if ( brCodePage.IsNull() )
  3224. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CODEPAGE);
  3225. if ( brCodePage.m_cb > 10 )
  3226. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3227. char szCodePage[31];
  3228. strncpy( szCodePage, (char*) brCodePage.m_pb, brCodePage.m_cb );
  3229. szCodePage[ brCodePage.m_cb ] = '\0';
  3230. char *pchEnd;
  3231. UINT uCodePage = UINT( strtoul( szCodePage, &pchEnd, 10 ) );
  3232. // verify that pchEnd is the NULL
  3233. if (*pchEnd != 0)
  3234. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3235. if ( FAILED( pHitObj->SetCodePage( uCodePage ) ) )
  3236. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_CODEPAGE);
  3237. else
  3238. {
  3239. m_wCodePage = uCodePage;
  3240. m_fCodePageSet = TRUE;
  3241. }
  3242. brSegment.Advance(DIFF(brCodePage.m_pb - brSegment.m_pb));
  3243. }
  3244. /* set LCID if required
  3245. see NOTE above for why we call we call GetTagName.
  3246. */
  3247. else if ( fTagLCID && CompTagName( brSegment, CTokenList::tknTagLCID ) )
  3248. {
  3249. fTagLCID = FALSE;
  3250. CByteRange brLCID = BrValueOfTag( brSegment, CTokenList::tknTagLCID );
  3251. if ( brLCID.IsNull() )
  3252. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_LCID);
  3253. char szLCID[31];
  3254. strncpy( szLCID, (char*) brLCID.m_pb, brLCID.m_cb );
  3255. szLCID[ brLCID.m_cb ] = '\0';
  3256. char *pchEnd;
  3257. UINT uLCID = UINT( strtoul( szLCID, &pchEnd, 10 ) );
  3258. // verify that pchEnd is the NULL
  3259. if (*pchEnd != 0)
  3260. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_LCID);
  3261. if ( FAILED( pHitObj->SetLCID( uLCID ) ) )
  3262. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_LCID);
  3263. else
  3264. {
  3265. m_lLCID = uLCID;
  3266. m_fLCIDSet = TRUE;
  3267. }
  3268. brSegment.Advance(DIFF(brLCID.m_pb - brSegment.m_pb));
  3269. }
  3270. /* Set transacted if requiured
  3271. see NOTE above for why we call GetTagName
  3272. */
  3273. else if ( fTagTransacted && CompTagName( brSegment, CTokenList::tknTagTransacted ) )
  3274. {
  3275. STACK_BUFFER( tempTransValue, 32 );
  3276. fTagTransacted = FALSE;
  3277. CByteRange brTransacted = BrValueOfTag( brSegment, CTokenList::tknTagTransacted );
  3278. if ( brTransacted.IsNull() )
  3279. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_TRANSACTED_VALUE);
  3280. if (!tempTransValue.Resize(brTransacted.m_cb + 1)) {
  3281. ThrowError(brSegment.m_pb, IDE_OOM);
  3282. }
  3283. LPSTR szTransacted = static_cast<LPSTR> (tempTransValue.QueryPtr());
  3284. strncpy(szTransacted, (LPCSTR)brTransacted.m_pb, brTransacted.m_cb);
  3285. szTransacted[brTransacted.m_cb]='\0';
  3286. if (!strcmpi(szTransacted, "REQUIRED"))
  3287. m_ttTransacted = ttRequired;
  3288. else if (!strcmpi(szTransacted, "REQUIRES_NEW"))
  3289. m_ttTransacted = ttRequiresNew;
  3290. else if (!strcmpi(szTransacted, "SUPPORTED"))
  3291. m_ttTransacted = ttSupported;
  3292. else if (!strcmpi(szTransacted, "NOT_SUPPORTED"))
  3293. m_ttTransacted = ttNotSupported;
  3294. else
  3295. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_TRANSACTED_VALUE);
  3296. brSegment.Advance(DIFF(brTransacted.m_pb - brSegment.m_pb));
  3297. }
  3298. /* Set session flag
  3299. see NOTE above for why we call GetTagName
  3300. */
  3301. else if ( fTagSession && CompTagName( brSegment, CTokenList::tknTagSession ) )
  3302. {
  3303. STACK_BUFFER( tempSession, 16 );
  3304. fTagSession = FALSE;
  3305. CByteRange brSession = BrValueOfTag( brSegment, CTokenList::tknTagSession );
  3306. if ( brSession.IsNull() )
  3307. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SESSION_VALUE);
  3308. if (!tempSession.Resize(brSession.m_cb + 1))
  3309. ThrowError(brSegment.m_pb, IDE_OOM);
  3310. LPSTR szSession = static_cast<LPSTR> (tempSession.QueryPtr());
  3311. strncpy(szSession, (LPCSTR)brSession.m_pb, brSession.m_cb);
  3312. szSession[brSession.m_cb]='\0';
  3313. if (strcmpi(szSession, "TRUE") == 0)
  3314. {
  3315. m_fSession = TRUE;
  3316. if (!pHitObj->PAppln()->QueryAppConfig()->fAllowSessionState())
  3317. ThrowError(brSegment.m_pb, IDE_TEMPLATE_CANT_ENABLE_SESSIONS);
  3318. }
  3319. else if (strcmpi(szSession, "FALSE") == 0)
  3320. m_fSession = FALSE;
  3321. else
  3322. ThrowError(brSegment.m_pb, IDE_TEMPLATE_BAD_SESSION_VALUE);
  3323. brSegment.Advance(DIFF(brSession.m_pb - brSegment.m_pb));
  3324. }
  3325. else
  3326. ThrowErrorSingleInsert( brSegment.m_pb,
  3327. IDE_TEMPLATE_BAD_AT_COMMAND,
  3328. brSegment.m_pb,
  3329. brSegment.m_cb
  3330. );
  3331. }
  3332. if (nFirstPass == 1)
  3333. ThrowErrorSingleInsert( brSegment.m_pb,
  3334. IDE_TEMPLATE_BAD_AT_COMMAND,
  3335. brSegment.m_pb,
  3336. brSegment.m_cb
  3337. );
  3338. // set flag true and ignore remainder of segment, since we only use this segment for page-level @ commands
  3339. WorkStore.m_fPageCommandsExecuted = TRUE;
  3340. goto LExit;
  3341. }
  3342. }
  3343. if(sseg == ssegTaggedScript)
  3344. {
  3345. if(!fScriptTagProcessed)
  3346. {
  3347. /* semantics of script-tag-processed flag:
  3348. - if false, we have a 'fresh' tagged script block, so we need to get its engine name
  3349. (which also advances past the script tag header) and then process the tagged segment
  3350. via indirect recursive call
  3351. - if true, we have aleady been called recursively, so we bypass further recursion
  3352. and simply append to store
  3353. */
  3354. CByteRange brIncludeFile;
  3355. GetScriptEngineOfSegment(brSegment, WorkStore.m_brCurEngine, brIncludeFile);
  3356. if (! brIncludeFile.IsNull())
  3357. {
  3358. STACK_BUFFER( tempInclude, 256 );
  3359. if (!tempInclude.Resize(brIncludeFile.m_cb + 1)) {
  3360. ThrowError(brSegment.m_pb, IDE_OOM);
  3361. }
  3362. // Create Null-terminated string from brIncludeFile
  3363. char *szFileSpec = reinterpret_cast<char *>(tempInclude.QueryPtr());
  3364. memcpy(szFileSpec, brIncludeFile.m_pb, brIncludeFile.m_cb);
  3365. szFileSpec[brIncludeFile.m_cb] = 0;
  3366. if (szFileSpec[0] == '\\') // metabase stuff chokes on initial '\' char
  3367. szFileSpec[0] = '/';
  3368. // read the include file (szFileSpec & brIncludeFile in this case point to same string contents.
  3369. // however, "brIncludeFile" is used as an error location.
  3370. //
  3371. TRY
  3372. ProcessIncludeFile2(szFileSpec, brIncludeFile, pfilemapCurrent, WorkStore, idSequence, pHitObj, FALSE);
  3373. CATCH(hrException)
  3374. // The TRY/CATCH below may re-throw a IDE_TEMPLATE_BAD_PROGLANG when the
  3375. // segment being processed is tagged script with a SRC file. The reason being
  3376. // that to properly report the error, the ThrowErrorSingleInsert must be called
  3377. // from the template which contained the script tag with the bad prog lang. If
  3378. // called from the template created containing the included script, then the
  3379. // brEngine assigned below is not pointing into the included script's filemap
  3380. // which results in AVs because we can't do the pointer math to determine the
  3381. // line number.
  3382. if(hrException == IDE_TEMPLATE_BAD_PROGLANG)
  3383. // exception code is really an error message id: set err id to it
  3384. ThrowErrorSingleInsert(
  3385. WorkStore.m_brCurEngine.m_pb,
  3386. IDE_TEMPLATE_BAD_PROGLANG,
  3387. WorkStore.m_brCurEngine.m_pb,
  3388. WorkStore.m_brCurEngine.m_cb
  3389. );
  3390. else
  3391. // other exception: re-throw
  3392. THROW(hrException);
  3393. END_TRY
  3394. // done - don't process script text
  3395. return;
  3396. }
  3397. else
  3398. ProcessTaggedScriptSegment(brSegment, pfilemapCurrent, WorkStore, pHitObj);
  3399. }
  3400. brEngine = WorkStore.m_brCurEngine;
  3401. }
  3402. TRY
  3403. // append script segment to store
  3404. WorkStore.m_ScriptStore.AppendScript(brSegment, brEngine, (sseg == ssegPrimaryScript), idSequence, pfilemapCurrent);
  3405. CATCH(hrException)
  3406. // NOTE exception code from AppendScript() is overloaded: it can be an error message id or a true exception
  3407. // if the brEngine does not point to memory within the current filemap, then
  3408. // we must have come into here because of a tagged script statement with a SRC=
  3409. // attrib. In which case, we won't call ThrowError from here but will re-throw
  3410. // the error to be caught above.
  3411. if((hrException == IDE_TEMPLATE_BAD_PROGLANG)
  3412. && (brEngine.m_pb >= pfilemapCurrent->m_pbStartOfFile)
  3413. && (brEngine.m_pb < (pfilemapCurrent->m_pbStartOfFile + pfilemapCurrent->GetSize()))) {
  3414. // exception code is really an error message id: set err id to it
  3415. ThrowErrorSingleInsert(
  3416. brEngine.m_pb,
  3417. IDE_TEMPLATE_BAD_PROGLANG,
  3418. brEngine.m_pb,
  3419. brEngine.m_cb
  3420. );
  3421. }
  3422. else
  3423. // other exception: re-throw
  3424. THROW(hrException);
  3425. END_TRY
  3426. LExit:
  3427. // set flag to say we can no longer set primary language (must be in first script segment, if at all)
  3428. WorkStore.m_fPageCommandsAllowed = FALSE;
  3429. }
  3430. /* ========================================================
  3431. CTemplate::ProcessMetadataSegment
  3432. Parses the metadata comment for typelib information.
  3433. Returns
  3434. HRESULT
  3435. */
  3436. HRESULT
  3437. CTemplate::ProcessMetadataSegment
  3438. (
  3439. const CByteRange& brSegment,
  3440. UINT *pidError,
  3441. CHitObj *pHitObj
  3442. )
  3443. {
  3444. // TYPELIB
  3445. if (FTagHasValue(brSegment,
  3446. CTokenList::tknTagType,
  3447. CTokenList::tknValueTypeLib))
  3448. {
  3449. return ProcessMetadataTypelibSegment(brSegment, pidError, pHitObj);
  3450. }
  3451. // METADATA INVALID in Global.asa
  3452. else if (m_fGlobalAsa)
  3453. {
  3454. ThrowError(brSegment.m_pb, IDE_TEMPLATE_METADATA_IN_GLOBAL_ASA);
  3455. return E_TEMPLATE_COMPILE_FAILED_DONT_CACHE; // to keep compiler happy; in reality doesn't return.
  3456. }
  3457. // COOKIE
  3458. else if (FTagHasValue(brSegment,
  3459. CTokenList::tknTagType,
  3460. CTokenList::tknValueCookie))
  3461. {
  3462. return ProcessMetadataCookieSegment(brSegment, pidError, pHitObj);
  3463. }
  3464. // Ignore everything else
  3465. else
  3466. {
  3467. return S_OK;
  3468. }
  3469. }
  3470. /* ========================================================
  3471. CTemplate::ProcessMetadataTypelibSegment
  3472. Parses the metadata comment for typelib information.
  3473. Returns
  3474. HRESULT
  3475. */
  3476. HRESULT
  3477. CTemplate::ProcessMetadataTypelibSegment
  3478. (
  3479. const CByteRange& brSegment,
  3480. UINT *pidError,
  3481. CHitObj *pHitObj
  3482. )
  3483. {
  3484. // Ignore ENDSPAN segments
  3485. if (GetTagName(brSegment, CTokenList::tknTagEndspan))
  3486. {
  3487. // ENDSPAN found - ignore
  3488. return S_OK;
  3489. }
  3490. HRESULT hr;
  3491. char szFile[MAX_PATH+1];
  3492. DWORD cbFile;
  3493. // Try to get the filename
  3494. CByteRange br = BrValueOfTag(brSegment, CTokenList::tknTagFile);
  3495. if (!br.IsNull())
  3496. {
  3497. // filename present
  3498. if (br.m_cb > MAX_PATH)
  3499. {
  3500. // file too long
  3501. *pidError = IDE_TEMPLATE_BAD_TYPELIB_SPEC;
  3502. return E_FAIL;
  3503. }
  3504. memcpy(szFile, br.m_pb, br.m_cb);
  3505. cbFile = br.m_cb;
  3506. szFile[cbFile] = '\0';
  3507. }
  3508. else
  3509. {
  3510. // No filename - use GUID, version, LCID to get file
  3511. char szUUID[44]; // {} + hex chars + dashes
  3512. char szVers[16]; // "1.0", etc
  3513. char szLCID[16]; // locale id - a number
  3514. br = BrValueOfTag(brSegment, CTokenList::tknTagUUID);
  3515. if (br.IsNull() || br.m_cb > sizeof(szUUID)-3)
  3516. {
  3517. // no filename and no uuid -> invalid typelib spec
  3518. *pidError = IDE_TEMPLATE_BAD_TYPELIB_SPEC;
  3519. return E_FAIL;
  3520. }
  3521. if (br.m_pb[0] == '{')
  3522. {
  3523. // already in braces
  3524. memcpy(szUUID, br.m_pb, br.m_cb);
  3525. szUUID[br.m_cb] = '\0';
  3526. }
  3527. else
  3528. {
  3529. // enclose in {}
  3530. szUUID[0] = '{';
  3531. memcpy(szUUID+1, br.m_pb, br.m_cb);
  3532. szUUID[br.m_cb+1] = '}';
  3533. szUUID[br.m_cb+2] = '\0';
  3534. }
  3535. // Optional Version
  3536. szVers[0] = '\0';
  3537. br = BrValueOfTag(brSegment, CTokenList::tknTagVersion);
  3538. if (!br.IsNull() && br.m_cb < sizeof(szVers)-1)
  3539. {
  3540. memcpy(szVers, br.m_pb, br.m_cb);
  3541. szVers[br.m_cb] = '\0';
  3542. }
  3543. // Optional LCID
  3544. LCID lcid;
  3545. br = BrValueOfTag(brSegment, CTokenList::tknTagLCID);
  3546. if (!br.IsNull() && br.m_cb < sizeof(szLCID)-1)
  3547. {
  3548. memcpy(szLCID, br.m_pb, br.m_cb);
  3549. szLCID[br.m_cb] = '\0';
  3550. lcid = strtoul(szLCID, NULL, 16);
  3551. }
  3552. else
  3553. {
  3554. // if the LCID is not defined -> use system's
  3555. lcid = GetSystemDefaultLCID();
  3556. }
  3557. // Get TYPELIB filename from registry
  3558. hr = GetTypelibFilenameFromRegistry
  3559. (
  3560. szUUID,
  3561. szVers,
  3562. lcid,
  3563. szFile,
  3564. MAX_PATH
  3565. );
  3566. if (FAILED(hr))
  3567. {
  3568. *pidError = IDE_TEMPLATE_BAD_TYPELIB_REG_SPEC;
  3569. return hr;
  3570. }
  3571. cbFile = strlen(szFile);
  3572. }
  3573. // Convert filename to double-byte to call LoadTypeLib()
  3574. STACK_BUFFER( tempFile, MAX_PATH * sizeof(WCHAR) );
  3575. if (!tempFile.Resize((cbFile+1) * sizeof(WCHAR))) {
  3576. *pidError = IDE_OOM;
  3577. return E_FAIL;
  3578. }
  3579. LPWSTR wszFile = (LPWSTR)tempFile.QueryPtr();
  3580. if (MultiByteToWideChar(pHitObj->GetCodePage(), MB_ERR_INVALID_CHARS,
  3581. szFile, cbFile, wszFile, cbFile) == 0)
  3582. {
  3583. *pidError = IDE_OOM;
  3584. return E_FAIL;
  3585. }
  3586. wszFile[cbFile] = L'\0';
  3587. // LoadTypeLib() to get ITypeLib*
  3588. ITypeLib *ptlb = NULL;
  3589. hr = LoadTypeLib(wszFile, &ptlb);
  3590. if (FAILED(hr))
  3591. {
  3592. *pidError = IDE_TEMPLATE_LOAD_TYPELIB_FAILED;
  3593. return hr;
  3594. }
  3595. // Remember ITypeLib* in the array
  3596. Assert(ptlb);
  3597. hr = m_rgpTypeLibs.append(ptlb);
  3598. if (FAILED(hr))
  3599. {
  3600. *pidError = IDE_TEMPLATE_LOAD_TYPELIB_FAILED;
  3601. return hr;
  3602. }
  3603. return S_OK;
  3604. }
  3605. /* ========================================================
  3606. CTemplate::ProcessMetadataCookieSegment
  3607. Parses the metadata comment for cookie information.
  3608. Returns
  3609. HRESULT
  3610. */
  3611. HRESULT
  3612. CTemplate::ProcessMetadataCookieSegment
  3613. (
  3614. const CByteRange& brSegment,
  3615. UINT *pidError,
  3616. CHitObj *pHitObj
  3617. )
  3618. {
  3619. HRESULT hr;
  3620. CByteRange br;
  3621. char *pszName;
  3622. char szFile[MAX_PATH+1];
  3623. TCHAR sztFile[MAX_PATH+1];
  3624. CMBCSToWChar convStr;
  3625. STACK_BUFFER( tempCookie, 64 );
  3626. STACK_BUFFER( tempFile, 64 );
  3627. // Try to get the cookie name
  3628. br = BrValueOfTag(brSegment, CTokenList::tknTagName);
  3629. if (br.IsNull() || (br.m_cb == 0)) {
  3630. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_NAME;
  3631. return E_FAIL;
  3632. }
  3633. if (!tempCookie.Resize(br.m_cb + 1)) {
  3634. *pidError = IDE_OOM;
  3635. return E_FAIL;
  3636. }
  3637. pszName = (char *)tempCookie.QueryPtr();
  3638. if (!pszName)
  3639. {
  3640. *pidError = IDE_OOM;
  3641. return E_FAIL;
  3642. }
  3643. memcpy(pszName, br.m_pb, br.m_cb);
  3644. pszName[br.m_cb] = '\0';
  3645. // Try to get the path to the script
  3646. br = BrValueOfTag(brSegment, CTokenList::tknTagSrc);
  3647. if (br.IsNull() || (br.m_cb >= MAX_PATH) || (br.m_cb == 0))
  3648. {
  3649. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_SRC;
  3650. return E_FAIL;
  3651. }
  3652. memcpy(szFile, br.m_pb, br.m_cb);
  3653. szFile[br.m_cb] = '\0';
  3654. // Convert file to physical path
  3655. Assert(pHitObj->PServer());
  3656. WCHAR *pCookieFile;
  3657. #if _IIS_5_1
  3658. // just use CP_ACP for 5.1 since the Core can't handle anything else anyway
  3659. if (FAILED (convStr.Init (szFile))) {
  3660. #else
  3661. // 6.0 can handle UNICODE. Convert using script code page
  3662. if (FAILED (convStr.Init (szFile,pHitObj->GetCodePage()))) {
  3663. #endif
  3664. *pidError = IDE_OOM;
  3665. return E_FAIL;
  3666. }
  3667. pCookieFile = convStr.GetString();
  3668. if (FAILED(pHitObj->PServer()->MapPathInternal(0, pCookieFile, sztFile)))
  3669. {
  3670. *pidError = IDE_TEMPLATE_BAD_COOKIE_SPEC_SRC;
  3671. return E_FAIL;
  3672. }
  3673. Normalize(sztFile);
  3674. // Construct 449-echo-cookie object
  3675. C449Cookie *p449 = NULL;
  3676. hr = Create449Cookie(pszName, sztFile, &p449);
  3677. if (FAILED(hr))
  3678. {
  3679. *pidError = IDE_TEMPLATE_LOAD_COOKIESCRIPT_FAILED;
  3680. return hr;
  3681. }
  3682. // Remember 449 cookie in the array
  3683. Assert(p449);
  3684. hr = m_rgp449.append(p449);
  3685. if (FAILED(hr)) {
  3686. *pidError = IDE_TEMPLATE_LOAD_COOKIESCRIPT_FAILED;
  3687. return hr;
  3688. }
  3689. return S_OK;
  3690. }
  3691. /* ========================================================
  3692. CTemplate::GetScriptEngineOfSegment
  3693. Returns script engine name for a script segment.
  3694. Returns
  3695. Byte range containing script engine name
  3696. Side effects
  3697. Advances segment byte range past close tag token
  3698. */
  3699. void
  3700. CTemplate::GetScriptEngineOfSegment
  3701. (
  3702. CByteRange& brSegment, // segment byte range
  3703. CByteRange& brEngine, // script engine name
  3704. CByteRange& brInclude // value of SRC tag
  3705. )
  3706. {
  3707. BYTE* pbCloseTag; // ptr to close of start tag
  3708. // tags contained in start tag
  3709. CByteRange brTags = BrTagsFromSegment(brSegment, CTokenList::tknCloseTaggedScript, &pbCloseTag);
  3710. // if no close found, throw error
  3711. if(pbCloseTag == NULL)
  3712. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  3713. Assert(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer));
  3714. // get engine name from tags
  3715. brEngine = BrValueOfTag(brTags, CTokenList::tknTagLanguage);
  3716. if(brEngine.IsNull())
  3717. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_ENGINE_NAME);
  3718. // Get SRC attribute from tags
  3719. brInclude = BrValueOfTag(brTags, CTokenList::tknTagSrc);
  3720. // advance segment past close tag token
  3721. gm_pTokenList->MovePastToken(CTokenList::tknCloseTag, pbCloseTag, brSegment);
  3722. }
  3723. /* ========================================================
  3724. CTemplate::ProcessTaggedScriptSegment
  3725. Processes a tagged script segment: within tagged script we
  3726. honor plain text (passed through as script text) and HTML comments.
  3727. See bug 423 for istudio scenarios that require this behavior.
  3728. Returns
  3729. Nothing
  3730. Side effects
  3731. None
  3732. */
  3733. void
  3734. CTemplate::ProcessTaggedScriptSegment
  3735. (
  3736. CByteRange& brSegment, // segment byte range
  3737. CFileMap* pfilemapCurrent,// ptr to filemap of parent file
  3738. CWorkStore& WorkStore, // working storage for source segments
  3739. CHitObj* pHitObj // Browser request object
  3740. )
  3741. {
  3742. _TOKEN* rgtknOpeners; // array of permitted open tokens
  3743. _TOKEN tknOpeners[1];
  3744. UINT ctknOpeners; // count of permitted open tokens
  3745. // populate array of permitted open tokens
  3746. ctknOpeners = 1;
  3747. rgtknOpeners = tknOpeners;
  3748. rgtknOpeners[0] = CTokenList::tknOpenHTMLComment;
  3749. // Process source segments embedded within tagged script segment
  3750. while(!brSegment.IsNull())
  3751. ExtractAndProcessSegment(
  3752. brSegment, // byte range to search for next segment-opening token
  3753. ssegTaggedScript, // type of 'leading', i.e. pre-token, source segment
  3754. rgtknOpeners, // array of permitted open tokens
  3755. ctknOpeners, // count of permitted open tokens
  3756. pfilemapCurrent, // ptr to filemap of parent file
  3757. WorkStore, // working storage for source segments
  3758. pHitObj, // Browser request object
  3759. TRUE, // script tag has been processed
  3760. FALSE // NOT in HTML segment
  3761. );
  3762. }
  3763. /* ============================================================================
  3764. CTemplate::ProcessObjectSegment
  3765. Processes an object segment.
  3766. Returns
  3767. Nothing
  3768. Side effects
  3769. throws on error
  3770. */
  3771. void
  3772. CTemplate::ProcessObjectSegment
  3773. (
  3774. CByteRange& brSegment, // segment byte range
  3775. CFileMap* pfilemapCurrent,// ptr to filemap of parent file
  3776. CWorkStore& WorkStore, // working storage for source segments
  3777. UINT idSequence // segment sequence id
  3778. )
  3779. {
  3780. BYTE* pbCloseTag; // ptr to close of start tag
  3781. // tags contained in start tag
  3782. CByteRange brTags = BrTagsFromSegment(brSegment, CTokenList::tknCloseObject, &pbCloseTag);
  3783. // if no close found, bail on error
  3784. if(pbCloseTag == NULL)
  3785. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  3786. // if this is a server object (RUNAT=Server), process its tags
  3787. if(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer))
  3788. {
  3789. CLSID ClsId; // clsid
  3790. // get name value
  3791. CByteRange brName = BrValueOfTag(brTags, CTokenList::tknTagID);
  3792. // if name is null, error out
  3793. if(brName.IsNull())
  3794. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_OBJECT_NAME);
  3795. if(!FValidObjectName(brName))
  3796. ThrowErrorSingleInsert(brName.m_pb, IDE_TEMPLATE_INVALID_OBJECT_NAME, brName.m_pb, brName.m_cb);
  3797. // get values for ClassID and ProgID tags
  3798. CByteRange brClassIDText = BrValueOfTag(brTags, CTokenList::tknTagClassID);
  3799. CByteRange brProgIDText = BrValueOfTag(brTags, CTokenList::tknTagProgID);
  3800. if(!brClassIDText.IsNull())
  3801. // if we find a text class id, set clsid with it
  3802. // NOTE progid tag is ignored if classid tag exists
  3803. GetCLSIDFromBrClassIDText(brClassIDText, &ClsId);
  3804. else if(!brProgIDText.IsNull())
  3805. // else if we find a prog id, resolve it into a class id
  3806. GetCLSIDFromBrProgIDText(brProgIDText, &ClsId);
  3807. else
  3808. // else, throw error; can't create an object without at least one of classid or progid
  3809. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_NO_CLASSID_PROGID, brName.m_pb, brName.m_cb);
  3810. // set scope; bail if bogus
  3811. CompScope csScope = csUnknown;
  3812. CByteRange brScope = BrValueOfTag(brTags, CTokenList::tknTagScope);
  3813. if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValuePage)) || brScope.IsNull())
  3814. // non-existent scope tag defaults to page scope
  3815. csScope = csPage;
  3816. else if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValueApplication)))
  3817. csScope = csAppln;
  3818. else if(brScope.FMatchesSz(SZ_TOKEN(CTokenList::tknValueSession)))
  3819. csScope = csSession;
  3820. else
  3821. ThrowError(brTags.m_pb, IDE_TEMPLATE_BAD_OBJECT_SCOPE);
  3822. if(!m_fGlobalAsa && csScope != csPage)
  3823. // error out on non-page-level object if we are processing anything but global.asa
  3824. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_BAD_PAGE_OBJECT_SCOPE, brName.m_pb, brName.m_cb);
  3825. else if(m_fGlobalAsa && csScope == csPage)
  3826. // error out on page-level object if we are processing global.asa
  3827. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_BAD_GLOBAL_OBJECT_SCOPE, brName.m_pb, brName.m_cb);
  3828. // set threading model
  3829. CompModel cmModel = cmSingle;
  3830. CompModelFromCLSID(ClsId, &cmModel);
  3831. // append object-info to store
  3832. WorkStore.m_ObjectInfoStore.AppendObject(brName, ClsId, csScope, cmModel, idSequence);
  3833. }
  3834. }
  3835. /* ============================================================================
  3836. CTemplate::GetCLSIDFromBrClassIDText
  3837. Sets a clsid from a byte range containing an ANSI text version of a class id
  3838. Returns
  3839. ptr to clsid (out-parameter)
  3840. Side effects
  3841. throws on error
  3842. */
  3843. void
  3844. CTemplate::GetCLSIDFromBrClassIDText
  3845. (
  3846. CByteRange& brClassIDText,
  3847. LPCLSID pclsid
  3848. )
  3849. {
  3850. // if class id text starts with its standard object tag prefix, advance past it
  3851. if(!_strnicmp((char*)brClassIDText.m_pb, "clsid:", 6))
  3852. brClassIDText.Advance(6);
  3853. // if class id text is bracketed with {}, adjust byte range to strip them
  3854. // NOTE we always add {} below, because normal case is that they are missing from input text
  3855. if(*brClassIDText.m_pb == '{')
  3856. brClassIDText.Advance(1);
  3857. if(*(brClassIDText.m_pb + brClassIDText.m_cb - 1) == '}')
  3858. brClassIDText.m_cb--;
  3859. // Allocate a wide char string for the string version of class id
  3860. // NOTE we add 3 characters to hold {} and null terminator
  3861. OLECHAR* pszWideClassID = new WCHAR[brClassIDText.m_cb + 3];
  3862. if (NULL == pszWideClassID)
  3863. THROW(E_OUTOFMEMORY);
  3864. // start wide string class id with left brace
  3865. pszWideClassID[0] = '{';
  3866. // Convert the string class id to wide chars
  3867. if (0 == MultiByteToWideChar( CP_ACP, // ANSI code page
  3868. MB_ERR_INVALID_CHARS, // err on invalid chars
  3869. (LPCSTR)brClassIDText.m_pb, // input ANSI string version of class id
  3870. brClassIDText.m_cb, // length of input string
  3871. pszWideClassID + 1, // location for output wide string class id
  3872. brClassIDText.m_cb // size of output buffer
  3873. ))
  3874. {
  3875. delete [] pszWideClassID;
  3876. THROW(E_FAIL);
  3877. }
  3878. // append right brace to wide string
  3879. pszWideClassID[brClassIDText.m_cb + 1] = '}';
  3880. // Null terminate the wide string
  3881. pszWideClassID[brClassIDText.m_cb + 2] = NULL;
  3882. // Now get the clsid from wide string class id
  3883. if(FAILED(CLSIDFromString(pszWideClassID, pclsid)))
  3884. {
  3885. delete [] pszWideClassID;
  3886. ThrowErrorSingleInsert(brClassIDText.m_pb, IDE_TEMPLATE_BAD_CLASSID, brClassIDText.m_pb, brClassIDText.m_cb);
  3887. }
  3888. if(NULL != pszWideClassID)
  3889. delete [] pszWideClassID;
  3890. }
  3891. /* ===================================================================
  3892. CTemplate::GetCLSIDFromBrProgIDText
  3893. Gets a clsid from the registry given a ProgID
  3894. Returns
  3895. ptr to clsid (out-parameter)
  3896. Side effects
  3897. throws on error
  3898. */
  3899. void
  3900. CTemplate::GetCLSIDFromBrProgIDText
  3901. (
  3902. CByteRange& brProgIDText,
  3903. LPCLSID pclsid
  3904. )
  3905. {
  3906. // allocate a wide char string for ProgID plus null terminator
  3907. OLECHAR* pszWideProgID = new WCHAR[brProgIDText.m_cb + 1];
  3908. if (NULL == pszWideProgID)
  3909. THROW(E_OUTOFMEMORY);
  3910. // Convert the string class id to wide chars
  3911. if (0 == MultiByteToWideChar( CP_ACP, // ANSI code page
  3912. MB_ERR_INVALID_CHARS, // err on invalid chars
  3913. (LPCSTR)brProgIDText.m_pb, // input ANSI string version of prog id
  3914. brProgIDText.m_cb, // length of input string
  3915. pszWideProgID, // location for output wide string prog id
  3916. brProgIDText.m_cb // size of output buffer
  3917. ))
  3918. {
  3919. delete [] pszWideProgID; pszWideProgID = NULL;
  3920. THROW(E_FAIL);
  3921. }
  3922. // Null terminate the wide string
  3923. pszWideProgID[brProgIDText.m_cb] = NULL;
  3924. // Now get clsid from ProgID
  3925. if(FAILED(CLSIDFromProgID(pszWideProgID, pclsid)))
  3926. {
  3927. delete [] pszWideProgID; pszWideProgID = NULL;
  3928. ThrowErrorSingleInsert(brProgIDText.m_pb, IDE_TEMPLATE_BAD_PROGID, brProgIDText.m_pb, brProgIDText.m_cb);
  3929. }
  3930. // Cache ProgId to CLSID mapping
  3931. g_TypelibCache.RememberProgidToCLSIDMapping(pszWideProgID, *pclsid);
  3932. if (NULL != pszWideProgID)
  3933. delete [] pszWideProgID;
  3934. }
  3935. /* ============================================================================
  3936. CTemplate::FValidObjectName
  3937. Determines whether an object name clashes with a Denali intrinsic object name.
  3938. Returns
  3939. TRUE or FALSE
  3940. Side effects
  3941. None
  3942. */
  3943. BOOLB
  3944. CTemplate::FValidObjectName
  3945. (
  3946. CByteRange& brName // object name
  3947. )
  3948. {
  3949. if(brName.FMatchesSz(SZ_OBJ_APPLICATION))
  3950. return FALSE;
  3951. if(brName.FMatchesSz(SZ_OBJ_REQUEST))
  3952. return FALSE;
  3953. if(brName.FMatchesSz(SZ_OBJ_RESPONSE))
  3954. return FALSE;
  3955. if(brName.FMatchesSz(SZ_OBJ_SERVER))
  3956. return FALSE;
  3957. if(brName.FMatchesSz(SZ_OBJ_CERTIFICATE))
  3958. return FALSE;
  3959. if(brName.FMatchesSz(SZ_OBJ_SESSION))
  3960. return FALSE;
  3961. if(brName.FMatchesSz(SZ_OBJ_SCRIPTINGNAMESPACE))
  3962. return FALSE;
  3963. return TRUE;
  3964. }
  3965. /* ============================================================================
  3966. CTemplate::ProcessIncludeFile
  3967. Processes an include file.
  3968. Returns
  3969. Nothing
  3970. */
  3971. void
  3972. CTemplate::ProcessIncludeFile
  3973. (
  3974. CByteRange& brSegment, // segment byte range
  3975. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  3976. CWorkStore& WorkStore, // current working storage
  3977. UINT idSequence, // sequence #
  3978. CHitObj* pHitObj, // Browser request object pointer
  3979. BOOL fIsHTML
  3980. )
  3981. {
  3982. CByteRange brFileSpec; // filespec of include file
  3983. BOOLB fVirtual = FALSE; // is include filespec virtual?
  3984. // filespec of include file (sz)
  3985. CHAR szFileSpec[MAX_PATH + 1];
  3986. LPSTR szTemp = szFileSpec; // temp ptr to filespec
  3987. // get value of FILE tag
  3988. brFileSpec = BrValueOfTag(brSegment, CTokenList::tknTagFile);
  3989. if(brFileSpec.IsNull())
  3990. {
  3991. // if we found no FILE tag, get value of VIRTUAL tag
  3992. brFileSpec = BrValueOfTag(brSegment, CTokenList::tknTagVirtual);
  3993. fVirtual = TRUE;
  3994. }
  3995. if(brFileSpec.IsNull())
  3996. // if we found neither, error out
  3997. ThrowError(brSegment.m_pb, IDE_TEMPLATE_NO_INCLUDE_NAME);
  3998. if (brFileSpec.m_cb>MAX_PATH)
  3999. {
  4000. // return the last MAX_PATH chars of the file name. This is done
  4001. // this way to avoid a Error Too Long message when the include
  4002. // file spec is exceedingly long.
  4003. char fileNameLast[MAX_PATH+4];
  4004. strcpy(fileNameLast, "...");
  4005. strcpy(fileNameLast+3, (LPSTR)(brFileSpec.m_pb+brFileSpec.m_cb-MAX_PATH));
  4006. ThrowErrorSingleInsert(brFileSpec.m_pb,
  4007. IDE_TEMPLATE_BAD_INCLUDE,
  4008. brFileSpec.m_pb,
  4009. brFileSpec.m_cb);
  4010. }
  4011. // NOTE we manipulate temp sz to preserve szFileSpec
  4012. if(fVirtual)
  4013. {
  4014. if(*brFileSpec.m_pb == '\\')
  4015. {
  4016. // if VIRTUAL path starts with backslash, replace it with fwd slash
  4017. *szTemp++ = '/';
  4018. brFileSpec.Advance(1);
  4019. }
  4020. else if(*brFileSpec.m_pb != '/')
  4021. // if VIRTUAL path starts with anything other than fwd slash or backslash, prepend fwd slash
  4022. *szTemp++ = '/';
  4023. }
  4024. // append supplied path to temp sz
  4025. strncpy(szTemp, (LPCSTR) brFileSpec.m_pb, brFileSpec.m_cb);
  4026. szTemp[brFileSpec.m_cb] = NULL;
  4027. if(!fVirtual)
  4028. {
  4029. // if FILE filespec starts with \ or /, hurl
  4030. if(*szFileSpec == '\\' || *szFileSpec == '/')
  4031. ThrowErrorSingleInsert(
  4032. brFileSpec.m_pb,
  4033. IDE_TEMPLATE_BAD_FILE_TAG,
  4034. brFileSpec.m_pb,
  4035. brFileSpec.m_cb
  4036. );
  4037. }
  4038. // NOTE: szFileSpec is the doctored path (it possibly has "/" prepended.
  4039. // brFileSpec is used as the error location.
  4040. //
  4041. ProcessIncludeFile2(szFileSpec, brFileSpec, pfilemapCurrent, WorkStore, idSequence, pHitObj, fIsHTML);
  4042. }
  4043. /* ============================================================================
  4044. CTemplate::ProcessIncludeFile2
  4045. adds a #include file to the CTemplate and starts the template to processing
  4046. the file.
  4047. Returns
  4048. Nothing
  4049. Side effects
  4050. Calls GetSegmentsFromFile recursively
  4051. NOTE - kind of an oddball thing here. The szFileSpec in this case is
  4052. intentionally ANSI as it came from the ASP script content. It may need
  4053. to be converted to UNICODE.
  4054. */
  4055. void
  4056. CTemplate::ProcessIncludeFile2
  4057. (
  4058. CHAR * szAnsiFileSpec, // file to include
  4059. CByteRange& brErrorLocation, // ByteRange in source where errors should be reported
  4060. CFileMap* pfilemapCurrent, // ptr to filemap of parent file
  4061. CWorkStore& WorkStore, // current working storage
  4062. UINT idSequence, // sequence #
  4063. CHitObj* pHitObj, // Browser request object pointer
  4064. BOOL fIsHTML
  4065. )
  4066. {
  4067. HRESULT hr;
  4068. TCHAR *szFileSpec;
  4069. #if UNICODE
  4070. CMBCSToWChar convFileSpec;
  4071. if (FAILED(hr = convFileSpec.Init(szAnsiFileSpec, pHitObj->GetCodePage()))) {
  4072. THROW(hr);
  4073. }
  4074. szFileSpec = convFileSpec.GetString();
  4075. #else
  4076. szFileSpec = szAnsiFileSpec;
  4077. #endif
  4078. // if parent paths are disallowed and filespec contains parent dir reference, hurl
  4079. if (!pHitObj->QueryAppConfig()->fEnableParentPaths() && _tcsstr(szFileSpec, _T("..")))
  4080. ThrowErrorSingleInsert(
  4081. brErrorLocation.m_pb,
  4082. IDE_TEMPLATE_DISALLOWED_PARENT_PATH,
  4083. brErrorLocation.m_pb,
  4084. brErrorLocation.m_cb
  4085. );
  4086. TRY
  4087. AppendMapFile(
  4088. szFileSpec,
  4089. pfilemapCurrent,
  4090. (szFileSpec[0] == _T('/')) || (szFileSpec[0] == _T('\\')), // fVirtual
  4091. pHitObj, // main file's hit object
  4092. FALSE // not the global.asa file
  4093. );
  4094. CATCH(hrException)
  4095. // MapFile() threw an exception: delete last filemap's memory and decrement filemap counter
  4096. // NOTE this is a bit hokey, but we need to do it here rather than AppendMapFile (where we allocated)
  4097. // because its other caller(s) may not want this behavior
  4098. delete m_rgpFilemaps[m_cFilemaps-- - 1];
  4099. /* NOTE exception code from MapFile() is overloaded: it can sometimes
  4100. be an error message id, sometimes a true exception
  4101. NOTE security error causes exception E_USER_LACKS_PERMISSIONS, rather than error id,
  4102. but we pass it thru as if it were an error id because the various error-catch routines
  4103. know how to handle E_USER_LACKS_PERMISSIONS specially.
  4104. */
  4105. UINT idErrMsg;
  4106. if(hrException == IDE_TEMPLATE_CYCLIC_INCLUDE || hrException == E_USER_LACKS_PERMISSIONS)
  4107. // exception code is really an error message id: set err id to it
  4108. idErrMsg = hrException;
  4109. else if(hrException == E_COULDNT_OPEN_SOURCE_FILE)
  4110. // exception is generic couldn't-open-file : set err id to generic bad-file error
  4111. idErrMsg = IDE_TEMPLATE_BAD_INCLUDE;
  4112. else
  4113. // other exception: re-throw
  4114. THROW(hrException);
  4115. ThrowErrorSingleInsert(
  4116. brErrorLocation.m_pb,
  4117. idErrMsg,
  4118. brErrorLocation.m_pb,
  4119. brErrorLocation.m_cb
  4120. );
  4121. END_TRY
  4122. // store ptr to current file map in local before recursive call (which may increment m_cFilemaps)
  4123. CFileMap* pfilemap = m_rgpFilemaps[m_cFilemaps - 1];
  4124. // get inc-file object from cache
  4125. CIncFile* pIncFile;
  4126. if(FAILED(hr = g_IncFileMap.GetIncFile(pfilemap->m_szPathTranslated, &pIncFile)))
  4127. THROW(hr);
  4128. // add this template to inc-file's template list
  4129. if (FAILED(hr = pIncFile->AddTemplate(this)))
  4130. THROW(hr);
  4131. // set filemap's inc-file ptr
  4132. pfilemap->m_pIncFile = pIncFile;
  4133. // get source segments from include file
  4134. // bugs 1363, 1364: process include file only after we establish dependencies;
  4135. // required for cache flushing to work correctly after compile errors
  4136. GetSegmentsFromFile(*pfilemap, WorkStore, pHitObj, fIsHTML);
  4137. }
  4138. /* ===================================================================
  4139. CTemplate::GetOpenToken
  4140. Returns the token index of and a ptr to the first valid open token
  4141. in search range. For the open token to be valid, we must bypass
  4142. segments we should not process, e.g. scripts or objects not tagged as 'server'
  4143. Returns
  4144. ptr to open token; ptr to open token enum value (out-parameter)
  4145. Side effects
  4146. None
  4147. */
  4148. BYTE*
  4149. CTemplate::GetOpenToken
  4150. (
  4151. CByteRange brSearch, // (ByVal) byte range to search for next segment-opening token
  4152. SOURCE_SEGMENT ssegLeading, // type of 'leading', i.e. pre-token, source segment
  4153. // (only used when deciding to ignore non-SSI comments)
  4154. _TOKEN* rgtknOpeners, // array of permitted open tokens
  4155. UINT ctknOpeners, // count of permitted open tokens
  4156. _TOKEN* ptknOpen // ptr to open token enum value (out-parameter)
  4157. )
  4158. {
  4159. BYTE* pbTokenOpen = NULL; // ptr to opening token
  4160. // keep getting segment-opening tokens until we find one that we need to process
  4161. while(TRUE)
  4162. {
  4163. // Get next open token in search range
  4164. *ptknOpen = gm_pTokenList->NextOpenToken(
  4165. brSearch,
  4166. rgtknOpeners,
  4167. ctknOpeners,
  4168. &pbTokenOpen,
  4169. m_wCodePage
  4170. );
  4171. /* Certain tokens must be followed immediately by white space; others need not.
  4172. NOTE it is pure coincidence that the 'white-space tokens' are also those that
  4173. get special processing below; hence we handle white space issue there.
  4174. If we ever add another 'white-space token' that doesn't require the special processing,
  4175. we will need to handle the white space issue here.
  4176. */
  4177. /* Similar thing applies to non-include and non-metadata HTML
  4178. comments. We really don't care for them to generate their
  4179. own segments -- we can reduce number of Response.WriteBlock()
  4180. calls by considering them part of the preceding HTML segment.
  4181. */
  4182. if (*ptknOpen == CTokenList::tknOpenHTMLComment)
  4183. {
  4184. if (ssegLeading != ssegHTML) // if not inside HTML
  4185. break; // generate a new segment
  4186. // for HTML comments check if it is an include or metadata
  4187. // and if not, this is not a separate segment - keep on looking
  4188. // for the next opener
  4189. // advance search range to just past open token
  4190. gm_pTokenList->MovePastToken(*ptknOpen, pbTokenOpen, brSearch);
  4191. // find end of comment
  4192. BYTE *pbClose = gm_pTokenList->GetToken(CTokenList::tknCloseHTMLComment,
  4193. brSearch, m_wCodePage);
  4194. if (pbClose == NULL)
  4195. {
  4196. // Error -- let other code handle this
  4197. break;
  4198. }
  4199. // construct comment byte range to limit search to it
  4200. CByteRange brComment = brSearch;
  4201. brComment.m_cb = DIFF(pbClose - brSearch.m_pb);
  4202. // look for metadata and include (only inside the comment)
  4203. if (gm_pTokenList->GetToken(CTokenList::tknCommandINCLUDE,
  4204. brComment, m_wCodePage))
  4205. {
  4206. // SSI inclide -- keep it
  4207. break;
  4208. }
  4209. else if (gm_pTokenList->GetToken(CTokenList::tknTagMETADATA,
  4210. brComment, m_wCodePage))
  4211. {
  4212. // METADATA -- keep it
  4213. break;
  4214. }
  4215. else if (gm_pTokenList->GetToken(CTokenList::tknTagFPBot,
  4216. brComment, m_wCodePage))
  4217. {
  4218. // METADATA -- keep it
  4219. break;
  4220. }
  4221. else
  4222. {
  4223. // Regular comment - ignore it
  4224. goto LKeepLooking;
  4225. }
  4226. }
  4227. else if (*ptknOpen == CTokenList::tknOpenTaggedScript || *ptknOpen == CTokenList::tknOpenObject)
  4228. {
  4229. /* if token was script or object tag, check to see if:
  4230. a) it is followed immediately by white space; if not keep looking
  4231. b) it opens a well-formed segment, i.e. one with a proper close tag; if not, throw error
  4232. c) it is designated runat-server; if not keep looking
  4233. */
  4234. // advance search range to just past open token
  4235. gm_pTokenList->MovePastToken(*ptknOpen, pbTokenOpen, brSearch);
  4236. // bug 760: if token is not followed immediately by white space, keep looking
  4237. if(!brSearch.IsNull())
  4238. if(!FWhiteSpace(*brSearch.m_pb))
  4239. goto LKeepLooking;
  4240. // ptr to close of start tag
  4241. BYTE* pbCloseTag;
  4242. // tags contained in start tag
  4243. CByteRange brTags = BrTagsFromSegment(
  4244. brSearch,
  4245. GetComplementToken(*ptknOpen),
  4246. &pbCloseTag
  4247. );
  4248. if(pbCloseTag == NULL)
  4249. {
  4250. // if no close tag, throw error
  4251. if(*ptknOpen == CTokenList::tknOpenObject)
  4252. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_OBJECT);
  4253. else if(*ptknOpen == CTokenList::tknOpenTaggedScript)
  4254. ThrowError(pbTokenOpen, IDE_TEMPLATE_NO_CLOSE_TSCRIPT);
  4255. }
  4256. // if this is a server object (RUNAT=Server), we will process it; else keep looking
  4257. if(FTagHasValue(brTags, CTokenList::tknTagRunat, CTokenList::tknValueServer))
  4258. break;
  4259. }
  4260. else
  4261. {
  4262. // if token was other than script or object tag, or comment
  4263. // we should process segment;
  4264. // hence we have found our open token, so break
  4265. break;
  4266. }
  4267. LKeepLooking: ;
  4268. }
  4269. return pbTokenOpen;
  4270. }
  4271. /* ===================================================================
  4272. CTemplate::GetCloseToken
  4273. Returns a ptr to the next token of type tknClose.
  4274. Returns
  4275. ptr to close token
  4276. Side effects
  4277. Detects and errors out on attempt to nest tagged script or object blocks.
  4278. */
  4279. BYTE*
  4280. CTemplate::GetCloseToken
  4281. (
  4282. CByteRange brSearch, // (ByVal) byte range to search
  4283. _TOKEN tknClose // close token
  4284. )
  4285. {
  4286. BYTE* pbTokenClose = gm_pTokenList->GetToken(tknClose, brSearch, m_wCodePage);
  4287. if(pbTokenClose != NULL)
  4288. if(tknClose == CTokenList::tknCloseTaggedScript || tknClose == CTokenList::tknCloseObject)
  4289. {
  4290. CByteRange brSegment;
  4291. BYTE* pbTokenOpen;
  4292. brSegment.m_pb = brSearch.m_pb;
  4293. brSegment.m_cb = DIFF(pbTokenClose - brSearch.m_pb);
  4294. if(NULL != (pbTokenOpen = gm_pTokenList->GetToken(GetComplementToken(tknClose), brSegment, m_wCodePage)))
  4295. {
  4296. if(tknClose == CTokenList::tknCloseTaggedScript)
  4297. ThrowError(pbTokenOpen, IDE_TEMPLATE_NESTED_TSCRIPT);
  4298. else if(tknClose == CTokenList::tknCloseObject)
  4299. ThrowError(pbTokenOpen, IDE_TEMPLATE_NESTED_OBJECT);
  4300. }
  4301. }
  4302. return pbTokenClose;
  4303. }
  4304. /*===================================================================
  4305. CTemplate::GetComplementToken
  4306. Returns a token's compement token.
  4307. Returns
  4308. Complement token
  4309. Side effects
  4310. None
  4311. */
  4312. _TOKEN
  4313. CTemplate::GetComplementToken
  4314. (
  4315. _TOKEN tkn
  4316. )
  4317. {
  4318. switch(tkn)
  4319. {
  4320. // open tokens
  4321. case CTokenList::tknOpenPrimaryScript:
  4322. return CTokenList::tknClosePrimaryScript;
  4323. case CTokenList::tknOpenTaggedScript:
  4324. return CTokenList::tknCloseTaggedScript;
  4325. case CTokenList::tknOpenObject:
  4326. return CTokenList::tknCloseObject;
  4327. case CTokenList::tknOpenHTMLComment:
  4328. return CTokenList::tknCloseHTMLComment;
  4329. // close tokens
  4330. case CTokenList::tknClosePrimaryScript:
  4331. return CTokenList::tknOpenPrimaryScript;
  4332. case CTokenList::tknCloseTaggedScript:
  4333. return CTokenList::tknOpenTaggedScript;
  4334. case CTokenList::tknCloseObject:
  4335. return CTokenList::tknOpenObject;
  4336. case CTokenList::tknCloseHTMLComment:
  4337. return CTokenList::tknOpenHTMLComment;
  4338. }
  4339. Assert(FALSE);
  4340. return CTokenList::tknEOF;
  4341. }
  4342. /*===================================================================
  4343. CTemplate::GetSegmentOfOpenToken
  4344. Returns the segment type of an open token.
  4345. Returns
  4346. source segment type of open token
  4347. Side effects
  4348. None
  4349. */
  4350. CTemplate::SOURCE_SEGMENT
  4351. CTemplate::GetSegmentOfOpenToken
  4352. (
  4353. _TOKEN tknOpen
  4354. )
  4355. {
  4356. switch(tknOpen)
  4357. {
  4358. case CTokenList::tknOpenPrimaryScript:
  4359. return ssegPrimaryScript;
  4360. case CTokenList::tknOpenTaggedScript:
  4361. return ssegTaggedScript;
  4362. case CTokenList::tknOpenObject:
  4363. return ssegObject;
  4364. case CTokenList::tknOpenHTMLComment:
  4365. return ssegHTMLComment;
  4366. }
  4367. return ssegHTML;
  4368. }
  4369. /* ========================================================
  4370. CTemplate::BrTagsFromSegment
  4371. Returns the tag range from an HTML start tag
  4372. Returns
  4373. tag byte range
  4374. Side effects
  4375. none
  4376. */
  4377. CByteRange
  4378. CTemplate::BrTagsFromSegment
  4379. (
  4380. CByteRange brSegment, // segment byte range
  4381. _TOKEN tknClose, // close token
  4382. BYTE** ppbCloseTag // ptr-to-ptr to close tag - returned to caller
  4383. )
  4384. {
  4385. CByteRange brTags; // tags return value - NOTE constructed null
  4386. // ptr to close token - NOTE null if none within segment byte range
  4387. BYTE* pbTokenClose = GetCloseToken(brSegment, tknClose);
  4388. // if no close tag found, return null tags
  4389. if(NULL == (*ppbCloseTag = gm_pTokenList->GetToken(CTokenList::tknCloseTag, brSegment, m_wCodePage)))
  4390. goto Exit;
  4391. // if next non-null close token occurs before close tag, close tag is invalid; return nulls
  4392. if((pbTokenClose != NULL) && (*ppbCloseTag > pbTokenClose ))
  4393. {
  4394. *ppbCloseTag = NULL;
  4395. goto Exit;
  4396. }
  4397. // crack tags from header tag
  4398. brTags.m_pb = brSegment.m_pb;
  4399. brTags.m_cb = DIFF(*ppbCloseTag - brSegment.m_pb);
  4400. Exit:
  4401. return brTags;
  4402. }
  4403. /* ========================================================
  4404. CTemplate::BrValueOfTag
  4405. Returns a tag's value from a byte range; null if tag is not found
  4406. NOTE value search algorithm per W3 HTML spec - see www.w3.org
  4407. Returns
  4408. byte range of tag's value
  4409. pfTagExists - does the tag exist in tags byte range? (out-parameter)
  4410. NOTE we default *pfTagExists = TRUE; most callers don't care and omit this parameter
  4411. Side effects
  4412. none
  4413. */
  4414. CByteRange
  4415. CTemplate::BrValueOfTag
  4416. (
  4417. CByteRange brTags, // tags byte range
  4418. _TOKEN tknTagName // tag name token
  4419. )
  4420. {
  4421. CByteRange brTemp = brTags; // temp byte range
  4422. CByteRange brValue; // byte range of value for the given tag - NOTE constructed null
  4423. char chDelimiter = NULL; // value delimiter
  4424. // ptr to tag name
  4425. BYTE* pbTagName = GetTagName(brTags, tknTagName);
  4426. // If we did not find tag, return
  4427. if(pbTagName == NULL)
  4428. return brValue;
  4429. // Move past tag name token and pre-separator white space
  4430. brTemp.Advance(DIFF(pbTagName - brTags.m_pb) + CCH_TOKEN(tknTagName));
  4431. LTrimWhiteSpace(brTemp);
  4432. if(brTemp.IsNull())
  4433. goto Exit;
  4434. // If we did not find separator, return
  4435. if(*brTemp.m_pb != CH_ATTRIBUTE_SEPARATOR)
  4436. goto Exit;
  4437. // Move past separator and post-separator white space
  4438. brTemp.Advance(sizeof(CH_ATTRIBUTE_SEPARATOR));
  4439. LTrimWhiteSpace(brTemp);
  4440. if(brTemp.IsNull())
  4441. goto Exit;
  4442. // If value begins with a quote mark, cache it as delimiter
  4443. if((*brTemp.m_pb == CH_SINGLE_QUOTE) || (*brTemp.m_pb == CH_DOUBLE_QUOTE))
  4444. chDelimiter = *brTemp.m_pb;
  4445. if(chDelimiter)
  4446. {
  4447. // move past delimiter
  4448. brTemp.Advance(sizeof(chDelimiter));
  4449. if(brTemp.IsNull())
  4450. goto Exit;
  4451. }
  4452. // provisionally set value to temp byte range
  4453. brValue = brTemp;
  4454. // advance temp byte range to end of value range
  4455. while(
  4456. (chDelimiter && (*brTemp.m_pb != chDelimiter)) // if we have a delimiter, find next delimiter
  4457. || (!chDelimiter && (!FWhiteSpace(*brTemp.m_pb))) // if we have no delimiter, find next white space
  4458. )
  4459. {
  4460. // advance temp byte range
  4461. brTemp.Advance(1);
  4462. if(brTemp.IsNull())
  4463. {
  4464. if(chDelimiter)
  4465. // we found no closing delimiter, so error out
  4466. ThrowErrorSingleInsert(brValue.m_pb, IDE_TEMPLATE_NO_ATTRIBUTE_DELIMITER,
  4467. pbTagName, CCH_TOKEN(tknTagName));
  4468. else
  4469. // value runs to end of temp byte range, so exit (since we already init'ed to temp)
  4470. goto Exit;
  4471. }
  4472. }
  4473. // set byte count so that value points to delimited range
  4474. brValue.m_cb = DIFF(brTemp.m_pb - brValue.m_pb);
  4475. Exit:
  4476. // if tag is empty, raise an error
  4477. if (brValue.IsNull())
  4478. {
  4479. ThrowErrorSingleInsert(brTags.m_pb, IDE_TEMPLATE_VALUE_REQUIRED, pbTagName, CCH_TOKEN(tknTagName));
  4480. }
  4481. // enforce mandatory tag values if required
  4482. if(tknTagName == CTokenList::tknTagRunat)
  4483. {
  4484. if(!brValue.FMatchesSz(SZ_TOKEN(CTokenList::tknValueServer)))
  4485. ThrowError(brTags.m_pb, IDE_TEMPLATE_RUNAT_NOT_SERVER);
  4486. }
  4487. return brValue;
  4488. }
  4489. /* ============================================================================
  4490. CTemplate::CompTagName
  4491. Compares characters in two buffers (case-insensitive) and returns TRUE or FALSE
  4492. Side effects
  4493. none
  4494. */
  4495. BOOL
  4496. CTemplate::CompTagName
  4497. (
  4498. CByteRange &brTags, // tags byte range
  4499. _TOKEN tknTagName // tag name token
  4500. )
  4501. {
  4502. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4503. UINT cbAttributeName = CCH_TOKEN(tknTagName); // length of tag name
  4504. LPSTR pszAttributeName = SZ_TOKEN(tknTagName); // tag name string
  4505. // search for potential matches on tag name string, case-insensitive
  4506. if(!brTemp.IsNull())
  4507. if( 0 == _memicmp( brTemp.m_pb, pszAttributeName, cbAttributeName ))
  4508. return TRUE;
  4509. return FALSE;
  4510. }
  4511. /* ============================================================================
  4512. CTemplate::GetTagName
  4513. Returns a ptr to a tag name in a byte range; null if not found
  4514. Returns
  4515. ptr to tag name
  4516. Side effects
  4517. none
  4518. */
  4519. BYTE*
  4520. CTemplate::GetTagName
  4521. (
  4522. CByteRange brTags, // tags byte range
  4523. _TOKEN tknTagName // tag name token
  4524. )
  4525. {
  4526. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4527. UINT cbAttributeName = CCH_TOKEN(tknTagName); // length of tag name
  4528. LPSTR pszAttributeName = SZ_TOKEN(tknTagName); // tag name string
  4529. // PREFIX: pszAttributeName could be NULL, though I don't think that can happen.
  4530. Assert (pszAttributeName != NULL);
  4531. while(TRUE)
  4532. {
  4533. // search for potential matches on tag name string, case-insensitive
  4534. while(!brTemp.IsNull())
  4535. {
  4536. if(0 == _strnicmp((char*)brTemp.m_pb, pszAttributeName, cbAttributeName ))
  4537. break;
  4538. brTemp.Advance(1);
  4539. }
  4540. // if we did not find tag name string at all, return 'not found'
  4541. if(brTemp.IsNull())
  4542. goto NotFound;
  4543. // if it is a valid HTML tag name, return it
  4544. if(FTagName(brTemp.m_pb, cbAttributeName))
  4545. goto Exit;
  4546. // if we found a matching but invalid substring, advance beyond it so we can search again
  4547. brTemp.Advance(cbAttributeName);
  4548. // if we have exhausted search range, return 'not found'
  4549. if(brTemp.IsNull())
  4550. goto NotFound;
  4551. }
  4552. Exit:
  4553. return brTemp.m_pb;
  4554. NotFound:
  4555. return NULL;
  4556. }
  4557. /* ============================================================================
  4558. CTemplate::GetTag
  4559. Returns a ptr to a tag name in a byte range; null if not found
  4560. Returns
  4561. ptr to tag name
  4562. Side effects
  4563. none
  4564. */
  4565. BOOL
  4566. CTemplate::GetTag
  4567. (
  4568. CByteRange &brTags, // tags byte range
  4569. int nIndex
  4570. )
  4571. {
  4572. CByteRange brTemp = brTags; // local byte range, so we don't change tags byte range
  4573. int nTIndex = 0;
  4574. while(TRUE)
  4575. {
  4576. // locate the start of a tag by skipping past the script tag "<%" and any leading white space
  4577. //
  4578. while(!brTemp.IsNull())
  4579. {
  4580. if( *brTemp.m_pb == '<' ||
  4581. *brTemp.m_pb == '%' ||
  4582. *brTemp.m_pb == '@' ||
  4583. FWhiteSpace(*brTemp.m_pb))
  4584. {
  4585. brTemp.Advance(1);
  4586. brTags.Advance(1);
  4587. }
  4588. else
  4589. break;
  4590. }
  4591. // search for potential matches on tag name string, case-insensitive
  4592. //
  4593. while(!brTemp.IsNull())
  4594. {
  4595. if( *brTemp.m_pb == '=' || FWhiteSpace(*brTemp.m_pb))
  4596. {
  4597. nTIndex++;
  4598. break;
  4599. }
  4600. brTemp.Advance(1);
  4601. }
  4602. // if we did not find tag name string at all, return 'not found'
  4603. if(brTemp.IsNull())
  4604. goto NotFound;
  4605. // if it is a valid HTML tag name, return it
  4606. if(FTagName(brTags.m_pb, DIFF(brTemp.m_pb - brTags.m_pb)))
  4607. if(nTIndex >= nIndex)
  4608. goto Exit;
  4609. // position past named pair data and reset start and if end of byte range then
  4610. // goto NotFound
  4611. //
  4612. while(!brTemp.IsNull() && !FWhiteSpace(*brTemp.m_pb))
  4613. brTemp.Advance(1);
  4614. if(brTemp.IsNull())
  4615. goto NotFound;
  4616. else
  4617. brTags.Advance(DIFF(brTemp.m_pb - brTags.m_pb));
  4618. }
  4619. Exit:
  4620. return TRUE;
  4621. NotFound:
  4622. return FALSE;
  4623. }
  4624. /* ============================================================================
  4625. CTemplate::FTagHasValue
  4626. Do tags include tknTag=tknValue?
  4627. Returns
  4628. TRUE if tags include value, else FALSE
  4629. Side effects
  4630. none
  4631. */
  4632. BOOLB
  4633. CTemplate::FTagHasValue
  4634. (
  4635. const CByteRange& brTags, // tags byte range to search
  4636. _TOKEN tknTag, // tag token
  4637. _TOKEN tknValue // value token
  4638. )
  4639. {
  4640. return (BrValueOfTag(brTags, tknTag) // byte range of value
  4641. .FMatchesSz(SZ_TOKEN(tknValue)));
  4642. }
  4643. /* =========================
  4644. CTemplate::CopySzAdv
  4645. Copies a string to a ptr and advances the ptr just beyond the copied string.
  4646. Returns
  4647. Nothing
  4648. Side effects
  4649. advances ptr beyond copied string
  4650. */
  4651. void
  4652. CTemplate::CopySzAdv
  4653. (
  4654. char* pchWrite, // write location ptr
  4655. LPSTR psz // string to copy
  4656. )
  4657. {
  4658. strcpy(pchWrite, psz);
  4659. pchWrite += strlen(psz);
  4660. }
  4661. /* ============================================================================
  4662. CTemplate::WriteTemplate
  4663. Writes the template out to a contiguous block of memory.
  4664. Returns:
  4665. nothing
  4666. Side effects:
  4667. Allocates, and possibly re-allocates, memory for the template.
  4668. HERE IS HOW IT WORKS
  4669. --------------------
  4670. - an 'offset' is the count of bytes from start-of-template to a location
  4671. within template memory
  4672. - at the start of the template are 3 USHORTs, the counts of script blocks,
  4673. object-infos and HTML blocks, respectively
  4674. - next are 4 ULONGs, each an offset to a block of offsets; in order, these are:
  4675. offset-to-offset to first script engine name
  4676. offset-to-offset to first script block (the script text itself)
  4677. offset-to-offset to first object-info
  4678. offset-to-offset to first HTML block
  4679. - next are a variable number of ULONGs, each an offset to a particular
  4680. template component. In order these ULONGs are:
  4681. Offsets to Count of offsets
  4682. ---------- ----------------
  4683. script engine names cScriptBlocks
  4684. script blocks cScriptBlocks
  4685. object-infos cObjectInfos
  4686. HTML blocks cHTMLBlocks
  4687. - next are the template components themselves, stored sequentially
  4688. in the following order:
  4689. script engine names
  4690. script blocks
  4691. object-infos
  4692. HTML blocks
  4693. HERE IS HOW IT LOOKS
  4694. --------------------
  4695. |--|--|--| 3 template component counts (USHORTs)
  4696. |-- --|-- --| 4 offsets to template component offsets (ULONGs)
  4697. |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs)
  4698. |-- --| ............... |-- --|
  4699. |-- --|-- --|-- --|-- --|-- --|
  4700. | ........................... | template components
  4701. | ........................... |
  4702. | ........................... |
  4703. | ........................... |
  4704. or, mnemonically:
  4705. cS cO cH 3 template component counts (USHORTs)
  4706. offSE offSB offOb offHT 4 offsets to template component offsets (ULONGs)
  4707. |-- --|-- --|-- --|-- --|-- --| template component offsets (ULONGs)
  4708. |-- --| ............... |-- --|
  4709. |-- --|-- --|-- --|-- --|-- --|
  4710. | ........................... | template components
  4711. | ........................... |
  4712. | ........................... |
  4713. | ........................... |
  4714. */
  4715. void
  4716. CTemplate::WriteTemplate
  4717. (
  4718. CWorkStore& WorkStore, // working storage for source segments
  4719. CHitObj* pHitObj
  4720. )
  4721. {
  4722. USHORT i; // loop index
  4723. CByteRange brWrite; // general-purpose byte range for writing stuff out
  4724. USHORT cScriptBlocks = WorkStore.CRequiredScriptEngines(m_fGlobalAsa); // count of script blocks - 1 per required engine
  4725. USHORT cObjectInfos = WorkStore.m_ObjectInfoStore.Count(); // count of object-infos
  4726. USHORT cHTMLBlocks = WorkStore.m_bufHTMLSegments.Count(); // count of HTML segments
  4727. // Calc count of offset-to-offsets == total count of all scripts, objects, etc
  4728. // NOTE we keep separate offset-to-offsets for script engine names and script text, hence 2x
  4729. USHORT cBlockPtrs = (2 * cScriptBlocks) + cObjectInfos + cHTMLBlocks;
  4730. // Calc total memory required
  4731. // NOTE header includes counts and ptr-ptrs
  4732. UINT cbRequiredHeader = (C_COUNTS_IN_HEADER * sizeof(USHORT)) + (C_OFFOFFS_IN_HEADER * sizeof(BYTE**));
  4733. UINT cbRequiredBlockPtrs = cBlockPtrs * sizeof(BYTE*);
  4734. // Init write-offset locations
  4735. // offset to location for writing the next header information; header is at start of template
  4736. UINT cbHeaderOffset = 0;
  4737. // offset to location for writing the next offset-to-offset; immediately follows header
  4738. UINT cbOffsetToOffset = cbRequiredHeader;
  4739. // offset to location for writing the next block of data; immediately follows offset-to-offsets
  4740. UINT cbDataOffset = cbOffsetToOffset + cbRequiredBlockPtrs;
  4741. // offset in source file (for html blocks)
  4742. ULONG cbSourceOffset;
  4743. // source filename (only if include file)
  4744. BYTE *pbIncFilename;
  4745. ULONG cbIncFilename;
  4746. // Allocate memory and init start-ptr; bail on fail
  4747. // NOTE here we init template member variables m_pbStart and m_cbTemplate
  4748. if(NULL == (m_pbStart = (BYTE*) CTemplate::LargeMalloc(m_cbTemplate = CB_TEMPLATE_DEFAULT)))
  4749. THROW(E_OUTOFMEMORY);
  4750. // write out template header
  4751. WriteHeader(cScriptBlocks, cObjectInfos, cHTMLBlocks, &cbHeaderOffset, &cbOffsetToOffset);
  4752. // Reset offset-to-offset ptr to beginning of its section
  4753. cbOffsetToOffset = cbRequiredHeader;
  4754. // write script engine names and prog lang ids at current of data section
  4755. for(i = 0; i < WorkStore.m_ScriptStore.CountPreliminaryEngines(); i++)
  4756. {
  4757. // bug 933: only write non-empty script engines
  4758. if(WorkStore.FScriptEngineRequired(i, m_fGlobalAsa))
  4759. {
  4760. WorkStore.m_ScriptStore.m_bufEngineNames.GetItem(i, brWrite);
  4761. WriteByteRangeAdv(brWrite, TRUE, &cbDataOffset, &cbOffsetToOffset);
  4762. MemCpyAdv(&cbDataOffset, &(WorkStore.m_ScriptStore.m_rgProgLangId[i]), sizeof(PROGLANG_ID), sizeof(DWORD));
  4763. }
  4764. }
  4765. // write the script blocks for each engine at current of data section
  4766. // NOTE we sequence this after script engine names (rather than interleave)
  4767. USHORT idEngine = 0;
  4768. for(i = 0; i < WorkStore.m_ScriptStore.CountPreliminaryEngines(); i++)
  4769. {
  4770. // bug 933: only write non-empty script engines
  4771. if(WorkStore.FScriptEngineRequired(i, m_fGlobalAsa))
  4772. {
  4773. // bug 933: we need to pass both 'preliminary' engine id (i) and id of instantiated engine (idEngine)
  4774. WriteScriptBlockOfEngine(i, idEngine, WorkStore, &cbDataOffset, &cbOffsetToOffset, pHitObj);
  4775. idEngine++;
  4776. }
  4777. }
  4778. // Write object-infos at current of data section
  4779. for(i = 0; i < cObjectInfos; i++)
  4780. {
  4781. // get i-th object info from work store
  4782. WorkStore.m_ObjectInfoStore.m_bufObjectNames.GetItem(i, brWrite);
  4783. // write object name
  4784. WriteByteRangeAdv(brWrite, TRUE, &cbDataOffset, &cbOffsetToOffset);
  4785. // write clsid, scope and model
  4786. /* CONSIDER include if we need to byte-align clsid
  4787. // NOTE byte-align clsid (16-byte), which then byte-aligns scope and model (both 4-byte)
  4788. MemCpyAdv(&cbDataOffset, &(WorkStore.m_ObjectInfoStore.m_pObjectInfos[i].m_clsid), sizeof(CLSID), TRUE); */
  4789. MemCpyAdv(&cbDataOffset, &(WorkStore.m_ObjectInfoStore.m_pObjectInfos[i].m_clsid), sizeof(CLSID), sizeof(DWORD));
  4790. MemCpyAdv(&cbDataOffset, &(WorkStore.m_ObjectInfoStore.m_pObjectInfos[i].m_scope), sizeof(CompScope));
  4791. MemCpyAdv(&cbDataOffset, &(WorkStore.m_ObjectInfoStore.m_pObjectInfos[i].m_model), sizeof(CompModel));
  4792. }
  4793. // if other than globals template, write HTML blocks at current of data section
  4794. if(!m_fGlobalAsa)
  4795. for(i = 0; i < cHTMLBlocks; i++)
  4796. {
  4797. // write byterange with html code
  4798. WorkStore.m_bufHTMLSegments.GetItem(i, brWrite);
  4799. WriteByteRangeAdv(brWrite, TRUE, &cbDataOffset, &cbOffsetToOffset);
  4800. // source offset and include file
  4801. cbSourceOffset = 0;
  4802. pbIncFilename = NULL;
  4803. cbIncFilename = 0;
  4804. if (brWrite.m_pfilemap)
  4805. {
  4806. // calculate offset from filemap
  4807. CFileMap *pFileMap = (CFileMap *)brWrite.m_pfilemap;
  4808. if (pFileMap->m_pbStartOfFile) // mapped?
  4809. {
  4810. cbSourceOffset = DIFF(brWrite.m_pb - pFileMap->m_pbStartOfFile) + 1;
  4811. if (pFileMap->GetParent() != NULL && // is include file?
  4812. pFileMap->m_szPathInfo) // path exists
  4813. {
  4814. pbIncFilename = (BYTE *)pFileMap->m_szPathInfo;
  4815. cbIncFilename = _tcslen(pFileMap->m_szPathInfo)*sizeof(TCHAR);
  4816. }
  4817. }
  4818. }
  4819. // write them
  4820. MemCpyAdv(&cbDataOffset, &cbSourceOffset, sizeof(ULONG));
  4821. MemCpyAdv(&cbDataOffset, &cbIncFilename, sizeof(ULONG));
  4822. if (cbIncFilename > 0)
  4823. MemCpyAdv(&cbDataOffset, pbIncFilename, cbIncFilename+sizeof(TCHAR));
  4824. }
  4825. // trim template memory to exactly what we used
  4826. // NOTE cbDataOffset now contains maximum reach we have written to
  4827. if(NULL == (m_pbStart = (BYTE*) CTemplate::LargeReAlloc(m_pbStart, m_cbTemplate = cbDataOffset)))
  4828. THROW(E_OUTOFMEMORY);
  4829. }
  4830. /* ============================================================================
  4831. CTemplate::WriteHeader
  4832. Writes template header, and writes vesrion stamp and source file name into
  4833. template data region.
  4834. Returns
  4835. nothing
  4836. Side effects
  4837. none
  4838. */
  4839. void
  4840. CTemplate::WriteHeader
  4841. (
  4842. USHORT cScriptBlocks, // count of script blocks
  4843. USHORT cObjectInfos, // count of object-infos
  4844. USHORT cHTMLBlocks, // count of HTML blocks
  4845. UINT* pcbHeaderOffset, // ptr to offset value for header write location
  4846. UINT* pcbOffsetToOffset // ptr to offset value for offset-to-offset write location
  4847. )
  4848. {
  4849. // Write template component counts out at start of header
  4850. WriteShortAdv(cScriptBlocks, pcbHeaderOffset);
  4851. WriteShortAdv(cObjectInfos, pcbHeaderOffset);
  4852. WriteShortAdv(cHTMLBlocks, pcbHeaderOffset);
  4853. // Write offsets-to-offset to script engine names, script blocks, object-infos, HTML blocks
  4854. // NOTE counts of script engine names and script blocks are identical
  4855. WriteOffsetToOffset(cScriptBlocks, pcbHeaderOffset, pcbOffsetToOffset);
  4856. WriteOffsetToOffset(cScriptBlocks, pcbHeaderOffset, pcbOffsetToOffset);
  4857. WriteOffsetToOffset(cObjectInfos, pcbHeaderOffset, pcbOffsetToOffset);
  4858. WriteOffsetToOffset(cHTMLBlocks, pcbHeaderOffset, pcbOffsetToOffset);
  4859. }
  4860. /* ============================================================================
  4861. CTemplate::WriteScriptBlockOfEngine
  4862. Writes out script block for idEngine-th script engine.
  4863. NOTE segment buffer [0] contains primary script segments
  4864. segment buffer [1] contains tagged script segments of default engine
  4865. segment buffer [i] contains tagged script segments of (i-1)th engine, for i >= 2
  4866. Returns
  4867. nothing
  4868. Side effects
  4869. none
  4870. */
  4871. void
  4872. CTemplate::WriteScriptBlockOfEngine
  4873. (
  4874. USHORT idEnginePrelim, // preliminary script engine id (assigned during template pre-processing)
  4875. USHORT idEngine, // actual script engine id (written into compiled template)
  4876. CWorkStore& WorkStore, // working storage for source segments
  4877. UINT* pcbDataOffset, // ptr to write location offset value
  4878. UINT* pcbOffsetToOffset, // ptr to offset-to-offset offset value
  4879. CHitObj* pHitObj
  4880. )
  4881. {
  4882. // NOTE works for all id's - see comment above
  4883. USHORT iTSegBuffer = idEnginePrelim + 1; // index of tagged segment buffer
  4884. CByteRange brSegment; // current script segment
  4885. UINT i; // loop index
  4886. UINT cbScriptBlockOffset; // offset to script block write-location
  4887. // count of tagged script segments
  4888. UINT cTaggedSegments = WorkStore.m_ScriptStore.m_ppbufSegments[iTSegBuffer]->Count();
  4889. // Byte-align data offset location, since next thing we will write there is script block length
  4890. // NOTE we use brSegment.m_cb generically; we really want CByteRange::m_cb
  4891. ByteAlignOffset(pcbDataOffset, sizeof(brSegment.m_cb));
  4892. // Cache current data offset location as offset to start of script block
  4893. cbScriptBlockOffset = *pcbDataOffset;
  4894. // Write offset to start of script block at current offset-to-offset offset
  4895. WriteLongAdv(cbScriptBlockOffset, pcbOffsetToOffset);
  4896. // advance data ptr (by init'ing script length value to 0)
  4897. WriteLongAdv(0, pcbDataOffset);
  4898. // reset counter that AppendSourceInfo uses
  4899. m_cbTargetOffsetPrevT = 0;
  4900. // if other than globals template and this is default script engine (prelim engine 0),
  4901. // write primary script procedure at current of data section
  4902. if(!m_fGlobalAsa)
  4903. if(idEnginePrelim == 0)
  4904. WritePrimaryScriptProcedure(0, WorkStore, pcbDataOffset, cbScriptBlockOffset + sizeof(long));
  4905. // write out tagged script segments at current of data section
  4906. for(i = 0; i < cTaggedSegments; i++)
  4907. {
  4908. WorkStore.m_ScriptStore.m_ppbufSegments[iTSegBuffer]->GetItem(i, brSegment);
  4909. WriteScriptSegment(
  4910. idEngine,
  4911. m_rgpSegmentFilemaps[brSegment.m_idSequence],
  4912. brSegment,
  4913. pcbDataOffset,
  4914. cbScriptBlockOffset + sizeof(long),
  4915. FALSE /* fAllowExprWrite - disallowed for tagged script */
  4916. );
  4917. }
  4918. // Write out null terminator
  4919. MemCpyAdv(pcbDataOffset, SZ_NULL, 1);
  4920. // convert script text to unicode, so script engine won't have to do this at runtime
  4921. // ptr to start of script is:
  4922. // ptr start of template + offset to script + size of script length
  4923. LPSTR szScript = (LPSTR) m_pbStart + cbScriptBlockOffset + sizeof(ULONG);
  4924. /* script block length is:
  4925. == EndOfScriptText - StartOfScriptText
  4926. == (current of data ptr - length of null ) - (start of primary script byte range + length of br.m_cb)
  4927. */
  4928. ULONG cbScript = (*pcbDataOffset - sizeof(BYTE)) - (cbScriptBlockOffset + sizeof(ULONG));
  4929. /* bug 887: we append one extra "pseudo-line" to the end of source-infos array
  4930. to cover the case where the script engine reports back an error line number
  4931. that falls after end of script. We always want the "pseudo-line" to point to the
  4932. main file, so that the debugger can display something reasonable, so we pass
  4933. m_rgpFilemaps[0] as the source file, which is the main file.
  4934. */
  4935. AppendSourceInfo(idEngine, m_rgpFilemaps[0],
  4936. NULL, // Don't calculate line #
  4937. UINT_MAX, // Don't care & calculation is expensive
  4938. UINT_MAX, // Start of script blocks
  4939. UINT_MAX, // Really don't care
  4940. 0, // zero characters exist past EOF
  4941. TRUE); // Line is HTML (bogus)
  4942. // get wide string version of script text, using hitobj's code page
  4943. // NOTE we may slightly over-allocate space for wstrScript by using cbScript (e.g. if script contains DBCS).
  4944. // However, this won't matter since we call MultiByteToWideChar with -1, telling it to calc length of szScript
  4945. LPOLESTR wstrScript = NULL;
  4946. DWORD cbConvert = ( cbScript + 1 ) * 2;
  4947. STACK_BUFFER( tempScript, 2048 );
  4948. if (!tempScript.Resize(cbConvert)) {
  4949. THROW(E_OUTOFMEMORY);
  4950. }
  4951. wstrScript = (LPOLESTR)tempScript.QueryPtr();
  4952. MultiByteToWideChar( m_wCodePage, 0, szScript, -1, wstrScript, (cbScript + 1) );
  4953. // reset data offset location to start of script
  4954. *pcbDataOffset = cbScriptBlockOffset + sizeof(ULONG);
  4955. // write wide string script text over top of ansi version
  4956. MemCpyAdv(pcbDataOffset, wstrScript, sizeof(WCHAR) * cbScript);
  4957. // write wide string null terminator
  4958. MemCpyAdv(pcbDataOffset, WSTR_NULL, sizeof(WCHAR));
  4959. // write script length at start of script byte range
  4960. // NOTE we do this here because script length was initially unknown
  4961. WriteLongAdv(sizeof(WCHAR) * cbScript, &cbScriptBlockOffset);
  4962. }
  4963. /* ============================================================================
  4964. CTemplate::WritePrimaryScriptProcedure
  4965. Writes out default-engine primary script procedure.
  4966. If VBScript is default-engine, the primary script procedure contains
  4967. interleaved script commands and HTML block-writes, like this:
  4968. Sub Main
  4969. ...
  4970. [script segment]
  4971. Response.WriteBlock([HTML block id])
  4972. ...
  4973. [script segment]
  4974. Response.WriteBlock([HTML block id])
  4975. ...
  4976. [script segment]
  4977. Response.WriteBlock([HTML block id])
  4978. ...
  4979. End Sub
  4980. NOTE segment buffer [0] == primary script segments
  4981. Returns
  4982. nothing
  4983. Side effects
  4984. none
  4985. */
  4986. void
  4987. CTemplate::WritePrimaryScriptProcedure
  4988. (
  4989. USHORT idEngine, // script engine id
  4990. CWorkStore& WorkStore, // working storage for source segments
  4991. UINT* pcbDataOffset, // ptr to write location offset value
  4992. UINT cbScriptBlockOffset // ptr to start of script engine code
  4993. )
  4994. {
  4995. USHORT cScriptSegmentsProcessed = 0; // count of script blocks processed
  4996. USHORT cHTMLBlocksProcessed = 0; // count of HTML blocks processed
  4997. CByteRange brScriptNext; // next script block to write out
  4998. CByteRange brHTMLNext; // next HTML block to write out
  4999. char szHTMLBlockID[6]; // sz representation of HTML block ID - NOTE limited to 5 digits
  5000. // count of primary script segments
  5001. USHORT cPrimaryScriptSegments = WorkStore.m_ScriptStore.m_ppbufSegments[0]->Count();
  5002. // count of HTML blocks
  5003. USHORT cHTMLBlocks = WorkStore.m_bufHTMLSegments.Count();
  5004. CFileMap* pfilemap; // file where HTML segment lives in
  5005. // get initial script segment and initial html segment
  5006. if(cPrimaryScriptSegments)
  5007. WorkStore.m_ScriptStore.m_ppbufSegments[0]->GetItem(0, brScriptNext);
  5008. if(cHTMLBlocks)
  5009. WorkStore.m_bufHTMLSegments.GetItem(0, brHTMLNext);
  5010. // While HTML block(s) or primary script segment(s) remain to be processed ...
  5011. while((cHTMLBlocksProcessed < cHTMLBlocks) || (cScriptSegmentsProcessed < cPrimaryScriptSegments))
  5012. {
  5013. // If HTML block(s) remain to be processed ...
  5014. if(cHTMLBlocksProcessed < cHTMLBlocks)
  5015. while (TRUE)
  5016. {
  5017. // Write out write-block command for each HTML segment earlier in source than next script segment
  5018. if(brHTMLNext.FEarlierInSourceThan(brScriptNext) || (cScriptSegmentsProcessed >= cPrimaryScriptSegments))
  5019. {
  5020. // append source-info for the target script line we just manufactured
  5021. pfilemap = m_rgpSegmentFilemaps[brHTMLNext.m_idSequence];
  5022. AppendSourceInfo(idEngine, pfilemap,
  5023. NULL, // Don't calculate line #
  5024. DIFF(brHTMLNext.m_pb - pfilemap->m_pbStartOfFile), // line offset
  5025. cbScriptBlockOffset,
  5026. *pcbDataOffset - cbScriptBlockOffset, // character offset in target script
  5027. CharAdvDBCS((WORD)m_wCodePage, // length of the segment
  5028. reinterpret_cast<char *>(brHTMLNext.m_pb),
  5029. reinterpret_cast<char *>(brHTMLNext.m_pb + brHTMLNext.m_cb),
  5030. INFINITE, NULL),
  5031. TRUE); // Line is HTML text
  5032. // Get block number as an sz
  5033. _itoa(cHTMLBlocksProcessed, szHTMLBlockID, 10);
  5034. // Write out write-block opener
  5035. WriteSzAsBytesAdv(WorkStore.m_szWriteBlockOpen, pcbDataOffset);
  5036. // Write out block number
  5037. WriteSzAsBytesAdv(szHTMLBlockID, pcbDataOffset);
  5038. // Write out write-block closer and newline
  5039. WriteSzAsBytesAdv(WorkStore.m_szWriteBlockClose, pcbDataOffset);
  5040. WriteSzAsBytesAdv(SZ_NEWLINE, pcbDataOffset);
  5041. if(++cHTMLBlocksProcessed >= cHTMLBlocks)
  5042. break;
  5043. // Get next HTML block
  5044. WorkStore.m_bufHTMLSegments.GetItem(cHTMLBlocksProcessed, brHTMLNext);
  5045. }
  5046. else
  5047. break;
  5048. }
  5049. // if primary script segment(s) remain to be processed ...
  5050. if(cScriptSegmentsProcessed < cPrimaryScriptSegments)
  5051. while (TRUE)
  5052. {
  5053. // Write out each primary script segment earlier in the source file than the next HTML block
  5054. if(brScriptNext.FEarlierInSourceThan(brHTMLNext) || (cHTMLBlocksProcessed >= cHTMLBlocks))
  5055. {
  5056. WriteScriptSegment(
  5057. idEngine,
  5058. m_rgpSegmentFilemaps[brScriptNext.m_idSequence],
  5059. brScriptNext,
  5060. pcbDataOffset,
  5061. cbScriptBlockOffset,
  5062. TRUE /* fAllowExprWrite - allowed for primary script */
  5063. );
  5064. if(++cScriptSegmentsProcessed >= cPrimaryScriptSegments)
  5065. break;
  5066. // Get next script segment
  5067. WorkStore.m_ScriptStore.m_ppbufSegments[0]->GetItem(cScriptSegmentsProcessed, brScriptNext);
  5068. }
  5069. else
  5070. break;
  5071. }
  5072. }
  5073. }
  5074. /* ============================================================================
  5075. CTemplate::WriteScriptSegment
  5076. Writes a script segment to template memory line-by-line.
  5077. NOTE a 'script segment' is a piece (possibly all) of a 'script block'
  5078. Returns
  5079. nothing
  5080. Side effects
  5081. none
  5082. */
  5083. void
  5084. CTemplate::WriteScriptSegment
  5085. (
  5086. USHORT idEngine, // script engine id
  5087. CFileMap* pfilemap, // ptr to source file map
  5088. CByteRange& brScript, // byte range containing script segment
  5089. UINT* pcbDataOffset, // ptr to write location offset value
  5090. UINT cbScriptBlockOffset,// ptr to beginning of the script text
  5091. BOOL fAllowExprWrite // allow short-hand expression write?
  5092. )
  5093. {
  5094. CByteRange brLine; // byte range containing next line
  5095. UINT cbPtrOffset = 0; // ptr offset - 0 tells WriteByteRangeAdv 'ignore this'
  5096. BOOL fExpression = FALSE; // is current line an expression?
  5097. BOOL fCalcLineNumber = TRUE; // calc source line number?
  5098. BOOL fFirstLine = TRUE; // first line in script segment?
  5099. if(FByteRangeIsWhiteSpace(brScript))
  5100. return;
  5101. // trim white space from beginning of script segment
  5102. if (FIsLangVBScriptOrJScript(idEngine))
  5103. LTrimWhiteSpace(brScript);
  5104. while(!(brScript.IsNull()))
  5105. {
  5106. // fetch next line from byte range
  5107. // NOTE LineFromByteRangeAdv advances through brScript until brScript is null
  5108. LineFromByteRangeAdv(brScript, brLine);
  5109. if(FByteRangeIsWhiteSpace(brLine))
  5110. {
  5111. // if line is blank, don't process it; simply force calc of line number on next non-blank line
  5112. fCalcLineNumber = TRUE;
  5113. continue;
  5114. }
  5115. // line is non-blank; trim its white space
  5116. if (FIsLangVBScriptOrJScript(idEngine))
  5117. LTrimWhiteSpace(brLine);
  5118. RTrimWhiteSpace(brLine);
  5119. // append source-info to array; if flag is set, calc line number
  5120. // from location in source file; else, simply increment previous line number (NULL indicates this)
  5121. AppendSourceInfo(idEngine, pfilemap,
  5122. fCalcLineNumber? brLine.m_pb : NULL, // info to calc line #
  5123. DIFF(brLine.m_pb - pfilemap->m_pbStartOfFile), // line offset
  5124. cbScriptBlockOffset,
  5125. *pcbDataOffset - cbScriptBlockOffset, // character offset in target script
  5126. CharAdvDBCS((WORD)m_wCodePage, // statement length
  5127. reinterpret_cast<char *>(brLine.m_pb),
  5128. reinterpret_cast<char *>(brLine.m_pb + brLine.m_cb),
  5129. INFINITE, NULL),
  5130. FALSE); // HTML?
  5131. /* if it's true, set calc-line-number flag false
  5132. NOTE this is purely an optimization, to make the call to AppendSourceInfo faster
  5133. on subsequent calls within a contiguous block of non-blank lines
  5134. */
  5135. if(fCalcLineNumber)
  5136. fCalcLineNumber = FALSE;
  5137. if(fAllowExprWrite && fFirstLine)
  5138. {
  5139. // bug 912: test for remainder of script segment null on temp copy of script byte range, not on actual
  5140. CByteRange brTemp = brScript;
  5141. LTrimWhiteSpace(brTemp); // NOTE will nullify brScript if it is all white space
  5142. if(brTemp.IsNull())
  5143. {
  5144. /* if
  5145. a) expr-write is allowed AND
  5146. b) this is only script line in this segment (i.e. first line in segment and remainder of segment is null)
  5147. then, test this line to see if it is an expression.
  5148. NOTE test (b) fixes bug 785
  5149. if this line is an expression, create a script command that reads
  5150. Response.Write([line contents])
  5151. */
  5152. if(fExpression = FExpression(brLine))
  5153. {
  5154. Assert(idEngine == 0); // =expr is only enabled for primary engine
  5155. WriteSzAsBytesAdv(m_pWorkStore->m_szWriteOpen, pcbDataOffset);
  5156. }
  5157. // in this case only, set actual script to (now null) temp copy, since brScript governs while loop termination
  5158. brScript = brTemp;
  5159. }
  5160. }
  5161. Assert(FImplies(fExpression, fFirstLine)); // if an expr, must be first line in segment
  5162. Assert(FImplies(fExpression, brScript.IsNull())); // if an expr, no more script lines remain
  5163. Assert(FImplies(!fFirstLine, !fExpression)); // if not first line in segment, line cannot be expr
  5164. Assert(FImplies(!brScript.IsNull(), !fExpression)); // if script lines remain, line cannot be expr
  5165. // write out line contents
  5166. WriteScriptMinusEscapeChars(brLine, pcbDataOffset, &cbPtrOffset);
  5167. // if this line is an expression, close script command
  5168. if(fExpression)
  5169. WriteSzAsBytesAdv(m_pWorkStore->m_szWriteClose, pcbDataOffset);
  5170. // write new-line and set first-line flag false
  5171. WriteSzAsBytesAdv(SZ_NEWLINE, pcbDataOffset);
  5172. fFirstLine = FALSE;
  5173. }
  5174. }
  5175. /* ============================================================================
  5176. CTemplate::WriteScriptMinusEscapeChars
  5177. Writes a script byte range to memory, minus its escape characters, if any.
  5178. Returns:
  5179. Nothing.
  5180. Side effects:
  5181. None.
  5182. */
  5183. void
  5184. CTemplate::WriteScriptMinusEscapeChars
  5185. (
  5186. CByteRange brScript, // (ByVal) script byte range
  5187. UINT* pcbDataOffset, // offset where data will be written
  5188. UINT* pcbPtrOffset // offset where ptr will be written
  5189. )
  5190. {
  5191. BYTE* pbToken;
  5192. while(NULL != (pbToken = gm_pTokenList->GetToken(CTokenList::tknEscapedClosePrimaryScript, brScript, m_wCodePage)))
  5193. {
  5194. CByteRange brTemp = brScript;
  5195. // set temp range to source range up to escaped-token
  5196. brTemp.m_cb = DIFF(pbToken - brTemp.m_pb);
  5197. // write out temp range and actual-token - this replaces escaped-token with actual-token
  5198. WriteByteRangeAdv(brTemp, FALSE, pcbDataOffset, pcbPtrOffset);
  5199. WriteSzAsBytesAdv(SZ_TOKEN(CTokenList::tknClosePrimaryScript), pcbDataOffset);
  5200. //advance source range past escaped-token
  5201. brScript.Advance(DIFF(pbToken - brScript.m_pb) + CCH_TOKEN(CTokenList::tknEscapedClosePrimaryScript));
  5202. }
  5203. // write remainder of source range
  5204. WriteByteRangeAdv(brScript, FALSE, pcbDataOffset, pcbPtrOffset);
  5205. }
  5206. /* ============================================================================
  5207. CTemplate::FVbsComment
  5208. Determines whether a script line is a VBS comment.
  5209. NOTE caller must ensure that brLine is non-blank and has no leading white space
  5210. Returns
  5211. TRUE if the line is a VBS comment, else FALSE
  5212. Side effects
  5213. none
  5214. */
  5215. BOOLB
  5216. CTemplate::FVbsComment(CByteRange& brLine)
  5217. {
  5218. // CONSIDER: SCRIPTLANG generic comment token
  5219. if(!_strnicmp((LPCSTR)brLine.m_pb, SZ_TOKEN(CTokenList::tknVBSCommentSQuote), CCH_TOKEN(CTokenList::tknVBSCommentSQuote)))
  5220. return TRUE;
  5221. if(!_strnicmp((LPCSTR)brLine.m_pb, SZ_TOKEN(CTokenList::tknVBSCommentRem), CCH_TOKEN(CTokenList::tknVBSCommentRem)))
  5222. return TRUE;
  5223. return FALSE;
  5224. }
  5225. /* ============================================================================
  5226. CTemplate::FExpression
  5227. Determines whether a script line is an expression, and if so returns
  5228. just the expression in brLine.
  5229. NOTE caller must ensure that brLine has no leading white space
  5230. Returns
  5231. TRUE if the line is an expression, else FALSE
  5232. Side effects
  5233. none
  5234. */
  5235. BOOLB
  5236. CTemplate::FExpression(CByteRange& brLine)
  5237. {
  5238. // may be whitespace (other languages besides VB & JScript will have whitespace)
  5239. char *pchLine = reinterpret_cast<char *>(brLine.m_pb);
  5240. int cchLine = brLine.m_cb;
  5241. while (cchLine > 0 && FWhiteSpace(*pchLine))
  5242. {
  5243. --cchLine;
  5244. ++pchLine;
  5245. }
  5246. // if line starts with =, it is an expression: bypass =, left-trim whitespace and return true
  5247. if(cchLine > 0 && *pchLine == '=')
  5248. {
  5249. brLine.Advance(1 + DIFF(reinterpret_cast<BYTE *>(pchLine) - brLine.m_pb)); // OK to advance past whitespace now.
  5250. LTrimWhiteSpace(brLine);
  5251. return TRUE;
  5252. }
  5253. // else return false
  5254. return FALSE;
  5255. }
  5256. /**
  5257. ** In the following function names:
  5258. ** 'Adv' == 'advance offset after writing'
  5259. **/
  5260. /* ============================================================================
  5261. CTemplate::WriteOffsetToOffset
  5262. Writes a offset-to-offset offset (0 if no blocks) into header,
  5263. and advances header offset and offset-to-offset.
  5264. Returns:
  5265. Nothing.
  5266. Side effects:
  5267. Advances offsets.
  5268. */
  5269. void
  5270. CTemplate::WriteOffsetToOffset
  5271. (
  5272. USHORT cBlocks, // count of blocks
  5273. UINT* pcbHeaderOffset, // ptr to header offset value
  5274. UINT* pcbOffsetToOffset // ptr to offset-to-offset value
  5275. )
  5276. {
  5277. // if blocks of this type, write offset to first of them into header;
  5278. // if no blocks of this type, write 0 into header
  5279. WriteLongAdv((cBlocks > 0) ? *pcbOffsetToOffset : 0, pcbHeaderOffset);
  5280. // advance offset-to-offset offset
  5281. *pcbOffsetToOffset += cBlocks * sizeof(ULONG);
  5282. }
  5283. /* ============================================================================
  5284. CTemplate::WriteSzAsBytesAdv
  5285. Writes a null-terminated string as bytes, i.e. without its null terminator
  5286. and advances offset
  5287. Returns:
  5288. Nothing.
  5289. Side effects:
  5290. Advances offset.
  5291. */
  5292. void
  5293. CTemplate::WriteSzAsBytesAdv
  5294. (
  5295. LPCSTR szSource, // source string
  5296. UINT* pcbDataOffset // ptr to offset value
  5297. )
  5298. {
  5299. if((szSource == NULL) || (*szSource == '\0'))
  5300. return;
  5301. MemCpyAdv(pcbDataOffset, (void*) szSource, strlen(szSource));
  5302. }
  5303. /* ============================================================================
  5304. CTemplate::WriteByteRangeAdv
  5305. Writes a byte range to memory at template offset location *pcbDataOffset and, optionally,
  5306. writes a ptr to the written data at template offset location *pcbPtrOffset
  5307. (pass *pcbPtrOffset == 0 to avoid this)
  5308. fWriteAsBsz == FALSE --> write only byte range's data
  5309. fWriteAsBsz == TRUE --> write length, followed by data, followed by NULL
  5310. NOTE bsz == length-prefixed, null-terminated string
  5311. Returns:
  5312. Nothing.
  5313. Side effects:
  5314. Advances offset(s).
  5315. */
  5316. void
  5317. CTemplate::WriteByteRangeAdv
  5318. (
  5319. CByteRange& brSource, // source data
  5320. BOOLB fWriteAsBsz, // write as bsz?
  5321. UINT* pcbDataOffset, // offset where data will be written
  5322. UINT* pcbPtrOffset // offset where ptr will be written
  5323. )
  5324. {
  5325. // bail if source is empty
  5326. if(brSource.IsNull())
  5327. return;
  5328. // If writing as a bsz, write length prefix
  5329. if(fWriteAsBsz)
  5330. WriteLongAdv(brSource.m_cb, pcbDataOffset);
  5331. // Write data
  5332. MemCpyAdv(pcbDataOffset, brSource.m_pb, brSource.m_cb);
  5333. // If writing as a bsz, write null terminator and advance target ptr
  5334. if(fWriteAsBsz)
  5335. MemCpyAdv(pcbDataOffset, SZ_NULL, 1);
  5336. // If caller passed a non-zero ptr offset, write offset to data there
  5337. if(*pcbPtrOffset > 0)
  5338. {
  5339. if(fWriteAsBsz)
  5340. /* if writing as a bsz ...
  5341. offset to start of data == current data offset
  5342. - null terminator
  5343. - data length
  5344. - sizeof length prefix
  5345. */
  5346. WriteLongAdv(*pcbDataOffset - 1 - brSource.m_cb - sizeof(brSource.m_cb), pcbPtrOffset);
  5347. else
  5348. // else, offset to start of data == current data offset - data length
  5349. WriteLongAdv(*pcbDataOffset - brSource.m_cb, pcbPtrOffset);
  5350. }
  5351. }
  5352. /*===================================================================
  5353. CTemplate::MemCpyAdv
  5354. Copies from a memory location to a template offset location,
  5355. and advances offset.
  5356. Returns:
  5357. Nothing.
  5358. Side effects:
  5359. Advances offset.
  5360. Re-allocates memory if required.
  5361. */
  5362. void
  5363. CTemplate::MemCpyAdv
  5364. (
  5365. UINT* pcbOffset, // ptr to offset value
  5366. void* pbSource, // ptr to source
  5367. ULONG cbSource, // length of source
  5368. UINT cbByteAlign // align bytes on short/long/dword boundary?
  5369. )
  5370. {
  5371. // byte-align offset location before write, if specified by caller
  5372. if(cbByteAlign > 0)
  5373. ByteAlignOffset(pcbOffset, cbByteAlign);
  5374. // calc number of bytes by which to grow allocated template memory:
  5375. // if projected reach exceeds current reach, we need to grow by the difference;
  5376. // else, no need to grow
  5377. if((*pcbOffset + cbSource) > m_cbTemplate)
  5378. {
  5379. // Reallocate space for storing local data - we grab twice what we had before
  5380. // or twice current growth requirement, whichever is more
  5381. m_cbTemplate = 2 * max(m_cbTemplate, (*pcbOffset + cbSource) - m_cbTemplate);
  5382. if(NULL == (m_pbStart = (BYTE*) CTemplate::LargeReAlloc(m_pbStart, m_cbTemplate)))
  5383. THROW(E_OUTOFMEMORY);
  5384. }
  5385. // copy source to template offset location
  5386. memcpy(m_pbStart + *pcbOffset, pbSource, cbSource);
  5387. // advance offset location
  5388. *pcbOffset += cbSource;
  5389. }
  5390. /* ============================================================================
  5391. CTemplate::GetAddress
  5392. Returns a ptr to the i-th object of type tcomp
  5393. */
  5394. BYTE*
  5395. CTemplate::GetAddress
  5396. (
  5397. TEMPLATE_COMPONENT tcomp,
  5398. USHORT i
  5399. )
  5400. {
  5401. DWORD* pdwBase;
  5402. Assert(NULL != m_pbStart);
  5403. // refer to CTemplate::WriteTemplate comments for the structure of what this is dealing with
  5404. pdwBase = (DWORD*)(m_pbStart + (C_COUNTS_IN_HEADER * sizeof(USHORT)));
  5405. // tcomp types are ptr-to-ptrs
  5406. DWORD* pdwTcompBase = (DWORD *) (m_pbStart + pdwBase[tcomp]);
  5407. return m_pbStart + pdwTcompBase[i];
  5408. }
  5409. /* ============================================================================
  5410. CTemplate::AppendSourceInfo
  5411. Appends a source line number for the current target line
  5412. NOTE if caller passes null source ptr, we append prev source line number + 1
  5413. Returns
  5414. Nothing
  5415. Side effects
  5416. allocates memory first time thru; may realloc
  5417. */
  5418. void
  5419. CTemplate::AppendSourceInfo
  5420. (
  5421. USHORT idEngine, // script engine id
  5422. CFileMap* pfilemap, // ptr to source file map
  5423. BYTE* pbSource, // ptr to current location in source file
  5424. ULONG cbSourceOffset, // byte offset of line in source file
  5425. ULONG cbScriptBlockOffset, // pointer to start of script text
  5426. ULONG cbTargetOffset, // character offset of line in target file
  5427. ULONG cchSourceText, // # of characters in source text
  5428. BOOL fIsHTML // TRUE if manufactured line
  5429. )
  5430. {
  5431. UINT i; // loop index
  5432. CSourceInfo si; // temporary CSourceInfo structure
  5433. vector<CSourceInfo> *prgSourceInfos; // pointer to line mapping table for the engine
  5434. ULONG cchSourceOffset = 0;// cch corresponding to cbSourceOffset
  5435. HRESULT hr = S_OK;
  5436. // if arrays are not yet allocated, allocate them
  5437. if (m_rgrgSourceInfos == NULL)
  5438. {
  5439. // transfer count of script engines from workstore to template
  5440. m_cScriptEngines = m_pWorkStore->CRequiredScriptEngines(m_fGlobalAsa);
  5441. // one source-info array per engine
  5442. if ((m_rgrgSourceInfos = new vector<CSourceInfo>[m_cScriptEngines]) == NULL)
  5443. THROW (E_OUTOFMEMORY);
  5444. }
  5445. // new script engine must be allocated in IdEngineFromBr (way upstream of this point),
  5446. // so we assert that current engine must already be covered
  5447. Assert(idEngine < m_pWorkStore->CRequiredScriptEngines(m_fGlobalAsa));
  5448. /* set current target line's source line number (SLN):
  5449. a) if caller passed a source ptr, calc SLN from the source ptr;
  5450. b) else if caller passed a filemap ptr, set SLN to prev target line's SLN plus one;
  5451. c) else set SLN to 0
  5452. semantics:
  5453. a) we have a source file location, but must calc a line # for that location
  5454. b) caller tells us (by passing NULL source file location) that this target line
  5455. immediately follows prev target line. This is an optimization because
  5456. SourceLineNumberFromPb is very slow.
  5457. change:
  5458. caller used to pass NULL filemap ptr that target line is 'manufactured'
  5459. i.e. has no corresponding authored line in source file
  5460. HOWEVER - now filemap ptr must NOT be NULL because 'manufactured' lines
  5461. are also stored in the file map array
  5462. */
  5463. Assert (pfilemap != NULL);
  5464. prgSourceInfos = &m_rgrgSourceInfos[idEngine];
  5465. if (pbSource == NULL)
  5466. {
  5467. if (prgSourceInfos->length() == 0)
  5468. si.m_idLine = 1;
  5469. else
  5470. si.m_idLine = (*prgSourceInfos)[prgSourceInfos->length() - 1].m_idLine + 1;
  5471. }
  5472. else
  5473. si.m_idLine = SourceLineNumberFromPb(pfilemap, pbSource);
  5474. // The EOF line does not have a source offset (caller passes -1 (UINT_MAX)). For this case, no
  5475. // DBCS calculations etc. should be done. (set cchSourceOffset to UINT_MAX).
  5476. if (cbSourceOffset == UINT_MAX)
  5477. cchSourceOffset = UINT_MAX;
  5478. else
  5479. {
  5480. // BUG 80901: Source offset needs to point to the beginning of leading white space on the line
  5481. // Adjust source length by one as we decrement source offset
  5482. // Note: whitepsace is never trailing byte, so loop will work with DBCS encoded character sets
  5483. while (cbSourceOffset > 0 && strchr(" \t\v\a\f", pfilemap->m_pbStartOfFile[cbSourceOffset - 1]))
  5484. {
  5485. --cbSourceOffset;
  5486. ++cchSourceText;
  5487. }
  5488. // BUG 95859
  5489. // If the cursor is on the opening token of a script block (the "<%" part of a line), the
  5490. // BP is set in the previous HTML, not in the script block, as is desired.
  5491. //
  5492. // To correct this, if we are in a script block, scan back two characters, see if it is the open
  5493. // token. If it is, set the offset back two, and add two to the length.
  5494. //
  5495. if (!fIsHTML)
  5496. {
  5497. // Skip whitespace (including newlines -- the previous step did not skip newlines)
  5498. //
  5499. ULONG cbOpen = cbSourceOffset;
  5500. while (cbOpen > 0 && strchr(" \t\v\a\f\r\n", pfilemap->m_pbStartOfFile[cbOpen - 1]))
  5501. --cbOpen;
  5502. if (cbOpen >= 2 && strncmp(reinterpret_cast<char *>(&pfilemap->m_pbStartOfFile[cbOpen - 2]), "<%", 2) == 0)
  5503. {
  5504. cbOpen -= 2;
  5505. cchSourceText += cbSourceOffset - cbOpen;
  5506. cbSourceOffset = cbOpen;
  5507. }
  5508. // Look for trailing "%>" in this snippet, and if it exists then include the end delimiter in
  5509. // the length. NOTE: No DBCS awareness needed here - if we find a lead byte we just get out
  5510. // of the loop. We are looking for <whitespace>*"%>" which is totally SBCS chars.
  5511. //
  5512. ULONG cbClose = cbSourceOffset + cchSourceText;
  5513. ULONG cbFile = pfilemap->GetSize();
  5514. while (cbClose < cbFile && strchr(" \t\v\a\f\r\n", pfilemap->m_pbStartOfFile[cbClose]))
  5515. ++cbClose;
  5516. if (cbClose < cbFile && strncmp(reinterpret_cast<char *>(&pfilemap->m_pbStartOfFile[cbClose]), "%>", 2) == 0)
  5517. cchSourceText += cbClose - (cbSourceOffset + cchSourceText) + 2;
  5518. }
  5519. // BUG 82222, 85584
  5520. // Compiler marks HTML segments starting with the newline on the previous line
  5521. // if the line ends with %>.
  5522. //
  5523. // This screws up the debugger, becasue when you press <F9>, the pointer is placed
  5524. // on the line above when it should point to the start of the whitespace on the next line.
  5525. if (fIsHTML)
  5526. {
  5527. UINT cbEOF = pfilemap->GetSize(), cbRover = cbSourceOffset;
  5528. // Skip initial whitespace
  5529. while (cbRover < cbEOF && strchr(" \t\a\f", pfilemap->m_pbStartOfFile[cbRover]))
  5530. ++cbRover;
  5531. // If what's left is a CR/LF pair, then advance cbSourceOffset to next line
  5532. BOOL fCR = FALSE, fLF = FALSE;
  5533. if (cbRover < cbEOF && strchr("\r\n", pfilemap->m_pbStartOfFile[cbRover]))
  5534. {
  5535. fCR = pfilemap->m_pbStartOfFile[cbRover] == '\r';
  5536. fLF = pfilemap->m_pbStartOfFile[cbRover] == '\n';
  5537. ++cbRover;
  5538. Assert (fCR || fLF);
  5539. }
  5540. // we allow either <CR>, <LF>, <CR><LF>, or <LF><CR> to terminate a line,
  5541. // so look for its opposite terminator if one is found (but don't require it)
  5542. if (fCR && cbRover < cbEOF && pfilemap->m_pbStartOfFile[cbRover] == '\n')
  5543. ++cbRover;
  5544. if (fLF && cbRover < cbEOF && pfilemap->m_pbStartOfFile[cbRover] == '\r')
  5545. ++cbRover;
  5546. // OK, adjust cbSourceOffset now
  5547. if ((fCR || fLF) && cbRover < cbEOF)
  5548. {
  5549. cchSourceText -= cbRover - cbSourceOffset; // adjust # of chars to select
  5550. cbSourceOffset = cbRover;
  5551. }
  5552. }
  5553. // Now that we have the source offset, calculate its CCH by finding
  5554. // the last time we sampled the value, then add that to the number
  5555. // of DBCS characters from that point to the current offset.
  5556. //
  5557. // For the case of includes, it's possible offset already exists
  5558. // (if the entry was previously generated by another instance of
  5559. // #include - therefore we have to search)
  5560. COffsetInfo *pOffsetInfoLE, *pOffsetInfoGE;
  5561. GetBracketingPair(
  5562. cbSourceOffset, // value to find
  5563. pfilemap->m_rgByte2DBCS.begin(), // beginning of array
  5564. pfilemap->m_rgByte2DBCS.end(), // end of array
  5565. CByteOffsetOrder(), // search for byte offset
  5566. &pOffsetInfoLE, &pOffsetInfoGE // return values
  5567. );
  5568. // If we find an equal match, don't insert any duplicates
  5569. if (pOffsetInfoLE == NULL || pOffsetInfoLE->m_cbOffset < cbSourceOffset)
  5570. {
  5571. // if pOffsetInfoLE is NULL, it means that the array is empty -
  5572. // create the mapping of offset 0 to offset 0.
  5573. //
  5574. // In the case of the first line of a file being an include directive,
  5575. // the first executable line from the file may not start at offset zero,
  5576. // so in this case we need to create this entry AND execute the next "if"
  5577. // block.
  5578. //
  5579. if (pOffsetInfoLE == NULL)
  5580. {
  5581. COffsetInfo oiZero; // ctor will init
  5582. if (FAILED(hr = pfilemap->m_rgByte2DBCS.append(oiZero)))
  5583. THROW(hr);
  5584. pOffsetInfoLE = pfilemap->m_rgByte2DBCS.begin();
  5585. Assert (pOffsetInfoLE != NULL);
  5586. }
  5587. // If cbSourceOffset is zero, we handled it above
  5588. if (cbSourceOffset != 0)
  5589. {
  5590. cchSourceOffset = pOffsetInfoLE->m_cchOffset +
  5591. CharAdvDBCS
  5592. (
  5593. (WORD)m_wCodePage,
  5594. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile + pOffsetInfoLE->m_cbOffset),
  5595. reinterpret_cast<char *>(pfilemap->m_pbStartOfFile + cbSourceOffset),
  5596. INFINITE,
  5597. NULL
  5598. );
  5599. // Now add the value to the table
  5600. COffsetInfo oi;
  5601. oi.m_cchOffset = cchSourceOffset;
  5602. oi.m_cbOffset = cbSourceOffset;
  5603. if (pOffsetInfoGE == NULL) // No offset greater
  5604. hr = pfilemap->m_rgByte2DBCS.append(oi);
  5605. else
  5606. hr = pfilemap->m_rgByte2DBCS.insertAt(DIFF(pOffsetInfoGE - pfilemap->m_rgByte2DBCS.begin()), oi);
  5607. if (FAILED(hr))
  5608. THROW(hr);
  5609. }
  5610. }
  5611. else
  5612. {
  5613. // If we're not adding anything for the table, Assert it's because there's
  5614. // a duplicate item
  5615. Assert (cbSourceOffset == pOffsetInfoLE->m_cbOffset);
  5616. cchSourceOffset = pOffsetInfoLE->m_cchOffset;
  5617. }
  5618. }
  5619. UINT cchTargetOffset = UINT_MAX;
  5620. if (cbTargetOffset != UINT_MAX)
  5621. {
  5622. // ptr to start of script is:
  5623. // ptr start of template + offset to script + size of script length
  5624. LPSTR szScript = (LPSTR) m_pbStart + cbScriptBlockOffset;
  5625. // Calculate cchTargetOffset (have the cb). The cch is the number of characters since the
  5626. // last cch calculated in the end of the array.
  5627. //
  5628. if (prgSourceInfos->length() > 0)
  5629. cchTargetOffset = (*prgSourceInfos)[prgSourceInfos->length() - 1].m_cchTargetOffset;
  5630. else
  5631. cchTargetOffset = 0;
  5632. cchTargetOffset += CharAdvDBCS
  5633. (
  5634. (WORD) m_wCodePage,
  5635. &szScript[m_cbTargetOffsetPrevT],
  5636. &szScript[cbTargetOffset],
  5637. INFINITE,
  5638. NULL
  5639. );
  5640. // Keeps track of offsets during compilation
  5641. //
  5642. m_cbTargetOffsetPrevT = cbTargetOffset;
  5643. }
  5644. // Store this record and move on.
  5645. //
  5646. si.m_pfilemap = pfilemap;
  5647. si.m_fIsHTML = fIsHTML;
  5648. si.m_cchSourceOffset = cchSourceOffset;
  5649. si.m_cchTargetOffset = cchTargetOffset;
  5650. si.m_cchSourceText = cchSourceText;
  5651. if (FAILED(prgSourceInfos->append(si)))
  5652. THROW(hr);
  5653. }
  5654. /* ============================================================================
  5655. CTemplate::SourceLineNumberFromPb
  5656. Returns the starting source line number for the given source file location
  5657. */
  5658. UINT
  5659. CTemplate::SourceLineNumberFromPb
  5660. (
  5661. CFileMap* pfilemap, // ptr to source file map
  5662. BYTE* pbSource // ptr to current location in source file
  5663. )
  5664. {
  5665. UINT cSourceLines = 1; // count of lines into source file
  5666. CByteRange brScan; // byte range to scan for newlines
  5667. CByteRange brSOL; // start-of-line ptr
  5668. if(pbSource == NULL || pfilemap == NULL)
  5669. return 0;
  5670. // Determine if there was a state stored on this ASP script earlier. If yes, then restore state.
  5671. // If the line being requested is prior to the saved state...Bail out and restart from the begining.
  5672. if(m_pWorkStore->m_cPrevSourceLines &&
  5673. (m_pWorkStore->m_pbPrevSource && (m_pWorkStore->m_pbPrevSource < pbSource)) &&
  5674. (m_pWorkStore->m_hPrevFile && (pfilemap->m_hFile==m_pWorkStore->m_hPrevFile)))
  5675. {
  5676. // The file handles match:with means that we are evaluating the current file. Restore state.
  5677. brScan.m_pb = m_pWorkStore->m_pbPrevSource;
  5678. brScan.m_cb = max(DIFF(pbSource - brScan.m_pb), 0);
  5679. cSourceLines = m_pWorkStore->m_cPrevSourceLines;
  5680. }
  5681. else
  5682. {
  5683. // set scan range to run from start-of-template to caller's ptr
  5684. brScan.m_pb = pfilemap->m_pbStartOfFile;
  5685. brScan.m_cb = max(DIFF(pbSource - brScan.m_pb), 0);
  5686. }
  5687. // get newlines in scan range
  5688. brSOL = BrNewLine(brScan);
  5689. while(!brSOL.IsNull())
  5690. {
  5691. // advance start-of-line ptr and scan byte range
  5692. brScan.Advance(DIFF((brSOL.m_pb + brSOL.m_cb) - brScan.m_pb));
  5693. // increment source line counter
  5694. cSourceLines++;
  5695. // find next newline
  5696. brSOL = BrNewLine(brScan);
  5697. }
  5698. // Store the state for the next call.
  5699. m_pWorkStore->m_pbPrevSource = pbSource;
  5700. m_pWorkStore->m_cPrevSourceLines = cSourceLines;
  5701. m_pWorkStore->m_hPrevFile = pfilemap->m_hFile;
  5702. return cSourceLines;
  5703. }
  5704. /* ============================================================================
  5705. CTemplate::RemoveFromIncFiles
  5706. Removes this template from inc-files on which it depends
  5707. Returns:
  5708. Nothing
  5709. Side effects:
  5710. None
  5711. */
  5712. void
  5713. CTemplate::RemoveFromIncFiles
  5714. (
  5715. )
  5716. {
  5717. // NOTE we loop from 1 to count, since 0-th filemap is for main file
  5718. for(UINT i = 1; i < m_cFilemaps; i++)
  5719. {
  5720. if(NULL != m_rgpFilemaps[i]->m_pIncFile)
  5721. m_rgpFilemaps[i]->m_pIncFile->RemoveTemplate(this);
  5722. }
  5723. }
  5724. /* ****************************************************************************
  5725. IDebugDocumentProvider implementation
  5726. */
  5727. /* ============================================================================
  5728. CTemplate::GetDocument
  5729. Return a pointer to the IDebugDocument implementation. (same object in this case)
  5730. Returns:
  5731. *ppDebugDoc is set to "this".
  5732. Notes:
  5733. always succeeds
  5734. */
  5735. HRESULT CTemplate::GetDocument
  5736. (
  5737. IDebugDocument **ppDebugDoc
  5738. )
  5739. {
  5740. return QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDoc));
  5741. }
  5742. /* ============================================================================
  5743. CTemplate::GetName
  5744. Return the various names of a document.
  5745. */
  5746. HRESULT CTemplate::GetName
  5747. (
  5748. /* [in] */ DOCUMENTNAMETYPE doctype,
  5749. /* [out] */ BSTR *pbstrName
  5750. )
  5751. {
  5752. TCHAR *szPathInfo = m_rgpFilemaps[0]->m_szPathInfo;
  5753. switch (doctype) {
  5754. case DOCUMENTNAMETYPE_APPNODE:
  5755. case DOCUMENTNAMETYPE_FILE_TAIL:
  5756. case DOCUMENTNAMETYPE_TITLE:
  5757. // Skip application path portion of the filename
  5758. {
  5759. // Make sure the template remembers the virtual path
  5760. // from the same application (it could be different
  5761. // if template is shared between two applications)
  5762. //
  5763. int cch = _tcslen(m_szApplnVirtPath);
  5764. if (_tcsncicmp(szPathInfo, m_szApplnVirtPath, cch) == 0)
  5765. szPathInfo += cch;
  5766. // Strip leading '/'
  5767. if (*szPathInfo == _T('/'))
  5768. szPathInfo++;
  5769. #if UNICODE
  5770. *pbstrName = SysAllocString(szPathInfo);
  5771. if (*pbstrName == NULL)
  5772. return E_OUTOFMEMORY;
  5773. return S_OK;
  5774. #else
  5775. return SysAllocStringFromSz(szPathInfo, 0, pbstrName, m_wCodePage);
  5776. #endif
  5777. }
  5778. case DOCUMENTNAMETYPE_URL:
  5779. // prefix with the URL, use szPathInfo for the rest of the path
  5780. {
  5781. STACK_BUFFER( tempName, MAX_PATH );
  5782. int cbURLPrefix = DIFF(m_szApplnVirtPath - m_szApplnURL) * sizeof (TCHAR);
  5783. if (!tempName.Resize(cbURLPrefix + (_tcslen(szPathInfo)*sizeof(TCHAR)) + sizeof(TCHAR))) {
  5784. return E_OUTOFMEMORY;
  5785. }
  5786. TCHAR *szURL = (TCHAR *)tempName.QueryPtr();
  5787. memcpy(szURL, m_szApplnURL, cbURLPrefix);
  5788. _tcscpy(&szURL[cbURLPrefix/sizeof(TCHAR)], szPathInfo);
  5789. #if UNICODE
  5790. *pbstrName = SysAllocString(szURL);
  5791. if (*pbstrName == NULL)
  5792. return E_OUTOFMEMORY;
  5793. return S_OK;
  5794. #else
  5795. return SysAllocStringFromSz(szURL, 0, pbstrName, m_wCodePage);
  5796. #endif
  5797. }
  5798. default:
  5799. return E_FAIL;
  5800. }
  5801. }
  5802. /* ****************************************************************************
  5803. IDebugDocumentText implementation
  5804. */
  5805. /* ============================================================================
  5806. CTemplate::GetSize
  5807. Return the number of lines & characters in the document
  5808. */
  5809. HRESULT CTemplate::GetSize
  5810. (
  5811. /* [out] */ ULONG *pcLines,
  5812. /* [out] */ ULONG *pcChars
  5813. )
  5814. {
  5815. /*
  5816. * NOTE: compilation is done in two phases.
  5817. * Errors are detected and reported in phase 1.
  5818. * The DBCS mapping is created in phase 2.
  5819. *
  5820. * If an error occurred during compilation, m_cChars will be equal to zero
  5821. * (Since zero length files are not compiled, m_cChars == 0 means "size
  5822. * is unknown", not "size is zero").
  5823. */
  5824. if (m_rgpFilemaps[0]->m_cChars == 0)
  5825. {
  5826. // Likely need to remap the file, then count
  5827. BOOL fRemapTemplate = !m_rgpFilemaps[0]->FIsMapped();
  5828. if (fRemapTemplate)
  5829. TRY
  5830. m_rgpFilemaps[0]->RemapFile();
  5831. CATCH (dwException)
  5832. return E_FAIL;
  5833. END_TRY
  5834. m_rgpFilemaps[0]->CountChars((WORD)m_wCodePage);
  5835. if (fRemapTemplate)
  5836. TRY
  5837. m_rgpFilemaps[0]->UnmapFile();
  5838. CATCH (dwException)
  5839. return E_FAIL;
  5840. END_TRY
  5841. // let's hope client is not relying on # of lines - expensive to compute
  5842. *pcChars = m_rgpFilemaps[0]->m_cChars;
  5843. *pcLines = ULONG_MAX;
  5844. }
  5845. else
  5846. {
  5847. /* The last line in the line mapping array of each engine is the <<EOF>> line
  5848. * for that engine. Therefore, the # of lines is the largest <<EOF>> line
  5849. * number - 1. The EOF line always points into the main file, so there are no
  5850. * include file glitches here.
  5851. */
  5852. ULONG cLinesMax = 0;
  5853. for (UINT i = 0; i < m_cScriptEngines; ++i)
  5854. {
  5855. ULONG cLinesCurrentEngine = m_rgrgSourceInfos[0][m_rgrgSourceInfos[0].length() - 1].m_idLine - 1;
  5856. if (cLinesCurrentEngine > cLinesMax)
  5857. cLinesMax = cLinesCurrentEngine;
  5858. }
  5859. *pcLines = cLinesMax;
  5860. *pcChars = m_rgpFilemaps[0]->m_cChars;
  5861. }
  5862. IF_DEBUG(SCRIPT_DEBUGGER) {
  5863. #if UNICODE
  5864. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%S\") returns %lu characters (%lu lines)\n", m_rgpFilemaps[0]->m_szPathTranslated, *pcChars, *pcLines));
  5865. #else
  5866. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%s\") returns %lu characters (%lu lines)\n", m_rgpFilemaps[0]->m_szPathTranslated, *pcChars, *pcLines));
  5867. #endif
  5868. }
  5869. return S_OK;
  5870. }
  5871. /* ============================================================================
  5872. CTemplate::GetDocumentAttributes
  5873. Return doc attributes
  5874. */
  5875. HRESULT CTemplate::GetDocumentAttributes
  5876. (
  5877. /* [out] */ TEXT_DOC_ATTR *ptextdocattr
  5878. )
  5879. {
  5880. // Easy way to tell debugger that we don't support editing.
  5881. *ptextdocattr = TEXT_DOC_ATTR_READONLY;
  5882. return S_OK;
  5883. }
  5884. /* ============================================================================
  5885. CTemplate::GetPositionOfLine
  5886. From a line number, return the character offset of the beginning
  5887. */
  5888. HRESULT CTemplate::GetPositionOfLine
  5889. (
  5890. /* [in] */ ULONG cLineNumber,
  5891. /* [out] */ ULONG *pcCharacterPosition
  5892. )
  5893. {
  5894. return GetPositionOfLine(m_rgpFilemaps[0], cLineNumber, pcCharacterPosition);
  5895. }
  5896. /* ============================================================================
  5897. CTemplate::GetLineOfPosition
  5898. From a character offset, return the line number and offset within the line
  5899. */
  5900. HRESULT CTemplate::GetLineOfPosition
  5901. (
  5902. /* [in] */ ULONG cCharacterPosition,
  5903. /* [out] */ ULONG *pcLineNumber,
  5904. /* [out] */ ULONG *pcCharacterOffsetInLine
  5905. )
  5906. {
  5907. return GetLineOfPosition(m_rgpFilemaps[0], cCharacterPosition, pcLineNumber, pcCharacterOffsetInLine);
  5908. }
  5909. /* ============================================================================
  5910. CTemplate::GetText
  5911. From a character offset and length, return the document text
  5912. */
  5913. HRESULT CTemplate::GetText
  5914. (
  5915. ULONG cchSourceOffset,
  5916. WCHAR *pwchText,
  5917. SOURCE_TEXT_ATTR *pTextAttr,
  5918. ULONG *pcChars,
  5919. ULONG cMaxChars
  5920. )
  5921. {
  5922. return m_rgpFilemaps[0]->GetText((WORD)m_wCodePage, cchSourceOffset, pwchText, pTextAttr, pcChars, cMaxChars);
  5923. }
  5924. /* ============================================================================
  5925. CTemplate::GetPositionOfContext
  5926. Decompose a document context into the document offset & length
  5927. */
  5928. HRESULT CTemplate::GetPositionOfContext
  5929. (
  5930. /* [in] */ IDebugDocumentContext *pUnknownDocumentContext,
  5931. /* [out] */ ULONG *pcchSourceOffset,
  5932. /* [out] */ ULONG *pcchText
  5933. )
  5934. {
  5935. // Make sure that the context is one of ours
  5936. CTemplateDocumentContext *pDocumentContext;
  5937. if (FAILED(pUnknownDocumentContext->QueryInterface(IID_IDenaliTemplateDocumentContext, reinterpret_cast<void **>(&pDocumentContext))))
  5938. return E_FAIL;
  5939. if (pcchSourceOffset)
  5940. *pcchSourceOffset = pDocumentContext->m_cchSourceOffset;
  5941. if (pcchText)
  5942. *pcchText = pDocumentContext->m_cchText;
  5943. pDocumentContext->Release();
  5944. return S_OK;
  5945. }
  5946. /* ============================================================================
  5947. CTemplate::GetContextOfPosition
  5948. Given the character position & number of characters in the document,
  5949. encapsulate this into a document context object.
  5950. */
  5951. HRESULT CTemplate::GetContextOfPosition
  5952. (
  5953. /* [in] */ ULONG cchSourceOffset,
  5954. /* [in] */ ULONG cchText,
  5955. /* [out] */ IDebugDocumentContext **ppDocumentContext
  5956. )
  5957. {
  5958. if (
  5959. (*ppDocumentContext = new CTemplateDocumentContext(this, cchSourceOffset, cchText))
  5960. == NULL
  5961. )
  5962. return E_OUTOFMEMORY;
  5963. return S_OK;
  5964. }
  5965. /* ****************************************************************************
  5966. IConnectionPointContainer implementation
  5967. */
  5968. /* ============================================================================
  5969. CTemplate::FindConnectionPoint
  5970. From a character offset and length, return the document text
  5971. */
  5972. HRESULT CTemplate::FindConnectionPoint
  5973. (
  5974. const GUID &uidConnection,
  5975. IConnectionPoint **ppCP
  5976. )
  5977. {
  5978. if (uidConnection == IID_IDebugDocumentTextEvents)
  5979. return m_CPTextEvents.QueryInterface(IID_IConnectionPoint, reinterpret_cast<void **>(ppCP));
  5980. else
  5981. {
  5982. *ppCP = NULL;
  5983. return E_NOINTERFACE;
  5984. }
  5985. }
  5986. /* ============================================================================
  5987. CTemplate::AttachTo
  5988. attach this to the debugger UI tree view.
  5989. */
  5990. HRESULT CTemplate::AttachTo
  5991. (
  5992. CAppln *pAppln
  5993. )
  5994. {
  5995. if (!m_fDontAttach && pAppln->FDebuggable())
  5996. {
  5997. // If we are already attached to this application, then ignore 2nd request
  5998. CDblLink *pNodeCurr = m_listDocNodes.PNext();
  5999. while (pNodeCurr != &m_listDocNodes)
  6000. {
  6001. if (pAppln == static_cast<CDocNodeElem *>(pNodeCurr)->m_pAppln)
  6002. return S_OK;
  6003. pNodeCurr = pNodeCurr->PNext();
  6004. }
  6005. // Create the node and store it in the linked list.
  6006. HRESULT hr;
  6007. IDebugApplicationNode *pDocRoot;
  6008. CDocNodeElem *pDocNodeElem;
  6009. // Create a document tree, showing the include file hierarchy
  6010. if (FAILED(hr = CreateDocumentTree(m_rgpFilemaps[0], &pDocRoot)))
  6011. return hr;
  6012. if (FAILED(hr = pDocRoot->Attach(pAppln->PAppRoot())))
  6013. return hr;
  6014. if ((pDocNodeElem = new CDocNodeElem(pAppln, pDocRoot)) == NULL)
  6015. return E_OUTOFMEMORY;
  6016. pDocNodeElem->AppendTo(m_listDocNodes);
  6017. pDocRoot->Release();
  6018. m_fDebuggable = TRUE;
  6019. }
  6020. return S_OK;
  6021. }
  6022. /* ============================================================================
  6023. CTemplate::DetachFrom
  6024. detach this from the debugger UI tree view.
  6025. */
  6026. HRESULT CTemplate::DetachFrom
  6027. (
  6028. CAppln *pAppln
  6029. )
  6030. {
  6031. // Enter the CS to prevent Detach() from detaching while we are scanning
  6032. // the list (causes application ptr to be deleted twice if this occurs)
  6033. DBG_ASSERT(m_fDebuggerDetachCSInited);
  6034. EnterCriticalSection(&m_csDebuggerDetach);
  6035. // Look for the node that has this application
  6036. CDblLink *pNodeCurr = m_listDocNodes.PNext();
  6037. while (pNodeCurr != &m_listDocNodes)
  6038. {
  6039. if (pAppln == static_cast<CDocNodeElem *>(pNodeCurr)->m_pAppln)
  6040. break;
  6041. pNodeCurr = pNodeCurr->PNext();
  6042. }
  6043. // If not found (pNodeCurr points back to head), then fail
  6044. if (pNodeCurr == &m_listDocNodes)
  6045. {
  6046. LeaveCriticalSection(&m_csDebuggerDetach);
  6047. return E_FAIL;
  6048. }
  6049. // Detach the node by deleting the current element
  6050. delete pNodeCurr;
  6051. // Turn off "Debuggable" flag if last application is detached
  6052. m_fDebuggable = !m_listDocNodes.FIsEmpty();
  6053. // At this point CS not needed
  6054. LeaveCriticalSection(&m_csDebuggerDetach);
  6055. // If we have just removed ourselves from the last application,
  6056. // then we call Detach(), to remove all cached script engines now.
  6057. if (!m_fDebuggable)
  6058. Detach();
  6059. return S_OK;
  6060. }
  6061. /* ============================================================================
  6062. CTemplate::Detach
  6063. detach this from the debugger UI tree view.
  6064. */
  6065. HRESULT CTemplate::Detach
  6066. (
  6067. )
  6068. {
  6069. // Enter the CS to prevent DetachFrom() from detaching while we are clearing
  6070. // the list (causes application ptr to be deleted twice if this occurs)
  6071. if (m_fDebuggerDetachCSInited)
  6072. EnterCriticalSection(&m_csDebuggerDetach);
  6073. // Detach all nodes
  6074. while (! m_listDocNodes.FIsEmpty())
  6075. delete m_listDocNodes.PNext();
  6076. // Done with CS
  6077. if (m_fDebuggerDetachCSInited)
  6078. LeaveCriticalSection(&m_csDebuggerDetach);
  6079. // Since we are not debuggable now, remove any script engines we may
  6080. // be holding on to. If we are detaching from change notification
  6081. // thread, queue engines to be released from debugger thread.
  6082. //
  6083. if (m_rgpDebugScripts)
  6084. {
  6085. Assert (g_dwDebugThreadId != 0);
  6086. BOOL fCalledFromDebugActivity = GetCurrentThreadId() == g_dwDebugThreadId;
  6087. for (UINT i = 0; i < m_cScriptEngines; i++)
  6088. {
  6089. CActiveScriptEngine *pEngine = m_rgpDebugScripts[i];
  6090. if (pEngine)
  6091. {
  6092. if (fCalledFromDebugActivity)
  6093. {
  6094. pEngine->FinalRelease();
  6095. }
  6096. else
  6097. {
  6098. g_ApplnMgr.AddEngine(pEngine);
  6099. pEngine->Release();
  6100. }
  6101. }
  6102. }
  6103. delete[] m_rgpDebugScripts;
  6104. m_rgpDebugScripts = NULL;
  6105. }
  6106. m_fDebuggable = FALSE;
  6107. return S_OK;
  6108. }
  6109. /* ============================================================================
  6110. CTemplate::CreateDocumentTree
  6111. Traverse the tree that we have embedded in the filemap structures,
  6112. and use it to create the include file structure
  6113. */
  6114. HRESULT CTemplate::CreateDocumentTree
  6115. (
  6116. CFileMap *pfilemapRoot,
  6117. IDebugApplicationNode **ppDocRoot
  6118. )
  6119. {
  6120. IDebugApplicationNode *pDocNode;
  6121. HRESULT hr = S_OK;
  6122. if (pfilemapRoot == NULL || ppDocRoot == NULL)
  6123. return E_POINTER;
  6124. // Create the root node
  6125. if (FAILED(hr = g_pDebugApp->CreateApplicationNode(ppDocRoot)))
  6126. return hr;
  6127. // From the filemap information, match it up with the correct provider
  6128. // "This" is the provider for the root document, others come from Inc file cache
  6129. if (pfilemapRoot == m_rgpFilemaps[0])
  6130. {
  6131. if (FAILED(hr = (*ppDocRoot)->SetDocumentProvider(this)))
  6132. return hr;
  6133. }
  6134. else
  6135. {
  6136. CIncFile *pIncFile;
  6137. if (FAILED(hr = g_IncFileMap.GetIncFile(pfilemapRoot->m_szPathTranslated, &pIncFile)))
  6138. return hr;
  6139. if (FAILED(hr = (*ppDocRoot)->SetDocumentProvider(pIncFile)))
  6140. return hr;
  6141. // SetDocumentProvider AddRef'ed
  6142. pIncFile->Release();
  6143. }
  6144. // Create a node from all of the children and attach it to this node
  6145. CFileMap *pfilemapChild = pfilemapRoot->m_pfilemapChild;
  6146. while (pfilemapChild != NULL)
  6147. {
  6148. IDebugApplicationNode *pDocChild;
  6149. if (FAILED(hr = CreateDocumentTree(pfilemapChild, &pDocChild)))
  6150. return hr;
  6151. if (FAILED(hr = pDocChild->Attach(*ppDocRoot)))
  6152. return hr;
  6153. pfilemapChild = pfilemapChild->m_fHasSibling? pfilemapChild->m_pfilemapSibling : NULL;
  6154. }
  6155. return S_OK;
  6156. }
  6157. /* ============================================================================
  6158. CTemplate::End
  6159. Place template in non-usable state (after this is called, last ref. should
  6160. be the any currently executing scripts. The count will naturally vanish
  6161. as the scripts finish. The template should never be recycled in cache after
  6162. this call.)
  6163. REF COUNTING NOTE:
  6164. Since debugging client has a reference to the template, the template needs
  6165. to dis-associate with the debugger at a point in time before destruction.
  6166. Otherwise, the reference will never go to zero.
  6167. */
  6168. ULONG
  6169. CTemplate::End
  6170. (
  6171. )
  6172. {
  6173. // Flag template as non-usable (for debugging)
  6174. m_fIsValid = FALSE;
  6175. Detach();
  6176. if (!m_CPTextEvents.FIsEmpty() && g_pDebugApp != NULL)
  6177. {
  6178. IEnumConnections *pConnIterator;
  6179. if (SUCCEEDED(m_CPTextEvents.EnumConnections(&pConnIterator)))
  6180. {
  6181. CONNECTDATA ConnectData;
  6182. while (pConnIterator->Next(1, &ConnectData, NULL) == S_OK)
  6183. {
  6184. IDebugDocumentTextEvents *pTextEventSink;
  6185. if (SUCCEEDED(ConnectData.pUnk->QueryInterface(IID_IDebugDocumentTextEvents, reinterpret_cast<void **>(&pTextEventSink))))
  6186. {
  6187. InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_ON_DESTROY, pTextEventSink);
  6188. pTextEventSink->Release();
  6189. }
  6190. ConnectData.pUnk->Release();
  6191. }
  6192. pConnIterator->Release();
  6193. }
  6194. }
  6195. return Release();
  6196. }
  6197. /* ============================================================================
  6198. CTemplate::NotifyDebuggerOnPageEvent
  6199. Let debugger know about page start/end
  6200. */
  6201. HRESULT
  6202. CTemplate::NotifyDebuggerOnPageEvent
  6203. (
  6204. BOOL fStart // TRUE = StartPage, FALSE = EndPage
  6205. )
  6206. {
  6207. CTemplateDocumentContext *pDebugContext = new CTemplateDocumentContext(this, 0, 0);
  6208. if (pDebugContext == NULL)
  6209. return E_OUTOFMEMORY;
  6210. HRESULT hr = S_OK;
  6211. if (g_pDebugApp)
  6212. hr = InvokeDebuggerWithThreadSwitch
  6213. (
  6214. g_pDebugApp,
  6215. fStart ? DEBUGGER_EVENT_ON_PAGEBEGIN : DEBUGGER_EVENT_ON_PAGEEND,
  6216. static_cast<IUnknown *>(pDebugContext)
  6217. );
  6218. pDebugContext->Release();
  6219. return hr;
  6220. }
  6221. /* ============================================================================
  6222. CTemplate::ReleaseTypeLibs
  6223. Release all typelibs collected from metadata
  6224. */
  6225. void
  6226. CTemplate::ReleaseTypeLibs()
  6227. {
  6228. if (m_rgpTypeLibs.length() > 0)
  6229. {
  6230. for (UINT i = 0; i < m_rgpTypeLibs.length(); i++)
  6231. {
  6232. m_rgpTypeLibs[i]->Release();
  6233. }
  6234. m_rgpTypeLibs.reshape(0);
  6235. }
  6236. }
  6237. /* ============================================================================
  6238. CTemplate::WrapTypeLibs
  6239. Wrap all typelibs collected from metadata into single IDispatch *
  6240. */
  6241. void
  6242. CTemplate::WrapTypeLibs(CHitObj *pHitObj)
  6243. {
  6244. HRESULT hr = S_OK;
  6245. Assert(m_pdispTypeLibWrapper == NULL);
  6246. if (m_rgpTypeLibs.length() > 0)
  6247. {
  6248. hr = ::WrapTypeLibs
  6249. (
  6250. m_rgpTypeLibs.begin(),
  6251. m_rgpTypeLibs.length(),
  6252. &m_pdispTypeLibWrapper
  6253. );
  6254. ReleaseTypeLibs();
  6255. }
  6256. if (FAILED(hr))
  6257. {
  6258. m_pbErrorLocation = NULL;
  6259. m_idErrMsg = IDE_TEMPLATE_WRAP_TYPELIB_FAILED;
  6260. ProcessSpecificError(*(m_rgpFilemaps[0]), pHitObj);
  6261. THROW(E_TEMPLATE_COMPILE_FAILED_DONT_CACHE);
  6262. }
  6263. }
  6264. /* ============================================================================
  6265. CTemplate::Release449
  6266. Release all 449-echo-cookie objects collected from metadata
  6267. */
  6268. void
  6269. CTemplate::Release449()
  6270. {
  6271. if (m_rgp449.length() > 0)
  6272. {
  6273. for (UINT i = 0; i < m_rgp449.length(); i++)
  6274. {
  6275. m_rgp449[i]->Release();
  6276. }
  6277. m_rgp449.reshape(0);
  6278. }
  6279. }
  6280. /* ============================================================================
  6281. CTemplate::Do449Processing
  6282. Generate 449 response in cookie negotiations with IE when needed
  6283. */
  6284. HRESULT
  6285. CTemplate::Do449Processing
  6286. (
  6287. CHitObj *pHitObj
  6288. )
  6289. {
  6290. if (m_rgp449.length() == 0 || pHitObj->F449Done())
  6291. return S_OK;
  6292. HRESULT hr = ::Do449Processing
  6293. (
  6294. pHitObj,
  6295. m_rgp449.begin(),
  6296. m_rgp449.length()
  6297. );
  6298. pHitObj->Set449Done();
  6299. return hr;
  6300. }
  6301. #if 0
  6302. /* ============================================================================
  6303. CTemplate::OutputDebugTables
  6304. print the debugging data structures to the debug window
  6305. */
  6306. void
  6307. CTemplate::OutputDebugTables()
  6308. {
  6309. unsigned i, j;
  6310. wchar_t wszDebugLine[256];
  6311. CWCharToMBCS convTarget;
  6312. CWCharToMBCS convSource;
  6313. // print line mapping table
  6314. DBGPRINTF((DBG_CONTEXT, "\nEngine HTML? Line# SourceOffset Length TargetOffset TargetText__________ SourceText__________ File\n"));
  6315. for (i = 0; i < m_cScriptEngines; ++i)
  6316. for (j = 0; j < m_rgrgSourceInfos[i].length(); ++j)
  6317. {
  6318. wchar_t wszSourceText[SNIPPET_SIZE + 1], wszTargetText[SNIPPET_SIZE + 1];
  6319. CSourceInfo *pSourceInfo = &m_rgrgSourceInfos[i][j];
  6320. // DON'T display sample script text on last line of each engine
  6321. if (j == m_rgrgSourceInfos[i].length() - 1)
  6322. {
  6323. wszTargetText[0] = 0;
  6324. wszSourceText[0] = 0;
  6325. }
  6326. else
  6327. {
  6328. // Get source & target text sample
  6329. GetScriptSnippets(
  6330. pSourceInfo->m_cchSourceOffset, pSourceInfo->m_pfilemap,
  6331. pSourceInfo->m_cchTargetOffset, i,
  6332. wszSourceText, wszTargetText
  6333. );
  6334. // Actually display each line
  6335. #if 0
  6336. #ifndef _NO_TRACING_
  6337. convTarget.Init(wszTargetText);
  6338. convSource.Init(wszSourceText);
  6339. DBGINFO((DBG_CONTEXT,
  6340. "%-6d %-5s %-5d %-12d %-6d %-12d %-20s %-20s %s\n",
  6341. i,
  6342. pSourceInfo->m_fIsHTML? "Yes" : "No",
  6343. pSourceInfo->m_idLine,
  6344. pSourceInfo->m_cchSourceOffset,
  6345. pSourceInfo->m_cchSourceText,
  6346. pSourceInfo->m_cchTargetOffset,
  6347. convTarget.GetString(),
  6348. convSource.GetString(),
  6349. pSourceInfo->m_pfilemap->m_szPathTranslated));
  6350. #else
  6351. CMBCSToWChar convPath;
  6352. convPath.Init(pSourceInfo->m_pfilemap->m_szPathTranslated);
  6353. wsprintfW(
  6354. wszDebugLine,
  6355. L"%-6d %-5s %-5d %-12d %-6d %-12d %-20s %-20s %s\n",
  6356. i,
  6357. pSourceInfo->m_fIsHTML? L"Yes" : L"No",
  6358. pSourceInfo->m_idLine,
  6359. pSourceInfo->m_cchSourceOffset,
  6360. pSourceInfo->m_cchSourceText,
  6361. pSourceInfo->m_cchTargetOffset,
  6362. wszTargetText,
  6363. wszSourceText,
  6364. convPath.GetString());
  6365. OutputDebugStringW(wszDebugLine);
  6366. #endif
  6367. #endif
  6368. }
  6369. }
  6370. OutputDebugStringA("\n\n");
  6371. for (i = 0; i < m_cFilemaps; ++i)
  6372. {
  6373. CFileMap *pFilemap = m_rgpFilemaps[i];
  6374. #if UNICODE
  6375. DBGPRINTF((DBG_CONTEXT, "DBCS mapping table for File %S:\n", pFilemap->m_szPathTranslated));
  6376. #else
  6377. DBGPRINTF((DBG_CONTEXT, "DBCS mapping table for File %s:\n", pFilemap->m_szPathTranslated));
  6378. #endif
  6379. DBGPRINTF((DBG_CONTEXT, "ByteOffset CharOffset\n"));
  6380. for (COffsetInfo *pOffsetInfo = pFilemap->m_rgByte2DBCS.begin();
  6381. pOffsetInfo < pFilemap->m_rgByte2DBCS.end();
  6382. ++pOffsetInfo)
  6383. DebugPrintf("%-10d %-10d\n", pOffsetInfo->m_cbOffset, pOffsetInfo->m_cchOffset);
  6384. DBGPRINTF((DBG_CONTEXT, "\n\n"));
  6385. }
  6386. DBGPRINTF((DBG_CONTEXT, "Include File Hierarchy\n"));
  6387. OutputIncludeHierarchy(m_rgpFilemaps[0], 0);
  6388. DBGPRINTF((DBG_CONTEXT, "\n"));
  6389. }
  6390. /* ============================================================================
  6391. CTemplate::OutputIncludeHierarchy
  6392. print the lineage information that we keep around for include files.
  6393. Print all nodes on one level at the current indentation, then descend for
  6394. nested includes.
  6395. */
  6396. void
  6397. CTemplate::OutputIncludeHierarchy
  6398. (
  6399. CFileMap* pfilemap,
  6400. int cchIndent
  6401. )
  6402. {
  6403. TCHAR szDebugString[256], *pchEnd;
  6404. for (;;)
  6405. {
  6406. pchEnd = szDebugString;
  6407. for (int i = 0; i < cchIndent; ++i)
  6408. *pchEnd++ = _T(' ');
  6409. pchEnd = strcpyEx(pchEnd, pfilemap->m_szPathTranslated);
  6410. *pchEnd++ = _T('\n');
  6411. *pchEnd = _T('\0');
  6412. DBGPRINTF((DBG_CONTEXT, szDebugString));
  6413. // Print anything that this file includes
  6414. if (pfilemap->m_pfilemapChild)
  6415. OutputIncludeHierarchy(pfilemap->m_pfilemapChild, cchIndent + 3);
  6416. // Stop when there are no more siblings on this level
  6417. if (! pfilemap->m_fHasSibling)
  6418. break;
  6419. // Advance to next sibling
  6420. pfilemap = pfilemap->m_pfilemapSibling;
  6421. }
  6422. }
  6423. /* ============================================================================
  6424. CTemplate::OutputScriptSnippets
  6425. print some script from both the source offset & its corresponding target.
  6426. Good way to visually see if the offset conversions are working.
  6427. */
  6428. void
  6429. CTemplate::GetScriptSnippets
  6430. (
  6431. ULONG cchSourceOffset,
  6432. CFileMap *pFilemapSource,
  6433. ULONG cchTargetOffset,
  6434. ULONG idTargetEngine,
  6435. wchar_t *wszSourceText,
  6436. wchar_t *wszTargetText
  6437. )
  6438. {
  6439. // Get target text sample
  6440. if (wszTargetText)
  6441. {
  6442. char *szEngineName;
  6443. PROGLANG_ID *pProgLangID;
  6444. const wchar_t *wszScriptText;
  6445. GetScriptBlock(idTargetEngine, &szEngineName, &pProgLangID, &wszScriptText);
  6446. wszScriptText += cchTargetOffset;
  6447. int cch = wcslen(wszScriptText);
  6448. wcsncpy(wszTargetText, wszScriptText, min(cch, SNIPPET_SIZE) + 1);
  6449. wszTargetText[min(cch, SNIPPET_SIZE)] = 0;
  6450. // Convert newlines to space
  6451. wchar_t *pwch = wszTargetText;
  6452. while (*pwch != 0)
  6453. if (iswspace(*pwch++))
  6454. pwch[-1] = ' ';
  6455. }
  6456. // Get source text sample
  6457. if (wszSourceText)
  6458. {
  6459. ULONG cchMax = 0;
  6460. pFilemapSource->GetText((WORD)m_wCodePage, cchSourceOffset, wszSourceText, NULL, &cchMax, SNIPPET_SIZE);
  6461. wszSourceText[cchMax] = 0;
  6462. // Convert newlines to space
  6463. wchar_t *pwch = wszSourceText;
  6464. while (*pwch != 0)
  6465. if (iswspace(*pwch++))
  6466. pwch[-1] = ' ';
  6467. }
  6468. }
  6469. #endif
  6470. /* ============================================================================
  6471. CTemplate::BuildPersistedDACL
  6472. Builds a DACL based on the SECURITY_DESCRIPTOR already
  6473. associated with the template. The PersistedDACL is modified to include
  6474. full access for administrators and delete access for everyone.
  6475. */
  6476. HRESULT CTemplate::BuildPersistedDACL(PACL *ppRetDACL)
  6477. {
  6478. HRESULT hr = S_OK;
  6479. BOOL bDaclPresent;
  6480. BOOL bDaclDefaulted;
  6481. PACL pSrcDACL = NULL;
  6482. EXPLICIT_ACCESS ea;
  6483. SID_IDENTIFIER_AUTHORITY WorldAuthority = SECURITY_WORLD_SID_AUTHORITY;
  6484. *ppRetDACL = NULL;
  6485. ZeroMemory(&ea, sizeof(EXPLICIT_ACCESS));
  6486. ea.grfAccessPermissions = SYNCHRONIZE | DELETE;
  6487. ea.grfAccessMode = GRANT_ACCESS;
  6488. ea.grfInheritance= SUB_CONTAINERS_AND_OBJECTS_INHERIT;
  6489. ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
  6490. if (m_rgpFilemaps[0]->m_pSecurityDescriptor == NULL) {
  6491. return S_OK;
  6492. }
  6493. if (!AllocateAndInitializeSid(&WorldAuthority,
  6494. 1,
  6495. SECURITY_WORLD_RID,
  6496. 0,0,0,0,0,0,0,
  6497. (PSID *)(&ea.Trustee.ptstrName)))
  6498. hr = HRESULT_FROM_WIN32(GetLastError());
  6499. else if (!GetSecurityDescriptorDacl(m_rgpFilemaps[0]->m_pSecurityDescriptor,
  6500. &bDaclPresent,
  6501. &pSrcDACL,
  6502. &bDaclDefaulted))
  6503. hr = HRESULT_FROM_WIN32(GetLastError());
  6504. else if ((hr = SetEntriesInAcl(1,
  6505. &ea,
  6506. bDaclPresent ? pSrcDACL : NULL,
  6507. ppRetDACL)) != ERROR_SUCCESS)
  6508. hr = HRESULT_FROM_WIN32(hr);
  6509. if (ea.Trustee.ptstrName)
  6510. FreeSid(ea.Trustee.ptstrName);
  6511. return hr;
  6512. }
  6513. /* ============================================================================
  6514. CTemplate::PersistData
  6515. Attempts to write the contents of the template memory to disk. Note that
  6516. the memory isn't freed here but later when the template ref count falls to
  6517. 1 (indicating that the only reference to the template is the one that the
  6518. cache has on it).
  6519. */
  6520. HRESULT CTemplate::PersistData(char *pszTempFilePath)
  6521. {
  6522. HRESULT hr = S_OK;
  6523. DWORD winErr = 0;
  6524. HANDLE hFile = NULL;
  6525. DWORD dwWritten;
  6526. HANDLE hImpersonationToken = NULL;
  6527. HANDLE hThread;
  6528. PACL pPersistDACL = NULL;
  6529. #if DBG_PERSTEMPL
  6530. DBGPRINTF((DBG_CONTEXT,
  6531. "CTemplate::PersistData() enterred.\n\tTemplate is %s\n\tPersistTempName is %s\n",
  6532. GetSourceFileName(),
  6533. m_szPersistTempName ? m_szPersistTempName : "<none>"));
  6534. #endif
  6535. // if for some reason this template has been marked as invalid, then it is
  6536. // not persistable
  6537. if (m_fIsValid == FALSE) {
  6538. hr = E_FAIL;
  6539. goto end;
  6540. }
  6541. // if it is already persisted, there is nothing to do
  6542. if (m_fIsPersisted) {
  6543. goto end;
  6544. }
  6545. // check to see if we already have a persist temp name. If a template moves
  6546. // from the persisted cache back to the memory cache, then the persisted flag
  6547. // will have been lifted but the cache name will remain as an optimization for
  6548. // future persisting.
  6549. if (m_szPersistTempName == NULL) {
  6550. hThread = GetCurrentThread();
  6551. if (OpenThreadToken( hThread,
  6552. TOKEN_READ | TOKEN_IMPERSONATE,
  6553. TRUE,
  6554. &hImpersonationToken )) {
  6555. RevertToSelf();
  6556. }
  6557. // allocate memory for this temp path
  6558. if (!(m_szPersistTempName = (LPSTR)CTemplate::LargeMalloc(MAX_PATH))) {
  6559. hr = E_OUTOFMEMORY;
  6560. }
  6561. // create the temp file. The location of the temp directory was passed
  6562. // in as an argument. The resulting tempfile name in m_szPersistTempName
  6563. // will include this path.
  6564. else if (GetTempFileNameA(pszTempFilePath,
  6565. "ASPTemplate",
  6566. 0,
  6567. m_szPersistTempName) == 0) {
  6568. hr = HRESULT_FROM_WIN32(GetLastError());
  6569. }
  6570. // build a security descriptor to use with this persisted file. It is
  6571. // comprised of the .asp's security descriptor plus a couple of DACLs
  6572. // to allow administrators full access and everyone delete access.
  6573. else if (FAILED(hr = BuildPersistedDACL(&pPersistDACL)));
  6574. else if (pPersistDACL
  6575. && (winErr = SetNamedSecurityInfoA((LPSTR)m_szPersistTempName,
  6576. SE_FILE_OBJECT,
  6577. DACL_SECURITY_INFORMATION,
  6578. NULL,
  6579. NULL,
  6580. pPersistDACL,
  6581. NULL)))
  6582. hr = HRESULT_FROM_WIN32(winErr);
  6583. // create the file
  6584. else if ((hFile = CreateFileA(m_szPersistTempName,
  6585. GENERIC_WRITE,
  6586. 0,
  6587. NULL,
  6588. CREATE_ALWAYS,
  6589. FILE_ATTRIBUTE_NORMAL,
  6590. NULL)) == INVALID_HANDLE_VALUE) {
  6591. hr = HRESULT_FROM_WIN32(GetLastError());
  6592. }
  6593. // slam out the entire contents of the template memory to the file
  6594. else if (WriteFile(hFile,
  6595. m_pbStart,
  6596. m_cbTemplate,
  6597. &dwWritten,
  6598. NULL) == 0) {
  6599. hr = HRESULT_FROM_WIN32(GetLastError());
  6600. }
  6601. // close
  6602. else if (CloseHandle(hFile) == 0) {
  6603. hr = HRESULT_FROM_WIN32(GetLastError());
  6604. }
  6605. else {
  6606. hFile = NULL;
  6607. }
  6608. if (FAILED(hr));
  6609. // make sure that the entire amount was written out
  6610. else if (dwWritten != m_cbTemplate) {
  6611. hr = E_FAIL;
  6612. }
  6613. if (hImpersonationToken) {
  6614. SetThreadToken(&hThread, hImpersonationToken);
  6615. CloseHandle(hImpersonationToken);
  6616. }
  6617. }
  6618. if (FAILED(hr));
  6619. else {
  6620. // if successfull, then note that the template is now persisted.
  6621. // Do an AddRef and Release as a safe way to check to see if the
  6622. // template memory can be freed.
  6623. m_fIsPersisted = TRUE;
  6624. AddRef();
  6625. Release();
  6626. }
  6627. // if errors occurred, clean up any resources.
  6628. if (hr != S_OK) {
  6629. if (hFile)
  6630. CloseHandle(hFile);
  6631. if (m_szPersistTempName)
  6632. CTemplate::LargeFree(m_szPersistTempName);
  6633. m_szPersistTempName = NULL;
  6634. }
  6635. // free the persisted SECURITY_DESCRIPTOR if allocated
  6636. if (pPersistDACL) {
  6637. LocalFree(pPersistDACL);
  6638. }
  6639. end:
  6640. #if DBG_PERSTEMPL
  6641. if (hr == S_OK) {
  6642. DBGPRINTF((DBG_CONTEXT,
  6643. "Persist Successful. TempName is %s\n",
  6644. m_szPersistTempName));
  6645. }
  6646. else {
  6647. DBGPRINTF((DBG_CONTEXT,
  6648. "Persist failed. hr = %x",
  6649. hr));
  6650. }
  6651. #endif
  6652. return hr;
  6653. }
  6654. /* ============================================================================
  6655. CTemplate::UnPersistData
  6656. Restores the template memory from disk.
  6657. */
  6658. HRESULT CTemplate::UnPersistData()
  6659. {
  6660. HRESULT hr = S_OK;
  6661. HANDLE hFile = NULL;
  6662. DWORD dwRead;
  6663. HANDLE hImpersonationToken = NULL;
  6664. HANDLE hThread;
  6665. #if DEB_PERSTEMPL
  6666. DBGPRINTF((DBG_CONTEXT,
  6667. "CTemplate::UnPersistData() enterred.\n\tTemplate is %s\n\tTempName is %s\n",
  6668. m_rgpFilemaps[0]->m_szPathTranslated,
  6669. m_szPersistTempName));
  6670. #endif
  6671. // check to see if the template is already loaded into memory. If so, then
  6672. // all this routine needs to do is lift the IsPersisted flag.
  6673. if (m_pbStart != NULL) {
  6674. m_fIsPersisted = FALSE;
  6675. goto end;
  6676. }
  6677. hThread = GetCurrentThread();
  6678. if (OpenThreadToken( hThread,
  6679. TOKEN_READ | TOKEN_IMPERSONATE,
  6680. TRUE,
  6681. &hImpersonationToken )) {
  6682. RevertToSelf();
  6683. }
  6684. // open the temp file for read
  6685. if ((hFile = CreateFileA(m_szPersistTempName,
  6686. GENERIC_READ,
  6687. 0,
  6688. NULL,
  6689. OPEN_EXISTING,
  6690. 0,
  6691. NULL)) == INVALID_HANDLE_VALUE) {
  6692. hr = HRESULT_FROM_WIN32(GetLastError());
  6693. }
  6694. // allocate the template memory
  6695. else if (!(m_pbStart = (BYTE *)CTemplate::LargeMalloc(m_cbTemplate))) {
  6696. hr = E_OUTOFMEMORY;
  6697. }
  6698. // read in the entire file
  6699. else if (ReadFile(hFile,
  6700. m_pbStart,
  6701. m_cbTemplate,
  6702. &dwRead,
  6703. NULL) == 0) {
  6704. hr = HRESULT_FROM_WIN32(GetLastError());
  6705. }
  6706. // we're done with the file
  6707. else if (CloseHandle(hFile) == 0) {
  6708. hr = HRESULT_FROM_WIN32(GetLastError());
  6709. }
  6710. else {
  6711. hFile = NULL;
  6712. }
  6713. if (FAILED(hr));
  6714. // check to make sure we got everything
  6715. else if (m_cbTemplate != dwRead) {
  6716. hr = E_FAIL;
  6717. }
  6718. else {
  6719. // if not, pretend like this is no longer persisted. Prevents errors
  6720. // in the future.
  6721. m_fIsPersisted = FALSE;
  6722. }
  6723. if (hr != S_OK) {
  6724. // make sure that the file handle was cleaned up
  6725. if (hFile)
  6726. CloseHandle(hFile);
  6727. }
  6728. end:
  6729. if (hImpersonationToken) {
  6730. SetThreadToken(&hThread, hImpersonationToken);
  6731. CloseHandle(hImpersonationToken);
  6732. }
  6733. #if DBG_PERSTEMPL
  6734. if (hr == S_OK) {
  6735. DBGPRINTF((DBG_CONTEXT,
  6736. "UnPersist Successful\n"));
  6737. }
  6738. else {
  6739. DBGPRINTF((DBG_CONTEXT,
  6740. "UnPersist failed. hr = %x",
  6741. hr));
  6742. }
  6743. #endif
  6744. return hr;
  6745. }
  6746. /* ============================================================================
  6747. CTemplate::PersistCleanup
  6748. Cleans up the temp file and the memory holding the temp file name.
  6749. */
  6750. HRESULT CTemplate::PersistCleanup()
  6751. {
  6752. HRESULT hr = S_OK;
  6753. HANDLE hImpersonationToken = NULL;
  6754. HANDLE hThread;
  6755. if (m_szPersistTempName == NULL) {
  6756. return (S_OK);
  6757. }
  6758. hThread = GetCurrentThread();
  6759. if (OpenThreadToken( hThread,
  6760. TOKEN_READ | TOKEN_IMPERSONATE,
  6761. TRUE,
  6762. &hImpersonationToken )) {
  6763. RevertToSelf();
  6764. }
  6765. if (DeleteFileA(m_szPersistTempName) == 0) {
  6766. hr = GetLastError();
  6767. }
  6768. else {
  6769. m_fIsPersisted = FALSE;
  6770. CTemplate::LargeFree(m_szPersistTempName);
  6771. m_szPersistTempName = NULL;
  6772. }
  6773. if (hImpersonationToken) {
  6774. SetThreadToken(&hThread, hImpersonationToken);
  6775. CloseHandle(hImpersonationToken);
  6776. }
  6777. return hr;
  6778. }
  6779. /* ============================================================================
  6780. CTemplate::CreateTransServiceConfig
  6781. Creates the ServicesConfig object for a transacted page
  6782. */
  6783. HRESULT CTemplate::CreateTransServiceConfig(BOOL fEnableTracker)
  6784. {
  6785. HRESULT hr;
  6786. IServiceInheritanceConfig *pIInheritConfig = NULL;
  6787. IServiceTransactionConfig *pITransConfig = NULL;
  6788. IServiceTrackerConfig *pITracker = NULL;
  6789. // see if there is any reason to create the object...
  6790. if ((fEnableTracker == FALSE) && (m_ttTransacted == ttUndefined)) {
  6791. return S_OK;
  6792. }
  6793. hr = CoCreateInstance(CLSID_CServiceConfig,
  6794. NULL,
  6795. CLSCTX_INPROC_SERVER,
  6796. IID_IUnknown,
  6797. (void **)&m_pServicesConfig);
  6798. if (FAILED(hr)) {
  6799. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not CCI ServicesConfig, hr = %#08x\n", hr));
  6800. goto LCleanup;
  6801. }
  6802. hr = m_pServicesConfig->QueryInterface(IID_IServiceInheritanceConfig, (void **)&pIInheritConfig);
  6803. if (FAILED(hr)) {
  6804. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IServiceInheritanceConfig, hr = %#08x\n", hr));
  6805. goto LCleanup;
  6806. }
  6807. hr = pIInheritConfig->ContainingContextTreatment(CSC_Inherit);
  6808. if (FAILED(hr)) {
  6809. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set Inherit mode, hr = %#08x\n", hr));
  6810. goto LCleanup;
  6811. }
  6812. if (m_ttTransacted != ttUndefined) {
  6813. CSC_TransactionConfig transConfig;
  6814. switch (m_ttTransacted) {
  6815. case ttNotSupported:
  6816. transConfig = CSC_NoTransaction;
  6817. break;
  6818. case ttSupported:
  6819. transConfig = CSC_IfContainerIsTransactional;
  6820. break;
  6821. case ttRequired:
  6822. transConfig = CSC_CreateTransactionIfNecessary;
  6823. break;
  6824. case ttRequiresNew:
  6825. transConfig = CSC_NewTransaction;
  6826. break;
  6827. }
  6828. hr = m_pServicesConfig->QueryInterface(IID_IServiceTransactionConfig, (void **)&pITransConfig);
  6829. if (FAILED(hr)) {
  6830. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IID_IServiceTransactionConfig, hr = %#08x\n", hr));
  6831. goto LCleanup;
  6832. }
  6833. hr = pITransConfig->ConfigureTransaction(transConfig);
  6834. if (FAILED(hr)) {
  6835. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set transaction type, hr = %#08x\n", hr));
  6836. goto LCleanup;
  6837. }
  6838. }
  6839. if (fEnableTracker) {
  6840. hr = m_pServicesConfig->QueryInterface(IID_IServiceTrackerConfig, (void **)&pITracker);
  6841. if (FAILED(hr)) {
  6842. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not QI for IID_IServiceTrackerConfig, hr = %#08x\n", hr));
  6843. goto LCleanup;
  6844. }
  6845. LPWSTR pwszAppURL;
  6846. LPWSTR pwszASPName;
  6847. #if UNICODE
  6848. pwszAppURL = m_szApplnVirtPath;
  6849. pwszASPName = GetSourceFileName(SOURCEPATHTYPE_VIRTUAL);
  6850. #else
  6851. CMBCSToWChar convAppln;
  6852. CMBCSToWChar convASPName;
  6853. hr = convAppln.Init(m_szApplnVirtPath);
  6854. if (FAILED(hr)) {
  6855. goto LCleanup;
  6856. }
  6857. hr = convASPName.Init(GetSourceFileName(SOURCEPATHTYPE_VIRTUAL));
  6858. if (FAILED(hr)) {
  6859. goto LCleanup;
  6860. }
  6861. pwszAppURL = convAppln.GetString();
  6862. pwszASPName = convASPName.GetString();
  6863. #endif
  6864. pwszASPName += wcslen(pwszAppURL) + 1;
  6865. hr = pITracker->TrackerConfig(CSC_UseTracker, pwszAppURL, pwszASPName);
  6866. if (FAILED(hr)) {
  6867. DBGWARN((DBG_CONTEXT, "CTemplate::CreateTransServiceConfig() - Could not set Inherit mode, hr = %#08x\n", hr));
  6868. goto LCleanup;
  6869. }
  6870. }
  6871. LCleanup:
  6872. if (pIInheritConfig)
  6873. pIInheritConfig->Release();
  6874. if (pITransConfig)
  6875. pITransConfig->Release();
  6876. return hr;
  6877. }
  6878. /* ****************************************************************************
  6879. CIncFile member functions
  6880. */
  6881. /* ============================================================================
  6882. CIncFile::CIncFile
  6883. Constructor
  6884. Returns:
  6885. Nothing
  6886. Side effects:
  6887. None
  6888. */
  6889. CIncFile::CIncFile
  6890. (
  6891. )
  6892. : m_szIncFile(NULL),
  6893. m_fCsInited(FALSE),
  6894. m_CPTextEvents(this, IID_IDebugDocumentTextEvents),
  6895. m_cRefs(0)
  6896. { }
  6897. /* ============================================================================
  6898. CIncFile::Init
  6899. Inits the CIncFile object
  6900. Returns:
  6901. HRESULT
  6902. Side effects:
  6903. None
  6904. */
  6905. HRESULT
  6906. CIncFile::Init
  6907. (
  6908. const TCHAR* szIncFile // file name
  6909. )
  6910. {
  6911. HRESULT hr = S_OK;
  6912. WIN32_FILE_ATTRIBUTE_DATA fad; // win32 file attributes data structure
  6913. ErrInitCriticalSection(&m_csUpdate, hr);
  6914. m_fCsInited = TRUE;
  6915. if(NULL == (m_szIncFile = (LPTSTR) CTemplate::SmallMalloc((_tcslen(szIncFile) + 1)*sizeof(TCHAR)))) {
  6916. hr = E_OUTOFMEMORY;
  6917. goto LExit;
  6918. }
  6919. _tcscpy(m_szIncFile, szIncFile);
  6920. // init hash table element base class
  6921. if(FAILED(hr = CLinkElem::Init(m_szIncFile, _tcslen(m_szIncFile)*sizeof(TCHAR))))
  6922. goto LExit;
  6923. LExit:
  6924. return hr;
  6925. }
  6926. /* ============================================================================
  6927. CIncFile::~CIncFile
  6928. Destructor
  6929. Returns:
  6930. Nothing
  6931. Side effects:
  6932. None
  6933. */
  6934. CIncFile::~CIncFile
  6935. (
  6936. )
  6937. {
  6938. #if UNICODE
  6939. DBGPRINTF((DBG_CONTEXT, "Include file deleted: %S\n", m_szIncFile));
  6940. #else
  6941. DBGPRINTF((DBG_CONTEXT, "Include file deleted: %s\n", m_szIncFile));
  6942. #endif
  6943. Assert(m_cRefs == 0);
  6944. SmallTemplateFreeNullify((void**) &m_szIncFile);
  6945. if(m_fCsInited)
  6946. DeleteCriticalSection(&m_csUpdate);
  6947. }
  6948. /* ============================================================================
  6949. CIncFile::GetTemplate
  6950. Get i'th template user from CIncFile
  6951. Returns:
  6952. NULL if "iTemplate" is out of range, m_rgpTemplates[iTemplate] otherwise
  6953. Side effects:
  6954. None
  6955. */
  6956. CTemplate*
  6957. CIncFile::GetTemplate
  6958. (
  6959. int iTemplate
  6960. )
  6961. {
  6962. if (iTemplate < 0 || iTemplate >= (signed int) m_rgpTemplates.length())
  6963. return NULL;
  6964. else
  6965. return m_rgpTemplates[iTemplate];
  6966. }
  6967. /* ============================================================================
  6968. CIncFile::QueryInterface
  6969. Provides QueryInterface implementation for CIncFile
  6970. NOTE: It is arbitrary which vtable we return for IDebugDocument & IDebugDocumentInfo.
  6971. */
  6972. HRESULT
  6973. CIncFile::QueryInterface(const GUID &uidInterface, void **ppvObj)
  6974. {
  6975. if (uidInterface == IID_IUnknown || uidInterface == IID_IDebugDocumentProvider)
  6976. *ppvObj = static_cast<IDebugDocumentProvider *>(this);
  6977. else if (uidInterface == IID_IDebugDocument || uidInterface == IID_IDebugDocumentInfo || uidInterface == IID_IDebugDocumentText)
  6978. *ppvObj = static_cast<IDebugDocumentText *>(this);
  6979. else if (uidInterface == IID_IConnectionPointContainer)
  6980. *ppvObj = static_cast<IConnectionPointContainer *>(this);
  6981. else
  6982. *ppvObj = NULL;
  6983. if (*ppvObj)
  6984. {
  6985. AddRef();
  6986. return S_OK;
  6987. }
  6988. else
  6989. return E_NOINTERFACE;
  6990. }
  6991. /* ============================================================================
  6992. CIncFile::AddRef
  6993. Adds a ref to this IncFile, thread-safely
  6994. */
  6995. ULONG
  6996. CIncFile::AddRef()
  6997. {
  6998. InterlockedIncrement(&m_cRefs);
  6999. return m_cRefs;
  7000. }
  7001. /* ============================================================================
  7002. CIncFile::Release
  7003. Releases a ref to this IncFile, thread-safely
  7004. */
  7005. ULONG
  7006. CIncFile::Release()
  7007. {
  7008. if (InterlockedDecrement(&m_cRefs) == 0)
  7009. {
  7010. delete this;
  7011. return 0;
  7012. }
  7013. return m_cRefs;
  7014. }
  7015. /* ****************************************************************************
  7016. IDebugDocumentProvider implementation for includes
  7017. */
  7018. /* ============================================================================
  7019. CIncFile::GetDocument
  7020. Return a pointer to the IDebugDocument implementation. (same object in this case)
  7021. Returns:
  7022. *ppDebugDoc is set to "this".
  7023. Notes:
  7024. always succeeds
  7025. */
  7026. HRESULT CIncFile::GetDocument
  7027. (
  7028. IDebugDocument **ppDebugDoc
  7029. )
  7030. {
  7031. return QueryInterface(IID_IDebugDocument, reinterpret_cast<void **>(ppDebugDoc));
  7032. }
  7033. /* ============================================================================
  7034. CIncFile::GetName
  7035. Return the various names of a document.
  7036. */
  7037. HRESULT CIncFile::GetName
  7038. (
  7039. /* [in] */ DOCUMENTNAMETYPE doctype,
  7040. /* [out] */ BSTR *pbstrName
  7041. )
  7042. {
  7043. switch (doctype) {
  7044. case DOCUMENTNAMETYPE_APPNODE:
  7045. case DOCUMENTNAMETYPE_FILE_TAIL:
  7046. case DOCUMENTNAMETYPE_TITLE:
  7047. // Use the name of the include file (char after last back-slash) converted to lower case.
  7048. {
  7049. TCHAR *szFilePart = _tcsrchr(m_szIncFile, _T('\\'));
  7050. Assert (szFilePart != NULL);
  7051. #if UNICODE
  7052. *pbstrName = SysAllocString(szFilePart + 1);
  7053. if (*pbstrName == NULL) {
  7054. return E_OUTOFMEMORY;
  7055. }
  7056. #else
  7057. if (FAILED(SysAllocStringFromSz(szFilePart + 1, 0, pbstrName, CP_ACP)))
  7058. return E_FAIL;
  7059. #endif
  7060. if (*pbstrName != NULL)
  7061. _wcslwr(*pbstrName);
  7062. return S_OK;
  7063. }
  7064. case DOCUMENTNAMETYPE_URL:
  7065. // prefix with the URL, use szPathInfo for the rest of the path
  7066. {
  7067. CTemplate::CFileMap *pFilemap = GetFilemap();
  7068. if (pFilemap->FHasVirtPath()) {
  7069. STACK_BUFFER( tempName, MAX_PATH );
  7070. CTemplate *pTemplate = m_rgpTemplates[0];
  7071. int cbURLPrefix = DIFF(pTemplate->m_szApplnVirtPath - pTemplate->m_szApplnURL)*sizeof(TCHAR);
  7072. if (!tempName.Resize(cbURLPrefix + ((_tcslen(pFilemap->m_szPathInfo) + 1)*sizeof(TCHAR)))) {
  7073. return E_OUTOFMEMORY;
  7074. }
  7075. TCHAR *szURL = (TCHAR *)tempName.QueryPtr();
  7076. memcpy(szURL, pTemplate->m_szApplnURL, cbURLPrefix);
  7077. _tcscpy(&szURL[cbURLPrefix/sizeof(TCHAR)], pFilemap->m_szPathInfo);
  7078. #if UNICODE
  7079. *pbstrName = SysAllocString(szURL);
  7080. if (*pbstrName == NULL) {
  7081. return (E_OUTOFMEMORY);
  7082. }
  7083. return S_OK;
  7084. #else
  7085. return SysAllocStringFromSz(szURL, 0, pbstrName, pTemplate->m_wCodePage);
  7086. #endif
  7087. }
  7088. else {
  7089. *pbstrName = NULL;
  7090. return E_FAIL;
  7091. }
  7092. }
  7093. default:
  7094. return E_FAIL;
  7095. }
  7096. }
  7097. /* ****************************************************************************
  7098. IDebugDocumentText implementation
  7099. */
  7100. /* ============================================================================
  7101. CIncFile::GetSize
  7102. Return the number of lines & characters in the document
  7103. */
  7104. HRESULT CIncFile::GetSize
  7105. (
  7106. /* [out] */ ULONG *pcLines,
  7107. /* [out] */ ULONG *pcChars
  7108. )
  7109. {
  7110. CTemplate::CFileMap *pfilemap = GetFilemap();
  7111. *pcLines = ULONG_MAX;
  7112. *pcChars = pfilemap->m_cChars;
  7113. #if UNICODE
  7114. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%S\") returns %lu characters (%lu lines)\n", pfilemap->m_szPathTranslated, *pcChars, *pcLines));
  7115. #else
  7116. DBGPRINTF((DBG_CONTEXT, "GetSize(\"%s\") returns %lu characters (%lu lines)\n", pfilemap->m_szPathTranslated, *pcChars, *pcLines));
  7117. #endif
  7118. return S_OK;
  7119. }
  7120. /* ============================================================================
  7121. CTemplate::GetDocumentAttributes
  7122. Return doc attributes
  7123. */
  7124. HRESULT CIncFile::GetDocumentAttributes
  7125. (
  7126. /* [out] */ TEXT_DOC_ATTR *ptextdocattr
  7127. )
  7128. {
  7129. // Easy way to tell debugger that we don't support editing.
  7130. *ptextdocattr = TEXT_DOC_ATTR_READONLY;
  7131. return S_OK;
  7132. }
  7133. /* ============================================================================
  7134. CIncFile::GetPositionOfLine
  7135. From a line number, return the character offset of the beginning
  7136. I don't think we need this function. It is meant to support line oriented
  7137. debuggers, of which Caesar is not one.
  7138. */
  7139. HRESULT CIncFile::GetPositionOfLine
  7140. (
  7141. /* [in] */ ULONG cLineNumber,
  7142. /* [out] */ ULONG *pcCharacterPosition
  7143. )
  7144. {
  7145. return m_rgpTemplates[0]->GetPositionOfLine(GetFilemap(), cLineNumber, pcCharacterPosition);
  7146. }
  7147. /* ============================================================================
  7148. CIncFile::GetLineOfPosition
  7149. From a character offset, return the line number and offset within the line
  7150. I don't think we need this function. It is meant to support line oriented
  7151. debuggers, of which Caesar is not one.
  7152. */
  7153. HRESULT CIncFile::GetLineOfPosition
  7154. (
  7155. /* [in] */ ULONG cCharacterPosition,
  7156. /* [out] */ ULONG *pcLineNumber,
  7157. /* [out] */ ULONG *pcCharacterOffsetInLine
  7158. )
  7159. {
  7160. return m_rgpTemplates[0]->GetLineOfPosition(GetFilemap(), cCharacterPosition, pcLineNumber, pcCharacterOffsetInLine);
  7161. }
  7162. /* ============================================================================
  7163. CIncFile::GetText
  7164. From a character offset and length, return the document text
  7165. */
  7166. HRESULT CIncFile::GetText
  7167. (
  7168. ULONG cchSourceOffset,
  7169. WCHAR *pwchText,
  7170. SOURCE_TEXT_ATTR *pTextAttr,
  7171. ULONG *pcChars,
  7172. ULONG cMaxChars
  7173. )
  7174. {
  7175. return GetFilemap()->GetText((WORD)m_rgpTemplates[0]->m_wCodePage, cchSourceOffset, pwchText, pTextAttr, pcChars, cMaxChars);
  7176. }
  7177. /* ============================================================================
  7178. CIncFile::GetPositionOfContext
  7179. Decompose a document context into the document offset & length
  7180. */
  7181. HRESULT CIncFile::GetPositionOfContext
  7182. (
  7183. /* [in] */ IDebugDocumentContext *pUnknownDocumentContext,
  7184. /* [out] */ ULONG *pcchSourceOffset,
  7185. /* [out] */ ULONG *pcchText
  7186. )
  7187. {
  7188. // Make sure that the context is one of ours
  7189. CIncFileDocumentContext *pDocumentContext;
  7190. if (FAILED(pUnknownDocumentContext->QueryInterface(IID_IDenaliIncFileDocumentContext, reinterpret_cast<void **>(&pDocumentContext))))
  7191. return E_FAIL;
  7192. if (pcchSourceOffset)
  7193. *pcchSourceOffset = pDocumentContext->m_cchSourceOffset;
  7194. if (pcchText)
  7195. *pcchText = pDocumentContext->m_cchText;
  7196. pDocumentContext->Release();
  7197. return S_OK;
  7198. }
  7199. /* ============================================================================
  7200. CIncFile::GetContextOfPosition
  7201. Given the character position & number of characters in the document,
  7202. encapsulate this into a document context object.
  7203. */
  7204. HRESULT CIncFile::GetContextOfPosition
  7205. (
  7206. /* [in] */ ULONG cchSourceOffset,
  7207. /* [in] */ ULONG cchText,
  7208. /* [out] */ IDebugDocumentContext **ppDocumentContext
  7209. )
  7210. {
  7211. if (
  7212. (*ppDocumentContext = new CIncFileDocumentContext(this, cchSourceOffset, cchText))
  7213. == NULL
  7214. )
  7215. return E_OUTOFMEMORY;
  7216. return S_OK;
  7217. }
  7218. /* ****************************************************************************
  7219. IConnectionPointContainer implementation
  7220. */
  7221. /* ============================================================================
  7222. CIncFile::FindConnectionPoint
  7223. From a character offset and length, return the document text
  7224. */
  7225. HRESULT CIncFile::FindConnectionPoint
  7226. (
  7227. const GUID &uidConnection,
  7228. IConnectionPoint **ppCP
  7229. )
  7230. {
  7231. if (uidConnection == IID_IDebugDocumentTextEvents)
  7232. return m_CPTextEvents.QueryInterface(IID_IConnectionPoint, reinterpret_cast<void **>(ppCP));
  7233. else
  7234. {
  7235. *ppCP = NULL;
  7236. return E_NOINTERFACE;
  7237. }
  7238. }
  7239. /* ============================================================================
  7240. CIncFile::GetFilemap
  7241. Returns a CFileMap pointer for this include file. (Note: There are several
  7242. CFileMaps that may be used, corresponding to each template. This function
  7243. selects one of them.)
  7244. Returns:
  7245. Corresponding CFileMap
  7246. Side effects:
  7247. None
  7248. */
  7249. CTemplate::CFileMap *
  7250. CIncFile::GetFilemap
  7251. (
  7252. )
  7253. {
  7254. // Get pointer to first template's filemaps
  7255. CTemplate::CFileMap **ppFilemapInc = &m_rgpTemplates[0]->m_rgpFilemaps[1];
  7256. BOOL fFoundInc = FALSE;
  7257. // Look for the filemap whose name corresponds to this IncFile. It had better exist
  7258. // in all template filemaps.
  7259. // NOTE: Start searching at position 1, because position 0 is the template itself.
  7260. //
  7261. for (unsigned i = 1; i < m_rgpTemplates[0]->m_cFilemaps && !fFoundInc; ++i)
  7262. if (_tcscmp(m_szIncFile, (*ppFilemapInc++)->m_szPathTranslated) == 0)
  7263. fFoundInc = TRUE;
  7264. Assert (fFoundInc);
  7265. return ppFilemapInc[-1];
  7266. }
  7267. /* ============================================================================
  7268. CIncFile::AddTemplate
  7269. Adds a template to the list of templates that include this inc-file
  7270. Returns:
  7271. HRESULT
  7272. Side effects:
  7273. None
  7274. */
  7275. HRESULT
  7276. CIncFile::AddTemplate
  7277. (
  7278. CTemplate* pTemplate
  7279. )
  7280. {
  7281. EnterCriticalSection(&m_csUpdate);
  7282. // Add the template to the list only if it does not exist
  7283. if (m_rgpTemplates.find(pTemplate) == -1)
  7284. {
  7285. if (FAILED(m_rgpTemplates.append(pTemplate)))
  7286. {
  7287. LeaveCriticalSection(&m_csUpdate);
  7288. return E_OUTOFMEMORY;
  7289. }
  7290. // Notify the debugger that template dependency has changed
  7291. // (Ignore failure)
  7292. //
  7293. if (g_pDebugApp)
  7294. {
  7295. IF_DEBUG(SCRIPT_DEBUGGER)
  7296. DBGPRINTF((DBG_CONTEXT, "AddTemplate: Notifying debugger to refresh breakpoints\n"));
  7297. InvokeDebuggerWithThreadSwitch
  7298. (
  7299. g_pDebugApp,
  7300. DEBUGGER_EVENT_ON_REFRESH_BREAKPOINT,
  7301. static_cast<IDebugDocument *>(this)
  7302. );
  7303. }
  7304. }
  7305. LeaveCriticalSection(&m_csUpdate);
  7306. return S_OK;
  7307. }
  7308. /* ============================================================================
  7309. CIncFile::RemoveTemplate
  7310. Removes a template from the template list
  7311. Returns:
  7312. Nothing
  7313. Side effects:
  7314. Compresses the removed template's ptr out of template ptrs array (see "back-copy", below)
  7315. Decrements template count
  7316. */
  7317. void
  7318. CIncFile::RemoveTemplate
  7319. (
  7320. CTemplate* pTemplate
  7321. )
  7322. {
  7323. EnterCriticalSection(&m_csUpdate);
  7324. // find the template in list
  7325. int i = m_rgpTemplates.find(pTemplate);
  7326. // Remove the element (If we found it - possible that this is 2nd instance of #include and was previously removed)
  7327. if (i != -1)
  7328. {
  7329. m_rgpTemplates.removeAt(i);
  7330. // Notify the debugger that template dependency has changed
  7331. // (Ignore failure)
  7332. //
  7333. if (g_pDebugApp)
  7334. {
  7335. IF_DEBUG(SCRIPT_DEBUGGER)
  7336. DBGPRINTF((DBG_CONTEXT, "RemoveTemplate: Notifying debugger to refresh breakpoints\n"));
  7337. InvokeDebuggerWithThreadSwitch
  7338. (
  7339. g_pDebugApp,
  7340. DEBUGGER_EVENT_ON_REFRESH_BREAKPOINT,
  7341. static_cast<IDebugDocument *>(this)
  7342. );
  7343. }
  7344. }
  7345. LeaveCriticalSection(&m_csUpdate);
  7346. }
  7347. /* ============================================================================
  7348. CIncFile::FlushTemplates
  7349. Flushes all of this inc-file's templates from the global template cache
  7350. Returns:
  7351. TRUE if all templates flushed, FALSE if some left
  7352. Side effects:
  7353. None
  7354. */
  7355. BOOL
  7356. CIncFile::FlushTemplates
  7357. (
  7358. )
  7359. {
  7360. /* NOTE we have a cross-dependency with RemoveTemplate() because the following call chain
  7361. occurs when an inc-file gets flushed:
  7362. CIncFileMap::Flush
  7363. CIncFile::FlushTemplates
  7364. CTemplateCacheManager::Flush
  7365. CTemplate::RemoveFromIncFiles
  7366. CIncFile::RemoveTemplate
  7367. The problem is that RemoveTemplate() updates m_cTemplates and m_rgTemplates, so these members
  7368. will not be stable during the loop within FlushTemplates.
  7369. To get around this, we make a local copy of m_rgTemplates.
  7370. */
  7371. EnterCriticalSection(&m_csUpdate);
  7372. STACK_BUFFER( tempTemplates, 128 );
  7373. STACK_BUFFER( tempFile, MAX_PATH );
  7374. UINT cTemplates = m_rgpTemplates.length();
  7375. if (!tempTemplates.Resize(cTemplates * sizeof(CTemplate*))) {
  7376. // failed to get memory. The best we can do is return FALSE to indicate
  7377. // that not all templates where flushed.
  7378. LeaveCriticalSection(&m_csUpdate);
  7379. return FALSE;
  7380. }
  7381. CTemplate** rgpTemplates = static_cast<CTemplate**> (tempTemplates.QueryPtr());
  7382. memcpy(rgpTemplates, m_rgpTemplates.vec(), sizeof(CTemplate *) * cTemplates);
  7383. UINT cTemplatesFlushed = 0;
  7384. for(UINT i = 0; i < cTemplates; i++)
  7385. {
  7386. // If the template is ready now, flush it
  7387. if(rgpTemplates[i]->m_fReadyForUse && !(rgpTemplates[i]->m_fDontCache))
  7388. {
  7389. // bug 917: make a local copy of template file name, since the member gets freed part way through g_TemplateCache.Flush
  7390. TCHAR* szTemp = NULL;
  7391. szTemp = rgpTemplates[i]->GetSourceFileName();
  7392. if (szTemp)
  7393. {
  7394. if (!tempFile.Resize((_tcslen(szTemp) + 1)*sizeof(TCHAR))) {
  7395. // failed on this one. Continue and try to flush as many
  7396. // as we can.
  7397. continue;
  7398. }
  7399. TCHAR *szTemplateFile = (TCHAR *)tempFile.QueryPtr();
  7400. _tcscpy(szTemplateFile, szTemp);
  7401. g_TemplateCache.Flush(szTemplateFile, MATCH_ALL_INSTANCE_IDS);
  7402. cTemplatesFlushed++;
  7403. }
  7404. }
  7405. // If the template was not ready, we don't flush. It will probably
  7406. // pick up the current include file anyway
  7407. }
  7408. LeaveCriticalSection(&m_csUpdate);
  7409. return (cTemplates == cTemplatesFlushed);
  7410. }
  7411. /* ============================================================================
  7412. CIncFile::OnIncFileDecache
  7413. Callback which we use to call onDestroy events in the debugger just before
  7414. we are removed from the IncFile cache.
  7415. REF COUNTING NOTE:
  7416. Since debugging client has a reference to the IDebugDocument, the include needs
  7417. to dis-associate with the debugger at a point in time before destruction.
  7418. Otherwise, the reference will never go to zero.
  7419. */
  7420. void
  7421. CIncFile::OnIncFileDecache
  7422. (
  7423. )
  7424. {
  7425. if (m_CPTextEvents.FIsEmpty() || g_pDebugApp == NULL)
  7426. return;
  7427. IEnumConnections *pConnIterator;
  7428. if (SUCCEEDED(m_CPTextEvents.EnumConnections(&pConnIterator)))
  7429. {
  7430. CONNECTDATA ConnectData;
  7431. while (pConnIterator->Next(1, &ConnectData, NULL) == S_OK)
  7432. {
  7433. IDebugDocumentTextEvents *pTextEventSink;
  7434. if (SUCCEEDED(ConnectData.pUnk->QueryInterface(IID_IDebugDocumentTextEvents, reinterpret_cast<void **>(&pTextEventSink))))
  7435. {
  7436. InvokeDebuggerWithThreadSwitch(g_pDebugApp, DEBUGGER_ON_DESTROY, pTextEventSink);
  7437. pTextEventSink->Release();
  7438. }
  7439. ConnectData.pUnk->Release();
  7440. }
  7441. pConnIterator->Release();
  7442. }
  7443. }
  7444. /* ****************************************************************************
  7445. CTemplate::CBuffer member functions
  7446. */
  7447. /* ============================================================================
  7448. CTemplate::CBuffer::CBuffer
  7449. Ctor
  7450. */
  7451. CTemplate::CBuffer::CBuffer()
  7452. :
  7453. m_pItems(NULL),
  7454. m_cSlots(0),
  7455. m_cItems(0),
  7456. m_pbData(NULL),
  7457. m_cbData(0),
  7458. m_cbDataUsed(0)
  7459. {
  7460. }
  7461. /* ============================================================================
  7462. CTemplate::CBuffer::~CBuffer
  7463. Dtor
  7464. */
  7465. CTemplate::CBuffer::~CBuffer()
  7466. {
  7467. if(m_pItems)
  7468. CTemplate::SmallFree(m_pItems);
  7469. if(m_pbData)
  7470. CTemplate::LargeFree(m_pbData);
  7471. }
  7472. /* ============================================================================
  7473. CTemplate::CBuffer::Init
  7474. Inits a CBuffer
  7475. */
  7476. void
  7477. CTemplate::CBuffer::Init
  7478. (
  7479. USHORT cSlots,
  7480. ULONG cbData
  7481. )
  7482. {
  7483. m_cSlots = cSlots;
  7484. m_cbData = cbData;
  7485. // Allocate space for storing byte range items
  7486. if(!(m_pItems = (CByteRange*) CTemplate::SmallMalloc(m_cSlots * sizeof(CByteRange))))
  7487. THROW(E_OUTOFMEMORY);
  7488. // Allocate space for storing local data, if there is any
  7489. if(m_cbData > 0)
  7490. {
  7491. if(!(m_pbData = (BYTE*) CTemplate::LargeMalloc(m_cbData)))
  7492. THROW(E_OUTOFMEMORY);
  7493. }
  7494. }
  7495. /* ============================================================================
  7496. CTemplate::CBuffer::Append
  7497. Appends to a CBuffer
  7498. */
  7499. void
  7500. CTemplate::CBuffer::Append
  7501. (
  7502. const CByteRange& br, // byte range to append
  7503. BOOL fLocal, // append local?
  7504. UINT idSequence, // segment sequence id
  7505. CFileMap* pfilemap,
  7506. BOOL fLocalString // append local as a string? (length-prefixed, null-terminated)
  7507. )
  7508. {
  7509. // calc bytes required to store byte range; allow for length prefix and null if a local string
  7510. ULONG cbRequired = (ULONG)(br.m_cb + (fLocalString ? sizeof(br.m_cb) + 1 : 0));
  7511. // If caller passed a non-local zero-length byte range, no-op and return;
  7512. // allows callers to ignore byte range size
  7513. // NOTE we store empty local byte ranges - required by token list
  7514. if(!fLocal && br.m_cb == 0)
  7515. return;
  7516. if(fLocal)
  7517. {
  7518. if((m_cbData - m_cbDataUsed) < cbRequired)
  7519. {
  7520. // Reallocate space for storing local data - we grab twice what we had before
  7521. // or twice current requirement, whichever is more
  7522. m_cbData = 2 * (m_cbData > cbRequired ? m_cbData : cbRequired);
  7523. if(!(m_pbData = (BYTE*) CTemplate::LargeReAlloc(m_pbData, m_cbData)))
  7524. THROW(E_OUTOFMEMORY);
  7525. }
  7526. // if appending as a local string, copy length-prefix to buffer
  7527. if(fLocalString)
  7528. {
  7529. memcpy(m_pbData + m_cbDataUsed, &(br.m_cb), sizeof(br.m_cb));
  7530. m_cbDataUsed += sizeof(br.m_cb);
  7531. }
  7532. // copy data to buffer
  7533. memcpy(m_pbData + m_cbDataUsed, br.m_pb, br.m_cb);
  7534. m_cbDataUsed += br.m_cb;
  7535. // if appending as a local string, copy null terminator to buffer
  7536. if(fLocalString)
  7537. *(m_pbData + m_cbDataUsed++) = NULL;
  7538. }
  7539. if(m_cItems >= m_cSlots)
  7540. {
  7541. // Reallocate space for storing byte range items - we grab twice what we had before
  7542. m_cSlots *= 2;
  7543. if(!(m_pItems = (CByteRange*) CTemplate::SmallReAlloc(m_pItems, m_cSlots * sizeof(*m_pItems))))
  7544. THROW(E_OUTOFMEMORY);
  7545. }
  7546. // Set the (new) last item to this byte range
  7547. SetItem(m_cItems++, br, fLocal, idSequence, pfilemap, fLocalString);
  7548. }
  7549. /* ============================================================================
  7550. CTemplate::CBuffer::GetItem
  7551. Gets an item from a CBuffer, as a byte range
  7552. Returns:
  7553. Nothing
  7554. Side effects:
  7555. None
  7556. */
  7557. void
  7558. CTemplate::CBuffer::GetItem
  7559. (
  7560. UINT i, // index of item
  7561. CByteRange& br // byte range containing returned item (out-parameter)
  7562. )
  7563. {
  7564. Assert(i < m_cItems);
  7565. // for local data, ptr is offset only; must add it to base ptr
  7566. br.m_pb = m_pItems[i].m_pb + (m_pItems[i].m_fLocal ? (DWORD_PTR) m_pbData : 0);
  7567. br.m_cb = m_pItems[i].m_cb;
  7568. br.m_fLocal = m_pItems[i].m_fLocal;
  7569. br.m_idSequence = m_pItems[i].m_idSequence;
  7570. br.m_pfilemap = m_pItems[i].m_pfilemap;
  7571. }
  7572. /* ============================================================================
  7573. CTemplate::CBuffer::SetItem
  7574. Sets a CBuffer item to a new value
  7575. Returns
  7576. Nothing
  7577. Side effects
  7578. Throws error on non-existent item index
  7579. */
  7580. void
  7581. CTemplate::CBuffer::SetItem
  7582. (
  7583. UINT i,
  7584. const CByteRange& br, // byte range to set item to
  7585. BOOL fLocal, // is item local in buffer?
  7586. UINT idSequence, // segment sequence id
  7587. CFileMap * pfilemap, // file where segment came from
  7588. BOOL fLocalString // append local as a string? (length-prefixed, null-terminated)
  7589. )
  7590. {
  7591. // If buffer item i does not exist, fail
  7592. if(i >= m_cSlots)
  7593. THROW(E_FAIL);
  7594. // for local data, store ptr as offset only - avoids fixup after realloc
  7595. // NOTE offset == data used offset - length of data - null terminator (if local string)
  7596. m_pItems[i].m_pb = (fLocal
  7597. ? (BYTE*)(m_cbDataUsed - br.m_cb -
  7598. (fLocalString
  7599. ? sizeof(BYTE)
  7600. : 0
  7601. ))
  7602. : (BYTE*)br.m_pb);
  7603. m_pItems[i].m_cb = br.m_cb;
  7604. m_pItems[i].m_fLocal = fLocal;
  7605. m_pItems[i].m_idSequence = idSequence;
  7606. m_pItems[i].m_pfilemap = pfilemap;
  7607. }
  7608. /* ============================================================================
  7609. CTemplate::CBuffer::PszLocal
  7610. Gets i-th locally-buffered string within the buffer.
  7611. Returns:
  7612. Ptr to locally-buffered string; NULL if not found
  7613. Side effects:
  7614. None
  7615. */
  7616. LPSTR
  7617. CTemplate::CBuffer::PszLocal
  7618. (
  7619. UINT i // index of item to retrieve
  7620. )
  7621. {
  7622. CByteRange br;
  7623. GetItem(i, br);
  7624. if(!br.m_fLocal)
  7625. return NULL;
  7626. return (LPSTR) br.m_pb;
  7627. }
  7628. /* ****************************************************************************
  7629. CTemplate::CScriptStore member functions
  7630. */
  7631. /* ============================================================================
  7632. CTemplate::CScriptStore::~CScriptStore
  7633. Destructor - frees memory
  7634. Returns:
  7635. nothing
  7636. Side effects:
  7637. none
  7638. */
  7639. CTemplate::CScriptStore::~CScriptStore()
  7640. {
  7641. UINT i;
  7642. for(i = 0; i < m_cSegmentBuffers; i++)
  7643. delete m_ppbufSegments[i];
  7644. if(m_ppbufSegments != NULL)
  7645. CTemplate::SmallFree(m_ppbufSegments);
  7646. if(m_rgProgLangId != NULL)
  7647. CTemplate::SmallFree(m_rgProgLangId);
  7648. }
  7649. /* ============================================================================
  7650. CTemplate::CScriptStore::Init
  7651. Inits the script store
  7652. Returns:
  7653. nothing
  7654. Side effects:
  7655. allocates memory
  7656. */
  7657. HRESULT
  7658. CTemplate::CScriptStore::Init
  7659. (
  7660. LPCSTR szDefaultScriptLanguage,
  7661. CLSID *pCLSIDDefaultEngine
  7662. )
  7663. {
  7664. HRESULT hr = S_OK;
  7665. UINT i;
  7666. CByteRange brDefaultScriptLanguage;
  7667. // Check for NULL pointers - can happen if Application has invalid default lang
  7668. if (szDefaultScriptLanguage == NULL || pCLSIDDefaultEngine == NULL)
  7669. return TYPE_E_ELEMENTNOTFOUND;
  7670. /* init segment buffers count based on:
  7671. - two for default engine (one primary, one tagged)
  7672. - one each for other engines (tagged only)
  7673. */
  7674. m_cSegmentBuffers = C_SCRIPTENGINESDEFAULT + 1;
  7675. // init segments buffers
  7676. if(NULL == (m_ppbufSegments = (CBuffer**) CTemplate::SmallMalloc(m_cSegmentBuffers * sizeof(CBuffer*))))
  7677. {
  7678. hr = E_OUTOFMEMORY;
  7679. goto LExit;
  7680. }
  7681. for(i = 0; i < m_cSegmentBuffers; i++)
  7682. {
  7683. if(NULL == (m_ppbufSegments[i] = new CBuffer))
  7684. {
  7685. hr = E_OUTOFMEMORY;
  7686. goto LExit;
  7687. }
  7688. m_ppbufSegments[i]->Init((C_SCRIPTSEGMENTSDEFAULT), 0);
  7689. }
  7690. // Append default engine to script store
  7691. brDefaultScriptLanguage.m_cb = strlen(szDefaultScriptLanguage);
  7692. brDefaultScriptLanguage.m_pb = (unsigned char *)szDefaultScriptLanguage;
  7693. hr = AppendEngine(brDefaultScriptLanguage, pCLSIDDefaultEngine, /* idSequence */ 0);
  7694. LExit:
  7695. return hr;
  7696. }
  7697. /* ============================================================================
  7698. CTemplate::CScriptStore::AppendEngine
  7699. Appends a script engine to the script store
  7700. Returns:
  7701. HRESULT
  7702. Side effects:
  7703. None
  7704. */
  7705. HRESULT
  7706. CTemplate::CScriptStore::AppendEngine
  7707. (
  7708. CByteRange& brEngine, // engine name
  7709. PROGLANG_ID* pProgLangId, // ptr to prog lang id - pass NULL to have this function get proglangid from registry
  7710. UINT idSequence // segment sequence id
  7711. )
  7712. {
  7713. HRESULT hr = S_OK;
  7714. USHORT cEngines; // count of engines
  7715. TRY
  7716. // if no engines yet, init engine names buffer
  7717. if(CountPreliminaryEngines() == 0)
  7718. m_bufEngineNames.Init(C_SCRIPTENGINESDEFAULT, 0);
  7719. // Append engine name to buffer
  7720. m_bufEngineNames.Append(brEngine, FALSE, idSequence, NULL);
  7721. CATCH(hrException)
  7722. hr = hrException;
  7723. goto LExit;
  7724. END_TRY
  7725. Assert(CountPreliminaryEngines() >= 1);
  7726. // malloc or realloc prog lang ids array
  7727. if((cEngines = CountPreliminaryEngines()) == 1)
  7728. m_rgProgLangId = (PROGLANG_ID*) CTemplate::SmallMalloc(cEngines * sizeof(PROGLANG_ID));
  7729. else
  7730. m_rgProgLangId = (PROGLANG_ID*) CTemplate::SmallReAlloc(m_rgProgLangId, cEngines * sizeof(PROGLANG_ID));
  7731. if(NULL == m_rgProgLangId)
  7732. {
  7733. hr = E_OUTOFMEMORY;
  7734. goto LExit;
  7735. }
  7736. if(NULL == pProgLangId)
  7737. // caller passed null progid ptr - get prog id from registry
  7738. hr = GetProgLangId(brEngine, &(m_rgProgLangId[cEngines - 1]));
  7739. else
  7740. // caller passed non-null progid ptr - set prog id from it
  7741. m_rgProgLangId[cEngines - 1] = *pProgLangId;
  7742. LExit:
  7743. return hr;
  7744. }
  7745. /* ============================================================================
  7746. CTemplate::CScriptStore::IdEngineFromBr
  7747. Determines the id of a script engine from its engine name
  7748. Returns:
  7749. id of script engine whose name is passed in
  7750. Side effects:
  7751. appends a new script engine name to engine names buffer
  7752. */
  7753. USHORT
  7754. CTemplate::CScriptStore::IdEngineFromBr
  7755. (
  7756. CByteRange& brEngine, // engine name
  7757. UINT idSequence // segment sequence id
  7758. )
  7759. {
  7760. Assert(!brEngine.IsNull()); // NOTE we trap/error null engine name earlier
  7761. USHORT cKnownEngines = CountPreliminaryEngines();
  7762. // search existing names for a match; return id if found
  7763. for(USHORT i = 0; i < cKnownEngines; i++)
  7764. {
  7765. Assert(m_bufEngineNames[i]);
  7766. Assert(m_bufEngineNames[i]->m_pb);
  7767. if(FByteRangesAreEqual(*(m_bufEngineNames[i]), brEngine))
  7768. return i;
  7769. }
  7770. // if not found by name try to find by engine id
  7771. // (some engines with different names share the same id, like J[ava]Script)
  7772. if (cKnownEngines > 0)
  7773. {
  7774. PROGLANG_ID ProgLandId;
  7775. // we will get the prog lang id again inside AppendEngine() but
  7776. // because it's cached and this only happens when > 1 engine, it's alright
  7777. if (SUCCEEDED(GetProgLangId(brEngine, &ProgLandId)))
  7778. {
  7779. for(i = 0; i < cKnownEngines; i++)
  7780. {
  7781. // If matches don't append -- just return the index
  7782. if (m_rgProgLangId[i] == ProgLandId)
  7783. return i;
  7784. }
  7785. }
  7786. }
  7787. /* if we did not find engine among those already buffered
  7788. - append engine to script store
  7789. - realloc segment buffers array if necessary
  7790. - return index of last engine (the one we just appended)
  7791. */
  7792. // append engine to script store
  7793. HRESULT hr = AppendEngine(brEngine, NULL, idSequence);
  7794. if(hr == TYPE_E_ELEMENTNOTFOUND)
  7795. // if prog lang not found, throw bad prog lang error id
  7796. THROW(IDE_TEMPLATE_BAD_PROGLANG);
  7797. else if(FAILED(hr))
  7798. // other failure: re-throw hresult
  7799. THROW(hr);
  7800. // realloc segment buffers array if necessary
  7801. if(CountPreliminaryEngines() > (m_cSegmentBuffers - 1))
  7802. {
  7803. // increment count of segment buffers
  7804. m_cSegmentBuffers++;
  7805. Assert(CountPreliminaryEngines() == m_cSegmentBuffers - 1);
  7806. // realloc array of ptrs
  7807. if(NULL == (m_ppbufSegments = (CBuffer**) CTemplate::SmallReAlloc(m_ppbufSegments, m_cSegmentBuffers * sizeof(CBuffer*))))
  7808. THROW(E_OUTOFMEMORY);
  7809. // allocate the new buffer
  7810. if(NULL == (m_ppbufSegments[m_cSegmentBuffers - 1] = new CBuffer))
  7811. THROW(E_OUTOFMEMORY);
  7812. // init the new buffer
  7813. m_ppbufSegments[m_cSegmentBuffers - 1]->Init(C_SCRIPTSEGMENTSDEFAULT, 0);
  7814. }
  7815. // return index of last engine (the one we just appended)
  7816. return (CountPreliminaryEngines() - 1);
  7817. }
  7818. /* ============================================================================
  7819. CTemplate::CScriptStore::AppendScript
  7820. Appends a script/engine pair to the store.
  7821. Returns:
  7822. Nothing
  7823. Side effects:
  7824. None
  7825. */
  7826. void
  7827. CTemplate::CScriptStore::AppendScript
  7828. (
  7829. CByteRange& brScript, // script text
  7830. CByteRange& brEngine, // script engine name
  7831. BOOLB fPrimary, // primary or tagged script?
  7832. UINT idSequence, // segment sequence id
  7833. CFileMap* pfilemapCurrent
  7834. )
  7835. {
  7836. USHORT iBuffer; // buffer id
  7837. Assert(fPrimary || !brEngine.IsNull()); // NOTE we trap/error null engine name earlier
  7838. Assert(m_bufEngineNames[0]); // page's primary engine must be known by this point
  7839. Assert(m_bufEngineNames[0]->m_pb);
  7840. if(fPrimary)
  7841. // if primary script (not tagged), buffer id is 0
  7842. iBuffer = 0;
  7843. else if((!fPrimary) && FByteRangesAreEqual(brEngine, /* bug 1008: primary script engine name */ *(m_bufEngineNames[0])))
  7844. // if tagged script and engine is primary, buffer id is 1
  7845. iBuffer = 1;
  7846. else
  7847. // else, buffer id is engine id plus 1
  7848. iBuffer = IdEngineFromBr(brEngine, idSequence) + 1;
  7849. // append script segment to iBuffer-th segments buffer
  7850. m_ppbufSegments[iBuffer]->Append(brScript, FALSE, idSequence, pfilemapCurrent);
  7851. }
  7852. /* ****************************************************************************
  7853. CTemplate::CObjectInfoStore member functions
  7854. */
  7855. /* ============================================================================
  7856. CTemplate::CObjectInfoStore::~CObjectInfoStore
  7857. */
  7858. CTemplate::CObjectInfoStore::~CObjectInfoStore
  7859. (
  7860. )
  7861. {
  7862. if(m_pObjectInfos)
  7863. CTemplate::SmallFree(m_pObjectInfos);
  7864. }
  7865. /* ============================================================================
  7866. CTemplate::CObjectInfoStore::Init
  7867. Inits the object-info store
  7868. */
  7869. void
  7870. CTemplate::CObjectInfoStore::Init()
  7871. {
  7872. m_bufObjectNames.Init(C_OBJECTINFOS_DEFAULT, 0);
  7873. // init object-infos array
  7874. if(NULL == (m_pObjectInfos = (CObjectInfo*) CTemplate::SmallMalloc(m_bufObjectNames.CountSlots() * sizeof(CObjectInfo))))
  7875. THROW(E_OUTOFMEMORY);
  7876. }
  7877. /* ============================================================================
  7878. CTemplate::CObjectInfoStore::AppendObject
  7879. Appends an object-info to the object-info store
  7880. */
  7881. void
  7882. CTemplate::CObjectInfoStore::AppendObject
  7883. (
  7884. CByteRange& brObjectName,
  7885. CLSID clsid,
  7886. CompScope scope,
  7887. CompModel model,
  7888. UINT idSequence
  7889. )
  7890. {
  7891. USHORT iObject = m_bufObjectNames.Count();
  7892. if(iObject >= m_bufObjectNames.CountSlots())
  7893. {
  7894. // Reallocate space for storing object-infos - we grab twice what we had before
  7895. // NOTE we keep no object count in CObjectInfoStore, but instead use count in object names buffer
  7896. (m_pObjectInfos = (CObjectInfo*)CTemplate::SmallReAlloc(m_pObjectInfos,
  7897. 2 * m_bufObjectNames.CountSlots() * sizeof(CObjectInfo)));
  7898. if (m_pObjectInfos == NULL)
  7899. THROW(E_OUTOFMEMORY);
  7900. }
  7901. m_pObjectInfos[iObject].m_clsid = clsid;
  7902. m_pObjectInfos[iObject].m_scope = scope;
  7903. m_pObjectInfos[iObject].m_model = model;
  7904. m_bufObjectNames.Append(brObjectName, FALSE, idSequence, NULL);
  7905. }
  7906. /* ****************************************************************************
  7907. CTemplate::CWorkStore member functions
  7908. */
  7909. /* ============================================================================
  7910. CTemplate::CWorkStore::CWorkStore
  7911. Constructor
  7912. Returns:
  7913. Nothing
  7914. Side effects:
  7915. None
  7916. */
  7917. CTemplate::CWorkStore::CWorkStore
  7918. (
  7919. )
  7920. :
  7921. m_idCurSequence(0),
  7922. m_fPageCommandsExecuted(FALSE),
  7923. m_fPageCommandsAllowed(TRUE),
  7924. m_szWriteBlockOpen(g_szWriteBlockOpen),
  7925. m_szWriteBlockClose(g_szWriteBlockClose),
  7926. m_szWriteOpen(g_szWriteOpen),
  7927. m_szWriteClose(g_szWriteClose),
  7928. m_pbPrevSource(NULL),
  7929. m_cPrevSourceLines(0),
  7930. m_hPrevFile (NULL)
  7931. { }
  7932. /* ============================================================================
  7933. CTemplate::CWorkStore::~CWorkStore
  7934. Destructor
  7935. Returns:
  7936. Nothing
  7937. Side effects:
  7938. None
  7939. */
  7940. CTemplate::CWorkStore::~CWorkStore
  7941. (
  7942. )
  7943. {
  7944. /* if language element ptrs are anything but their constant defaults or null,
  7945. they must have been allocated during compilation - free them now
  7946. */
  7947. if(m_szWriteBlockOpen != g_szWriteBlockOpen && m_szWriteBlockOpen != NULL)
  7948. CTemplate::SmallFree(m_szWriteBlockOpen);
  7949. if(m_szWriteBlockClose != g_szWriteBlockClose && m_szWriteBlockClose != NULL)
  7950. CTemplate::SmallFree(m_szWriteBlockClose);
  7951. if(m_szWriteOpen != g_szWriteOpen && m_szWriteOpen != NULL)
  7952. CTemplate::SmallFree(m_szWriteOpen);
  7953. if(m_szWriteClose != g_szWriteClose && m_szWriteClose != NULL)
  7954. CTemplate::SmallFree(m_szWriteClose);
  7955. }
  7956. /* ============================================================================
  7957. CTemplate::CWorkStore::Init
  7958. Inits the workstore
  7959. Returns:
  7960. Nothing
  7961. Side effects:
  7962. None
  7963. */
  7964. void
  7965. CTemplate::CWorkStore::Init
  7966. (
  7967. )
  7968. {
  7969. /*
  7970. NOTE init we the scriptstore separately from rest of workstore
  7971. because try-catch in CTemplate::Init() apparently doesn't work to detect
  7972. bogus script engine name; we need to get an hr back instead.
  7973. m_ScriptStore.Init(brDefaultEngine);
  7974. */
  7975. m_ObjectInfoStore.Init();
  7976. m_bufHTMLSegments.Init(C_HTMLSEGMENTSDEFAULT, 0);
  7977. }
  7978. /* ============================================================================
  7979. CTemplate::CWorkStore::CRequiredScriptEngines
  7980. Returns the count of script engines in the script store that are required
  7981. to run the template.
  7982. NOTE this function is part of the fix for bug 933
  7983. Returns:
  7984. Count of non-empty script engines
  7985. Side effects:
  7986. None
  7987. */
  7988. USHORT
  7989. CTemplate::CWorkStore::CRequiredScriptEngines
  7990. (
  7991. BOOL fGlobalAsa // bug 1394: is template global.asa?
  7992. )
  7993. {
  7994. USHORT cPreliminaryEngines = m_ScriptStore.CountPreliminaryEngines();
  7995. USHORT cRequiredEngines = 0;
  7996. for(USHORT i = 0; i < cPreliminaryEngines; i++)
  7997. {
  7998. if(FScriptEngineRequired(i, fGlobalAsa))
  7999. cRequiredEngines++;
  8000. }
  8001. return cRequiredEngines;
  8002. }
  8003. /* ============================================================================
  8004. CTemplate::CWorkStore::FScriptEngineRequired
  8005. Is a given preliminary script engine required to run the template?
  8006. NOTE this function is part of the fix for bug 933
  8007. Returns:
  8008. TRUE or FALSE
  8009. Side effects:
  8010. None
  8011. */
  8012. BOOLB
  8013. CTemplate::CWorkStore::FScriptEngineRequired
  8014. (
  8015. USHORT idEnginePrelim,
  8016. BOOL fGlobalAsa // bug 1394: is template global.asa?
  8017. )
  8018. {
  8019. if(idEnginePrelim == 0)
  8020. return ( // primary engine (id 0) required if
  8021. (m_ScriptStore.m_ppbufSegments[0]->Count() > 0) // ... script buffer 0 has segments
  8022. || (m_ScriptStore.m_ppbufSegments[1]->Count() > 0) // ... or script buffer 1 has segments
  8023. || ((m_bufHTMLSegments.Count() > 0) && !fGlobalAsa) // ... or html buffer has segments and (bug 1394) template is not global.asa
  8024. );
  8025. // non-primary engine required if script buffer id+1 has segments
  8026. return (m_ScriptStore.m_ppbufSegments[idEnginePrelim + 1]->Count() > 0);
  8027. }
  8028. /* ****************************************************************************
  8029. CTemplate::CFileMap member functions
  8030. */
  8031. /* ============================================================================
  8032. CTemplate::CFileMap::CFileMap
  8033. Constructor
  8034. Returns
  8035. Nothing
  8036. Side effects
  8037. None
  8038. */
  8039. CTemplate::CFileMap::CFileMap()
  8040. :
  8041. m_szPathInfo(NULL),
  8042. m_szPathTranslated(NULL),
  8043. m_pfilemapSibling(NULL),
  8044. m_pfilemapChild(NULL),
  8045. m_hFile(NULL),
  8046. m_hMap(NULL),
  8047. m_pbStartOfFile(NULL),
  8048. m_pIncFile(NULL),
  8049. m_pSecurityDescriptor(NULL),
  8050. m_dwSecDescSize(0),
  8051. m_cChars(0),
  8052. m_pDME(NULL)
  8053. {
  8054. m_ftLastWriteTime.dwLowDateTime = 0;
  8055. m_ftLastWriteTime.dwHighDateTime = 0;
  8056. }
  8057. /* ============================================================================
  8058. CTemplate::CFileMap::~CFileMap
  8059. Destructor
  8060. Returns
  8061. Nothing
  8062. Side effects
  8063. None
  8064. */
  8065. CTemplate::CFileMap::~CFileMap()
  8066. {
  8067. if (m_pDME)
  8068. {
  8069. m_pDME->Release();
  8070. m_pDME = NULL;
  8071. }
  8072. if(m_szPathInfo != NULL)
  8073. CTemplate::SmallFree(m_szPathInfo);
  8074. if(m_szPathTranslated != NULL)
  8075. CTemplate::SmallFree(m_szPathTranslated);
  8076. if(m_pSecurityDescriptor != NULL)
  8077. CTemplate::SmallFree(m_pSecurityDescriptor);
  8078. if (m_pIncFile != NULL)
  8079. m_pIncFile->Release();
  8080. }
  8081. /* ============================================================================
  8082. CTemplate::CFileMap::MapFile
  8083. Memory-maps a file.
  8084. Returns
  8085. Nothing
  8086. Side effects
  8087. Throws **overloaded** exception on error: exception code can sometimes be
  8088. an error message id, sometimes a true exception. Caller must handle.
  8089. */
  8090. void
  8091. CTemplate::CFileMap::MapFile
  8092. (
  8093. LPCTSTR szFileSpec, // file spec for this file
  8094. LPCTSTR szApplnPath, // application path (in case its global.asa)
  8095. CFileMap* pfilemapParent, // ptr to filemap of parent file
  8096. BOOL fVirtual, // is file spec virtual or relative?
  8097. CHitObj* pHitObj, // ptr to template's hit object
  8098. BOOL fGlobalAsa // is this file the global.asa file?
  8099. )
  8100. {
  8101. BOOL fMustNormalize = TRUE;
  8102. BOOL fImpersonatedUser = FALSE;
  8103. HANDLE hVirtIncImpToken = NULL;
  8104. HANDLE hCurImpToken = NULL;
  8105. Assert((pfilemapParent != NULL) || (pHitObj->PIReq() != NULL) || fGlobalAsa);
  8106. /* three possible cases:
  8107. 1) we are processing global.asa file
  8108. 2) we are processing the "main" .asp file
  8109. 3) we are processing an include file
  8110. */
  8111. if(fGlobalAsa)
  8112. {
  8113. // case 1) we are processing global.asa file
  8114. Assert(pHitObj->GlobalAspPath());
  8115. DWORD cchPathTranslated = _tcslen(pHitObj->GlobalAspPath());
  8116. m_szPathTranslated = (TCHAR *)CTemplate::SmallMalloc((cchPathTranslated+1)*sizeof(TCHAR));
  8117. if (!m_szPathTranslated)
  8118. THROW(E_OUTOFMEMORY);
  8119. _tcscpy(m_szPathTranslated, pHitObj->GlobalAspPath());
  8120. DWORD cchPathInfo = _tcslen(szApplnPath) + 11; // "/global.asa"
  8121. m_szPathInfo = (TCHAR *)CTemplate::SmallMalloc((cchPathInfo+1) * sizeof(TCHAR));
  8122. if (!m_szPathInfo)
  8123. THROW(E_OUTOFMEMORY);
  8124. _tcscpy(strcpyEx(m_szPathInfo, szApplnPath), _T("/global.asa"));
  8125. // no need to normalize in this case, since global.asa path is already normalized
  8126. Assert(IsNormalized((const TCHAR*)m_szPathTranslated));
  8127. fMustNormalize = FALSE;
  8128. m_fHasVirtPath = TRUE;
  8129. }
  8130. else if(pfilemapParent == NULL)
  8131. {
  8132. // case 2) we are processing the "main" .asp file: get path-info and path-tran from ecb
  8133. Assert(pHitObj->PIReq());
  8134. TCHAR *szVirtPath = pHitObj->PSzCurrTemplateVirtPath();
  8135. TCHAR *szPhysPath = pHitObj->PSzCurrTemplatePhysPath();
  8136. m_szPathInfo = static_cast<LPTSTR>(CTemplate::SmallMalloc((_tcslen(szVirtPath) + 1)*sizeof(TCHAR)));
  8137. m_szPathTranslated = static_cast<LPTSTR>(CTemplate::SmallMalloc((_tcslen(szPhysPath) + 1)*sizeof(TCHAR)));
  8138. if (!m_szPathInfo || !m_szPathTranslated)
  8139. THROW(E_OUTOFMEMORY);
  8140. _tcscpy(m_szPathInfo, szVirtPath);
  8141. _tcscpy(m_szPathTranslated, szPhysPath);
  8142. // no need to normalize in this case, since ecb's path-tran is already normalized
  8143. Assert(IsNormalized((const TCHAR*)m_szPathTranslated));
  8144. fMustNormalize = FALSE;
  8145. m_fHasVirtPath = TRUE;
  8146. }
  8147. else
  8148. {
  8149. /* case 3) we are processing an include file: resolve filespec into path-info and path-tran
  8150. based on whether file was included with VIRTUAL tag or FILE tag
  8151. */
  8152. Assert(szFileSpec);
  8153. // in this case, we don't know path lengths up front so we alloc the max and realloc below
  8154. m_szPathInfo = static_cast<LPTSTR> (CTemplate::SmallMalloc((MAX_PATH + 1)*sizeof(TCHAR)));
  8155. m_szPathTranslated = static_cast<LPTSTR> (CTemplate::SmallMalloc((MAX_PATH + 1)*sizeof(TCHAR)));
  8156. if (!m_szPathInfo || !m_szPathTranslated)
  8157. THROW(E_OUTOFMEMORY);
  8158. STACK_BUFFER(tempPathT, (MAX_PATH+1)*sizeof(TCHAR) );
  8159. if (!tempPathT.Resize((_tcslen(szFileSpec) + 1)*sizeof(TCHAR))) {
  8160. THROW(E_OUTOFMEMORY);
  8161. }
  8162. LPTSTR szPathTranslatedT = (TCHAR *)tempPathT.QueryPtr(); // temp path-tran
  8163. if(fVirtual) {
  8164. DWORD dwSzLength = tempPathT.QuerySize(); // length of path string buffer
  8165. if (_tcslen(szFileSpec) > MAX_PATH)
  8166. THROW(E_FAIL);
  8167. // VIRTUAL: path-info is simply virtual filespec
  8168. _tcscpy(m_szPathInfo, szFileSpec);
  8169. // VIRTUAL: path-tran is translation of path-info
  8170. _tcscpy(szPathTranslatedT, m_szPathInfo);
  8171. if (!pHitObj->PIReq()->MapUrlToPath(szPathTranslatedT, &dwSzLength))
  8172. THROW(E_FAIL);
  8173. // Check the translated path for a UNC specified path
  8174. if ((dwSzLength >= (2*sizeof(TCHAR)))
  8175. && (szPathTranslatedT[0] == _T('\\'))
  8176. && (szPathTranslatedT[1] == _T('\\'))) {
  8177. // if UNC, then ask WAM for the impersonation token for
  8178. // this UNC VRoot. Silently fail.
  8179. if (pHitObj->PIReq()->ServerSupportFunction(
  8180. HSE_REQ_GET_VIRTUAL_PATH_TOKEN,
  8181. (void *)szFileSpec,
  8182. (DWORD *) &hVirtIncImpToken,
  8183. NULL)) {
  8184. // set the impersonation token and note that we did so
  8185. // NOTE - there is intentionally no error checking. The
  8186. // assumption being that we are doing best effort at the
  8187. // impersonation because throwing an error here could be
  8188. // tricky for the user to interpret the problem. However,
  8189. // if the impersonation fails, and ASP can still open the
  8190. // file (e.g. passthru authentication), then everyone's
  8191. // happy.
  8192. AspDoRevertHack(&hCurImpToken);
  8193. fImpersonatedUser = ImpersonateLoggedOnUser(hVirtIncImpToken)
  8194. ? TRUE
  8195. : FALSE;
  8196. if (!fImpersonatedUser)
  8197. {
  8198. AspUndoRevertHack(&hCurImpToken);
  8199. }
  8200. }
  8201. }
  8202. m_fHasVirtPath = TRUE;
  8203. }
  8204. else
  8205. {
  8206. TCHAR szParentDir[MAX_PATH], *szT;
  8207. _tcscpy(szParentDir, pfilemapParent->m_szPathInfo);
  8208. if ((szT = _tcsrchr(szParentDir, _T('/'))) != NULL)
  8209. *szT = _T('\0');
  8210. // If we don't allow parent paths, we can save lots of time (Always have a valid virtual path)
  8211. if (!pHitObj->QueryAppConfig()->fEnableParentPaths())
  8212. {
  8213. int strlen_szParentDir = (int)(szT - szParentDir);
  8214. if ((strlen_szParentDir + 1 + _tcslen(szFileSpec)) > MAX_PATH)
  8215. THROW(E_FAIL);
  8216. strcpyEx(strcpyEx(strcpyEx(m_szPathInfo, szParentDir), _T("/")), szFileSpec);
  8217. m_fHasVirtPath = TRUE;
  8218. }
  8219. else
  8220. {
  8221. // NOTE: If we must translate ".." paths, there is no need to verify them (by remapping)
  8222. // because: If the file does not exist, that case will show up when the file is mapped
  8223. // If we ".." ourselves out of the vroot space, (out of the app or into another app)
  8224. // DotPathToPath will detect this.
  8225. //
  8226. if (DotPathToPath(m_szPathInfo, szFileSpec, szParentDir))
  8227. m_fHasVirtPath = TRUE;
  8228. else
  8229. {
  8230. GetPathFromParentAndFilespec(pfilemapParent->m_szPathTranslated, szFileSpec, &m_szPathInfo);
  8231. m_fHasVirtPath = FALSE;
  8232. }
  8233. }
  8234. GetPathFromParentAndFilespec(pfilemapParent->m_szPathTranslated, szFileSpec, &szPathTranslatedT);
  8235. }
  8236. // bug 1214: get canonical path-tran, without . and ..
  8237. // CONSIDER check for . or .. in name before calling GetFullPathName? UNCs? what else?
  8238. GetFullPathName(
  8239. szPathTranslatedT, // LPCSTR lpFileName, // address of name of file to find path for
  8240. MAX_PATH + 1, // DWORD nBufferLength, // size, in characters, of path buffer
  8241. m_szPathTranslated, // LPSTR lpBuffer, // address of path buffer
  8242. NULL // LPSTR *lpFilePart // address of filename in path
  8243. );
  8244. // realloc path strings to only use required memory (see note above)
  8245. m_szPathInfo = static_cast<LPTSTR> (CTemplate::SmallReAlloc(m_szPathInfo, (_tcslen(m_szPathInfo) + 1)*sizeof(TCHAR)));
  8246. m_szPathTranslated = static_cast<LPTSTR> (CTemplate::SmallReAlloc(m_szPathTranslated, (_tcslen(m_szPathTranslated) + 1)*sizeof(TCHAR)));
  8247. if (!m_szPathInfo || !m_szPathTranslated) {
  8248. if (fImpersonatedUser)
  8249. AspUndoRevertHack(&hCurImpToken);
  8250. if (hVirtIncImpToken)
  8251. CloseHandle(hVirtIncImpToken);
  8252. THROW(E_OUTOFMEMORY);
  8253. }
  8254. }
  8255. // if required, normalize path-tran so that
  8256. // a) cyclic include check can ignore case; b) inc-file cache lookups will work
  8257. if(fMustNormalize)
  8258. Normalize(m_szPathTranslated);
  8259. Assert(IsNormalized(m_szPathTranslated));
  8260. // Bug 99071: Attempt to open the file **BEFORE** we add it to the tree of file
  8261. // dependencies. Otherwise if it fails to open, we will have
  8262. // dangling references. Since FCyclicInclude depends on us adding
  8263. // to the tree, if it is cyclic, we need to unmap then. Since that
  8264. // is a very uncommon error case, the extra overhead is probably OK
  8265. //
  8266. // RemapFile will throw if it fails. If the exception is that the source file is empty
  8267. // and we are trying to process an include file, we will handle the exception here.
  8268. // in all other cases, rethrow the exception. We do this so that an empty include file
  8269. // will be harmless, but an empty primary file will fail.
  8270. TRY
  8271. RemapFile();
  8272. CATCH(hrException)
  8273. if (hrException != E_SOURCE_FILE_IS_EMPTY || pfilemapParent == NULL) {
  8274. if (fImpersonatedUser)
  8275. AspUndoRevertHack(&hCurImpToken);
  8276. if (hVirtIncImpToken)
  8277. CloseHandle(hVirtIncImpToken);
  8278. THROW(hrException);
  8279. }
  8280. END_TRY
  8281. if (fImpersonatedUser)
  8282. AspUndoRevertHack(&hCurImpToken);
  8283. if (hVirtIncImpToken)
  8284. CloseHandle(hVirtIncImpToken);
  8285. // Create the tree structure for this file
  8286. if (pfilemapParent != NULL)
  8287. {
  8288. // See if this file is already included once on this level. (Don't show duplicates in the
  8289. // debugger tree view)
  8290. //
  8291. BOOL fDuplicateExists = FALSE;
  8292. CFileMap *pFilemap = pfilemapParent->m_pfilemapChild;
  8293. while (pFilemap != NULL && !fDuplicateExists)
  8294. {
  8295. if (_tcscmp(pFilemap->m_szPathTranslated, m_szPathTranslated) == 0)
  8296. fDuplicateExists = TRUE;
  8297. pFilemap = pFilemap->m_fHasSibling? pFilemap->m_pfilemapSibling : NULL;
  8298. }
  8299. // If the include file is #include'd more than once, don't add it as a sibling.
  8300. // Rather orphan the pfilemap and just set the parent pointer.
  8301. //
  8302. if (!fDuplicateExists)
  8303. {
  8304. if (pfilemapParent->m_pfilemapChild == NULL)
  8305. pfilemapParent->m_pfilemapChild = this;
  8306. else
  8307. pfilemapParent->m_pfilemapChild->AddSibling(this);
  8308. }
  8309. }
  8310. // in both of the above code paths, we are always added as the LAST child, (or we are an orphan node)
  8311. // so it is safe to set the parent without calling SetParent()
  8312. m_fHasSibling = FALSE; // Paranoia
  8313. m_pfilemapParent = pfilemapParent;
  8314. // hurl if this file is being included by itself (perhaps indirectly)
  8315. if(FCyclicInclude(m_szPathTranslated)) {
  8316. UnmapFile();
  8317. THROW(IDE_TEMPLATE_CYCLIC_INCLUDE);
  8318. }
  8319. }
  8320. /* ============================================================================
  8321. CTemplate::CFileMap::RemapFile
  8322. map a file that was previously mapped.
  8323. Returns
  8324. Nothing
  8325. Side effects
  8326. Throws **overloaded** exception on error: exception code can sometimes be
  8327. an error message id, sometimes a true exception. Caller must handle.
  8328. Does not decrypt EASPs on remapping. Caller must decrypt if required. This
  8329. function is called by the debugger, and the debugger does not allow access
  8330. to decrypted files, so decryption is a waste of time.
  8331. */
  8332. void
  8333. CTemplate::CFileMap::RemapFile
  8334. (
  8335. )
  8336. {
  8337. WIN32_FILE_ATTRIBUTE_DATA fad; // win32 file attributes data structure
  8338. if (FIsMapped())
  8339. return;
  8340. if(INVALID_HANDLE_VALUE == (m_hFile =
  8341. CreateFile(
  8342. m_szPathTranslated, // file name
  8343. GENERIC_READ, // access (read-write) mode
  8344. FILE_SHARE_READ, // share mode
  8345. NULL, // pointer to security descriptor
  8346. OPEN_EXISTING, // how to create
  8347. FILE_ATTRIBUTE_NORMAL, // file attributes
  8348. NULL // handle to file with attributes to copy
  8349. )))
  8350. {
  8351. DWORD dwLastError = GetLastError();
  8352. if(dwLastError == ERROR_ACCESS_DENIED)
  8353. {
  8354. // typically, we end up here if the user has no permissions on the file
  8355. // bug 1007: however, we also end up here if the user gave us a directory name, instead of a file name
  8356. if(FAILED(AspGetFileAttributes(m_szPathTranslated, &fad)))
  8357. {
  8358. // bug 1495: file in a secured directory will end up here - we need to re-GetLastError to see if access is denied
  8359. dwLastError = GetLastError();
  8360. if(dwLastError == ERROR_ACCESS_DENIED)
  8361. {
  8362. THROW(E_USER_LACKS_PERMISSIONS);
  8363. }
  8364. // GetFileAttributes call failed; don't know why
  8365. THROW(E_FAIL);
  8366. }
  8367. else if(FILE_ATTRIBUTE_DIRECTORY & fad.dwFileAttributes)
  8368. {
  8369. // bug 1007: the user gave us a directory name
  8370. #if UNICODE
  8371. DBGPRINTF((DBG_CONTEXT, "Failed to open file %S because it is a directory.\n", m_szPathTranslated));
  8372. #else
  8373. DBGPRINTF((DBG_CONTEXT, "Failed to open file %s because it is a directory.\n", m_szPathTranslated));
  8374. #endif
  8375. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8376. }
  8377. else
  8378. {
  8379. THROW(E_USER_LACKS_PERMISSIONS);
  8380. }
  8381. }
  8382. else
  8383. {
  8384. #if DBG
  8385. char szError[128];
  8386. if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM,
  8387. NULL,
  8388. dwLastError,
  8389. 0L, // lang ID - defaults to LANG_NEUTRAL
  8390. szError,
  8391. sizeof szError,
  8392. NULL) )
  8393. {
  8394. sprintf(szError, "%d", dwLastError);
  8395. }
  8396. #if UNICODE
  8397. DBGPRINTF((DBG_CONTEXT, "Failed to open file %S\n", m_szPathTranslated));
  8398. #else
  8399. DBGPRINTF((DBG_CONTEXT, "Failed to open file %s\n", m_szPathTranslated));
  8400. #endif
  8401. DBGPRINTF((DBG_CONTEXT, " The error returned was: %s\n", szError));
  8402. #endif
  8403. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8404. }
  8405. }
  8406. // Get the file's last access time. Only do this for NT
  8407. if (Glob(fWinNT))
  8408. {
  8409. if (SUCCEEDED(AspGetFileAttributes(m_szPathTranslated, &fad)))
  8410. {
  8411. m_ftLastWriteTime.dwLowDateTime = fad.ftLastWriteTime.dwLowDateTime;
  8412. m_ftLastWriteTime.dwHighDateTime = fad.ftLastWriteTime.dwHighDateTime;
  8413. }
  8414. }
  8415. // get file's security descriptor
  8416. if(!GetSecurityDescriptor())
  8417. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8418. // map the file
  8419. if(NULL == (m_hMap =
  8420. CreateFileMapping(
  8421. m_hFile, // handle to file to map
  8422. NULL, // optional security attributes
  8423. PAGE_READONLY, // protection for mapping object
  8424. 0, // high-order 32 bits of object size
  8425. 0, // low-order 32 bits of object size
  8426. NULL // name of file-mapping object
  8427. )))
  8428. {
  8429. if (SUCCEEDED(AspGetFileAttributes(m_szPathTranslated, &fad)))
  8430. {
  8431. if(fad.nFileSizeHigh == 0 && fad.nFileSizeLow == 0)
  8432. {
  8433. #if UNICODE
  8434. DBGPRINTF((DBG_CONTEXT, "Empty source file %S\n", m_szPathTranslated));
  8435. #else
  8436. DBGPRINTF((DBG_CONTEXT, "Empty source file %s\n", m_szPathTranslated));
  8437. #endif
  8438. THROW(E_SOURCE_FILE_IS_EMPTY);
  8439. }
  8440. }
  8441. else
  8442. {
  8443. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8444. }
  8445. }
  8446. // set file's start-of-file ptr
  8447. if(NULL == (m_pbStartOfFile =
  8448. (PBYTE) MapViewOfFile(
  8449. m_hMap, // file-mapping object to map into address space
  8450. FILE_MAP_READ, // access mode
  8451. 0, // high-order 32 bits of file offset
  8452. 0, // low-order 32 bits of file offset
  8453. 0 // number of bytes to map
  8454. )))
  8455. THROW(E_COULDNT_OPEN_SOURCE_FILE);
  8456. }
  8457. /* ============================================================================
  8458. CTemplate::CFileMap::SetParent
  8459. Set the parent for this filemap
  8460. */
  8461. void
  8462. CTemplate::CFileMap::SetParent
  8463. (
  8464. CFileMap* pfilemapParent
  8465. )
  8466. {
  8467. CFileMap *pfilemap = this;
  8468. while (pfilemap->m_fHasSibling)
  8469. pfilemap = pfilemap->m_pfilemapSibling;
  8470. pfilemap->m_pfilemapParent = pfilemapParent;
  8471. }
  8472. /* ============================================================================
  8473. CTemplate::CFileMap::GetParent
  8474. Get the parent for this filemap
  8475. */
  8476. CTemplate::CFileMap*
  8477. CTemplate::CFileMap::GetParent
  8478. (
  8479. )
  8480. {
  8481. register CFileMap *pfilemap = this;
  8482. while (pfilemap->m_fHasSibling)
  8483. pfilemap = pfilemap->m_pfilemapSibling;
  8484. return pfilemap->m_pfilemapParent;
  8485. }
  8486. /* ============================================================================
  8487. CTemplate::CFileMap::AddSibling
  8488. Add a new node as a sibling of this
  8489. */
  8490. void
  8491. CTemplate::CFileMap::AddSibling
  8492. (
  8493. register CFileMap* pfilemapSibling
  8494. )
  8495. {
  8496. register CFileMap *pfilemap = this;
  8497. while (pfilemap->m_fHasSibling)
  8498. pfilemap = pfilemap->m_pfilemapSibling;
  8499. pfilemapSibling->m_fHasSibling = FALSE;
  8500. pfilemapSibling->m_pfilemapParent = pfilemap->m_pfilemapParent;
  8501. pfilemap->m_fHasSibling = TRUE;
  8502. pfilemap->m_pfilemapSibling = pfilemapSibling;
  8503. }
  8504. /* ============================================================================
  8505. CTemplate::CFileMap::FCyclicInclude
  8506. Is a file among this filemap's ancestors? (i.e. does it occur anywhere
  8507. in the filemap's parent chain?)
  8508. Returns
  8509. TRUE or FALSE
  8510. Side effects
  8511. None
  8512. */
  8513. BOOL
  8514. CTemplate::CFileMap::FCyclicInclude
  8515. (
  8516. LPCTSTR szPathTranslated
  8517. )
  8518. {
  8519. CFileMap *pfilemapParent = GetParent();
  8520. if(pfilemapParent == NULL)
  8521. return FALSE;
  8522. // NOTE we ignore case because path-tran was normalized
  8523. if(_tcscmp(szPathTranslated, pfilemapParent->m_szPathTranslated) == 0)
  8524. return TRUE;
  8525. return pfilemapParent->FCyclicInclude(szPathTranslated);
  8526. }
  8527. /* ============================================================================
  8528. CTemplate::CFileMap::GetSecurityDescriptor
  8529. Gets a file's security descriptor
  8530. Returns
  8531. TRUE if we got security descriptor, else FALSE
  8532. Side effects
  8533. allocates memory
  8534. */
  8535. BOOL
  8536. CTemplate::CFileMap::GetSecurityDescriptor
  8537. (
  8538. )
  8539. // ACLs: the following code should in future be shared with IIS (see creatfil.cxx in IIS project)
  8540. {
  8541. BOOL fRet = TRUE; // return value
  8542. BOOL fGotSecurityDescriptor; // did we get a security descriptor?
  8543. DWORD dwSecDescSizeNeeded = 0; // required size of security descriptor
  8544. DWORD dwLastError; // last error code
  8545. const SECURITY_INFORMATION si = OWNER_SECURITY_INFORMATION // security info struct
  8546. | GROUP_SECURITY_INFORMATION
  8547. | DACL_SECURITY_INFORMATION;
  8548. // we dont support security on win95
  8549. if (!Glob(fWinNT))
  8550. return(TRUE);
  8551. // get required buffer size before malloc
  8552. // NOTE this costs us an extra system call, but means the cached template will often use less memory
  8553. // we must do this up front by passing 0 buffer size because when the call succeeds it returns
  8554. // dwSecDescSizeNeeded == 0 (i.e. we can't realloc to shrink after successful call)
  8555. GetKernelObjectSecurity(
  8556. m_hFile, // handle of object to query
  8557. si, // requested information
  8558. NULL, // address of security descriptor
  8559. 0, // size of buffer for security descriptor
  8560. &dwSecDescSizeNeeded // address of required size of buffer
  8561. );
  8562. if((dwLastError = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)
  8563. {
  8564. // pretend everything's fine -- just NULL security descriptor
  8565. if(m_pSecurityDescriptor != NULL)
  8566. CTemplate::SmallFree(m_pSecurityDescriptor);
  8567. m_pSecurityDescriptor = NULL;
  8568. m_dwSecDescSize = 0;
  8569. if (dwLastError == ERROR_NOT_SUPPORTED)
  8570. return TRUE;
  8571. else
  8572. return FALSE;
  8573. }
  8574. // set member buffer size to just enough chunks to accommodate security descriptor size needed
  8575. m_dwSecDescSize = ((dwSecDescSizeNeeded + SECURITY_DESC_GRANULARITY - 1) / SECURITY_DESC_GRANULARITY)
  8576. * SECURITY_DESC_GRANULARITY;
  8577. // allocate memory for security descriptor
  8578. // (Note: security descriptor may already be allocated if this is a remap)
  8579. if (m_pSecurityDescriptor == NULL)
  8580. if(NULL == (m_pSecurityDescriptor = (PSECURITY_DESCRIPTOR) CTemplate::SmallMalloc(m_dwSecDescSize)))
  8581. THROW(E_OUTOFMEMORY);
  8582. // try to get security descriptor until we succeed, or until we fail for some reason other than buffer-too-small
  8583. while(TRUE)
  8584. {
  8585. // attempt to get security descriptor
  8586. fGotSecurityDescriptor = GetKernelObjectSecurity(
  8587. m_hFile, // handle of object to query
  8588. si, // requested information
  8589. m_pSecurityDescriptor, // address of security descriptor
  8590. m_dwSecDescSize, // size of buffer for security descriptor
  8591. &dwSecDescSizeNeeded // address of required size of buffer
  8592. );
  8593. // get last error immediately after call
  8594. dwLastError = fGotSecurityDescriptor
  8595. ? 0 // we got a security descriptor: set last error to 0
  8596. : GetLastError(); // we didn't get a security descriptor: get last error
  8597. if(fGotSecurityDescriptor)
  8598. // we got a security descriptor, so break
  8599. // NOTE we can't realloc m_pSecurityDescriptor to free its unused memory
  8600. // because dwSecDescSizeNeeded is 0 after successful call
  8601. break;
  8602. else if(dwLastError == ERROR_INSUFFICIENT_BUFFER)
  8603. {
  8604. // we didn't get a security descriptor because buffer was too small: increase buffer size, realloc and continue.
  8605. Assert(m_dwSecDescSize < dwSecDescSizeNeeded);
  8606. // set member buffer size to just enough chunks to accommodate security descriptor size needed
  8607. m_dwSecDescSize = ((dwSecDescSizeNeeded + SECURITY_DESC_GRANULARITY - 1) / SECURITY_DESC_GRANULARITY)
  8608. * SECURITY_DESC_GRANULARITY;
  8609. if(NULL == (m_pSecurityDescriptor = (PSECURITY_DESCRIPTOR) CTemplate::SmallReAlloc(m_pSecurityDescriptor, m_dwSecDescSize)))
  8610. THROW(E_OUTOFMEMORY);
  8611. }
  8612. else
  8613. {
  8614. // we didn't get a security descriptor for some random reason: return failure
  8615. fRet = FALSE;
  8616. break;
  8617. }
  8618. }
  8619. return fRet;
  8620. }
  8621. /* ============================================================================
  8622. CTemplate::CFileMap::UnmapFile
  8623. Unmaps a memory-mapped file
  8624. NOTE this leaves the filemap's path-info and path-tran members intact
  8625. Returns
  8626. Nothing
  8627. Side effects
  8628. None
  8629. */
  8630. void
  8631. CTemplate::CFileMap::UnmapFile
  8632. (
  8633. )
  8634. {
  8635. if(m_pbStartOfFile != NULL)
  8636. if(!UnmapViewOfFile(m_pbStartOfFile)) THROW(E_FAIL);
  8637. if(m_hMap!= NULL)
  8638. if(!CloseHandle(m_hMap)) THROW(E_FAIL);
  8639. if(m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
  8640. if(!CloseHandle(m_hFile)) THROW(E_FAIL);
  8641. // Null-ify ptr and handles, since MapFile checks for non-null
  8642. m_pbStartOfFile = NULL;
  8643. m_hMap = NULL;
  8644. m_hFile = NULL;
  8645. }
  8646. /* ============================================================================
  8647. CTemplate::CFileMap::CountChars
  8648. Count the number of characters in the (open) filemap
  8649. Returns:
  8650. # of characters in the file
  8651. */
  8652. DWORD
  8653. CTemplate::CFileMap::CountChars
  8654. (
  8655. WORD wCodePage
  8656. )
  8657. {
  8658. // Bug 84284: Scripts containing object tags only do not have the DBCS table built
  8659. // (Because there is no line mapping table to base it from)
  8660. //
  8661. CTemplate::COffsetInfo *pOffsetInfoLast, oiZero;
  8662. pOffsetInfoLast = (m_rgByte2DBCS.length() == 0)
  8663. ? &oiZero
  8664. : &m_rgByte2DBCS[m_rgByte2DBCS.length() - 1];
  8665. // If GetSize() fails don't count the remaining DBCS characters - otherwise an AV
  8666. DWORD cchFilemap = GetSize();
  8667. if (cchFilemap != 0xFFFFFFFF && cchFilemap != 0)
  8668. {
  8669. // Count DBCS characters
  8670. m_cChars = pOffsetInfoLast->m_cchOffset +
  8671. CharAdvDBCS(wCodePage,
  8672. reinterpret_cast<char *>(m_pbStartOfFile + pOffsetInfoLast->m_cbOffset),
  8673. reinterpret_cast<char *>(m_pbStartOfFile + cchFilemap),
  8674. INFINITE, NULL);
  8675. }
  8676. else
  8677. {
  8678. m_cChars = 0;
  8679. }
  8680. // Done counting DBCS characters
  8681. return m_cChars;
  8682. }
  8683. /* ============================================================================
  8684. CTemplate::CFileMap::GetText
  8685. From a character offset and length, return the document text
  8686. File must be mapped
  8687. */
  8688. HRESULT CTemplate::CFileMap::GetText
  8689. (
  8690. WORD wCodePage,
  8691. ULONG cchSourceOffset,
  8692. WCHAR *pwchText,
  8693. SOURCE_TEXT_ATTR *pTextAttr,
  8694. ULONG *pcChars,
  8695. ULONG cMaxChars
  8696. )
  8697. {
  8698. ULONG cCharsCopied;
  8699. if (pcChars == NULL)
  8700. pcChars = &cCharsCopied;
  8701. // Map the file (temporarily) if not mapped
  8702. BOOL fRemapFile = !FIsMapped();
  8703. TRY
  8704. RemapFile();
  8705. CATCH (dwException)
  8706. return E_FAIL;
  8707. END_TRY
  8708. /* Find the byte offset closest to cchSourceOffset. This will be
  8709. * the place where we start looping with CharNext() to get the full
  8710. * byte offset.
  8711. */
  8712. COffsetInfo *pOffsetInfoLE = NULL, OffsetInfoT;
  8713. /*
  8714. * NOTE: compilation is done in two phases.
  8715. * Errors are detected and reported in phase 1.
  8716. * The DBCS mapping is created in phase 2.
  8717. *
  8718. * If an error occurred during compilation, the DBCS table does not exist.
  8719. * If there is no DBCS mapping table, then pretend like we found entry with
  8720. * nearest offset == 0. (unless this is SBCS in which case nearest
  8721. * offset == cchSourceOffset)
  8722. */
  8723. if (m_rgByte2DBCS.length() == 0)
  8724. {
  8725. CPINFO CpInfo;
  8726. GetCPInfo(wCodePage, &CpInfo);
  8727. OffsetInfoT.m_cbOffset = OffsetInfoT.m_cchOffset = (CpInfo.MaxCharSize == 1)? cchSourceOffset : 0;
  8728. pOffsetInfoLE = &OffsetInfoT;
  8729. }
  8730. else
  8731. GetBracketingPair(
  8732. cchSourceOffset, // value to search for
  8733. m_rgByte2DBCS.begin(), // beginning of array to search
  8734. m_rgByte2DBCS.end(), // end of array
  8735. CCharOffsetOrder(), // order by character offsets
  8736. &pOffsetInfoLE, // desired offset
  8737. static_cast<COffsetInfo **>(NULL) // don't care
  8738. );
  8739. /* OK - pOffsetLE->cbOffset contains the closest offset not exceeding
  8740. * cchSourceOffset. Iterate over the remainder of the characters
  8741. * to convert the cch to a cb. It had better exist!
  8742. */
  8743. Assert (pOffsetInfoLE != NULL);
  8744. // Advance over remaining characters
  8745. char *pchStart;
  8746. CharAdvDBCS(wCodePage,
  8747. reinterpret_cast<char *>(m_pbStartOfFile + pOffsetInfoLE->m_cbOffset),
  8748. reinterpret_cast<char *>(m_pbStartOfFile + GetSize()),
  8749. cchSourceOffset - pOffsetInfoLE->m_cchOffset,
  8750. &pchStart
  8751. );
  8752. // Compute # of Characters to copy
  8753. Assert (m_cChars >= cchSourceOffset);
  8754. *pcChars = min(cMaxChars, m_cChars - cchSourceOffset);
  8755. // Compute # of Bytes to copy
  8756. char *pchEnd;
  8757. CharAdvDBCS(wCodePage,
  8758. pchStart,
  8759. reinterpret_cast<char *>(m_pbStartOfFile + GetSize()),
  8760. *pcChars,
  8761. &pchEnd
  8762. );
  8763. if (pwchText)
  8764. MultiByteToWideChar(
  8765. (WORD)wCodePage,
  8766. 0,
  8767. pchStart,
  8768. DIFF(pchEnd - pchStart),
  8769. pwchText,
  8770. cMaxChars
  8771. );
  8772. // We don't support syntax coloring, so set all the character attributes to
  8773. // default color (black)
  8774. if (pTextAttr)
  8775. memset(pTextAttr, 0, *pcChars);
  8776. // Unmap the file (but only if we previously remapped it)
  8777. if (fRemapFile)
  8778. TRY
  8779. UnmapFile();
  8780. CATCH (dwException)
  8781. return E_FAIL;
  8782. END_TRY
  8783. return S_OK;
  8784. }
  8785. /* ****************************************************************************
  8786. CTemplate::CTokenList member functions
  8787. */
  8788. /* ============================================================================
  8789. CTemplate::CTokenList::Init
  8790. Populates token list with tokens
  8791. Returns
  8792. Nothing
  8793. Side effects
  8794. None
  8795. */
  8796. void
  8797. CTemplate::CTokenList::Init
  8798. (
  8799. )
  8800. {
  8801. // Init tokens buffer for local storage
  8802. m_bufTokens.Init(tkncAll, CB_TOKENS_DEFAULT);
  8803. // append tokens to buffer
  8804. // NOTE *** TOKENS MUST BE IN SAME ORDER AS ENUM TYPE VALUES ***
  8805. // NOTE 'superset' token must precede 'subset' token (e.g. <!--#INCLUDE before <!--)
  8806. AppendToken(tknOpenPrimaryScript, "<%");
  8807. AppendToken(tknOpenTaggedScript, "<SCRIPT");
  8808. AppendToken(tknOpenObject, "<OBJECT");
  8809. AppendToken(tknOpenHTMLComment, "<!--");
  8810. AppendToken(tknNewLine, SZ_NEWLINE);
  8811. AppendToken(tknClosePrimaryScript, "%>");
  8812. AppendToken(tknCloseTaggedScript, "</SCRIPT>");
  8813. AppendToken(tknCloseObject, "</OBJECT>");
  8814. AppendToken(tknCloseHTMLComment, "-->");
  8815. AppendToken(tknEscapedClosePrimaryScript, "%\\>");
  8816. AppendToken(tknCloseTag, ">");
  8817. AppendToken(tknCommandINCLUDE, "#INCLUDE");
  8818. AppendToken(tknTagRunat, "RUNAT");
  8819. AppendToken(tknTagLanguage, "LANGUAGE");
  8820. AppendToken(tknTagCodePage, "CODEPAGE");
  8821. AppendToken(tknTagCodePage, "LCID");
  8822. AppendToken(tknTagTransacted, "TRANSACTION");
  8823. AppendToken(tknTagSession, "ENABLESESSIONSTATE");
  8824. AppendToken(tknTagID, "ID");
  8825. AppendToken(tknTagClassID, "CLASSID");
  8826. AppendToken(tknTagProgID, "PROGID");
  8827. AppendToken(tknTagScope, "SCOPE");
  8828. AppendToken(tknTagVirtual, "VIRTUAL");
  8829. AppendToken(tknTagFile, "FILE");
  8830. AppendToken(tknTagMETADATA, "METADATA");
  8831. // AppendToken(tknTagSetPriScriptLang, "@");
  8832. AppendToken(tknTagName, "NAME");
  8833. AppendToken(tknValueTypeLib, "TYPELIB");
  8834. AppendToken(tknTagType, "TYPE");
  8835. AppendToken(tknTagUUID, "UUID");
  8836. AppendToken(tknTagVersion, "VERSION");
  8837. AppendToken(tknTagStartspan, "STARTSPAN");
  8838. AppendToken(tknTagEndspan, "ENDSPAN");
  8839. AppendToken(tknValueCookie, "COOKIE");
  8840. AppendToken(tknTagSrc, "SRC");
  8841. AppendToken(tknValueServer, "Server");
  8842. AppendToken(tknValueApplication, "Application");
  8843. AppendToken(tknValueSession, "Session");
  8844. AppendToken(tknValuePage, "Page");
  8845. AppendToken(tknVBSCommentSQuote, "'");
  8846. AppendToken(tknVBSCommentRem, "REM "); // NOTE ends with space character
  8847. AppendToken(tknTagFPBot, "webbot");
  8848. AppendToken(tknEOF, "");
  8849. AppendToken(tkncAll, "");
  8850. }
  8851. /* ============================================================================
  8852. CTemplate::CTokenList::AppendToken
  8853. Appends a string to tokens buffer
  8854. NOTE we keep the unused tkn parameter because it enforces consistency and
  8855. readability in CTemplate::CTokenList::Init(), e.g.
  8856. AppendToken(tknOpenPrimaryScript, "<%");
  8857. rather than
  8858. AppendToken("<%");
  8859. Returns:
  8860. Nothing
  8861. Side effects:
  8862. None
  8863. */
  8864. void
  8865. CTemplate::CTokenList::AppendToken
  8866. (
  8867. _TOKEN tkn, // token value
  8868. char* sz // token string
  8869. )
  8870. {
  8871. // construct byte range from token string
  8872. CByteRange br;
  8873. br.m_pb = (BYTE*) sz;
  8874. br.m_cb = strlen(sz);
  8875. // append to tokens buffer as local string
  8876. m_bufTokens.Append(br, TRUE, 0, NULL, TRUE);
  8877. }
  8878. /* ============================================================================
  8879. CTemplate::CTokenList::NextOpenToken
  8880. Returns value of next open token in search range
  8881. Returns
  8882. token value of next open token in search range; ptr to ptr to open token (out-parameter)
  8883. Side effects
  8884. None
  8885. */
  8886. _TOKEN
  8887. CTemplate::CTokenList::NextOpenToken
  8888. (
  8889. CByteRange& brSearch, // search byte range
  8890. TOKEN* rgtknOpeners, // array of permitted open tokens
  8891. UINT ctknOpeners, // count of permitted open tokens
  8892. BYTE** ppbToken, // ptr to ptr to open token (out-parameter)
  8893. LONG lCodePage
  8894. )
  8895. {
  8896. BYTE* pbTemp = NULL; // temp pointer
  8897. _TOKEN tkn = tknEOF; // return value
  8898. USHORT i; // loop index
  8899. // Init caller's token ptr to null
  8900. *ppbToken = NULL;
  8901. // If input is empty, return
  8902. if (brSearch.IsNull())
  8903. return tkn;
  8904. // Prepare array of LPSTR pointers to tokens.
  8905. // Do it here once, because to get LPSTR is not free.
  8906. LPSTR rgszTokens[TOKEN_OPENERS_MAX];
  8907. UINT rgcchTokens[TOKEN_OPENERS_MAX];
  8908. Assert(ctknOpeners <= TOKEN_OPENERS_MAX);
  8909. for (i = 0; i < ctknOpeners; i++)
  8910. {
  8911. LPSTR pszStr = m_bufTokens.PszLocal((UINT)(rgtknOpeners[i]));
  8912. rgszTokens[i] = pszStr;
  8913. rgcchTokens[i] = (pszStr != NULL) ? strlen(pszStr) : 0;
  8914. }
  8915. // Call a method to find one of the strings in the range
  8916. UINT idToken;
  8917. pbTemp = brSearch.PbOneOfAspOpenerStringTokens(
  8918. rgszTokens, rgcchTokens, ctknOpeners, &idToken);
  8919. if (pbTemp != NULL)
  8920. {
  8921. *ppbToken = pbTemp;
  8922. tkn = rgtknOpeners[idToken];
  8923. }
  8924. // If we found no open token, position token pointer at end of search range
  8925. if (tkn == tknEOF)
  8926. *ppbToken = brSearch.m_pb + brSearch.m_cb;
  8927. return tkn;
  8928. }
  8929. /* ============================================================================
  8930. CTemplate::CTokenList::MovePastToken
  8931. Moves a byte range past a token contained within it
  8932. */
  8933. void
  8934. CTemplate::CTokenList::MovePastToken
  8935. (
  8936. _TOKEN tkn,
  8937. BYTE* pbToken,
  8938. CByteRange& brSearch
  8939. )
  8940. {
  8941. Assert(pbToken >= brSearch.m_pb);
  8942. Assert(brSearch.m_cb >= (DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn)));
  8943. brSearch.Advance(DIFF(pbToken - brSearch.m_pb) + CCH_TOKEN_X(tkn));
  8944. }
  8945. /* ============================================================================
  8946. CTemplate::CTokenList::GetToken
  8947. Gets the next occurrence of a token within a byte range.
  8948. Returns:
  8949. ptr to token
  8950. Side effects
  8951. none
  8952. */
  8953. BYTE*
  8954. CTemplate::CTokenList::GetToken
  8955. (
  8956. TOKEN tkn,
  8957. CByteRange& brSearch,
  8958. LONG lCodePage
  8959. )
  8960. {
  8961. return brSearch.PbString(m_bufTokens.PszLocal((UINT)tkn), lCodePage);
  8962. }
  8963. /* ============================================================================
  8964. The Big Three for CTemplateConnPt
  8965. NOTES:
  8966. Since this interface is embedded in CTemplate,
  8967. AddRef() and Release() delegate to the container object (because that
  8968. is the CTemplate pointer)
  8969. */
  8970. HRESULT
  8971. CTemplateConnPt::QueryInterface(const GUID &uidInterface, void **ppvObj)
  8972. {
  8973. if (uidInterface == IID_IUnknown || uidInterface == IID_IConnectionPoint)
  8974. {
  8975. *ppvObj = this;
  8976. AddRef();
  8977. return S_OK;
  8978. }
  8979. else
  8980. {
  8981. *ppvObj = NULL;
  8982. return E_NOINTERFACE;
  8983. }
  8984. }
  8985. ULONG
  8986. CTemplateConnPt::AddRef()
  8987. {
  8988. return m_pUnkContainer->AddRef();
  8989. }
  8990. ULONG
  8991. CTemplateConnPt::Release()
  8992. {
  8993. return m_pUnkContainer->Release();
  8994. }
  8995. /* ============================================================================
  8996. Constructor for CDocNode
  8997. */
  8998. CTemplate::CDocNodeElem::CDocNodeElem(CAppln *pAppln, IDebugApplicationNode *pDocRoot)
  8999. {
  9000. Assert (pAppln != NULL);
  9001. Assert (pDocRoot != NULL);
  9002. (m_pAppln = pAppln)->AddRef();
  9003. (m_pDocRoot = pDocRoot)->AddRef();
  9004. }
  9005. /* ============================================================================
  9006. Destructor for CDocNode
  9007. */
  9008. CTemplate::CDocNodeElem::~CDocNodeElem()
  9009. {
  9010. m_pAppln->Release();
  9011. DestroyDocumentTree(m_pDocRoot);
  9012. }
  9013. /* ============================================================================
  9014. CTemplate::fIsLangVBScriptOrJScript(USHORT idEngine)
  9015. This function returns T/F to determine if the requested script engine
  9016. is VBScript or JScript. This function is used as an indicator to determin
  9017. if spaces need to be preserved for non MS Scripting languages
  9018. There is an assumption here that the GUIDs for VBScript and JScript will not change
  9019. Inputs
  9020. Index to a script engine
  9021. Returns
  9022. BOOL
  9023. */
  9024. BOOLB CTemplate::FIsLangVBScriptOrJScript(USHORT idEngine)
  9025. {
  9026. // {b54f3741-5b07-11cf-a4b0-00aa004a55e8} VBScript
  9027. static const GUID uid_VBScript = {0xb54f3741, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}};
  9028. // {f414c260-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript
  9029. static const GUID uid_JScript = {0xf414c260, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}};
  9030. // {b54f3743-5b07-11cf-a4b0-00aa004a55e8} VBScript.Encode
  9031. static const GUID uid_VBScriptEncode = {0xb54f3743, 0x5b07, 0x11cf, {0xa4, 0xb0, 0x00, 0xaa, 0x00, 0x4a, 0x55, 0xe8}};
  9032. // {f414c262-6ac0-11cf-b6d1-00aa00bbbb58} JavaScript.Encode
  9033. static const GUID uid_JScriptEncode = {0xf414c262, 0x6ac0, 0x11cf, {0xb6, 0xd1, 0x00, 0xaa, 0x00, 0xbb, 0xbb, 0x58}};
  9034. GUID &uidLang = m_pWorkStore->m_ScriptStore.m_rgProgLangId[idEngine];
  9035. return
  9036. uidLang == uid_VBScript || uidLang == uid_VBScriptEncode ||
  9037. uidLang == uid_JScript || uidLang == uid_JScriptEncode;
  9038. }
  9039. SIZE_T
  9040. _RoundUp(
  9041. SIZE_T dwBytes)
  9042. {
  9043. #if 1
  9044. // 16KB <= dwBytes? Round up to next multiple of 4KB
  9045. if (16*1024 <= dwBytes)
  9046. dwBytes = ((dwBytes + (1<<12) - 1) >> 12) << 12;
  9047. // 4KB <= dwBytes < 16KB? Round up to next multiple of 1KB
  9048. else if (4*1024 <= dwBytes)
  9049. dwBytes = ((dwBytes + (1<<10) - 1) >> 10) << 10;
  9050. // 1KB <= dwBytes < 4KB? Round up to next multiple of 256 bytes
  9051. else if (1024 <= dwBytes)
  9052. dwBytes = ((dwBytes + (1<<8) - 1) >> 8) << 8;
  9053. // dwBytes < 1KB? Round up to next multiple of 32 bytes
  9054. else
  9055. dwBytes = ((dwBytes + (1<<5) - 1) >> 5) << 5;
  9056. #endif
  9057. return dwBytes;
  9058. }
  9059. void*
  9060. CTemplate::SmallMalloc(SIZE_T dwBytes)
  9061. {
  9062. DBG_ASSERT(sm_hSmallHeap != NULL);
  9063. dwBytes = _RoundUp(dwBytes);
  9064. return ::HeapAlloc(sm_hSmallHeap, 0, dwBytes);
  9065. }
  9066. void*
  9067. CTemplate::SmallReAlloc(void* pvMem, SIZE_T dwBytes)
  9068. {
  9069. DBG_ASSERT(sm_hSmallHeap != NULL);
  9070. dwBytes = _RoundUp(dwBytes);
  9071. return ::HeapReAlloc(sm_hSmallHeap, 0, pvMem, dwBytes);
  9072. }
  9073. void
  9074. CTemplate::SmallFree(void* pvMem)
  9075. {
  9076. DBG_ASSERT(sm_hSmallHeap != NULL);
  9077. ::HeapFree(sm_hSmallHeap, 0, pvMem);
  9078. }
  9079. void*
  9080. CTemplate::LargeMalloc(SIZE_T dwBytes)
  9081. {
  9082. DBG_ASSERT(sm_hLargeHeap != NULL);
  9083. dwBytes = _RoundUp(dwBytes);
  9084. return ::HeapAlloc(sm_hLargeHeap, 0, dwBytes);
  9085. }
  9086. void*
  9087. CTemplate::LargeReAlloc(void* pvMem, SIZE_T dwBytes)
  9088. {
  9089. DBG_ASSERT(sm_hLargeHeap != NULL);
  9090. dwBytes = _RoundUp(dwBytes);
  9091. return ::HeapReAlloc(sm_hLargeHeap, 0, pvMem, dwBytes);
  9092. }
  9093. void
  9094. CTemplate::LargeFree(void* pvMem)
  9095. {
  9096. DBG_ASSERT(sm_hLargeHeap != NULL);
  9097. ::HeapFree(sm_hLargeHeap, 0, pvMem);
  9098. }