/********************************************************************************\
*  This sample is supplied as is with no implied warranty.  
*  It is designed to assist you in using the Cisco AnyConnect VPN API. 
*  It is assumed that you will build a production application and 
*  refer to this sample as a reference only.
\********************************************************************************/

#include "StdAfx.h"
#include <afxpriv.h> //for WM_IDLEUPDATECMDUI
#include <vector>

#include "CppComSampleOtherDlgs.h"

/***************************************************************************************/
// CDlgStatusBar
CDlgStatusBar::CDlgStatusBar()
{
}

CDlgStatusBar::~CDlgStatusBar()
{
}

LRESULT CDlgStatusBar::OnIdleUpdateCmdUI(WPARAM wParam, LPARAM /*not used*/)
{
    if (IsWindowVisible())
    {
        CFrameWnd *pParent = (CFrameWnd *)GetParent();
        if (pParent != NULL)
        {
            OnUpdateCmdUI(pParent, (BOOL)wParam);
        }
    }

    return 0;
}

BEGIN_MESSAGE_MAP(CDlgStatusBar, CStatusBar)
    ON_MESSAGE(WM_IDLEUPDATECMDUI, OnIdleUpdateCmdUI)
END_MESSAGE_MAP()


/***************************************************************************************/
//CBannerDlg
CBannerDlg::CBannerDlg(CString strBannerMessage) 
    : CDialog(CBannerDlg::IDD)
{
    m_strBannerMessage = strBannerMessage;
}

void CBannerDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    GetDlgItem(IDC_BANNER_TEXT)->SetWindowText(m_strBannerMessage);
}

BEGIN_MESSAGE_MAP(CBannerDlg, CDialog)
END_MESSAGE_MAP()

/***************************************************************************************/
//CVPNRoutesDlg
CVPNRoutesDlg::CVPNRoutesDlg(CWnd* pParent /* = NULL*/) 
    : CDialog(CVPNRoutesDlg::IDD),
        m_bIsFirstTime(true)
{
}

void CVPNRoutesDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST_SECURE_ROUTES, m_SecureRoutesListCtrl);
    DDX_Control(pDX, IDC_LIST_NONSECURE_ROUTES, m_NonSecureRoutesListCtrl);
}

BOOL CVPNRoutesDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    CRect listRect;
    m_SecureRoutesListCtrl.GetWindowRect(listRect);
    int columnWidth = listRect.Width() / 2 - 2;

    CString strTemp;

    strTemp.LoadString(IDS_NETWORK_ROUTE);
    m_SecureRoutesListCtrl.InsertColumn(0, 
                                        strTemp,
                                        LVCFMT_LEFT, 
                                        columnWidth);
    
    m_NonSecureRoutesListCtrl.InsertColumn(0,
                                           strTemp,
                                           LVCFMT_LEFT,
                                           columnWidth);

    strTemp.LoadString(IDS_SUBNET_MASK);
    m_SecureRoutesListCtrl.InsertColumn(1, 
                                        strTemp, 
                                        LVCFMT_LEFT, 
                                        columnWidth);

    m_NonSecureRoutesListCtrl.InsertColumn(1,
                                           strTemp, 
                                           LVCFMT_LEFT, 
                                           columnWidth);

    return TRUE;
}


