/*++

   Copyright    (c)    1994-1998    Microsoft Corporation

   Module  Name :

        facc.cpp

   Abstract:

        FTP Accounts Property Page

   Author:

        Ronald Meijer (ronaldm)

   Project:

        Internet Services Manager

   Revision History:

--*/

//
// Include Files
//
#include "stdafx.h"
#include "common.h"
#include "inetprop.h"
#include "InetMgrApp.h"
#include "supdlgs.h"
#include "shts.h"
#include "ftpsht.h"
#include "facc.h"




#ifdef _DEBUG
#undef THIS_FILE
static char BASED_CODE THIS_FILE[] = __FILE__;
#endif



IMPLEMENT_DYNCREATE(CFtpAccountsPage, CInetPropertyPage)



CFtpAccountsPage::CFtpAccountsPage(
    IN CInetPropertySheet * pSheet
    )
/*++

Routine Description:

    Constructor for FTP service property page

Arguments:

    CInetPropertySheet * pSheet : Associated property sheet

Return Value:

    N/A

--*/
    : CInetPropertyPage(CFtpAccountsPage::IDD, pSheet),
      m_ListBoxRes(
        IDB_ACLUSERS,
        CAccessEntryListBox::nBitmaps
        ),
      m_oblSID(),
      m_fPasswordSyncChanged(FALSE),
      m_fUserNameChanged(FALSE),
      m_fPasswordSyncMsgShown(FALSE)
{
#ifdef _DEBUG

    afxMemDF |= checkAlwaysMemDF;

#endif // _DEBUG

#if 0 // Keep Class Wizard happy

    //{{AFX_DATA_INIT(CFtpAccountsPage)
    m_strUserName = _T("");
    m_fAllowAnonymous = TRUE;
    m_fOnlyAnonymous = FALSE;
    m_fPasswordSync = FALSE;
    //}}AFX_DATA_INIT

#endif // 0

    m_list_Administrators.AttachResources(&m_ListBoxRes);
}



CFtpAccountsPage::~CFtpAccountsPage()
/*++

Routine Description:

    Destructor

Arguments:

    N/A

Return Value:

    N/A

--*/
{
}



void
CFtpAccountsPage::DoDataExchange(
    IN CDataExchange * pDX
    )
/*++

Routine Description:

    Initialise/Store control data

Arguments:

    CDataExchange * pDX - DDX/DDV control structure

Return Value:

    None

--*/
{
    CInetPropertyPage::DoDataExchange(pDX);

    //{{AFX_DATA_MAP(CFtpAccountsPage)
    DDX_Check(pDX, IDC_CHECK_ALLOW_ANONYMOUS, m_fAllowAnonymous);
    DDX_Check(pDX, IDC_CHECK_ONLY_ANYMOUS, m_fOnlyAnonymous);
    DDX_Check(pDX, IDC_CHECK_ENABLE_PW_SYNCHRONIZATION, m_fPasswordSync);
    DDX_Control(pDX, IDC_BUTTON_ADD, m_button_Add);
    DDX_Control(pDX, IDC_EDIT_PASSWORD, m_edit_Password);
    DDX_Control(pDX, IDC_EDIT_USERNAME, m_edit_UserName);
    DDX_Control(pDX, IDC_STATIC_PW, m_static_Password);
    DDX_Control(pDX, IDC_STATIC_USERNAME, m_static_UserName);
    DDX_Control(pDX, IDC_STATIC_ACCOUNT_PROMPT, m_static_AccountPrompt);
    DDX_Control(pDX, IDC_BUTTON_CHECK_PASSWORD, m_button_CheckPassword);
    DDX_Control(pDX, IDC_BUTTON_BROWSE_USER, m_button_Browse);
    DDX_Control(pDX, IDC_BUTTON_DELETE, m_button_RemoveAdministrator);
    DDX_Control(pDX, IDC_CHECK_ENABLE_PW_SYNCHRONIZATION, m_chk_PasswordSync);
    DDX_Control(pDX, IDC_CHECK_ALLOW_ANONYMOUS, m_chk_AllowAnymous);
    DDX_Control(pDX, IDC_CHECK_ONLY_ANYMOUS, m_chk_OnlyAnonymous);
    //}}AFX_DATA_MAP

    //
    // Private DDX/DDV Routines
    //
    DDX_Control(pDX, IDC_LIST_ADMINISTRATORS, m_list_Administrators);

    //
    // Set password/username only during load stage,
    // or if saving when allowing anonymous logons
    //
    if (!pDX->m_bSaveAndValidate || m_fAllowAnonymous)
    {
        DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName);
        DDV_MinMaxChars(pDX, m_strUserName, 1, UNLEN);

        //
        // Some people have a tendency to add "\\" before
        // the computer name in user accounts.  Fix this here.
        //
        m_strUserName.TrimLeft();

        while (*m_strUserName == '\\')
        {
            m_strUserName = m_strUserName.Mid(2);
        }


        //
        // Display the remote password sync message if
        // password sync is on, the account is not local,
        // password sync has changed or username has changed
        // and the message hasn't already be shown.
        //
        if (pDX->m_bSaveAndValidate && m_fPasswordSync 
            && !IsLocalAccount(m_strUserName)
            && (m_fPasswordSyncChanged || m_fUserNameChanged)
            && !m_fPasswordSyncMsgShown
            )
        {
            if (!NoYesMessageBox(IDS_WRN_PWSYNC))
            {
                pDX->Fail();
            }

            //
            // Don't show it again
            //
            m_fPasswordSyncMsgShown = TRUE;
        }

        if (!m_fPasswordSync || !pDX->m_bSaveAndValidate)
        {
            DDX_Password(
                pDX, 
                IDC_EDIT_PASSWORD, 
                m_strPassword, 
                g_lpszDummyPassword
                );
        }

        if (!m_fPasswordSync)
        {
            DDV_MaxChars(pDX, m_strPassword, PWLEN);
        }
    }

}


