/**********************************************************************/
/**                       Microsoft Windows/NT                       **/
/**                Copyright(c) Microsoft Corporation, 1997 - 1999 **/
/**********************************************************************/

/*
    Servpp.h   
        Server properties implementation file

    FILE HISTORY:
        
*/

#include "stdafx.h"
#include "Servpp.h"
#include "server.h"
#include "service.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

const DWORD c_dwChangableFlagMask = 
    TAPISERVERCONFIGFLAGS_ENABLESERVER |
    TAPISERVERCONFIGFLAGS_SETACCOUNT |          
    TAPISERVERCONFIGFLAGS_SETTAPIADMINISTRATORS;

const TCHAR szPasswordNull[] = _T("               ");   // Empty password

BOOL
IsLocalSystemAccount(LPCTSTR pszAccount)
{
 BOOL						 bRet = FALSE;
 DWORD                       dwSidSize = 128;
 DWORD                       dwDomainNameSize = 128;
 SID_IDENTIFIER_AUTHORITY    NtAuthority = SECURITY_NT_AUTHORITY;
 PSID                        pLocalSid = NULL;
 PSID                        pLocalServiceSid = NULL;
 PSID                        pNetworkServiceSid = NULL;
 PSID                        accountSid = NULL;
 SID_NAME_USE                SidType;
 LPWSTR                      pwszDomainName;


    do
    {
        // Attempt to allocate a buffer for the SID. Note that apparently in the
        // absence of any error theData->m_Sid is freed only when theData goes
        // out of scope.

        accountSid = LocalAlloc( LMEM_FIXED, dwSidSize );
        pwszDomainName = (LPWSTR) LocalAlloc( LMEM_FIXED, dwDomainNameSize * sizeof(WCHAR) );

        // Was space allocated for the SID and domain name successfully?

        if ( accountSid == NULL || pwszDomainName == NULL )
        {
            if ( accountSid != NULL )
            {
                LocalFree( accountSid );
                accountSid = NULL;
            }

            if ( pwszDomainName != NULL )
            {
                LocalFree( pwszDomainName );
            }

            goto ExitHere;
        }

        // Attempt to Retrieve the SID and domain name. If LookupAccountName failes
        // because of insufficient buffer size(s) dwSidSize and dwDomainNameSize
        // will be set correctly for the next attempt.

        if (LookupAccountName (NULL,
                               pszAccount,
                               accountSid,
                               &dwSidSize,
                               pwszDomainName,
                               &dwDomainNameSize,
                               &SidType ))
        {
            break;
        }

        if (ERROR_INSUFFICIENT_BUFFER != GetLastError ())
        {
            goto ExitHere;
        }

        // domain name isn't needed at any time
        LocalFree (pwszDomainName);
        LocalFree (accountSid);

    } while ( TRUE );

    if (!AllocateAndInitializeSid (
        &NtAuthority, 
        1, 
        SECURITY_LOCAL_SYSTEM_RID, 
        0, 0, 0, 0, 0, 0, 0, 
        &pLocalSid) ||
        !AllocateAndInitializeSid (
        &NtAuthority, 
        1, 
        SECURITY_LOCAL_SERVICE_RID, 
        0, 0, 0, 0, 0, 0, 0, 
        &pLocalServiceSid) ||
        !AllocateAndInitializeSid (
        &NtAuthority, 
        1, 
        SECURITY_NETWORK_SERVICE_RID, 
        0, 0, 0, 0, 0, 0, 0, 
        &pNetworkServiceSid)
        )
    {
        goto ExitHere;
    }

    if (EqualSid(pLocalSid, accountSid) ||
        EqualSid(pLocalServiceSid, accountSid) ||
        EqualSid(pNetworkServiceSid, accountSid)) 
    {
        bRet = TRUE;
    } 

ExitHere:

    if (NULL != pwszDomainName)
    {
        LocalFree (pwszDomainName);
    }

    if (NULL != accountSid) 
    {
        LocalFree (accountSid);
    }

    if (NULL != pLocalSid) 
    {
        FreeSid(pLocalSid);
    }
    if (NULL != pLocalServiceSid)
    {
        FreeSid (pLocalServiceSid);
    }
    if (NULL != pNetworkServiceSid)
    {
        FreeSid (pNetworkServiceSid);
    }

    return bRet;
}

/////////////////////////////////////////////////////////////////////////////
//
// CServerProperties holder
//
/////////////////////////////////////////////////////////////////////////////
CServerProperties::CServerProperties
(
    ITFSNode *          pNode,
    IComponentData *    pComponentData,
    ITFSComponentData * pTFSCompData,
    ITapiInfo *         pTapiInfo,
    LPCTSTR             pszSheetName,
    BOOL                fTapiInfoLoaded
) : CPropertyPageHolderBase(pNode, pComponentData, pszSheetName),
    m_fTapiInfoLoaded(fTapiInfoLoaded)
{
    //ASSERT(pFolderNode == GetContainerNode());

    m_bAutoDeletePages = FALSE; // we have the pages as embedded members

    AddPageToList((CPropertyPageBase*) &m_pageSetup);
    AddPageToList((CPropertyPageBase*) &m_pageRefresh);

    Assert(pTFSCompData != NULL);
    m_spTFSCompData.Set(pTFSCompData);
    
    m_spTapiInfo.Set(pTapiInfo);

    m_hScManager = NULL;
    m_paQSC = NULL;

    m_pszServiceName = TAPI_SERVICE_NAME;

}

