|
|
/*++
Copyright (c) 1994-1998 Microsoft Corporation
Module Name :
usersess.cpp
Abstract:
FTP User Sessions Dialog
Author:
Ronald Meijer (ronaldm)
Project:
Internet Services Manager
Revision History:
--*/
//
// Include Files
//
#include "stdafx.h"
#include "fscfg.h"
#include "usersess.h"
#include <lmerr.h>
#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__; #endif
//
// Registry key name for this dialog
//
const TCHAR g_szRegKey[] = _T("User Sessions");
//
// User Sessions Listbox Column Definitions
//
static const ODL_COLUMN_DEF_EX BASED_CODE g_aColumns[] = { // ==================================================================================================
// Weight Label Sort Helper Function
// ==================================================================================================
{ 2, IDS_CONNECTED_USERS, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByName }, { 1, IDS_FROM, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByHostAddress }, { 1, IDS_TIME, (CObjectPlus::PCOBJPLUS_ORDER_FUNC)&CFtpUserInfo::OrderByTime }, };
#define NUM_COLUMNS (sizeof(g_aColumns) / sizeof(g_aColumns[0]))
CFtpUserInfo::CFtpUserInfo( IN LPIIS_USER_INFO_1 lpUserInfo ) /*++
Routine Description:
Constructor
Arguments:
LPIIS_USER_INFO_1 lpUserInfo : User info structure
Return Value:
N/A
--*/ : m_idUser(lpUserInfo->idUser), m_strUser(lpUserInfo->pszUser), m_fAnonymous(lpUserInfo->fAnonymous), // Network Byte Order
// ||
// \/
m_iaHost(lpUserInfo->inetHost, TRUE), m_tConnect(lpUserInfo->tConnect) { }
int CFtpUserInfo::OrderByName( IN const CObjectPlus * pobFtpUser ) const /*++
Routine Description:
Sorting helper function to sort by user name. The CObjectPlus pointer really refers to another CFtpUserInfo object
Arguments:
LPIIS_USER_INFO_1 lpUserInfo : User info structure
Return Value:
Sort return code (-1, 0, +1)
--*/ { const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser; ASSERT(pob != NULL);
return ::lstrcmpi(QueryUserName(), pob->QueryUserName()); }
int CFtpUserInfo::OrderByTime( IN const CObjectPlus * pobFtpUser ) const /*++
Routine Description:
Sorting helper function to sort by user connect time. The CObjectPlus pointer really refers to another CFtpUserInfo object
Arguments:
LPIIS_USER_INFO_1 lpUserInfo : User info structure
Return Value:
Sort return code (-1, 0, +1)
--*/ { const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser; ASSERT(pob != NULL);
return QueryConnectTime() > pob->QueryConnectTime() ? +1 : QueryConnectTime() == pob->QueryConnectTime() ? 0 : -1; }
int CFtpUserInfo::OrderByHostAddress( IN const CObjectPlus * pobFtpUser ) const /*++
Routine Description:
Sorting helper function to sort by host address. The CObjectPlus pointer really refers to another CFtpUserInfo object
Arguments:
LPIIS_USER_INFO_1 lpUserInfo : User info structure
Return Value:
Sort return code (-1, 0, +1)
--*/ { const CFtpUserInfo * pob = (CFtpUserInfo *)pobFtpUser; ASSERT(pob != NULL);
return QueryHostAddress().CompareItem(pob->QueryHostAddress()); }
IMPLEMENT_DYNAMIC(CFtpUsersListBox, CHeaderListBox);
//
// User listbox bitmaps
//
enum { BMP_USER = 0, BMP_ANONYMOUS,
//
// Don't move this one
//
BMP_TOTAL };
const int CFtpUsersListBox::nBitmaps = BMP_TOTAL;
CFtpUsersListBox::CFtpUsersListBox() /*++
Routine Description:
Constructor
Arguments:
None
Return Value:
N/A
--*/ : m_strTimeSep(_T(":")), CHeaderListBox(HLS_DEFAULT, g_szRegKey) { //
// Get intl time seperator
//
VERIFY(::GetLocaleInfo( ::GetUserDefaultLCID(), LOCALE_STIME, m_strTimeSep.GetBuffer(10), 10 )); }
void CFtpUsersListBox::DrawItemEx( IN CRMCListBoxDrawStruct & ds ) /*++
Routine Description:
Draw item. This is called from the CRMCListBox base class
Arguments:
CRMCListBoxDrawStruct & ds : Drawing structure
Return Value:
None
--*/ { CFtpUserInfo * pFTPUser = (CFtpUserInfo *)ds.m_ItemData; ASSERT(pFTPUser != NULL);
//
// Display a user bitmap
//
DrawBitmap(ds, 0, pFTPUser->QueryAnonymous() ? BMP_ANONYMOUS : BMP_USER); ColumnText(ds, 0, TRUE, pFTPUser->QueryUserName()); ColumnText(ds, 1, FALSE, pFTPUser->QueryHostAddress());
DWORD dwTime = pFTPUser->QueryConnectTime(); DWORD dwHours = dwTime / (60L * 60L); DWORD dwMinutes = (dwTime / 60L) % 60L; DWORD dwSeconds = dwTime % 60L;
CString strTime;
strTime.Format( _T("%d%s%02d%s%02d"), dwHours, (LPCTSTR)m_strTimeSep, dwMinutes, (LPCTSTR)m_strTimeSep, dwSeconds);
ColumnText(ds, 2, FALSE, strTime); }
/* virtual */ BOOL CFtpUsersListBox::Initialize() /*++
Routine Description:
Initialize the listbox. Insert the columns as requested, and lay them out appropriately
Arguments:
None
Return Value:
TRUE for succesful initialisation, FALSE otherwise
--*/ { if (!CHeaderListBox::Initialize()) { return FALSE; }
//
// Build all columns
//
for (int nCol = 0; nCol < NUM_COLUMNS; ++nCol) { InsertColumn( nCol, g_aColumns[nCol].cd.nWeight, g_aColumns[nCol].cd.nLabelID ); }
//
// Try to set the widths from the stored registry value,
// otherwise distribute according to column weights specified
//
if (!SetWidthsFromReg()) { DistributeColumns(); }
return TRUE; }
CUserSessionsDlg::CUserSessionsDlg( IN LPCTSTR lpstrServerName, IN DWORD dwInstance, IN CWnd * pParent OPTIONAL ) /*++
Routine Description:
Constructor for FTP user sessions dialog
Arguments:
LPCTSTR lpstrServerName : Server name to connect to CWnd * pParent : Pointer to parent window
Return Value:
N/A
--*/ : m_list_Users(), m_ListBoxRes( IDB_USERS, m_list_Users.nBitmaps ), m_oblFtpUsers(), m_strServerName(lpstrServerName), m_nSortColumn(0), m_dwInstance(dwInstance), CDialog(CUserSessionsDlg::IDD, pParent) { //{{AFX_DATA_INIT(CUserSessionsDlg)
//}}AFX_DATA_INIT
m_list_Users.AttachResources(&m_ListBoxRes); VERIFY(m_strTotalConnected.LoadString(IDS_USERS_TOTAL)); }
void CUserSessionsDlg::DoDataExchange( IN CDataExchange * pDX ) /*++
Routine Description:
Initialise/Store control data
Arguments:
CDataExchange * pDX - DDX/DDV control structure
Return Value:
None
--*/ { CDialog::DoDataExchange(pDX); //{{AFX_DATA_MAP(CUserSessionsDlg)
DDX_Control(pDX, IDC_STATIC_NUM_CONNECTED, m_static_Total); DDX_Control(pDX, IDC_BUTTON_DISCONNECT_ALL, m_button_DisconnectAll); DDX_Control(pDX, IDC_BUTTON_DISCONNECT, m_button_Disconnect); //}}AFX_DATA_MAP
DDX_Control(pDX, IDC_LIST_USERS, m_list_Users); }
//
// Message Map
//
BEGIN_MESSAGE_MAP(CUserSessionsDlg, CDialog) //{{AFX_MSG_MAP(CUserSessionsDlg)
ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, OnButtonDisconnect) ON_BN_CLICKED(IDC_BUTTON_DISCONNECT_ALL, OnButtonDisconnectAll) ON_BN_CLICKED(IDC_BUTTON_REFRESH, OnButtonRefresh) ON_LBN_SELCHANGE(IDC_LIST_USERS, OnSelchangeListUsers) //}}AFX_MSG_MAP
ON_NOTIFY_RANGE(HDN_ITEMCLICK, 0, 0xFFFF, OnHeaderItemClick)
END_MESSAGE_MAP()
DWORD CUserSessionsDlg::SortUsersList() /*++
Routine Description:
Sort the list of ftp users on the current sorting key
Arguments:
None
Return Value:
ERROR return code
--*/ { ASSERT(m_nSortColumn >= 0 && m_nSortColumn < NUM_COLUMNS);
BeginWaitCursor(); DWORD err = m_oblFtpUsers.Sort( (CObjectPlus::PCOBJPLUS_ORDER_FUNC)g_aColumns[m_nSortColumn].pSortFn); EndWaitCursor();
return err; }
HRESULT CUserSessionsDlg::BuildUserList() /*++
Routine Description:
Call the FtpEnum api and build the list of currently connected users.
Arguments:
None
Return Value:
ERROR return code
--*/ { CError err; LPIIS_USER_INFO_1 lpUserInfo = NULL; DWORD dwCount = 0L;
m_oblFtpUsers.RemoveAll();
BeginWaitCursor(); err = ::IISEnumerateUsers(TWSTRREF((LPCTSTR)m_strServerName), 1, INET_FTP_SVC_ID, m_dwInstance, &dwCount, (LPBYTE *)&lpUserInfo ); EndWaitCursor();
TRACEEOLID("IISEnumerateUsers returned " << err);
if (err.Failed()) { return err; }
try { for (DWORD i = 0; i < dwCount; ++i) { m_oblFtpUsers.AddTail(new CFtpUserInfo(lpUserInfo++)); } } catch(CMemoryException * e) { err = ERROR_NOT_ENOUGH_MEMORY; e->Delete(); }
SortUsersList();
//
// Free up the data allocated by IISEnumerateUsers
//
if( lpUserInfo ) { for( DWORD i = 0; i < dwCount; ++i ) { if( lpUserInfo[i].pszUser ) { MIDL_user_free( lpUserInfo[i].pszUser ); } }
MIDL_user_free( lpUserInfo ); }
return err; }
HRESULT CUserSessionsDlg::DisconnectUser( IN CFtpUserInfo * pUserInfo ) /*++
Routine Description:
Disconnect a single user
Arguments:
CFtpUserInfo * pUserInfo : User to disconnect
Return Value:
ERROR return code
--*/ { CError err(::IISDisconnectUser(TWSTRREF((LPCTSTR)m_strServerName), INET_FTP_SVC_ID, m_dwInstance, pUserInfo->QueryUserID() ));
if (err.Win32Error() == NERR_UserNotFound) { //
// As long as he's gone now, that's alright
//
err.Reset(); }
return err; }
void CUserSessionsDlg::UpdateTotalCount() /*++
Routine Description:
Update the count of total users
Arguments:
None
Return Value:
None
--*/ { CString str; str.Format(m_strTotalConnected, m_oblFtpUsers.GetCount() );
m_static_Total.SetWindowText(str); }
void CUserSessionsDlg::FillListBox( IN CFtpUserInfo * pSelection OPTIONAL ) /*++
Routine Description:
Show the users in the listbox
Arguments:
CFtpUserInfo * pSelection : Item to be selected or NULL
Return Value:
None
--*/ { CObListIter obli(m_oblFtpUsers); const CFtpUserInfo * pUserEntry = NULL;
m_list_Users.SetRedraw(FALSE); m_list_Users.ResetContent(); int cItems = 0;
for ( /**/ ; pUserEntry = (CFtpUserInfo *)obli.Next(); ++cItems) { m_list_Users.AddItem(pUserEntry); }
if (pSelection) { //
// Select the desired entry
//
m_list_Users.SelectItem(pSelection); }
m_list_Users.SetRedraw(TRUE);
//
// Update the count text on the dialog
//
UpdateTotalCount(); }
HRESULT CUserSessionsDlg::RefreshUsersList() /*++
Routine Description:
Rebuild the user list
Arguments:
None
Return Value:
Error return code
--*/ { TEMP_ERROR_OVERRIDE(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA); TEMP_ERROR_OVERRIDE(RPC_S_SERVER_UNAVAILABLE, IDS_SERVICE_NOT_STARTED); TEMP_ERROR_OVERRIDE(RPC_S_UNKNOWN_IF, IDS_SERVICE_NOT_STARTED); TEMP_ERROR_OVERRIDE(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE);
CError err; err = BuildUserList(); if (!err.MessageBoxOnFailure()) { FillListBox(); SetControlStates(); }
return err; }
void CUserSessionsDlg::SetControlStates() /*++
Routine Description:
Set the connect/disconnect buttons depending on the selection state in the listbox.
Arguments:
None
Return Value:
None
--*/ { m_button_Disconnect.EnableWindow(m_list_Users.GetSelCount() > 0); m_button_DisconnectAll.EnableWindow(m_list_Users.GetCount() > 0); }
//
// Message Handlers
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void CUserSessionsDlg::OnButtonDisconnect() /*++
Routine Description:
'Disconnect User' button has been pressed. Disconnect the currently selected user.
Arguments:
None
Return Value:
None
--*/ { //
// Ask for confirmation
//
if (!NoYesMessageBox(IDS_CONFIRM_DISCONNECT_USER)) { //
// Changed his mind
//
return; }
CError err; m_list_Users.SetRedraw(FALSE); CWaitCursor wait; CFtpUserInfo * pUserEntry; int nSel = 0; BOOL fProblems = FALSE; while((pUserEntry = GetNextSelectedItem(&nSel)) != NULL) { err = DisconnectUser(pUserEntry); if (err.Failed()) { ++fProblems;
if (err.MessageBoxFormat( IDS_DISCONNECT_ERR, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2, NO_HELP_CONTEXT, (LPCTSTR)pUserEntry->QueryUserName() ) == IDYES) { //
// Continue trying to delete
//
++nSel; continue; } else { break; } } m_oblFtpUsers.RemoveIndex(nSel); m_list_Users.DeleteString(nSel);
//
// Don't advance counter to account for offset
//
}
m_list_Users.SetRedraw(TRUE); UpdateTotalCount(); SetControlStates();
if (!fProblems) { //
// Ensure button not disabled
//
GetDlgItem(IDC_BUTTON_REFRESH)->SetFocus(); } }
void CUserSessionsDlg::OnButtonDisconnectAll() /*++
Routine Description:
'Disconnect All Users' button has been pressed. Disconnect all users
Arguments:
None
Return Value:
None
--*/ { //
// Ask for confirmation
//
if (!NoYesMessageBox(IDS_CONFIRM_DISCONNECT_ALL)) { //
// Changed his mind
//
return; } CObListIter obli(m_oblFtpUsers); CFtpUserInfo * pUserEntry;
m_list_Users.SetRedraw(FALSE); CWaitCursor wait; int cItems = 0;
CError err; int nSel = 0; BOOL fProblems = FALSE; for ( /**/; pUserEntry = (CFtpUserInfo *)obli.Next(); ++cItems) { err = DisconnectUser(pUserEntry); if (err.Failed()) { ++fProblems;
if (err.MessageBoxFormat( IDS_DISCONNECT_ERR, MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2, NO_HELP_CONTEXT, (LPCTSTR)pUserEntry->QueryUserName() ) == IDYES) { //
// Continue trying to delete
//
++nSel; continue; } else { break; } }
m_oblFtpUsers.RemoveIndex(nSel); m_list_Users.DeleteString(nSel); }
m_list_Users.SetRedraw(TRUE); UpdateTotalCount(); SetControlStates();
if (!fProblems) { //
// Ensure button not disabled
//
GetDlgItem(IDC_BUTTON_REFRESH)->SetFocus(); } }
void CUserSessionsDlg::OnButtonRefresh() /*++
Routine Description:
'Refresh' Button has been pressed. Refresh the user list
Arguments:
None
Return Value:
None
--*/ { RefreshUsersList(); }
void CUserSessionsDlg::OnSelchangeListUsers() /*++
Routine Description:
Respond to a change in selection in the user listbox
Arguments:
None
Return Value:
None
--*/ { SetControlStates(); }
void CUserSessionsDlg::OnHeaderItemClick( IN UINT nID, IN NMHDR * pNMHDR, OUT LRESULT * plResult ) /*++
Routine Description:
Header item has been clicked in the listbox. Change the sort key as appropriate.
Arguments:
None
Return Value:
None
--*/ { HD_NOTIFY * pNotify = (HD_NOTIFY *)pNMHDR; TRACEEOLID("Header Button clicked.");
//
// Can't press a button out of range, surely...
//
ASSERT(pNotify->iItem < m_list_Users.QueryNumColumns()); int nOldSortColumn = m_nSortColumn; m_nSortColumn = pNotify->iItem;
if(m_nSortColumn != nOldSortColumn) { //
// Rebuild the list
//
SortUsersList(); CFtpUserInfo * pSelector = GetSelectedListItem(); FillListBox(pSelector); SetControlStates(); }
//
// Message Fully Handled
//
*plResult = 0; }
BOOL CUserSessionsDlg::OnInitDialog() /*++
Routine Description:
WM_INITDIALOG handler. Initialize the dialog.
Arguments:
None.
Return Value:
TRUE if no focus is to be set automatically, FALSE if the focus is already set.
--*/ { CDialog::OnInitDialog();
m_list_Users.Initialize();
if (RefreshUsersList() != ERROR_SUCCESS) { EndDialog(IDCANCEL); return FALSE; }
return TRUE; }
|