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.

1824 lines
33 KiB

  1. /*++
  2. Copyright (c) 1994-2000 Microsoft Corporation
  3. Module Name :
  4. iismachine.cpp
  5. Abstract:
  6. IIS Machine node
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Sergei Antonov (sergeia)
  10. Project:
  11. Internet Services Manager
  12. Revision History:
  13. 10/28/2000 sergeia Split from iisobj.cpp
  14. --*/
  15. #include "stdafx.h"
  16. #include "common.h"
  17. #include "inetprop.h"
  18. #include "InetMgrApp.h"
  19. #include "supdlgs.h"
  20. #include "connects.h"
  21. #include "metaback.h"
  22. #include "iisobj.h"
  23. #include "shutdown.h"
  24. #include "machsht.h"
  25. #include "w3sht.h"
  26. #include "fltdlg.h"
  27. #ifdef _DEBUG
  28. #undef THIS_FILE
  29. static char BASED_CODE THIS_FILE[] = __FILE__;
  30. #endif
  31. #define new DEBUG_NEW
  32. /* static */ LPOLESTR CIISMachine::_cszNodeName = _T("LM");
  33. /* static */ CComBSTR CIISMachine::_bstrYes;
  34. /* static */ CComBSTR CIISMachine::_bstrNo;
  35. /* static */ CComBSTR CIISMachine::_bstrVersionFmt;
  36. /* static */ BOOL CIISMachine::_fStaticsLoaded = FALSE;
  37. //
  38. // Define result view for machine objects
  39. //
  40. /* static */ int CIISMachine::_rgnLabels[COL_TOTAL] =
  41. {
  42. IDS_RESULT_COMPUTER_NAME,
  43. IDS_RESULT_COMPUTER_LOCAL,
  44. IDS_RESULT_COMPUTER_VERSION,
  45. IDS_RESULT_STATUS,
  46. };
  47. /* static */ int CIISMachine::_rgnWidths[COL_TOTAL] =
  48. {
  49. 200,
  50. 50,
  51. //100,
  52. 150,
  53. 200,
  54. };
  55. /* static */
  56. void
  57. CIISMachine::InitializeHeaders(
  58. IN LPHEADERCTRL lpHeader
  59. )
  60. /*++
  61. Routine Description:
  62. Initialize the result view headers for a machine object
  63. Arguments:
  64. LPHEADERCTRL lpHeader : Pointer to header control
  65. Return Value:
  66. None.
  67. --*/
  68. {
  69. BuildResultView(lpHeader, COL_TOTAL, _rgnLabels, _rgnWidths);
  70. if (!_fStaticsLoaded)
  71. {
  72. _fStaticsLoaded =
  73. _bstrYes.LoadString(IDS_YES) &&
  74. _bstrNo.LoadString(IDS_NO) &&
  75. _bstrVersionFmt.LoadString(IDS_VERSION_FMT);
  76. }
  77. }
  78. /* static */
  79. HRESULT
  80. CIISMachine::VerifyMachine(
  81. IN OUT CIISMachine *& pMachine
  82. )
  83. /*++
  84. Routine Description:
  85. Create the interface on the given machine object.
  86. Arguments:
  87. CIISMachine *& pMachine : Machine object
  88. BOOL fAskBeforeRedirecting
  89. Return Value:
  90. HRESULT
  91. Notes:
  92. THe CIISMachine object pass in may refer to the cluster master
  93. on return.
  94. --*/
  95. {
  96. CError err;
  97. if (pMachine)
  98. {
  99. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  100. CWaitCursor wait;
  101. //
  102. // Attempt to create the interface to ensure the machine
  103. // contains a metabase.
  104. //
  105. err = pMachine->CreateInterface(FALSE);
  106. }
  107. return err;
  108. }
  109. CIISMachine::CIISMachine(
  110. CComAuthInfo * pAuthInfo,
  111. CIISRoot * pRoot
  112. )
  113. : m_pInterface(NULL),
  114. m_bstrDisplayName(NULL),
  115. m_auth(pAuthInfo),
  116. m_pRootExt(pRoot),
  117. m_err(),
  118. //
  119. // By default we assume the password is entered.
  120. // If this machine object is constructed from the
  121. // cache, it will get reset by InitializeFromStream()
  122. //
  123. m_fPasswordEntered(TRUE),
  124. m_dwVersion(MAKELONG(5, 0)), // Assume as a default
  125. CIISMBNode(this, _cszNodeName)
  126. {
  127. //
  128. // Load one-liner error messages
  129. //
  130. SetErrorOverrides(m_err, TRUE);
  131. SetDisplayName();
  132. }
  133. CIISMachine::~CIISMachine()
  134. /*++
  135. Routine Description:
  136. Destructor
  137. Arguments:
  138. N/A
  139. Return Value:
  140. N/A
  141. --*/
  142. {
  143. if (m_bstrDisplayName)
  144. {
  145. ::SysFreeString(m_bstrDisplayName);
  146. }
  147. SAFE_DELETE(m_pInterface);
  148. }
  149. /* static */
  150. HRESULT
  151. CIISMachine::ReadFromStream(
  152. OUT IN IStream * pStream,
  153. OUT CIISMachine ** ppMachine
  154. )
  155. /*++
  156. Routine Description:
  157. Static helper function to allocate a new CIISMachine object read
  158. from the storage stream.
  159. Arguments:
  160. IStream * pStream : Stream to read from
  161. CIISMachine ** ppMachine : Returns CIISMachine object
  162. Return Value:
  163. HRESULT
  164. --*/
  165. {
  166. CComBSTR strMachine, strUser;
  167. ASSERT_WRITE_PTR(ppMachine);
  168. ASSERT_READ_WRITE_PTR(pStream);
  169. CError err;
  170. *ppMachine = NULL;
  171. do
  172. {
  173. err = strMachine.ReadFromStream(pStream);
  174. BREAK_ON_ERR_FAILURE(err);
  175. err = strUser.ReadFromStream(pStream);
  176. BREAK_ON_ERR_FAILURE(err);
  177. *ppMachine = new CIISMachine(CComAuthInfo(strMachine, strUser));
  178. if (!*ppMachine)
  179. {
  180. err = ERROR_NOT_ENOUGH_MEMORY;
  181. break;
  182. }
  183. err = (*ppMachine)->InitializeFromStream(pStream);
  184. }
  185. while(FALSE);
  186. return err;
  187. }
  188. HRESULT
  189. CIISMachine::WriteToStream(
  190. IN OUT IStream * pStgSave
  191. )
  192. /*++
  193. Routine Description:
  194. Write machine information to stream.
  195. Arguments:
  196. IStream * pStgSave : Open stream
  197. Return Value:
  198. HRESULT
  199. Notes:
  200. Be sure to keep this information in sync with CIISMachine::InitializeFromStream()
  201. --*/
  202. {
  203. ASSERT_READ_WRITE_PTR(pStgSave);
  204. CComBSTR bstrServerName(m_auth.QueryServerName());
  205. CComBSTR bstrUserName(m_auth.QueryUserName());
  206. CError err;
  207. ULONG cb;
  208. do
  209. {
  210. err = bstrServerName.WriteToStream(pStgSave);
  211. BREAK_ON_ERR_FAILURE(err);
  212. err = bstrUserName.WriteToStream(pStgSave);
  213. BREAK_ON_ERR_FAILURE(err);
  214. //
  215. // Now cache the dynamically-generated information, such
  216. // as version number, snapin status etc. This will be
  217. // displayed in the result view before the interface is
  218. // created.
  219. //
  220. err = pStgSave->Write(&m_dwVersion, sizeof(m_dwVersion), &cb);
  221. BREAK_ON_ERR_FAILURE(err);
  222. }
  223. while(FALSE);
  224. return err;
  225. }
  226. HRESULT
  227. CIISMachine::InitializeFromStream(
  228. IN IStream * pStream
  229. )
  230. /*++
  231. Routine Description:
  232. Read version number and other cached parameters that will
  233. be overridden at runtime when the interface is created.
  234. This is cached, because it's required before the interface
  235. is created.
  236. Arguments:
  237. IStream * pStream : Open stream
  238. Return Value:
  239. HRESULT
  240. Notes:
  241. Be sure to keep this information in sync with CIISMachine::WriteToStream()
  242. --*/
  243. {
  244. ASSERT_READ_PTR(pStream);
  245. CError err;
  246. ULONG cb;
  247. //
  248. // Passwords are never cached. IIS status will
  249. // always be verified when the actual interface
  250. // is created.
  251. //
  252. m_fPasswordEntered = FALSE;
  253. //
  254. // Version number
  255. //
  256. err = pStream->Read(&m_dwVersion, sizeof(m_dwVersion), &cb);
  257. return err;
  258. }
  259. void
  260. CIISMachine::SetDisplayName()
  261. /*++
  262. Routine Description:
  263. Create a special display name for this machine object if it's
  264. either the local machine, or
  265. --*/
  266. {
  267. CString fmt;
  268. if (IsLocal())
  269. {
  270. //
  271. // Use the local computer name, and not the name
  272. // that's on the server object, because that could
  273. // be and ip address or "localhost".
  274. //
  275. TCHAR szLocalServer[MAX_PATH + 1];
  276. DWORD dwSize = MAX_PATH;
  277. VERIFY(::GetComputerName(szLocalServer, &dwSize));
  278. fmt.Format(IDS_LOCAL_COMPUTER, szLocalServer);
  279. }
  280. else
  281. {
  282. //
  283. // No special display name necessary
  284. //
  285. m_bstrDisplayName = NULL;
  286. return;
  287. }
  288. m_bstrDisplayName = ::SysAllocStringLen(fmt, fmt.GetLength());
  289. TRACEEOLID("Machine display name: " << m_bstrDisplayName);
  290. }
  291. LPOLESTR
  292. CIISMachine::QueryDisplayName()
  293. /*++
  294. Routine Description:
  295. Get the display name for the machine/cluster object
  296. Arguments:
  297. None
  298. Return Value:
  299. Display Name
  300. --*/
  301. {
  302. if (m_pRootExt != NULL)
  303. return m_pRootExt->QueryDisplayName();
  304. else
  305. return m_bstrDisplayName ? m_bstrDisplayName : QueryServerName();
  306. }
  307. int
  308. CIISMachine::QueryImage() const
  309. /*++
  310. Routine Description:
  311. Return machine bitmap index appropriate for the current
  312. state of this machine object.
  313. Arguments:
  314. None
  315. Return Value:
  316. Bitmap index
  317. --*/
  318. {
  319. if (m_pRootExt != NULL)
  320. {
  321. return m_pRootExt->QueryImage();
  322. }
  323. else
  324. {
  325. if (m_err.Failed())
  326. {
  327. return iErrorMachine;
  328. }
  329. return IsLocal() ? iLocalMachine : iMachine;
  330. }
  331. }
  332. HRESULT
  333. CIISMachine::CreateInterface(
  334. IN BOOL fShowError
  335. )
  336. /*++
  337. Routine Description:
  338. Create the interface. If the interface is already created, recreate it.
  339. Arguments:
  340. BOOL fShowError : TRUE to display error messages
  341. Return Value:
  342. HRESULT
  343. Notes:
  344. This function is deliberately NOT called from the constructor for performance
  345. reasons.
  346. --*/
  347. {
  348. CError err;
  349. if (HasInterface())
  350. {
  351. //
  352. // Recreate the interface (this should re-use the impersonation)
  353. //
  354. TRACEEOLID("Warning: Rebinding existing interface.");
  355. err = m_pInterface->Regenerate();
  356. }
  357. else
  358. {
  359. //
  360. // Create new interface
  361. //
  362. m_pInterface = new CMetaKey(&m_auth);
  363. err = m_pInterface
  364. ? m_pInterface->QueryResult()
  365. : ERROR_NOT_ENOUGH_MEMORY;
  366. }
  367. if (err.Succeeded())
  368. {
  369. //
  370. // Load its display parameters
  371. //
  372. err = RefreshData();
  373. CMetabasePath path;
  374. err = DetermineIfAdministrator(
  375. m_pInterface,
  376. path,
  377. &m_fIsAdministrator
  378. );
  379. }
  380. if (err.Failed())
  381. {
  382. if (fShowError)
  383. {
  384. DisplayError(err);
  385. }
  386. //
  387. // Kill bogus interface
  388. //
  389. SAFE_DELETE(m_pInterface);
  390. }
  391. return err;
  392. }
  393. /* virtual */
  394. int
  395. CIISMachine::CompareScopeItem(
  396. IN CIISObject * pObject
  397. )
  398. /*++
  399. Routine Description:
  400. Compare against another CIISMachine object.
  401. Arguments:
  402. CIISObject * pObject : Object to compare against
  403. Return Value:
  404. 0 if the two objects are identical
  405. <0 if this object is less than pObject
  406. >0 if this object is greater than pObject
  407. --*/
  408. {
  409. ASSERT_READ_PTR(pObject);
  410. //
  411. // First criteria is object type
  412. //
  413. int n1 = QuerySortWeight();
  414. int n2 = pObject->QuerySortWeight();
  415. if (n1 != n2)
  416. {
  417. return n1 - n2;
  418. }
  419. //
  420. // pObject is a CIISMachine object (same sortweight)
  421. //
  422. CIISMachine * pMachine = (CIISMachine *)pObject;
  423. //
  424. // Next sort on local key (local sorts before non-local)
  425. //
  426. n1 = IsLocal() ? 0 : 1;
  427. n2 = pMachine->IsLocal() ? 0 : 1;
  428. if (n1 != n2)
  429. {
  430. return n1 - n2;
  431. }
  432. if (!n1 && !n2)
  433. {
  434. //
  435. // This is the local machine, even if the name is different
  436. //
  437. return 0;
  438. }
  439. //
  440. // Else sort on name.
  441. //
  442. return ::lstrcmpi(QueryServerName(), pMachine->QueryServerName());
  443. }
  444. BOOL
  445. CIISMachine::SetCacheDirty()
  446. /*++
  447. Routine Description:
  448. Set the cache as dirty
  449. Arguments:
  450. None
  451. Return Value:
  452. TRUE for success, FALSE if the cache was not found
  453. --*/
  454. {
  455. ASSERT(m_pRootExt == NULL);
  456. //
  457. // Cache is stored at the root object
  458. //
  459. CIISRoot * pRoot = GetRoot();
  460. ASSERT_PTR(pRoot);
  461. if (pRoot)
  462. {
  463. pRoot->m_scServers.SetDirty();
  464. return TRUE;
  465. }
  466. return FALSE;
  467. }
  468. int
  469. CIISMachine::ResolvePasswordFromCache()
  470. /*++
  471. Routine Description:
  472. Look through the machine cache for machines with the same username
  473. as this object. If they have a password entered, grab it.
  474. Arguments:
  475. None
  476. Return Value:
  477. TRUE if a machine with the same username was found whose password
  478. we stole. FALSE otherwise.
  479. --*/
  480. {
  481. BOOL fUpdated = FALSE;
  482. //
  483. // Doesn't make sense if this machine object doesn't use impersonation
  484. // or already has a password.
  485. //
  486. ASSERT(UsesImpersonation() && !PasswordEntered());
  487. CIISRoot * pRoot = GetRoot();
  488. ASSERT_PTR(pRoot);
  489. if (pRoot)
  490. {
  491. CIISMachine * pMachine = pRoot->m_scServers.GetFirst();
  492. while(pMachine)
  493. {
  494. if (pMachine->UsesImpersonation() && pMachine->PasswordEntered())
  495. {
  496. if (!::lstrcmpi(QueryUserName(), pMachine->QueryUserName()))
  497. {
  498. TRACEEOLID("Swiping cached password from " << pMachine->QueryServerName());
  499. StorePassword(pMachine->QueryPassword());
  500. ++fUpdated;
  501. break;
  502. }
  503. }
  504. pMachine = pRoot->m_scServers.GetNext();
  505. }
  506. }
  507. return fUpdated;
  508. }
  509. HRESULT
  510. CIISMachine::Impersonate(
  511. IN LPCTSTR szUserName,
  512. IN LPCTSTR szPassword
  513. )
  514. /*++
  515. Routine Description:
  516. Set and store proxy blanket security information. Store username/password
  517. for use by metaback and other interfaces.
  518. Arguments:
  519. LPCTSTR szUserName : Username (domain\username)
  520. LPCTSTR szPassword : Password
  521. Return Value:
  522. None
  523. --*/
  524. {
  525. ASSERT_READ_PTR(szUserName);
  526. CError err;
  527. if (m_pInterface)
  528. {
  529. //
  530. // Already have an interface created; Change the
  531. // the security blanket.
  532. //
  533. err = m_pInterface->ChangeProxyBlanket(szUserName, szPassword);
  534. }
  535. if (err.Succeeded())
  536. {
  537. //
  538. // Store new username/password
  539. //
  540. m_auth.SetImpersonation(szUserName, szPassword);
  541. m_fPasswordEntered = TRUE;
  542. }
  543. return err;
  544. }
  545. void
  546. CIISMachine::RemoveImpersonation()
  547. /*++
  548. Routine Description:
  549. Remove impersonation parameters. Destroy any existing interface.
  550. Arguments:
  551. None
  552. Return Value:
  553. N/A
  554. --*/
  555. {
  556. m_auth.RemoveImpersonation();
  557. m_fPasswordEntered = FALSE;
  558. SAFE_DELETE(m_pInterface);
  559. }
  560. void
  561. CIISMachine::StorePassword(
  562. IN LPCTSTR szPassword
  563. )
  564. /*++
  565. Routine Description:
  566. Store password.
  567. Arguments:
  568. LPCTSTR szPassword : Password
  569. Return Value:
  570. None
  571. --*/
  572. {
  573. ASSERT_READ_PTR(szPassword);
  574. m_auth.StorePassword(szPassword);
  575. m_fPasswordEntered = TRUE;
  576. }
  577. BOOL
  578. CIISMachine::ResolveCredentials()
  579. /*++
  580. Routine Description:
  581. If this machine object uses impersonation, but hasn't entered a password
  582. yet, check to see if there are any other machines in the cache with the
  583. same username and grab its password. If not, prompt the user for it.
  584. Arguments:
  585. None
  586. Return Value:
  587. TRUE if a password was entered. FALSE otherwise.
  588. --*/
  589. {
  590. BOOL fPasswordEntered = FALSE;
  591. if (UsesImpersonation() && !PasswordEntered())
  592. {
  593. //
  594. // Attempt to find the password from the cache
  595. //
  596. if (!ResolvePasswordFromCache())
  597. {
  598. //
  599. // Didn't find the password in the cache. Prompt
  600. // the user for it.
  601. //
  602. CLoginDlg dlg(LDLG_ENTER_PASS, this);
  603. if (dlg.DoModal() == IDOK)
  604. {
  605. fPasswordEntered = TRUE;
  606. if (dlg.UserNameChanged())
  607. {
  608. //
  609. // User name has changed -- remember to
  610. // save the machine cache later.
  611. //
  612. SetCacheDirty();
  613. }
  614. }
  615. else
  616. {
  617. //
  618. // Pressing cancel on this dialog means the user
  619. // wants to stop using impersonation.
  620. //
  621. RemoveImpersonation();
  622. SetCacheDirty();
  623. }
  624. }
  625. }
  626. return fPasswordEntered;
  627. }
  628. BOOL
  629. CIISMachine::HandleAccessDenied(
  630. IN OUT CError & err
  631. )
  632. /*++
  633. Routine Description:
  634. After calling interface method, pass the error object to this function
  635. to handle the access denied case. If the error is access denied,
  636. give the user a chance to change credentials. Since we assume an
  637. attempt has been made to create an interface at least -- the interface
  638. will be recreated with the new credentials.
  639. Arguments:
  640. CError & err : Error object. Checked for ACCESS_DENIED on entry,
  641. will contain new error code on exit if the interface
  642. was recreated.
  643. Return Value:
  644. TRUE if new credentials were applied
  645. --*/
  646. {
  647. BOOL fPasswordEntered = FALSE;
  648. //
  649. // If access denied occurs here -- give another chance
  650. // at entering the password.
  651. //
  652. if (err.Win32Error() == ERROR_ACCESS_DENIED)
  653. {
  654. CLoginDlg dlg(LDLG_ACCESS_DENIED, this);
  655. if (dlg.DoModal() == IDOK)
  656. {
  657. fPasswordEntered = TRUE;
  658. err.Reset();
  659. if (!HasInterface())
  660. {
  661. //
  662. // If we already had an interface, the login dialog
  663. // will have applied the new security blanket.
  664. // If we didn't have an interface, it needs to be
  665. // recreated with the new security blanket.
  666. //
  667. CWaitCursor wait;
  668. err = CreateInterface(FALSE);
  669. }
  670. }
  671. }
  672. return fPasswordEntered;
  673. }
  674. HRESULT
  675. CIISMachine::CheckCapabilities()
  676. /*++
  677. Routine Description:
  678. Load the capabilities information for this server.
  679. Arguments:
  680. None
  681. Return Value:
  682. HRESULT
  683. --*/
  684. {
  685. HRESULT hr = S_OK;
  686. //
  687. // Fetch capability bits and version numbers.
  688. //
  689. CString strMDInfo;
  690. CMetabasePath::GetServiceInfoPath(_T(""), strMDInfo);
  691. CServerCapabilities sc(m_pInterface, strMDInfo);
  692. hr = sc.LoadData();
  693. if (SUCCEEDED(hr))
  694. {
  695. DWORD dwVersion = sc.QueryMajorVersion();
  696. if (dwVersion)
  697. {
  698. m_dwVersion = dwVersion | (sc.QueryMinorVersion() << SIZE_IN_BITS(WORD));
  699. }
  700. m_fCanAddInstance = sc.HasMultipleSites();
  701. m_fHas10ConnectionsLimit = sc.Has10ConnectionLimit();
  702. }
  703. return hr;
  704. }
  705. /* virtual */
  706. HRESULT
  707. CIISMachine::RefreshData()
  708. /*++
  709. Routine Description:
  710. Refresh relevant configuration data required for display.
  711. Arguments:
  712. None
  713. Return Value:
  714. HRESULT
  715. --*/
  716. {
  717. //
  718. // Check capability and version information.
  719. //
  720. CError err(CheckCapabilities());
  721. SetDisplayName();
  722. return err;
  723. }
  724. /* virtual */
  725. void
  726. CIISMachine::SetInterfaceError(
  727. IN HRESULT hr
  728. )
  729. /*++
  730. Routine Description:
  731. Set the interface error. If different from current error,
  732. change the display icon
  733. Arguments:
  734. HRESULT hr : Error code (S_OK is acceptable)
  735. Return Value:
  736. None
  737. --*/
  738. {
  739. if (m_err.HResult() != hr)
  740. {
  741. //
  742. // Change to error/machine icon for the parent machine.
  743. //
  744. m_err = hr;
  745. RefreshDisplay();
  746. }
  747. }
  748. /* virtual */
  749. HRESULT
  750. CIISMachine::BuildMetaPath(
  751. OUT CComBSTR & bstrPath
  752. ) const
  753. /*++
  754. Routine Description:
  755. Recursively build up the metabase path from the current node
  756. and its parents
  757. Arguments:
  758. CComBSTR & bstrPath : Returns metabase path
  759. Return Value:
  760. HRESULT
  761. --*/
  762. {
  763. //
  764. // This starts off the path
  765. //
  766. bstrPath.Append(_cszSeparator);
  767. bstrPath.Append(QueryNodeName());
  768. return S_OK;
  769. }
  770. /* virtual */
  771. HRESULT
  772. CIISMachine::BuildURL(
  773. OUT CComBSTR & bstrURL
  774. ) const
  775. /*++
  776. Routine Description:
  777. Recursively build up the URL from the current node
  778. and its parents. The URL built up from a machine node
  779. doesn't make a lot of sense, but for want of anything better,
  780. this will bring up the default web site.
  781. Arguments:
  782. CComBSTR & bstrURL : Returns URL
  783. Return Value:
  784. HRESULT
  785. --*/
  786. {
  787. CString strOwner;
  788. if (IsLocal())
  789. {
  790. //
  791. // Security reasons restrict this to "localhost" oftentimes
  792. //
  793. strOwner = _bstrLocalHost;
  794. }
  795. else
  796. {
  797. LPOLESTR lpOwner = QueryMachineName();
  798. strOwner = PURE_COMPUTER_NAME(lpOwner);
  799. }
  800. //
  801. // An URL on the machine node is built in isolation.
  802. //
  803. // ISSUE: Is this really a desirable URL? Maybe we should
  804. // use something else.
  805. //
  806. bstrURL = _T("http://");
  807. bstrURL.Append(strOwner);
  808. return S_OK;
  809. }
  810. /* virtual */
  811. HRESULT
  812. CIISMachine::CreatePropertyPages(
  813. IN LPPROPERTYSHEETCALLBACK lpProvider,
  814. IN LONG_PTR handle,
  815. IN IUnknown * pUnk,
  816. IN DATA_OBJECT_TYPES type
  817. )
  818. /*++
  819. Routine Description:
  820. Create the property pages for the given object
  821. Arguments:
  822. LPPROPERTYSHEETCALLBACK lpProvider : Provider
  823. LONG_PTR handle : Handle.
  824. IUnknown * pUnk,
  825. DATA_OBJECT_TYPES type
  826. Return Value:
  827. HRESULT
  828. --*/
  829. {
  830. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  831. CComBSTR bstrPath;
  832. //
  833. // ISSUE: What to do with m_err? This might be
  834. // a bad machine object in the first place. Aborting
  835. // when the machine object has an error code isn't
  836. // such a bad solution here. If the error condition
  837. // no longer exists, a refresh will cure.
  838. //
  839. if (m_err.Failed())
  840. {
  841. m_err.MessageBox();
  842. return m_err;
  843. }
  844. CError err(BuildMetaPath(bstrPath));
  845. if (err.Succeeded())
  846. {
  847. CIISMachineSheet * pSheet = new CIISMachineSheet(
  848. QueryAuthInfo(),
  849. bstrPath,
  850. GetMainWindow(),
  851. (LPARAM)this,
  852. handle
  853. );
  854. if (pSheet)
  855. {
  856. pSheet->SetModeless();
  857. //
  858. // Add pages
  859. //
  860. err = AddMMCPage(lpProvider, new CIISMachinePage(pSheet));
  861. }
  862. else
  863. {
  864. err = ERROR_NOT_ENOUGH_MEMORY;
  865. }
  866. }
  867. err.MessageBoxOnFailure();
  868. return err;
  869. }
  870. /* virtual */
  871. HRESULT
  872. CIISMachine::EnumerateScopePane(
  873. IN HSCOPEITEM hParent
  874. )
  875. /*++
  876. Routine Description:
  877. Enumerate scope child items.
  878. Arguments:
  879. HSCOPEITEM hParent : Parent console handle
  880. Return Value:
  881. HRESULT
  882. --*/
  883. {
  884. ASSERT_PTR(_lpConsoleNameSpace);
  885. ASSERT(m_hScopeItem == hParent);
  886. CError err;
  887. CString str;
  888. CIISService * pService, * pWebService = NULL;
  889. CWaitCursor wait;
  890. CMetaEnumerator * pme = NULL;
  891. if (IsExpanded())
  892. {
  893. //
  894. // Verify user credentials are satisfactorily resolved.
  895. // Machines objects are loaded from the cache without a
  896. // password, so the function below will ask for it.
  897. //
  898. ResolveCredentials();
  899. wait.Restore();
  900. BOOL fShouldRefresh = !HasInterface();
  901. err = AssureInterfaceCreated(FALSE);
  902. if (err.Succeeded())
  903. {
  904. //
  905. // Creation of the interface will have loaded display parameters, which
  906. // may differ from the cached parameters.
  907. //
  908. if (fShouldRefresh)
  909. {
  910. RefreshDisplay();
  911. }
  912. err = CreateEnumerator(pme);
  913. }
  914. //
  915. // Only check for acces denied now, because virtually any idiot
  916. // is allowed to create a metabase interface, but will get the
  917. // access denied when calling a method, such as enumeration.
  918. //
  919. if (HandleAccessDenied(err))
  920. {
  921. wait.Restore();
  922. //
  923. // Credentials were changed. Try again (interface should be
  924. // created already)
  925. //
  926. SAFE_DELETE(pme);
  927. if (err.Succeeded())
  928. {
  929. err = CreateEnumerator(pme);
  930. }
  931. }
  932. //
  933. // Enumerate administerable services from the metabase
  934. //
  935. while (err.Succeeded())
  936. {
  937. err = pme->Next(str);
  938. if (err.Succeeded())
  939. {
  940. TRACEEOLID("Enumerating node: " << str);
  941. pService = new CIISService(this, str);
  942. if (!pService)
  943. {
  944. err = ERROR_NOT_ENOUGH_MEMORY;
  945. break;
  946. }
  947. //
  948. // See if we care
  949. //
  950. if (pService->IsManagedService())
  951. {
  952. err = pService->AddToScopePane(hParent);
  953. if (err.Succeeded())
  954. {
  955. if (0 == lstrcmpi(pService->GetNodeName(), SZ_MBN_WEB))
  956. {
  957. pWebService = pService;
  958. }
  959. }
  960. }
  961. else
  962. {
  963. //
  964. // Node is not a managed service, or we're managing the
  965. // cluster and the service is not clustered.
  966. //
  967. delete pService;
  968. }
  969. }
  970. }
  971. if (err.Win32Error() == ERROR_NO_MORE_ITEMS)
  972. {
  973. err.Reset();
  974. }
  975. // If we are encountered web service, we should add
  976. // Application Pools container before this service
  977. //
  978. if (err.Succeeded() && pWebService != NULL)
  979. {
  980. // We could have iis5 machine which doesn't have any pools
  981. //
  982. CMetaKey mk(pme, _T("/LM/W3SVC/AppPools"));
  983. if (mk.Succeeded())
  984. {
  985. CAppPoolsContainer * pPools = new CAppPoolsContainer(
  986. this, pWebService);
  987. if (!pPools)
  988. {
  989. err = ERROR_NOT_ENOUGH_MEMORY;
  990. goto Fail;
  991. }
  992. // Insert pools container before Web Services node
  993. err = pPools->AddToScopePane(
  994. pWebService->QueryScopeItem(), FALSE, TRUE);
  995. }
  996. }
  997. Fail:
  998. if (err.Failed())
  999. {
  1000. DisplayError(err);
  1001. }
  1002. SetInterfaceError(err);
  1003. //
  1004. // Clean up
  1005. //
  1006. SAFE_DELETE(pme);
  1007. }
  1008. return err;
  1009. }
  1010. /* virtual */
  1011. HRESULT
  1012. CIISMachine::RemoveScopeItem()
  1013. /*++
  1014. Routine Description:
  1015. Remove the machine from the scope view and the cache.
  1016. Arguments:
  1017. None
  1018. Return Value:
  1019. HRESULT
  1020. --*/
  1021. {
  1022. ASSERT(m_pRootExt == NULL);
  1023. //
  1024. // Find out root before deleting scope node
  1025. //
  1026. CIISRoot * pRoot = GetRoot();
  1027. ASSERT_PTR(pRoot);
  1028. //
  1029. // Remove from the tree
  1030. //
  1031. HRESULT hr = CIISMBNode::RemoveScopeItem();
  1032. if (SUCCEEDED(hr) && pRoot)
  1033. {
  1034. pRoot->m_scServers.Remove(this);
  1035. }
  1036. return hr;
  1037. }
  1038. /* virtual */
  1039. LPOLESTR
  1040. CIISMachine::GetResultPaneColInfo(int nCol)
  1041. /*++
  1042. Routine Description:
  1043. Return result pane string for the given column number
  1044. Arguments:
  1045. int nCol : Column number
  1046. Return Value:
  1047. String
  1048. --*/
  1049. {
  1050. if (m_pRootExt != NULL)
  1051. {
  1052. return m_pRootExt->GetResultPaneColInfo(nCol);
  1053. }
  1054. ASSERT(_fStaticsLoaded);
  1055. switch(nCol)
  1056. {
  1057. case COL_NAME:
  1058. return QueryDisplayName();
  1059. case COL_LOCAL:
  1060. return IsLocal() ? _bstrYes : _bstrNo;
  1061. case COL_VERSION:
  1062. {
  1063. CString str;
  1064. str.Format(_bstrVersionFmt, QueryMajorVersion(), QueryMinorVersion());
  1065. _bstrResult = str;
  1066. }
  1067. return _bstrResult;
  1068. case COL_STATUS:
  1069. {
  1070. if (m_err.Succeeded())
  1071. {
  1072. return OLESTR("");
  1073. }
  1074. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  1075. return m_err;
  1076. }
  1077. }
  1078. ASSERT_MSG("Bad column number");
  1079. return OLESTR("");
  1080. }
  1081. /* virtual */
  1082. HRESULT
  1083. CIISMachine::AddMenuItems(
  1084. IN LPCONTEXTMENUCALLBACK lpContextMenuCallback,
  1085. IN OUT long * pInsertionAllowed,
  1086. IN DATA_OBJECT_TYPES type
  1087. )
  1088. /*++
  1089. Routine Description:
  1090. Add menu items to the context menu
  1091. Arguments:
  1092. LPCONTEXTMENUCALLBACK lpContextMenuCallback : Context menu callback
  1093. long * pInsertionAllowed : Insertion allowed
  1094. DATA_OBJECT_TYPES type : Object type
  1095. Return Value:
  1096. HRESULT
  1097. --*/
  1098. {
  1099. ASSERT_READ_PTR(lpContextMenuCallback);
  1100. //
  1101. // Add base menu items
  1102. //
  1103. HRESULT hr = CIISObject::AddMenuItems(
  1104. lpContextMenuCallback,
  1105. pInsertionAllowed,
  1106. type
  1107. );
  1108. if (SUCCEEDED(hr))
  1109. {
  1110. if (IsAdministrator() && (*pInsertionAllowed & CCM_INSERTIONALLOWED_TASK) != 0)
  1111. {
  1112. AddMenuItemByCommand(lpContextMenuCallback, IDM_METABACKREST);
  1113. AddMenuItemByCommand(lpContextMenuCallback, IDM_SHUTDOWN);
  1114. }
  1115. #if 0
  1116. if (CanAddInstance())
  1117. {
  1118. ASSERT(pInsertionAllowed != NULL);
  1119. if ((*pInsertionAllowed & CCM_INSERTIONALLOWED_NEW) != 0)
  1120. {
  1121. #define ADD_SERVICE_MENU(x)\
  1122. if (!bSepAdded)\
  1123. {\
  1124. AddMenuSeparator(lpContextMenuCallback);\
  1125. bSepAdded = TRUE;\
  1126. }\
  1127. AddMenuItemByCommand(lpContextMenuCallback, (x))
  1128. HSCOPEITEM hChild = NULL, hCurrent;
  1129. LONG_PTR cookie;
  1130. BOOL bSepAdded = FALSE;
  1131. hr = _lpConsoleNameSpace->GetChildItem(QueryScopeItem(), &hChild, &cookie);
  1132. while (SUCCEEDED(hr) && hChild != NULL)
  1133. {
  1134. CIISMBNode * pNode = (CIISMBNode *)cookie;
  1135. ASSERT(pNode != NULL);
  1136. if (lstrcmpi(pNode->GetNodeName(), SZ_MBN_FTP) == 0)
  1137. {
  1138. ADD_SERVICE_MENU(IDM_NEW_FTP_SITE);
  1139. }
  1140. else if (lstrcmpi(pNode->GetNodeName(), SZ_MBN_WEB) == 0)
  1141. {
  1142. ADD_SERVICE_MENU(IDM_NEW_WEB_SITE);
  1143. }
  1144. else if (lstrcmpi(pNode->GetNodeName(), SZ_MBN_APP_POOLS) == 0)
  1145. {
  1146. ADD_SERVICE_MENU(IDM_NEW_APP_POOL);
  1147. }
  1148. hCurrent = hChild;
  1149. hr = _lpConsoleNameSpace->GetNextItem(hCurrent, &hChild, &cookie);
  1150. }
  1151. }
  1152. }
  1153. #endif
  1154. //
  1155. // CODEWORK: Add new instance commands for each of the services
  1156. // keeping in mind which ones are installed and all.
  1157. // add that info to the table, remembering that this
  1158. // is per service.
  1159. //
  1160. }
  1161. return hr;
  1162. }
  1163. #if 0
  1164. // BUGBUG: It should be quite different -> we don't know in advance
  1165. // which service is this site for
  1166. HRESULT
  1167. CIISMachine::InsertNewInstance(DWORD inst)
  1168. {
  1169. CError err;
  1170. // Now we should insert and select this new site
  1171. TCHAR buf[16];
  1172. CIISSite * pSite = new CIISSite(m_pOwner, this, _itot(inst, buf, 10));
  1173. if (pSite != NULL)
  1174. {
  1175. // If machine is not expanded we will get error and no effect
  1176. if (!IsExpanded())
  1177. {
  1178. SelectScopeItem();
  1179. IConsoleNameSpace2 * pConsole
  1180. = (IConsoleNameSpace2 *)GetConsoleNameSpace();
  1181. pConsole->Expand(QueryScopeItem());
  1182. }
  1183. // Now we should find the relevant service node, and inset this one under
  1184. // this node
  1185. err = pSite->AddToScopePaneSorted(QueryScopeItem(), FALSE);
  1186. if (err.Succeeded())
  1187. {
  1188. VERIFY(SUCCEEDED(pSite->SelectScopeItem()));
  1189. }
  1190. }
  1191. else
  1192. {
  1193. err = ERROR_NOT_ENOUGH_MEMORY;
  1194. }
  1195. return err;
  1196. }
  1197. #endif
  1198. HRESULT
  1199. CIISMachine::Command(
  1200. IN long lCommandID,
  1201. IN CSnapInObjectRootBase * pObj,
  1202. IN DATA_OBJECT_TYPES type
  1203. )
  1204. /*++
  1205. Routine Description:
  1206. Handle command from context menu.
  1207. Arguments:
  1208. long lCommandID : Command ID
  1209. CSnapInObjectRootBase * pObj : Base object
  1210. DATA_OBJECT_TYPES type : Data object type
  1211. Return Value:
  1212. HRESULT
  1213. --*/
  1214. {
  1215. HRESULT hr = S_OK;
  1216. switch (lCommandID)
  1217. {
  1218. case IDM_DISCONNECT:
  1219. hr = OnDisconnect();
  1220. break;
  1221. case IDM_METABACKREST:
  1222. hr = OnMetaBackRest();
  1223. break;
  1224. case IDM_SHUTDOWN:
  1225. hr = OnShutDown();
  1226. break;
  1227. #if 0
  1228. case IDM_NEW_FTP_SITE:
  1229. if (SUCCEEDED(hr = AddFTPSite(pObj, type, &inst)))
  1230. {
  1231. hr = InsertNewInstance(inst);
  1232. }
  1233. break;
  1234. case IDM_NEW_WEB_SITE:
  1235. if (SUCCEEDED(hr = AddWebSite(pObj, type, &inst)))
  1236. {
  1237. hr = InsertNewInstance(inst);
  1238. }
  1239. break;
  1240. case IDM_NEW_APP_POOL:
  1241. hr = AddAppPool(pObj, type);
  1242. break;
  1243. #endif
  1244. //
  1245. // Pass on to base class
  1246. //
  1247. default:
  1248. hr = CIISMBNode::Command(lCommandID, pObj, type);
  1249. }
  1250. return hr;
  1251. }
  1252. HRESULT
  1253. CIISMachine::OnDisconnect()
  1254. /*++
  1255. Routine Description:
  1256. Disconnect this machine. Confirm user choice.
  1257. Arguments:
  1258. None
  1259. Return Value:
  1260. HRESULT
  1261. --*/
  1262. {
  1263. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  1264. CString str;
  1265. str.Format(IDS_CONFIRM_DISCONNECT, QueryDisplayName());
  1266. if (NoYesMessageBox(str))
  1267. {
  1268. return RemoveScopeItem();
  1269. }
  1270. return S_OK;
  1271. }
  1272. HRESULT
  1273. CIISMachine::OnMetaBackRest()
  1274. /*++
  1275. Routine Description:
  1276. Backup/Restore the metabase
  1277. Arguments:
  1278. None
  1279. Return Value:
  1280. HRESULT
  1281. --*/
  1282. {
  1283. CError err;
  1284. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  1285. //
  1286. // Verify user credentials are satisfactorily resolved.
  1287. // Machines objects are loaded from the cache without a
  1288. // password, so the function below will ask for it.
  1289. //
  1290. ResolveCredentials();
  1291. CBackupDlg dlg(this, GetMainWindow());
  1292. dlg.DoModal();
  1293. if (dlg.HasChangedMetabase())
  1294. {
  1295. //
  1296. // Refresh and re-enumerate child objects
  1297. //
  1298. err = Refresh(TRUE);
  1299. }
  1300. return err;
  1301. }
  1302. HRESULT
  1303. CIISMachine::OnShutDown()
  1304. /*++
  1305. Routine Description:
  1306. Bring up the IIS shutdown dialog. If the services on the remote
  1307. machine are restarted, the metabase interface should be recreated.
  1308. Arguments:
  1309. None
  1310. Return Value:
  1311. HRESULT
  1312. --*/
  1313. {
  1314. CError err;
  1315. AFX_MANAGE_STATE(::AfxGetStaticModuleState());
  1316. //
  1317. // Verify user credentials are satisfactorily resolved.
  1318. // Machines objects are loaded from the cache without a
  1319. // password, so the function below will ask for it.
  1320. //
  1321. ResolveCredentials();
  1322. CIISShutdownDlg dlg(this, GetMainWindow());
  1323. dlg.DoModal();
  1324. if (dlg.ServicesWereRestarted())
  1325. {
  1326. //
  1327. // Rebind all metabase handles on this server
  1328. //
  1329. err = CreateInterface(TRUE);
  1330. //
  1331. // Now do a refresh on the computer node. Since we've forced
  1332. // the rebinding already, we should not get the disconnect warning.
  1333. //
  1334. if (err.Succeeded())
  1335. {
  1336. err = Refresh(TRUE);
  1337. }
  1338. }
  1339. return err;
  1340. }