CServerProperties::~CServerProperties()
{
    // Close the service control manager database
    if (m_hScManager != NULL)
    {
        (void)::CloseServiceHandle(m_hScManager);
    }

    // Free the allocated pointers
    if (m_paQSC)
        delete m_paQSC;
    
    RemovePageFromList((CPropertyPageBase*) &m_pageSetup, FALSE);
    RemovePageFromList((CPropertyPageBase*) &m_pageRefresh, FALSE);
}

BOOL
CServerProperties::FInit()
{
    // get the service account information here
    SC_HANDLE hService = NULL;
    DWORD cbBytesNeeded = 0;
    BOOL fSuccess = TRUE;
    BOOL f;
    DWORD dwErr;

    m_uFlags = 0;

    if (!FOpenScManager())
    {
        // Unable to open service control database
        return FALSE;
    }

    /*
    **  Open service with querying access-control
    */
    hService = ::OpenService(m_hScManager,
                             m_pszServiceName,
                             SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG);
    if (hService == NULL)
    {
        TapiMessageBox(::GetLastError());
        return FALSE;
    }

    /*
    **  Query the service status
    */
    Trace1("# QueryServiceStatus(%s)...\n", m_pszServiceName);
    f = ::QueryServiceStatus(hService, OUT &m_SS);
    if (f)
    {
        //m_uFlags |= mskfValidSS;
    }
    else
    {
        ::TapiMessageBox(::GetLastError());
        fSuccess = FALSE;
    }

    /*
    **  Query the service config
    */
    Trace1("# QueryServiceConfig(%s)...\n", m_pszServiceName);
    f = ::QueryServiceConfig(hService,
                             NULL,
                             0,
                             OUT &cbBytesNeeded);   // Compute how many bytes we need to allocate

    cbBytesNeeded += 100;       // Add extra bytes (just in case)
    delete m_paQSC;             // Free previously allocated memory (if any)
    
    m_paQSC = (QUERY_SERVICE_CONFIG *) new BYTE[cbBytesNeeded];
    f = ::QueryServiceConfig(hService,
                             OUT m_paQSC,
                             cbBytesNeeded,
                             OUT &cbBytesNeeded);
    if (f)
    {
        m_strServiceDisplayName = m_paQSC->lpDisplayName;
        m_strLogOnAccountName = m_paQSC->lpServiceStartName;
    }
    else
    {
        ::TapiMessageBox(::GetLastError());
        fSuccess = FALSE;
    }

    VERIFY(::CloseServiceHandle(hService));
    return fSuccess;
}

/////////////////////////////////////////////////////////////////////
//  FOpenScManager()
//
//  Open the service control manager database (if not already opened).
//  The idea for such a function is to recover from a broken connection.
//
//  Return TRUE if the service control database was opened successfully,
//  othersise false.
//
BOOL
CServerProperties::FOpenScManager()
{
    if (m_hScManager == NULL)
    {
        m_hScManager = ::OpenSCManager(m_strMachineName,
                                       NULL,
                                       SC_MANAGER_CONNECT);
    }
    
    if (m_hScManager == NULL)
    {
        TapiMessageBox(::GetLastError());
        return FALSE;
    }
    
    return TRUE;
} // CServicePropertyData::FOpenScManager()

BOOL
CServerProperties::FUpdateServiceInfo(LPCTSTR pszName, LPCTSTR pszPassword, DWORD dwStartType)
{
    SC_HANDLE hService = NULL;
    BOOL fSuccess = TRUE;
    BOOL f;
    DWORD dwServiceType = 0;

    Trace1("INFO: Updating data for service %s...\n", (LPCTSTR)m_pszServiceName);
    // Re-open service control manager (in case it was closed)
    if (!FOpenScManager())
    {
        return FALSE;
    }

    /*
    **  Open service with write access
    **
    **  CODEWORK Could provide a more specific error message
    **    if SERVICE_CHANGE_CONFIG is available but not SERVICE_START
    */
    hService = ::OpenService(m_hScManager,
                             m_pszServiceName,
                             SERVICE_CHANGE_CONFIG);
    if (hService == NULL)
    {
        TapiMessageBox(::GetLastError());
        return FALSE;
    }

    Trace1("# ChangeServiceConfig(%s)...\n", m_pszServiceName);
    
    if (pszName)
    {
        if (IsLocalSystemAccount(pszName))
        {
            pszPassword = szPasswordNull;
        }
        dwServiceType = m_paQSC->dwServiceType & ~SERVICE_INTERACTIVE_PROCESS;
    }
    else
    {
        dwServiceType = SERVICE_NO_CHANGE;
    }

    f = ::ChangeServiceConfig(hService,           // Handle to service 
                              dwServiceType,      // Type of service 
                              dwStartType,        // When/How to start service
                              SERVICE_NO_CHANGE,  // dwErrorControl - severity if service fails to start 
                              NULL,               // Pointer to service binary file name 
                              NULL,               // lpLoadOrderGroup - pointer to load ordering group name 
                              NULL,               // lpdwTagId - pointer to variable to get tag identifier 
                              NULL,               // lpDependencies - pointer to array of dependency names 
                              pszName,            // Pointer to account name of service 
                              pszPassword,        // Pointer to password for service account
                              m_strServiceDisplayName);
            
    if (!f)
    {
        DWORD dwErr = ::GetLastError();
        Assert(dwErr != ERROR_SUCCESS);
        Trace2("ERR: ChangeServiceConfig(%s) failed. err= %u.\n", m_pszServiceName, dwErr);
        TapiMessageBox(dwErr);
        fSuccess = FALSE;
    }
    else
    {
        m_strLogOnAccountName = pszName;

        // if pszName is null, we aren't changing the account info, so don't check 
        // the logon as service info
        if (pszName && !IsLocalSystemAccount(pszName))
        {
            /*
            **  Make sure there is an LSA account with POLICY_MODE_SERVICE privilege
            **  This function reports its own errors, failure is only advisory
            */
            FCheckLSAAccount();
        } 
    }

    VERIFY(::CloseServiceHandle(hService));

    return fSuccess;
}

