|
|
/*++
Copyright (c) 1994-2001 Microsoft Corporation
Module Name : usersess.cpp
Abstract: FTP User Sessions Dialog
Author: Ronald Meijer (ronaldm) Sergei Antonov (sergeia)
Project: Internet Services Manager
Revision History:
--*/
//
// Include Files
//
#include "stdafx.h"
#include "resource.h"
#include "common.h"
#include "inetprop.h"
#include "ftpsht.h"
#include "fservic.h"
#include "usersess.h"
#include <lmerr.h>
HRESULT ImpersonateUser(LPCTSTR pszUserName, LPCTSTR pszDomain, LPCTSTR pszPassword, HANDLE *pCurImpToken, HANDLE *pLoggedOnUserToken); HRESULT UnImpersonateUser(HANDLE hSavedImpToken, HANDLE hLoggedOnUser);
#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(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( 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( 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( 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() : 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
//
HINSTANCE hInst = AfxGetResourceHandle(); for (int nCol = 0; nCol < NUM_COLUMNS; ++nCol) { InsertColumn( nCol, g_aColumns[nCol].cd.nWeight, g_aColumns[nCol].cd.nLabelID, hInst ); }
//
// Try to set the widths from the stored registry value,
// otherwise distribute according to column weights specified
//
// if (!SetWidthsFromReg())
// {
DistributeColumns(); // }
return TRUE; }
CUserSessionsDlg::CUserSessionsDlg( LPCTSTR lpstrServerName, DWORD dwInstance, LPCTSTR pAdminName, LPCTSTR pAdminPassword, CWnd * pParent ) /*++
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_strAdminName(pAdminName), m_strAdminPassword(pAdminPassword), m_nSortColumn(0), m_dwInstance(dwInstance), m_hImpToken(INVALID_HANDLE_VALUE), m_hLogToken(INVALID_HANDLE_VALUE), 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(CDataExchange * pDX) { 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( (LPTSTR)(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();
return err; }
HRESULT CUserSessionsDlg::DisconnectUser(CFtpUserInfo * pUserInfo) /*++
Routine Description:
Disconnect a single user
Arguments:
CFtpUserInfo * pUserInfo : User to disconnect
Return Value:
ERROR return code
--*/ { CError err(::IISDisconnectUser( (LPTSTR)(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(CFtpUserInfo * pSelection) /*++
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
--*/ { CError err;
//
// Add some friendly error message overrides
//
err.AddOverride(EPT_S_NOT_REGISTERED, IDS_ERR_RPC_NA); err.AddOverride(RPC_S_SERVER_UNAVAILABLE, IDS_FTP_SERVICE_NOT_STARTED); err.AddOverride(RPC_S_UNKNOWN_IF, IDS_FTP_SERVICE_NOT_STARTED); err.AddOverride(RPC_S_PROCNUM_OUT_OF_RANGE, IDS_ERR_INTERFACE);
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();
if (!m_strAdminName.IsEmpty()) { CError err = ImpersonateUser(m_strAdminName, _T(""), m_strAdminPassword, &m_hImpToken, &m_hLogToken); }
m_list_Users.Initialize();
if (RefreshUsersList() != ERROR_SUCCESS) { EndDialog(IDCANCEL); return FALSE; }
return TRUE; }
void CUserSessionsDlg::OnDestroy() { if (m_hImpToken != INVALID_HANDLE_VALUE || m_hLogToken != INVALID_HANDLE_VALUE) { UnImpersonateUser(m_hImpToken, m_hLogToken); } }
HRESULT ImpersonateUser(LPCTSTR pszUserName, LPCTSTR pszDomain, LPCTSTR pszPassword, HANDLE *pCurImpToken, HANDLE *pLoggedOnUserToken) { HRESULT hr = S_OK;
ASSERT(pCurImpToken); ASSERT(pLoggedOnUserToken);
*pCurImpToken = INVALID_HANDLE_VALUE; *pLoggedOnUserToken = INVALID_HANDLE_VALUE;
// logon the user. This creates an primary impersonation
// token. If this fails, an error will be returned.
if (!LogonUser((LPTSTR)pszUserName, (LPTSTR)pszDomain, (LPTSTR)pszPassword, LOGON32_LOGON_BATCH, LOGON32_PROVIDER_DEFAULT, pLoggedOnUserToken)) {
hr = HRESULT_FROM_WIN32(GetLastError()); } // get the current impersonation token. If an error occurs,
// that is OK. This just means that no impersonation on the
// thread is occurring, so there is no need to do the RevertToSelf.
else if (OpenThreadToken( GetCurrentThread(), TOKEN_READ | TOKEN_IMPERSONATE, TRUE, pCurImpToken)) {
RevertToSelf(); }
// if everything's been successful so far, than call
// ImpersonateLoggedOnUser with the token created above.
if (SUCCEEDED(hr)) {
if (!ImpersonateLoggedOnUser(*pLoggedOnUserToken)) {
hr = HRESULT_FROM_WIN32(GetLastError()); } }
// if there were failures, clean up any tokens that we created
// or hold.
if (FAILED(hr)) {
// cleanup the LogonUser token
if (*pLoggedOnUserToken != INVALID_HANDLE_VALUE) { CloseHandle(*pLoggedOnUserToken); *pLoggedOnUserToken = INVALID_HANDLE_VALUE; }
// cleanup the token from the OpenThreadToken call
if (*pCurImpToken != INVALID_HANDLE_VALUE) { HANDLE hThread = GetCurrentThread(); SetThreadToken(&hThread, *pCurImpToken); CloseHandle(*pCurImpToken); *pCurImpToken = INVALID_HANDLE_VALUE; } }
return hr; }
HRESULT UnImpersonateUser(HANDLE hSavedImpToken, HANDLE hLoggedOnUser) { // if there is an hSavedImpToken, then call SetThreadToken to
// restore it.
if (hSavedImpToken != INVALID_HANDLE_VALUE) { HANDLE hThread = GetCurrentThread(); SetThreadToken(&hThread, hSavedImpToken); CloseHandle(hSavedImpToken); }
// cleanup the LogonUser token
if (hLoggedOnUser != INVALID_HANDLE_VALUE) { CloseHandle(hLoggedOnUser); }
return S_OK; }
|