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.

1016 lines
17 KiB

  1. /*++
  2. Copyright (c) 1994-1998 Microsoft Corporation
  3. Module Name :
  4. usersess.cpp
  5. Abstract:
  6. FTP User Sessions Dialog
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Project:
  10. Internet Services Manager
  11. Revision History:
  12. --*/
  13. //
  14. // Include Files
  15. //
  16. #include "stdafx.h"
  17. #include "fscfg.h"
  18. #include "usersess.h"
  19. #include <lmerr.h>
  20. #ifdef _DEBUG
  21. #undef THIS_FILE
  22. static char BASED_CODE THIS_FILE[] = __FILE__;
  23. #endif
  24. //
  25. // Registry key name for this dialog
  26. //
  27. const TCHAR g_szRegKey[] = _T("User Sessions");
  28. //
  29. // User Sessions Listbox Column Definitions
  30. //
  31. static const ODL_COLUMN_DEF_EX BASED_CODE g_aColumns[] =
  32. {
  33. // ==================================================================================================
  34. // Weight Label Sort Helper Function
  35. // ==================================================================================================
  36. { 2, IDS_CONNECTED_USERS, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByName },
  37. { 1, IDS_FROM, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByHostAddress },
  38. { 1, IDS_TIME, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByTime },
  39. };
  40. #define NUM_COLUMNS (sizeof(g_aColumns) / sizeof(g_aColumns[0]))
  41. CFtpUserInfo::CFtpUserInfo(
  42. IN LPIIS_USER_INFO_1 lpUserInfo
  43. )
  44. /*++
  45. Routine Description:
  46. Constructor
  47. Arguments:
  48. LPIIS_USER_INFO_1 lpUserInfo : User info structure
  49. Return Value:
  50. N/A
  51. --*/
  52. : m_idUser(lpUserInfo->idUser),
  53. m_strUser(lpUserInfo->pszUser),
  54. m_fAnonymous(lpUserInfo->fAnonymous),
  55. // Network Byte Order
  56. // ||
  57. // \/
  58. m_iaHost(lpUserInfo->inetHost, TRUE),
  59. m_tConnect(lpUserInfo->tConnect)
  60. {
  61. }
  62. int
  63. CFtpUserInfo::OrderByName(
  64. IN const CObjectPlus * pobFtpUser
  65. ) const
  66. /*++
  67. Routine Description:
  68. Sorting helper function to sort by user name. The CObjectPlus pointer
  69. really refers to another CFtpUserInfo object
  70. Arguments:
  71. LPIIS_USER_INFO_1 lpUserInfo : User info structure
  72. Return Value:
  73. Sort return code (-1, 0, +1)
  74. --*/
  75. {
  76. const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser;
  77. ASSERT(pob != NULL);
  78. return ::lstrcmpi(QueryUserName(), pob->QueryUserName());
  79. }
  80. int
  81. CFtpUserInfo::OrderByTime(
  82. IN const CObjectPlus * pobFtpUser
  83. ) const
  84. /*++
  85. Routine Description:
  86. Sorting helper function to sort by user connect time. The CObjectPlus
  87. pointer really refers to another CFtpUserInfo object
  88. Arguments:
  89. LPIIS_USER_INFO_1 lpUserInfo : User info structure
  90. Return Value:
  91. Sort return code (-1, 0, +1)
  92. --*/
  93. {
  94. const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser;
  95. ASSERT(pob != NULL);
  96. return QueryConnectTime() > pob->QueryConnectTime()
  97. ? +1
  98. : QueryConnectTime() == pob->QueryConnectTime()
  99. ? 0
  100. : -1;
  101. }
  102. int
  103. CFtpUserInfo::OrderByHostAddress(
  104. IN const CObjectPlus * pobFtpUser
  105. ) const
  106. /*++
  107. Routine Description:
  108. Sorting helper function to sort by host address. The CObjectPlus
  109. pointer really refers to another CFtpUserInfo object
  110. Arguments:
  111. LPIIS_USER_INFO_1 lpUserInfo : User info structure
  112. Return Value:
  113. Sort return code (-1, 0, +1)
  114. --*/
  115. {
  116. const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser;
  117. ASSERT(pob != NULL);
  118. return QueryHostAddress().CompareItem(pob->QueryHostAddress());
  119. }
  120. IMPLEMENT_DYNAMIC(CFtpUsersListBox, CHeaderListBox);
  121. //
  122. // User listbox bitmaps
  123. //
  124. enum
  125. {
  126. BMP_USER = 0,
  127. BMP_ANONYMOUS,
  128. //
  129. // Don't move this one
  130. //
  131. BMP_TOTAL
  132. };
  133. const int CFtpUsersListBox::nBitmaps = BMP_TOTAL;
  134. CFtpUsersListBox::CFtpUsersListBox()
  135. /*++
  136. Routine Description:
  137. Constructor
  138. Arguments:
  139. None
  140. Return Value:
  141. N/A
  142. --*/
  143. : m_strTimeSep(_T(":")),
  144. CHeaderListBox(HLS_DEFAULT, g_szRegKey)
  145. {
  146. //
  147. // Get intl time seperator
  148. //
  149. VERIFY(::GetLocaleInfo(
  150. ::GetUserDefaultLCID(),
  151. LOCALE_STIME,
  152. m_strTimeSep.GetBuffer(10),
  153. 10
  154. ));
  155. }
  156. void
  157. CFtpUsersListBox::DrawItemEx(
  158. IN CRMCListBoxDrawStruct & ds
  159. )
  160. /*++
  161. Routine Description:
  162. Draw item. This is called from the CRMCListBox base class
  163. Arguments:
  164. CRMCListBoxDrawStruct & ds : Drawing structure
  165. Return Value:
  166. None
  167. --*/
  168. {
  169. CFtpUserInfo * pFTPUser = (CFtpUserInfo *)ds.m_ItemData;
  170. ASSERT(pFTPUser != NULL);
  171. //
  172. // Display a user bitmap
  173. //
  174. DrawBitmap(ds, 0, pFTPUser->QueryAnonymous() ? BMP_ANONYMOUS : BMP_USER);
  175. ColumnText(ds, 0, TRUE, pFTPUser->QueryUserName());
  176. ColumnText(ds, 1, FALSE, pFTPUser->QueryHostAddress());
  177. DWORD dwTime = pFTPUser->QueryConnectTime();
  178. DWORD dwHours = dwTime / (60L * 60L);
  179. DWORD dwMinutes = (dwTime / 60L) % 60L;
  180. DWORD dwSeconds = dwTime % 60L;
  181. CString strTime;
  182. strTime.Format(
  183. _T("%d%s%02d%s%02d"),
  184. dwHours, (LPCTSTR)m_strTimeSep,
  185. dwMinutes, (LPCTSTR)m_strTimeSep,
  186. dwSeconds);
  187. ColumnText(ds, 2, FALSE, strTime);
  188. }
  189. /* virtual */
  190. BOOL
  191. CFtpUsersListBox::Initialize()
  192. /*++
  193. Routine Description:
  194. Initialize the listbox. Insert the columns as requested, and lay
  195. them out appropriately
  196. Arguments:
  197. None
  198. Return Value:
  199. TRUE for succesful initialisation, FALSE otherwise
  200. --*/
  201. {
  202. if (!CHeaderListBox::Initialize())
  203. {
  204. return FALSE;
  205. }
  206. //
  207. // Build all columns
  208. //
  209. for (int nCol = 0; nCol < NUM_COLUMNS; ++nCol)
  210. {
  211. InsertColumn(
  212. nCol,
  213. g_aColumns[nCol].cd.nWeight,
  214. g_aColumns[nCol].cd.nLabelID
  215. );
  216. }
  217. //
  218. // Try to set the widths from the stored registry value,
  219. // otherwise distribute according to column weights specified
  220. //
  221. if (!SetWidthsFromReg())
  222. {
  223. DistributeColumns();
  224. }
  225. return TRUE;
  226. }
  227. CUserSessionsDlg::CUserSessionsDlg(
  228. IN LPCTSTR lpstrServerName,
  229. IN DWORD dwInstance,
  230. IN CWnd * pParent OPTIONAL
  231. )
  232. /*++
  233. Routine Description:
  234. Constructor for FTP user sessions dialog
  235. Arguments:
  236. LPCTSTR lpstrServerName : Server name to connect to
  237. CWnd * pParent : Pointer to parent window
  238. Return Value:
  239. N/A
  240. --*/
  241. : m_list_Users(),
  242. m_ListBoxRes(
  243. IDB_USERS,
  244. m_list_Users.nBitmaps
  245. ),
  246. m_oblFtpUsers(),
  247. m_strServerName(lpstrServerName),
  248. m_nSortColumn(0),
  249. m_dwInstance(dwInstance),
  250. CDialog(CUserSessionsDlg::IDD, pParent)
  251. {
  252. //{{AFX_DATA_INIT(CUserSessionsDlg)
  253. //}}AFX_DATA_INIT
  254. m_list_Users.AttachResources(&m_ListBoxRes);
  255. VERIFY(m_strTotalConnected.LoadString(IDS_USERS_TOTAL));
  256. }
  257. void
  258. CUserSessionsDlg::DoDataExchange(
  259. IN CDataExchange * pDX
  260. )
  261. /*++
  262. Routine Description:
  263. Initialise/Store control data
  264. Arguments:
  265. CDataExchange * pDX - DDX/DDV control structure
  266. Return Value:
  267. None
  268. --*/
  269. {
  270. CDialog::DoDataExchange(pDX);
  271. //{{AFX_DATA_MAP(CUserSessionsDlg)
  272. DDX_Control(pDX, IDC_STATIC_NUM_CONNECTED, m_static_Total);
  273. DDX_Control(pDX, IDC_BUTTON_DISCONNECT_ALL, m_button_DisconnectAll);
  274. DDX_Control(pDX, IDC_BUTTON_DISCONNECT, m_button_Disconnect);
  275. //}}AFX_DATA_MAP
  276. DDX_Control(pDX, IDC_LIST_USERS, m_list_Users);
  277. }
  278. //
  279. // Message Map
  280. //
  281. BEGIN_MESSAGE_MAP(CUserSessionsDlg, CDialog)
  282. //{{AFX_MSG_MAP(CUserSessionsDlg)
  283. ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, OnButtonDisconnect)
  284. ON_BN_CLICKED(IDC_BUTTON_DISCONNECT_ALL, OnButtonDisconnectAll)
  285. ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh)
  286. ON_LBN_SELCHANGE(IDC_LIST_USERS, OnSelchangeListUsers)
  287. //}}AFX_MSG_MAP
  288. ON_NOTIFY_RANGE(HDN_ITEMCLICK, 0, 0xFFFF, OnHeaderItemClick)
  289. END_MESSAGE_MAP()
  290. DWORD
  291. CUserSessionsDlg::SortUsersList()
  292. /*++
  293. Routine Description:
  294. Sort the list of ftp users on the current sorting key
  295. Arguments:
  296. None
  297. Return Value:
  298. ERROR return code
  299. --*/
  300. {
  301. ASSERT(m_nSortColumn >= 0 && m_nSortColumn < NUM_COLUMNS);
  302. BeginWaitCursor();
  303. DWORD err = m_oblFtpUsers.Sort(
  304. (CObjectPlus::PCOBJPLUS_ORDER_FUNC)g_aColumns[m_nSortColumn].pSortFn);
  305. EndWaitCursor();
  306. return err;
  307. }
  308. HRESULT
  309. CUserSessionsDlg::BuildUserList()
  310. /*++
  311. Routine Description:
  312. Call the FtpEnum api and build the list of currently connected users.
  313. Arguments:
  314. None
  315. Return Value:
  316. ERROR return code
  317. --*/
  318. {
  319. CError err;
  320. LPIIS_USER_INFO_1 lpUserInfo = NULL;
  321. DWORD dwCount = 0L;
  322. m_oblFtpUsers.RemoveAll();
  323. BeginWaitCursor();
  324. err = ::IISEnumerateUsers(TWSTRREF((LPCTSTR)m_strServerName),
  325. 1,
  326. INET_FTP_SVC_ID,
  327. m_dwInstance,
  328. &dwCount,
  329. (LPBYTE *)&lpUserInfo
  330. );
  331. EndWaitCursor();
  332. TRACEEOLID("IISEnumerateUsers returned " << err);
  333. if (err.Failed())
  334. {
  335. return err;
  336. }
  337. try
  338. {
  339. for (DWORD i = 0; i < dwCount; ++i)
  340. {
  341. m_oblFtpUsers.AddTail(new CFtpUserInfo(lpUserInfo++));
  342. }
  343. }
  344. catch(CMemoryException * e)
  345. {
  346. err = ERROR_NOT_ENOUGH_MEMORY;
  347. e->Delete();
  348. }
  349. SortUsersList();
  350. //
  351. // Free up the data allocated by IISEnumerateUsers
  352. //
  353. if( lpUserInfo )
  354. {
  355. for( DWORD i = 0; i < dwCount; ++i )
  356. {
  357. if( lpUserInfo[i].pszUser )
  358. {
  359. MIDL_user_free( lpUserInfo[i].pszUser );
  360. }
  361. }
  362. MIDL_user_free( lpUserInfo );
  363. }
  364. return err;
  365. }
  366. HRESULT
  367. CUserSessionsDlg::DisconnectUser(
  368. IN CFtpUserInfo * pUserInfo
  369. )
  370. /*++
  371. Routine Description:
  372. Disconnect a single user
  373. Arguments:
  374. CFtpUserInfo * pUserInfo : User to disconnect
  375. Return Value:
  376. ERROR return code
  377. --*/
  378. {
  379. CError err(::IISDisconnectUser(TWSTRREF((LPCTSTR)m_strServerName),
  380. INET_FTP_SVC_ID,
  381. m_dwInstance,
  382. pUserInfo->QueryUserID()
  383. ));
  384. if (err.Win32Error() == NERR_UserNotFound)
  385. {
  386. //
  387. // As long as he's gone now, that's alright
  388. //
  389. err.Reset();
  390. }
  391. return err;
  392. }
  393. void
  394. CUserSessionsDlg::UpdateTotalCount()
  395. /*++
  396. Routine Description:
  397. Update the count of total users
  398. Arguments:
  399. None
  400. Return Value:
  401. None
  402. --*/
  403. {
  404. CString str;
  405. str.Format(m_strTotalConnected, m_oblFtpUsers.GetCount() );
  406. m_static_Total.SetWindowText(str);
  407. }
  408. void
  409. CUserSessionsDlg::FillListBox(
  410. IN CFtpUserInfo * pSelection OPTIONAL
  411. )
  412. /*++
  413. Routine Description:
  414. Show the users in the listbox
  415. Arguments:
  416. CFtpUserInfo * pSelection : Item to be selected or NULL
  417. Return Value:
  418. None
  419. --*/
  420. {
  421. CObListIter obli(m_oblFtpUsers);
  422. const CFtpUserInfo * pUserEntry = NULL;
  423. m_list_Users.SetRedraw(FALSE);
  424. m_list_Users.ResetContent();
  425. int cItems = 0;
  426. for ( /**/ ; pUserEntry = (CFtpUserInfo *)obli.Next(); ++cItems)
  427. {
  428. m_list_Users.AddItem(pUserEntry);
  429. }
  430. if (pSelection)
  431. {
  432. //
  433. // Select the desired entry
  434. //
  435. m_list_Users.SelectItem(pSelection);
  436. }
  437. m_list_Users.SetRedraw(TRUE);
  438. //
  439. // Update the count text on the dialog
  440. //
  441. UpdateTotalCount();
  442. }
  443. HRESULT
  444. CUserSessionsDlg::RefreshUsersList()
  445. /*++
  446. Routine Description:
  447. Rebuild the user list
  448. Arguments:
  449. None
  450. Return Value:
  451. Error return code
  452. --*/
  453. {
  454. TEMP_ERROR_OVERRIDE(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA);
  455. TEMP_ERROR_OVERRIDE(RPC_S_SERVER_UNAVAILABLE, IDS_SERVICE_NOT_STARTED);
  456. TEMP_ERROR_OVERRIDE(RPC_S_UNKNOWN_IF, IDS_SERVICE_NOT_STARTED);
  457. TEMP_ERROR_OVERRIDE(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE);
  458. CError err;
  459. err = BuildUserList();
  460. if (!err.MessageBoxOnFailure())
  461. {
  462. FillListBox();
  463. SetControlStates();
  464. }
  465. return err;
  466. }
  467. void
  468. CUserSessionsDlg::SetControlStates()
  469. /*++
  470. Routine Description:
  471. Set the connect/disconnect buttons depending on the selection state
  472. in the listbox.
  473. Arguments:
  474. None
  475. Return Value:
  476. None
  477. --*/
  478. {
  479. m_button_Disconnect.EnableWindow(m_list_Users.GetSelCount() > 0);
  480. m_button_DisconnectAll.EnableWindow(m_list_Users.GetCount() > 0);
  481. }
  482. //
  483. // Message Handlers
  484. //
  485. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  486. void
  487. CUserSessionsDlg::OnButtonDisconnect()
  488. /*++
  489. Routine Description:
  490. 'Disconnect User' button has been pressed. Disconnect the currently
  491. selected user.
  492. Arguments:
  493. None
  494. Return Value:
  495. None
  496. --*/
  497. {
  498. //
  499. // Ask for confirmation
  500. //
  501. if (!NoYesMessageBox(IDS_CONFIRM_DISCONNECT_USER))
  502. {
  503. //
  504. // Changed his mind
  505. //
  506. return;
  507. }
  508. CError err;
  509. m_list_Users.SetRedraw(FALSE);
  510. CWaitCursor wait;
  511. CFtpUserInfo * pUserEntry;
  512. int nSel = 0;
  513. BOOL fProblems = FALSE;
  514. while((pUserEntry = GetNextSelectedItem(&nSel)) != NULL)
  515. {
  516. err = DisconnectUser(pUserEntry);
  517. if (err.Failed())
  518. {
  519. ++fProblems;
  520. if (err.MessageBoxFormat(
  521. IDS_DISCONNECT_ERR,
  522. MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2,
  523. NO_HELP_CONTEXT,
  524. (LPCTSTR)pUserEntry->QueryUserName()
  525. ) == IDYES)
  526. {
  527. //
  528. // Continue trying to delete
  529. //
  530. ++nSel;
  531. continue;
  532. }
  533. else
  534. {
  535. break;
  536. }
  537. }
  538. m_oblFtpUsers.RemoveIndex(nSel);
  539. m_list_Users.DeleteString(nSel);
  540. //
  541. // Don't advance counter to account for offset
  542. //
  543. }
  544. m_list_Users.SetRedraw(TRUE);
  545. UpdateTotalCount();
  546. SetControlStates();
  547. if (!fProblems)
  548. {
  549. //
  550. // Ensure button not disabled
  551. //
  552. GetDlgItem(IDC_BUTTON_REFRESH)->SetFocus();
  553. }
  554. }
  555. void
  556. CUserSessionsDlg::OnButtonDisconnectAll()
  557. /*++
  558. Routine Description:
  559. 'Disconnect All Users' button has been pressed. Disconnect all users
  560. Arguments:
  561. None
  562. Return Value:
  563. None
  564. --*/
  565. {
  566. //
  567. // Ask for confirmation
  568. //
  569. if (!NoYesMessageBox(IDS_CONFIRM_DISCONNECT_ALL))
  570. {
  571. //
  572. // Changed his mind
  573. //
  574. return;
  575. }
  576. CObListIter obli(m_oblFtpUsers);
  577. CFtpUserInfo * pUserEntry;
  578. m_list_Users.SetRedraw(FALSE);
  579. CWaitCursor wait;
  580. int cItems = 0;
  581. CError err;
  582. int nSel = 0;
  583. BOOL fProblems = FALSE;
  584. for ( /**/; pUserEntry = (CFtpUserInfo *)obli.Next(); ++cItems)
  585. {
  586. err = DisconnectUser(pUserEntry);
  587. if (err.Failed())
  588. {
  589. ++fProblems;
  590. if (err.MessageBoxFormat(
  591. IDS_DISCONNECT_ERR,
  592. MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2,
  593. NO_HELP_CONTEXT,
  594. (LPCTSTR)pUserEntry->QueryUserName()
  595. ) == IDYES)
  596. {
  597. //
  598. // Continue trying to delete
  599. //
  600. ++nSel;
  601. continue;
  602. }
  603. else
  604. {
  605. break;
  606. }
  607. }
  608. m_oblFtpUsers.RemoveIndex(nSel);
  609. m_list_Users.DeleteString(nSel);
  610. }
  611. m_list_Users.SetRedraw(TRUE);
  612. UpdateTotalCount();
  613. SetControlStates();
  614. if (!fProblems)
  615. {
  616. //
  617. // Ensure button not disabled
  618. //
  619. GetDlgItem(IDC_BUTTON_REFRESH)->SetFocus();
  620. }
  621. }
  622. void
  623. CUserSessionsDlg::OnButtonRefresh()
  624. /*++
  625. Routine Description:
  626. 'Refresh' Button has been pressed. Refresh the user list
  627. Arguments:
  628. None
  629. Return Value:
  630. None
  631. --*/
  632. {
  633. RefreshUsersList();
  634. }
  635. void
  636. CUserSessionsDlg::OnSelchangeListUsers()
  637. /*++
  638. Routine Description:
  639. Respond to a change in selection in the user listbox
  640. Arguments:
  641. None
  642. Return Value:
  643. None
  644. --*/
  645. {
  646. SetControlStates();
  647. }
  648. void
  649. CUserSessionsDlg::OnHeaderItemClick(
  650. IN UINT nID,
  651. IN NMHDR * pNMHDR,
  652. OUT LRESULT * plResult
  653. )
  654. /*++
  655. Routine Description:
  656. Header item has been clicked in the listbox. Change the sort key
  657. as appropriate.
  658. Arguments:
  659. None
  660. Return Value:
  661. None
  662. --*/
  663. {
  664. HD_NOTIFY * pNotify = (HD_NOTIFY *)pNMHDR;
  665. TRACEEOLID("Header Button clicked.");
  666. //
  667. // Can't press a button out of range, surely...
  668. //
  669. ASSERT(pNotify->iItem < m_list_Users.QueryNumColumns());
  670. int nOldSortColumn = m_nSortColumn;
  671. m_nSortColumn = pNotify->iItem;
  672. if(m_nSortColumn != nOldSortColumn)
  673. {
  674. //
  675. // Rebuild the list
  676. //
  677. SortUsersList();
  678. CFtpUserInfo * pSelector = GetSelectedListItem();
  679. FillListBox(pSelector);
  680. SetControlStates();
  681. }
  682. //
  683. // Message Fully Handled
  684. //
  685. *plResult = 0;
  686. }
  687. BOOL
  688. CUserSessionsDlg::OnInitDialog()
  689. /*++
  690. Routine Description:
  691. WM_INITDIALOG handler. Initialize the dialog.
  692. Arguments:
  693. None.
  694. Return Value:
  695. TRUE if no focus is to be set automatically, FALSE if the focus
  696. is already set.
  697. --*/
  698. {
  699. CDialog::OnInitDialog();
  700. m_list_Users.Initialize();
  701. if (RefreshUsersList() != ERROR_SUCCESS)
  702. {
  703. EndDialog(IDCANCEL);
  704. return FALSE;
  705. }
  706. return TRUE;
  707. }