//Check if the user has the write access on service config info
BOOL 
CServerProperties::FHasServiceControl()
{
    BOOL fRet = FALSE;

    if (FIsTapiInfoLoaded())
    {
        fRet = m_spTapiInfo->FHasServiceControl();
    }
    else 
    {
        if (!FOpenScManager())
        {
            fRet = FALSE;
        }
        else
        {
            SC_HANDLE hService = NULL;

            hService = ::OpenService(m_hScManager,
                             m_pszServiceName,
                             SERVICE_CHANGE_CONFIG);

            fRet = (hService != NULL);
            
            if (hService)
            {
                VERIFY(::CloseServiceHandle(hService));
            }
        }
    }

    return fRet;
}

/////////////////////////////////////////////////////////////////////////////
// CServerPropRefresh property page

IMPLEMENT_DYNCREATE(CServerPropRefresh, CPropertyPageBase)

CServerPropRefresh::CServerPropRefresh() : CPropertyPageBase(CServerPropRefresh::IDD)
{
    //{{AFX_DATA_INIT(CServerPropRefresh)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
}

CServerPropRefresh::~CServerPropRefresh()
{
}

void CServerPropRefresh::DoDataExchange(CDataExchange* pDX)
{
    CPropertyPageBase::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CServerPropRefresh)
    DDX_Control(pDX, IDC_EDIT_MINUTES, m_editMinutes);
    DDX_Control(pDX, IDC_EDIT_HOURS, m_editHours);
    DDX_Control(pDX, IDC_SPIN_MINUTES, m_spinMinutes);
    DDX_Control(pDX, IDC_SPIN_HOURS, m_spinHours);
    DDX_Control(pDX, IDC_CHECK_ENABLE_STATS, m_checkEnableStats);
    //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CServerPropRefresh, CPropertyPageBase)
    //{{AFX_MSG_MAP(CServerPropRefresh)
    ON_BN_CLICKED(IDC_CHECK_ENABLE_STATS, OnCheckEnableStats)
    ON_EN_KILLFOCUS(IDC_EDIT_HOURS, OnKillfocusEditHours)
    ON_EN_KILLFOCUS(IDC_EDIT_MINUTES, OnKillfocusEditMinutes)
    ON_EN_CHANGE(IDC_EDIT_HOURS, OnChangeEditHours)
    ON_EN_CHANGE(IDC_EDIT_MINUTES, OnChangeEditMinutes)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerPropRefresh message handlers

BOOL CServerPropRefresh::OnInitDialog() 
{
    CPropertyPageBase::OnInitDialog();
    
    m_spinHours.SetRange(0, AUTO_REFRESH_HOURS_MAX);
    m_spinMinutes.SetRange(0, AUTO_REFRESH_MINUTES_MAX);

    m_checkEnableStats.SetCheck(m_bAutoRefresh);

    // update the refresh interval
    int nHours, nMinutes;
    DWORD dwRefreshInterval = m_dwRefreshInterval;

    nHours = dwRefreshInterval / MILLISEC_PER_HOUR;
    dwRefreshInterval -= nHours * MILLISEC_PER_HOUR;

    nMinutes = dwRefreshInterval / MILLISEC_PER_MINUTE;
    dwRefreshInterval -= nMinutes * MILLISEC_PER_MINUTE;

    m_spinHours.SetPos(nHours);
    m_spinMinutes.SetPos(nMinutes);

    m_editHours.LimitText(2);
    m_editMinutes.LimitText(2);

    // set the button states
    UpdateButtons();

    SetDirty(FALSE);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}

void CServerPropRefresh::UpdateButtons()
{
    int nCheck = m_checkEnableStats.GetCheck();

    GetDlgItem(IDC_EDIT_HOURS)->EnableWindow(nCheck != 0);
    GetDlgItem(IDC_EDIT_MINUTES)->EnableWindow(nCheck != 0);

    GetDlgItem(IDC_SPIN_HOURS)->EnableWindow(nCheck != 0);
    GetDlgItem(IDC_SPIN_MINUTES)->EnableWindow(nCheck != 0);
}

void CServerPropRefresh::OnCheckEnableStats() 
{
    SetDirty(TRUE);
    
    UpdateButtons();    
}

void CServerPropRefresh::OnKillfocusEditHours() 
{
}

void CServerPropRefresh::OnKillfocusEditMinutes() 
{
}

void CServerPropRefresh::OnChangeEditHours() 
{
    ValidateHours();
    SetDirty(TRUE);
}

void CServerPropRefresh::OnChangeEditMinutes() 
{
    ValidateMinutes();
    SetDirty(TRUE);
}

