windows-nt/Source/XPSP1/NT/inetsrv/iis/ui/admin/w3scfg/mmmdlg.cpp

1232 lines
21 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1994-1998 Microsoft Corporation
Module Name :
mmmdlg.cpp
Abstract:
Multi-multi-multi dialog editor
Author:
Ronald Meijer (ronaldm)
Project:
Internet Services Manager
Revision History:
--*/
//
// Include Files
//
#include "stdafx.h"
#include "w3scfg.h"
#include "mmmdlg.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//
// Registry key name for this dialog
//
const TCHAR g_szRegKeyIP[] = _T("MMMIpBindings");
const TCHAR g_szRegKeySSL[] = _T("MMMSSLBindings");
//
// IP Bindings Listbox Column Definitions
//
static const ODL_COLUMN_DEF g_aIPColumns[] =
{
// ===============================================
// Weight Label
// ===============================================
{ 6, IDS_MMM_IP_ADDRESS, },
{ 3, IDS_MMM_TCP_PORT, },
{ 10, IDS_MMM_DOMAIN_NAME, },
};
//
// SSL Bindings Listbox Column Definitions
//
static const ODL_COLUMN_DEF g_aSSLColumns[] =
{
// ===============================================
// Weight Label
// ===============================================
{ 2, IDS_MMM_IP_ADDRESS, },
{ 1, IDS_MMM_SSL_PORT, },
};
#define NUM_COLUMNS(cols) (sizeof(cols) / sizeof(cols[0]))
IMPLEMENT_DYNAMIC(CMMMListBox, CRMCListBox);
//
// Bitmap indices
//
enum
{
BMPID_BINDING,
//
// Don't move this one
//
BMPID_TOTAL
};
const int CMMMListBox::nBitmaps = BMPID_TOTAL;
CMMMListBox::CMMMListBox(
IN LPCTSTR lpszRegKey,
IN int cColumns,
IN const ODL_COLUMN_DEF * pColumns
)
/*++
Routine Description:
Constructor
Arguments:
None
Return Value:
N/A
--*/
: CHeaderListBox(HLS_STRETCH, lpszRegKey),
m_cColumns(cColumns),
m_pColumns(pColumns)
{
VERIFY(m_strDefaultIP.LoadString(IDS_DEFAULT));
VERIFY(m_strNoPort.LoadString(IDS_MMM_NA));
}
void
CMMMListBox::DrawItemEx(
IN CRMCListBoxDrawStruct & ds
)
/*++
Routine Description:
Draw item in the listbox
Arguments:
CRMCListBoxDrawStruct & ds : Draw item structure
Return Value:
None
--*/
{
CString & strBinding = *(CString *)ds.m_ItemData;
TRACEEOLID(strBinding);
UINT nPort;
LPCTSTR lp;
CString strHostHeader;
CString strPort;
CString strIP;
CIPAddress iaIpAddress;
CInstanceProps::CrackBinding(
strBinding,
iaIpAddress,
nPort,
strHostHeader
);
//
// Display Granted/Denied with appropriate bitmap
//
DrawBitmap(ds, 0, BMPID_BINDING);
if (iaIpAddress.IsZeroValue())
{
lp = m_strDefaultIP;
}
else
{
lp = iaIpAddress.QueryIPAddress(strIP);
}
ColumnText(ds, 0, TRUE, lp);
if (nPort > 0)
{
strPort.Format(_T("%u"), nPort);
lp = strPort;
}
else
{
lp = m_strNoPort;
}
ColumnText(ds, 1, FALSE, lp);
ColumnText(ds, 2, FALSE, strHostHeader);
}
/* virtual */
BOOL
CMMMListBox::Initialize()
/*++
Routine Description:
Initialize the listbox. Insert the columns as requested, and lay
them out appropriately
Arguments:
None
Return Value:
TRUE for succesful initialisation, FALSE otherwise
--*/
{
if (!CHeaderListBox::Initialize())
{
return FALSE;
}
//
// Build all columns
//
for (int nCol = 0; nCol < m_cColumns; ++nCol)
{
InsertColumn(nCol, m_pColumns[nCol].nWeight, m_pColumns[nCol].nLabelID);
}
//
// Try to set the widths from the stored registry value,
// otherwise distribute according to column weights specified
//
if (!SetWidthsFromReg())
{
DistributeColumns();
}
return TRUE;
}
void AFXAPI
DDXV_UINT(
IN CDataExchange * pDX,
IN UINT nID,
IN OUT UINT & uValue,
IN UINT uMin,
IN UINT uMax,
IN UINT nEmptyErrorMsg OPTIONAL
)
/*++
Routine Description:
DDX/DDV Function that uses a space to denote a 0 value
Arguments:
CDataExchange * pDX : Data exchange object
UINT nID : Resource ID
OUT UINT & uValue : Value
UINT uMin : Minimum value
UINT uMax : Maximum value
UINT nEmptyErrorMsg : Error message ID for empty unit, or 0 if empty OK
Return Value:
None.
--*/
{
ASSERT(uMin <= uMax);
CWnd * pWnd = CWnd::FromHandle(pDX->PrepareEditCtrl(nID));
ASSERT(pWnd != NULL);
if (pDX->m_bSaveAndValidate)
{
if (pWnd->GetWindowTextLength() > 0)
{
DDX_Text(pDX, nID, uValue);
DDV_MinMaxUInt(pDX, uValue, uMin, uMax);
}
else
{
uValue = 0;
if (nEmptyErrorMsg)
{
::AfxMessageBox(nEmptyErrorMsg);
pDX->Fail();
}
}
}
else
{
if (uValue != 0)
{
DDX_Text(pDX, nID, uValue);
}
else
{
pWnd->SetWindowText(_T(""));
}
}
}
BOOL
IsBindingUnique(
IN CString & strBinding,
IN CStringList & strlBindings,
IN int iCurrent OPTIONAL
)
/*++
Routine Description:
Helper function to determine if a binding is unique.
Arguments:
CString & strBinding : Binding string
CStringList & strlBindings : List of bindings
int iCurrent : Index of "current" item.
Not used for uniqueness checking.
Return Value:
TRUE if the binding is unique, FALSE otherwise.
--*/
{
int iItem = 0;
for(POSITION pos = strlBindings.GetHeadPosition(); pos != NULL; /**/ )
{
CString & str = strlBindings.GetNext(pos);
if (iItem != iCurrent && &str != &strBinding && str == strBinding)
{
//
// Not unique!
//
return FALSE;
}
++iItem;
}
//
// Unique
//
return TRUE;
}
//
// Multi-multi-multi editing dialog
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
CMMMEditDlg::CMMMEditDlg(
IN CString & strServerName,
IN CStringList & strlBindings,
IN CStringList & strlOtherBindings,
IN OUT CString & entry,
IN BOOL fIPBinding,
IN CWnd * pParent OPTIONAL
)
/*++
Routine Description:
Constructor
Arguments:
CString & strServerName : Server name
CStringList & strlBindings : bindings
CStringList & strlOtherBindings : "other" bindings list
CString & entry : Entry being edited
BOOL fIPBinding : TRUE for IP, FALSE for SSL
CWnd * pParent : Optional parent window
Return Value:
N/A
--*/
: CDialog(CMMMEditDlg::IDD, pParent),
m_strServerName(strServerName),
m_strlBindings(strlBindings),
m_strlOtherBindings(strlOtherBindings),
m_entry(entry),
m_fIPBinding(fIPBinding),
m_nIpAddressSel(-1)
{
#if 0 // Keep class wizard happy
//{{AFX_DATA_INIT(CMMMEditDlg)
m_nIpAddressSel = -1;
//}}AFX_DATA_INIT
#endif // 0
CInstanceProps::CrackBinding(
m_entry,
m_iaIpAddress,
m_nPort,
m_strDomainName
);
}
void
CMMMEditDlg::DoDataExchange(
IN CDataExchange * pDX
)
/*++
Routine Description:
Initialise/Store control data
Arguments:
CDataExchange * pDX - DDX/DDV control structure
Return Value:
None
--*/
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMMMEditDlg)
DDX_Text(pDX, IDC_EDIT_DOMAIN_NAME, m_strDomainName);
DDV_MaxChars(pDX, m_strDomainName, MAX_PATH);
DDX_Control(pDX, IDC_COMBO_IP_ADDRESSES, m_combo_IpAddresses);
DDX_Control(pDX, IDC_STATIC_PORT, m_static_Port);
//}}AFX_DATA_MAP
DDXV_UINT(pDX, IDC_EDIT_PORT, m_nPort, 1, 65535);
DDX_CBIndex(pDX, IDC_COMBO_IP_ADDRESSES, m_nIpAddressSel);
if (pDX->m_bSaveAndValidate && !FetchIpAddressFromCombo(
m_combo_IpAddresses,
m_oblIpAddresses,
m_iaIpAddress
))
{
pDX->Fail();
}
}
//
// Message Map
//
BEGIN_MESSAGE_MAP(CMMMEditDlg, CDialog)
//{{AFX_MSG_MAP(CMMMEditDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//
// Message Handlers
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
BOOL
CMMMEditDlg::OnInitDialog()
/*++
Routine Description:
WM_INITDIALOG handler. Initialize the dialog.
Arguments:
None.
Return Value:
TRUE if focus is to be set automatically, FALSE if the focus
is already set.
--*/
{
CDialog::OnInitDialog();
BeginWaitCursor();
PopulateComboWithKnownIpAddresses(
m_strServerName,
m_combo_IpAddresses,
m_iaIpAddress,
m_oblIpAddresses,
m_nIpAddressSel
);
EndWaitCursor();
//
// Configure dialog for either SSL or IP binding editing
//
CString str;
VERIFY(str.LoadString(m_fIPBinding
? IDS_EDIT_MMM_TITLE
: IDS_EDIT_SSL_MMM_TITLE));
SetWindowText(str);
VERIFY(str.LoadString(m_fIPBinding
? IDS_TCP_PORT
: IDS_SSL_PORT));
m_static_Port.SetWindowText(str);
ActivateControl(*GetDlgItem(IDC_STATIC_HEADER_NAME), m_fIPBinding);
ActivateControl(*GetDlgItem(IDC_EDIT_DOMAIN_NAME), m_fIPBinding);
return TRUE;
}
void
CMMMEditDlg::OnOK()
/*++
Routine Description:
OK button handler. Verify values are acceptable, and change
Arguments:
None
Return Value:
None
--*/
{
if (!UpdateData(TRUE))
{
return;
}
if (m_nPort == 0)
{
::AfxMessageBox(IDS_NO_PORT);
return;
}
CString strOldBinding(m_entry);
CInstanceProps::BuildBinding(m_entry, m_iaIpAddress, m_nPort, m_strDomainName);
//
// Ensure the ip/address doesn't exist in the "other" binding list
//
if (CInstanceProps::IsPortInUse(m_strlOtherBindings, m_iaIpAddress, m_nPort))
{
//
// Restore the old binding
//
m_entry = strOldBinding;
::AfxMessageBox(m_fIPBinding
? IDS_ERR_PORT_IN_USE_SSL
: IDS_ERR_PORT_IN_USE_TCP);
return;
}
if (!IsBindingUnique(m_entry, m_strlBindings))
{
//
// Restore the old binding
//
m_entry = strOldBinding;
::AfxMessageBox(IDS_ERR_BINDING);
return;
}
CDialog::OnOK();
}
//
// Multi-multi-multi list dialog
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
CMMMDlg::CMMMDlg(
IN LPCTSTR lpServerName,
IN DWORD dwInstance,
IN OUT CStringList & strlBindings,
IN OUT CStringList & strlSecureBindings,
IN CWnd * pParent OPTIONAL
)
/*++
Routine Description:
Constructor
Arguments:
CStringList & strlBindings : Service bindings
CStringList & strlSecureBindings : SSL port bindings
CWnd * pParent : Optional parent window
Return Value:
N/A
--*/
: CDialog(CMMMDlg::IDD, pParent),
m_ListBoxRes(
IDB_BINDINGS,
CMMMListBox::nBitmaps
),
m_list_Bindings(g_szRegKeyIP, NUM_COLUMNS(g_aIPColumns), g_aIPColumns),
m_list_SSLBindings(g_szRegKeySSL, NUM_COLUMNS(g_aSSLColumns), g_aSSLColumns),
m_strlBindings(),
m_strlSecureBindings(),
m_strServerName(lpServerName),
m_fCertInstalled(IsCertInstalledOnServer(lpServerName, dwInstance)),
m_fDirty(FALSE)
{
//{{AFX_DATA_INIT(CMMMDlg)
//}}AFX_DATA_INIT
m_strlBindings.AddTail(&strlBindings);
m_strlSecureBindings.AddTail(&strlSecureBindings);
m_list_Bindings.AttachResources(&m_ListBoxRes);
m_list_SSLBindings.AttachResources(&m_ListBoxRes);
}
void
CMMMDlg::DoDataExchange(
IN CDataExchange * pDX
)
/*++
Routine Description:
Initialise/Store control data
Arguments:
CDataExchange * pDX - DDX/DDV control structure
Return Value:
None
--*/
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CMMMDlg)
DDX_Control(pDX, IDC_BUTTON_ADD, m_button_Add);
DDX_Control(pDX, IDC_BUTTON_REMOVE, m_button_Remove);
DDX_Control(pDX, IDC_BUTTON_EDIT, m_button_Edit);
DDX_Control(pDX, IDC_BUTTON_ADD_SSL, m_button_AddSSL);
DDX_Control(pDX, IDC_BUTTON_REMOVE_SSL, m_button_RemoveSSL);
DDX_Control(pDX, IDC_BUTTON_EDIT_SSL, m_button_EditSSL);
DDX_Control(pDX, IDOK, m_button_OK);
//}}AFX_DATA_MAP
DDX_Control(pDX, IDC_LIST_MMM, m_list_Bindings);
DDX_Control(pDX, IDC_LIST_SSL_MMM, m_list_SSLBindings);
}
BOOL
CMMMDlg::OnItemChanged()
/*++
Routine Description:
Mark that the dialog as dirty
Arguments:
None
Return Value:
TRUE if the remove button is enabled
--*/
{
m_fDirty = TRUE;
return SetControlStates();
}
BOOL
CMMMDlg::SetControlStates()
/*++
Routine Description:
Set the enabled state of the controls depending on the current
values in the dialog
Arguments:
None
Return Value:
TRUE if the remove button is enabled
--*/
{
BOOL fSel = m_list_Bindings.GetSelCount() > 0;
m_button_Remove.EnableWindow(fSel);
m_button_Edit.EnableWindow(m_list_Bindings.GetSelCount() == 1);
m_button_RemoveSSL.EnableWindow(m_list_SSLBindings.GetSelCount() > 0);
m_button_EditSSL.EnableWindow(m_list_SSLBindings.GetSelCount() == 1);
m_button_OK.EnableWindow(m_fDirty && m_list_Bindings.GetCount() > 0);
return fSel;
}
void
CMMMDlg::AddBindings(
IN CMMMListBox & list,
IN CStringList & strlBindings
)
/*++
Routine Description:
Add bindings information to the specified listbox
Arguments:
CMMMListBox & list : MMM Listbox (SSL or IP)
CStringList & strlBindings : SSL or IP bindings
Return Value:
None
--*/
{
for (POSITION pos = strlBindings.GetHeadPosition(); pos != NULL; /**/)
{
CString & strBinding = strlBindings.GetNext(pos);
list.AddItem(strBinding);
}
}
//
// Message Map
//
BEGIN_MESSAGE_MAP(CMMMDlg, CDialog)
//{{AFX_MSG_MAP(CMMMDlg)
ON_BN_CLICKED(IDC_BUTTON_ADD, OnButtonAdd)
ON_BN_CLICKED(IDC_BUTTON_EDIT, OnButtonEdit)
ON_BN_CLICKED(IDC_BUTTON_REMOVE, OnButtonRemove)
ON_BN_CLICKED(IDC_BUTTON_ADD_SSL, OnButtonAddSsl)
ON_BN_CLICKED(IDC_BUTTON_EDIT_SSL, OnButtonEditSsl)
ON_BN_CLICKED(IDC_BUTTON_REMOVE_SSL, OnButtonRemoveSsl)
ON_LBN_DBLCLK(IDC_LIST_MMM, OnDblclkListMmm)
ON_LBN_DBLCLK(IDC_LIST_SSL_MMM, OnDblclkListSslMmm)
ON_LBN_SELCHANGE(IDC_LIST_MMM, OnSelchangeListMmm)
ON_LBN_SELCHANGE(IDC_LIST_SSL_MMM, OnSelchangeListSslMmm)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
//
// Message Handlers
//
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
void
CMMMDlg::OnButtonAdd()
/*++
Routine Description:
Add button handler
Arguments:
None
Return Value:
None
--*/
{
//
// Add entry
//
CString strEntry;
CMMMEditDlg dlg(
m_strServerName,
m_strlBindings,
m_strlSecureBindings,
strEntry,
TRUE, /* IP binding */
this
);
if (dlg.DoModal() == IDOK)
{
POSITION pos = m_strlBindings.AddTail(strEntry);
int nSel = m_list_Bindings.AddItem(m_strlBindings.GetAt(pos));
m_list_Bindings.SetCurSel(nSel);
OnItemChanged();
}
}
void
CMMMDlg::OnButtonEdit()
/*++
Routine Description:
Edit button handler
Arguments:
None
Return Value:
None
--*/
{
//
// Edit entry
//
int nCurSel = m_list_Bindings.GetCurSel();
if (nCurSel != LB_ERR)
{
CString & strEntry = m_list_Bindings.GetItem(nCurSel);
CMMMEditDlg dlg(
m_strServerName,
m_strlBindings,
m_strlSecureBindings,
strEntry,
TRUE, /* IP binding */
this
);
if (dlg.DoModal() == IDOK)
{
m_list_Bindings.InvalidateSelection(nCurSel);
OnItemChanged();
}
}
}
void
CMMMDlg::OnButtonRemove()
/*++
Routine Description:
Remove button handler
Arguments:
None
Return Value:
None
--*/
{
int nCurSel = m_list_Bindings.GetCurSel();
int nSel = 0;
int cChanges = 0;
while (nSel < m_list_Bindings.GetCount())
{
//
// Remove Selected entry
//
if (m_list_Bindings.GetSel(nSel))
{
m_strlBindings.RemoveAt(m_strlBindings.FindIndex(nSel));
m_list_Bindings.DeleteString(nSel);
++cChanges;
continue;
}
++nSel;
}
if (cChanges)
{
m_list_Bindings.SetCurSel(nCurSel);
if (!OnItemChanged())
{
m_button_Add.SetFocus();
}
}
/*
//
// Remove Selected entry
//
int nCurSel = m_list_Bindings.GetCurSel();
if (nCurSel != LB_ERR)
{
m_strlBindings.RemoveAt(m_strlBindings.FindIndex(nCurSel));
m_list_Bindings.DeleteString(nCurSel);
if (nCurSel >= m_list_Bindings.GetCount())
{
--nCurSel;
}
m_list_Bindings.SetCurSel(nCurSel);
if (!OnItemChanged())
{
m_button_Add.SetFocus();
}
}
*/
}
void
CMMMDlg::OnDblclkListMmm()
/*++
Routine Description:
Double click notification handler
Arguments:
None
Return Value:
None
--*/
{
OnButtonEdit();
}
void
CMMMDlg::OnSelchangeListMmm()
/*++
Routine Description:
selection change notification handler
Arguments:
None
Return Value:
None
--*/
{
SetControlStates();
}
void
CMMMDlg::OnButtonAddSsl()
/*++
Routine Description:
'Add SSL' button handler
Arguments:
None
Return Value:
None
--*/
{
//
// Add entry
//
CString strEntry;
CMMMEditDlg dlg(
m_strServerName,
m_strlSecureBindings,
m_strlBindings,
strEntry,
FALSE, /* SSL binding */
this
);
if (dlg.DoModal() == IDOK)
{
POSITION pos = m_strlSecureBindings.AddTail(strEntry);
int nSel = m_list_SSLBindings.AddItem(m_strlSecureBindings.GetAt(pos));
m_list_SSLBindings.SetCurSel(nSel);
OnItemChanged();
}
}
void
CMMMDlg::OnButtonEditSsl()
/*++
Routine Description:
'Edit SSL' button handler
Arguments:
None
Return Value:
None
--*/
{
//
// Edit entry
//
int nCurSel = m_list_SSLBindings.GetCurSel();
if (nCurSel != LB_ERR)
{
CString & strEntry = m_list_SSLBindings.GetItem(nCurSel);
CMMMEditDlg dlg(
m_strServerName,
m_strlSecureBindings,
m_strlBindings,
strEntry,
FALSE, /* SSL binding */
this
);
if (dlg.DoModal() == IDOK)
{
m_list_SSLBindings.InvalidateSelection(nCurSel);
OnItemChanged();
}
}
}
void
CMMMDlg::OnButtonRemoveSsl()
/*++
Routine Description:
'Remove SSL' button handler
Arguments:
None
Return Value:
None
--*/
{
int nCurSel = m_list_SSLBindings.GetCurSel();
int nSel = 0;
int cChanges = 0;
while (nSel < m_list_SSLBindings.GetCount())
{
//
// Remove Selected entry
//
if (m_list_SSLBindings.GetSel(nSel))
{
m_strlSecureBindings.RemoveAt(m_strlSecureBindings.FindIndex(nSel));
m_list_SSLBindings.DeleteString(nSel);
++cChanges;
continue;
}
++nSel;
}
if (cChanges)
{
m_list_SSLBindings.SetCurSel(nCurSel);
OnItemChanged();
if (m_list_SSLBindings.GetSelCount() == 0)
{
//
// Remove will be disabled
//
m_button_AddSSL.SetFocus();
}
}
/*
//
// Remove Selected entry
//
int nCurSel = m_list_SSLBindings.GetCurSel();
if (nCurSel != LB_ERR)
{
m_strlSecureBindings.RemoveAt(m_strlSecureBindings.FindIndex(nCurSel));
m_list_SSLBindings.DeleteString(nCurSel);
if (nCurSel >= m_list_SSLBindings.GetCount())
{
--nCurSel;
}
m_list_SSLBindings.SetCurSel(nCurSel);
OnItemChanged();
if (m_list_SSLBindings.GetCurSel() == LB_ERR)
{
//
// Remove will be disabled
//
m_button_AddSSL.SetFocus();
}
}
*/
}
void
CMMMDlg::OnDblclkListSslMmm()
/*++
Routine Description:
SSL List 'double click' handler
Arguments:
None
Return Value:
None
--*/
{
OnButtonEditSsl();
}
void
CMMMDlg::OnSelchangeListSslMmm()
/*++
Routine Description:
SSL List 'selection change' handler
Arguments:
None
Return Value:
None
--*/
{
SetControlStates();
}
BOOL
CMMMDlg::OnInitDialog()
/*++
Routine Description:
WM_INITDIALOG handler. Initialize the dialog.
Arguments:
None.
Return Value:
TRUE if focus is to be set automatically, FALSE if the focus
is already set.
--*/
{
CDialog::OnInitDialog();
m_list_Bindings.Initialize();
m_list_SSLBindings.Initialize();
AddBindings(m_list_Bindings, m_strlBindings);
AddBindings(m_list_SSLBindings, m_strlSecureBindings);
//
// No certificates, no SSL
//
GetDlgItem(IDC_GROUP_SSL)->EnableWindow(m_fCertInstalled);
m_list_SSLBindings.EnableWindow(m_fCertInstalled);
m_button_AddSSL.EnableWindow(m_fCertInstalled);
m_button_RemoveSSL.EnableWindow(m_fCertInstalled);
m_button_EditSSL.EnableWindow(m_fCertInstalled);
SetControlStates();
return TRUE;
}