//
// Message Map
//
BEGIN_MESSAGE_MAP(CFtpAccountsPage, CInetPropertyPage)
    //{{AFX_MSG_MAP(CFtpAccountsPage)
    ON_BN_CLICKED(IDC_BUTTON_CHECK_PASSWORD, OnButtonCheckPassword)
    ON_BN_CLICKED(IDC_BUTTON_ADD, OnButtonAdd)
    ON_CBN_SELCHANGE(IDC_LIST_ADMINISTRATORS, OnSelchangeListAdministrators)
    ON_BN_CLICKED(IDC_BUTTON_DELETE, OnButtonDelete)
    ON_BN_CLICKED(IDC_CHECK_ENABLE_PW_SYNCHRONIZATION, OnCheckEnablePwSynchronization)
    ON_EN_CHANGE(IDC_EDIT_USERNAME, OnChangeEditUsername)
    //}}AFX_MSG_MAP

    ON_EN_CHANGE(IDC_EDIT_PASSWORD, OnItemChanged)
    ON_BN_CLICKED(IDC_CHECK_ALLOW_ANONYMOUS, OnCheckAllowAnonymous)
    ON_BN_CLICKED(IDC_CHECK_ONLY_ANYMOUS, OnCheckAllowOnlyAnonymous)
    ON_BN_CLICKED(IDC_BUTTON_BROWSE_USER, OnButtonBrowseUser)

END_MESSAGE_MAP()



void
CFtpAccountsPage::SetControlStates(
    IN BOOL fAllowAnonymous
    )
/*++

Routine Description:

    Set the states of the dialog control depending on its current
    values.

Arguments:

    BOOL fAllowAnonymous : If TRUE, 'allow anonymous' is on.

Return Value:

    None

--*/
{
    m_static_Password.EnableWindow(
        fAllowAnonymous 
     && !m_fPasswordSync 
     && HasAdminAccess()
        );

    m_edit_Password.EnableWindow(
        fAllowAnonymous 
     && !m_fPasswordSync
     && HasAdminAccess()
        );

    m_button_CheckPassword.EnableWindow(
        fAllowAnonymous 
     && !m_fPasswordSync
     && HasAdminAccess()
        );

    m_static_AccountPrompt.EnableWindow(fAllowAnonymous);
    m_static_UserName.EnableWindow(fAllowAnonymous && HasAdminAccess());
    m_edit_UserName.EnableWindow(fAllowAnonymous && HasAdminAccess());
    m_button_Browse.EnableWindow(fAllowAnonymous && HasAdminAccess());
    m_chk_PasswordSync.EnableWindow(fAllowAnonymous && HasAdminAccess());
    m_chk_OnlyAnonymous.EnableWindow(fAllowAnonymous);
}