void CServerPropRefresh::ValidateHours() 
{
    CString strValue;
    int nValue;

    if (m_editHours.GetSafeHwnd() != NULL)
    {
        m_editHours.GetWindowText(strValue);
        if (!strValue.IsEmpty())
        {
            nValue = _ttoi(strValue);

            if ((nValue >= 0) &&
                (nValue <= AUTO_REFRESH_HOURS_MAX))
            {
                // everything is good
                return;
            }

            if (nValue > AUTO_REFRESH_HOURS_MAX)
                nValue = AUTO_REFRESH_HOURS_MAX;
            else
            if (nValue < 0)
                nValue = 0;

            // set the new value and beep
            CString strText;
            LPTSTR pBuf = strText.GetBuffer(5);
            
            _itot(nValue, pBuf, 10);
            strText.ReleaseBuffer();

            MessageBeep(MB_ICONEXCLAMATION);

            m_editHours.SetWindowText(strText);
            
            m_editHours.SetSel(0, -1);
            m_editHours.SetFocus();
        }
    }
}

void CServerPropRefresh::ValidateMinutes() 
{
    CString strValue;
    int nValue;

    if (m_editMinutes.GetSafeHwnd() != NULL)
    {
        m_editMinutes.GetWindowText(strValue);
        if (!strValue.IsEmpty())
        {
            nValue = _ttoi(strValue);

            if ((nValue >= 0) &&
                (nValue <= AUTO_REFRESH_MINUTES_MAX))
            {
                // everything is good
                return;
            }
            
            if (nValue > AUTO_REFRESH_MINUTES_MAX)
                nValue = AUTO_REFRESH_MINUTES_MAX;
            else
            if (nValue < 0)
                nValue = 0;

            CString strText;
            LPTSTR pBuf = strText.GetBuffer(5);
            
            _itot(nValue, pBuf, 10);
            strText.ReleaseBuffer();

            MessageBeep(MB_ICONEXCLAMATION);

            m_editMinutes.SetWindowText(strText);
            
            m_editMinutes.SetSel(0, -1);
            m_editMinutes.SetFocus();
        }
    }
}

BOOL CServerPropRefresh::OnApply() 
{
    if (!IsDirty())
        return TRUE;

    UpdateData();

    m_bAutoRefresh = (m_checkEnableStats.GetCheck() == 1) ? TRUE : FALSE;

    int nHours = m_spinHours.GetPos();
    int nMinutes = m_spinMinutes.GetPos();
    
    m_dwRefreshInterval = nHours * MILLISEC_PER_HOUR;
    m_dwRefreshInterval += nMinutes * MILLISEC_PER_MINUTE;

    if (m_bAutoRefresh && m_dwRefreshInterval == 0)
    {
        CString strMessage;
        
        AfxMessageBox(IDS_ERR_AUTO_REFRESH_ZERO);
        m_editHours.SetSel(0, -1);
        m_editHours.SetFocus();

        return FALSE;
    }
    
    BOOL bRet = CPropertyPageBase::OnApply();

    if (bRet == FALSE)
    {
        // Something bad happened... grab the error code
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
        ::TapiMessageBox(GetHolder()->GetError());
    }

    return bRet;
}

BOOL CServerPropRefresh::OnPropertyChange(BOOL bScope, LONG_PTR *ChangeMask)
{
    SPITFSNode      spNode;
    CTapiServer *   pServer;
    DWORD           dwError;

    // do stuff here.
    BEGIN_WAIT_CURSOR;

    spNode = GetHolder()->GetNode();
    pServer = GETHANDLER(CTapiServer, spNode);

    pServer->SetAutoRefresh(spNode, m_bAutoRefresh, m_dwRefreshInterval);

    SPITFSNodeMgr   spNodeMgr;
    SPITFSNode spRootNode;

    spNode->GetNodeMgr(&spNodeMgr);
    spNodeMgr->GetRootNode(&spRootNode);
    spRootNode->SetData(TFS_DATA_DIRTY, TRUE);

    END_WAIT_CURSOR;
    return FALSE;
}

/////////////////////////////////////////////////////////////////////////////
// CServerPropSetup property page

IMPLEMENT_DYNCREATE(CServerPropSetup, CPropertyPageBase)

CServerPropSetup::CServerPropSetup() : CPropertyPageBase(CServerPropSetup::IDD)
{
    //{{AFX_DATA_INIT(CServerPropSetup)
        // NOTE: the ClassWizard will add member initialization here
    //}}AFX_DATA_INIT
    
    m_dwNewFlags = 0;
}

CServerPropSetup::~CServerPropSetup()
{
}

void CServerPropSetup::DoDataExchange(CDataExchange* pDX)
{
    CPropertyPage::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CServerPropSetup)
    DDX_Control(pDX, IDC_LIST_ADMINS, m_listAdmins);
    //}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CServerPropSetup, CPropertyPageBase)
    //{{AFX_MSG_MAP(CServerPropSetup)
    ON_BN_CLICKED(IDC_BUTTON_ADD_ADMIN, OnButtonAdd)
    ON_BN_CLICKED(IDC_BUTTON_CHOOSE_USER, OnButtonChooseUser)
    ON_BN_CLICKED(IDC_BUTTON_REMOVE_ADMIN, OnButtonRemove)
    ON_BN_CLICKED(IDC_CHECK_ENABLE_SERVER, OnCheckEnableServer)
    ON_EN_CHANGE(IDC_EDIT_NAME, OnChangeEditName)
    ON_EN_CHANGE(IDC_EDIT_PASSWORD, OnChangeEditPassword)
    ON_LBN_SELCHANGE(IDC_LIST_ADMINS, OnSelchangeListAdmins)
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CServerPropSetup message handlers

