windows-nt/Source/XPSP1/NT/inetsrv/iis/ui/admin/fscfg/usersess.cpp
2020-09-26 16:20:57 +08:00

1017 lines
17 KiB
C++

/*++
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;
}