BOOL
CFtpAccountsPage::SetAdminRemoveState()
/*++

Routine Description:

    Set the state of the remove button depending on the selection in the
    administrators listbox.  Remove is only enabled if ALL selected
    items are removable.

Arguments:

    None

Return Value:

    TRUE if the remove button is enabled.

--*/
{
    int nSel = 0;
    int cSelectedItems = 0;
    BOOL fAllDeletable = TRUE;
    CAccessEntry * pAccess;

    while ((pAccess = m_list_Administrators.GetNextSelectedItem(&nSel)) != NULL)
    {
        ++cSelectedItems;

        if (!pAccess->IsDeletable())
        {
            fAllDeletable = FALSE;
            break;
        }

        ++nSel;
    }

    fAllDeletable = fAllDeletable && (cSelectedItems > 0);

    m_button_RemoveAdministrator.EnableWindow(
        fAllDeletable 
     && HasOperatorList()
     && HasAdminAccess()
        );

    return fAllDeletable;
}



//
// Message Handlers
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<


BOOL
CFtpAccountsPage::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.

--*/
{
    CInetPropertyPage::OnInitDialog();

    m_list_Administrators.Initialize();

    CWaitCursor wait;

    //
    // Build the ACL list
    //        
    CError err(BuildAclOblistFromBlob(
        ((CFtpSheet *)GetSheet())->GetInstanceProperties().m_acl,
        m_oblSID
        ));

    err.MessageBoxOnFailure();

    m_list_Administrators.FillAccessListBox(m_oblSID);

    //
    // check if the operators controls are accessible
    //
    m_button_Add.EnableWindow(HasOperatorList() && HasAdminAccess());
    m_list_Administrators.EnableWindow(HasOperatorList() && HasAdminAccess());

    GetDlgItem(IDC_STATIC_OPERATOR_PROMPT1)->EnableWindow(
        HasOperatorList() 
     && HasAdminAccess()
        );

    GetDlgItem(IDC_STATIC_OPERATOR_PROMPT2)->EnableWindow(
        HasOperatorList() 
     && HasAdminAccess()
        );

    SetControlStates(m_fAllowAnonymous);
    SetAdminRemoveState();

    return TRUE;
}



/* virtual */
HRESULT
CFtpAccountsPage::FetchLoadedValues()
/*++

Routine Description:
    
    Move configuration data from sheet to dialog controls

Arguments:

    None

Return Value:

    HRESULT

--*/
{
    CError err;

    BEGIN_META_INST_READ(CFtpSheet)
        FETCH_INST_DATA_FROM_SHEET(m_strUserName);
        FETCH_INST_DATA_FROM_SHEET(m_strPassword);
        FETCH_INST_DATA_FROM_SHEET(m_fAllowAnonymous);
        FETCH_INST_DATA_FROM_SHEET(m_fOnlyAnonymous);
        FETCH_INST_DATA_FROM_SHEET(m_fPasswordSync);
    END_META_INST_READ(err)

    return err;
}



/* virtual */
HRESULT
CFtpAccountsPage::SaveInfo()
/*++

Routine Description:

    Save the information on this property page

Arguments:

    None

Return Value:

    Error return code

--*/
{
    ASSERT(IsDirty());

    TRACEEOLID("Saving FTP service page now...");

    //
    // Use m_ notation because the message crackers require it
    //
    CBlob m_acl;
    BOOL fAclDirty = BuildAclBlob(m_oblSID, m_acl);

    CError err;

    BeginWaitCursor();
    BEGIN_META_INST_WRITE(CFtpSheet)
        STORE_INST_DATA_ON_SHEET(m_strUserName)
        STORE_INST_DATA_ON_SHEET(m_fOnlyAnonymous)
        STORE_INST_DATA_ON_SHEET(m_fAllowAnonymous)
        STORE_INST_DATA_ON_SHEET(m_fPasswordSync)
        if (fAclDirty)
        {
            STORE_INST_DATA_ON_SHEET(m_acl)
        }
        if (m_fPasswordSync)
        {
            //
            // Delete password
            //
            // CODEWORK: Shouldn't need to know ID number.
            // Implement m_fDelete flag in CMP template maybe?
            //
            FLAG_INST_DATA_FOR_DELETION(MD_ANONYMOUS_PWD);
        }
        else
        {
            STORE_INST_DATA_ON_SHEET(m_strPassword);
        }
    END_META_INST_WRITE(err)
    EndWaitCursor();

    return err;
}



void
CFtpAccountsPage::OnItemChanged()
/*++

Routine Description:

    Register a change in control value on this page.  Mark the page as dirty.
    All change messages map to this function

Arguments:

    None

Return Value:

    None

--*/
{
    SetModified(TRUE);
    SetControlStates(m_chk_AllowAnymous.GetCheck() > 0);
}