BOOL CServerPropSetup::OnInitDialog() 
{
    SPITapiInfo     spTapiInfo;
    CString         strName;
    HRESULT         hr = hrOK;

    CPropertyPageBase::OnInitDialog();

    CServerProperties * pServerProp = (CServerProperties * ) GetHolder();
    pServerProp->GetTapiInfo(&spTapiInfo);
    Assert(spTapiInfo);

    BOOL fIsNTS = TRUE;

    if (pServerProp->FIsServiceRunning())
    {
        fIsNTS = spTapiInfo->IsServer();

        hr = spTapiInfo->GetConfigInfo(&m_tapiConfigInfo);
        if (FAILED(hr))
        {
            Panic1("ServerPropSetup - GetConfigInfo failed! %x", hr);
        }

        // update the checkbox
        ((CButton *) GetDlgItem(IDC_CHECK_ENABLE_SERVER))->SetCheck(spTapiInfo->IsTapiServer());

        // now update any TAPI administrators
        for (int i = 0; i < m_tapiConfigInfo.m_arrayAdministrators.GetSize(); i++)
        {
            ((CListBox *) GetDlgItem(IDC_LIST_ADMINS))->AddString(m_tapiConfigInfo.m_arrayAdministrators[i].m_strName);
        }
        
    }
    else
    {
        // check to see if the machine is NTS  
        TFSIsNTServer(pServerProp->m_strMachineName, &fIsNTS);
    }

    if (fIsNTS)
    {
        // fill in the username and password
        strName = pServerProp->GetServiceAccountName();
        GetDlgItem(IDC_EDIT_NAME)->SetWindowText(strName);
        GetDlgItem(IDC_EDIT_PASSWORD)->SetWindowText(szPasswordNull);

        m_dwNewFlags = TAPISERVERCONFIGFLAGS_ISSERVER;
    }
    else
    {
        m_dwNewFlags = 0;
    }
    EnableButtons(fIsNTS);

    m_fRestartService = FALSE;
    m_dwInitFlags = m_tapiConfigInfo.m_dwFlags;

    SetDirty(FALSE);

    return TRUE;  // return TRUE unless you set the focus to a control
                  // EXCEPTION: OCX Property Pages should return FALSE
}

void CServerPropSetup::OnButtonAdd() 
{
    CGetUsers getUsers(TRUE);

    if (!getUsers.GetUsers(GetSafeHwnd()))
        return;

    for (int nCount = 0; nCount < getUsers.GetSize(); nCount++)
    {
        CUserInfo userTemp;

        userTemp = getUsers[nCount];

        BOOL fDuplicate = FALSE;
        for (int i = 0; i < m_tapiConfigInfo.m_arrayAdministrators.GetSize(); i++)
        {
            if (m_tapiConfigInfo.m_arrayAdministrators[i].m_strName.CompareNoCase(userTemp.m_strName) == 0)
            {
                fDuplicate = TRUE;
                break;
            }
        }

        if (!fDuplicate)
        {
            // add to the array
            int nIndex = (int)m_tapiConfigInfo.m_arrayAdministrators.Add(userTemp);

            // now add to the listbox
            m_listAdmins.AddString(m_tapiConfigInfo.m_arrayAdministrators[nIndex].m_strName);
        }
        else
        {
            // tell the user we're not adding this to the list
            CString strMessage;
            AfxFormatString1(strMessage, IDS_ADMIN_ALREADY_IN_LIST, userTemp.m_strName);
            AfxMessageBox(strMessage);
        }

        SetDirty(TRUE);
    }

    m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETTAPIADMINISTRATORS;

    EnableButtons();
}

void CServerPropSetup::OnButtonRemove() 
{
    CString strSelectedName;
    int nCurSel = m_listAdmins.GetCurSel();

    m_listAdmins.GetText(nCurSel, strSelectedName);

    // remove from the list
    for (int i = 0; i < m_tapiConfigInfo.m_arrayAdministrators.GetSize(); i++)
    {
        if (strSelectedName.Compare(m_tapiConfigInfo.m_arrayAdministrators[i].m_strName) == 0)
        {
            // found it.  remove from the list
            m_tapiConfigInfo.m_arrayAdministrators.RemoveAt(i);
            break;
        }
    }

    // now remove from the list box
    m_listAdmins.DeleteString(nCurSel);

    m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETTAPIADMINISTRATORS;

    SetDirty(TRUE);

    EnableButtons();
}

void CServerPropSetup::OnButtonChooseUser() 
{
    CGetUsers getUsers;

    if (!getUsers.GetUsers(GetSafeHwnd()))
        return;

    if (0 == getUsers.GetSize())
        return;

    CUserInfo userTemp;

    userTemp = getUsers[0];

    GetDlgItem(IDC_EDIT_NAME)->SetWindowText(userTemp.m_strName);
            
    m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;

    SetDirty(TRUE);
    EnableButtons();
}

void CServerPropSetup::OnCheckEnableServer() 
{
    if (((CButton *) GetDlgItem(IDC_CHECK_ENABLE_SERVER))->GetCheck())
    {
        m_dwNewFlags |= TAPISERVERCONFIGFLAGS_ENABLESERVER;
    }
    else
    {
        m_dwNewFlags &= ~TAPISERVERCONFIGFLAGS_ENABLESERVER;
    }

    EnableButtons ();

    SetDirty(TRUE); 
}

