windows-nt/Source/XPSP1/NT/net/homenet/config/dll/sadlg.cpp
2020-09-26 16:20:57 +08:00

2219 lines
62 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright (c) 1998, Microsoft Corporation, all rights reserved
//
// sadlg.c
// Remote Access Common Dialog APIs
// Shared Access Settings property sheet
//
// 10/20/1998 Abolade Gbadegesin
//
//#include "pch.h"
#pragma hdrstop
#include "sautil.h"
#include <winsock2.h>
#include "sainfo.h"
#include "ipnat.h"
#include "fwpages.h"
// extern(s)
// replaced global atom with "HNETCFG_SADLG"
// Loopback address (127.0.0.1) in network and host byte order
//
#define LOOPBACK_ADDR 0x0100007f
#define LOOPBACK_ADDR_HOST_ORDER 0x7f000001
// 'Shared Access Settings' common block
//
typedef struct
_SADLG
{
HWND hwndOwner;
HWND hwndDlg;
HWND hwndSrv;
HWND hwndServers;
IHNetCfgMgr *pHNetCfgMgr;
IHNetConnection *pHNetConn;
LIST_ENTRY PortMappings;
BOOL fModified;
TCHAR *ComputerName;
IUPnPService * pUPS; // iff downlevel
}
SADLG;
// Info block for port mapping entries
//
typedef struct
_SAPM
{
LIST_ENTRY Link;
IHNetPortMappingProtocol *pProtocol;
IHNetPortMappingBinding *pBinding;
BOOL fProtocolModified;
BOOL fBindingModified;
BOOL fNewEntry;
BOOL fDeleted;
TCHAR *Title;
BOOL Enabled;
BOOL BuiltIn;
UCHAR Protocol;
USHORT ExternalPort;
USHORT InternalPort;
TCHAR *InternalName;
IStaticPortMapping * pSPM;
}
SAPM;
#define HTONS(s) ((UCHAR)((s) >> 8) | ((UCHAR)(s) << 8))
#define HTONL(l) ((HTONS(l) << 16) | HTONS((l) >> 16))
#define NTOHS(s) HTONS(s)
#define NTOHL(l) HTONL(l)
#define SAPAGE_Servers 0
#define SAPAGE_Applications 1
#define SAPAGE_FirewallLogging 2
#define SAPAGE_ICMPSettings 3
#define SAPAGE_PageCount 4
inline SADLG * SasContext(HWND hwnd)
{
return (SADLG*)GetProp(GetParent(hwnd), _T("HNETCFG_SADLG"));
}
#define SasErrorDlg(h,o,e,a) \
ErrorDlgUtil(h,o,e,a,g_hinstDll,SID_SharedAccessSettings,SID_FMT_ErrorMsg)
const TCHAR c_szEmpty[] = TEXT("");
static DWORD g_adwSrvHelp[] =
{
CID_SS_LV_Services, HID_SS_LV_Services,
CID_SS_PB_Add, HID_SS_PB_Add,
CID_SS_PB_Edit, HID_SS_PB_Edit,
CID_SS_PB_Delete, HID_SS_PB_Delete,
0, 0
};
static DWORD g_adwSspHelp[] =
{
CID_SS_EB_Service, HID_SS_EB_Service,
CID_SS_EB_ExternalPort, -1,
CID_SS_EB_InternalPort, HID_SS_EB_Port,
CID_SS_PB_Tcp, HID_SS_PB_Tcp,
CID_SS_PB_Udp, HID_SS_PB_Udp,
CID_SS_EB_Address, HID_SS_EB_Address,
0, 0
};
// FORWARD DECLARATIONS
//
HRESULT
DeleteRemotePortMappingEntry(
SADLG *pDlg,
SAPM * pPortMapping
);
VOID
FreePortMappingEntry(
SAPM *pPortMapping );
VOID
FreeSharingAndFirewallSettings(
SADLG* pDlg );
HRESULT
LoadPortMappingEntry(
IHNetPortMappingBinding *pBinding,
SADLG* pDlg,
SAPM **ppPortMapping );
HRESULT
LoadRemotePortMappingEntry (
IDispatch * pDisp,
/* SADLG* pDlg, */
SAPM **ppPortMapping );
HRESULT
LoadSharingAndFirewallSettings(
SADLG* pDlg );
VOID
SasApply(
SADLG* pDlg );
LVXDRAWINFO*
SasLvxCallback(
HWND hwndLv,
DWORD dwItem );
INT_PTR CALLBACK
SasSrvDlgProc(
HWND hwnd,
UINT unMsg,
WPARAM wparam,
LPARAM lparam );
HRESULT
SavePortMappingEntry(
SADLG *pDlg,
SAPM *pPortMapping );
BOOL
SharedAccessPortMappingDlg(
IN HWND hwndOwner,
IN OUT SAPM** PortMapping );
INT_PTR CALLBACK
SspDlgProc(
IN HWND hwnd,
IN UINT unMsg,
IN WPARAM wparam,
IN LPARAM lparam );
VOID
SrvAddOrEditEntry(
SADLG* pDlg,
LONG iItem,
SAPM* PortMapping );
BOOL
SrvCommand(
IN SADLG* pDlg,
IN WORD wNotification,
IN WORD wId,
IN HWND hwndCtrl );
BOOL
SrvConflictDetected(
SADLG* pDlg,
SAPM* PortMapping );
BOOL
SrvInit(
HWND hwndPage,
SADLG* pDlg );
#define WM_PRIVATE_CANCEL 0x8000
VOID
SrvUpdateButtons(
SADLG* pDlg,
BOOL fAddDelete,
LONG iSetCheckItem );
void DisplayError (HWND hwnd, int idError, int idTitle)
{
TCHAR* pszError = PszFromId (g_hinstDll, idError);
if (pszError) {
TCHAR* pszTitle = PszFromId (g_hinstDll, idTitle);
if (pszTitle) {
MessageBox (hwnd,
pszError, pszTitle,
MB_OK | MB_ICONERROR | MB_APPLMODAL);
Free (pszTitle);
}
Free (pszError);
}
}
BOOL APIENTRY
HNetSharedAccessSettingsDlg(
BOOL fSharedAccessMode,
HWND hwndOwner )
// Displays the shared access settings property-sheet.
// On input, 'hwndOwner' indicates the window of the caller,
// with respect to which we offset the displayed property-sheet.
//
{
HRESULT hr;
IHNetCfgMgr *pHNetCfgMgr;
BOOL fComInitialized = FALSE;
BOOL fModified = FALSE;
TRACE("HNetSharedAccessSettingsDlg");
//
// Make sure COM is initialized on this thread
//
hr = CoInitializeEx(NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (SUCCEEDED(hr))
{
fComInitialized = TRUE;
}
else if (RPC_E_CHANGED_MODE == hr)
{
hr = S_OK;
}
//
// Create the HNetCfgMgr
//
if (SUCCEEDED(hr))
{
hr = CoCreateInstance(
CLSID_HNetCfgMgr,
NULL,
CLSCTX_ALL,
IID_IHNetCfgMgr,
(VOID**) &pHNetCfgMgr
);
if (SUCCEEDED(hr))
{
fModified = HNetSharingAndFirewallSettingsDlg(
hwndOwner,
pHNetCfgMgr,
FALSE,
NULL
);
pHNetCfgMgr->Release();
}
}
if (TRUE == fComInitialized)
{
CoUninitialize();
}
return fModified;
}
int CALLBACK
UnHelpCallbackFunc(
IN HWND hwndDlg,
IN UINT unMsg,
IN LPARAM lparam )
// A standard Win32 commctrl PropSheetProc. See MSDN documentation.
//
// Returns 0 always.
//
{
TRACE2( "UnHelpCallbackFunc(m=%d,l=%08x)",unMsg, lparam );
if (unMsg == PSCB_PRECREATE)
{
extern BOOL g_fNoWinHelp;
// Turn off context help button if WinHelp won't work. See
// common\uiutil\ui.c.
//
if (g_fNoWinHelp)
{
DLGTEMPLATE* pDlg = (DLGTEMPLATE* )lparam;
pDlg->style &= ~(DS_CONTEXTHELP);
}
}
return 0;
}
HRESULT APIENTRY HNetGetSharingServicesPage (IUPnPService * pUPS, PROPSHEETPAGE * psp)
{
// _asm int 3
if (!pUPS) return E_INVALIDARG;
if (!psp) return E_INVALIDARG;
// psp->dwSize muust be filled out by caller!
if (psp->dwSize == 0)
return E_INVALIDARG;
SADLG* pDlg = (SADLG*)Malloc(sizeof(*pDlg));
if (!pDlg)
return E_OUTOFMEMORY;
ZeroMemory(pDlg, sizeof(*pDlg));
pDlg->hwndOwner = (HWND)psp->lParam; // double-secret place to hang the owning window
pDlg->pUPS = pUPS;
pUPS->AddRef();
InitializeListHead(&pDlg->PortMappings);
HRESULT hr = LoadSharingAndFirewallSettings(pDlg);
if (SUCCEEDED(hr)) {
// use the size we're given
DWORD dwSize = psp->dwSize;
ZeroMemory (psp, dwSize); // double-secret place gets wiped here
psp->dwSize = dwSize;
psp->hInstance = g_hinstDll;
psp->pszTemplate = MAKEINTRESOURCE(PID_SS_SharedAccessServices);
psp->pfnDlgProc = SasSrvDlgProc;
psp->lParam = (LPARAM)pDlg;
} else {
FreeSharingAndFirewallSettings(pDlg);
Free(pDlg);
}
return hr;
}
HRESULT APIENTRY HNetFreeSharingServicesPage (PROPSHEETPAGE * psp)
{ // this must be called if and only if the psp has not been displayed
// NOTE: these tests are not definitive!!!
if (IsBadReadPtr ((void*)psp->lParam, sizeof(SADLG)))
return E_UNEXPECTED;
SADLG * pDlg = (SADLG *)psp->lParam;
if (pDlg->pUPS == NULL)
return E_UNEXPECTED;
// TODO: should I walk the heap?
FreeSharingAndFirewallSettings(pDlg);
Free(pDlg);
return S_OK;
}
BOOL
APIENTRY
HNetSharingAndFirewallSettingsDlg(
IN HWND hwndOwner,
IN IHNetCfgMgr *pHNetCfgMgr,
IN BOOL fShowFwOnlySettings,
IN OPTIONAL IHNetConnection *pHNetConn )
// Displays the shared access settings property-sheet.
// On input, 'hwndOwner' indicates the window of the caller,
// with respect to which we offset the displayed property-sheet.
//
{
// _asm int 3
DWORD dwErr;
BOOL fModified = FALSE;
SADLG* pDlg;
PROPSHEETHEADER psh;
PROPSHEETPAGE psp[SAPAGE_PageCount];
TCHAR* pszCaption;
CFirewallLoggingDialog FirewallLoggingDialog = {0};
CICMPSettingsDialog ICMPSettingsDialog = {0};
HRESULT hr;
HRESULT hFirewallLoggingResult = E_FAIL;
HRESULT hICMPSettingsResult = E_FAIL;
TRACE("HNetSharingAndFirewallSettingsDlg");
// Allocate and initialize the property-sheet's context block,
// and read into it the current shared access settings.
//
pDlg = (SADLG*)Malloc(sizeof(*pDlg));
if (!pDlg) { return FALSE; }
ZeroMemory(pDlg, sizeof(*pDlg));
pDlg->hwndOwner = hwndOwner;
pDlg->pHNetCfgMgr = pHNetCfgMgr;
pDlg->pHNetConn = pHNetConn;
InitializeListHead(&pDlg->PortMappings);
hr = LoadSharingAndFirewallSettings(pDlg);
if (SUCCEEDED(hr))
{
// Construct the property sheet.
// We use a single DlgProc for both our pages, and distinguish the pages
// by setting the applications page's 'lParam' to contain the shared
// context-block.
// (See the 'WM_INITDIALOG' handling in 'SasDlgProc'.)
//
int nPages = 0;
ZeroMemory(psp, sizeof(psp));
ZeroMemory(&psh, sizeof(psh));
if(NULL != pHNetConn && fShowFwOnlySettings)
{
hFirewallLoggingResult = CFirewallLoggingDialog_Init(&FirewallLoggingDialog, pHNetCfgMgr);
hICMPSettingsResult = CICMPSettingsDialog_Init(&ICMPSettingsDialog, pHNetConn);
}
if(NULL != pHNetConn)
{
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
psp[nPages].hInstance = g_hinstDll;
psp[nPages].pszTemplate =
MAKEINTRESOURCE(PID_SS_SharedAccessServices);
psp[nPages].pfnDlgProc = SasSrvDlgProc;
psp[nPages].lParam = (LPARAM)pDlg;
nPages++;
}
if(SUCCEEDED(hFirewallLoggingResult))
{
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
psp[nPages].hInstance = g_hinstDll;
psp[nPages].pszTemplate =
MAKEINTRESOURCE(PID_FW_FirewallLogging);
psp[nPages].pfnDlgProc = CFirewallLoggingDialog_StaticDlgProc;
psp[nPages].lParam = (LPARAM)&FirewallLoggingDialog;
nPages++;
}
if(SUCCEEDED(hICMPSettingsResult))
{
psp[nPages].dwSize = sizeof(PROPSHEETPAGE);
psp[nPages].hInstance = g_hinstDll;
psp[nPages].pszTemplate =
MAKEINTRESOURCE(PID_FW_ICMP);
psp[nPages].pfnDlgProc = CICMPSettingsDialog_StaticDlgProc;
psp[nPages].lParam = (LPARAM)&ICMPSettingsDialog;
nPages++;
}
psh.dwSize = sizeof(PROPSHEETHEADER);
psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW | PSH_USECALLBACK;
psh.hInstance = g_hinstDll;
psh.nPages = nPages;
psh.hwndParent = hwndOwner;
psh.ppsp = (LPCPROPSHEETPAGE)psp;
pszCaption = pHNetConn
? PszFromId(g_hinstDll, SID_SharedAccessSettings)
: PszFromId(g_hinstDll, SID_NetworkApplicationSettings);
psh.pszCaption = (pszCaption ? pszCaption : c_szEmpty);
psh.pfnCallback = UnHelpCallbackFunc;
if (PropertySheet(&psh) == -1)
{
dwErr = GetLastError();
TRACE1("SharedAccessSettingsDlg: PropertySheet=%d", dwErr);
SasErrorDlg(hwndOwner, SID_OP_LoadDlg, dwErr, NULL);
}
fModified = pDlg->fModified;
Free0(pszCaption); // REVIEW is this right
if(SUCCEEDED(hICMPSettingsResult))
{
CICMPSettingsDialog_FinalRelease(&ICMPSettingsDialog);
}
if(SUCCEEDED(hFirewallLoggingResult))
{
CFirewallLoggingDialog_FinalRelease(&FirewallLoggingDialog);
}
FreeSharingAndFirewallSettings(pDlg);
}
Free(pDlg);
return fModified;
}
VOID
FreePortMappingEntry(
SAPM *pPortMapping )
{
ASSERT(NULL != pPortMapping);
if (NULL != pPortMapping->pProtocol)
{
pPortMapping->pProtocol->Release();
}
if (NULL != pPortMapping->pBinding)
{
pPortMapping->pBinding->Release();
}
if (pPortMapping->pSPM)
pPortMapping->pSPM->Release();
Free0(pPortMapping->Title);
Free0(pPortMapping->InternalName);
Free(pPortMapping);
}
VOID
FreeSharingAndFirewallSettings(
SADLG* pDlg )
// Frees all sharing and firewall settings
//
{
PLIST_ENTRY pLink;
SAPM *pPortMapping;
ASSERT(pDlg);
//
// Free port-mapping entries
//
while (!IsListEmpty(&pDlg->PortMappings))
{
pLink = RemoveHeadList(&pDlg->PortMappings);
pPortMapping = CONTAINING_RECORD(pLink, SAPM, Link);
ASSERT(pPortMapping);
FreePortMappingEntry(pPortMapping);
}
//
// Free computer name
//
Free0(pDlg->ComputerName);
if (pDlg->pUPS) {
pDlg->pUPS->Release();
pDlg->pUPS = NULL;
}
}
#define NAT_API_ENTER
#define NAT_API_LEAVE
#include "natutils.h"
#include "sprtmapc.h"
HRESULT GetCollectionFromService (IUPnPService * pUPS, IStaticPortMappingCollection ** ppSPMC)
{
CComObject<CStaticPortMappingCollection> * pC = NULL;
HRESULT hr = CComObject<CStaticPortMappingCollection>::CreateInstance (&pC);
if (pC) {
pC->AddRef();
// init
hr = pC->Initialize (pUPS);
if (SUCCEEDED(hr))
hr = pC->QueryInterface (__uuidof(IStaticPortMappingCollection), (void**)ppSPMC);
pC->Release();
}
return hr;
}
HRESULT GetStaticPortMappingCollection (
SADLG* pDlg,
IStaticPortMappingCollection ** ppSPMC)
{
_ASSERT (pDlg);
_ASSERT (pDlg->pUPS);
_ASSERT (ppSPMC);
*ppSPMC = NULL;
return GetCollectionFromService (pDlg->pUPS, ppSPMC);
}
HRESULT
LoadRemotePortMappingEntry (IDispatch * pDisp, /* SADLG* pDlg, */ SAPM **ppPortMapping )
{ // NOTE: may need pDlg to get computer name if loopback
*ppPortMapping = NULL;
SAPM *pMapping = (SAPM*)Malloc(sizeof(*pMapping));
if (!pMapping)
return E_OUTOFMEMORY;
ZeroMemory(pMapping, sizeof(*pMapping));
InitializeListHead(&pMapping->Link);
HRESULT hr = pDisp->QueryInterface (__uuidof(IStaticPortMapping),
(void**)&pMapping->pSPM);
if (SUCCEEDED(hr)) {
// get title (description)
CComBSTR cbDescription;
hr = pMapping->pSPM->get_Description (&cbDescription);
if (SUCCEEDED(hr)) {
// immediately figure out if it's "built-in"
#define BUILTIN_KEY L" [MICROSOFT]"
OLECHAR * tmp = wcsstr (cbDescription.m_str, BUILTIN_KEY);
if (tmp && (tmp[wcslen(BUILTIN_KEY)] == 0)) {
// if the key exists and is at the end, then it's a built-in mapping
pMapping->BuiltIn = TRUE;
*tmp = 0;
}
pMapping->Title = StrDupTFromW (cbDescription);
if (NULL == pMapping->Title)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr)) {
// get protocol
CComBSTR cbProtocol;
hr = pMapping->pSPM->get_Protocol (&cbProtocol);
if (SUCCEEDED(hr)) {
if (!_wcsicmp (L"tcp", cbProtocol))
pMapping->Protocol = NAT_PROTOCOL_TCP;
else
if (!_wcsicmp (L"udp", cbProtocol))
pMapping->Protocol = NAT_PROTOCOL_UDP;
else {
_ASSERT (0 && "bad protocol!?");
hr = E_UNEXPECTED;
}
if (SUCCEEDED(hr)) {
// get external port
long lExternalPort = 0;
hr = pMapping->pSPM->get_ExternalPort (&lExternalPort);
if (SUCCEEDED(hr)) {
_ASSERT (lExternalPort > 0);
_ASSERT (lExternalPort < 65536);
pMapping->ExternalPort = ntohs ((USHORT)lExternalPort);
// get internal port
long lInternalPort = 0;
hr = pMapping->pSPM->get_InternalPort (&lInternalPort);
if (SUCCEEDED(hr)) {
_ASSERT (lInternalPort > 0);
_ASSERT (lInternalPort < 65536);
pMapping->InternalPort = ntohs ((USHORT)lInternalPort);
// get Enabled
VARIANT_BOOL vb;
hr = pMapping->pSPM->get_Enabled (&vb);
if (SUCCEEDED(hr)) {
pMapping->Enabled = vb == VARIANT_TRUE;
}
}
}
}
}
}
}
if (SUCCEEDED(hr)) {
// lastly, get private IP or host name (hard one)
// TODO: check for loopback, etc., like in LoadPortMappingEntry code below
CComBSTR cbInternalClient;
hr = pMapping->pSPM->get_InternalClient (&cbInternalClient);
if (SUCCEEDED(hr)) {
if (!(cbInternalClient == L"0.0.0.0")) {
pMapping->InternalName = StrDupTFromW (cbInternalClient);
if (!pMapping->InternalName)
hr = E_OUTOFMEMORY;
}
}
}
if (SUCCEEDED(hr))
*ppPortMapping = pMapping;
else
FreePortMappingEntry (pMapping);
return hr;
}
HRESULT
LoadPortMappingEntry(
IHNetPortMappingBinding *pBinding,
SADLG* pDlg,
SAPM **ppPortMapping )
{
HRESULT hr = S_OK;
IHNetPortMappingProtocol *pProtocol = NULL;
SAPM *pMapping;
BOOLEAN fTemp;
OLECHAR *pwsz;
ASSERT(NULL != pBinding);
ASSERT(NULL != ppPortMapping);
pMapping = (SAPM*) Malloc(sizeof(*pMapping));
if (NULL != pMapping)
{
ZeroMemory(pMapping, sizeof(*pMapping));
InitializeListHead(&pMapping->Link);
hr = pBinding->GetProtocol (&pProtocol);
if (SUCCEEDED(hr))
{
hr = pProtocol->GetName (&pwsz);
if (SUCCEEDED(hr))
{
pMapping->Title = StrDupTFromW(pwsz);
if (NULL == pMapping->Title)
{
hr = E_OUTOFMEMORY;
}
CoTaskMemFree(pwsz);
}
if (SUCCEEDED(hr))
{
hr = pProtocol->GetBuiltIn (&fTemp);
}
if (SUCCEEDED(hr))
{
pMapping->BuiltIn = !!fTemp;
hr = pProtocol->GetIPProtocol (&pMapping->Protocol);
}
if (SUCCEEDED(hr))
{
hr = pProtocol->GetPort (&pMapping->ExternalPort);
}
pMapping->pProtocol = pProtocol;
pMapping->pProtocol->AddRef();
pProtocol->Release();
}
}
else
{
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = pBinding->GetTargetPort (&pMapping->InternalPort);
}
if (SUCCEEDED(hr))
{
hr = pBinding->GetEnabled (&fTemp);
}
if (SUCCEEDED(hr))
{
pMapping->Enabled = !!fTemp;
hr = pBinding->GetCurrentMethod (&fTemp);
}
if (SUCCEEDED(hr))
{
if (fTemp)
{
hr = pBinding->GetTargetComputerName (&pwsz);
if (SUCCEEDED(hr))
{
pMapping->InternalName = StrDupTFromW(pwsz);
if (NULL == pMapping->InternalName)
{
hr = E_OUTOFMEMORY;
}
CoTaskMemFree(pwsz);
}
}
else
{
ULONG ulAddress;
hr = pBinding->GetTargetComputerAddress (&ulAddress);
if (SUCCEEDED(hr))
{
if (LOOPBACK_ADDR == ulAddress)
{
//
// The mapping is directed at this machine, so
// replace the loopback address with our
// machine name
//
pMapping->InternalName = _StrDup(pDlg->ComputerName);
if (NULL == pMapping->InternalName)
{
hr = E_OUTOFMEMORY;
}
}
else if (0 != ulAddress)
{
pMapping->InternalName =
(LPTSTR) Malloc(16 * sizeof(TCHAR));
if (NULL != pMapping->InternalName)
{
IpHostAddrToPsz(
NTOHL(ulAddress),
pMapping->InternalName
);
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
}
}
if (SUCCEEDED(hr))
{
hr = S_OK;
pMapping->pBinding = pBinding;
pMapping->pBinding->AddRef();
*ppPortMapping = pMapping;
}
else if (NULL != pMapping)
{
FreePortMappingEntry(pMapping);
}
return hr;
}
class CWaitDialog
{
public:
struct SWaitDialog
{
HWND hwndOwner;
private:
CComAutoCriticalSection m_acs;
HWND m_hwndDlg;
public:
SWaitDialog (HWND hwnd)
{
hwndOwner = hwnd;
m_hwndDlg = NULL;
}
void SetWindow (HWND hwnd)
{
m_acs.Lock();
if (m_hwndDlg == NULL)
m_hwndDlg = hwnd;
m_acs.Unlock();
}
HWND GetWindow () { return m_hwndDlg; }
};
private:
SWaitDialog * m_pwd;
public:
CWaitDialog (HWND hwndOwner)
{
m_pwd = new SWaitDialog (hwndOwner);
if (m_pwd) {
// create thread
DWORD ThreadId = NULL;
CloseHandle (CreateThread (NULL, 0,
CWaitDialog::ThreadProc,
(void*)m_pwd,
0, &ThreadId));
}
}
~CWaitDialog ()
{
if (m_pwd) {
HWND hwnd = m_pwd->GetWindow();
m_pwd->SetWindow ((HWND)INVALID_HANDLE_VALUE);
if (hwnd != NULL)
EndDialog (hwnd, 1);
}
}
static DWORD WINAPI ThreadProc (VOID *pVoid)
{
SWaitDialog * pWD = (SWaitDialog *)pVoid;
EnableWindow (pWD->hwndOwner, FALSE);
DialogBoxParam (g_hinstDll,
MAKEINTRESOURCE(PID_SS_PleaseWait),
pWD->hwndOwner,
CWaitDialog::DlgProc,
(LPARAM)pWD);
EnableWindow (pWD->hwndOwner, TRUE);
delete pWD;
return 1;
}
static INT_PTR CALLBACK DlgProc (HWND hwnd, UINT uMsg, WPARAM wparam, LPARAM lparam)
{
switch (uMsg) {
case WM_INITDIALOG:
{
// hang onto my data
SWaitDialog * pWD = (SWaitDialog *)lparam;
SetWindowLongPtr (hwnd, DWLP_USER, (LONG_PTR)pWD);
// center window on owner
CenterWindow (hwnd, pWD->hwndOwner);
// fill out dlg's hwnd
pWD->SetWindow (hwnd);
if (pWD->GetWindow() == INVALID_HANDLE_VALUE) // already destructed!
PostMessage (hwnd, 0x8000, 0, 0L);
return TRUE;
}
case WM_PAINT:
{
SWaitDialog * pWD = (SWaitDialog *)GetWindowLongPtr (hwnd, DWLP_USER);
if (pWD->GetWindow() == INVALID_HANDLE_VALUE) // already destructed!
PostMessage (hwnd, 0x8000, 0, 0L);
break;
}
case 0x8000:
EndDialog (hwnd, 1);
return TRUE;
}
return FALSE;
}
};
HRESULT
LoadSharingAndFirewallSettings(
SADLG* pDlg )
{
CWaitDialog wd(pDlg->hwndOwner); // may be NULL
HRESULT hr = S_OK;
IHNetProtocolSettings *pProtSettings;
ULONG ulCount;
DWORD dwError;
ASSERT(pDlg);
//
// Load the name of the computer
//
#ifndef DOWNLEVEL_CLIENT // downlevel client doesn't have this call
ulCount = 0;
if (!GetComputerNameEx(ComputerNameDnsHostname, NULL, &ulCount))
{
dwError = GetLastError();
if (ERROR_MORE_DATA == dwError)
{
pDlg->ComputerName = (TCHAR*) Malloc(ulCount * sizeof(TCHAR));
if (NULL != pDlg->ComputerName)
{
if (!GetComputerNameEx(
ComputerNameDnsHostname,
pDlg->ComputerName,
&ulCount
))
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = HRESULT_FROM_WIN32(dwError);
}
}
else
{
//
// Since we didn't pass in a buffer, this should never happen.
//
ASSERT(FALSE);
hr = E_UNEXPECTED;
}
if (FAILED(hr))
return hr;
#else
// downlevel client version
TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH+1];
DWORD dwSize = MAX_COMPUTERNAME_LENGTH+1;
if (!GetComputerName (szComputerName, &dwSize))
hr = HRESULT_FROM_WIN32(GetLastError());
else {
pDlg->ComputerName = _StrDup (szComputerName);
if (!pDlg->ComputerName)
hr = E_OUTOFMEMORY;
}
#endif
// do downlevel and remote case here
if (pDlg->pUPS) {
CComPtr<IStaticPortMappingCollection> spSPMC = NULL;
hr = GetCollectionFromService (pDlg->pUPS, &spSPMC);
if (spSPMC) {
CComPtr<IEnumVARIANT> spEV = NULL;
CComPtr<IUnknown> spunk = NULL;
hr = spSPMC->get__NewEnum (&spunk);
if (spunk)
hr = spunk->QueryInterface (
__uuidof(IEnumVARIANT),
(void**)&spEV);
if (spEV) {
CComVariant cv;
while (S_OK == spEV->Next (1, &cv, NULL)) {
if (V_VT (&cv) == VT_DISPATCH) {
SAPM *pSAPortMap = NULL;
hr = LoadRemotePortMappingEntry (V_DISPATCH (&cv), /* pDlg, */ &pSAPortMap);
if (SUCCEEDED(hr))
InsertTailList(&pDlg->PortMappings, &pSAPortMap->Link);
}
cv.Clear();
}
}
}
}
// do stuff below iff not remote
if (NULL != pDlg->pHNetConn)
{
IEnumHNetPortMappingBindings *pEnumBindings = NULL;
//
// Load port-mapping settings
//
hr = pDlg->pHNetConn->EnumPortMappings (FALSE, &pEnumBindings);
if (SUCCEEDED(hr))
{
IHNetPortMappingBinding *pBinding;
do
{
hr = pEnumBindings->Next (1, &pBinding, &ulCount);
if (SUCCEEDED(hr) && 1 == ulCount)
{
SAPM *pSAPortMap;
hr = LoadPortMappingEntry(pBinding, pDlg, &pSAPortMap);
if (SUCCEEDED(hr))
{
InsertTailList(&pDlg->PortMappings, &pSAPortMap->Link);
}
else
{
//
// Even though an error occured for this entry we'll
// keep on going -- this allows the UI to show up.
//
hr = S_OK;
}
pBinding->Release();
}
} while (SUCCEEDED(hr) && 1 == ulCount);
pEnumBindings->Release();
}
}
return hr;
}
extern BOOL IsICSHost (); // in upnpnat.cpp
VOID
SasApply(
SADLG* pDlg )
// Called to save all changes made in the property sheet.
//
{
if (!pDlg->fModified)
{
return;
}
if (pDlg->hwndServers)
{
SAPM* pPortMapping;
#if DBG
LONG i = -1;
while ((i = ListView_GetNextItem(pDlg->hwndServers, i, LVNI_ALL))
>= 0)
{
pPortMapping = (SAPM*)ListView_GetParamPtr(pDlg->hwndServers, i);
ASSERT(pPortMapping->Enabled == ListView_GetCheck(pDlg->hwndServers, i));
}
#endif
//
// Commit modified port-mapping entries. Since entries marked
// for deletion were placed at the front of the port-mapping
// list there's no chance of having a new or modified entry
// conflicting with a deleted entry.
//
HRESULT hr = S_OK;
PLIST_ENTRY Link;
for (Link = pDlg->PortMappings.Flink;
Link != &pDlg->PortMappings; Link = Link->Flink)
{
pPortMapping = CONTAINING_RECORD(Link, SAPM, Link);
if (pPortMapping->fDeleted)
{
Link = Link->Blink;
RemoveEntryList(&pPortMapping->Link);
if(NULL != pPortMapping->pProtocol)
{
pPortMapping->pProtocol->Delete();
}
else if (pPortMapping->pSPM)
{
HRESULT hr = DeleteRemotePortMappingEntry (pDlg, pPortMapping);
if (FAILED(hr)) {
// TODO: should I pop up some UI?
}
}
FreePortMappingEntry(pPortMapping);
}
else if (pPortMapping->fProtocolModified
|| pPortMapping->fBindingModified
|| pPortMapping->fNewEntry)
{
HRESULT hr2 = SavePortMappingEntry(pDlg, pPortMapping);
if (SUCCEEDED(hr2))
{
pPortMapping->fProtocolModified = FALSE;
pPortMapping->fBindingModified = FALSE;
pPortMapping->fNewEntry = FALSE;
} else {
if (SUCCEEDED(hr))
hr = hr2; // grab first error
}
}
}
if (FAILED(hr)) {
if (pDlg->pUPS && !IsICSHost ())
DisplayError (pDlg->hwndDlg,
SID_OP_TheirGatewayError,
SID_PopupTitle);
else
DisplayError (pDlg->hwndDlg,
SID_OP_GenericPortMappingError,
SID_PopupTitle);
}
}
}
INT_PTR CALLBACK
SasSrvDlgProc(
HWND hwnd,
UINT unMsg,
WPARAM wparam,
LPARAM lparam )
// Called to handle messages for the 'Services' pages.
//
{
// Give the extended list-control a chance to look at all messages first.
//
if (ListView_OwnerHandler(hwnd, unMsg, wparam, lparam, SasLvxCallback))
{
return TRUE;
}
switch (unMsg)
{
case WM_INITDIALOG:
{
SADLG* pDlg = (SADLG*)((PROPSHEETPAGE*)lparam)->lParam;
return SrvInit(hwnd, pDlg);
}
case WM_PRIVATE_CANCEL:
{
SADLG* pDlg = SasContext(hwnd);
PostMessage (pDlg->hwndDlg, PSM_PRESSBUTTON, PSBTN_CANCEL, 0L);
return TRUE;
}
case WM_HELP:
case WM_CONTEXTMENU:
{
SADLG* pDlg = SasContext(hwnd);
ContextHelp(g_adwSrvHelp, hwnd, unMsg, wparam, lparam);
break;
}
case WM_COMMAND:
{
SADLG* pDlg = SasContext(hwnd);
return SrvCommand(
pDlg, HIWORD(wparam), LOWORD(wparam), (HWND)lparam);
}
case WM_NOTIFY:
{
SADLG* pDlg = SasContext(hwnd);
switch (((NMHDR*)lparam)->code)
{
case PSN_APPLY:
{
SasApply(pDlg);
return TRUE;
}
case NM_DBLCLK:
{
SendMessage(
GetDlgItem(hwnd, CID_SS_PB_Edit), BM_CLICK, 0, 0);
return TRUE;
}
case LVXN_SETCHECK:
{
pDlg->fModified = TRUE;
SrvUpdateButtons(
pDlg, FALSE, ((NM_LISTVIEW*)lparam)->iItem);
return TRUE;
}
case LVN_ITEMCHANGED:
{
if ((((NM_LISTVIEW*)lparam)->uNewState & LVIS_SELECTED)
!= (((NM_LISTVIEW*)lparam)->uOldState & LVIS_SELECTED))
{
SrvUpdateButtons(
pDlg, FALSE, ((NM_LISTVIEW*)lparam)->iItem);
}
return TRUE;
}
}
break;
}
}
return FALSE;
}
BOOL
SasInit(
HWND hwndPage,
SADLG* pDlg )
// Called to initialize the settings property sheet.
// Sets the window-property in which the shared context-block is stored,
// and records the dialog's window-handle.
//
{
HWND hwndDlg = GetParent(hwndPage);
if (!SetProp(hwndDlg, _T("HNETCFG_SADLG"), pDlg))
{
return FALSE;
}
pDlg->hwndDlg = hwndDlg;
return TRUE;
}
LVXDRAWINFO*
SasLvxCallback(
HWND hwndLv,
DWORD dwItem )
// Callback for extended list-controls on the 'Applications' and 'Services'
// pages.
//
{
static LVXDRAWINFO info = { 1, 0, LVXDI_DxFill, { LVXDIA_Static } };
return &info;
}
HRESULT DeleteRemotePortMappingEntry (SADLG *pDlg, SAPM * pPortMapping)
{
_ASSERT (pPortMapping);
_ASSERT (!pPortMapping->pProtocol);
_ASSERT (!pPortMapping->pBinding);
_ASSERT (pPortMapping->pSPM);
// don't use value in pPortMapping struct: they could have been edited.
long lExternalPort = 0;
HRESULT hr = pPortMapping->pSPM->get_ExternalPort (&lExternalPort);
if (SUCCEEDED(hr)) {
CComBSTR cbProtocol;
hr = pPortMapping->pSPM->get_Protocol (&cbProtocol);
if (SUCCEEDED(hr)) {
// get IStaticPortMappings interface (collection has Remove method)
CComPtr<IStaticPortMappingCollection> spSPMC = NULL;
hr = GetStaticPortMappingCollection (pDlg, &spSPMC);
if (spSPMC)
hr = spSPMC->Remove (lExternalPort, cbProtocol);
}
}
return hr;
}
HRESULT
SaveRemotePortMappingEntry(
SADLG *pDlg,
SAPM *pPortMapping )
{
_ASSERT (pPortMapping);
_ASSERT (!pPortMapping->pProtocol);
_ASSERT (!pPortMapping->pBinding);
_ASSERT (pDlg->pUPS); // either remote or downlevel
USES_CONVERSION;
HRESULT hr = S_OK;
// common params
long lExternalPort = htons (pPortMapping->ExternalPort);
long lInternalPort = htons (pPortMapping->InternalPort);
CComBSTR cbClientIPorDNS = T2OLE(pPortMapping->InternalName);
CComBSTR cbDescription = T2OLE(pPortMapping->Title);
CComBSTR cbProtocol;
if (pPortMapping->Protocol == NAT_PROTOCOL_TCP)
cbProtocol = L"TCP";
else
cbProtocol = L"UDP";
if (NULL == pPortMapping->pSPM) {
// brand-new entry:
// delete dup if any
// add new entry
CComPtr<IStaticPortMappingCollection> spSPMC = NULL;
hr = GetStaticPortMappingCollection (pDlg, &spSPMC);
if (spSPMC) {
spSPMC->Remove (lExternalPort, cbProtocol); // just in case
hr = spSPMC->Add (lExternalPort,
cbProtocol,
lInternalPort,
cbClientIPorDNS,
pPortMapping->Enabled ? VARIANT_TRUE : VARIANT_FALSE,
cbDescription,
&pPortMapping->pSPM);
}
return hr;
}
// edited case: check what changed.
// if ports or protocol changed,...
long lOldExternalPort = 0;
pPortMapping->pSPM->get_ExternalPort (&lOldExternalPort);
CComBSTR cbOldProtocol;
pPortMapping->pSPM->get_Protocol (&cbOldProtocol);
if ((lOldExternalPort != lExternalPort) ||
(!(cbOldProtocol == cbProtocol))) {
// ... delete old entry and create new entry
CComPtr<IStaticPortMappingCollection> spSPMC = NULL;
hr = GetStaticPortMappingCollection (pDlg, &spSPMC);
if (spSPMC)
hr = spSPMC->Remove (lOldExternalPort, cbOldProtocol);
if (SUCCEEDED(hr)) {
pPortMapping->pSPM->Release();
pPortMapping->pSPM = NULL;
hr = spSPMC->Add (lExternalPort,
cbProtocol,
lInternalPort,
cbClientIPorDNS,
pPortMapping->Enabled ? VARIANT_TRUE : VARIANT_FALSE,
cbDescription,
&pPortMapping->pSPM);
}
return hr;
}
// else, just edit in place.
// Note that the client address must be filled out before trying to enable
// did the client IP address change?
CComBSTR cbOldClientIP;
pPortMapping->pSPM->get_InternalClient (&cbOldClientIP);
if (!(cbClientIPorDNS == cbOldClientIP)) {
hr = pPortMapping->pSPM->EditInternalClient (cbClientIPorDNS);
if (FAILED(hr))
return hr;
}
// did the internal port change?
long lOldInternalPort = 0;
pPortMapping->pSPM->get_InternalPort (&lOldInternalPort);
if (lOldInternalPort != lInternalPort) {
hr = pPortMapping->pSPM->EditInternalPort (lInternalPort);
if (FAILED(hr))
return hr;
}
// did the enabled flag change?
VARIANT_BOOL vbEnabled = FALSE;
pPortMapping->pSPM->get_Enabled (&vbEnabled);
if (vbEnabled != (pPortMapping->Enabled ? VARIANT_TRUE : VARIANT_FALSE)) {
hr = pPortMapping->pSPM->Enable (pPortMapping->Enabled ? VARIANT_TRUE : VARIANT_FALSE);
}
return hr;
}
HRESULT
SavePortMappingEntry(
SADLG *pDlg,
SAPM *pPortMapping )
{
if (pDlg->pUPS) // remote case
return SaveRemotePortMappingEntry (pDlg, pPortMapping);
HRESULT hr = S_OK;
OLECHAR *wszTitle;
ASSERT(NULL != pDlg);
ASSERT(NULL != pPortMapping);
wszTitle = StrDupWFromT(pPortMapping->Title);
if (NULL == wszTitle)
{
hr = E_OUTOFMEMORY;
}
else if (pPortMapping->fNewEntry)
{
IHNetProtocolSettings *pSettings;
ASSERT(NULL == pPortMapping->pProtocol);
ASSERT(NULL == pPortMapping->pBinding);
hr = pDlg->pHNetCfgMgr->QueryInterface (IID_IHNetProtocolSettings,
(void**)&pSettings);
if (SUCCEEDED(hr))
{
hr = pSettings->CreatePortMappingProtocol(
wszTitle,
pPortMapping->Protocol,
pPortMapping->ExternalPort,
&pPortMapping->pProtocol
);
pSettings->Release();
}
if (SUCCEEDED(hr))
{
hr = pDlg->pHNetConn->GetBindingForPortMappingProtocol(
pPortMapping->pProtocol,
&pPortMapping->pBinding
);
if (SUCCEEDED(hr))
{
//
// At this point, the protocol is set. However, we
// still need to save the binding information
//
pPortMapping->fProtocolModified = FALSE;
pPortMapping->fBindingModified = TRUE;
}
}
}
if (SUCCEEDED(hr) && pPortMapping->fProtocolModified)
{
hr = pPortMapping->pProtocol->SetName (wszTitle);
if (SUCCEEDED(hr))
{
hr = pPortMapping->pProtocol->SetIPProtocol (
pPortMapping->Protocol);
}
if (SUCCEEDED(hr))
{
hr = pPortMapping->pProtocol->SetPort (pPortMapping->ExternalPort);
}
}
if (SUCCEEDED(hr)
&& pPortMapping->fBindingModified
&& NULL != pPortMapping->InternalName)
{
ULONG ulAddress = INADDR_NONE;
if (lstrlen(pPortMapping->InternalName) >= 7)
{
//
// 1.2.3.4 -- minimum of 7 characters
//
ulAddress = IpPszToHostAddr(pPortMapping->InternalName);
}
if (INADDR_NONE == ulAddress)
{
//
// Check to see if the target name is either
// 1) this computer's name, or
// 2) "localhost"
//
// If so, use the loopback address instead of the name.
//
if (0 == _tcsicmp(pPortMapping->InternalName, pDlg->ComputerName)
|| 0 == _tcsicmp(pPortMapping->InternalName, _T("localhost")))
{
ulAddress = LOOPBACK_ADDR_HOST_ORDER;
}
}
//
// We can't just check for INADDR_NONE here, since that
// is 0xFFFFFFFF, which is 255.255.255.255. To catch this
// we need to compare the name against that explicit string
// address.
//
if (INADDR_NONE == ulAddress
&& 0 != _tcsicmp(pPortMapping->InternalName, _T("255.255.255.255")))
{
OLECHAR *wsz;
wsz = StrDupWFromT(pPortMapping->InternalName);
if (NULL != wsz)
{
hr = pPortMapping->pBinding->SetTargetComputerName (wsz);
Free(wsz);
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = pPortMapping->pBinding->SetTargetComputerAddress
(HTONL(ulAddress));
}
if (SUCCEEDED(hr))
{
hr = pPortMapping->pBinding->SetEnabled (!!pPortMapping->Enabled);
}
if (SUCCEEDED(hr))
{
hr = pPortMapping->pBinding->SetTargetPort (pPortMapping->InternalPort);
}
}
Free0(wszTitle);
return hr;
}
VOID
SrvAddOrEditEntry(
SADLG* pDlg,
LONG iItem,
SAPM* PortMapping )
// Called to display the 'Add' or 'Edit' dialog for a service.
//
{
LV_ITEM lvi;
// Display the dialog, and return if the user cancels.
// Otherwise, remove the old item (if any) and insert the added or edited
// item.
//
if (!SharedAccessPortMappingDlg(pDlg->hwndDlg, &PortMapping))
{
return;
}
if (iItem != -1)
{
ListView_DeleteItem(pDlg->hwndServers, iItem);
}
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.lParam = (LPARAM)PortMapping;
lvi.pszText = PortMapping->Title;
lvi.cchTextMax = lstrlen(PortMapping->Title) + 1;
lvi.iItem = 0;
iItem = ListView_InsertItem(pDlg->hwndServers, &lvi);
if (iItem == -1)
{
RemoveEntryList(&PortMapping->Link);
if (NULL != PortMapping->pProtocol)
{
PortMapping->pProtocol->Delete();
}
else if (NULL != PortMapping->pSPM)
{
DeleteRemotePortMappingEntry (pDlg, PortMapping);
}
FreePortMappingEntry(PortMapping);
return;
}
// Update the item's 'enabled' state. Setting the check on the item
// triggers an update of the button state as well as conflict detection.
// (See 'SrvUpdateButtons' and the LVXN_SETCHECK handling in 'SasDlgProc').
//
ListView_SetCheck(pDlg->hwndServers, iItem, PortMapping->Enabled);
ListView_SetItemState(
pDlg->hwndServers, iItem, LVIS_SELECTED, LVIS_SELECTED);
pDlg->fModified = TRUE;
}
BOOL
SrvCommand(
IN SADLG* pDlg,
IN WORD wNotification,
IN WORD wId,
IN HWND hwndCtrl )
// Called to process a 'WM_COMMAND' message from one of the page's buttons.
//
{
switch (wId)
{
case CID_SS_PB_Add:
{
SrvAddOrEditEntry(pDlg, -1, NULL);
return TRUE;
}
case CID_SS_PB_Edit:
{
LONG i = ListView_GetNextItem(pDlg->hwndServers, -1, LVNI_SELECTED);
SAPM* PortMapping;
if (i == -1)
{
MsgDlg(pDlg->hwndDlg, SID_NoModifySelection, NULL);
SetFocus(pDlg->hwndServers);
return FALSE;
}
PortMapping = (SAPM*)ListView_GetParamPtr(pDlg->hwndServers, i);
if (PortMapping)
{
SrvAddOrEditEntry(pDlg, i, PortMapping);
}
SetFocus(pDlg->hwndServers);
return TRUE;
}
case CID_SS_PB_Delete:
{
LONG i = ListView_GetNextItem(pDlg->hwndServers, -1, LVNI_SELECTED);
SAPM* PortMapping;
if (i == -1)
{
MsgDlg(pDlg->hwndDlg, SID_NoDeleteSelection, NULL);
SetFocus(pDlg->hwndServers);
return FALSE;
}
// Delete each selected item. Items with marked 'built-in'
// cannot be deleted, and are ignored.
//
do {
PortMapping = (SAPM*)ListView_GetParamPtr(pDlg->hwndServers, i);
if(NULL == PortMapping)
{
break;
}
if (PortMapping->BuiltIn)
{
++i;
}
else
{
ListView_DeleteItem(pDlg->hwndServers, i);
--i;
//
// If this is a new entry we can immediately remove
// it from the list and free it; otherwise, we move
// it to the front of the list and mark it for
// deletion.
//
RemoveEntryList(&PortMapping->Link);
if (PortMapping->fNewEntry)
{
_ASSERT(NULL == PortMapping->pProtocol);
_ASSERT(NULL == PortMapping->pSPM);
FreePortMappingEntry(PortMapping);
}
else
{
InsertHeadList(&pDlg->PortMappings, &PortMapping->Link);
PortMapping->fDeleted = TRUE;
}
}
i = ListView_GetNextItem(pDlg->hwndServers, i, LVNI_SELECTED);
} while (i != -1);
// Update the dialog and synchronize the button-states with the
// current selection, if any.
//
pDlg->fModified = TRUE;
SetFocus(pDlg->hwndServers);
SrvUpdateButtons(pDlg, TRUE, -1);
return TRUE;
}
}
return TRUE;
}
BOOL
SrvConflictDetected(
SADLG* pDlg,
SAPM* PortMapping )
// Called to determine whether the given item conflicts with any other
// item and, if so, to display a message.
//
{
SAPM* Duplicate;
PLIST_ENTRY Link;
for (Link = pDlg->PortMappings.Flink;
Link != &pDlg->PortMappings; Link = Link->Flink)
{
Duplicate = CONTAINING_RECORD(Link, SAPM, Link);
if (PortMapping != Duplicate &&
!Duplicate->fDeleted &&
PortMapping->Protocol == Duplicate->Protocol &&
PortMapping->ExternalPort == Duplicate->ExternalPort)
{
MsgDlg(pDlg->hwndDlg, SID_DuplicatePortNumber, NULL);
return TRUE;
}
}
return FALSE;
}
BOOL
SrvInit(
HWND hwndPage,
SADLG* pDlg )
// Called to initialize the services page. Fills the list-control with
// configured services.
//
{
BOOL fModified;
LONG i;
LV_ITEM lvi;
PLIST_ENTRY Link;
SAPM* PortMapping;
// Initialize the containing property-sheet, then store this page's
// data in the shared control-block at 'pDlg'.
//
if (!SasInit(hwndPage, pDlg))
{
return FALSE;
}
// Store this page's data in the shared control-block at 'pDlg'.
//
pDlg->hwndSrv = hwndPage;
pDlg->hwndServers = GetDlgItem(hwndPage, CID_SS_LV_Services);
// Initialize the list-control with checkbox-handling,
// insert a single column, and fill the list-control with the configured
// items.
//
ListView_InstallChecks(pDlg->hwndServers, g_hinstDll);
ListView_InsertSingleAutoWidthColumn(pDlg->hwndServers);
fModified = pDlg->fModified;
for (Link = pDlg->PortMappings.Flink;
Link != &pDlg->PortMappings; Link = Link->Flink)
{
PortMapping = CONTAINING_RECORD(Link, SAPM, Link);
ZeroMemory(&lvi, sizeof(lvi));
lvi.mask = LVIF_TEXT | LVIF_PARAM;
lvi.iItem = 0;
lvi.lParam = (LPARAM)PortMapping;
lvi.pszText = PortMapping->Title;
lvi.cchTextMax = lstrlen(PortMapping->Title) + 1;
i = ListView_InsertItem(pDlg->hwndServers, &lvi);
if (i != -1)
{
ListView_SetCheck(pDlg->hwndServers, i, PortMapping->Enabled);
}
}
pDlg->fModified = fModified;
// Finally, update the appearance of the buttons for the current selection.
//
ListView_SetItemState(pDlg->hwndServers, 0, LVIS_SELECTED, LVIS_SELECTED);
SrvUpdateButtons(pDlg, TRUE, -1);
// if we got no portmappings, check to see if the button allowing
// other network users to control the gateway is unchecked (on the host)
if (IsListEmpty (pDlg->PortMappings.Flink) &&
pDlg->pUPS && IsICSHost ()) {
// display error
DisplayError (pDlg->hwndDlg, SID_OP_OurGatewayError, SID_PopupTitle);
// cancel
PostMessage (hwndPage, WM_PRIVATE_CANCEL, 0, 0L);
}
return TRUE;
}
VOID
SrvUpdateButtons(
SADLG* pDlg,
BOOL fAddDelete,
LONG iSetCheckItem )
// Called to set an initial selection if necessary, update the appearance
// of the 'Edit' and 'Delete' buttons, and perform conflict-detection
// if an entry's checkbox was set.
//
{
LONG i;
SAPM* PortMapping;
// If an entry was added or removed, ensure that there is a selection.
// If there are no entries at all, disable the 'Edit' button.
//
if (fAddDelete)
{
if (ListView_GetItemCount(pDlg->hwndServers))
{
ListView_SetItemState(
pDlg->hwndServers, 0, LVIS_SELECTED, LVIS_SELECTED);
EnableWindow(GetDlgItem(pDlg->hwndSrv, CID_SS_PB_Edit), TRUE);
}
else
{
EnableWindow(GetDlgItem(pDlg->hwndSrv, CID_SS_PB_Edit), FALSE);
}
}
// Disable the 'Delete' button, and enable it only if at least one of the
// selected items is not a built-in item.
//
EnableWindow(GetDlgItem(pDlg->hwndSrv, CID_SS_PB_Delete), FALSE);
i = ListView_GetNextItem(pDlg->hwndServers, -1, LVNI_SELECTED);
while (i != -1)
{
PortMapping = (SAPM*)ListView_GetParamPtr(pDlg->hwndServers, i);
if ( (NULL != PortMapping)
&& (!PortMapping->BuiltIn))
{
EnableWindow(GetDlgItem(pDlg->hwndSrv, CID_SS_PB_Delete), TRUE);
break;
}
i = ListView_GetNextItem(pDlg->hwndServers, i, LVNI_SELECTED);
}
// If the check-state of an item was changed and the item is now checked,
// perform conflict-detection. If a conflict is detected, clear the item's
// check state.
//
if (iSetCheckItem != -1)
{
PortMapping =
(SAPM*)ListView_GetParamPtr(pDlg->hwndServers, iSetCheckItem);
if(NULL == PortMapping)
{
return;
}
if (ListView_GetCheck(pDlg->hwndServers, iSetCheckItem))
{
if (SrvConflictDetected(pDlg, PortMapping))
{
ListView_SetCheck(pDlg->hwndServers, iSetCheckItem, FALSE);
SrvAddOrEditEntry(pDlg, iSetCheckItem, PortMapping);
}
else
{
PortMapping->Enabled = TRUE;
PortMapping->fBindingModified = TRUE;
// If the item is marked 'built-in' and it is being enabled
// for the first time, pop up the edit-dialog so the user can
// specify an internal IP address or name for the server.
//
if (PortMapping->BuiltIn &&
(!PortMapping->InternalName
|| !lstrlen(PortMapping->InternalName)))
{
//
// We fill in the local computer name as the default
// target. It's OK if this allocation fails; the UI
// will show an empty field, so the user will be
// required to enter a target.
//
PortMapping->InternalName = _StrDup(pDlg->ComputerName);
SrvAddOrEditEntry(pDlg, iSetCheckItem, PortMapping);
if (!PortMapping->InternalName
|| !lstrlen(PortMapping->InternalName))
{
ListView_SetCheck(
pDlg->hwndServers, iSetCheckItem, FALSE);
PortMapping->Enabled = FALSE;
PortMapping->fBindingModified = FALSE;
}
}
}
}
else
{
PortMapping->Enabled = FALSE;
PortMapping->fBindingModified = TRUE;
}
}
}
BOOL
SharedAccessPortMappingDlg(
IN HWND hwndOwner,
IN OUT SAPM** PortMapping )
// Called to display the dialog for adding or editing a service-entry.
// 'Server' points to NULL if adding, or the target entry if editing.
//
{
LRESULT nResult =
DialogBoxParam(g_hinstDll, MAKEINTRESOURCE(DID_SS_Service),
hwndOwner, SspDlgProc, (LPARAM)PortMapping);
return nResult == -1 ? FALSE : (BOOL)nResult;
}
INT_PTR CALLBACK
SspDlgProc(
IN HWND hwnd,
IN UINT unMsg,
IN WPARAM wparam,
IN LPARAM lparam )
// Called to handle messages for the add/edit service dialog.
//
{
switch (unMsg)
{
case WM_INITDIALOG:
{
SADLG* pDlg;
SAPM* PortMapping;
// Retrieve the context-block for the settings dialog from which
// this dialog was launched.
//
if (!(pDlg = SasContext(hwnd)))
{
EndDialog(hwnd, FALSE);
return TRUE;
}
Edit_LimitText(GetDlgItem(hwnd, CID_SS_EB_ExternalPort), 5);
Edit_LimitText(GetDlgItem(hwnd, CID_SS_EB_InternalPort), 5);
// Create new service if adding a service, or retrieve the service
// to be edited.
//
if (!(PortMapping = *(SAPM**)lparam))
{
PortMapping = (SAPM*)Malloc(sizeof(*PortMapping));
if (!PortMapping)
{
EndDialog(hwnd, FALSE);
return TRUE;
}
*(SAPM**)lparam = PortMapping;
ZeroMemory(PortMapping, sizeof(*PortMapping));
PortMapping->Enabled = TRUE;
PortMapping->fNewEntry = TRUE;
InitializeListHead(&PortMapping->Link);
CheckDlgButton(hwnd, CID_SS_PB_Tcp, TRUE);
}
else
{
EnableWindow(GetDlgItem(hwnd, CID_SS_EB_Service), FALSE);
SetDlgItemText(hwnd, CID_SS_EB_Service, PortMapping->Title);
SetDlgItemInt(hwnd, CID_SS_EB_ExternalPort, ntohs(PortMapping->ExternalPort), FALSE);
SetDlgItemInt(hwnd, CID_SS_EB_InternalPort, ntohs(PortMapping->InternalPort), FALSE);
CheckDlgButton(
hwnd, CID_SS_PB_Tcp, PortMapping->Protocol == NAT_PROTOCOL_TCP);
CheckDlgButton(
hwnd, CID_SS_PB_Udp, PortMapping->Protocol != NAT_PROTOCOL_TCP);
SetDlgItemText(hwnd, CID_SS_EB_Address, PortMapping->InternalName);
// If the entry to be modified is marked 'built-in', disable
// all input fields except the server-name, which the user must
// now enter.
//
if (PortMapping->BuiltIn)
{
EnableWindow(GetDlgItem(hwnd, CID_SS_EB_ExternalPort), FALSE);
EnableWindow(GetDlgItem(hwnd, CID_SS_EB_InternalPort), FALSE);
EnableWindow(GetDlgItem(hwnd, CID_SS_PB_Tcp), FALSE);
EnableWindow(GetDlgItem(hwnd, CID_SS_PB_Udp), FALSE);
}
}
SetWindowLongPtr(hwnd, DWLP_USER, (ULONG_PTR)PortMapping);
CenterWindow(hwnd, GetParent(hwnd));
AddContextHelpButton(hwnd);
return TRUE;
}
case WM_HELP:
case WM_CONTEXTMENU:
{
ContextHelp( g_adwSspHelp, hwnd, unMsg, wparam, lparam );
break;
}
case WM_COMMAND:
{
if (HIWORD(wparam) != BN_CLICKED) { return FALSE; }
// If the user is dismissing the dialog, clean up and return.
//
SAPM* PortMapping;
SADLG* pDlg;
if (IDCANCEL == LOWORD(wparam))
{
PortMapping = (SAPM*)GetWindowLongPtr(hwnd, DWLP_USER);
if (IsListEmpty(&PortMapping->Link))
{
FreePortMappingEntry(PortMapping);
}
EndDialog (hwnd, FALSE);
return TRUE;
}
else if (LOWORD(wparam) != IDOK)
{
return FALSE;
}
else if (!(pDlg = SasContext(hwnd)))
{
return FALSE;
}
// Retrieve the service to be added or modified.
//
PortMapping = (SAPM*)GetWindowLongPtr(hwnd, DWLP_USER);
// Retrieve the values specified by the user,
// and attempt to save them in the new or modified entry.
//
UCHAR Protocol = IsDlgButtonChecked(hwnd, CID_SS_PB_Tcp)
? NAT_PROTOCOL_TCP : NAT_PROTOCOL_UDP;
BOOL Success;
ULONG ExternalPort = GetDlgItemInt(hwnd, CID_SS_EB_ExternalPort, &Success, FALSE);
if (!Success || ExternalPort < 1 || ExternalPort > 65535)
{
MsgDlg(hwnd, SID_TypePortNumber, NULL);
SetFocus(GetDlgItem(hwnd, CID_SS_EB_ExternalPort));
return FALSE;
}
ExternalPort = htons((USHORT)ExternalPort);
//
// Check to see if this is a duplicate. To do this we need
// to save the old port and protocol values, put the new
// values into the protocol entry, perform the check, and
// then restore the old values.
//
USHORT OldExternalPort = PortMapping->ExternalPort;
PortMapping->ExternalPort = (USHORT)ExternalPort;
UCHAR OldProtocol = PortMapping->Protocol;
PortMapping->Protocol = Protocol;
if (SrvConflictDetected(pDlg, PortMapping))
{
PortMapping->ExternalPort = OldExternalPort;
PortMapping->Protocol = OldProtocol;
SetFocus(GetDlgItem(hwnd, CID_SS_EB_ExternalPort));
return FALSE;
}
PortMapping->ExternalPort = OldExternalPort;
PortMapping->Protocol = OldProtocol;
// per BillI, there's no need to test internal ports for conflicts
ULONG InternalPort = GetDlgItemInt(hwnd, CID_SS_EB_InternalPort, &Success, FALSE);
if (InternalPort == 0)
InternalPort = ExternalPort;
else {
if (InternalPort < 1 || InternalPort > 65535)
{
MsgDlg(hwnd, SID_TypePortNumber, NULL);
SetFocus(GetDlgItem(hwnd, CID_SS_EB_InternalPort));
return FALSE;
}
InternalPort = htons((USHORT)InternalPort);
}
TCHAR* InternalName = GetText(GetDlgItem(hwnd, CID_SS_EB_Address));
if (!InternalName || !lstrlen(InternalName))
{
MsgDlg(hwnd, SID_SS_TypeAddress, NULL);
SetFocus(GetDlgItem(hwnd, CID_SS_EB_Address));
return FALSE;
}
if (IsListEmpty(&PortMapping->Link))
{
PortMapping->Title = GetText(GetDlgItem(hwnd, CID_SS_EB_Service));
if (!PortMapping->Title || !lstrlen(PortMapping->Title))
{
MsgDlg(hwnd, SID_TypeEntryName, NULL);
SetFocus(GetDlgItem(hwnd, CID_SS_EB_Service));
Free0(InternalName);
return FALSE;
}
}
if (PortMapping->Protocol != Protocol ||
PortMapping->ExternalPort != (USHORT)ExternalPort ||
PortMapping->InternalPort != (USHORT)InternalPort)
{
PortMapping->fProtocolModified = TRUE;
}
PortMapping->fBindingModified = TRUE;
PortMapping->Protocol = Protocol;
PortMapping->ExternalPort = (USHORT)ExternalPort;
PortMapping->InternalPort = (USHORT)InternalPort;
if (PortMapping->InternalName)
{
Free(PortMapping->InternalName);
}
PortMapping->InternalName = InternalName;
if (IsListEmpty(&PortMapping->Link))
{
InsertTailList(&pDlg->PortMappings, &PortMapping->Link);
}
EndDialog (hwnd, TRUE);
return TRUE;
}
}
return FALSE;
}