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.
1208 lines
24 KiB
1208 lines
24 KiB
/*++
|
|
|
|
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,
|
|
BOOL fLocal
|
|
)
|
|
/*++
|
|
|
|
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),
|
|
m_NetUseSessionCreated(FALSE),
|
|
m_fLocal(fLocal),
|
|
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::ConnectToComputer()
|
|
{
|
|
CError err;
|
|
DWORD rc;
|
|
BOOL already_connected = FALSE;
|
|
CString server, user, password;
|
|
server = m_strServerName;
|
|
|
|
// check if lpMachineName starts with \\
|
|
// if it doesn't then make sure it does, or it's null (for local machine)
|
|
server = _T("\\\\");
|
|
server += PURE_COMPUTER_NAME((LPCTSTR) m_strServerName);
|
|
|
|
user = m_strAdminName;
|
|
password = m_strAdminPassword;
|
|
|
|
//
|
|
// As it turned out in some cases we cannot get access to file system
|
|
// even if we are connected to metabase. We will add connection in this
|
|
// case also.
|
|
//
|
|
if (!m_fLocal)
|
|
{
|
|
BOOL bEmptyPassword = FALSE;
|
|
|
|
// Add use for this resource
|
|
NETRESOURCE nr;
|
|
nr.dwType = RESOURCETYPE_DISK;
|
|
nr.lpLocalName = NULL;
|
|
nr.lpRemoteName = (LPTSTR)(LPCTSTR)server;
|
|
nr.lpProvider = NULL;
|
|
|
|
// We need to close connections that we may have to this resource
|
|
rc = WNetCancelConnection2((LPTSTR)(LPCTSTR)server, 0, TRUE);
|
|
if (rc == ERROR_OPEN_FILES)
|
|
{
|
|
already_connected = TRUE;
|
|
}
|
|
|
|
// Empty strings below mean no password, which is wrong. NULLs mean
|
|
// default user and default password -- this could work better for local case.
|
|
LPCTSTR p1 = password, p2 = user;
|
|
// In case when password is really was set empty, passing NULL will fail.
|
|
if (password.IsEmpty() && !bEmptyPassword)
|
|
{
|
|
p1 = NULL;
|
|
}
|
|
if (user.IsEmpty())
|
|
{
|
|
p2 = NULL;
|
|
}
|
|
if (!already_connected)
|
|
{
|
|
rc = WNetAddConnection2(&nr, p1, p2, 0);
|
|
if (NO_ERROR != rc)
|
|
{
|
|
err = rc;
|
|
return err;
|
|
}
|
|
m_NetUseSessionCreated = TRUE;
|
|
}
|
|
}
|
|
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())
|
|
{
|
|
// try to net use to the machine if this failed...
|
|
if (ERROR_ACCESS_DENIED == err.Win32Error())
|
|
{
|
|
err = ConnectToComputer();
|
|
|
|
// try again.
|
|
BeginWaitCursor();
|
|
err = ::IISEnumerateUsers(
|
|
(LPTSTR)(LPCTSTR)m_strServerName,
|
|
1,
|
|
INET_FTP_SVC_ID,
|
|
m_dwInstance,
|
|
&dwCount,
|
|
(LPBYTE *)&lpUserInfo
|
|
);
|
|
EndWaitCursor();
|
|
|
|
if (err.Failed())
|
|
{
|
|
return err;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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(m_hWnd))
|
|
{
|
|
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(
|
|
m_hWnd,
|
|
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(
|
|
m_hWnd,
|
|
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);
|
|
}
|
|
}
|
|
|
|
CUserSessionsDlg::~CUserSessionsDlg()
|
|
{
|
|
if (m_NetUseSessionCreated)
|
|
{
|
|
CString server;
|
|
server = m_strServerName;
|
|
// check if lpMachineName starts with \\
|
|
// if it doesn't then make sure it does, or it's null (for local machine)
|
|
server = _T("\\\\");
|
|
server += PURE_COMPUTER_NAME((LPCTSTR) m_strServerName);
|
|
|
|
WNetCancelConnection2((LPTSTR)(LPCTSTR)server, 0, TRUE);
|
|
m_NetUseSessionCreated = FALSE;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|