void CServerPropSetup::OnChangeEditName() 
{
    m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;

    SetDirty(TRUE); 
}

void CServerPropSetup::OnChangeEditPassword() 
{
    m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;
    
    m_fRestartService = TRUE;

    SetDirty(TRUE); 
}

void CServerPropSetup::OnSelchangeListAdmins() 
{
    EnableButtons();    
}

void CServerPropSetup::EnableButtons(BOOL fIsNtServer)
{
    BOOL fServiceRunning = ((CServerProperties *) GetHolder())->FIsServiceRunning();
    
    //if we are unable to get the write access to tapisrv service, we need to disable
    // some controls
    BOOL fHasServiceControl = ((CServerProperties *) GetHolder())->FHasServiceControl();

    //We enable the admin controls only if we sucessfully loaded the tapi info
    BOOL fTapiInfoLoaded = ((CServerProperties *) GetHolder())->FIsTapiInfoLoaded();

    BOOL fIsAdmin = ((CServerProperties *) GetHolder())->FIsAdmin();

    // if this isn't an NT server, disable all controls 
    if (!fIsNtServer)
        fServiceRunning = FALSE;
    
    //Enable the Admin controls only if 
    //(1) the service is running
    //(2) successfully loaded the tapi config info
    //(3) the user is a machine admin or tapi admin
    BOOL fEnableAdminControls = fServiceRunning && fTapiInfoLoaded && fIsAdmin;

    // enable the admin controls on
    GetDlgItem(IDC_STATIC_ADMINS)->EnableWindow(fEnableAdminControls);
    GetDlgItem(IDC_STATIC_NOTE)->EnableWindow(fEnableAdminControls);
    GetDlgItem(IDC_STATIC_LISTBOX)->EnableWindow(fEnableAdminControls);
    GetDlgItem(IDC_BUTTON_ADD_ADMIN)->EnableWindow(fEnableAdminControls);
    GetDlgItem(IDC_BUTTON_REMOVE_ADMIN)->EnableWindow(fEnableAdminControls);
    GetDlgItem(IDC_LIST_ADMINS)->EnableWindow(fEnableAdminControls);

    //If the user is not admin, then they don't have ServiceControl write access
    //So fHasServiceControl covers fIsAdmin
    
    GetDlgItem(IDC_CHECK_ENABLE_SERVER)->EnableWindow(fServiceRunning 
                                                    && fHasServiceControl
                                                    && fTapiInfoLoaded);
                                                    
    // these should always be available if we have service control access 
    // and we are running on server
    GetDlgItem(IDC_STATIC_USERNAME)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_STATIC_PASSWORD)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_STATIC_ACCOUNT)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_BUTTON_CHOOSE_USER)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_EDIT_NAME)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_EDIT_PASSWORD)->EnableWindow(fIsNtServer && fHasServiceControl);
    GetDlgItem(IDC_STATIC_ACCOUNT_INFO)->EnableWindow(fIsNtServer && fHasServiceControl);

    if (fServiceRunning)
    {

        // enable the remove button if something is selected
        BOOL fEnableRemove = m_listAdmins.GetCurSel() != LB_ERR;

        //if we will disable the remove button and the remove button has the focus, 
        //we should change focus to the next control
        CWnd * pwndRemove = GetDlgItem(IDC_BUTTON_REMOVE_ADMIN);

        if (!fEnableRemove && GetFocus() == pwndRemove)
        {
            NextDlgCtrl();
        }

        pwndRemove->EnableWindow(fEnableRemove);
    }
}

