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.

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