void CVPNRoutesDlg::UpdateStats(const CComPtr<IVpnStats>& spVpnStats)
{
    if (!spVpnStats)
    {
        MessageBox(_T("The VpnStats COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    if (!IsWindowVisible() && !m_bIsFirstTime)
    {
        //There is no point in getting the routes if the UI is not visible,
        //except for first initialization.
        //
        return; 
    }

    m_bIsFirstTime = false;

    //Clear the previous contents.
    m_SecureRoutesListCtrl.DeleteAllItems();
    m_NonSecureRoutesListCtrl.DeleteAllItems();

    try
    {
        CComPtr<IInterfaceCollection> spCollection =  spVpnStats->SecureRoutes;

        //NOTE: COM Collections are 1's-based indexed.
        for (long nCount = 1; nCount <= spCollection->Count; nCount++)
        {
            //Obtain the text and then insert it in the appropriate column.
            //
            CComQIPtr<IRouteInfo> spRouteInfo(spCollection->Item[nCount]);
            if (spRouteInfo) //NOTE: This is not checking the smart pointer's address on the stack.
                             //      This actually calls implicit cast, CComQIPtr::operator T*(), which is what we want.
                             //      This code checks that the interface being queried is valid.
            {
                m_SecureRoutesListCtrl.InsertItem(nCount - 1, (LPCTSTR)spRouteInfo->Network);
                m_SecureRoutesListCtrl.SetItemText(nCount - 1, 1, (LPCTSTR)spRouteInfo->Subnet);
            }
        }
        
        //Add the non-secure routes. Note that re-using the smart pointer releases the previous
        //instance automatically.
        //
        spCollection =  spVpnStats->NonsecureRoutes;

        for (long nCount = 1; nCount <= spCollection->Count; nCount++)
        {
            //Obtain the text, then insert it into the appropriate column.
            //
            CComQIPtr<IRouteInfo> spRouteInfo(spCollection->Item[nCount]);
            if (spRouteInfo) //See earlier comments.
            {
                m_NonSecureRoutesListCtrl.InsertItem(nCount - 1, (LPCTSTR)spRouteInfo->Network);
                m_NonSecureRoutesListCtrl.SetItemText(nCount - 1, 1, (LPCTSTR)spRouteInfo->Subnet);
            }
        }
    }
    catch(_com_error err)
    {
        CString strErr; 
        strErr.Format(_T("Failed to obtain the the VPN Route information, error = 0x%.8X, [%s] description: %s"),
                      err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
        MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
    }

    UpdateWindow();
}

BEGIN_MESSAGE_MAP(CVPNRoutesDlg, CDialog)
END_MESSAGE_MAP()


/***************************************************************************************/
//CVPNStatsDlg
CVPNStatsDlg::CVPNStatsDlg(CWnd* pParent /* = NULL*/) 
    : CDialog(CVPNStatsDlg::IDD),
    m_bIsFirstTime(true),
    m_bIsStarted(true)
{
}

void CVPNStatsDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
}

BOOL CVPNStatsDlg::OnInitDialog()
{
    CDialog::OnInitDialog();


    return TRUE;
}

void CVPNStatsDlg::UpdateStats(const CComPtr<IVpnStats>& spVpnStats)
{
    if (!spVpnStats)
    {
        MessageBox(_T("The VpnStats COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    if (!m_bIsFirstTime && !IsWindowVisible())
    {
        //There is no point in getting the routes if the UI is not visible,
        //except for first initialization.
        //
        return; 
    }

    m_bIsFirstTime = false;

    //The sample application just iterates over all the available
    //tags and obtain a Label and Value for each one to demonstrate the API.
    //A developer would probably implement a fixed set of items and map
    //them to the VPNStatsTag enumerator in a more selective manner.
    //
    for (unsigned long nCount = 0; nCount < InvalidVPNStatsTag; nCount++)
    {
        try
        {
            //A developer might not obtain the translated label via this method, 
            //depending on what existing I18N the application already has.
            //
            _bstr_t bstrVal = spVpnStats->TranslatedLabel[static_cast<VPNStatsTag>(nCount)];
            CString controlStr = (LPCTSTR)bstrVal;
            controlStr += _T(" ");
            //NOTE: with COM property calls, you could use .tlh wrapped methods:
            //spVpnStats->GetTranslatedLabel(static_cast<VPNStatsTag>(nCount));
            //or
            //spVpnStats->TranslatedLabel[static_cast<VPNStatsTag>(nCount)];
            //

            //Get the stat value corresponding to the index and 
            //just append it to the string value.
            //
            bstrVal = spVpnStats->StatValue[static_cast<VPNStatsTag>(nCount)];
            controlStr += (LPCTSTR)bstrVal;

            //Set the windows text.
            //Note that the resources are sequentially valued from IDC_STAT1 - IDC_STATxx.
            //An application might create these controls dynamically instead of using this method.
            //
            CWnd* pWindow = GetDlgItem(IDC_STAT1 + nCount);
            if (pWindow != NULL)
            {
                pWindow->SetWindowText(controlStr);
            }

        }
        catch(_com_error err)
        {
            CString strErr; 
            strErr.Format(_T("Failed to obtain the the VPN stats information, error = 0x%.8X, [%s] description: %s"),
                          err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
            MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
        }
    }

    UpdateWindow();
}

void CVPNStatsDlg::OnBnClickedResetStats()
{
    if (!m_spVpnApi)
    {
        MessageBox(_T("The VpnApi COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    try
    {
        //Call the API ResetStats.
        //
        m_spVpnApi->ResetStats();

    }
    catch(_com_error err)
    {
        CString strErr; 
        strErr.Format(_T("Failed to trying to reset stats., error = 0x%.8X, [%s] description: %s"),
                      err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
        MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
    }
}

void CVPNStatsDlg::OnBnClickedStopStartStats()
{
    if (!m_spVpnApi)
    {
        MessageBox(_T("The VpnApi COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    try
    {
        //Call the API StopStats or StartStats.
        //
        if (m_bIsStarted)
        {
            m_spVpnApi->StopStats();
            CString strText;
            strText.LoadString(IDS_START_STATS);
            GetDlgItem(IDC_STOP_START_STATS)->SetWindowText(strText);
        }
        else
        {
            m_spVpnApi->StartStats();
            CString strText;
            strText.LoadString(IDS_STOP_STATS);
            GetDlgItem(IDC_STOP_START_STATS)->SetWindowText(strText);
        }

        m_bIsStarted = !m_bIsStarted;
    }
    catch(_com_error err)
    {
        CString strErr; 
        strErr.Format(_T("Failed to trying to reset stats., error = 0x%.8X, [%s] description: %s"),
                      err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
        MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
    }
   
}

BEGIN_MESSAGE_MAP(CVPNStatsDlg, CDialog)
    ON_BN_CLICKED(IDC_RESET_STATS, &CVPNStatsDlg::OnBnClickedResetStats)
    ON_BN_CLICKED(IDC_STOP_START_STATS, &CVPNStatsDlg::OnBnClickedStopStartStats)
END_MESSAGE_MAP()



/***************************************************************************************/
//CVPNProtocolDlg
CVPNProtocolDlg::CVPNProtocolDlg(CWnd* pParent /* = NULL*/) 
    : CDialog(CVPNProtocolDlg::IDD),
      m_bIsFirstTime(true)
{
}

void CVPNProtocolDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST_PROTOCOLS, m_ProtocolListCtrl);
}

BOOL CVPNProtocolDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    CRect listRect;
    m_ProtocolListCtrl.GetWindowRect(listRect);

    std::vector<int> columns;
    columns.push_back(IDS_PROTOCOL_STATE);
    columns.push_back(IDS_PROTOCOL_NAME);
    columns.push_back(IDS_PROTOCOL_CIPHER);
    columns.push_back(IDS_PROTCOL_COMPPRESSION);

    int columnWidth = listRect.Width() / columns.size() - 1;

    for (int i=0; i<columns.size(); i++)
    {
        CString strTemp;
        strTemp.LoadString(columns[i]);
        m_ProtocolListCtrl.InsertColumn(i, 
                                        strTemp,
                                        LVCFMT_LEFT, 
                                        columnWidth);
    }

    return TRUE;
}


void CVPNProtocolDlg::UpdateStats(const CComPtr<IVpnStats>& spVpnStats)
{
    if (!spVpnStats)
    {
        MessageBox(_T("The VpnStats COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    if (!IsWindowVisible() && !m_bIsFirstTime)
    {
        //There is no point in getting the routes if the UI is not visible,
        //except for first initialization.
        //
        return; 
    }
    m_bIsFirstTime = false;

    //Clear the previous contents.
    m_ProtocolListCtrl.DeleteAllItems();

    try
    {
        CComPtr<IInterfaceCollection> spCollection =  spVpnStats->ProtocolInfo;

        //NOTE: COM Collections are 1's-based indexed.
        for (long nCount = 1; nCount <= spCollection->Count; nCount++)
        {
            //Obtain the text, then insert it into the appropriate column.
            //
            CComQIPtr<IProtocolInfo> spProtocolInfo(spCollection->Item[nCount]);
            if (spProtocolInfo) //NOTE: This is not checking the smart pointer's address on the stack.
                                //      This actually calls implicit cast, CComQIPtr::operator T*(), which is what we want.
                                //      This code checks that the interface being queried is valid.
            {
                for (unsigned long nCol = 0; nCol < InvalidProtocolInfoTag; nCol++)
                {
                    if (nCol == 0)
                    {
                        m_ProtocolListCtrl.InsertItem(nCount - 1, spProtocolInfo->ProtocolValue[static_cast<ProtocolInfoTag>(nCol)]);
                    }
                    else
                    {
                        m_ProtocolListCtrl.SetItemText(nCount - 1, nCol, spProtocolInfo->ProtocolValue[static_cast<ProtocolInfoTag>(nCol)]);
                    }
                }
                spProtocolInfo->IsActive;
            }
        }
    }
    catch(_com_error err)
    {
        CString strErr; 
        strErr.Format(_T("Failed to obtain the the VPN protocol information, error = 0x%.8X, [%s] description: %s"),
                      err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
        MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
    }

    UpdateWindow();
}

BEGIN_MESSAGE_MAP(CVPNProtocolDlg, CDialog)
END_MESSAGE_MAP()



// CVpnFirewall dialog

IMPLEMENT_DYNAMIC(CVpnFirewall, CDialog)

CVpnFirewall::CVpnFirewall(CWnd* pParent /*=NULL*/)
	: CDialog(CVpnFirewall::IDD, pParent),
      m_bIsFirstTime(true)
{

}

CVpnFirewall::~CVpnFirewall()
{
}

void CVpnFirewall::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
    DDX_Control(pDX, IDC_LIST_FIREWALL_RULES, m_FirewallRulesCtrl);
}

BOOL CVpnFirewall::OnInitDialog()
{
    CDialog::OnInitDialog();

    CRect listRect;
    m_FirewallRulesCtrl.GetWindowRect(listRect);

    std::vector<int> columns;
    columns.push_back(IDS_FIREWALL_INTERFACE);
    columns.push_back(IDS_FIREWALL_PERMISSION);
    columns.push_back(IDS_FIREWALL_PROTOCOL);
    columns.push_back(IDS_FIREWALL_SOURCE_PORT);
    columns.push_back(IDS_FIREWALL_DEST_PORT);
    columns.push_back(IDS_FIREWALL_DEST_ADDRESS);

    int columnWidth = listRect.Width() / columns.size() - 1;

    for (int i=0; i<columns.size(); i++)
    {
        CString strTemp;
        strTemp.LoadString(columns[i]);
        m_FirewallRulesCtrl.InsertColumn(i, 
                                        strTemp,
                                        LVCFMT_LEFT, 
                                        columnWidth);
    }

    return TRUE;
}

void CVpnFirewall::UpdateStats(const CComPtr<IVpnStats>& spVpnStats)
{
    if (!spVpnStats)
    {
        MessageBox(_T("The VpnStats COM Component is NULL"), _T("ERROR"), MB_OK | MB_ICONERROR);
        return;
    }

    if (!IsWindowVisible() && !m_bIsFirstTime)
    {
        //There is no point in getting the firewall stats if the UI is not visible,
        //except for first initialization.
        //
        return; 
    }

    m_bIsFirstTime = false;

    //Clear the previous contents.
    m_FirewallRulesCtrl.DeleteAllItems();

    try
    {
        CComPtr<IInterfaceCollection> spCollection =  spVpnStats->FirewallInfo;

        //NOTE: COM Collections are 1's-based indexed.
        for (long nCount = 1; nCount <= spCollection->Count; nCount++)
        {
            //Obtain the text and then insert it in the appropriate column.
            //
            CComQIPtr<IFirewallInfo> spFirewallInfo(spCollection->Item[nCount]);
            if (spFirewallInfo) //NOTE: This is not checking the smart pointer's address on the stack.
                             //      This actually calls implicit cast, CComQIPtr::operator T*(), which is what we want.
                             //      This code checks that the interface being queried is valid.
            {
                m_FirewallRulesCtrl.InsertItem(nCount - 1, (LPCTSTR)spFirewallInfo->Interface);
                m_FirewallRulesCtrl.SetItemText(nCount - 1, 1, (LPCTSTR)spFirewallInfo->Permission);
                m_FirewallRulesCtrl.SetItemText(nCount - 1, 2, (LPCTSTR)spFirewallInfo->Protocol);
                m_FirewallRulesCtrl.SetItemText(nCount - 1, 3, (LPCTSTR)spFirewallInfo->SrcPortRange);
                m_FirewallRulesCtrl.SetItemText(nCount - 1, 4, (LPCTSTR)spFirewallInfo->DstPortRange);
                m_FirewallRulesCtrl.SetItemText(nCount - 1, 5, (LPCTSTR)spFirewallInfo->DstAddr);
            }
        }
    }
    catch(_com_error err)
    {
        CString strErr; 
        strErr.Format(_T("Failed to obtain the the VPN Route information, error = 0x%.8X, [%s] description: %s"),
                      err.Error(), (LPCTSTR)err.Source(), (LPCTSTR)err.Description());                              
        MessageBox(strErr, _T("ERROR"), MB_OK | MB_ICONERROR);
    }

    UpdateWindow();
}


BEGIN_MESSAGE_MAP(CVpnFirewall, CDialog)
    ON_BN_CLICKED(IDOK, &CVpnFirewall::OnBnClickedOk)
END_MESSAGE_MAP()

void CVpnFirewall::OnBnClickedOk()
{
    OnOK();
}

//CCertBlockedDlg
CCertBlockedDlg::CCertBlockedDlg(const CString &strUntrustedServer)
    : CDialog(CCertBlockedDlg::IDD)
{
    m_strBlockedMessage = strUntrustedServer;
    m_strBlockedMessage += _T(" cannot be verified. Do you want to change settings to allow for untrusted VPN servers?");
}

void CCertBlockedDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    GetDlgItem(IDC_CERT_BLOCKED_TEXT)->SetWindowText(m_strBlockedMessage);
}

BEGIN_MESSAGE_MAP(CCertBlockedDlg, CDialog)
    ON_BN_CLICKED(IDC_CHANGE_SETTING, &CCertBlockedDlg::OnBnClickedChangeSetting)
    ON_BN_CLICKED(IDC_KEEP_ME_SAFE, &CCertBlockedDlg::OnBnClickedKeepMeSafe)
END_MESSAGE_MAP()
void CCertBlockedDlg::OnBnClickedChangeSetting()
{
    EndDialog(IDC_CHANGE_SETTING);
}

void CCertBlockedDlg::OnBnClickedKeepMeSafe()
{
   EndDialog(IDC_KEEP_ME_SAFE);
}

//CCertWarningDlg
CCertWarningDlg::CCertWarningDlg(const CString &strUntrustedServer,
                                 const std::list<CString> &lCertErrors,
                                 bool bImportAllowed)
    : CDialog(CCertWarningDlg::IDD),
      m_bImportAllowed(bImportAllowed)
{
    m_strWarningMessage = _T("The server certificate from ");
    m_strWarningMessage += strUntrustedServer;
    m_strWarningMessage += _T(" contains the following errors:\n");

    size_t numErrors = lCertErrors.size();
    size_t i = 0;
    for (std::list<CString>::const_iterator cit = lCertErrors.begin();
         cit != lCertErrors.end();
         i++, cit++)
    {
        m_strWarningMessage += _T(" - ");
        m_strWarningMessage += *cit;
        if (i != (numErrors - 1))
        {
            m_strWarningMessage += _T("\n");
        }
    }
}

void CCertWarningDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    GetDlgItem(IDC_WARNING_MSG)->SetWindowText(m_strWarningMessage);
    if (!m_bImportAllowed)
    {
        GetDlgItem(IDC_ALWAYS_CONNECT)->ShowWindow(SW_HIDE);
    }
}

BEGIN_MESSAGE_MAP(CCertWarningDlg, CDialog)
    ON_BN_CLICKED(IDC_CONNECT_ANYWAY, &CCertWarningDlg::OnBnClickedContinue)
    ON_BN_CLICKED(IDC_ALWAYS_CONNECT, &CCertWarningDlg::OnBnClickedAlwaysConnect)
    ON_BN_CLICKED(IDC_CANCEL_CONNECT, &CCertWarningDlg::OnBnClickedCancel)
END_MESSAGE_MAP()

void CCertWarningDlg::OnBnClickedContinue()
{
    EndDialog(IDC_CONNECT_ANYWAY);
}

void CCertWarningDlg::OnBnClickedAlwaysConnect()
{
    EndDialog(IDC_ALWAYS_CONNECT);
}

void CCertWarningDlg::OnBnClickedCancel()
{
   EndDialog(IDC_CANCEL_CONNECT);
}