BOOL CServerPropSetup::OnApply() 
{
    CString     strAccount, strPassword;
    BOOL        fUpdateAccount = FALSE;
    BOOL        fUpdateTapiServer = FALSE;
    BOOL        bRet = TRUE;
    BOOL        bWasServer, bToBeServer;
    DWORD       dwStartType = SERVICE_NO_CHANGE;

    if (!IsDirty())
        return bRet;

    CServerProperties * pServerProp = (CServerProperties * ) GetHolder();

    UpdateData();

    //  Check to see if there is any change on enabling server
    //  or user account name, that requires service restarting
    if (!m_fRestartService)
    {
        bWasServer = m_dwInitFlags & TAPISERVERCONFIGFLAGS_ENABLESERVER;
        bToBeServer = ((CButton *) GetDlgItem(IDC_CHECK_ENABLE_SERVER))->GetCheck();
        if (bWasServer && !bToBeServer || !bWasServer && bToBeServer)
        {
            m_fRestartService = TRUE;
        }
        if (m_dwNewFlags & TAPISERVERCONFIGFLAGS_SETACCOUNT)
        {
            GetDlgItem(IDC_EDIT_NAME)->GetWindowText(strAccount);
            if (strAccount.CompareNoCase(pServerProp->GetServiceAccountName()) != 0)
            {
                m_fRestartService = TRUE;
            }
            else
            {
                m_dwNewFlags &= ~TAPISERVERCONFIGFLAGS_SETACCOUNT;
            }
        }
    }

    // if the account information has changed, the update the info struct now
    if (m_dwNewFlags & TAPISERVERCONFIGFLAGS_SETACCOUNT)
    {
        GetDlgItem(IDC_EDIT_NAME)->GetWindowText(strAccount);
        GetDlgItem(IDC_EDIT_PASSWORD)->GetWindowText(strPassword);

        // verify that the user is an admin on the machine
        if (!IsLocalSystemAccount(strAccount))
        {
            DWORD   dwErr;
            BOOL    fIsAdmin;
            CString strMessage;
            
            dwErr = ::IsAdmin(pServerProp->m_strMachineName, strAccount, strPassword, &fIsAdmin);

            if (!fIsAdmin)
            {
                AfxFormatString1(strMessage, IDS_ERR_USER_NOT_ADMIN, pServerProp->m_strMachineName);
                AfxMessageBox(strMessage);
            
                GetDlgItem(IDC_EDIT_NAME)->SetFocus();
                ((CEdit *) GetDlgItem(IDC_EDIT_NAME))->SetSel(0, -1);

                return FALSE;
            }
        }

        // clear the flag so we don't use the TAPI MMC APIs to set this
        m_dwNewFlags &= ~TAPISERVERCONFIGFLAGS_SETACCOUNT;
        fUpdateAccount = TRUE;
    }

    // if we are changing the server state or admin stuff then
    if (((CButton *) GetDlgItem(IDC_CHECK_ENABLE_SERVER))->GetCheck())
    {
        m_dwNewFlags |= TAPISERVERCONFIGFLAGS_ENABLESERVER;
    }

    // only update config information if it has changed
    if ((pServerProp->FIsServiceRunning()) &&
        (m_tapiConfigInfo.m_dwFlags != m_dwNewFlags))
    {
        // if we modify the tapi server status then we need to change the 
        // service statrt type as well.
        if ((m_dwNewFlags & TAPISERVERCONFIGFLAGS_ENABLESERVER) &&
            !(m_tapiConfigInfo.m_dwFlags & TAPISERVERCONFIGFLAGS_ENABLESERVER))
        {
            fUpdateTapiServer = TRUE;
        }

        dwStartType = (m_dwNewFlags & TAPISERVERCONFIGFLAGS_ENABLESERVER) ? SERVICE_AUTO_START : SERVICE_DEMAND_START;
        
        bRet = CPropertyPageBase::OnApply();
    }

    if (bRet == FALSE)
    {
        // Something bad happened... grab the error code
        AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
        ::TapiMessageBox(WIN32_FROM_HRESULT(GetHolder()->GetError()));

        // restore the flag
        if (fUpdateAccount) 
            m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;
    }
    else
    {
        m_dwNewFlags = TAPISERVERCONFIGFLAGS_ISSERVER;

        if (fUpdateAccount || fUpdateTapiServer)
        {
            // do the account change
            BEGIN_WAIT_CURSOR

            LPCTSTR pszAccount = (fUpdateAccount) ? (LPCTSTR) strAccount : NULL;
            LPCTSTR pszPassword = (fUpdateAccount) ? (LPCTSTR) strPassword : NULL;

            bRet = pServerProp->FUpdateServiceInfo(pszAccount, pszPassword, dwStartType);
            
            if (bRet)
            {
                /*$REVIEW
                Tapisrv occupies a seperate house in NT server. It lives with the other 
                services on NT Professional Edition(workstation). We do not need to 
                sperate/merge services anymore. Users should not be allowed to change 
                account information from TAPI MMC on NT workstation(Disabled). 

                HRESULT hr;

                // if the change was successful, update the svc host information
                hr = UpdateSvcHostInfo(pServerProp->m_strMachineName, IsLocalSystemAccount(strAccount));
                if (FAILED(hr))
                {
                    // restore the flag
                    if (fUpdateAccount)
                    {
                        m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;
                    }

                    ::TapiMessageBox(WIN32_FROM_HRESULT(hr));
                    return FALSE;
                }
                */
            }
            else if (fUpdateAccount)
            {
                // set account failed, so set the flag again.
                m_dwNewFlags |= TAPISERVERCONFIGFLAGS_SETACCOUNT;
            }

            END_WAIT_CURSOR
        }

        // if everything went OK and we changed something that requires a service restart then
        // do ask the user if they want to do it now
        if (bRet && m_fRestartService)
        {
            CString strText;
            BOOL    fServiceRunning = pServerProp->FIsServiceRunning();
            
            ::TFSIsServiceRunning(pServerProp->m_strMachineName, 
                                  TAPI_SERVICE_NAME, 
                                  &fServiceRunning);

            if (fServiceRunning)
                strText.LoadString(IDS_ACCOUNT_CHANGE_RESTART);
            else
                strText.LoadString(IDS_ACCOUNT_CHANGE_START);

            // Tell the user the service needs to be restarted in order to make the changes
            if (AfxMessageBox(strText, MB_YESNO) == IDYES)
            {
                if (RestartService() == ERROR_SUCCESS)
                {
                    m_fRestartService = FALSE;
                    m_dwInitFlags = m_tapiConfigInfo.m_dwFlags;
                }
            }
        }
    }

    if (!bRet)
        SetDirty(TRUE);

    return bRet;
}