void
CFtpAccountsPage::OnCheckAllowAnonymous()
/*++

Routine Description:

    Respond to 'allow anonymous' checkbox being pressed

Arguments:

    None

Return Value:

    None

--*/
{
    if (m_chk_AllowAnymous.GetCheck() == 0)
    {
        //
        // Show security warning
        //
        CClearTxtDlg dlg;

        if (dlg.DoModal() != IDOK)
        {
            m_chk_AllowAnymous.SetCheck(1);
            return;
        }
    }

    SetControlStates(m_chk_AllowAnymous.GetCheck() > 0);
    OnItemChanged();
}



void
CFtpAccountsPage::OnCheckAllowOnlyAnonymous()
/*++

Routine Description:

    Respond to 'allow only anonymous' checkbox being pressed

Arguments:

    None

Return Value:

    None

--*/
{
    if (m_chk_OnlyAnonymous.GetCheck() == 0)
    {
        //
        // Show security warning
        //
        CClearTxtDlg dlg;

        if (dlg.DoModal() != IDOK)
        {
            m_chk_OnlyAnonymous.SetCheck(1);
            return;
        }
    }

    OnItemChanged();
}



void 
CFtpAccountsPage::OnButtonBrowseUser()
/*++

Routine Description:

    User browser button has been pressed.  Browse for IUSR account name

Arguments:

    None

Return Value:

    None

--*/
{
    CString str;

    if (GetIUsrAccount(str))
    {
        //
        // If the name is non-local (determined by having
        // a slash in the name, password sync is disabled,
        // and a password should be entered.
        //
        m_edit_UserName.SetWindowText(str);

        if (!(m_fPasswordSync = IsLocalAccount(str)))
        {
            m_edit_Password.SetWindowText(_T(""));
            m_edit_Password.SetFocus();
        }

        m_chk_PasswordSync.SetCheck(m_fPasswordSync);
        OnItemChanged();
    }
}



void 
CFtpAccountsPage::OnButtonCheckPassword() 
/*++

Routine Description:

    Check password button has been pressed.

Arguments:

    None

Return Value:

    None

--*/
{
    if (!UpdateData(TRUE))
    {
        return;
    }

    CError err(CComAuthInfo::VerifyUserPassword(m_strUserName, m_strPassword));

    if (!err.MessageBoxOnFailure())
    {
        ::AfxMessageBox(IDS_PASSWORD_OK);
    }
}



void
CFtpAccountsPage::OnButtonAdd()
/*++

Routine Description:

    'Add' button has been pressed

Arguments:

    None

Return Value:

    None

--*/
{
    if (m_list_Administrators.AddToAccessList(
        this,
        QueryServerName(),
        m_oblSID
        ))
    {
        OnItemChanged();
    }

    SetAdminRemoveState();
}



void
CFtpAccountsPage::OnSelchangeListAdministrators()
/*++

Routine Description:

    Selection Change in admin list box handler

Arguments:

    None.

Return Value:

    None

--*/
{
    SetAdminRemoveState();
}



void 
CFtpAccountsPage::OnButtonDelete()
/*++

Routine Description:

    Delete all selected items in the list box

Arguments:

    None.

Return Value:

    None

--*/
{
    int nSel = 0;
    int cChanges = 0;
    CAccessEntry * pAccess;

    while ((pAccess = m_list_Administrators.GetNextSelectedItem(&nSel)) != NULL)
    {
        //
        // Remove button should be disabled unless all selected
        // items are deletable
        //
        ASSERT(pAccess->IsDeletable());
        if (pAccess->IsDeletable())
        {
            ++cChanges;
            pAccess->FlagForDeletion();
            m_list_Administrators.DeleteString(nSel);

            //
            // Don't advance counter to account for shift
            //
            continue;
        }

        ++nSel;
    }

    if (cChanges)
    {
        OnItemChanged();
    }

    if (!SetAdminRemoveState())
    {
        m_button_Add.SetFocus();
    }
}



void 
CFtpAccountsPage::OnCheckEnablePwSynchronization() 
/*++

Routine Description:

    Handler for 'enable password synchronization' checkbox press

Arguments:

    None

Return Value:

    None

--*/
{
    m_fPasswordSyncChanged = TRUE;
    m_fPasswordSync = !m_fPasswordSync;
    OnItemChanged();
    SetControlStates(m_chk_AllowAnymous.GetCheck() > 0);

    if (!m_fPasswordSync )
    {
        m_edit_Password.SetSel(0,-1);
        m_edit_Password.SetFocus();
    }
}



void 
CFtpAccountsPage::OnChangeEditUsername() 
/*++

Routine description:

    Handler for 'username' edit box change messages

Arguments:

    None

Return Value:

    None

--*/
{
    m_fUserNameChanged = TRUE;
    OnItemChanged();
}