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

2060 lines
61 KiB

  1. //
  2. // MODULE: APGTSCTX.CPP
  3. //
  4. // PURPOSE: Implementation file for Thread Context
  5. // Fully implements class APGTSContext, which provides the full context for a "pool" thread
  6. // to perform a task
  7. // Also includes helper class CCommands.
  8. //
  9. // PROJECT: Generic Troubleshooter DLL for Microsoft AnswerPoint
  10. //
  11. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  12. //
  13. // AUTHOR: Roman Mach, Joe Mabel
  14. //
  15. // ORIGINAL DATE: 8-2-96
  16. //
  17. // NOTES:
  18. // 1. Several things in this file are marked as $ENGLISH. That means we've hard-coded
  19. // English-language returns. This may yet be revisited, but as of 10/29/98 discussion
  20. // between Ron Prior of Microsoft and Joe Mabel of Saltmine, we couldn't come up with a
  21. // better solution to this. Notes are in the specification for the fall 1998 work on
  22. // the Online Troubleshooter.
  23. // 2. some of the methods of APGTSContext are implemented in file STATUSPAGES.CPP
  24. //
  25. // Version Date By Comments
  26. //--------------------------------------------------------------------
  27. // V0.1 - RM Original
  28. // V3.0 7-22-98 JM Major revision, deprecate IDH, totally new approach to logging.
  29. //
  30. #pragma warning(disable:4786)
  31. #include "stdafx.h"
  32. #include <time.h>
  33. #include "event.h"
  34. #include "apgts.h"
  35. #include "apgtscls.h"
  36. #include "apgtscfg.h"
  37. #include "apgtsmfc.h"
  38. #include "CounterMgr.h"
  39. #include "CharConv.h"
  40. #include "SafeTime.h"
  41. #include "RegistryPasswords.h"
  42. #ifdef LOCAL_TROUBLESHOOTER
  43. #include "HTMLFragLocal.h"
  44. #endif
  45. #include "Sniff.h"
  46. // HTTP header variable name where cookie information is stored.
  47. #define kHTTP_COOKIE "HTTP_COOKIE"
  48. //
  49. // CCommands ------------------------------------------------------
  50. // The next several CCommands functions are analogous to MFC CArray
  51. //
  52. int APGTSContext::CCommands::GetSize( ) const
  53. {
  54. return m_arrPair.size();
  55. }
  56. void APGTSContext::CCommands::RemoveAll( )
  57. {
  58. m_arrPair.clear();
  59. }
  60. bool APGTSContext::CCommands::GetAt( int nIndex, NID &nid, int &value ) const
  61. {
  62. if (nIndex<0 || nIndex>=m_arrPair.size())
  63. return false;
  64. nid = m_arrPair[nIndex].nid;
  65. value = m_arrPair[nIndex].value;
  66. return true;
  67. }
  68. int APGTSContext::CCommands::Add( NID nid, int value )
  69. {
  70. NID_VALUE_PAIR pair;
  71. pair.nid = nid;
  72. pair.value = value;
  73. try
  74. {
  75. m_arrPair.push_back(pair);
  76. }
  77. catch (exception& x)
  78. {
  79. CString str;
  80. // Note STL exception in event log.
  81. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  82. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  83. SrcLoc.GetSrcFileLineStr(),
  84. CCharConversion::ConvertACharToString(x.what(), str),
  85. _T(""),
  86. EV_GTS_STL_EXCEPTION );
  87. return( -1 );
  88. }
  89. return m_arrPair.size();
  90. }
  91. // a funky manipulation to deal with the following issue from old (pre V3.0) troubleshooters:
  92. // Pre V3.0 Logging sequence and the service node's behavior were both based on some assumptions
  93. // about the sequence of name/value pairs in the command list. Basically, the
  94. // assumption was that the "table" would be on top of the form and the "questions"
  95. // below it. This would result in ProblemAsk in first position (after any "template=
  96. // <template-name> and type=<troubleshooter-name>, but that's weeded out before we ever
  97. // hit the command list). If the HTI file has put "the table" at the bottom, that assumption
  98. // is invalidated, so we have to manipulate the array.
  99. // Because we could get old GET-method queries, we still have to deal with this as a backward
  100. // compatibility issue.
  101. void APGTSContext::CCommands::RotateProblemPageToFront()
  102. {
  103. int dwPairs = m_arrPair.size();
  104. // Rotate till ProblemAsk is in position 0. (no known scenario where it starts out
  105. // anywhere past position 1)
  106. try
  107. {
  108. for (int i= 0; i<dwPairs; i++)
  109. {
  110. NID_VALUE_PAIR pair = m_arrPair.front(); // note: first element, not i-th element
  111. if (pair.nid == nidProblemPage)
  112. break;
  113. m_arrPair.erase(m_arrPair.begin());
  114. m_arrPair.push_back(pair);
  115. }
  116. }
  117. catch (exception& x)
  118. {
  119. CString str;
  120. // Note STL exception in event log.
  121. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  122. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  123. SrcLoc.GetSrcFileLineStr(),
  124. CCharConversion::ConvertACharToString(x.what(), str),
  125. _T(""),
  126. EV_GTS_STL_EXCEPTION );
  127. }
  128. }
  129. //
  130. // CCommandsAddManager --------------------------------------------------
  131. //
  132. void APGTSContext::CCommandsAddManager::Add(NID nid, int value, bool sniffed)
  133. {
  134. if (sniffed)
  135. {
  136. int nCommands = m_Commands.GetSize();
  137. for (int i = nCommands - 1; i >= 0; i--) // higher possibility, that matching
  138. { // node will be in the end of array
  139. NID nid_curr;
  140. int value_curr;
  141. m_Commands.GetAt(i, nid_curr, value_curr);
  142. if (nid_curr == nid)
  143. {
  144. if (value_curr != value)
  145. {
  146. // If we're here, it means, that user has changed value
  147. // of sniffed node in history table, therefore it is
  148. // no longer treated as sniffed.
  149. return;
  150. }
  151. else
  152. {
  153. m_Sniffed.Add(nid, value);
  154. return;
  155. }
  156. }
  157. }
  158. // sniffed node does not have matches in m_Commands
  159. ASSERT(false);
  160. }
  161. else
  162. {
  163. m_Commands.Add(nid, value);
  164. }
  165. }
  166. //
  167. // CAdditionalInfo ------------------------------------------------------
  168. // The next several CAdditionalInfo functions are analogous to MFC CArray
  169. //
  170. int APGTSContext::CAdditionalInfo::GetSize( ) const
  171. {
  172. return m_arrPair.size();
  173. }
  174. void APGTSContext::CAdditionalInfo::RemoveAll( )
  175. {
  176. m_arrPair.clear();
  177. }
  178. bool APGTSContext::CAdditionalInfo::GetAt( int nIndex, CString& name, CString& value ) const
  179. {
  180. if (nIndex<0 || nIndex>=m_arrPair.size())
  181. return false;
  182. name = m_arrPair[nIndex].name;
  183. value = m_arrPair[nIndex].value;
  184. return true;
  185. }
  186. int APGTSContext::CAdditionalInfo::Add( const CString& name, const CString& value )
  187. {
  188. NAME_VALUE_PAIR pair;
  189. pair.name = name;
  190. pair.value = value;
  191. try
  192. {
  193. m_arrPair.push_back(pair);
  194. }
  195. catch (exception& x)
  196. {
  197. CString str;
  198. // Note STL exception in event log.
  199. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  200. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  201. SrcLoc.GetSrcFileLineStr(),
  202. CCharConversion::ConvertACharToString(x.what(), str),
  203. _T(""),
  204. EV_GTS_STL_EXCEPTION );
  205. return( -1 );
  206. }
  207. return m_arrPair.size();
  208. }
  209. //-----------------
  210. // INPUT *pECB - Describes the user request that has come in. This is our abstraction of
  211. // Win32 EXTENSION_CONTROL_BLOCK, which is ISAPI's packaging of CGI data.
  212. // INPUT *pConf - access to registry info & contents of all loaded troubleshooters
  213. // INPUT *pLog - access to logging
  214. // INPUT *pStat - statistical info, including pStat->dwRollover which is a unique number
  215. // for this request, unique within the time the DLL has been loaded
  216. APGTSContext::APGTSContext( CAbstractECB *pECB,
  217. CDBLoadConfiguration *pConf,
  218. CHTMLLog *pLog,
  219. GTS_STATISTIC *pStat,
  220. CSniffConnector* pSniffConnector
  221. ) :
  222. m_pECB(pECB),
  223. m_dwErr(0),
  224. m_strHeader(_T("Content-Type: text/html\r\n")),
  225. m_pConf(pConf),
  226. m_strVRoot(m_pConf->GetVrootPath()),
  227. m_pszQuery(NULL),
  228. m_pLog(pLog),
  229. m_bPostType(true),
  230. m_dwBytes(0),
  231. m_pStat(pStat),
  232. m_bPreload(false),
  233. m_bNewCookie(false),
  234. m_pcountUnknownTopics (&(g_ApgtsCounters.m_UnknownTopics)),
  235. m_pcountAllAccessesFinish (&(g_ApgtsCounters.m_AllAccessesFinish)),
  236. m_pcountStatusAccesses (&(g_ApgtsCounters.m_StatusAccesses)),
  237. m_pcountOperatorActions (&(g_ApgtsCounters.m_OperatorActions)),
  238. m_TopicName(_T("")),
  239. m_infer(pSniffConnector),
  240. m_CommandsAddManager(m_Commands, m_Sniffed)
  241. // You can compile with the SHOWPROGRESS option to get a report on the progress of this page.
  242. #ifdef SHOWPROGRESS
  243. , timeCreateContext(0),
  244. timeStartInfer(0),
  245. timeEndInfer(0),
  246. timeEndRender(0)
  247. #endif // SHOWPROGRESS
  248. {
  249. #ifdef SHOWPROGRESS
  250. time(&timeCreateContext);
  251. #endif // SHOWPROGRESS
  252. // obtain local host IP address
  253. APGTS_nmspace::GetServerVariable(m_pECB, "SERVER_NAME", m_strLocalIPAddress);
  254. // HTTP response code. This or 302 Object Moved.
  255. _tcscpy(m_resptype, _T("200 OK")); // initially assume we will respond without trouble
  256. // supports GET, POST
  257. if (!strcmp(m_pECB->GetMethod(), "GET")) {
  258. m_bPostType = false;
  259. m_dwBytes = strlen(m_pECB->GetQueryString());
  260. }
  261. else
  262. m_dwBytes = m_pECB->GetBytesAvailable();
  263. _tcscpy(m_ipstr,_T(""));
  264. DWORD bufsize = MAXBUF - 1;
  265. if (! m_pECB->GetServerVariable("REMOTE_ADDR", m_ipstr, &bufsize))
  266. _stprintf(m_ipstr,_T("IP?"));
  267. try
  268. {
  269. m_pszQuery = new TCHAR[m_dwBytes + 1];
  270. //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
  271. if(!m_pszQuery)
  272. throw bad_alloc();
  273. }
  274. catch (bad_alloc&)
  275. {
  276. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  277. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  278. SrcLoc.GetSrcFileLineStr(),
  279. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  280. m_dwErr = EV_GTS_ERROR_NO_CHAR;
  281. return;
  282. }
  283. if (m_bPostType)
  284. memcpy(m_pszQuery, m_pECB->GetData(), m_dwBytes);
  285. else
  286. memcpy(m_pszQuery, m_pECB->GetQueryString(), m_dwBytes);
  287. m_pszQuery[m_dwBytes] = _T('\0');
  288. }
  289. //
  290. // Even though this is a destructor, it does a lot of work:
  291. // - sends the HTTP (HTML or cookie)to the user over the net
  292. // - writes to the log.
  293. APGTSContext::~APGTSContext()
  294. {
  295. DWORD dwLen;
  296. TCHAR *ptr;
  297. // RESPONSE_HEADER
  298. m_strHeader += _T("\r\n");
  299. dwLen = m_strHeader.GetLength();
  300. ptr = m_strHeader.GetBuffer(0);
  301. m_pECB->ServerSupportFunction( HSE_REQ_SEND_RESPONSE_HEADER,
  302. m_resptype,
  303. &dwLen,
  304. (LPDWORD) ptr );
  305. m_strHeader.ReleaseBuffer();
  306. // HTML content follows
  307. {
  308. //////////////////////////////////////////////////////////////////////////////////////
  309. //
  310. // mando : 01.28.2002
  311. //
  312. // Fixing a PREfast bug: Renaming dwLen to dwLen1 because one more dwLen is being
  313. // declared in the outer scope.
  314. //
  315. //////////////////////////////////////////////////////////////////////////////////////
  316. // DWORD dwLen= 0;
  317. DWORD dwLen1= 0;
  318. if (! m_dwErr)
  319. dwLen1 = m_strText.GetLength();
  320. if (!dwLen1)
  321. {
  322. // $ENGLISH (see note at head of file)
  323. SetError(_T("<P>Errors Occurred in This Context"));
  324. dwLen1 = m_strText.GetLength();
  325. }
  326. #ifdef SHOWPROGRESS
  327. CString strProgress;
  328. CSafeTime safetimeCreateContext(timeCreateContext);
  329. CSafeTime safetimeStartInfer(timeStartInfer);
  330. CSafeTime safetimeEndInfer(timeEndInfer);
  331. CSafeTime safetimeEndRender(timeEndRender);
  332. strProgress = _T("\nRequested ");
  333. strProgress += safetimeCreateContext.StrLocalTime();
  334. strProgress += _T("\n<BR>Start Infer ");
  335. strProgress += safetimeStartInfer.StrLocalTime();
  336. strProgress += _T("\n<BR>End Infer ");
  337. strProgress += safetimeEndInfer.StrLocalTime();
  338. strProgress += _T("\n<BR>End Render ");
  339. strProgress += safetimeEndRender.StrLocalTime();
  340. strProgress += _T("\n<BR>");
  341. int i = m_strText.Find(_T("<BODY"));
  342. i = m_strText.Find(_T('>'), i); // end of BODY tag
  343. if (i>=0)
  344. {
  345. m_strText= m_strText.Left(i+1)
  346. + strProgress
  347. + m_strText.Mid(i+1);
  348. }
  349. dwLen1 += strProgress.GetLength();
  350. #endif // SHOWPROGRESS
  351. // (LPCTSTR) cast gives us the underlying text bytes.
  352. // >>> $UNICODE Actually, this would screw up under Unicode compile, because for HTML,
  353. // this must be SBCS. Should really be a conversion to LPCSTR, which is non-trivial
  354. // in a Unicode compile. JM 1/7/99
  355. m_pECB->WriteClient((LPCTSTR)m_strText, &dwLen1);
  356. //////////////////////////////////////////////////////////////////////////////////////
  357. }
  358. // connection complete
  359. m_logstr.AddCurrentNode(m_infer.NIDSelected());
  360. if (m_dwErr)
  361. m_logstr.AddError(m_dwErr, 0);
  362. // finish up log
  363. {
  364. if (m_pLog)
  365. {
  366. CString strLog (m_logstr.GetStr());
  367. m_dwErr = m_pLog->NewLog(strLog);
  368. if (m_dwErr)
  369. {
  370. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  371. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  372. SrcLoc.GetSrcFileLineStr(),
  373. _T(""),
  374. _T(""),
  375. m_dwErr );
  376. }
  377. }
  378. }
  379. if (m_pszQuery)
  380. delete [] m_pszQuery;
  381. }
  382. // Fully process a normal user request
  383. // Should be called within the user context created by ImpersonateLoggedOnUser
  384. void APGTSContext::ProcessQuery()
  385. {
  386. CheckAndLogCookie();
  387. if (m_dwErr)
  388. {
  389. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  390. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  391. SrcLoc.GetSrcFileLineStr(),
  392. _T("Remote IP Address:"),
  393. m_ipstr,
  394. m_dwErr );
  395. }
  396. else
  397. {
  398. DoContent();
  399. // You can compile with the SHOWPROGRESS option to get a report on the progress of this page.
  400. #ifdef SHOWPROGRESS
  401. time (&timeEndRender);
  402. #endif // SHOWPROGRESS
  403. }
  404. // Log the completion of all queries, good and bad.
  405. m_pcountAllAccessesFinish->Increment();
  406. }
  407. //
  408. //
  409. void APGTSContext::DoContent()
  410. {
  411. TCHAR pszCmd[MAXBUF], pszValue[MAXBUF];
  412. if (m_bPostType)
  413. {
  414. // validate incoming POST request
  415. if (strcmp(m_pECB->GetContentType(), CONT_TYPE_STR) != 0)
  416. {
  417. // Output the content type to the event log.
  418. CString strContentType;
  419. if (strlen( m_pECB->GetContentType() ))
  420. strContentType= m_pECB->GetContentType();
  421. else
  422. strContentType= _T("not specified");
  423. m_strText += _T("<P>Bad Data Received\n");
  424. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  425. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  426. SrcLoc.GetSrcFileLineStr(),
  427. m_ipstr,
  428. strContentType,
  429. EV_GTS_USER_BAD_DATA );
  430. return;
  431. }
  432. }
  433. if (RUNNING_ONLINE_TS())
  434. {
  435. // Cookies are only used in the Online TS.
  436. if (_tcsstr( m_pszQuery, C_COOKIETAG ) != NULL)
  437. {
  438. // V3.2 - Parse out cookies passed in either as hidden fields or as part of the URL.
  439. if (m_Qry.GetFirst(m_pszQuery, pszCmd, pszValue))
  440. {
  441. CString strCookieFreeQuery;
  442. bool bFoundAtLeastOneCookie= false;
  443. do
  444. {
  445. // This is supposed to be a case sensitive setting as per the specification.
  446. if (!_tcsncmp( pszCmd, C_COOKIETAG, _tcslen( C_COOKIETAG )))
  447. {
  448. // Found a cookie, add it to the map.
  449. CString strCookieAttr= pszCmd + _tcslen( C_COOKIETAG );
  450. APGTS_nmspace::CookieDecodeURL( strCookieAttr );
  451. CString strCookieValue= pszValue;
  452. APGTS_nmspace::CookieDecodeURL( strCookieValue );
  453. // Check the cookie name for compliance.
  454. bool bCookieIsCompliant= true;
  455. for (int nPos= 0; nPos < strCookieAttr.GetLength(); nPos++)
  456. {
  457. TCHAR tcTmp= strCookieAttr.GetAt( nPos );
  458. if ((!_istalnum( tcTmp )) && (tcTmp != _T('_')))
  459. {
  460. bCookieIsCompliant= false;
  461. break;
  462. }
  463. }
  464. if (bCookieIsCompliant)
  465. {
  466. // Check the cookie setting for compliance.
  467. if (strCookieValue.Find( _T("&lt") ) != -1)
  468. {
  469. bCookieIsCompliant= false;
  470. }
  471. else if (strCookieValue.Find( _T("&gt") ) != -1)
  472. {
  473. bCookieIsCompliant= false;
  474. }
  475. #if ( 0 )
  476. // >>> I don't think that this check is necessary. RAB-20000408.
  477. else
  478. {
  479. for (int nPos= 0; nPos < strCookieValue.GetLength(); nPos++)
  480. {
  481. TCHAR tcTmp= strCookieValue.GetAt( nPos );
  482. if ((tcTmp == _T('<')) || (tcTmp == _T('>')))
  483. {
  484. bCookieIsCompliant= false;
  485. break;
  486. }
  487. }
  488. }
  489. #endif
  490. }
  491. if (bCookieIsCompliant)
  492. {
  493. try
  494. {
  495. m_mapCookiesPairs[ strCookieAttr ]= strCookieValue;
  496. }
  497. catch (exception& x)
  498. {
  499. CString str;
  500. // Note STL exception in event log.
  501. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  502. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  503. SrcLoc.GetSrcFileLineStr(),
  504. CCharConversion::ConvertACharToString(x.what(), str),
  505. _T(""),
  506. EV_GTS_STL_EXCEPTION );
  507. }
  508. }
  509. bFoundAtLeastOneCookie= true;
  510. }
  511. else
  512. {
  513. // Not a cookie, add it to the cookie free query.
  514. if (strCookieFreeQuery.GetLength())
  515. strCookieFreeQuery+= C_AMPERSAND;
  516. strCookieFreeQuery+= pszCmd;
  517. strCookieFreeQuery+= C_EQUALSIGN;
  518. strCookieFreeQuery+= pszValue;
  519. }
  520. }
  521. while (m_Qry.GetNext( pszCmd, pszValue )) ;
  522. if (bFoundAtLeastOneCookie)
  523. {
  524. // Replace the original query string with a cookie free query.
  525. memcpy( m_pszQuery, strCookieFreeQuery, strCookieFreeQuery.GetLength() );
  526. m_pszQuery[ strCookieFreeQuery.GetLength() ] = _T('\0');
  527. }
  528. }
  529. }
  530. }
  531. // >>> The following code is commented by me as it is raw, and it will not work since
  532. // topic pointer in m_infer is not set yet. Now we are taking SNIFFED_ nodes down
  533. // to the level of APGTSContext::NextCommand, and parsing it there by
  534. // APGTSContext::StripSniffedNodePrefix, and adding to sniffed array using
  535. // functionality of APGTSContext::CCommandsAddManager class.
  536. // Oleg. 10.29.99
  537. /*
  538. // In v3.2, sniffing is only in the Local TS. There's nothing inherent about that,
  539. // but as long as it's so, might as well optimize for it.
  540. if (RUNNING_LOCAL_TS())
  541. {
  542. ClearSniffedList();
  543. if (_tcsstr( m_pszQuery, C_SNIFFTAG ) != NULL)
  544. {
  545. // V3.2 - Parse out sniffed nodes passed in as hidden fields
  546. if (m_Qry.GetFirst(m_pszQuery, pszCmd, pszValue))
  547. {
  548. CString strSniffFreeQuery;
  549. bool bFoundAtLeastOneSniff= false;
  550. do
  551. {
  552. // This is supposed to be a case sensitive setting as per the specification.
  553. if (!_tcsncmp( pszCmd, C_SNIFFTAG, _tcslen( C_SNIFFTAG )))
  554. {
  555. // Found a sniffed node, add it to the list of sniffed nodes.
  556. CString strSniffedNode= pszCmd + _tcslen( C_SNIFFTAG );
  557. // >>> I believe that despite its name, CookieDecodeURL is
  558. // exactly what we want - JM 10/11/99
  559. APGTS_nmspace::CookieDecodeURL( strSniffedNode );
  560. CString strSniffedState= pszValue;
  561. APGTS_nmspace::CookieDecodeURL( strSniffedState );
  562. NID nid= NIDFromSymbolicName(strSniffedNode);
  563. int ist = _ttoi(strSniffedState);
  564. if (ist != -1)
  565. PlaceNodeInSniffedList(nid, ist);
  566. bFoundAtLeastOneSniff= true;
  567. }
  568. else
  569. {
  570. // Not a Sniffed node, add it to the sniff-free query.
  571. if (strSniffFreeQuery.GetLength())
  572. strSniffFreeQuery+= C_AMPERSAND;
  573. strSniffFreeQuery+= pszCmd;
  574. strSniffFreeQuery+= C_EQUALSIGN;
  575. strSniffFreeQuery+= pszValue;
  576. }
  577. }
  578. while (m_Qry.GetNext( pszCmd, pszValue )) ;
  579. if (bFoundAtLeastOneSniff)
  580. {
  581. // Replace the original query string with a cookie free query.
  582. memcpy( m_pszQuery, strSniffFreeQuery, strSniffFreeQuery.GetLength() );
  583. m_pszQuery[ strSniffFreeQuery.GetLength() ] = _T('\0');
  584. }
  585. }
  586. }
  587. }
  588. */
  589. eOpAction OpAction = IdentifyOperatorAction(m_pECB);
  590. if (OpAction != eNoOpAction)
  591. {
  592. if (m_bPostType == true)
  593. {
  594. // Note: Hard-coded text that should be replaced.
  595. m_strText += _T("<P>Post method not permitted for operator actions\n");
  596. }
  597. else
  598. {
  599. // Increment the number of operator action requests.
  600. m_pcountOperatorActions->Increment();
  601. CString strArg;
  602. OpAction = ParseOperatorAction(m_pECB, strArg);
  603. if (OpAction != eNoOpAction)
  604. ExecuteOperatorAction(m_pECB, OpAction, strArg);
  605. }
  606. }
  607. else if (m_Qry.GetFirst(m_pszQuery, pszCmd, pszValue))
  608. {
  609. DWORD dwStat = ProcessCommands(pszCmd, pszValue);
  610. if (dwStat != 0)
  611. {
  612. if (dwStat == EV_GTS_INF_FIRSTACC ||
  613. dwStat == EV_GTS_INF_FURTHER_GLOBALACC ||
  614. dwStat == EV_GTS_INF_THREAD_OVERVIEWACC ||
  615. dwStat == EV_GTS_INF_TOPIC_STATUSACC)
  616. {
  617. // Don't want to show contents of query, because it would put the actual
  618. // password in the file.
  619. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  620. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  621. SrcLoc.GetSrcFileLineStr(),
  622. _T(""),
  623. _T(""),
  624. dwStat );
  625. }
  626. else
  627. {
  628. m_dwErr = dwStat;
  629. if (m_dwBytes > 78)
  630. {
  631. // It's longer than we want to stick in the event log.
  632. // Cut it off with an ellipsis at byte 75, then null terminate it.
  633. m_pszQuery[75] = _T('.');
  634. m_pszQuery[76] = _T('.');
  635. m_pszQuery[77] = _T('.');
  636. m_pszQuery[78] = _T('\0');
  637. }
  638. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  639. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  640. SrcLoc.GetSrcFileLineStr(),
  641. m_pszQuery,
  642. _T(""),
  643. dwStat );
  644. }
  645. }
  646. }
  647. else {
  648. m_strText += _T("<P>No Input Parameters Specified\n");
  649. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  650. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  651. SrcLoc.GetSrcFileLineStr(),
  652. m_ipstr,
  653. _T(""),
  654. EV_GTS_USER_NO_STRING );
  655. }
  656. }
  657. //
  658. // Read a cookie (or write one, if there isn't one already)
  659. void APGTSContext::CheckAndLogCookie()
  660. {
  661. // Suppressed this in Local TS, becuase it isn't using any cookies.
  662. if (RUNNING_LOCAL_TS())
  663. return;
  664. CString str; // scratch only
  665. char szCookieNameValue[256]; // never Unicode, because cookies always ASCII
  666. char *pszValue= NULL; // never Unicode, because cookies always ASCII
  667. DWORD dwCookieLen = 255;
  668. if ( m_pECB->GetServerVariable( kHTTP_COOKIE,
  669. szCookieNameValue,
  670. &dwCookieLen))
  671. {
  672. // Got a Cookie. Parse it
  673. pszValue = GetCookieValue("GTS-COOKIE", szCookieNameValue);
  674. }
  675. if( !pszValue )
  676. {
  677. // Build a funky string for the cookie value. We want uniqueness for logging purposes.
  678. // Make a local copy of remote IP address, then massage its dots into letters, each
  679. // dependent on 4 bits of dwTempRO
  680. // While not strictly unique, there are 12 bits worth of "uniqueness" here. However,
  681. // every time the DLL is restarted, we go back to zero. Also, all servers start at zero.
  682. // Later we form the cookie value by appending time to this, so it should be "pretty unique"
  683. DWORD dwTempRO = m_pStat->dwRollover; // this value is unique to this user request
  684. TCHAR *pch;
  685. ////////////////////////////////////////////////////////////////////////////////////////////////
  686. //
  687. // mando : 10.28.2002
  688. //
  689. // PREfast bug # 517884 being fixed.
  690. //
  691. ////////////////////////////////////////////////////////////////////////////////////////////////
  692. // TCHAR szTemp[50];
  693. TCHAR szTemp[MAX_PATH + 1];
  694. ////////////////////////////////////////////////////////////////////////////////////////////////
  695. _tcscpy(szTemp, m_ipstr);
  696. while ((pch = _tcschr(szTemp, _T('.'))) != NULL)
  697. {
  698. *pch = (TCHAR)(dwTempRO & 0x0F) + _T('A');
  699. dwTempRO >>= 4;
  700. }
  701. // Create a cookie
  702. time_t timeNow; // current time
  703. time_t timeExpire; // when we set the cookie to expire
  704. time(&timeNow);
  705. timeExpire = timeNow + (m_pConf->GetCookieLife() * 60 /* secs in a minute */);
  706. // char, not TCHAR: cookie is always ASCII.
  707. char szExpire[30];
  708. {
  709. CSafeTime safetimeExpire (timeExpire);
  710. asctimeCookie(safetimeExpire.GMTime(), szExpire);
  711. }
  712. // char, not TCHAR: cookie is always ASCII.
  713. char szNewCookie[256];
  714. char szHeader[256];
  715. sprintf(szNewCookie, "%s%ld, ", szTemp, timeNow);
  716. sprintf(szHeader, "Set-Cookie: GTS-COOKIE=%s; expires=%s; \r\n",
  717. szNewCookie, szExpire);
  718. CCharConversion::ConvertACharToString(szHeader, str);
  719. m_strHeader += str;
  720. pszValue = szNewCookie;
  721. m_bNewCookie = true;
  722. }
  723. m_logstr.AddCookie(CCharConversion::ConvertACharToString(pszValue, str));
  724. }
  725. //
  726. // This takes the string returned by getting the cookie environment
  727. // variable and a specific cookie name and returns a the value
  728. // of that cookie (if it exists). There could potentially be
  729. // more than one cookie in the cookie string
  730. //
  731. // Cookies contain one or more semicolon-separated name/value pair:
  732. // name1=value1;name2=value2; (etc.)
  733. //
  734. // INPUT *pszName name we're seeking
  735. // INPUT *pszCookie the whole cookie string
  736. // OUTPUT *pszCookie this string has been written to & should not be relied on
  737. // RETURN value corresponding to *pszName (physically points into *pszCookie string)
  738. // Returns NULL if not found
  739. char *APGTSContext::GetCookieValue(char *pszName, char *pszCookie)
  740. {
  741. char *sptr, *eptr;
  742. sptr = pszCookie;
  743. while (sptr != NULL) {
  744. if ((eptr = strstr(sptr,"=")) == NULL)
  745. return(NULL);
  746. // replace the '=' with NULL
  747. *eptr = _T('\0');
  748. if (!strncmp(sptr,pszName,strlen(pszName)) ){
  749. // get the value
  750. sptr = eptr + 1;
  751. if ((eptr = strstr(sptr,";")) != NULL){
  752. *eptr = _T('\0');
  753. return(sptr);
  754. } else {
  755. // this is the last variable
  756. return(sptr);
  757. }
  758. }
  759. if ((eptr = strstr(sptr,";")) != NULL)
  760. sptr = eptr +1;
  761. else
  762. sptr = NULL;
  763. }
  764. return(NULL);
  765. }
  766. // INPUT gmt
  767. // INPUT szOut must point to a buffer of at least 29 characters to hold 28 character
  768. // text plus terminating null.
  769. // OUTPUT szOut: a pointer to a string containing a text version of date/time info.
  770. // Typical form would be "Sun, 3-Jan-1998 12:03:08 GMT" There is no choice about
  771. // this form. It should always be exactly 28 characters.
  772. // Regardless of whether the program is compiled for Unicode, this must always be ASCII:
  773. // HTTP cookies are ASCII.
  774. void APGTSContext::asctimeCookie(const struct tm &gmt, char * szOut)
  775. {
  776. char temp[20];
  777. switch (gmt.tm_wday) {
  778. case 0: strcpy(szOut, "Sun, "); break;
  779. case 1: strcpy(szOut, "Mon, "); break;
  780. case 2: strcpy(szOut, "Tue, "); break;
  781. case 3: strcpy(szOut, "Wed, "); break;
  782. case 4: strcpy(szOut, "Thu, "); break;
  783. case 5: strcpy(szOut, "Fri, "); break;
  784. case 6: strcpy(szOut, "Sat, "); break;
  785. default: return;
  786. }
  787. sprintf(temp, "%02d-", gmt.tm_mday);
  788. strcat(szOut, temp);
  789. switch (gmt.tm_mon) {
  790. case 0: strcat(szOut, "Jan-"); break;
  791. case 1: strcat(szOut, "Feb-"); break;
  792. case 2: strcat(szOut, "Mar-"); break;
  793. case 3: strcat(szOut, "Apr-"); break;
  794. case 4: strcat(szOut, "May-"); break;
  795. case 5: strcat(szOut, "Jun-"); break;
  796. case 6: strcat(szOut, "Jul-"); break;
  797. case 7: strcat(szOut, "Aug-"); break;
  798. case 8: strcat(szOut, "Sep-"); break;
  799. case 9: strcat(szOut, "Oct-"); break;
  800. case 10: strcat(szOut, "Nov-"); break;
  801. case 11: strcat(szOut, "Dec-"); break;
  802. default: return;
  803. }
  804. sprintf(temp, "%04d ", gmt.tm_year +1900);
  805. strcat(szOut, temp);
  806. sprintf(temp, "%d:%02d:%02d GMT", gmt.tm_hour, gmt.tm_min, gmt.tm_sec);
  807. strcat(szOut, temp);
  808. }
  809. //
  810. // Assumes m_Qry.GetFirst has already been called.
  811. // INPUT pszCmd & pszValue are the outputs of m_Qry.GetFirst.
  812. DWORD APGTSContext::ProcessCommands(LPTSTR pszCmd,
  813. LPTSTR pszValue)
  814. {
  815. bool bTryStatus = false; // true = try to parse as an operator status request.
  816. CString str; // strictly scratch
  817. DWORD dwStat = 0;
  818. // Check first if this is a HTI independent of DSC request.
  819. if (!_tcsicmp( pszCmd, C_TEMPLATE))
  820. {
  821. CString strBaseName, strHTItemplate;
  822. bool bValid;
  823. // Force the HTI file to be in the resource directory and have a HTI extension.
  824. strBaseName= CAbstractFileReader::GetJustNameWithoutExtension( pszValue );
  825. // Check for the case where the filename passed in was just a name.
  826. // This is a workaround for what is a questionable implementation of
  827. // GetJustNameWithoutExtension() i.e. returning an empty string when no
  828. // forward slashes or backslashes or dots are detected. RAB-981215.
  829. if ((strBaseName.IsEmpty()) && (_tcslen( pszValue )))
  830. {
  831. // Set the base name from the passed in string.
  832. strBaseName= pszValue;
  833. strBaseName.TrimLeft();
  834. strBaseName.TrimRight();
  835. }
  836. if (!strBaseName.IsEmpty())
  837. {
  838. strHTItemplate= m_pConf->GetFullResource();
  839. strHTItemplate+= strBaseName;
  840. strHTItemplate+= _T(".hti");
  841. }
  842. // Check if HTI file already exists in the map of alternate HTI templates.
  843. if (m_pConf->RetTemplateInCatalogStatus( strHTItemplate, bValid ))
  844. {
  845. // Template has been loaded, check if it is valid.
  846. if (!bValid)
  847. strHTItemplate= _T("");
  848. }
  849. else
  850. {
  851. CP_TEMPLATE cpTemplate;
  852. // Add the HTI file to the list of active alternate templates and then attempt to
  853. // load the template.
  854. m_pConf->AddTemplate( strHTItemplate );
  855. m_pConf->GetTemplate( strHTItemplate, cpTemplate, m_bNewCookie);
  856. // If the load failed then set the alternate name to blank so that the default
  857. // template is used instead.
  858. if (cpTemplate.IsNull())
  859. strHTItemplate= _T("");
  860. }
  861. // If we have a valid HTI file, set the alternate HTI template.
  862. if (!strHTItemplate.IsEmpty())
  863. SetAltHTIname( strHTItemplate );
  864. // Attempt to acquire the next step of name-value pairs.
  865. m_Qry.GetNext( pszCmd, pszValue );
  866. }
  867. if (!_tcsicmp(pszCmd, C_TOPIC_AND_PROBLEM))
  868. {
  869. // Code in this area uses ++ and -- on TCHAR* pointers, rather than using _tcsinc()
  870. // and _tcsdec. This is OK because we never use non-ASCII in the query string.
  871. // value attached to first command is commma-separated topic & problem
  872. TCHAR * pchComma= _tcsstr(pszValue, _T(","));
  873. if (pchComma)
  874. {
  875. // commma found
  876. *pchComma = 0; // replace it with a null
  877. TCHAR * pchProblem = pchComma;
  878. ++pchProblem; // to first character past the comma
  879. // strip any blanks or other junk after the comma
  880. while (*pchProblem > _T('\0') && *pchProblem <= _T(' '))
  881. ++pchProblem;
  882. --pchComma; // make pchComma point to last character before the comma
  883. // strip any blanks or other junk before the comma
  884. while (pchComma > pszValue && *pchComma > _T('\0') && *pchComma<= _T(' '))
  885. *(pchComma--) = 0;
  886. // Now push the problem back onto the query string to be found by a later GetNext()
  887. CString strProbPair(_T("ProblemAsk="));
  888. strProbPair += pchProblem;
  889. m_Qry.Push(strProbPair);
  890. }
  891. // else treat this as just a topic
  892. _tcscpy(pszCmd, C_TOPIC);
  893. }
  894. // first command should be troubleshooter type (symbolic name)
  895. // C_PRELOAD here means we've already done some "sniffing"
  896. // All of these commands take type symbolic belief-network name as their value
  897. // C_TYPE & C_PRELOAD use (deprecated) IDHs
  898. // C_TOPIC uses NIDs
  899. if (!_tcsicmp(pszCmd, C_TYPE) || !_tcsicmp(pszCmd, C_PRELOAD)
  900. || !_tcsicmp(pszCmd, C_TOPIC) )
  901. {
  902. bool bUsesIDH = _tcsicmp(pszCmd, C_TOPIC)? true:false; // True if NOT "topic".
  903. // The (deprecated) others use IDH.
  904. CString strTopicName = pszValue;
  905. strTopicName.MakeLower();
  906. m_TopicName= pszValue; // let outer world know what topic we are working on
  907. // We use a reference-counting smart pointer to hold onto the CTopic. As long as
  908. // cpTopic remains in scope, the relevant CTopic is guaranteed to remain in
  909. // existence.
  910. CP_TOPIC cpTopic;
  911. m_pConf->GetTopic(strTopicName, cpTopic, m_bNewCookie);
  912. CTopic * pTopic = cpTopic.DumbPointer();
  913. if (pTopic)
  914. {
  915. m_logstr.AddTopic(strTopicName);
  916. // You can compile with the SHOWPROGRESS option to get a report on the progress of this page.
  917. #ifdef SHOWPROGRESS
  918. time (&timeStartInfer);
  919. #endif // SHOWPROGRESS
  920. dwStat = DoInference(pszCmd, pszValue, pTopic, bUsesIDH);
  921. #ifdef SHOWPROGRESS
  922. time (&timeEndInfer);
  923. #endif // SHOWPROGRESS
  924. }
  925. else
  926. {
  927. dwStat = EV_GTS_ERROR_INF_BADTYPECMD;
  928. // $ENGLISH (see note at head of file)
  929. str = _T("<P>Unexpected troubleshooter topic:");
  930. str += strTopicName;
  931. SetError(str);
  932. m_logstr.AddTopic(_T("*UNKNOWN*"));
  933. m_pcountUnknownTopics->Increment();
  934. }
  935. }
  936. //
  937. //
  938. // Now we are going to deal with status pages.
  939. // But those pages require knowing of the IP address of the local machine.
  940. // If m_strLocalIPAddress.GetLength() == 0, we were not able to identify
  941. // the IP address and have to display an error message.
  942. else if (0 == m_strLocalIPAddress.GetLength())
  943. {
  944. dwStat = EV_GTS_ERROR_IP_GET;
  945. // $ENGLISH (see note at head of file)
  946. SetError(_T("<P>Status request must explicitly give IP address of the server."));
  947. }
  948. #ifndef LOCAL_TROUBLESHOOTER
  949. else if (!_tcsicmp(pszCmd, C_FIRST))
  950. {
  951. DisplayFirstPage(false);
  952. dwStat = EV_GTS_INF_FIRSTACC;
  953. m_pcountStatusAccesses->Increment();
  954. }
  955. #endif
  956. // You can compile with the NOPWD option to suppress all password checking.
  957. // This is intended mainly for creating test versions with this feature suppressed.
  958. #ifdef NOPWD
  959. else
  960. bTryStatus = true;
  961. #else
  962. else if (!_tcsicmp(pszCmd, C_PWD))
  963. {
  964. CString strPwd;
  965. CCharConversion::ConvertACharToString( pszValue, strPwd );
  966. CRegistryPasswords pwd;
  967. if (pwd.KeyValidate( _T("StatusAccess"), strPwd) )
  968. {
  969. time_t timeNow;
  970. time(&timeNow);
  971. // Generate a temporary password
  972. m_strTempPwd = CCharConversion::ConvertACharToString(m_ipstr, str);
  973. str.Format(_T("%d"), timeNow);
  974. m_strTempPwd += str;
  975. m_pConf->GetRecentPasswords().Add(m_strTempPwd);
  976. // Attempt to acquire the next step of name-value pairs.
  977. m_Qry.GetNext( pszCmd, pszValue );
  978. bTryStatus = true;
  979. }
  980. else if (m_pConf->GetRecentPasswords().Validate(strPwd) )
  981. {
  982. m_strTempPwd = strPwd;
  983. // Attempt to acquire the next step of name-value pairs.
  984. m_Qry.GetNext( pszCmd, pszValue );
  985. bTryStatus = true;
  986. }
  987. }
  988. #endif // ifndef NOPWD
  989. else {
  990. dwStat = EV_GTS_ERROR_INF_BADCMD;
  991. // $ENGLISH (see note at head of file)
  992. str = _T("<P>Unexpected command: ");
  993. str += pszCmd;
  994. SetError(str);
  995. }
  996. #ifndef LOCAL_TROUBLESHOOTER
  997. if (bTryStatus)
  998. {
  999. if (!_tcsicmp(pszCmd, C_FIRST))
  1000. {
  1001. DisplayFirstPage(true);
  1002. dwStat = EV_GTS_INF_FIRSTACC;
  1003. m_pcountStatusAccesses->Increment();
  1004. }
  1005. else if (!_tcsicmp(pszCmd, C_FURTHER_GLOBAL))
  1006. {
  1007. DisplayFurtherGlobalStatusPage();
  1008. dwStat = EV_GTS_INF_FURTHER_GLOBALACC;
  1009. m_pcountStatusAccesses->Increment();
  1010. }
  1011. else if (!_tcsicmp(pszCmd, C_THREAD_OVERVIEW))
  1012. {
  1013. DisplayThreadStatusOverviewPage();
  1014. dwStat = EV_GTS_INF_THREAD_OVERVIEWACC;
  1015. m_pcountStatusAccesses->Increment();
  1016. }
  1017. else if (!_tcsicmp(pszCmd, C_TOPIC_STATUS))
  1018. {
  1019. DisplayTopicStatusPage(pszValue);
  1020. dwStat = EV_GTS_INF_TOPIC_STATUSACC;
  1021. m_pcountStatusAccesses->Increment();
  1022. }
  1023. else {
  1024. dwStat = EV_GTS_ERROR_INF_BADCMD;
  1025. // $ENGLISH (see note at head of file)
  1026. str = _T("<P>Unexpected command: ");
  1027. str += pszCmd;
  1028. SetError(str);
  1029. }
  1030. }
  1031. #endif
  1032. return (dwStat);
  1033. }
  1034. BOOL APGTSContext::StrIsDigit(LPCTSTR pSz)
  1035. {
  1036. BOOL bRet = TRUE;
  1037. while (*pSz)
  1038. {
  1039. if (!_istdigit(*pSz))
  1040. {
  1041. bRet = FALSE;
  1042. break;
  1043. }
  1044. pSz = _tcsinc(pSz);
  1045. }
  1046. return bRet;
  1047. }
  1048. // INPUT szNodeName - symbolic name of node.
  1049. // RETURNS symbolic node number.
  1050. // On unrecognized symbolic name, returns nidNil
  1051. NID APGTSContext::NIDFromSymbolicName(LPCTSTR szNodeName)
  1052. {
  1053. // first handle all the special cases
  1054. if (0 == _tcsicmp(szNodeName, NODE_PROBLEM_ASK))
  1055. return nidProblemPage;
  1056. if (0 == _tcsicmp(szNodeName, NODE_SERVICE))
  1057. return nidService;
  1058. if (0 == _tcsicmp(szNodeName, NODE_FAIL))
  1059. return nidFailNode;
  1060. if (0 == _tcsicmp(szNodeName, NODE_FAILALLCAUSESNORMAL))
  1061. return nidSniffedAllCausesNormalNode;
  1062. if (0 == _tcsicmp(szNodeName, NODE_IMPOSSIBLE))
  1063. return nidImpossibleNode;
  1064. if (0 == _tcsicmp(szNodeName, NODE_BYE))
  1065. return nidByeNode;
  1066. // normal symbolic name
  1067. NID nid = m_infer.INode(szNodeName);
  1068. if (nid == -1)
  1069. return nidNil;
  1070. else
  1071. return nid;
  1072. }
  1073. // Validate and convert a list of nodes and their associated states.
  1074. //
  1075. // INPUT pszCmd & pszValue are the outputs of m_Qry.GetNext.
  1076. // INPUT index into the HTTP query parameters. Parameters may follow any of the following
  1077. // PATTERNS.
  1078. // INPUT dwCount is the number shown at left in each of these patterns. Remember that this
  1079. // function never sees dwCount=1; that's been used to set m_bPreload:
  1080. // INPUT bUsesIDH - Interpret numerics as IDHs (a deprecated feature) rather than NIDs
  1081. // DEPRECATED BUT SUPPORTED PATTERNS when bUsesIDH == true
  1082. // "Preload"
  1083. // PROBABLY NOT EFFECTIVE BACKWARD COMPATIBLITY
  1084. // because if they've added any nodes, # of nodes in network will have changed)
  1085. // 1 - preload=<troubleshooter symbolic name>
  1086. // 2 - <# of nodes in network + 1000>=<problem node number + 1000>
  1087. // 3+ - <node symbolic name>=<node state value>
  1088. // Old "type" (we never generate these, but we have them here for backward compatibility.
  1089. // PROBABLY NOT EFFECTIVE BACKWARD COMPATIBLITY
  1090. // because if they've added any nodes, # of nodes in network will have changed)
  1091. // 1 - type=<troubleshooter symbolic name>
  1092. // 2 - <# of nodes in network + 1000>=<problem node number + 1000>
  1093. // 3+ - <node # + 1000>=<node state value>
  1094. // Newer "type", should be fully backward compatible.
  1095. // 1 - type=<troubleshooter symbolic name>
  1096. // 2 - ProblemAsk=<problem node symbolic name>
  1097. // 3+ - <node symbolic name>=<node state value>
  1098. // It is presumably OK for us to allow a slight superset of the known formats, which yields:
  1099. // 1 - preload=<troubleshooter symbolic name> OR
  1100. // type=<troubleshooter symbolic name>
  1101. // Determines m_bPreload before this fn is called
  1102. // 2 - <# of nodes in network + 1000>=<problem node number + 1000> OR
  1103. // ProblemAsk=<problem node symbolic name>
  1104. // We can distinguish between these by whether pszCmd is numeric
  1105. // 3+ - <node # + 1000>=<node state value> OR
  1106. // <node symbolic name>=<node state value>
  1107. // We can distinguish between these by whether pszCmd is numeric
  1108. // The only assumption in this overloading is that a symbolic name will never be entirely
  1109. // numeric.
  1110. // SUPPORTED PATTERN when bUsesIDH == false
  1111. // 1 - topic=<troubleshooter symbolic name>
  1112. // 2 - ProblemAsk=<problem node symbolic name>
  1113. // 3+ - <node symbolic name>=<node state value>
  1114. //
  1115. // LIMITATION ON STATE NUMBERS
  1116. // As of 11/97, <node state value> must always be one of:
  1117. // 0 - Fixed/Unfixed: Haven't solved problem
  1118. // Info: First option
  1119. // 1 - Info: Second option
  1120. // 101 - Go to "Bye" Page (User succeeded - applies to Fixed/Unfixed or Support Nodes only)
  1121. // 102 - Unknown (user doesn't know the correct answer here - applies to Fixed/Unfixed and
  1122. // Info nodes only)
  1123. // 103 - "Anything Else I Can Try"
  1124. // Since inputs of state values should always come from forms we generated, we don't
  1125. // systematically limit state numbers in the code here.
  1126. // V3.0 allows other numeric states: 0-99 should all be legal.
  1127. //
  1128. // RETURN 0 on success, otherwise an error code
  1129. DWORD APGTSContext::NextCommand(LPTSTR pszCmd, LPTSTR pszValue, bool bUsesIDH)
  1130. {
  1131. NID nid;
  1132. int value = 0; // if pszValue is numeric, a NID or state.
  1133. // otherwise, pszValue is the symbolic name of a node,
  1134. // and this is its NID
  1135. bool sniffed = false;
  1136. if (StrIsDigit(pszCmd))
  1137. {
  1138. // only should arise for bUsesIDH
  1139. // it's an IDH (typically node # + 1000), but can be <# of nodes in network> + 1000,
  1140. // interpreted as "ProblemAsk"
  1141. // The pages we generate never give us these values, but we recognize them.
  1142. IDH idh = _ttoi(pszCmd);
  1143. nid = m_infer.NIDFromIDH(idh);
  1144. }
  1145. else
  1146. {
  1147. // The command is a symbolic name.
  1148. sniffed = StripSniffedNodePrefix(pszCmd);
  1149. nid= NIDFromSymbolicName(pszCmd);
  1150. }
  1151. if (StrIsDigit(pszValue))
  1152. {
  1153. if (bUsesIDH)
  1154. {
  1155. int valueIDH = _ttoi(pszValue);
  1156. if (nid == nidProblemPage)
  1157. // problem node number + 1000
  1158. value = m_infer.NIDFromIDH(valueIDH);
  1159. else
  1160. // state value
  1161. value = valueIDH;
  1162. }
  1163. else
  1164. {
  1165. // value is a state number.
  1166. value = _ttoi(pszValue);
  1167. }
  1168. }
  1169. else if (nid == nidProblemPage)
  1170. {
  1171. // Symbolic name of problem node
  1172. value = NIDFromSymbolicName(pszValue);
  1173. }
  1174. else
  1175. return EV_GTS_ERROR_INF_BADPARAM;
  1176. m_CommandsAddManager.Add(nid, value, sniffed);
  1177. return 0;
  1178. }
  1179. DWORD APGTSContext::NextAdditionalInfo(LPTSTR pszCmd, LPTSTR pszValue)
  1180. {
  1181. if (RUNNING_LOCAL_TS())
  1182. {
  1183. if ( 0 == _tcscmp(pszCmd, C_ALLOW_AUTOMATIC_SNIFFING_NAME) &&
  1184. (0 == _tcsicmp(pszValue, C_ALLOW_AUTOMATIC_SNIFFING_CHECKED) ||
  1185. 0 == _tcsicmp(pszValue, C_ALLOW_AUTOMATIC_SNIFFING_UNCHECKED)))
  1186. {
  1187. m_AdditionalInfo.Add(pszCmd, pszValue);
  1188. return 0;
  1189. }
  1190. if (0 == _tcscmp(pszCmd, C_LAST_SNIFFED_MANUALLY))
  1191. {
  1192. if (0 == _tcscmp(pszValue, SZ_ST_SNIFFED_MANUALLY_TRUE))
  1193. m_infer.SetLastSniffedManually(true);
  1194. return 0;
  1195. }
  1196. }
  1197. return EV_GTS_ERROR_INF_BADPARAM;
  1198. }
  1199. // Name - value pairs that we can ignore
  1200. DWORD APGTSContext::NextIgnore(LPTSTR pszCmd, LPTSTR pszValue)
  1201. {
  1202. if (RUNNING_LOCAL_TS())
  1203. {
  1204. // Value "-1" can come from a field, used for manual sniffing,
  1205. // when other then "Sniff" button is pressed
  1206. CString strValue(pszValue);
  1207. CString strSniffFailure;
  1208. strValue.TrimLeft();
  1209. strValue.TrimRight();
  1210. strSniffFailure.Format(_T("%d"), SNIFF_FAILURE_RESULT);
  1211. if (strValue == strSniffFailure)
  1212. {
  1213. // Name in this case should be a valid node name
  1214. NID nid = nidNil;
  1215. StripSniffedNodePrefix(pszCmd);
  1216. nid = NIDFromSymbolicName(pszCmd);
  1217. if (nid != nidNil)
  1218. return 0;
  1219. }
  1220. }
  1221. return EV_GTS_ERROR_INF_BADPARAM;
  1222. }
  1223. VOID APGTSContext::ClearCommandList()
  1224. {
  1225. m_Commands.RemoveAll();
  1226. }
  1227. VOID APGTSContext::ClearSniffedList()
  1228. {
  1229. m_Sniffed.RemoveAll();
  1230. }
  1231. VOID APGTSContext::ClearAdditionalInfoList()
  1232. {
  1233. m_AdditionalInfo.RemoveAll();
  1234. }
  1235. /*
  1236. // Return false on failure; shouldn't ever arise.
  1237. bool APGTSContext::PlaceNodeInCommandList(NID nid, IST ist)
  1238. {
  1239. return (m_Commands.Add(nid, ist) > 0);
  1240. }
  1241. */
  1242. /*
  1243. // Return false on failure; shouldn't ever arise.
  1244. bool APGTSContext::PlaceNodeInSniffedList(NID nid, IST ist)
  1245. {
  1246. return (m_Sniffed.Add(nid, ist) > 0);
  1247. }
  1248. */
  1249. /*
  1250. // Return false on failure; shouldn't ever arise.
  1251. bool APGTSContext::PlaceInAdditionalInfoList(const CString& name, const CString& value)
  1252. {
  1253. return (m_AdditionalInfo.Add(name, value) > 0);
  1254. }
  1255. */
  1256. // INPUT: node short name, possibly prefixed by SNIFFED_
  1257. // OUTPUT: node short name
  1258. // RETURN: true is prefix was stripped
  1259. bool APGTSContext::StripSniffedNodePrefix(LPTSTR szName)
  1260. {
  1261. if (0 == _tcsnicmp(szName, C_SNIFFTAG, _tcslen(C_SNIFFTAG)))
  1262. {
  1263. // use "memmove" since we are operating with overlapped regions!
  1264. memmove(szName,
  1265. szName + _tcslen(C_SNIFFTAG),
  1266. _tcslen(szName + _tcslen(C_SNIFFTAG)) + 1);
  1267. return true;
  1268. }
  1269. return false;
  1270. }
  1271. VOID APGTSContext::SetNodesPerCommandList()
  1272. {
  1273. int nCommands = m_Commands.GetSize();
  1274. for (int i= 0; i<nCommands; i++)
  1275. {
  1276. NID nid;
  1277. int value; // typically a state (IST), except if nid==nidProblemPage, where value is a NID
  1278. m_Commands.GetAt( i, nid, value );
  1279. m_infer.SetNodeState(nid, value);
  1280. }
  1281. }
  1282. VOID APGTSContext::SetNodesPerSniffedList()
  1283. {
  1284. int nSniffed = m_Sniffed.GetSize();
  1285. for (int i= 0; i<nSniffed; i++)
  1286. {
  1287. NID nid;
  1288. int ist;
  1289. m_Sniffed.GetAt( i, nid, ist );
  1290. m_infer.AddToSniffed(nid, ist);
  1291. }
  1292. }
  1293. VOID APGTSContext::ProcessAdditionalInfoList()
  1294. {
  1295. int nCount = m_AdditionalInfo.GetSize();
  1296. for (int i= 0; i < nCount; i++)
  1297. {
  1298. if (RUNNING_LOCAL_TS())
  1299. {
  1300. CString name;
  1301. CString value;
  1302. m_AdditionalInfo.GetAt( i, name, value );
  1303. if (name == C_ALLOW_AUTOMATIC_SNIFFING_NAME)
  1304. {
  1305. value.MakeLower();
  1306. if (m_infer.GetSniff())
  1307. {
  1308. // set AllowAutomaticSniffing flag
  1309. if (value == C_ALLOW_AUTOMATIC_SNIFFING_CHECKED)
  1310. m_infer.GetSniff()->SetAllowAutomaticSniffingPolicy(true);
  1311. if (value == C_ALLOW_AUTOMATIC_SNIFFING_UNCHECKED)
  1312. m_infer.GetSniff()->SetAllowAutomaticSniffingPolicy(false);
  1313. }
  1314. }
  1315. }
  1316. else
  1317. {
  1318. // process additional info in Online TS here
  1319. }
  1320. }
  1321. }
  1322. VOID APGTSContext::ReadPolicyInfo()
  1323. {
  1324. if (RUNNING_LOCAL_TS())
  1325. {
  1326. if (m_infer.GetSniff())
  1327. {
  1328. // set AllowManualSniffing flag
  1329. DWORD dwManualSniffing = 0;
  1330. m_pConf->GetRegistryMonitor().GetNumericInfo(CAPGTSRegConnector::eSniffManual, dwManualSniffing);
  1331. m_infer.GetSniff()->SetAllowManualSniffingPolicy(dwManualSniffing ? true : false);
  1332. // >>> $TODO$ I do not like setting Policy Editor values explicitely,
  1333. // as it done here. Probably we will have to implement
  1334. // CSniffPolicyInfo (abstract) class, designed for passing
  1335. // those values to sniffer (CSniff).
  1336. // Oleg. 11.05.99
  1337. }
  1338. }
  1339. }
  1340. VOID APGTSContext::LogNodesPerCommandList()
  1341. {
  1342. int nCommands = m_Commands.GetSize();
  1343. for (int i= 0; i<nCommands; i++)
  1344. {
  1345. NID nid;
  1346. int value; // typically a state (IST), except if nid==nidProblemPage, where value is a NID
  1347. m_Commands.GetAt( i, nid, value );
  1348. if (nid == nidProblemPage)
  1349. m_logstr.AddNode(value, 1);
  1350. else
  1351. m_logstr.AddNode(nid, value);
  1352. }
  1353. }
  1354. // builds and returns the Start Over link
  1355. // only relevant for Online TS
  1356. // >>> JM 10/8/99: I believe it would be redundant to URL-encode
  1357. CString APGTSContext::GetStartOverLink()
  1358. {
  1359. CString str;
  1360. #ifndef LOCAL_TROUBLESHOOTER
  1361. bool bHasQuestionMark = false;
  1362. // ISAPI DLL's URL
  1363. str = m_strVRoot;
  1364. // CK_ name value pairs
  1365. if (!m_mapCookiesPairs.empty())
  1366. {
  1367. // V3.2 - Output any CK_name-value pairs as hidden fields.
  1368. for (CCookiePairs::const_iterator it = m_mapCookiesPairs.begin(); it != m_mapCookiesPairs.end(); ++it)
  1369. {
  1370. if (bHasQuestionMark)
  1371. str += _T("&");
  1372. else
  1373. {
  1374. str += _T("?");
  1375. bHasQuestionMark = true;
  1376. }
  1377. CString strAttr= it->first;
  1378. CString strValue= it->second;
  1379. APGTS_nmspace::CookieEncodeURL( strAttr );
  1380. APGTS_nmspace::CookieEncodeURL( strValue );
  1381. str += C_COOKIETAG + strAttr;
  1382. str += _T("=");
  1383. str += strValue;
  1384. }
  1385. }
  1386. // template
  1387. const CString strAltHTIname= GetAltHTIname();
  1388. if (!strAltHTIname.IsEmpty())
  1389. {
  1390. if (bHasQuestionMark)
  1391. str += _T("&");
  1392. else
  1393. {
  1394. str += _T("?");
  1395. bHasQuestionMark = true;
  1396. }
  1397. str += C_TEMPLATE;
  1398. str += _T("=");
  1399. str += strAltHTIname;
  1400. }
  1401. // topic
  1402. if (!m_TopicName.IsEmpty())
  1403. {
  1404. if (bHasQuestionMark)
  1405. str += _T("&");
  1406. else
  1407. {
  1408. str += _T("?");
  1409. bHasQuestionMark = true;
  1410. }
  1411. str += C_TOPIC;
  1412. str += _T("=");
  1413. str += m_TopicName;
  1414. }
  1415. #endif
  1416. return str;
  1417. }
  1418. // Assumes m_Qry.GetFirst has already been called.
  1419. // INPUT pszCmd & pszValue are the outputs of m_Qry.GetFirst.
  1420. // These same buffers are used for subsequent calls to m_Qry.GetNext.
  1421. // INPUT *pTopic - represents contents of appropriate DSC, HTI, BES
  1422. // INPUT bUsesIDH - Interpret numerics as IDHs (a deprecated feature) rather than NIDs
  1423. // Return 0 on success, EV_GTS_ERROR_INF_BADPARAM on failure.
  1424. DWORD APGTSContext::DoInference(LPTSTR pszCmd,
  1425. LPTSTR pszValue,
  1426. CTopic * pTopic,
  1427. bool bUsesIDH)
  1428. {
  1429. DWORD dwStat = 0;
  1430. CString strTopic = pszValue;
  1431. CString strHTTPcookies;
  1432. if (!_tcsicmp(pszCmd, C_PRELOAD))
  1433. m_bPreload = true;
  1434. m_infer.SetTopic(pTopic);
  1435. ClearCommandList();
  1436. ClearSniffedList();
  1437. ClearAdditionalInfoList();
  1438. while (m_Qry.GetNext(pszCmd, pszValue))
  1439. {
  1440. if ((dwStat = NextCommand(pszCmd, pszValue, bUsesIDH)) != 0)
  1441. if ((dwStat = NextAdditionalInfo(pszCmd, pszValue)) != 0)
  1442. if ((dwStat = NextIgnore(pszCmd, pszValue)) != 0)
  1443. break;
  1444. }
  1445. if (!dwStat)
  1446. {
  1447. m_Commands.RotateProblemPageToFront();
  1448. LogNodesPerCommandList();
  1449. SetNodesPerCommandList();
  1450. SetNodesPerSniffedList();
  1451. ProcessAdditionalInfoList();
  1452. ReadPolicyInfo();
  1453. // Append to m_strText: contents of an HTML page based on HTI template.
  1454. // History & next recommendation
  1455. // Build the $StartForm string.
  1456. CString strStartForm;
  1457. CString strTmpLine;
  1458. const CString strAltHTIname= GetAltHTIname();
  1459. if (RUNNING_LOCAL_TS())
  1460. strStartForm = _T("<FORM NAME=\"ButtonForm\">\n");
  1461. else
  1462. strStartForm.Format( _T("<FORM METHOD=POST ACTION=\"%s\">\n"), m_pConf->GetVrootPath() );
  1463. if (RUNNING_ONLINE_TS())
  1464. {
  1465. // This processes name-value pairs, parallel to those which would come
  1466. // from a cookie to determine look and feel, but which in this case actually
  1467. // were originally sent in as CK_ pairs in Get or Post.
  1468. // These values are only used in the Online TS.
  1469. try
  1470. {
  1471. if (!m_mapCookiesPairs.empty())
  1472. {
  1473. // V3.2 - Output any CK_name-value pairs as hidden fields.
  1474. for (CCookiePairs::const_iterator it = m_mapCookiesPairs.begin(); it != m_mapCookiesPairs.end(); ++it)
  1475. {
  1476. CString strAttr= it->first;
  1477. CString strValue= it->second;
  1478. APGTS_nmspace::CookieEncodeURL( strAttr );
  1479. APGTS_nmspace::CookieEncodeURL( strValue );
  1480. strTmpLine.Format( _T("<INPUT TYPE=HIDDEN NAME=\"%s%s\" VALUE=\"%s\">\n"),
  1481. C_COOKIETAG, strAttr, strValue );
  1482. strStartForm+= strTmpLine;
  1483. }
  1484. }
  1485. // This processes name-value pairs, which actually come
  1486. // from a cookie to determine look and feel.
  1487. // These values are only used in the Online TS.
  1488. // V3.2 - Extract all look-and-feel cookie name-value pairs from the HTTP headers.
  1489. char szCookieNameValue[ 1024 ]; // never Unicode, because cookies always ASCII
  1490. DWORD dwCookieLen= 1023;
  1491. if ( m_pECB->GetServerVariable( kHTTP_COOKIE, szCookieNameValue, &dwCookieLen ))
  1492. strHTTPcookies= szCookieNameValue;
  1493. else
  1494. {
  1495. // Determine if the buffer was too small.
  1496. if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  1497. {
  1498. // never Unicode, because cookies always ASCII.
  1499. char *pszCookieNameValue= new char[ dwCookieLen + 1 ];
  1500. if ( m_pECB->GetServerVariable( kHTTP_COOKIE, pszCookieNameValue, &dwCookieLen ))
  1501. strHTTPcookies= pszCookieNameValue;
  1502. delete [] pszCookieNameValue;
  1503. }
  1504. else
  1505. {
  1506. // Note memory failure in event log.
  1507. CString strLastError;
  1508. strLastError.Format( _T("%d"), ::GetLastError() );
  1509. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1510. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1511. SrcLoc.GetSrcFileLineStr(),
  1512. strLastError, _T(""),
  1513. EV_GTS_ERROR_EXTRACTING_HTTP_COOKIES );
  1514. }
  1515. }
  1516. }
  1517. catch (bad_alloc&)
  1518. {
  1519. // Note memory failure in event log.
  1520. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1521. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1522. SrcLoc.GetSrcFileLineStr(),
  1523. _T(""), _T(""), EV_GTS_CANT_ALLOC );
  1524. }
  1525. catch (exception& x)
  1526. {
  1527. CString str;
  1528. // Note STL exception in event log.
  1529. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1530. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1531. SrcLoc.GetSrcFileLineStr(),
  1532. CCharConversion::ConvertACharToString(x.what(), str),
  1533. _T(""),
  1534. EV_GTS_STL_EXCEPTION );
  1535. }
  1536. }
  1537. if (!strAltHTIname.IsEmpty())
  1538. {
  1539. // Add the alternate HTI template name to the $StartForm string.
  1540. strTmpLine.Format( _T("<INPUT TYPE=HIDDEN NAME=\"template\" VALUE=\"%s\">\n"),
  1541. CAbstractFileReader::GetJustName( strAltHTIname ) );
  1542. strStartForm+= strTmpLine;
  1543. }
  1544. strTmpLine.Format( _T("<INPUT TYPE=HIDDEN NAME=\"topic\" VALUE=\"%s\">\n"), strTopic );
  1545. strStartForm+= strTmpLine;
  1546. // Determine whether an alternate HTI template should be used.
  1547. bool bAlternatePageGenerated= false;
  1548. if (!strAltHTIname.IsEmpty())
  1549. {
  1550. // Attempt to extract a pointer to the requested HTI template and if successful
  1551. // then create a page from it.
  1552. CP_TEMPLATE cpTemplate;
  1553. m_pConf->GetTemplate( strAltHTIname, cpTemplate, m_bNewCookie );
  1554. CAPGTSHTIReader *pHTI= cpTemplate.DumbPointer();
  1555. if (pHTI)
  1556. {
  1557. CString strResourcePath;
  1558. m_pConf->GetRegistryMonitor().GetStringInfo(CAPGTSRegConnector::eResourcePath, strResourcePath);
  1559. #ifdef LOCAL_TROUBLESHOOTER
  1560. CHTMLFragmentsLocal frag( strResourcePath, pHTI->HasHistoryTable() );
  1561. #else
  1562. CHTMLFragmentsTS frag( strResourcePath, pHTI->HasHistoryTable() );
  1563. #endif
  1564. // Add the $StartForm string to the HTML fragments.
  1565. frag.SetStartForm(strStartForm);
  1566. frag.SetStartOverLink(GetStartOverLink());
  1567. // JSM V3.2 get list of Net prop names needed by HTI;
  1568. // pass them to frag. CInfer will fill in
  1569. // the net prop values, using these names, in FillInHTMLFragments()
  1570. {
  1571. vector<CString> arr_props;
  1572. pHTI->ExtractNetProps(arr_props);
  1573. for(vector<CString>::iterator i = arr_props.begin(); i < arr_props.end(); i++)
  1574. frag.AddNetPropName(*i);
  1575. }
  1576. m_infer.IdentifyPresumptiveCause();
  1577. m_infer.FillInHTMLFragments(frag);
  1578. pHTI->CreatePage( frag, m_strText, m_mapCookiesPairs, strHTTPcookies );
  1579. bAlternatePageGenerated= true;
  1580. }
  1581. }
  1582. if (!bAlternatePageGenerated)
  1583. {
  1584. // The page was not generated from an alternate HTI template, generate it now.
  1585. // >>> $MAINT an awful lot of common code with the above. Can't we
  1586. // set up a private method?
  1587. CString strResourcePath;
  1588. m_pConf->GetRegistryMonitor().GetStringInfo(CAPGTSRegConnector::eResourcePath, strResourcePath);
  1589. #ifdef LOCAL_TROUBLESHOOTER
  1590. CHTMLFragmentsLocal frag( strResourcePath, pTopic->HasHistoryTable() );
  1591. #else
  1592. CHTMLFragmentsTS frag( strResourcePath, pTopic->HasHistoryTable() );
  1593. #endif
  1594. // Add the $StartForm string to the HTML fragments.
  1595. frag.SetStartForm(strStartForm);
  1596. frag.SetStartOverLink(GetStartOverLink());
  1597. // JSM V3.2 get list of Net prop names needed by HTI;
  1598. // pass them to frag. CInfer will fill in
  1599. // the net prop values, using these names, in FillInHTMLFragments()
  1600. {
  1601. vector<CString> arr_props;
  1602. pTopic->ExtractNetProps(arr_props);
  1603. for(vector<CString>::iterator i = arr_props.begin(); i < arr_props.end(); i++)
  1604. frag.AddNetPropName(*i);
  1605. }
  1606. m_infer.IdentifyPresumptiveCause();
  1607. m_infer.FillInHTMLFragments(frag);
  1608. pTopic->CreatePage( frag, m_strText, m_mapCookiesPairs, strHTTPcookies );
  1609. }
  1610. if (m_infer.AppendBESRedirection(m_strHeader))
  1611. // We have no more recommendations, but there is a BES file present, so we
  1612. // have to redirect the user
  1613. _tcscpy(m_resptype, _T("302 Object Moved"));
  1614. }
  1615. else
  1616. {
  1617. SetError(_T(""));
  1618. }
  1619. return dwStat;
  1620. }
  1621. CString APGTSContext::RetCurrentTopic() const
  1622. {
  1623. return( m_TopicName );
  1624. }
  1625. // Operator actions which must be performed by the main thread are caught in
  1626. // APGTSExtension::IsEmergencyRequest
  1627. // All other operator actions can be identified by this routine.
  1628. // INPUT *pECB: our abstraction from EXTENSION_CONTROL_BLOCK, which is ISAPI's way of
  1629. // packaging CGI data. pECB should never be null.
  1630. APGTSContext::eOpAction APGTSContext::IdentifyOperatorAction(CAbstractECB *pECB)
  1631. {
  1632. if (strcmp(pECB->GetMethod(), "GET"))
  1633. return eNoOpAction;
  1634. if (strncmp(pECB->GetQueryString(), SZ_EMERGENCY_DEF, strlen(SZ_OP_ACTION)))
  1635. return eNoOpAction;
  1636. if ( ! strncmp(pECB->GetQueryString() + strlen(SZ_OP_ACTION),
  1637. SZ_RELOAD_TOPIC, strlen(SZ_RELOAD_TOPIC)))
  1638. return eReloadTopic;
  1639. if ( ! strncmp(pECB->GetQueryString() + strlen(SZ_OP_ACTION),
  1640. SZ_KILL_THREAD, strlen(SZ_KILL_THREAD)))
  1641. return eKillThread;
  1642. if ( ! strncmp(pECB->GetQueryString() + strlen(SZ_OP_ACTION),
  1643. SZ_RELOAD_ALL_TOPICS, strlen(SZ_RELOAD_ALL_TOPICS)))
  1644. return eReloadAllTopics;
  1645. if ( ! strncmp(pECB->GetQueryString() + strlen(SZ_OP_ACTION),
  1646. SZ_SET_REG, strlen(SZ_SET_REG)))
  1647. return eSetReg;
  1648. return eNoOpAction;
  1649. }
  1650. // Identify a request to perform an operator action that does not have to be performed
  1651. // on the main thread.
  1652. // Should be called only after we have determined this is an operator action: sends an
  1653. // error msg to end user if it's not.
  1654. // Based loosely on APGTSExtension::ParseEmergencyRequest
  1655. // INPUT *pECB: our abstraction from EXTENSION_CONTROL_BLOCK, which is ISAPI's way of
  1656. // packaging CGI data. pECB should never be null.
  1657. // OUTPUT strArg - any argument for this operation
  1658. // Returns identified operator action.
  1659. APGTSContext::eOpAction APGTSContext::ParseOperatorAction(
  1660. CAbstractECB *pECB,
  1661. CString & strArg)
  1662. {
  1663. TCHAR *pszProblem= NULL;
  1664. CHAR * ptr = pECB->GetQueryString();
  1665. CHAR * ptrArg = strstr(pECB->GetQueryString(), "&");
  1666. if(ptrArg)
  1667. {
  1668. // Turn the ampersand into a terminator and point past it.
  1669. // Everything past it is an argument.
  1670. *(ptrArg++) = '\0';
  1671. CCharConversion::ConvertACharToString(ptrArg, strArg) ;
  1672. }
  1673. else
  1674. strArg = _T("");
  1675. // In a sense this test is redundant (should know this before this fn is called) but
  1676. // this seemed like a safer way to code JM 11/2/98
  1677. eOpAction ret = IdentifyOperatorAction(pECB);
  1678. if ( ret == eNoOpAction)
  1679. pszProblem= _T("Wrong Format");
  1680. else
  1681. {
  1682. switch(ret)
  1683. {
  1684. case eReloadTopic:
  1685. ptr += strlen(SZ_OP_ACTION) + strlen(SZ_RELOAD_TOPIC);
  1686. break;
  1687. case eKillThread:
  1688. ptr += strlen(SZ_OP_ACTION) + strlen(SZ_KILL_THREAD);
  1689. break;
  1690. case eReloadAllTopics:
  1691. ptr += strlen(SZ_OP_ACTION) + strlen(SZ_RELOAD_ALL_TOPICS);
  1692. break;
  1693. case eSetReg:
  1694. ptr += strlen(SZ_OP_ACTION) + strlen(SZ_SET_REG);
  1695. break;
  1696. default:
  1697. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1698. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1699. SrcLoc.GetSrcFileLineStr(),
  1700. _T(""), _T(""),
  1701. EV_GTS_ERROR_INVALIDOPERATORACTION );
  1702. }
  1703. // You can compile with the NOPWD option to suppress all password checking.
  1704. // This is intended mainly for creating test versions with this feature suppressed.
  1705. #ifndef NOPWD
  1706. CRegistryPasswords pwd;
  1707. CString str;
  1708. if (! pwd.KeyValidate(
  1709. _T("ActionAccess"),
  1710. CCharConversion::ConvertACharToString(ptr, str) ) )
  1711. {
  1712. pszProblem= _T("Bad password");
  1713. ret = eNoOpAction;
  1714. }
  1715. #endif // ifndef NOPWD
  1716. }
  1717. if (pszProblem)
  1718. {
  1719. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1720. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1721. SrcLoc.GetSrcFileLineStr(),
  1722. pszProblem,
  1723. _T(""),
  1724. EV_GTS_CANT_PROC_OP_ACTION );
  1725. m_dwErr = EV_GTS_CANT_PROC_OP_ACTION;
  1726. }
  1727. return ret;
  1728. }
  1729. // Execute a request to do one of:
  1730. // - Reload one topic
  1731. // - Kill (and restart) one pool thread
  1732. // - Reload all monitored files.
  1733. // INPUT *pECB: our abstraction from EXTENSION_CONTROL_BLOCK, which is ISAPI's way of
  1734. // packaging CGI data. pECB should never be null.
  1735. // INPUT action - chooses among the three possible actions
  1736. // INPUT strArg - provides any necessary arguments for that action
  1737. // RETURNS HSE_STATUS_SUCCESS, HSE_STATUS_ERROR
  1738. void APGTSContext::ExecuteOperatorAction(
  1739. CAbstractECB *pECB,
  1740. eOpAction action,
  1741. const CString & strArg)
  1742. {
  1743. m_strText += _T("<HTML><HEAD><TITLE>AP GTS Command</TITLE></HEAD>");
  1744. m_strText += _T("<BODY BGCOLOR=#FFFFFF>");
  1745. switch (action)
  1746. {
  1747. case eReloadTopic:
  1748. {
  1749. bool bAlreadyInCatalog;
  1750. m_strText += _T("<H1>Reload Topic ");
  1751. m_strText += strArg;
  1752. m_strText += _T("</H1>");
  1753. m_pConf->GetTopicShop().BuildTopic(strArg, &bAlreadyInCatalog);
  1754. if (!bAlreadyInCatalog)
  1755. {
  1756. m_strText += strArg;
  1757. m_strText += _T(" is not a known topic. Either it is not in the current LST file")
  1758. _T(" or the Online Troubleshooter is waiting to see the resource directory")
  1759. _T(" &quot;settle&quot; before loading the LST file.");
  1760. }
  1761. break;
  1762. }
  1763. case eKillThread:
  1764. m_strText += _T("<H1>Kill Thread");
  1765. m_strText += strArg;
  1766. m_strText += _T("</H1>");
  1767. if (m_pConf->GetThreadPool().ReinitializeThread(_ttoi(strArg)))
  1768. m_strText += _T("Thread killed. System will attempt to spin a new thread.");
  1769. else
  1770. m_strText += _T("No such thread");
  1771. break;
  1772. case eReloadAllTopics:
  1773. m_strText += _T("<H1>Reload All Topics</H1>");
  1774. m_pConf->GetTopicShop().RebuildAll();
  1775. break;
  1776. case eSetReg:
  1777. {
  1778. CHttpQuery query;
  1779. TCHAR szCmd[MAXBUF];
  1780. TCHAR szVal[MAXBUF];
  1781. CString strCmd, strVal;
  1782. query.GetFirst(strArg, szCmd, szVal);
  1783. CCharConversion::ConvertACharToString(szCmd, strCmd);
  1784. CCharConversion::ConvertACharToString(szVal, strVal);
  1785. m_strText += _T("<H1>Set registry value");
  1786. m_strText += strCmd;
  1787. m_strText += _T(" = ");
  1788. m_strText += strVal;
  1789. m_strText += _T("</H1>");
  1790. CAPGTSRegConnector RegConnect( _T("") );
  1791. bool bChanged ;
  1792. bool bExists = RegConnect.SetOneValue(szCmd, szVal, bChanged );
  1793. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  1794. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  1795. SrcLoc.GetSrcFileLineStr(),
  1796. strCmd,
  1797. strVal,
  1798. bExists ?
  1799. EV_GTS_SET_REG_VALUE :
  1800. EV_GTS_CANT_SET_REG_VALUE);
  1801. if (bChanged)
  1802. m_strText += _T("Successful.");
  1803. else if (bExists)
  1804. {
  1805. m_strText += strCmd;
  1806. m_strText += _T(" already had value ");
  1807. m_strText += strVal;
  1808. m_strText += _T(".");
  1809. }
  1810. else
  1811. {
  1812. m_strText += strCmd;
  1813. m_strText += _T(" Unknown.");
  1814. }
  1815. break;
  1816. }
  1817. default:
  1818. m_strText += _T("<H1>Unknown operation</H1>");
  1819. break;
  1820. }
  1821. m_strText += strArg;
  1822. m_strText += _T("</BODY></HTML>");
  1823. }
  1824. // override any partially written page with an error page.
  1825. void APGTSContext::SetError(LPCTSTR szMessage)
  1826. {
  1827. _tcscpy(m_resptype, _T("400 Bad Request"));
  1828. CString str(_T("<H3>Possible invalid data received</H3>\n"));
  1829. str += szMessage;
  1830. m_pConf->CreateErrorPage(str, m_strText);
  1831. }
  1832. void APGTSContext::SetAltHTIname( const CString& strHTIname )
  1833. {
  1834. m_strAltHTIname= strHTIname;
  1835. }
  1836. CString APGTSContext::GetAltHTIname() const
  1837. {
  1838. return( m_strAltHTIname );
  1839. }