BOOL CServerPropSetup::OnPropertyChange(BOOL bScope, LONG_PTR *ChangeMask)
{
    SPITapiInfo     spTapiInfo;
    HRESULT         hr = hrOK;
    BOOL            fServiceRunning = TRUE;
    DWORD           dwOldFlags;
    DWORD           dwErr = ERROR_SUCCESS;

    CServerProperties * pServerProp = (CServerProperties * ) GetHolder();

    pServerProp->GetTapiInfo(&spTapiInfo);
    Assert(spTapiInfo);

    // if the service isn't running, try to start it
    //if (!pServerProp->FIsServiceRunning())
    dwErr = ::TFSIsServiceRunning(pServerProp->m_strMachineName, 
                                  TAPI_SERVICE_NAME, 
                                  &fServiceRunning);
    if (!fServiceRunning)
    {
        // service has stopped from under us.  Return an error.
        GetHolder()->SetError(HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE));
        return FALSE;
    }

    // if everything is cool then make the changes
    if (dwErr == ERROR_SUCCESS)
    {
        dwOldFlags = m_tapiConfigInfo.m_dwFlags;

        //clear the changable bits in old flags
        m_tapiConfigInfo.m_dwFlags &= ~c_dwChangableFlagMask;

        //set the changable bits
        m_tapiConfigInfo.m_dwFlags |= (m_dwNewFlags & c_dwChangableFlagMask);
                
        hr = spTapiInfo->SetConfigInfo(&m_tapiConfigInfo);

        //Bug 276787 We should clear the two write bits
        m_tapiConfigInfo.m_dwFlags &= ~(TAPISERVERCONFIGFLAGS_SETACCOUNT | 
                                        TAPISERVERCONFIGFLAGS_SETTAPIADMINISTRATORS);

        if (FAILED(hr))
        {
            GetHolder()->SetError(hr);
            m_tapiConfigInfo.m_dwFlags = dwOldFlags;
        }
    }

    return FALSE;
}

HRESULT CServerPropSetup::UpdateSvcHostInfo(LPCTSTR pszMachine, BOOL fLocalSystemAccount)
{
    HRESULT     hr = hrOK;
    MULTI_QI    qi;
    SPIRemoteNetworkConfig  spRemote;
    
    hr = CoInitialize(NULL);
    if (FAILED(hr))
    {
        return hr;
    }

    if (IsLocalMachine(pszMachine))
    {
        hr = CoCreateInstance(CLSID_RemoteRouterConfig,
                              NULL,
                              CLSCTX_SERVER,
                              IID_IRemoteNetworkConfig,
                              (LPVOID *) &(qi.pItf));
    }
    else
    {
        COSERVERINFO    csi;
        
        qi.pIID = &IID_IRemoteNetworkConfig;
        qi.pItf = NULL;
        qi.hr = 0;
        
        csi.dwReserved1 = 0;
        csi.dwReserved2 = 0;
        csi.pwszName = (LPWSTR) (LPCTSTR) pszMachine;
        csi.pAuthInfo = NULL;
        hr = CoCreateInstanceEx(CLSID_RemoteRouterConfig,
                                NULL,
                                CLSCTX_SERVER,
                                &csi,
                                1,
                                &qi);
    }
    
    Trace1("CServerPropSetup::UpdateSvcHostInfo - CoCreateInstance returned %lx\n", hr);

    if (hr == S_OK)
    {
        CString strGroup;

        strGroup = _T("Tapisrv");

        spRemote = (IRemoteNetworkConfig *)qi.pItf;
        hr = spRemote->SetUserConfig(TAPI_SERVICE_NAME, strGroup);
    
        Trace1("CServerPropSetup::UpdateSvcHostInfo - SetUserConfig returned %lx\n", hr);
    }

    CoUninitialize();

    return hr;
}

DWORD CServerPropSetup::RestartService()
{
    // restart the service if requested
    CServerProperties * pServerProp = (CServerProperties * ) GetHolder();
    DWORD dwErr = ERROR_SUCCESS;
    BOOL fRestart = FALSE;
    
    SPITapiInfo     spTapiInfo;
    pServerProp->GetTapiInfo(&spTapiInfo);

    // gotta clean up before the service stops
    spTapiInfo->Destroy();

    // any time we stop/start the service we need to call this
    ::UnloadTapiDll();

    // stop the service if it is running
    BOOL    fServiceRunning = pServerProp->FIsServiceRunning();
    
    ::TFSIsServiceRunning(pServerProp->m_strMachineName, 
                          TAPI_SERVICE_NAME, 
                          &fServiceRunning);

    if (fServiceRunning)
    {
        dwErr = ::TFSStopService(pServerProp->m_strMachineName, TAPI_SERVICE_NAME, pServerProp->GetServiceDisplayName());
    }

    if (dwErr != ERROR_SUCCESS)
    {
        CString strText;
        strText.LoadString(IDS_ERR_SERVICE_NOT_STOPPED);
        TapiMessageBox(dwErr, MB_OK, strText);
    }

    // start the service
    if (dwErr == ERROR_SUCCESS)
    {
        dwErr = ::TFSStartService(pServerProp->m_strMachineName, TAPI_SERVICE_NAME, pServerProp->GetServiceDisplayName());

        if (dwErr != ERROR_SUCCESS)
        {
            CString strText;
            strText.LoadString(IDS_ERR_SERVICE_NOT_STARTED);
            TapiMessageBox(dwErr, MB_OK, strText);
        }
    }

    StartRefresh();

    return dwErr;
}

void CServerPropSetup::StartRefresh()
{
    // refresh the snapin to reflect the changes
    SPITFSNode      spNode;
    CTapiServer *   pServer;

    spNode = GetHolder()->GetNode();
    pServer = GETHANDLER(CTapiServer, spNode);

    pServer->OnRefresh(spNode, NULL, 0, 0, 0);
}